Performing calculations for cohorts on reporting module

@mseaton It seems to me that all Cohorts in the reporting module are based on patient IDs and therefore return patient IDs for specific cohort.

What I want to do is to calculate the median of cd4 of patients in a specific cohort and include it in the report that has all other normal cohorts which return counts of patients in the given cohort, how can I best do it?

Thanks

@carapai, assuming you are using CohortIndicators in your report, a CohortIndicator has an additional couple of properties on it that you can set if you want to return an indicator based on aggregate data on the cohort, rather than simply the count of patients in the Cohort. These are:

Class<? extends Aggregator> aggregator (there are a couple of included Aggregator types, including MeanAggregator)

Mapped<? extends PatientDataDefinition> dataToAggregate (there are a variety of PatientDataDefinition types that are built in that you can choose from)

In your case, I would choose to use an ObsForPersonDataDefinition (which=LAST, question=CD4 Count). This is a PersonDataDefinition, so you will need to convert this to a PatientDataDefinition by using the PersonToPatientDataDefinition adapter class as well.

I don’t know whether this is doable from the UI or without writing Java code to utilize these constructs.

Hope this helps, Mike

Thank you @mseaton I will follow you suggestions, if I get any issues, I will surely contact you

@mseaton,

I have this code:

CohortDefinition netCurrentCohort = df.createPatientComposition("(", startedArtInFacility, “OR”, netTransferIn, ") AND NOT ", netTransferredOut); PatientDataDefinition latestCD4 = hivPatientDataLibrary.getRecentCD4(); CohortIndicatorDataSetDefinition dsd = new CohortIndicatorDataSetDefinition();

addMedianIndicator(dsd, “16”, “Median CD4”, netCurrentCohort, latestCD4); public void addMedianIndicator(CohortIndicatorDataSetDefinition dsd, String key, String label, CohortDefinition cohortDefinition, PatientDataDefinition data) { CohortIndicator ci = new CohortIndicator(); ci.addParameter(ReportingConstants.START_DATE_PARAMETER); ci.addParameter(ReportingConstants.END_DATE_PARAMETER); ci.setType(CohortIndicator.IndicatorType.FRACTION); ci.setAggregator(MedianAggregator.class); ci.setDataToAggregate(Mapped.mapStraightThrough(data)); ci.setCohortDefinition(Mapped.mapStraightThrough(cohortDefinition)); dsd.addColumn(key, label, Mapped.mapStraightThrough(ci), “”); } I am wondering if that is the right way of doing it or there something I am doing wrong? What I am trying to

@carapai - I take it you are borrowing some code from the pihmalawi module - nice :slight_smile:

I think your post was cut off, but one thing I notice is that you are setting the Cohort Indicator type to FRACTION, whereas it should be type LOGIC. See “CohortIndicatorResult.java” for why.

Hope this helps, Mike

@mseaton, I am very sorry to disturb you, but I am not understanding why this is happening, that the date parameter is not being passed from report to the custom definition I created.

The date value from the definition is always null

I have this code,

/**

  • Created by carapai on 13/05/2016. */ @Caching( strategy = ConfigurationPropertyCachingStrategy.class ) public class ObsForPersonInPeriodDataDefinition extends BaseDataDefinition implements PatientDataDefinition {

    @ConfigurationProperty private Period obsPeriod; @ConfigurationProperty private Period encounterPeriod; @ConfigurationProperty private Date onDate; @ConfigurationProperty private List encounterTypes; @ConfigurationProperty private Concept question; @ConfigurationProperty private TimeQualifier whichEncounter; @ConfigurationProperty private TimeQualifier whichObs; @ConfigurationProperty private List answers; @ConfigurationProperty private int periodToAdd = 0; @ConfigurationProperty private boolean encountersInclusive = false; @ConfigurationProperty private boolean valueDatetime = false;

    public ObsForPersonInPeriodDataDefinition() { super(); }

    public ObsForPersonInPeriodDataDefinition(String name) { super(name); }

    @Override public Class<?> getDataType() { return Obs.class; }

    // Getters and Setters }

/**

  • Created by carapai on 13/05/2016. */ @Handler(supports = ObsForPersonInPeriodDataDefinition.class, order = 50) public class ObsForPersonInPeriodDataDefinitionEvaluator implements PatientDataEvaluator { protected static final Log log = LogFactory.getLog(ObsForPersonInPeriodDataDefinitionEvaluator.class);

    @Autowired private EvaluationService evaluationService;

    @Override public EvaluatedPatientData evaluate(PatientDataDefinition definition, EvaluationContext context) throws EvaluationException { ObsForPersonInPeriodDataDefinition def = (ObsForPersonInPeriodDataDefinition) definition;

     EvaluatedPatientData c = new EvaluatedPatientData(def, context);
    
     if (context.getBaseCohort() != null && context.getBaseCohort().isEmpty()) {
         return c;
     }
    
     Period encounterPeriod = def.getEncounterPeriod();
    

    // This date is always null Date anotherDate = def.getOnDate();

     // Code to do the evaluation
    
     return c;
    

    } }

@Component public class DataFactory {

@Autowired
HIVMetadata hivMetadata;

public Parameter getStartDateParameter() {
    return ReportingConstants.START_DATE_PARAMETER;
}

public Parameter getEndDateParameter() {
    return ReportingConstants.END_DATE_PARAMETER;
}

public Parameter getOnDateParameter() {
    return new Parameter("onDate", "On Date", Date.class);
}

// Data Converters


// Composition Cohorts


// Patient Data Definitions

public PatientDataDefinition getObsValue(Concept question, List<EncounterType> encounterTypes, DataConverter converter) {
    ObsForPersonInPeriodDataDefinition def = new ObsForPersonInPeriodDataDefinition();
    def.setQuestion(question);
    def.setEncounterTypes(encounterTypes);
    def.addParameter(new Parameter("onDate", "On Date", Date.class));
    return convert(def, ObjectUtil.toMap("onDate=startDate"), converter);
}


public PatientDataDefinition getObsValue(Concept question, List<EncounterType> encounterTypes, List<Concept> answers, DataConverter converter) {
    ObsForPersonInPeriodDataDefinition def = new ObsForPersonInPeriodDataDefinition();
    def.setAnswers(answers);
    def.setQuestion(question);
    def.setEncounterTypes(encounterTypes);
    def.addParameter(new Parameter("onDate", "On Date", Date.class));
    return convert(def, ObjectUtil.toMap("onDate=startDate"), converter);
}

public PatientDataDefinition hasVisitDuringPeriod(Period period, Integer periodToAdd, DataConverter converter) {
    ObsForPersonInPeriodDataDefinition def = new ObsForPersonInPeriodDataDefinition();
    def.setEncounterTypes(hivMetadata.getArtEncounterTypes());
    def.setEncounterPeriod(period);
    def.setObsPeriod(period);
    def.setWhichObs(TimeQualifier.LAST);
    def.setPeriodToAdd(periodToAdd);
    def.addParameter(new Parameter("onDate", "On Date", Date.class));
    return convert(def, ObjectUtil.toMap("onDate=startDate"), converter);
}

public PatientDataDefinition getObsValueDuringPeriod(Concept question, List<EncounterType> encounterTypes, List<Concept> answers, Integer periodToAdd, Map<String, Object> args, DataConverter converter) {
    ObsForPersonInPeriodDataDefinition def = new ObsForPersonInPeriodDataDefinition();
    def.setQuestion(question);
    def.setEncounterTypes(encounterTypes);
    def.setAnswers(answers);
    Period obsPeriod = null;
    Period encounterPeriod = null;
    Boolean includeEncounters = false;
    TimeQualifier whichEncounter = null;
    TimeQualifier whichObs = null;

    Set<String> keys = args.keySet();

    if (keys.contains("obsPeriod")) {
        obsPeriod = (Period) args.get("obsPeriod");
    }

    if (keys.contains("encounterPeriod")) {
        encounterPeriod = (Period) args.get("encounterPeriod");
    }

    if (keys.contains("includeEncounters")) {
        includeEncounters = (Boolean) args.get("includeEncounters");
    }
    if (keys.contains("whichObs")) {
        whichObs = (TimeQualifier) args.get("whichObs");
    }

    if (keys.contains("whichEncounter")) {
        whichEncounter = (TimeQualifier) args.get("whichEncounter");
    }

    if (includeEncounters) {
        def.setWhichEncounter(whichEncounter);
        def.setEncounterPeriod(encounterPeriod);
    }
    def.setWhichObs(whichObs);
    def.setObsPeriod(obsPeriod);
    def.setPeriodToAdd(periodToAdd);
    def.addParameter(new Parameter("onDate", "On Date", Date.class));
    return convert(def, ObjectUtil.toMap("onDate=startDate"), converter);
}

public PatientDataDefinition getObsValueDuringPeriod(Concept question, List<EncounterType> encounterTypes, Map<String, Object> args, Integer periodToAdd, DataConverter converter) {
    ObsForPersonInPeriodDataDefinition def = new ObsForPersonInPeriodDataDefinition();
    def.setQuestion(question);
    def.setEncounterTypes(encounterTypes);
    Period obsPeriod = null;
    Period encounterPeriod = null;
    Boolean includeEncounters = false;
    TimeQualifier whichEncounter = null;
    TimeQualifier whichObs = null;
    Boolean valueDatetime = false;

    Set<String> keys = args.keySet();

    if (keys.contains("obsPeriod")) {
        obsPeriod = (Period) args.get("obsPeriod");
    }

    if (keys.contains("encounterPeriod")) {
        encounterPeriod = (Period) args.get("encounterPeriod");
    }

    if (keys.contains("includeEncounters")) {
        includeEncounters = (Boolean) args.get("includeEncounters");
    }
    if (keys.contains("whichObs")) {
        whichObs = (TimeQualifier) args.get("whichObs");
    }

    if (keys.contains("whichEncounter")) {
        whichEncounter = (TimeQualifier) args.get("whichEncounter");
    }

    if (keys.contains("valueDatetime")) {
        valueDatetime = (Boolean) args.get("valueDatetime");
    }

    if (includeEncounters) {
        def.setWhichEncounter(whichEncounter);
        def.setEncounterPeriod(encounterPeriod);
    }
    def.setWhichObs(whichObs);
    def.setObsPeriod(obsPeriod);
    def.setPeriodToAdd(periodToAdd);
    def.setValueDatetime(valueDatetime);
    def.addParameter(new Parameter("onDate", "On Date", Date.class));
    return convert(def, ObjectUtil.toMap("onDate=startDate"), converter);
}
//

public PatientDataDefinition getObsValueDuringPeriod(Concept question, Integer periodToAdd, Map<String, Object> args, DataConverter converter) {
    ObsForPersonInPeriodDataDefinition def = new ObsForPersonInPeriodDataDefinition();
    def.setQuestion(question);
    Period obsPeriod = null;
    Period encounterPeriod = null;
    Boolean includeEncounters = false;
    TimeQualifier whichEncounter = null;
    TimeQualifier whichObs = null;
    Boolean valueDatetime = false;

    Set<String> keys = args.keySet();

    if (keys.contains("obsPeriod")) {
        obsPeriod = (Period) args.get("obsPeriod");
    }

    if (keys.contains("encounterPeriod")) {
        encounterPeriod = (Period) args.get("encounterPeriod");
    }

    if (keys.contains("includeEncounters")) {
        includeEncounters = (Boolean) args.get("includeEncounters");
    }
    if (keys.contains("whichObs")) {
        whichObs = (TimeQualifier) args.get("whichObs");
    }

    if (keys.contains("whichEncounter")) {
        whichEncounter = (TimeQualifier) args.get("whichEncounter");
    }

    if (keys.contains("valueDatetime")) {
        valueDatetime = (Boolean) args.get("valueDatetime");
    }

    if (includeEncounters) {
        def.setWhichEncounter(whichEncounter);
        def.setEncounterPeriod(encounterPeriod);
    }
    def.setWhichObs(whichObs);
    def.setObsPeriod(obsPeriod);
    def.setPeriodToAdd(periodToAdd);
    def.setValueDatetime(valueDatetime);
    def.addParameter(new Parameter("onDate", "On Date", Date.class));
    return convert(def, ObjectUtil.toMap("onDate=startDate"), converter);
}

public PatientDataDefinition getObsValueDuringPeriod(Concept question, List<Concept> answers, Integer periodToAdd, Map<String, Object> args, DataConverter converter) {
    ObsForPersonInPeriodDataDefinition def = new ObsForPersonInPeriodDataDefinition();
    def.setQuestion(question);
    def.setAnswers(answers);
    Period obsPeriod = null;
    Period encounterPeriod = null;
    Boolean includeEncounters = false;
    TimeQualifier whichEncounter = null;
    TimeQualifier whichObs = null;

    Set<String> keys = args.keySet();

    if (keys.contains("obsPeriod")) {
        obsPeriod = (Period) args.get("obsPeriod");
    }

    if (keys.contains("encounterPeriod")) {
        encounterPeriod = (Period) args.get("encounterPeriod");
    }

    if (keys.contains("includeEncounters")) {
        includeEncounters = (Boolean) args.get("includeEncounters");
    }
    if (keys.contains("whichObs")) {
        whichObs = (TimeQualifier) args.get("whichObs");
    }

    if (keys.contains("whichEncounter")) {
        whichEncounter = (TimeQualifier) args.get("whichEncounter");
    }

    if (includeEncounters) {
        def.setWhichEncounter(whichEncounter);
        def.setEncounterPeriod(encounterPeriod);
    }
    def.setWhichObs(whichObs);
    def.setObsPeriod(obsPeriod);
    def.setPeriodToAdd(periodToAdd);
    def.addParameter(new Parameter("onDate", "On Date", Date.class));
    return convert(def, ObjectUtil.toMap("onDate=startDate"), converter);
}

// Cohorts Definitions

}

/**

  • Attributes of a person not defined within the core OpenMRS definition */ @Component public class HIVPatientDataLibrary extends BaseDefinitionLibrary { @Autowired private DataFactory df;

    @Autowired private HIVMetadata hivMetadata;

    @Override public Class<? super PatientDataDefinition> getDefinitionType() { return PatientDataDefinition.class; }

    @Override public String getKeyPrefix() { return “aijar.patientdata.”; }

    public PatientDataDefinition getCPT(Integer num) { return getLastObsValueDuringQuarter(hivMetadata.getCPTDosage(),num, df.getObsDatetimeConverter()); }

    protected PatientDataDefinition getFirstObsValueDuringQuarter(Concept question, Integer periodToAdd, DataConverter converter) { Map<String, Object> map = new HashMap<String, Object>(); map.put(“includeEncounters”, false); map.put(“obsPeriod”, Period.QUARTERLY); map.put(“whichObs”, TimeQualifier.FIRST); return df.getObsValueDuringPeriod(question, periodToAdd, map, converter); }

    protected PatientDataDefinition getLastObsValueDuringQuarter(Concept question, Integer periodToAdd, DataConverter converter) { Map<String, Object> map = new HashMap<String, Object>(); map.put(“includeEncounters”, false); map.put(“obsPeriod”, Period.QUARTERLY); map.put(“whichObs”, TimeQualifier.LAST); return df.getObsValueDuringPeriod(question, periodToAdd, map, converter); } }

/**

  • Pre-ART Register */ @Component public class SetupPreARTRegister extends AijarDataExportManager {

    @Autowired private DataFactory df;

    @Autowired ARTClinicCohortDefinitionLibrary hivCohorts;

    @Autowired private CommonReportMetadata commonMetadata;

    @Autowired private HIVCohortDefinitionLibrary hivCohortDefinitionLibrary;

    @Autowired private HIVMetadata hivMetadata;

    @Autowired private BuiltInPatientDataLibrary builtInPatientData;

    @Autowired private HIVPatientDataLibrary hivPatientData;

    @Autowired private BasePatientDataLibrary basePatientData;

    @Override public String getExcelDesignUuid() { return “98e9202d-8c00-415f-9882-43917181f087”; }

    @Override public String getUuid() { return “9c85e206-c3cd-4dc1-b332-13f1d02f1cc2”; }

    @Override public String getName() { return “Pre-ART Register”; }

    @Override public String getDescription() { return “Pre-ART Register”; }

    @Override public List getParameters() { List l = new ArrayList(); l.add(df.getStartDateParameter()); return l; }

    @Override public List constructReportDesigns(ReportDefinition reportDefinition) { List l = new ArrayList(); l.add(buildReportDesign(reportDefinition)); return l; }

    @Override public ReportDesign buildReportDesign(ReportDefinition reportDefinition) { ReportDesign rd = createExcelTemplateDesign(getExcelDesignUuid(), reportDefinition, “FacilityPreARTRegister.xls”); Properties props = new Properties(); props.put(“repeatingSections”, “sheet:1,row:6,dataset:PRE_ART”); props.put(“sortWeight”, “5000”); rd.setProperties(props); return rd; }

    @Override public ReportDefinition constructReportDefinition() {

     ReportDefinition rd = new ReportDefinition();
     rd.setUuid(getUuid());
     rd.setName(getName());
     rd.setDescription(getDescription());
    

    // Want this parameters to be passed to final ObsForPersonInPeriodDataDefinition onDate rd.setParameters(getParameters());

     PatientDataSetDefinition dsd = new PatientDataSetDefinition();
     dsd.setName(getName());
     dsd.setParameters(getParameters());
    
     CohortDefinition everEnrolledCare = Cohorts.getPatientsWhoEnrolledInCareInYear();
     dsd.addRowFilter(Mapped.mapStraightThrough(everEnrolledCare));
     dsd.addSortCriteria("Date Enrolled", SortCriteria.SortDirection.ASC);
     dsd.addSortCriteria("Unique ID no", SortCriteria.SortDirection.ASC);
    
     addColumn(dsd, "Date Enrolled", hivPatientData.getEnrollmentDate());
     addColumn(dsd, "Unique ID no", hivPatientData.getClinicNumber());
    
    
     Map<String, Object> mappings = new HashMap<String, Object>();
    
     rd.addDataSetDefinition("PRE_ART", dsd, mappings);
    
     for (int i = 0; i <= 15; i++) {
     // This throws error that the date is null
         addColumn(dsd, "Fu Status " + i, hivPatientData.getCPT(i));
     }
     return rd;
    

    }

    @Override public String getVersion() { return “0.1”; } }

I need you help, how can I pass parameters from UI (report parameter) to custom ObsForPersonInPeriodDataDefinition so that date field is not null. Is there what I am not doing well.

@carapai, that’s a big chunk of code :slight_smile:

One clear problem is in these lines:

Mappings are what control what parameters are passed from a “parent” definition to a “child” definition. So, in order to successfully pass a startDate parameter from the reportDefinition to the “child” patientDataSetDefinition, you need to explicitly pass this through. If the parameters are the same in each, the simplest thing to do is to use the convenience method: Mapped.mapStraightThrough(T). In your case above, change that code to:

rd.addDataSetDefinition(“PRE_ART”, Mapped.mapStraightThrough(dsd));

Hope this helps…

Mike

Hello, Mike, I hope this email finds you fine and healthy. I appreciate all the help you have been giving me in relation to the reporting module. I am learning things related to the reporting module. I am now trying to learn the reporting rest api, but one thing I have failed to understand is how create corhortDefinitions, datasetDefinitions (generally posting definitions)

Please it would be nice to create tests or post the formats of the json that can be accepted by the reporting reporting rest endpoints.

Thank you and hope to hear from you soon Charles Olupot

@carapai, the reportingrest module does not support creating or updating definitions. It only supports listing them (e.g. at .../ws/rest/v1/reportingrest/dataSetDefinition), or evaluating them (e.g. at .../ws/rest/v1/reportingrest/dataSet).

Thank you. Was just thinking that since there is the post endpoint, this made it possible for creation and updating