Core Reporting Framework


Reporting Data Model

The OpenMRS reporting data model, version 1.0β. Click image for full size image

org.openmrs.reporting

ReportContext <<class>>

The report context will be used to hold parameters accessed at report generation.


ReportDesign <<interface>>

The report definition is the main interface for the report API. All reports will need to have a report definition. There will likely be a BaseReportDefintion that can be used by all reporting frameworks as the basis for all report operation. The reporting frameworks can also implement the ReportDefinition interface if they need to override or introduce new elements to a report.

  • String getName();
  • String getDescription();
  • String getType();
  • ReportDefinitionFile getReportDefintionFile(); // report definition xml (applies to jasper, birt, and jfreereport)
  • Map<String, DataSet> getDataSets();
  • List<IParameter> getParameters()
  • Set<IReportDefinition> getSubReports();
  • Set<IReportElement> getElements();

ReportDesignFile <<interface>>

The report design file is an interface that defines the way in which a report is persisted as a file. Currently, the report API only really needs to know the XML of the report design, but we decided that we may need to know the MimeType when downloading the design file (sometimes we may want to instruct the browser to open the file in a specific application).

  • String getXml();
  • String getMimeType();

ReportElement <<interface>>

Report Elements represent discrete elements (such as graphs, tables, charts) that are found within a report. This is required because elements in a report can have different data sets.

  • DataSet getDataSet();

Parameter <<interface>>

Describes parameters that can be used within a report, cohort, or report element to constrain the data used within that report.

  • String getName();
  • String getDescription();
  • Object getValue(); // Should this be an object
  • Class getValueClass(); // java.lang.Integer
  • Class getMappedClass(); // org.openmrs.Concept
  • String getDefaultValue();
  • String getLabel(); // calls Context.getLocale()
  • String getLabel(Locale); //

DataSource <<interface>>

  • TBD

DataSet <<interface>>

  • ColumnSet getColumnSet(); // i.e. tokens, concepts, data columns, etc
  • RowSet getRowSet(); // a set of rows

ColumnSet <<interface>>

  • Set<Column> getColumns();

RowSet <<interface>>

  • Set<Integer> getIds();

Implementations: PatientRowSet, EncounterRowSet

Column <<interface>>

  • String getName()
  • String getTemplateText()

Implementation: DataColumn, TokenColumn, ConceptColumn, CalculatedColumn

org.openmrs.reporting.engine

ReportService <<interface>>

Interface that defines the methods that the core reporting service needs to implement. Notice that these methods have nothing to do with the underlying reporting engine. All of that is handled within the connector framework.

  • IReportDefinition getReport(Integer id);
  • Set<IReportDefinition> getReports();
  • importReport(IReportDefinition rd);
  • exportReport(IReportDefinition rd);
  • createReport(IReportDefinition rd);
  • updateReport(IReportDefinition rd);
  • deleteReport(IReportDefinition rd);

ReportServiceImpl

Simple implementation of the IReportService interface. Provides CRUD and import/export.

ReportEngineManager

Manages the report engine connectors and picks the correct connector for a given report definition.

  • void register(IReportEngineConnector connector);
  • void unregister(IReportEngineConnector connector);
  • List<IReportEngineConnector> getConnectors();
  • IReportEngineConnector getConnector(Integer id);
  • IReportEngineConnector getConnectorFor(ReportDefinition rd);

ReportEngineDescriptor

Metadata about the report engine connector.

  • String name;
  • String description;
  • String fileExtension;
  • String mimeType;
  • String connectorClassName;
  • String version;

ReportEngineConnector <<interface>>

Interface that defines the contract that the reporting engines must adhere to

  • supportsReport(ReportDefinition, String outputFormat);
  • generateReport(ReportDefinition, String outputFormat);
  • IReportDefinition createDefaultReportDefinition();
  • boolean supportsMultipleDatasource();
  • boolean supportsSubreports();
  • boolean supportsParameters();
  • String getConnectorVersion();
  • String getEngineVersion();

Implementations: BirtReportEngineConnector, JasperReportEngineConnector, JFreeReportEngineConnector

org.openmrs.reporting.export

Needs a lot more work ...

IDataExport

IDataSet getDataSet();


IDataExportService

  • void generateExport(IDataSet dataset);
  • DataExport getDataExport(Integer id); // get an in-memory version of the CSV


Discussion Questions

Question 1

Should the DataSet interface not be a child of an 'IDataSource' type class which can then also be the parent other classes of data source (such as JDBC, EJB, POJO, Hibernate, MDX, and XML).

Yes, I think that's a great idea. I want to mimic the reporting frameworks as much as possible and BIRT definitely represents a Data Source in it's object model. However, it didn't fit into OpenMRS reporting API at the time I was writing the BIRT report module and I didn't want to complicate matters by including another class/interface that added little or no value. What are some of the attributes that you could see going into the Data Source. BIRT designed data sources to be relatively open -- a data source has general properties that are specified within the BIRT ODA UI plugin and validated by the ODA driver's runtime plugin (as far as I know).

For example:

   <data-sources>
       <oda-data-source extensionID="org.eclipse.datatools.connectivity.oda.flatfile" name="Data Source" id="4">
           <property name="HOME">C:\Application Data\OpenMRS\reports\datasets</property>
           <property name="CHARSET">UTF-8</property>
           <property name="INCLTYPELINE">NO</property>
       </oda-data-source>
   </data-sources>

Question 2

In your IParameter class I don't see a way of telling what the type of the parameter is. i.e. String, Integer, Boolean, Date etc.

That was an oversight on my part. Thanks for pointing that out. I like the idea of distinguishing between the value and mapped class. In the case where you would use a mapped class, you would also specify a value class for the actually integer or string foreign key value, right?

Do we need to make room for format (for instance, date or floating point format) for parameters, as well as for dataset columns?

Question 3

How do you see the IReportElement class being used?

Report Elements are elements, such as graphs, charts, etc that are included within a report and may have their own data source / data set associated with them. I think JasperReport would just use a sub-report to include multiple elements within the same report. Can you include two sub-reports on the same page?

I want to have a place for them in our API in case we go down the route of providing a Report Designer within OpenMRS.

Question 4

In fact I would love to see a snippet of code that uses this API, do you have such a snippet I could look at?

I don't have any code yet, but will let you take a peek once I get something in place. I'll flesh out the Reporting API to show some code samples.