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 :
This is what is specific to a complex obs and that must be done to it prior to saving it:
Set the concept complex.
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.
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
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.
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.)
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
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.
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.
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
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);