Displaying OpenMRS observations using Groovy

July 11, 2011

I wanted to list out some observations for a patient and used the following Groovy script:

def sql(s) {admin.executeSQL(s,true) }

patientIdentifier = "999-3" // a test patient, of course

sql("""
select
date(o.obs_datetime),
(select name from concept_name where concept_id=o.concept_id limit 1) as question,
case c.datatype_id
when 1  /* numeric  */ then cast(o.value_numeric as char)
when 2  /* coded    */ then (select min(name) from concept_name where concept_id=o.value_coded)
when 3  /* text     */ then value_text
when 6  /* date     */ then cast(date(o.value_datetime) as char)
when 7  /* time     */ then cast(time(o.value_datetime) as char)
when 8  /* datetime */ then cast(o.value_datetime as char)
when 10 /* boolean  */ then if(o.value_numeric=1,'TRUE','FALSE')
else '?'
end as answer
from
obs o
left outer join
concept c
on c.concept_id=o.concept_id
where
o.person_id = (select patient_id from patient_identifier where identifier = '$patientIdentifier' limit 1)
order by
o.obs_datetime desc
""").collect{ it.join(": ") }.join("n")

which generated output like this:

2011-02-18: PATIENT HAD SEX IN LAST 6MO: TRUE
2011-02-18: PlAN FOR METHOD OF FAMILY PLANNING, DETAILED: ?
2011-02-18: QUANTITY: 5
2011-02-18: FAMILY PLANNING METHOD PLAN: INITIATION
2011-02-18: METHOD OF FAMILY PLANNING: ECPS
2011-02-18: HIV DISCLOSURE TO ANYONE, SPECIFIC: OTHER HOUSEHOLD MEMBER
2011-02-18: REASON FOR REFUSAL - FAMILY PLANNING: TRYING TO CONCEIVE NOW
2011-02-18: PlAN FOR METHOD OF FAMILY PLANNING, DETAILED: ?
2011-02-18: METHOD OF FAMILY PLANNING: MALE CONDOMS
2011-02-18: QUANTITY: 5
2011-02-18: FAMILY PLANNING METHOD PLAN: INITIATION
2011-02-18: METHOD OF FAMILY PLANNING: BTL
2011-02-18: FREETEXT, GENERAL: POSITIVE
2011-02-18: REASON FOR REFUSAL - FAMILY PLANNING: ABSTINENCE
2011-02-18: FAMILY PLANNING: TRUE
2011-02-18: REVIEW OF MEDICAL HISTORY: ICTERUS
2011-02-18: PATIENT REPORTED PROBLEM: YES
2011-02-18: CURRENT MEDICATIONS: ALUVIA
2011-02-18: REVIEW OF MEDICAL HISTORY: DEPRESSION

Sometimes you need a little web server

July 2, 2011

Here’s a simple jetty server using a short Groovy script:

@Grab(group='org.mortbay.jetty', module='jetty-embedded', version='6.1.14')

import org.mortbay.jetty.*
import org.mortbay.jetty.servlet.*
import groovy.servlet.*
import javax.servlet.*
import javax.servlet.http.*

class MyServlet extends HttpServlet  {
  void doPost(HttpServletRequest request, HttpServletResponse response) {
    println "--- Received ${new Date()}n" + request.getParameter('data')
  }
}

def server = new Server(8888)
def root = new Context(server, "/", Context.SESSIONS)
root.setResourceBase(".")
root.addServlet(new ServletHolder(new MyServlet()), "/")
server.start()

Running the script with groovy AtlasServer.groovy will accept HTTP POSTs at the designated port (8888) and dump posted data to the screen.  For example:

$ curl -X POST -d "data=Hello world!" http://localhost:8888/

should dump “Hello world!” to the screen.  Press Ctrl+C to abort the server.

Flowsheet Code Jam in Chennai

January 15, 2011

Here’s a shout out to our friends at ThoughtWorks. They had a code jam on the Flowsheet Module a few weeks ago and knocked out over half a dozen key tickets in a single day as well as making progress on others. The Flowsheet Module is being installed into OpenMRS at AMPATH in Kenya to allow both data managers and providers convenient access to their patients’ data and to help improve the efficiency and quality of care for the over 300,000 patients within the system… thanks to: Khaarthigha S, Rajiv RA, Pavithra K, Geetha B, Saravana K, Arvind Kumar C, Senthil V S, Prabha P, Shanum, Ponnulingam R, Alexal, Vinoth KR, Nithyan, Gobinath T, Balaji G, Ragavan G, and Chandru. You all rock! And it looks like you had some fun in the process. I especially enjoyed the image of two laptops per lap!

Improving OpenMRS Code Review

April 15, 2010

Trying to figure out how we can improve the efficiency of our code reviews.  Finding some interesting stuff on the web…

