KenyaEMR Add-on Module API

The goal of this API is to allow implementing organisations to customise KenyaEMR by providing additional content such as forms and reports etc.

This API is still in its early stages and is subject to change. We strongly recommend that potential add-on module developers join the KenyaEMR mailing list to be notified about changes and also provide input.

What is an add-on module?

An add-on module is a regular OpenMRS module that provides content to KenyaEMR. Such a module should require KenyaEMR so ensure that you specify this in the add-on module's config.xml:

<require_modules>
  <require_module version="13.3">org.openmrs.module.kenyaemr</require_module>
</require_modules>

Next we need to add kenyaemr-api and it's dependencies to the add-on module's main pom.xml. There are too many to list here so take a look at this POM for the example add-on module

<dependency>
  <groupId>org.openmrs.module</groupId>
  <artifactId>kenyaemr-api</artifactId>
  <version>13.3</version>
  <scope>provided</scope>
</dependency>

To integrate with the UI of KenyaEMR, add-on modules should provide a UI Framework configuration in their webModuleApplicationContext.xml file, e.g.

<bean class="org.openmrs.ui.framework.StandardModuleUiConfiguration">
  <property name="moduleId" value="keaddonexample"/>
</bean>

Checkout the code for this example add-on module.

What content can be added?

An add-on module can add the following content to Kenya EMR:

Apps

KenyaEMR uses the OpenMRS App Framework module to manage apps. This means that add-on modules can add new apps by simply defining app descriptor beans in their moduleApplicationContext.xml file, e.g. 

 
<bean id="keaddonexample.app.example"  factory-bean="kenyacore.appFactoryBean" factory-method="createInstance">
		<constructor-arg value="keaddonexample.example" />
		<constructor-arg value="Example" />
		<constructor-arg value="keaddonexample/example/home.page" />
		<constructor-arg value="keaddonexample:apps/example.png" />
		<constructor-arg value="700" />
	</bean>

An add-on module can define as many new apps as it wants and these will all appear on the KenyaEMR home page. The icon URL must be in format provider:path where provider is UI Framework resource provider and path is the path of the image resource.

In KenyaEMR 13.2, page controllers must use AppUiUtils.startApp(...) to signify which app they belong to. That class is due to removed from the App Framework module and so in 13.3 page controllers will use new @AppPage and @SharedPage annotations to show which app they belong to. These annotations will check that the current user has the required privilege to access the specified app.

Metadata

KenyaEMR uses the Metadata Deploy module to facilitate the installation of metadata. Add-on modules can provide their own metadata bundles and these will be automatically installed during distribution startup.

To create a bundle, add metadatadeploy as a requirement of your add-on module and then define a component class which extends AbstractMetadataBundle, e.g.

@Component
public class ExampleMetadata extends AbstractMetadataBundle {
	public static class _EncounterType {
		public static final String EXAMPLE = "d69dedbd-3933-4e44-8292-bea939ce980a";
	}

	public static class _Form {
		public static final String EXAMPLE = "b694b1bc-2086-47dd-a4ad-ba48f9471e4b";
	}

	@Override
	public void install() {
		install(encounterType("Example encounter", "Just an example", _EncounterType.EXAMPLE));
		install(form("Example form", null, _EncounterType.EXAMPLE, "1", _Form.EXAMPLE));
	}
}

Bundles can also install metadata sharing packages, though this not recommended as package installation can be slow. You can see an example of this here

Forms

Once you've added a form via a metadata bundle, you can integrate it into KenyaEMR by describing it with a form descriptor. Add something like this to your moduleApplicationContext.xml, e.g.

Describing a form with a form descriptor
<bean class="org.openmrs.module.kenyacore.form.FormDescriptor">
  <property name="targetUuid" value="b694b1bc-2086-47dd-a4ad-ba48f9471e4b" />
  <property name="apps">
    <set>
      <ref bean="kenyaemr.app.clinician" />
      <ref bean="kenyaemr.app.chart" />
    </set>
  </property>
  <property name="icon" value="keaddonexample:forms/example.png" />
  <property name="htmlform" value="keaddonexample:htmlforms/example.xml" />
  <property name="order" value="3001" />
</bean>

The property values should be as follows:

  • targetUuid is the UUID of the form object - NOT the htmlform
  • apps specifies the set of apps that will include this form. These are references to AppDescriptor beans in the application context.
  • icon is the path to the icon for this form (optional)
  • htmlform is the path to the form XML file (optional)
  • order is a number used for ordering the forms (optional)

If you specify htmlform then KenyaEMR will load the form content as a UI Framework resource from the specified XML file. Otherwise, KenyaEMR will try to load it from the database. 

If you want to use the form as a general visit form, add it to a new FormConfiguration like this

