Skip to content
Custom EMR application for Mirebalais Hospital
Java Groovy Shell Other
Branch: master
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
.tx Add file for Spanish translations Jul 17, 2018
api change task name of Lab Results form task to "Add Lab Results" Aug 23, 2019
ci [maven-release-plugin] prepare for next development iteration Mar 11, 2013
distro UHM-3975: Remove Logic Module from OpenMRS distro Aug 12, 2019
docker/docker-compose Fix docker-compose/ Jun 26, 2019
omod SL-71 Added Card/Label printing for SL BUT this breaks HUM. I don't h… Jul 2, 2019
.gitignore moved gem install to .rubygems directory Feb 9, 2015 Add CES documentation (#32) Mar 4, 2019 Update Jun 18, 2019 update pihemr deploy to include -U Jul 13, 2017
pom.xml UHM-3975: Remove Logic Module from OpenMRS distro Aug 12, 2019

Overview of the PIH-EMR

The PIH-EMR is a distribution of OpenMRS, an open-source medical record systems

A few links from the OpenMRS wiki that are worth reading:

Therep are 40+ OpenMRS modules that make up the PIH EMR, of which most are OpenMRS community modules, but we also have several PIH-specific modules as well. Of those there are four top-level modules that provide most of the PIH-specific configuration:

These modules do several things, but their main tasks are:

(Due to the organic way the PIH EMR developed, it's there's not always clear guidelines as to the distribution of functionality between the the four modules, but generally Mirebalais metadata provides concepts, Mirebalais reports provides reports, and Mirebalais and PIH Core provide other metadata and configuration. The mirebalais module runs at the top of stack. For reference, "Mirebalais" refers to Hopital Universitaire Mirebalais where the PIH EMR was first installed.)

For deploying the PIH-EMR to our various staging and production servers, we use Puppet (

Our Puppet configuration scripts can be found here:

Sites Supported

This repository/implementation supports several different sites.

  • Haiti

    • Mirebalais Hospital / CDI configuration
    • Default Health Center configuration
    • Mental Health laptop configuration
    • HIV cloud system (coming soon!)
  • Liberia

    • Pleebo Health Center
    • JJ Dossen Health Center
  • Sierra Leone

    • Wellbody Health Center
  • Mexico

Communications and management

There are a few tools that we use extensively and that all PIH devs should have set up:

Please install Telegram on your development machine and then ask an existing developer to invite you to the appropriate groups.

Please request an account by asking another PIH developer or emailing

Setting up a Dev Environment

The preferred method to set up a development environment is using the OpenMRS SDK, with some custom configuration steps.


First, install git, mvn, and the OpenMRS SDK by following the "Installation" instructions here:

The OpenMRS SDK uses the H2 database by default, but H2 doesn't work with some of the modules we use, so we must use MySQL, which needs to be configured separately. To do this, you can either install MySQL directly on your machine, or install mysql within a docker container.

If installing directly, install MySQL Community Server 5.6 following the instructions for your platform. It must be version 5.6, other versions will not work.

An easier approach is likely to install Docker ( and use the OpenMRS SDK to set up an instance of MySQL within a docker container.


Step 1: Clone the "mirebalais-puppet" project

The various configuration files that determine what applications and options are turned on on different servers are found here and later on in process you will need to tell OpenMRS where to find them.

$ git clone

Step 2: (If you are directly installing MySQL on your machine) Ensure that MySQL has a password set up for the root user.

  • If you are able to run $ mysql -u root and access the MySQL Monitor without receiving an access denied error, it means that there is no root password set and you have to set it following the instructions here:
  • Once the root password has been set, you should be able to access the MySQL Monitor by running:
    $ mysql -u root -p followed by entering the password when prompted.

Step 3: Set up the environment

Set up the environment via the following command, chhosing the serverId and dbName you want to use. Specify the DB password for your root user as set in Step 2.

The Application Data Directory will be set up at ~/openmrs/[serverId].

The convention for dbNames are "openmrs_[some name]".

$ mvn openmrs-sdk:setup -DserverId=[serverId] -Ddistro=org.openmrs.module:mirebalais:1.2-SNAPSHOT
  • When prompted, select the port you'd like to run tomcat on

  • When prompted, set the port to debug on (standard is 1044)

  • Select which database you'd like to use

  • If you are connecting to a MySQL 5.6 instance running on your local machine, specifc the URI, and a username and password to connect to the DB

  • Select the JDK to use

Step 4: Link the configuration directory into the application data directory

Some sites have a configuration directory under mirebalais-puppet/mirebalais-modules/openmrs/files/app-data-config. To use one, symlink it in to you application data directory with

cd ~/openmrs/[serverId]  # cd into the application data directory
ln -s $(realpath ~/path/to/mirebalais-puppet/mirebalais-modules/openmrs/files/app-data-config/[your-site]/configuration) .

On Mac OS

cd ~/openmrs/[serverId]  # cd into the application data directory
ln -s /path/to/mirebalais-puppet/mirebalais-modules/openmrs/files/app-data-config/[your-site]/configuration .

You should then have a symlinked directory at ~/openmrs/[serverId]/configuration.

Step 5: Start up the server

$ mvn openmrs-sdk:run -DserverId=[serverId]

It should run for several minutes, setting up the database, (you may have to go to http://localhost:8080/openmrs to trigger this) BUT, in the end, it will fail. You should cancel the current run (Ctrl-C in the terminal window).

After it fails, notice that a file should have been created in the ~/openmrs/[serverId]

You will need to add two lines to these file, one specifying which of our configs to use for this server, and another referencing the location of the config files (which you checked out as part of the mirebalais-puppet project above). For instance, if you want to set up the Mirebalais CI environment, and you checked out the mirebalais puppet project into your home directory, add the following into the runtime properties:

  • pih.config=mirebalais,mirebalais-humci
  • pih.config.dir=/[path to]/mirebalais-puppet/mirebalais-modules/openmrs/files/config

Then rerun:

$ mvn openmrs-sdk:run -DserverId=[serverId]

Startup should take several minutes as it loads in all required metadata, etc, for the first time.

Step 6: Create a local identifier source

After startup, login

  • Enter "http://localhost:8080/openmrs/login.htm" into the Chrome web browser
    • Log in with the following details:
      • Username: admin
      • Password: Admin123
    • (The password is the default password, it is referenced in the file within the ~/openmrs/[serverId] folder)
  • Enter the legacy admin page "http://localhost:8080/openmrs/admin"
  • Go to "Manage Patient Identifier Sources" under the header "Patients"

Check if there is an existing local identifier source. If there isn't, you'll need to create a local identifier source to generate "fake" ZL EMR IDs:

  • Add a new "Local Identifier Generator" for the "ZL EMR ID" with the following settings:
    • Name: ZL Identifier Generator
    • Base Character Set: ACDEFGHJKLMNPRTUVWXY1234567890
    • First Identifier Base: 1000
    • Prefix: Y
    • Suffix: (Leave Blank)
    • Max Length: 6
    • Min Length: 6
  • Link the local generator to the Local Pool of Zl Identifiers
    • Click the Configure Action next to the local pool
    • Set "Pool Identifier Source" to "ZL Identifier Generator"
    • Change "When to fill" to "When you request an identifier"

Configuring functionality in a PIH EMR OpenMRS Instance


There are two parts to address configuration.

First there is the Address Hierarchy module configuration, which manages the address hierarchy data in MySQL. This is done by adding AddressHierarchy config files (see "Activator Loading of Address Configuration & Entries") to the directory mirebalais-puppet/mirebalais-modules/openmrs/files/app-data-config/<config_dir>/configuration/addresshierarchy/. config_dir is specified in your site hieradata file, see e.g. ces-reforma.yaml.

The old way of configuring AddressHierarchy is to create an address bundle, see e.g. LiberiaAddressBundle. Now that AddressHierarchy has built-in config file support, this is deprecated.

Then there's the RegistrationApp configuration with respect to addresses. The two settings of note here are the shortcut field and the manual (i.e., free-text) fields. These are configured in the addressConfig tree in your PIH config file (the ones in mirebalais-puppet/mirebalais-modules/openmrs/files/config). These options are handled by mirebalais/.../PatientRegistrationApp. If you don’t provide this configuration, this file provides defaults.

Apps & Components

The configuration for which components are enabled is in mirebalais-puppet/.../pih-config-.json. Components are defined in pihcore/.../config/ Based on these component selections (and often some other criteria) CALF (mirebalais/.../ loads apps and forms. Apps are defined in mirebalais/.../

Registration Summary Dashboard

The RegistrationApp seems to provide, by default, a single widget, which displays the information in the "demographics" section. It only will display patient attributes - concept/observation data added to the demographics section will always show a blank answer in the dashboard widget.

RegistrationApp is configured in mirebalais/.../apps/patientregistration/. SectionsDefault provides the default Registration application. Some of it is configurable using the site configuration JSON, mirebalais-puppet/.../pih-config-.json. Parts of it can be overridden in the site-specific Sections file, e.g.

Person Attributes should be added to the section with id demographics. Other registration components should be added elsewhere. For these, you will also need to edit the corresponding registration form section, pihcore/.../htmlforms/patientRegistration-<section>.xml. This XML file is what is used by the registration dashboard to configure the "view" widget, as well as the "edit registration" forms.


In OpenMRS, the list of diagnoses to use is the set of all diagnosis concepts contained in the concept sets of diagnoses contained in the set of sets of diagnoses named by the global property emr.concept.diagnosisSetOfSets. Read that carefully.

To break it down a bit:

  1. Create a concept on the Concepts server called something like "MySite primary care diagnosis set" or simply "MySite diagnosis".
  2. Set this concept to be a ConvSet (with datatype NA), and check that it is a set.
  3. Add all the diagnoses you want to it.
  4. Create a concept on the Concepts server called something like "MySite diagnosis set of sets"
  5. Set this concept to be a ConvSet (with datatype NA), and check that it is a set.
  6. Add "MySite primary care diagnosis set" (or whatever you called the other concept) to it.
  7. Export a new version of the MDS package "HUM Clinical Concepts", adding "MySite diagnosis set of sets" to it.
  8. Update that MDS package in mirebalaismetadata.
  9. Add to pihcore/.../GlobalPropertiesBundle a line resembling public static final String LIBERIA_DIAGNOSIS_SET_OF_SETS = "ed97232b-1a09-4260-b06c-d193107c32a7";, but with your site name and the UUID of your "MySite diagnosis set of sets" concept.
  10. Add to your site's MetadataBundle file a line like properties.put(EmrApiConstants.GP_DIAGNOSIS_SET_OF_SETS, GlobalPropertiesBundle.Concepts.LIBERIA_DIAGNOSIS_SET_OF_SETS);, but with the constant that you just added to GlobalPropertiesBundle. See for example pihcore/.../LiberiaMetadataBundle.


Forms live in pihcore/omod/src/.../resources/htmlforms and are edited in code. The xml files that represent forms are parsed by the HTML FormEntry Module. Check out the HTML/DSL Reference.

See this example of a check-in form.

The application logic that specifies when to display forms, and which form files to use, is found in CALF. This class is responsible for loading forms from code into the database. It doesn’t always succeed in doing this dynamically, however, when forms are being edited, so as a back-up forms are manually loaded in mirebalais/setup/HtmlFormSetup.

Note that this application logic often depends both on which components are enabled (see "Country-specific settings" below) and which location tags are enabled at the active location, which are set in openmrs-module-pihcore/api/src/main/java/org/openmrs/module/pihcore/setup/

If you want to customize an existing form for your site, copy it into htmlforms/yoursite, and make your changes to to copy there. It must have the same name as the default form, and CALF must know that your site has a folder in htmlforms.

To view changes to forms with page refreshes, you need to make sure the query string in the address bar contains breadcrumbOverride=breadcrumbUiOverride.

Adding a New Type of Form

To add a new type of form is to create a new encounter type. See pihcore PR #10 and mirebalais PR #16 for an example of how to accomplish this, along with creating a corresponding new app.

Registration Form

The Registration form is produced by RegistrationApp based on the configuration specified in mirebalais/apploader/apps.patientregistration/. This also generates some of the Edit Registration forms, but not all of them. RegistrationApp is able to provide View and Edit UI for sections that do not have concept questions. For sections with concept questions, you will need to create a .xml file (like patientRegistration-contact.xml) to define those views.


Enabling a Program

There are a number of programs that come with PIH EMR. Each one has its own component. To enable a program:

  • Enable the component by adding it to mirebalais-puppet/.../pih-config-mycountry.json.
  • Ensure the program bundle will be loaded -- check the @Requires section of pihcore/.../ If there is no such bundle for your country, you will need to create one.
    • If you create a MyCountryMetadataToInstallAfterConceptsBundle, you will need to call it from openmrs-module-mirebalaismetadata/.../ in the function installMetadataBundles.
  • Ensure you have the concepts required by the program. These are listed in openmrs-module-pihcore/.../ Please see the MirebalaisMetadata README for information about concept management.

Creating a program

  1. Create a concept for your program (see the MirebalaisMetadata README
  2. Add your new concept, as well as an outcomes concept (the default is fine for most things) to pihcore/.../
  3. Create a program definition based on pihcore/.../
    1. Be sure to change the name, description, concepts, and UUID.
  4. Create a program bundle based on pihcore/.../
  5. Add the bundle to your mirebalaismetadata/.../ file (see above, "Enabling a program")
  6. Create a component for your program by adding a line to pihcore/.../
  7. Document it with the bundle name, your implementation name, and the MDS package name if it's not HUM NCD.
  8. Add a block to CALF (mirebalais/.../ in the enablePrograms function. It should have two lines, one call to supportedPrograms and one call to configureBasicProgramDashboard.
  9. Enable the component by adding it to mirebalais-puppet/.../pih-config-mycountry.json.

Country-specific settings

Global properties for a country installation are defined in the mirebalais-puppet repo, .e.g., these two are used for Haiti:


Language specific settings are configured in these places:

  1. Translation for concepts will be done via the OpenMRS Dictionary UI (do this locally for now, eventually this will be done on the staging server, and concepts will be packaged using Metadata Sharing)

  2. OpenMRS settings, allowed locales example:

  3. Locale specific resource files, e.g.,



Localized String Management

Transifex is used for managing translations. To add new strings, add to the file for that project (mirebalais example). The localized strings are pulled from Transifex and committed into mirebalais_*.properties files.

Join the "PIH EMR" project on transifex:

Using transifex for locale specific strings:

Notes from Mark Goodrich:

However, we will start needing to create files for Spanish translating and getting a translator working on them. I don't think any of the modules that PIH owns will have Spanish translations, though the OpenMRS ones may. You can see a list of the modules that PIH owns and has in Transifex here (Dominic I just sent you a Transifex invite):

For each of these modules (with the exception of the legacy Patient Registration module) we should add the (Priority can be given to the main modules like PIH Core, Mirebalais Reports, Mirebalais... we won't need translations in Lab Tracking or ED Triage unless we plan on using that functionality in Mexico)

Steps to add a

  1. Create the file in the resources directory of the api project of the module:

  2. Add the locale "es" to the config.xml of the module:

  3. Add the locale "es" to the Transifex configuration file for the module:

Of course, after we do this, we need to start having translators edit translations and pull them into the projects.

Documentation can be found here: (In particular see the section 'Updating A Module With New Translations" and how to install the Transifex command line client).


Once you have installed the distribution, you should be able to update it with the following commands...

If you are watching any modules, first execute "mvn openmrs-sdk:pull" to pull in any changes to these modules via git.

Then, from the base directory of the mirebalais project run the following two commands to update any changes to modules you aren't watching:

$ git pull
$ mvn openmrs-sdk:deploy -Ddistro=api/src/main/resources/ -U

(I have created a shell script shortcut to execute the two commands above,

To run the server:

$ mvn openmrs-sdk:run

Making Things Easy


(You might be able to set the "debug" flag at project creation time now. is a utility script created by Mark Goodrich.)

$ alias omrs-pull='mvn openmrs-sdk:pull'
$ alias omrs-deploy='cd /home/mgoodrich/openmrs/modules/mirebalais && ./'
$ alias omrs-run='mvn openmrs-sdk:run -Ddebug'

So to do a daily update of the system, run:

$ omrs-pull
$ omrs-deploy
$ omrs-run


There's an Invoke file for doing local development of the PIH EMR here. Feel free to make pull requests.


I'm getting a NullPointerException immediately on navigating to the EMR

Something like

UI Framework Error
Root Error
	at org.openmrs.module.appui.fragment.controller.HeaderFragmentController.controller(
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

The error itself is meaningless. What it tells us is that something probably went wrong during initialization. Look at the server logs, probably somewhere between the Liquibase stuff and the error you're seeing.

The server isn't reflecting the changes I'm making in code

Make sure that the module you're working on hasn't come un-watched. Look at the watched.projects line of in the App Data directory.

After fixing an error while developing on a metadata bundle, the metadata still isn't updating

When a module fails to start, the next time OpenMRS runs it will not try to start it. On realizing this has happened, you may be inclined to start it from the Admin UI. However, there's a problem. Metadata bundles don't load when starting the mirebalais module via the admin UI on an already-running server. Therefore you must always make sure that your modules are enabled prior to running the server, especially after running into problems during initialization.

This can be accomplished by logging in to mysql and running

update global_property set property_value='true' where property like '%started%';

or by running invoke enable-modules if you're using PyInvoke.

OpenMRS displays errors like this after starting up, where Foo is the name of the module I'm working on:

Foo Module cannot be started because it requires the following module(s): Bar 1.2.3-SNAPSHOT

As of this writing, we use a lot of snapshots. One thing that can happen is that when you openmrs-sdk:run, Maven might pull a new snapshot version of some module (here, Foo), but its dependencies may have updated, coming out of sync with the Foo POM you have locally. So the snapshot version of Foo expects BAR 1.2.3-SNAPSHOT, but your local Foo POM still requires Bar 1.2.3.

The thing to do is to pull the latest changes to Foo from master (merging/rebasing into your branch if you're in a branch), and then run openmrs-sdk:deploy (or ./ or invoke deploy), then try running again.

Error about com.mycila when building core

If, when building core, you see an error like...

[ERROR] Failed to execute goal com.mycila:license-maven-plugin:3.0:check (default) on project openmrs-test: Execution default of goal com.mycila:license-maven-plugin:3.0:check failed: Cannot read header document license-header.txt. Cause: Resource license-header.txt not found in file system, classpath or URL: no protocol: license-header.txt -> [Help 1]

... then try commenting out the mycila plugin in the main pom of the project

Source Code

All source code for OpenMRS is stored on Github, in either the OpenMRS organizaiton (for core and community modules) or the PIH organization (for PIH-specific modules).

The naming convention for a module repo is "openmrs-module-[module_name]".

Getting Set Up with IntelliJ

At least with openmrs-module-pihcore, IntelliJ may want to identify some directories as modules that are not modules, and are in fact subdirectories of actual modules. The problem is that these phony modules don't inherit Maven dependency information from the parent modules, so IntelliJ will fail to resolve references to those dependencies. To fix this, go to Project Structure -> Modules and remove those directories.

Other than that, this project is more or less plug-and-play in IntelliJ.

You can’t perform that action at this time.