How to use the Reportingrest Module

My team is working on the new cohort builder and we need a way to make some queries using the reporting module via the reportingrest module. However, we’ve not found any documentation that will aid us. Let me explain in details what we’re trying to do. There is a patient attributes tab with a form that can be used to search patients based on gender, age/date of birth and whether the patient is alive or not. Below is an image of the form

We’ve gone through all the endpoints associated with patients in the webservices module and we saw the GET /patient (Search for patients). Below is the image showing how to query the endpoint. However, the issue we’re having with this is that we can’t specify the attributes we want to search with and we can’t see those attributes here. I tried using the “q” query parameter but it only returned patients whose names contain the value specified in “q”.

So the webservices endpoints can’t help us. Essentially all we want is to be able to send those form fields value as queries to the endpoints. Has anyone done this before or any idea on how to go about it?

@femi, looking for documentation on the reportingrest module puts you on the right track. Unfortunately, I don’t think much documentation exists. @darius is the one who knows the most about these resources, but he is not fully available at the moment, so I’ll try to weigh in quickly.

At a high-level, evaluating Cohort Definitions using the reporting module API is the approach you need to take, not querying the main OpenMRS API. The reasons for this are:

  • This is one of the primary use cases that it was written for
  • It is built for efficiency and speed for finding Cohorts
  • it already has built in searches for most of the use cases that you are trying to support.
  • it has built in support for creating compositions of searches (eg. 1 AND 2 OR (3 AND NOT 4)

So, before getting into how to access this API over REST, it is important to understand what these resources are accessing behind the scenes in the reporting module API, because you will need to configure your searches to support that.

Taking the Demographic form you show above, I can see this supported by a few different existing Cohort Definitions:

For this UI, you might just intersect (AND) the results of all of these queries together. If and when you want to have more complex composition logic, you would use a CompositionCohortDefinition.

Now, the easiest thing to do if you are trying to write a UI that has a pre-specified selection of Cohort Queries available (and if you have the requisite Java/OpenMRS knowledge to do so) would be to expose your own Web Services that you can hit, and simply use the reporting module classes in your API implementation to produce your Cohorts.

If you are limited to only developing in the UI and writing Javascript to hit existing resources, then the main one you need to hit is the “reportingrest/cohort” resource, found here.

@darius did some initial work demonstrating how to post “serializedXml” to this resource, which represents the configured Cohort Definition that you wish to evaluate. I’m not sure if that is going to be the best approach to use here or not. A lot of this will depend on whether or not we can write any convenience server-side classes for this new cohort builder tool, or if everything needs to live within an OWA.

I know this answer will only get you so far, but hopefully it gets the conversation moving and we can progress from here.

Mike

(quick answer)

In addition to what Mike said about Cohort Definitions in the reporting module, it also supports the idea of “definition libraries” which we can use to preconfigure the combinations of queries that we need for the cohort builder.

Look for the class BuiltInCohortDefinitionLibrary in the reporting module for an example of how this works.

I was thinking:

(1) we should define any of the queries we need for the cohort builder as a definition library within the reporting module (so cohort builder can just be an OWA)

(2) if there is not yet an easy to use endpoint in reporting rest that lets you evaluate a query from a definition library easily, we should add this.

-Darius (by phone)

The current cohort resource supports all existing definition libraries. The only problem with it is that, for performance purposes, it does not load the full patient. As a result, the display property is blank, and yet the cohort builder needs to display the patient name and identifier. With this ticket, https://issues.openmrs.org/browse/CB-43 i have created a cohortbuilder resource to load the full patient. Secondly the existing cohort resource does not deal with compositions of various searches (eg. 1 AND 2 OR (3 AND NOT 4). Hence the second reason for creating the new cohortbuilder resource, which will cater for this.

@darius and @mseaton does this look like the correct strategy?

@femi @fortune and the rest of the team, i have attached a reportingrest compiled module on this ticket CB-43 It does not have all the end points we shall need, but it unblocks you by letting you get started with the existing built in definition libraries.

For instance, to get males, you can use this rest call: http://localhost:8080/openmrs/ws/rest/v1/reportingrest/cohortbuilder/males

To get females, you can use: http://localhost:8080/openmrs/ws/rest/v1/reportingrest/cohortbuilder/females

To get patients within a certain age range effective from today, you can use: http://localhost:8080/openmrs/ws/rest/v1/reportingrest/cohortbuilder/ageRangeOnDate?effectiveDate=2017-03-14&minAge=0&maxAge=20

You can see more built in libraries to try out at: https://github.com/openmrs/openmrs-module-reporting/blob/master/api/src/main/java/org/openmrs/module/reporting/cohort/definition/library/BuiltInCohortDefinitionLibrary.java

As i said, we are going to be adding more built in dataset definitions and also look for a strategy of making requests that are combining them into various compositions.

1 Like

Thanks @dkayiwa.

I’ll let @darius weigh in when he can, but my initial reaction is that this is not what we would want. There is a reason that we do not retrieve the full patient in the web service. As you acknowledge, this is because of the performance penalty. This performance penalty is no more acceptable in the cohort builder than anywhere else, and maybe even less so.

I personally have always viewed the list of patients and some of their details as a nice-to-have during the query process, but by no means the essential requirement of the tool. I would be perfectly happy having this list able to be generated on demand when desired (and for the end user to be able to customize what is shown via Data Definitions), then to add this massive performance hit. If we must have this, I would also think that it is perfectly sufficient for this to be accomplished via one or more separate REST calls to get the patient details for a given patient uuid.

As for not being able to handle compositions easily, that’s not entirely accurate. A composition cohort definition is just another type of cohort definition, and can be evaluated by passing in the more complex “serializedXml” format if needed. If we can come up with a better way of exposing a REST service for evaluating a composition, I would rather we have this be an explicit resource, that is available not just in “cohortbuilder”, and call it “compositionCohort” or something. In general, I don’t really see the value in having a “cohortbuilder” resource, unless there is something very specific to this tool that we don’t think is otherwise generally useful.

Mike

Thanks @mseaton for the very fast response! :smile:

What do you suggest that we could have as a replacement for the current cohort builder which lists patient names for returned results? Should we display just the uuid?

Do you mind giving an example of serializedXml for a query like: “Males who are between 5 and 10 years of age that are Alive”?

@dkayiwa, to get that serialized xml, you’d need to either construct it in code (create such a cohort query and serialize it with the reportingserializer and spit this out to a file), or use the UI to build it and then select the value out of the serialized_object table in the DB. It’s not a great solution, and not what we are hoping for long-term, but it was basically worked as-is, and @darius needed something quick that would meet his needs recently, so he added support for it. So I’m not really saying it’s optimal, just that is is what we currently have. And if we build a replacement, I would want one that we could use generally take advantage of, not just via cohort builder.

For the displayed results, what I would recommend generally is that we use the reporting module for this too. eg. Have a default row-per-patient dataset that is evaluated to produce the patient results, and set the baseCohort for this data set to the set of patients that you want to retrieve information for at a given time (using paging so you don’t load millions of patients at a time).

By default, we would have this data set configured with the columns that match the current cohort builder behavior. But the nice thing about this approach is that end users could opt to use an alternative row-per-patent data set, which would allow them to fully customize what is displayed on this view (eg. certain identifiers, person attributes, name configuration, whatever).

Let me know what you think and if this makes sense…

Mike

Example of a composition:

+1 to Mike’s points:

(a) initial rest call that evaluates the query needs to be fast and light and should only return the list of patients; fetch more details about those patients, page by page, in a follow-up rest call.

(b) use a row per patient dsd in the reporting module to get those details.

© let’s not introduce a query endpoint or library called cohort builder, because the queries are generally useful, and not really specific to cohort builder.

-Darius (by phone)

Hi, thanks so much for your replies, We’re still a little confused but at least we’ve been able to do some things. I studied what @darius wrote about serializedXml and I was able to programmatically generate a serializedXml to get all males. <org.openmrs.module.reporting.cohort.definition.DefinitionLibraryCohortDefinition><definitionKey>reporting.library.cohortDefinition.builtIn.males</definitionKey></org.openmrs.module.reporting.cohort.definition.DefinitionLibraryCohortDefinition> I then made a POST to http://localhost:8080/openmrs-standalone/ws/rest/v1/reportingrest/cohort and I got a response like this

And I decided to just display some basic details it on the cohort builder and it looks like this (I know it’s not looking good for now)

I could get the patient’s UUID & the link to get full details about the patient. I saw one of @mseaton post about fetching patient details per page, does that mean if we’re displaying 10 patients on a page, we only get the full details of those 10 patients on the page.

We still don’t fully understand how to combine two search parameters, e.g age and gender. I’m still trying to wrap my head around @darius last post about composition.

Thanks for your support

@darius, @mseaton & @dkayiwa Please can I get some sort of insight from you about my last post. I just need like a sample of a serializedxml that combines male with age for a search. We should be able to figure out how to combine other search parameters as need arises. Also I want to know about how we intend to display the names of the patients. Are we going to make separate api calls to retrieve patients information per page?

Thanks

Femi, I believe the link I sent does show an example of a composition.

The relevant Java class from reporting should be CompositionCohortDefinition. If you don’t find it in the link I sent them search for that classname on GitHub and find an example there.

-Darius (by phone)

@femi specifically, this is a composition: https://github.com/djazayeri/openmrs-owa-managerdashboard/blob/master/js/query-constants.js#L38

Thanks @darius we tried using the composition you sent in your last post. We discovered that there were some concatenation, so we ran the code and malesWithEncounterDuringPeriod looked like this <org.openmrs.module.reporting.cohort.definition.CompositionCohortDefinition><parameters> <org.openmrs.module.reporting.evaluation.parameter.Parameter> <name>startDate</name> <label></label> <type>java.util.Date</type> <required>true</required> </org.openmrs.module.reporting.evaluation.parameter.Parameter> <org.openmrs.module.reporting.evaluation.parameter.Parameter> <name>endDate</name> <label></label> <type>java.util.Date</type> <required>true</required> </org.openmrs.module.reporting.evaluation.parameter.Parameter> </parameters><compositionString>male AND hasEncounter</compositionString><searches> <entry> <string>male</string> <org.openmrs.module.reporting.evaluation.parameter.Mapped> <parameterizable class="org.openmrs.module.reporting.cohort.definition.DefinitionLibraryCohortDefinition"> <definitionKey>reporting.library.cohortDefinition.builtIn.males</definitionKey> </parameterizable> <parameterMappings/> </org.openmrs.module.reporting.evaluation.parameter.Mapped> </entry> <entry> <string>hasEncounter</string> <org.openmrs.module.reporting.evaluation.parameter.Mapped> <parameterizable class="org.openmrs.module.reporting.cohort.definition.DefinitionLibraryCohortDefinition"> <definitionKey>reporting.library.cohortDefinition.builtIn.anyEncounterDuringPeriod</definitionKey><parameters> <org.openmrs.module.reporting.evaluation.parameter.Parameter> <name>startDate</name> <label></label> <type>java.util.Date</type> <required>true</required> </org.openmrs.module.reporting.evaluation.parameter.Parameter> <org.openmrs.module.reporting.evaluation.parameter.Parameter> <name>endDate</name> <label></label> <type>java.util.Date</type> <required>true</required> </org.openmrs.module.reporting.evaluation.parameter.Parameter> </parameters> </parameterizable> <parameterMappings> <entry> <string>startDate</string> <string>1980-01-01</string> </entry> <entry> <string>endDate</string> <string>2017-01-01</string> </entry> </parameterMappings> </org.openmrs.module.reporting.evaluation.parameter.Mapped> </entry></searches></org.openmrs.module.reporting.cohort.definition.CompositionCohortDefinition>

We posted this as the serializedXml value to the /cohort resource but we got this error "message": "[Invalid submitted cohort definition]"

Is there something we’re missing? Thanks

I can’t tell from looking at that huge block of XML what the problem is. :slight_smile:

Look at the part of the error message that says SAXParseException and then something about parameterizableclass. I don’t see that in the text, but it must be coming from somewhere…

In case anyone stumbles upon this thread in the future because of its title, the Reporting REST API is now documented at: https://github.com/openmrs/openmrs-module-reportingrest/wiki