Developing Custom Data Handlers for Complex Obs
Complex Obs Support gives OpenMRS the ability to store observations (Obs) on a patient with complex values such as an x-ray image, a document, or a media file.
To promote further flexibility over which data types can be supported, we recently implemented a custom handler system.
Custom data types can be divided into two main categories
Data which doesn’t refer to an external database
Domain data types ( data types based on existing domain objects)
● These represent data types which can be stored in the OpenMRS database as either a reference or as an independent value
● Domain data types don’t refer to any external database
● Already implemented custom data types are PatientHandler, PatientFieldGenObsHandler, LocationHandler and LocationFieldGenObsHandler.
Data which refers to an external file system
Large data files such as jpgs or text files
These are objects which are too large to be stored in the database alone.
These objects are usually stored in the external database (in the OpenMRS Application data directory etc.).
Reference on how to locate these files are then stored in the OpenMRS database.
To create a complex observation, you must have a complex concept in your concept dictionary.
Complex concepts may be created using the default handlers already provided in the trunk.
The only requirement to add an additional custom data type to the available list is to create a valid handler class, and have it registered by spring. You may then use it to create complex concepts and observations.
Let’s go through the necessary steps to create such a handler.
Creating a custom data handler
Handler class name must end with ‘Handler’ (for ease of identification)
Handler class should be stored in the appropriate packages of the api layer or web layer. This is not a must, but is preferred.
Handler class must implement ComplexObsHandler interface.
*However please note that not all classes which end with the suffix ‘-Handler’ are complex obs handlers. For example, other unrelated handlers used by OpenMRS custom tags also end with the suffix ‘-handler’.
At spring initialization time, spring searches through the source code for any spring components that implement the ComplexObsHandler interface. These are detected and loaded as valid handlers based on their priority.
These handlers are then displayed in the ConceptForm.jsp. You may now use the handler to create complex concepts and observations.
To ensure that your handler will be detected and loaded, make sure that it contains the following annotations at its head.
@Component
@Order(Ordered.LOWEST_PRECEDENCE)
//Decide the order value based on your needs
The ComplexObsHandler Interface requires that you implement several methods. These are,
public Obs saveObs(Obs obs) throws APIException;
public Obs getObs(Obs obs, String view);
public boolean purgeComplexData(Obs obs);
public String getHandlerType();
public boolean validate(String handlerConfig, Obs obs);
Let’s look at PatientHandler.java as an example of how you may create a hander for a custom data type
● The getObs and saveObs methods deal with the storage / retrieval of data.
● The saveObs methods’ job is to take your data, and find a way to represent it in the database.
In case of PatientHandlers, the reference value stored in the database (in obs.valueComplex) is in the format of -
Value display text | identifier
For each obs, valueComplex must contain a user friendly display value and an identifier (key) separated by a delimiter. As an example, the valueComplex for a patient would look as follows,
The getObs method takes the reference value stored in obs.valueComplex, and transforms it back into the data originally stored by the user.
The method obsValue(Obs) is used to return the complex data object only. It takes the Observation, retrieves the complex data stored in it, and returns it as an object.
This method is essential to work with fieldGen tags.
In other cases, the method returns null.
Also note the variable ‘displayLink’.
Let’s assume that your handler is for a custom domain object. If so, chances are that there already exists a dashboard or form which is the best way to display the given object.
The aim of the displayLink variable is to give a developer who created the handler the opportunity to define the best way to view an observation value created using this handler.
For example, the default displayLink for PatientHandler leads to the PatientDashboard.
However, this value applies only to custom data types (handlers representing domain classes)
Registering the your module's handler
The code below is added to the moduleApplicationContext.xml file, if it is a web based handler, add it to webModuleApplicationContext.xml instead
PLEASE NOTE: A lot depends on the HANDLER_TYPE Constant defined in each handler class. If your custom data type is unique, and does not extend a core handler already in your system, then make sure that the HANDLER_TYPE value is unique, and lets you recognize your handler easily.
On the other hand, if your new handler extends an existing handler class, then please don’t attempt to set a new HANDLER _TYPE value.