Hack in the Right Direction

March 12, 2014
Hack in the right direction!
For over a decade, I’ve been using the phrase “hacking in the right direction” and, while I’ve explained it several times over the years, I was surprised to discover that I never blogged about it. So, here’s an explanation…

As any experienced coder knows, there’s some code that they are proud of and other code that is just there to get the job done. The latter is often referred to as “a hack” or “hacky code.” While it’s fair to say that coders wish all of their code could be beautiful, the reality of implementation requirements, delivery timelines, and competing demands nearly always force some amount of compromise. This leads many developers to approach projects in one of two ways:

Conditions Approach
  • Sufficient timeline
  • Critical code
  • Shared or peer-reviewed code
Write good code
  • Short timelines
  • Not critical code
  • Short term need
  • Used by a single persion/group
Hack

While it’s not always this black & white, most developers are balancing these two approaches in their head and their code is an amalgam of these two approaches. In OpenMRS, we often contrast “Development” with “Implementation coding,” where the latter tends to involve more hacking to meet time constraints.

How can we leverage these two approaches? Do these approaches have to contradict each other or can we make them complimentary? This is where hacking in the right direction comes in:

Hack in the Right Direction

The next time life intervenes and prevents you from taking the time to generate beautiful code, before you just hack something out, take a moment to consider how you could hack in the right direction. This approach takes your ultimate goal(s) into consideration, but allows for guilt-free compromises.

The fundamental requirement of hacking in the right direction is understanding where you want to be. While a journey of a thousand miles may begin with a single step, it helps to take that step toward the destination.

patient selection widgetHow does this work in real life? I’ll give you an example that came up not too long ago. Imagine that you are asked to make a patient selection widget for an application. It would be pretty natural to have a method like this:

/* Get the selected patient */
public Patient getSelection();

But what if you took 20-30 minutes to review how the patient selection widget will be used in the application and learn that we want to use it for all patient selections, including for report generation where more than one patient may be selected? Then you would probably change your method to something like this:

/* Get the selected patient(s) */
public Patient[] getSelection();

It’s a subtle change, but it could have a dramatic effect on how you write and use your code. In this example, it’s a simple change to the method signature, so worth the effort. The trick is finding these easy wins without over-designing your hack. This approach will allow you to meet tight time constraints while at least building toward a longer-term vision.

A recipe for Hacking in the Right Direction

  1. Spend 20-30 minutes with one or more informed product owners to understand, as best as you can, their longterm vision.
  2. As you hack, look for inexpensive adjustments to your design that will support the longterm vision.

It’s not too hard and the investment is small, even when time constraints are tight. The next time you need to hack, take a moment, and try to hack in the right direction!

Fun with HTTP

January 9, 2014

Playing around with cURL and learning about some handy online tools…

requestbin

 

 

Visit requestb.in and you’ve instantly got a link against which you can post data and see the results.

When posting a text file with curl, I usually remember the -H “Content-type: text/plain”, but I try -d @filename.txt and get frustrated before I rediscover –data-binary @filename.txt.

CLBIN

clbin.com is a nifty tool for directing command line output straight to a short url.

Alfred Workflows

April 3, 2013

If you own a Mac and haven’t discovered Alfred 2 yet, well I’m sorry.  Combined with its PowerPack, it’s a formidable tool for doing things quickly on the Mac.  I’ve used QuickSilver and LaunchBar.  I held out for a long time as a staunch LaunchBar user, but recently made the switch to Alfred 2 and I’ve got it doing anything everything I need (and boy do I need a lot… I’m lazy and I’ll love keyboard shortcuts).  The only shortcoming with Alfred is its clipboard history, which is limited to text (no images… bummer).  LaunchBar does the clipboard history just right, so I map Alt+Cmd+K to LaunchBar’s clipboard history and I’m good to go.  Sure, LaunchBar is an expensive tool just for clipboard history, but I already owned it and why use Alfred’s crappy clipboard history when I have access to LaunchBar? 🙂

Anyway, I thought I’d share a few of my (now ~24) Alfred 2 workflows for anyone interested:

icon Arrows
Easily type arrow characters (↑, ↓, ←, →) using HTML entity names: uarr, darr, larr, and rarr.
IRC Cloud IRC Cloud
Open IRC Cloud in Chrome with irc.  If you’ve already got IRC Cloud in a tab, it doesn’t open it in a second tab.
icon OpenMRS
A workflow for OpenMRS developers with a few handy shortcuts.

  • op — launches the OpenMRS wiki
  • ot [ticket] — launches OpenMRS JIRA. Naming a ticket goes straight to that ticket; giving just a number will open the corresponding TRUNK ticket; leaving off the ticket just takes you to JIRA.
  • dev — opens the OpenMRS Developers Forum page
  • raf — types Rafał’s name properly into your editor (and into your clipboard in case you need it again)
  • paul – pastes “Paul is a dork” into your editor
