targets.js

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.
analytics.getTargetDocs()Returns three target documents of the contact, calculated for the last three reporting intervals, including the current one. When viewing one of the current logged in user’s associated facilities, returns the target documents for the contact associated with the current logged in user. Returns an empty array if no target documents are found (for example when viewing a contact that does not upload targets). Introduced in v4.11.0

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'], 
    ['can_view_reports', 'can_verify_reports']
]);
const averageFn = cht.v1.getExtensionLib('average.js');
const targetDocs = cht.v1.analytics.getTargetDocs();

Code Samples

This sample targets.js generates three widgets, and uses functions written in the targets-extras.js file.

targets.js

const { isHealthyDelivery, countReportsSubmittedInWindow } = require('./targets-extras');

module.exports = [
  // BIRTHS THIS MONTH
  {
    id: 'births-this-month',
    type: 'count',
    icon: 'infant',
    goal: -1,
    translation_key: 'targets.births.title',
    subtitle_translation_key: 'targets.this_month.subtitle',

    appliesTo: 'reports',
    appliesIf: isHealthyDelivery,
    date: 'reported',
  },

  // % DELIVERIES ALL TIME WITH 1+ VISITS
  {
    id: 'delivery-with-min-1-visit',
    type: 'percent',
    icon: 'nurse',
    goal: 100,
    translation_key: 'targets.delivery_1_visit.title',
    subtitle_translation_key: 'targets.all_time.subtitle',

    appliesTo: 'reports',
    idType: 'report',
    appliesIf: isHealthyDelivery,
    passesIf: function(c, r) {
      var visits = countReportsSubmittedInWindow(c.reports, antenatalForms, r.reported_date - MAX_DAYS_IN_PREGNANCY*MS_IN_DAY, r.reported_date);
      return visits > 0;
    },
    date: 'now',
  },

  {
    id: '2-home-visits-per-family',
    icon: 'home-visit',
    type: 'percent',
    goal: 100,
    translation_key: `target.2-home-visits-per-family`,
    context: 'user.role === "chw"',
    date: 'reported',

    appliesTo: 'contacts',
    appliesToType: 'person',
    idType: contact => {
      // Determines the target ids which will be in the group.
      // eg. "family1~2000-02-15" and "family1~2000-02-16"
      const householdVisitDates = new Set(contact.reports.map(report => toDateString(report.reported_date)));
      const familyId = contact.contact.parent._id;
      return Array.from(householdVisitDates).map(date => `${familyId}~${date}`);
    },
    groupBy: contact => contact.contact.parent._id,
    passesIfGroupCount: { gte: 2 },
  }
]

targets-extras.js

module.exports = {
  isHealthyDelivery(c, r) {
    return r.form === 'D' ||
        (r.form === 'delivery' && r.fields.pregnancy_outcome === 'healthy');
  },

  countReportsSubmittedInWindow(reports, form, start, end) {
    var reportsFound = 0;
    reports.forEach(function(r) {
      if (form.indexOf(r.form) >= 0) {
        if (r.reported_date >= start && r.reported_date <= end) {
          reportsFound++;
        }
      }
    });
    return reportsFound;
  },
};

Build

To build your targets into your app, you must compile them into app-settings, then upload them to your instance.

cht --local compile-app-settings backup-app-settings upload-app-settings


Building > Targets > Overview

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.



Last modified 04.11.2024: Move target section (#1681) (fba607ab)