Help with embedding complex obs inside RadiologyReport.java

Hi @dkayiwa, @pascal, @wyclif, @lluismf and others. Help needed here :slight_smile:

I am trying to embed complex obs inside radiology report object. Currently to report an order inside the radiology module, you’d have to enter some text inside a text field(tinymce) and on submission, the content of that field is stored as a string inside the radiology_report table in the database. We have decided that instead of storing these report contents as strings inside the database, we’ll store them as html files in the file system from the contents of the editor. This way it will help harmonize this with reports created with mrrt templates. So we decided to store the reports as complex obs. I’ve done some research about complex obs and learnt a lot but not enough to get me to start coding. I have read the wiki on how to create complex obs from the legacyui but I’ve not seen one that gives a walkthrough of all the code changes needed. I am currently looking at ObsFormController.java and obsForm.jsp so I can see how this is done in the legacyui but so it’s not going very well. Here is a commit where I’ve started by replacing RadiologyReport.body(of type sting )to RadiologyReport.obs(org.openmrs.Obs) and also modified the database accordingly. But it’s not very clear what my next step should be. I know its weekend but am still hoping I can get some help :

Hi @ivange94,

This is what is specific to a complex obs and that must be done to it prior to saving it:

  1. Set the concept complex.
  2. Set the complex data.

In terms of the code it is as simple as:

Obs obs = new Obs(..);
obs.setConcept(concept);  // This can also be done through the ad-hoc constructor
obs.setComplexData(complexData);
...
obsService.saveObs(obs);

1. The concept object

It must be a concept complex so one of the subtype ConceptComplex. That is how we guide ObsService to know which obs handler to use for saving the obs, or more precisely, what to do with the complex data attached to the obs (see point 2). You can just create this concept from the admin UI and ensure that the correct handler is set. Here are the handlers shipped with the Core: https://github.com/openmrs/openmrs-core/tree/1.11.x/api/src/main/java/org/openmrs/obs/handler Of course you may just design your own handlers, depending on what you are up to exactly.

2. The complexData object

An instance of an object that inherits ComplexData or simply just an instance of ComplexData for starters. ComplexData is a wrapper around the actual data that you want to save through the handler, whether it is an image, a text file, a video, … etc, anything really. It also holds some metadata for it, such as a title.


I hope this provided some guidance. More here from the wiki: ‘Creating Complex Observations and Concepts’, in particular on how to set the concept complex from the admin UI.

As a side note, you may find our Visit Documents UI module useful to keep an eye on all complex obs saved for a patient. It is not yet Platform 2.0 compatible though, this might be a blocker. This would let you visualise through a 2.x UI all complex obs that are saved for a patient.

Good luck, don’t hesitate to ask more about manipulating complex obs.

2 Likes

@mksd thanks for the time you took to give a very detailed answer. I am going through it now and will revert back to you if I get stuck. :slight_smile:

One thing to point out: this implies that you are going to store a free-standing obs, and store a reference to that in the radiology report. E.g. you’ll end up with:

  • an HTML file on the filesystem
  • a row in the obs table, whose complex_value points to the HTML file
  • a column in the radiology report table, whose value is a foreign key to the obs_id of the obs.

That may be what you want to do, but the alternative is to just use a mechanism similar to the complex obs one, but not_mechanism_, but not actually store an obs. E.g. instead you just have:

  • an HTML file on the filesystem
  • a column in the radiology report table that points to the HTML file
  • (no obs)

Hi @darius,

thanks for pointing out the 2. no obs option. Did you mention this option just so we know its out there when we make our decision in how to move forward? Or do you think the approach with complex obs is overkill or do you have other concerns with it?

We thought saving the file as obs would be a good idea since later on you could retrieve all obs for a patient containing radiology reports which is just another observation. Thought this would be best practice/follow the OpenMRS model.

What do you mean by “freestanding obs”?

1 Like

The idea that the radiology report is an obs does make sense.

My only concern is that adding the extra level of indirection makes things more complicated, and makes it more likely that thing will get out of sync (e.g. what if someone goes and manually edits/deletes the obs).