Adding a form as a general visit form
<bean id="keaddonexample.config.form" class="org.openmrs.module.kenyacore.form.FormConfiguration">
	<property name="generalVisitForms">
		<set>
			<ref bean="keaddonexample.form.example" />
		</set>
	</property>
</bean>

If you want to use the form as a visit form for an existing program in KenyaEMR, add it with a reference to that program;

Adding the form to the existing HIV program
<bean id="keaddonexample.config.form" class="org.openmrs.module.kenyacore.form.FormConfiguration">
	<property name="programVisitForms">
		<map>
			<entry key-ref="kenyaemr.program.hiv">
				<set><ref bean="keaddonexample.form.example" /></set>
			</entry>
		</map>
	</property>
</bean>

If you want to add the form to a new program, you have to first describe that new program...

Programs

Programs are described with program descriptor beans.

Describing a program with a program descriptor
<bean id="keaddonexample.program.example" class="org.openmrs.module.kenyacore.program.ProgramDescriptor">
	<property name="targetUuid" value="dfdc6d40-2f2f-463d-ba90-cc97350441a8" />
	<property name="eligibilityCalculation" value="org.openmrs.module.keaddonexample.calculation.library.example.EligibleForExampleProgramCalculation" />
	<property name="defaultEnrollmentForm" ref="keaddonexample.form.exampleEnrollment" />
	<property name="defaultCompletionForm" ref="keaddonexample.form.exampleCompletion" />
	<property name="visitForms">
		<set>
			<ref bean="keaddonexample.form.example" />
		</set>
	</property>
	<property name="fragments">
		<map>
			<entry key="enrollment-summary" value="keaddonexample:program/example/exampleEnrollmentSummary" />
			<entry key="care-panel" value="keaddonexample:program/example/exampleCarePanel" />
			<entry key="completion-summary" value="keaddonexample:program/example/exampleCompletionSummary" />
		</map>
	</property>
	<property name="order" value="900" />
</bean>

The property values should be as follows:

  • targetUuid is a reference to the actual OpenMRS program object
  • eligibilityCalculation is calculation which will determine if a given patient is eligible to be enrolled in this program
  • defaultEnrollmentForm is the default form for enrollment
  • defaultCompletionForm is the default form for discontinuation
  • visitForms are the forms which will be included in the "Available Forms" UI component which providers see
  • fragments are the UI Framework components which are used to render program specific content

Flags

Patient flags are calculation objects which are evaluated for the patient being viewed. To tell KenyaEMR that a calculation should be used as a patient flag make it implement FlagCalculation, e.g.

public class ExampleFlagCalculation extends BaseEmrCalculation implements FlagCalculation {
  @Override
  public String getFlagMessage() {
    return "Example";
  }
  @Override
  public CalculationResultMap evaluate(Collection<Integer> cohort, Map<String, Object> params, PatientCalculationContext context) {
    ...
  }
}

The calculation should return a true value for any patient who the alert should be displayed for.

Identifiers

Identifier types are described with identifier descriptor beans.

<bean id="keaddonexample.identifier.example" class="org.openmrs.module.kenyacore.identifier.IdentifierDescriptor">
  <property name="targetUuid" value="2086081F-BD0F-4AFE-83BA-61A8A383F9F6" />
</bean>

Reports

To define your own report first create a report descriptor to describe how the report will be used in the EMR, e.g.

<bean id="keaddonexample.report.example" class="org.openmrs.module.kenyacore.report.IndicatorReportDescriptor">
  <property name="targetUuid" value="925FA2EF-000F-436B-B81E-3BABDE687972" />
  <property name="name" value="Example Report" />
  <property name="description" value="Just an example" />
  <property name="apps"><set><ref bean="kenyaemr.app.reports" /></set></property>
</bean>

Then create a builder component to construct the ReportDefinition during distribution startup, e.g.

@Component
@Builds("keaddonexample.report.example")
public class ExampleReport implements ReportBuilder {
  @Override
  public ReportDefinition build(ReportDescriptor report) {..}
}

Regimens

This area of functionality is expected to change significantly in the 14.1 release of KenyaEMR

Lab tests

This area of functionality is expected to change significantly in the 14.1 release of KenyaEMR

Chores

A chore is a task which is run once during a startup of KenyaEMR. Typically a module will use a liquibase changeset to make one-time low-level changes to databases. Chores provide a mechanism to make one-time changes in Java at the end of the KenyaEMR startup process. This means a chore has access to all of the content used by KenyaEMR.

Example chore
@Component("kenyaemr.chore.example1")
public class ExampleChore extends AbstractChore {
	
	@Override
	public void perform(PrintWriter output) throws Exception {
		// Perform the chore!
	}
}

The name of the chore component is used as a global property so ensure that it doesn't contain punctuation except spaces