Offline Reports

Avni allows you to create different indicator reports that are available offline to the field users. These reports help field users to derive more insights on the captured data. Creating an offline report is a two-step process. First, we need to create a report card that holds the actual query function and then we group multiple cards to a dashboard.

Creating a Report Card

Creating a new report card is no different than creating any other Avni entity. Open app designer and go to the report card tab. Click on the new report card and provide the details like name description and query for the card. The provided query runs whenever the user opens the dashboard containing this card. Please note that the query function should always return a list of Individuals.

Creating a Dashboard

After all the cards are done it's time to group them together using the dashboard. Click on the dashboard tab on the app designer and click on the new dashboard. This will take you to the new dashboard screen. Provide the name and description of the dashboard and select all the cards you need to add to this dashboard. After adding all the cards, you can re-arrange the cards in the order you want them to see in the field app. Once all the changes are done. Save the dashboard.

After saving the dashboard sync the field app, and from the bottom more tab click on the dashboard option. It will take you to the dashboard screen and will show all the cards that are added to the dashboard.

Clicking any card will take the user to the subject listing page, which will display all the subject names returned by the card query.

Users can click on any subject and navigate to their dashboard.

Report card query example

As mentioned earlier query should return a list of Individuals. DB instance is passed using the params and useful libraries like lodash and moment are available in the imports parameter of the function. The below function returns a list of pregnant women having any high-risk conditions.

'use strict';
({params, imports}) => {
    const isHighRiskWomen = (enrolment) => {
        const weight = enrolment.findLatestObservationInEntireEnrolment('Weight');
        const hb = enrolment.findLatestObservationInEntireEnrolment('Hb');
        const numberOfLiveChildren = enrolment.findLatestObservationInEntireEnrolment('Number of live children');
        return (weight && weight.getReadableValue() < 40) || (hb && hb.getReadableValue() < 8) ||
            (numberOfLiveChildren && numberOfLiveChildren.getReadableValue() > 3)
    };
    return params.db.objects('Individual')
        .filtered(`SUBQUERY(enrolments, $enrolment, SUBQUERY($enrolment.encounters, $encounter, $encounter.encounterType.name = 'Monthly monitoring of pregnant woman' and $encounter.voided = false)[email protected] > 0 and $enrolment.voided = false and voided = false)[email protected] > 0`)
        .filter((individual) => individual.voided === false && _.some(individual.enrolments, (enrolment) => enrolment.program.name === 'Pregnant Woman' && isHighRiskWomen(enrolment)))
};

It is important to write optimized query and load very less data in memory. There will be the cases where query can't be written in realm and we need to load the data in memory, but remember more data we load into the memory slower will be the reports. As an example consider below two cases, in the first case we directly query realm to fetch all the individuals enrolled in Child program, but in the second case we first load all individuals into memory and then filter those cases.

'use strict';
({params, imports}) => {
    return params.db.objects('Individual')
        .filtered(`SUBQUERY(enrolments, $enrolment, $enrolment.program.name = 'Child' and voided = false)[email protected] > 0`)
};
'use strict';
({params, imports}) => {
    return params.db.objects('Individual')
        .filter((individual) => individual.voided === false && _.some(individual.enrolments, (enrolment) => enrolment.program.name === 'Child'))
};