iPad Pro

April 17th, 2010

Own a MacBook Pro and an iPad?  Tired of carrying around both?  Meet the iPad Pro.

I’m looking forward to the day when the monitor of my MacBook Pro becomes an iPad.  No extra things to carry around.  If you want to run to a quick meeting or browse on the couch, just snap off your monitor and go (it’s an iPad).  Miss your keyboard, DVD drive, extra ports, extra battery life, etc.?  Just plug your iPad back onto the iPad Pro base and you’ve got a full laptop.  Keep a cloth in your bag to wipe off those fingerprints and stop freaking out when people touch your monitor — it’s a multitouch monitor, after all.

Improving OpenMRS Code Review

April 15th, 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.

  • Review fewer than 200-400 lines of code at a time
  • Aim for less than 300-500 LOC/hour
  • Not more than 60-90 minutes at a time
  • Authors annotate prior to review
  • Establish quantifiable goals and capture metrics to improve the process (I’m noticing a theme here)
  • Checklists are good
  • Verify that defects are fixed
  • Managers must foster a good code review culture in which finding defects is viewed positively.
  • Beware the “Big Brother” effect.
  • The Ego Effect: Do at least some code review, even if you don’t have time to review it all.
  • Lightweight-style code reviews are efficient, practical, and effective at finding bugs.

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

  • There will be bugs.  Don’t aim for zero.
  • “Same goes for ‘we should all just spend time looking at each others patches and trying to find bugs in them’. That’s not a solution, that’s a drug-induced dream you’re living in.”

Plan on talking with developers to brainstorm on it…

Dad

November 11th, 2009

It appears that the folks at Shapiro’s Deli are almost as proud of Dad as I am…

Dad-Shapiros

The World is Flat

October 26th, 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 21st, 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{ """
  <b>${it[0]}</b>
  <small><em><font color=gray>${it[1]}</font></em></small>
  """ }
.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 30th, 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:

  • patient id — the subject for the list, do we want to open this up to person id?
  • list type — allergy vs. problem, other types of lists could follow
  • obs id — reference to the observation that created this list entry
  • sort weight — for managing sequence of lists
  • date started
  • date stopped
  • ?stop reason (resolved vs. error correction)
  • voided — when the associated obs is voided, the list entry gets voided as well
  • void reason
  • creator
  • date created
  • changed by
  • date changed

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:

  • allergy type — drug, food, misc, etc. (see HL7 table 0127)
  • allergen
  • reaction
  • severity — severe, moderate, mild, unknown (see HL7 table 0128)

Problem list properties:

  • diagnosis/symptom concept
  • certainty

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.

Keiaho…yes!

April 29th, 2009

Good news today!  Looks like the Colts are resigning Keiaho!!!  Most excellent!  Hopefully, Freddy will take this opportunity to prove how lucky the Colts are to have him back.

Move over TED prize…

December 15th, 2008

I’ve seen some amazing TED Talks, but Dad does it better.  Do I sound biased?  I’m not sure he could do it justice in only 18 minutes, but I’d rate this presentation over most of the TED Talks I’ve seen to date.

Translating Listserv archive to mbox format

November 30th, 2008

We wanted to import our old mailing list entries from the OpenMRS mailing list Listserv archives into Nabble.  No problem.  Finding the GET listname FILELIST and GET listname file1, GET listname file2, … commands was easy enough.  A quick search of Nabble support made it clear that I needed to send them mbox files.  So, I set out in search of a Listserv to mbox converter.  I found a couple scripts: one in perl and another in PHP.  But trying them out, made it clear that I was going to have to do some tweaking.  After a few near misses, I thought: “I could do this easier in Groovy.”  So, I ended up with this script.  Basically, it came down to leaving the messages and their headers alone and just adding a From_ line in front.  Otherwise, the only tricky part was getting the dates right (GMT time without timezone specified in the From_ line and a some reshuffling of the date format in the message header).

Both for future me and anyone else who might benefit, here’s the script I ended up with:

import java.text.SimpleDateFormat

delim = '=' * 73	// LISTSERV separates messages with a bar of equal signs
foundDelim = false	// we skip all content until first delimiter
inHeader = true		// true when processing header data
def header = ""		// holds current header data

dfListserv = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss z")
dfHeader = new SimpleDateFormat("E MMM dd HH:mm:ss yyyy z")
dfMbox = new SimpleDateFormat("E MMM dd HH:mm:ss yyyy")
dfMbox.timeZone = TimeZone.getTimeZone("GMT")	// for mbox, convert to GMT and drop timezone reference
cal = Calendar.instance

// Process input line by line from stdin
System.in.eachLine() { line ->
  if (!foundDelim)
    foundDelim = (line == delim)	// skip until we find first delim
  else if (inHeader) {
    // within header
    if (line =~ /^\s*$/) {
      // empty line signals end of header

      // fetch Date from header and reformat it for output
      m1 = header =~ /(?ms)^Date:\s+(.*?)\s*$/
      date = dfListserv.parse(m1[0][1])
      cal.time = date
      mboxDate = dfMbox.format(cal.time)
      headerDate = dfHeader.format(cal.time)

      // fetch From from header
      m2 = header =~ /(?ms)^From:\s+(.*?)\s*$/
      fromHeader = m2[0][1]
      leftBracket = fromHeader.indexOf('<')
      rightBracket = fromHeader.indexOf('>')
      if (leftBracket > 0 && rightBracket > leftBracket)
        from = fromHeader.substring(leftBracket+1, rightBracket)
      else
        from = fromHeader

      // output header with mbox-required From_ line up front and reformatted date
      header = "From $from $mboxDate\n" + header.replaceAll(/(?m)^Date:\s+.*$/, "Date: $headerDate")
      println "$header\n"

      inHeader = false	// no longer in header
      header = ""	// clear for next message
    } else {
      header += "$line\n"	// accumulate full header data
    }
  } else if (line == delim) {
    // if we find a delim, begin processing next line as header
    print "\n\n"
    inHeader = true
  } else {
    // within a message, just send it through untouched
    println line
  }
}

Creating a Desktop Shortcut on a Mac

November 26th, 2008

Making a desktop shortcut icon on the Mac is pretty easy.  Open up the AppleScript script editor and make a script like this:

do shell script “/Applications/Firefox.app/Contents/MacOS/firefox-bin -P Developer -no-remote &”

Then save as an application on the desktop.  Done.

This example shows how to pass parameters from AppleScript.  In particular, it shows how to open a 2nd Firefox window with the “Developer” profile by passing some command line parameters from AppleScript.