Like this article: Limit the checklist to 7±2 items.  Automate the automate-able and let existing code review data drive the list of common mistakes.

Here is a list of articles, white-papers, and documentation around peer code review.  Hmmm. Several resources, but not enough time to look at them all.

Some nice pointers here, but nothing game changing.

A good summary article here.

And here (and here) is an interesting take by Torvalds that Paul found.

Plan on talking with developers to brainstorm on it…

The World is Flat

October 26, 2009

At the IMeCA Meeting in Lima, Peru listening to Hamish present about OpenMRS in English with real-time Spanish translation … and Hamish is in Kigali, Rwanda!  Now that is cool!

Groovy thought

August 21, 2009

During some noodling about OpenMRS design issues with Paul, we wanted to look at the most recent version of some of our tables.  Did I open up MySQL?  No sirree!  I have 1.6 dev running on an appliance, so I just opened the Groovy module and ran this script:

/* Let's make a convenient function for executing SQL */
def sql = { s -> admin.executeSQL(s, false /* read-only */ ) }

sql("describe obs")
.collect{ """
  ${it[0]}
  ${it[1]}
  """ }
.join("n") /* separate lines */

Which dumps out this:

obs_id int(11)
person_id int(11)
concept_id int(11)
encounter_id int(11)
order_id int(11)
obs_datetime datetime
location_id int(11)
obs_group_id int(11)
accession_number varchar(255)
value_group_id int(11)
value_boolean tinyint(1)
value_coded int(11)
value_coded_name_id int(11)
value_drug int(11)
value_datetime datetime
value_numeric double
value_modifier varchar(2)
value_text text
date_started datetime
date_stopped datetime
comments varchar(255)
creator int(11)
date_created datetime
voided smallint(6)
voided_by int(11)
date_voided datetime
void_reason varchar(255)
value_complex varchar(255)
uuid char(36)

Active Lists in OpenMRS

April 30, 2009

I took some time this afternoon to brainstorm about active lists w/ Paul…in preparation for an OIP project this summer with Upul.

Fundamentally, we need to add support for allergy lists and problem lists within core OpenMRS.  In the end, we’ll have allergy and problem methods within the Patient Service; however, the pattern of list management cries out for a core list management machine.

We could either simply create new data points for these lists, but since we have traditionally and it makes sense to continue storing observations for these.  So, we imagined that these lists would, for the most part, be references to existing observations.  We’re even more confident in this approach after learning that some HL7 v3 RIM-based projects at Regenstrief are defining allergy and problem lists this way (as references to observations).

Here is the basic model for Lists we would propose:

Start and stop dates could go on the obs (we considered putting these in obs before), but on further reflection HL7 v2 has these in the OBR and it’s for timed urines and other observations that may take some time — tracking how long it took to obtain the observation and not tracking the duration of the concept itself.

Active list entries for patient would be all rows of a certain list type that are not voided and have a null stop date.

Basic properties of an allergy:

Problem list properties:

Getting this done may depend on building the answer_class and answer_set features (ticket 73), so that — for example — the scope of possible allergens could be defined as any concept in the allergens set and any concepts with a class of medication, medication set, etc.

Here’s some ideas of possible service methods…

PatientService
List<Allergy> getAllergies(Patient p);

List<Allergy> getAllergiesAsOfDate(Patient p);

void addAllergy(Patient p, Allergy a);

void updateAllergy(Allergy a);

List<Problem> getProblems(Patient p);

List<Problem> getProblemsAsOfDate(Problem p);

void addProblem(Patient p, Problem p);

void updateProblem(Problem p);

This is a work in progress.  I wanted to get it posted for discussion on today’s developers conf call.

Logic-a-thon Wrap-up

November 7, 2008

This was a great hack-a-thon.  We tackled a lot of different issues for the logic service.  Unfortunately, a number of these will require additional effort to be completed.

We knocked out some of the simpler tickets:

We made some decisions (like not moving Result methods to a utility class) and we make some serious inroads (including code) on how to solve some of the more difficult issues (persisting tokens #924, persisting token tags #969, caching issues #1107, and changing “today” within criteria #929).

All-in-all.  A great job by all involved!  A special thanks to Tammy for making it a success!

OpenMRS Logic-a-thon

November 6, 2008

So far, so good at the OpenMRS! To our surprise, we’re actually getting code written. We’re not sure if it’s because of Tammy’s amazing prep work or because Paul & Hamish couldn’t make it. 🙂

Looking forward to tomorrow…

OpenMRS Test-a-Thon

August 22, 2008

As with many of our hackathons, we learned a lot about where we stand… in this case, on unit testing. We worked through a lot of the conventions and methodology to create a pathway to improve our unit testing coverage. We’re embracing at least the method-naming aspects of BDD. I am confident that we can increase our success rate:

And while we may have a ways to go to think that we’ve got a decent percentage of our behaviors covered, we’ve at least got Darius covered: