Complex Obs Support

Introduction

This page describes the changes to OpenMRS to support Complex Observations. See ticket:656 and ticket:107.* 
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. These complex data are large and so are stored on the file system outside of the OpenMRS database. In order to do this, a few things are necessary:

  • The Concept Dictionary must have at least one Concept with Datatype as Complex.
  • That ConceptComplex must be associated with a registered ComplexObsHandler
  • The question for the Obs (Obs.getConcept()) must be set to that ConceptComplex (a Concept with Datatype="Complex").

Intro to ComplexObsHandler

  • ComplexObsHandler classes are hidden from the user and only used by the ObsService.
  • They do the internal work of saving and retrieving ComplexData from an Obs that is "complex"
  • Each ComplexObsHandler is mapped in the Spring framework with a unique key.
  • ComplexObsHandlers can be registered in any of 4 places:
    • API level handlers: metadata/api/spring/applicationContextService.xml
    • Web layer handlers: web/WEB-INF/openmrs-servlet.xml
    • Module loaded handlers: metadata/moduleApplicationContext.xml
    • Programmatically using ObsService.registerHandler(Class handler)
  • If two handlers are mapped to the same key, the last one loaded by Spring will override the first.
    • The overriding class should save the complex data in the same place as the parent

Workflow

  • Create a new Concept in the Concept Dictionary and set the Datatype to "Complex".
    • Now set the "Handler" for this concept to the built in ImageHandler
  • Create a new Obs
    • Set the concept (question) to be your new complex type of concept
    • Choose an image file to upload
    • Save the obs
  • View the newly created Obs and see the image and a link to download it

 

Technical Workflow

Save a complex obs and data:ConceptComplex conceptComplex = Context.getConceptService().getConceptComplex(1867);
// this is assumed to have happened
// conceptComplex.setHandler("ImageHandler");
 
// Set the required properties.
Obs obs = new Obs(new Person(48609), conceptComplex, new Date(), new Location());
 
BufferedImage img = ImageIO.read(new File("/home/bmckown/Desktop/test/logo.png"));
// or:
// InputStream img = new FileInputStream(new File("folder", "filename"));
 
ComplexData complexData = new ComplexData("test-image.jpg", img);
obs.setComplexData(complexData);
 
Context.getObsService().saveObs(obs, null);
 
// obs.getComplexData() will be null here
Retrieve a complex obs and its dataInteger obsId = obs.getObsId();
Obs complexObs = Context.getObsService().getComplexObs(obsId, OpenmrsConstants.RAW_VIEW);
ComplexData complexData = complexObs.getComplexData();
Object object = complexData.getData();
// object will be a BufferedImage object
Getting back binary-data / binary-stream compleObs – eg: for Video files, Zip files, kind of large files, etc
        Obs complexObs = Context.getObsService().getComplexObs(videoObsId, OpenmrsConstants.RAW_VIEW);
        ComplexData complexData = complexObs.getComplexData();
        byte[] videoObjectData = ((byte[]) complexData.getData()); // cast Object --to--> byte array object

How to Create a ComplexObsHandler

Requirements

  • A ComplexObsHandler must implement the ComplexObsHandler interface
  • A ComplexObsHandler may extend one of the default API layer ComplexObsHandlers
  • The ComplexObsHandler must be registered by Spring with a key.

Example

WebImageHandler

  • Found in the org.openmrs.web.controller.observation.handler package
  • Extends org.openmrs.obs.handler.ImageHandler
  • NOTE! Uses ImageHandler to saveObs().
  • Overrides the getComplexData() method to provide web-specific ComplexData
    • Provides hyperlink to ComplexObsServlet instead of the heavyweight data.

      public class WebImageHandler extends ImageHandler {
        public Obs getComplexData(Obs obs, String view) {
           if (Webutils.HYPERLINK_VIEW.equals(view)) {
               String link = "/ComplexObsServlet?obsId=" + obs.getObsId();
               obs.set(new ComplexData("some title", link);
               return obs;
           }
           return super.getComplexObs(obs, view);
        }
      }
      

Register WebImageHandler

  • Register WebImageHandler in openmrs-servlet.xml so that it is only seen if used in the webapp...not in the jar alone.
  • Or alternatively, for a module register in ModuleApplicationContext.xml

        <bean parent="obsServiceTarget" >
            <property name="handlers">
                <map>
                    <entry>
                        <key><value>ImageHandler</value></key>
                        <bean class="org.openmrs.web.controller.observation.handler.WebImageHandler"/>
                    </entry>
                </map>
            </property>
        </bean>