I just wanted to make sure you had considered the options.

I meant an obs that isn’t part of an encounter. (But typing this I realize that maybe there is an encounter.)

Hi @mksd

This is what I was trying to say in the mail I sent. From your answer it wasn’t so clear how I’ll get an instance of Concept and ComplexData. I have a form like below

<form>
    <table>
        <tr>
            <td>Select File</td>
            <td><input type="file" name="file" id="file"/></td>
        </tr>
        <tr>
            <td><input type="submit"/></td>
        </tr>
    </table>
</form>

I have created a complex concept from the legacyui. I want the file selected by the user to be attached with that complex concept that I created from the UI and save it as a complex obs. How do I get an instance of that concept from the jsp and set the file selected by the user to be its complex data and send it to my controller. I hope this time its a bit clearer.

In your controller you will have to do something similar to this in ObsFormController. The above linked method is setting the complex data to the obs after extracting the actual file data from the POST request. Of course yours is named "file" and not "complexDataFile".

As for the concept complex, start by simply fetching it by its UUID in your controller, that would get you going. A cleaner and more sustainable approach (but you can do that later) would be to save this concept through your module’s activator first, under a UUID that is given by global constant in your module. Then you could again refer to that global constant UUID in your controller. It depends on how static this concept complex has to be or not.

@mksd thanks, that was helpful. :slight_smile:

Hi @mksd

Following your advice, I have gone ahead and created a complex concept inside my module activator so that I can reference it when I need to create a complex obs. You can see it here, https://github.com/ivange94/openmrs-module-radiology/commit/20c1b143f4c02ac17904a42837713f18c40c5a0b#diff-e9dbb51a501d912666be093474248d90R37. But am having some troubles to properly build the concept object. I mean from the admin UI, I am able set datatype to Conplex, select handler as text handler. What are the proper method signatures to do that in java code. I see a set handler method but it expects a string, I don’t know what to pass there(“text” or “textHandler”). I have looked at the ConceptFormController.java but it does not create a concept as I am doing in ModuleActivator so its been of little help to me. I am using this concept here, https://github.com/ivange94/openmrs-module-radiology/commit/20c1b143f4c02ac17904a42837713f18c40c5a0b#diff-35a478f7924bab0804b5e4e573aee08dR149 but its not properly constructed.

Here are two examples from VDUI’s activator that illustrate how to create concepts complex.


The handler name that you have to set is the key defined in the application context XML file:

<key><value>TextHandler</value></key>
<bean class="org.openmrs.obs.handler.TextHandler"/> 

So "TextHandler" in your case (capital T).

Thanks for your assistance. I had a walk around for this. I created a global property that holds uuid of complex concepts created from the admin UI. Then I just retrieve the value of the global property to retrieve a complex concept that was created from the admin UI or one that is included in the concept dictionary.

That’s the easier way to get you going as I said in a previous post. When this is all working fine, you can refine your code by making it create the concept complex in the first place. You know, one piece of plumbing at a time.

Just a quick practical tip: it would be helpful if you could point others directly to the piece of code that you want them to look at. As opposed to the whole commit itself, because then people have to figure out where exactly (which file, which section) to look at.

Let me know how it goes, good luck.

actually I don’t know how to select a whole portion of code on github and share. But the links I sent where pointing to particular lines, those are the lines where I wanted you to look at. I’ll try better next time :slight_smile:

Come on! Google! :wink:


No problem. Keep us posted on your progresses with the Radiology module + complex obs.

Sure. Actually I didn’t know it was possible until I saw you do it.

I am having this error when I try to save the obs

‘obs id is null’ failed to validate with reason: error.noValue

my code looks somewhat like this

final Concept concept = Context.getConceptService().getConcept(6200); final Obs obs = new Obs(new Person(1), concept, new Date(), new Location(1)); Context.getObsService().saveObs(obs, null);

My logs are clean, so it doesn’t help here

You never set a value to the obs. Since this is a presumably a complex obs, this means that you have not set the complex data to it.

1 Like

Yes that was it. Thanks