How to set up In-app analytics

Hi Fred
Was looking for a way of of getting analytics on how many cases have been registered, tested, positive and negative from the forms filled per day and exploring the following:

  1. Did the suggested method work for you? What analytics does telemetry provide?
  2. How do you activate analytics tab on the Medic Dashboard (Target and target aggregates)?
    Hope to configure our instance tomorrow to receive analytics data, will appreciate any feedback, thank you

@oyierphil we have a tutorial that may help Building Target Widgets | Community Health Toolkit. Have you gone through that? Did you face any challenges?

How do you activate analytics tab on the Medic Dashboard (Target and target aggregates)?

The role needs to have the can_view_analytics and can_view_analytics_tab permissions set.

We also have some examples (through a bit dated) in our default config

Derick
I had gone through the tutorial and just done the steps again now, configuration is done correctly but the analytics tab doesnā€™t show anything as shown below. Iā€™m using a test VM for this exercise

Want to review further the target widget settings, and re-run


Are you by chance using an admin account to log in to your local instance? You may be experiencing this Targets are disabled for admin users. If you need to see targets, login as a normal user

Derick
Normal user is which role? Tried with Program Officer role and same message, ā€œTargets are disabled for admin usersā€
Trying to review this code and relate to our instance before re-running:
module.exports = [
{
id: ā€˜assessments-all-timeā€™,
type: ā€˜countā€™,
icon: ā€˜icon-healthcare-assessmentā€™,
goal: -1,
translation_key: ā€˜targets.assessments.titleā€™,
subtitle_translation_key: ā€˜targets.all_time.subtitleā€™,

appliesTo: 'reports',
appliesToType: ['assessment'],
date: 'now'

},
{
id: ā€˜assessments-this-monthā€™,
type: ā€˜countā€™,
icon: ā€˜icon-healthcare-assessmentā€™,
goal: -1,
translation_key: ā€˜targets.assessments.titleā€™,
subtitle_translation_key: ā€˜targets.this_month.subtitleā€™,

appliesTo: 'reports',
appliesToType: ['assessment'],
date: 'reported'

}
];

Normal would mean a non-admin user that is restricted to their place.

For your situation, you would need to make the Program Officer role offline.

In app_settings.json (assuming your local app_settings is in sync with what is uploaded on your instance, you can modify the role as shown below: (modify to match your local naming)


"roles": {
    "program_officer": {
      "name": "usertype.program-officer",
      "offline": true
    },
  },

Derick
Is it possible to get a sample form and analytics target file for the same?
Figuring out how to link the id property of targets to the actual form fields, then I should be good to go

Like looking at the code below:

// 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',

},
Which section picks data from our forms? There is something I am not getting, the link between the target values and data collected through the forms, any help?

  1. appliesTo: 'reports'

Since your target applies to reports, it counts all the reports that match the condition specified with appliesIf.

  1. appliesIf: isHealthyDelivery

The function isHealthyDelivery must be defined somewhere accessible. If we look at this example from the standard config:

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

Here, it will return true for all D and delivery reports which have the field pregnancy_outcome equal to healthy. So only those reports will be counted towards the target. You can change it to match your requirements.

  1. date: 'reported'
    This means that the target will count reports with a reported_date within the current month.

We have a reference page for targets.js that might be helpful to learn about these different fields.

Dear Binod
I understand the fields from the online documentation, what I missed is the link between targets and actual fields on the different forms. I see the function definition, and I see it picks both the form and field

For example we have been screening some target population for TB, Covid and PCR in different regions, thus our hierarchy is two level, one root with four children.

We only have three (3) forms (Contact creating and editing, case investigation with the screening algorithm), thus I want to create targets and visualize percentage tested and percentage screened so far both from the root (National Office) and the four branches (County1, County2,County3, County4 )

Let me figure it out, will create a function percTested() and percScreened(), will revert once I test

I also saw errors with indentation while testing targets yesterday, does one tab space work or normally spacing?

We use 2 spaces for indenting. You can get our coding style guide here.

If you need a percentage target, you need this information:

  1. What is the denominator? (counted with appliesIf)
  2. What is the numerator? (counted with passesIf)

For your percentage screening, letā€™s make some assumptions:

  1. Denominator: Count of all contacts created this month
  2. Numerator: Count of contacts from the denominator (who made through appliesIf), and having at least one screening report

Please note the following things:

  1. The target applies to contacts, not to the reports.
  2. You might want to only look for contacts who are persons, but not count the health workers or other staff.
  3. We can see get the reports for the contact with contact.reports and determine whether to count the contact or not, based on the reports.

