Reporting Framework
Contents |
The Data Model
ReportSchema
First off, we've fleshed out our definition of ReportSchema. This will be a core part of OpenMRS, and will define the limits on what can easily be passed to external applications. So, if you're reading this and you notice a weakness here, please comment!
ReportSchema = String name String description ReportType (whether the report accepts an on-the-fly cohort as input, or defines its own) Cohort definedCohort List<Parameter> parameters List<DataSetDefinition> dataSets List<ReportSchema> subReports (not yet thought out completely)
Parameter
Parameter = a value that may be requested of the user when the report is run, or else calculated from other parameters
Data Sets
DataSetDefinition
DataSetDefinition = Interface representing a list of column headings
DataSet
DataSet = Interface representing a two-dimensional data table
DataSetProvider
There will be multiple DataSetProviders that define concrete implementations of DataSetDefinition and DataSet, and provide the code to generate a DataSet from a DataSetDefinition. We will be implementing several different types of data sets in the OpenMRS core, and we expect modules to define others. In the immediate future we will focus on implementing CohortDataSet, as this will get us the most reports quickly for the least effort.
LogicDataSet
LogicDataSet = This will be the successor to the one-row-per-patient data export tool, but more powerful. You’ll specify columns as a Token, a LogicCriteria, and Parameters. This will allow you to access various Logic Data Sources (Obs, Program enrollment, Drug Order, etc), as well as Rules which are calculated upon those data sources. See Logic Service for details. [This data set needs to also support one-row-per-patient-program-enrollment. Burke, hopefully you can confirm that this will be possible.]
ObsDataSet + EncounterDataSet
ObsDataSet + EncounterDataSet = We will also implement row-per-Obs and row-per-Encounter data sets in the core.
CohortDataSet
CohortDataSet = This will let you quickly build standard government- and funding agency-style reports. Each column in this type of data set consists of a name and a cohort definition. For example the name could be “Male adults who enrolled in the HIV Program during the period†(or “1.bâ€) and the definition could be a saved patient search from the cohort builder that represents patients in the HIV program whose enrollment date is between ${report.startDate} and ${report.endDate}. Evaluating one of these data sets would result in set of key-value pairs, where the key is a name, and the value is the actual patient cohort meeting that criteria:
1.a -> [1, 2, 3, 5, 7, 12] 1.b -> [4, 6, 15, 16] 1.c -> [1, 2, 4] ...
Most Report Renderers would merely display the cohort as a number, but one built with the knowledge of the OpenMRS reporting data model could display this in a more interactive way.
Report Renderers
Report Renderer = An OpenMRS component or a link to an external application that can take report data, and render it in some format that the end-user wants. The OpenMRS core will be able to render single-data-set reports to CSV or TSV, and multi-data-set reports to XLS as a plain data dump. More sophisticated report-rendering will be done by modules, such as Justin’s BIRT Reporting Module, which will be ported over to the new framework. This interface will also allow developers to write some modules that make it simple to define report layout (e.g. via variable substitution like @1.a@ in Excel via POI, or PDF via XSL-FO.)
The User Interface
There are two different types of report: the kind that you can run on a group of patients that you’ve arrived at on-the-fly (e.g. print me summaries for these 12 patients), and the kind that define their own cohort (e.g. the PEPFAR report for Rwinkwavu for September 2007). A ReportSchema defines which type of report it is. The first type will be accessed from a link on the Cohort Builder, while the second type will be accessed from a “Run a Report†link in the Administration section. (After UI redesign happens, this will go somewhere more intuitive.)
In either case, the user will pick a report from those available to be run in that context. If the report or any of its sub-elements require user-provided parameters, then the user will be prompted for those. In addition the user will pick the output format, from all renderers capable of rendering the chosen report. (Generally there will be multiple renderers that can render a report. Those with specific knowledge of the chosen report will be the preferred options, whereas generic renderers like "CSV" will appear at the bottom of the select list.)
Once all required parameters are correctly provided, the report will run.
The End-to-End Flow of Control
- User picks a report from the UI (either from Cohort Builder or from the Run Reports page.)
- ReportService determines whether any parameters are needed from the user
- If parameters are required, the UI queries the user, and validates the user's entry
- ReportService queries all registered ReportRenderers to see whether any of them can render the chosen report, and whether they’re the preferred renderer for that report.
- If multiple renderers are available, ask the user which one to use.
- If no renderers can render the report, throw an exception
- ReportService produces the report data, e.g. it takes a ReportSchema and produces ReportData
- Evaluate the input cohort definition, if any
- For each DataSetDefinition, call DataSetService.evaluate(DataSetDefinition, inputCohort)
- DataSetService will delegate that call to the registered DataSetProvider
- Package all DataSets, user-entered, and calculated parameters into a ReportData object
- Report generating servlet asks the chosen ReportRenderer what content type it will return
- Report generating servlet calls render(reportData, servletOutputStream) on the chosen renderer.
- User happily receives a PDF/HTML/XLS/CSV/??? Report.