icon Volume Controls
Provides some shortcuts for controlling your volume. You thought you were lazy? I know there are dedicated keys for this, but they’re way over there. Alfred shortcuts can be habituated (once learned, you just think and it happens) and don’t require your hands to leave the “home row” of the keyboard.

  • vl — volume low (about one “dot” of volume on the Mac)
  • vh — volume high (crank it up!)
  • mute ‐ guess what this does. 🙂

One-Eyed Pinky Trick

March 23, 2013

Have you ever seen a harvest moon?

Harvest Moon

Have you ever wondered why the moon looked so big?  Is it some sort of refraction of the atmosphere?  Actually, it’s just an optical illusion.  It’s time for the One-Eyed Pinky Trick!

Pinky

  1. Extend your arm toward the moon.
  2. Stick your pinky (smallest finger) straight up and hold it right next to the moon.
  3. Close one eye.

Notice anything different?

That’s right, the moon is smaller.  Don’t believe me?  Try it.  It works on the sun too (just don’t stare at the sun, ok?).

How does this work?  Here’s my theory…

Poof!  Optical Illusion busted thanks to the One-Eyed Pinky Trick!

IRCCloud ftw!

March 12, 2013

I recently discovered IRCCloud and it has brought me back to IRC.  I no longer have to worry about opening or maintaing a client or an IRC bot.  I can check into IRC on my own terms and easily catch up on what I’ve missed (even days of history) with little effort.  Perfect!

But it gets better.  After a day or so playing around with IRCCloud, I hopped into the feedback channel to give some props to the IRCCloud team and ask some questions.  I had noticed that I couldn’t page up/down through the history using the keyboard.  Look what happened:

IRCCloud continuous delivery

From feature request to deployment in under 4 hours… now that is continuous delivery!  Well done, IRCCloud!!!

IRCloud is still in a beta phase.  You can sign up, but it might be a while before you get an invite.  If you’re looking for an invite to IRCCloud Beta, let me know and I may be able to send you one.

Partial checkout with Subversion

June 6, 2012

I wanted to check out OpenMRS trunk and a couple branches from the subversion repository within the same local copy so that I could apply a set of changes to trunk and backport it to a couple prior versions in a single commit. Typically, when you check out a directory, you get everything underneath. But OpenMRS trunk and the branches I wanted are only a few of the bazillion folders and files underneath http://svn.openmrs.org/openmrs/. Checking out all of those folders would take a long time, place an unnecessary tax on the OpenMRS repository, and end up transferring a bazillion files that I didn’t need. While it’s not obvious, there is a way to do this with Subversion.  The answer is in Subversions sparse directories feature. Thank you, stackoverflow! Here’s how I checked out only a subset of folders (trunk and a couple branches) from subversion:

$ svn co --depth empty http://svn.openmrs.org/openmrs
$ svn update --set-depth infinity openmrs/trunk
$ svn update --set-depth empty openmrs/branches
$ svn update --set-depth infinity openmrs/branches/1.8.x
$ svn update --set-depth infinity openmrs/branches/1.9.x

jsonpretty: handy when playing with web services

October 17, 2011

I just ran into a handy little utility: jsonpretty.

$sudo gem install json jsonpretty

While

$curl -i http://localhost:8081/openmrs-standalone/ws/rest/v1/catalog

gives you something like this:

{"catalog":[{"name":"Cohort","operations":[{"name":"GET http://localhost:8081/openmrs/ws/rest/v1/cohort?q","description":"Fetch all non-retired that match this parameter"},{"name":"GET http://localhost:8081/openmrs/ws/rest/v1/cohort/{uuid}","description":"Fetch by unique uuid"},{"name":"GET http://localhost:8081/openmrs/ws/rest/v1/cohort","description":"Fetch all non-retired"},{"name":"POST http://localhost:8081/openmrs/ws/rest/v1/cohort","description":"Create with properties in request"},{"name":"POST http://localhost:8081/openmrs/ws/rest/v1/cohort/{uuid}","description":"Edit with given uuid, only modifying properties in request"},{"name":"DELETE http://localhost:8081/openmrs/ws/rest/v1/cohort/{uuid}?!purge","description":"Delete this object from the database"},{"name":"DELETE http://localhost:8081/openmrs/ws/rest/v1/cohort/{uuid}?purge","description":"Delete this object from the database"}],"url":"http://localhost:8081/openmrs/ws/rest/v1/cohort","representations":[{"name":"ref","properties":["uuid","display","links"]},{"name":"default","properties":["uuid","name","description","voided","memberIds","links"]},{"name":"full","properties":["uuid","name","description","memberIds","voided","auditInfo","links"]}]},{"name":"CohortMember","operations":[{"name":"GET http://localhost:8081/openmrs/ws/rest/v1/cohort/{parentUuid}/members/{uuid}","description":"Fetch by unique uuid"},{"name":"GET http://localhost:8081/openmrs/ws/rest/v1/cohort/{parentUuid}/members","description":"Fetch all non-retired"},{"name":"POST http://localhost:8081/openmrs/ws/rest/v1/cohort/{parentUuid}/members","description":"Create with properties in request"},{"name":"POST http://localhost:8081/openmrs/ws/rest/v1/cohort/{parentUuid}/members/{uuid}","description":"Edit with given uuid, only modifying properties in request"},{"name":"DELETE http://localhost:8081/openmrs/ws/rest/v1/cohort/{parentUuid}/members/{uuid}?!purge","description":"Delete this object from the database"},{"name":"DELETE http://localhost:8081/openmrs/ws/rest/v1/cohort/{parentUuid}/members/{uuid}?purge","description":"Delete this object from the database"}],...

adding jsonpretty:

$curl -i http://localhost:8081/openmrs-standalone/ws/rest/v1/catalog | jsonpretty

gets you something much prettier:

{
  "catalog": [
    {
      "name": "Cohort",
      "operations": [
        {
          "name": "GET http://localhost:8081/openmrs/ws/rest/v1/cohort?q",
          "description": "Fetch all non-retired that match this parameter"
        },
        {
          "name": "GET http://localhost:8081/openmrs/ws/rest/v1/cohort/{uuid}",
          "description": "Fetch by unique uuid"
        },
        {
          "name": "GET http://localhost:8081/openmrs/ws/rest/v1/cohort",
          "description": "Fetch all non-retired"
        },
        {
          "name": "POST http://localhost:8081/openmrs/ws/rest/v1/cohort",
          "description": "Create with properties in request"
        },
        {
          "name": "POST http://localhost:8081/openmrs/ws/rest/v1/cohort/{uuid}",
          "description": "Edit with given uuid, only modifying properties in request"
        },
        {
          "name": "DELETE http://localhost:8081/openmrs/ws/rest/v1/cohort/{uuid}?!purge",
          "description": "Delete this object from the database"
        },
        {
          "name": "DELETE http://localhost:8081/openmrs/ws/rest/v1/cohort/{uuid}?purge",
          "description": "Delete this object from the database"
        }
      ],
      "url": "http://localhost:8081/openmrs/ws/rest/v1/cohort",
      "representations": [
        {
          "name": "ref",
          "properties": [
            "uuid",
            "display",
            "links"
          ]
        },
        {
          "name": "default",
          "properties": [
            "uuid",
            "name",
            "description",
            "voided",
            "memberIds",
            "links"
          ]
        },
        {
          "name": "full",
          "properties": [
            "uuid",
            "name",
            "description",
            "memberIds",
            "voided",
            "auditInfo",
            "links"
          ]
        }
      ]
    },
    ...

Nice! Thank you jsonpretty! 🙂

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

Swipe down to close tab in FF4

November 16, 2010

Just upgraded to Firefox 4 beta 7 on my MacBook Pro and the (three finger) swipe down gesture I had grown to love (thanks to Will Henderson’s MultiClutch), suddenly became a “show tab view” gesture.  This is no good!  I’ve already become accustomed to swiping down to close tabs in Firefox.

A little googling, and I was able to get back my swipe-down-to-close-tab gesture and move the show tab view gesture to SHIFT + swipe down:

  1. Open up a tab and browse to “about:config” to bring up the Firefox configuration — proceed at your own risk.
  2. Type “gesture” in the Filter field to filter down to gesture-related preferences.
  3. You should see the preference browser.gesture.swipe.down of type string set to value Browser:ShowTabView.
  4. Right-click on the preference name and select New –> String from the context menu.
  5. Enter browser.gesture.swipe.down.shift as the new preference name.
  6. Enter Browser:ShowTabView as the new preference’s value.
  7. So, now you have added a SHIFT + swipe down gesture (swiping three fingers down while holding down the shift key) to perform the default function of swipe down.  Now we take back our swipe down gesture…
  8. Double-click on the value of the browser.gesture.swipe.down preference and change it to cmd_close.
  9. You’re done!  Swipe down and the about:config tab should disappear.  Hold down shift while swiping down and you’ll see the tab view.

Now that gestures are supported on Firefox, I no longer need MultiClutch settings for Firefox.

Creating a Desktop Shortcut on a Mac

November 26, 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.