Then your target config might have something like this:

   ...
    type: 'percent',
    appliesTo: 'contacts',
    appliesToType: ['person'],
    appliesIf: function (contact) {
      //skipping CHWs
      return contact.role !== 'chw';
    },
    passesIf: function (contact) {
      //If there is a screening report, count the contact as pass
      return contact.reports.some(report => report.form === 'screening');
    },
   date: 'reported'

Binod
I have tried the following code for only Covid targets:
module.exports = [
{
id: ā€˜percentage-covid-testā€™,
type: ā€˜percentā€™,
icon: ā€˜icon-healthcare-assessmentā€™,
goal: -1,
translation_key: ā€˜targets.covid_test.titleā€™,
subtitle_translation_key: ā€˜targets.this_month.subtitleā€™,

appliesTo: 'contacts',
appliesToType: ['person'],
appliesIf: covidPercTested,

date: 'reported'

},

];

And the following for nools-extra
module.exports = {
covidPercTested(c,r) {
return r.form === ā€˜covā€™ && r.fields.screening_cov === ā€˜trueā€™);
},
I get the error below:

@oyierphil, you are getting a reference error. It means that the function covidPercTested is not accessible from your targets.js. In order to get the reference correctly, you need to export the function from nools-extras.js and import it in targets.js.

Please see this example from the default config where the function isAlive is defined here, exported here and then imported here.

Also, since your target now applies to the contacts, you might want to check the contacts instead of reports that match your criteria. For targets applying to contacts, the report will always be undefined in the appliesIf(contact, report) function.

Something like this might work, but you might want to use it in passesIf instead:

covidPercTested(contact) {
  return contact.reports.some(r => r.form === ā€˜covā€™ && r.fields.screening_cov === ā€˜trueā€™);
},

Binod
I read and re-wrote my code again, I get an error which I cant see
target file
const extras = require(ā€˜./nools-extrasā€™);

const {
covidPercTested

} = extras;

module.exports = [
{
id: ā€˜percentage-covid-testā€™,
type: ā€˜percentā€™,
icon: ā€˜icon-healthcare-assessmentā€™,
goal: -1,
translation_key: ā€˜targets.covid_test.titleā€™,
subtitle_translation_key: ā€˜targets.this_month.subtitleā€™,

appliesTo: 'contacts',
appliesToType: ['person'],

appliesIf: covidPercTested,

passesIf: covidPercTested(contact)

{
return contact.reports.cov(report => report.form === ā€˜screeningā€™);
},

date: 'reported'

},

];

nools-extras.js

function covidPercTested(contact) {
{
return report.form === ā€˜samplingā€™ && report.fields.screening_cov === ā€˜trueā€™);
},

module.exports = {
covidPercTested,
};

@oyierphil I have not taken a close look at your logic here, but I think the source of your SyntaxError is that I think you mean to use function instead of covidPercTested in this section:

    passesIf: covidPercTested(contact)
    {
      return contact.reports.cov(report => report.form === 'screening');
    },

So, it would just read like this:

    passesIf: function(contact)
    {
      return contact.reports.cov(report => report.form === 'screening');
    },

jkuester,
I made the correction and got the same error, ā€™ SyntaxError: Unexpected token )ā€™
Seem not to be able to point where the error is

Trying to review if I picked the correct details from my form for the following: contact.reports.X and
report => report.form
I have the following field from the forms, screening.cov from which I want to set the targets for Covid
Regards

Out of curiosity, is your SyntaxError actually coming from the nools-extras.js file now?

  ERROR Failed to parse file /var/Bodaboda_Test/config-boda-moh-ke-master/nools-extras.js. SyntaxError: Unexpected token )

I noticed in your code above for the covidPercTested function it looked like there was an extra ) (and a few other syntax issues). So

function covidPercTested(contact) {
{
  return report.form === 'sampling' && report.fields.screening_cov === 'true');
},

module.exports = {
  covidPercTested,
};

should actually be something like:

function covidPercTested(contact)
{
  return report.form === 'sampling' && report.fields.screening_cov === 'true';
}

module.exports = {
  covidPercTested,
};

That may fix the SyntaxErrors, but is still seems like there is something wrong with the logic in that covidPercTested function (since you are not actually loading anything off the contact). Maybe you intended that function to look similar to the one Binod suggested above:

function covidPercTested(contact)
{
  return contact.reports.some(report => report.form === 'sampling' && report.fields.screening_cov === 'true');
}

jkuester
Trying to figure out what ā€œcontact.reports.someā€ mean
What value does report.form === X take and report.fields.Y
Going back to basics now, appreciate the support

Array.some(ā€¦) basically just checks to see if any of the elements in the array match the given test. So in this case covidPercTested would return true for any contact that had at least one report matching report.form === 'sampling' && report.fields.screening_cov === 'true'. (That check gets run for each report entry in the contact.reports array.)