Monday, November 26, 2012

inline-block

This is probably old hat to css veterans, but, for someone like myself who last did hardcore html work in days of tables, it was a neat trick for me to learn.  On our login page the user is asked to select their location within the hospital.  To start out with, we had a simple selection list with all of the locations.  But, for improved usability, the actual design called for the locations to be grouped into three columns. I was prepping myself for an alignment nightmare when Alex showed me that it could be done simply by setting the "display" attribute to "inline-block".  This allows you to set each element as a fixed width, and, tada, you end up with aligned columns.

To get this...



... all we had to do was this...

<div id="sessionLocation">
       <% locations.each { %>
             <span class="locationOption" value="${it.id}">${ui.format(it)}</span>
       <% } %>
</div>


and

.locationOption {
    display: inline-block;
    font-size: 1em;
    padding: 3px 15px;
    width: 250px;
}

(Then you can just use some simple javascript to mimic selection list functionality)

I found a good article that discusses inline-block in greater detail:

What’s the Deal With Display: Inline-Block?

All right, I'm going to head back to work on the API now and stay out of the web layer as much as possible... :)

Friday, November 23, 2012

OpenMRS Style Tip: standardizing our DAOs, and avoiding data-level services

We've developed a new best-practice way of organizing services and DAOs in OpenMRS, and I wanted to share that here.

1. We should have one DAO class for every domain object, and the boilerplate code should be encapsulated in an abstract base class

2. A service can contain multiple DAOs, for related domain functionality:

3. The service methods should expose higher-level functionality, rather than just CRUD

Below are (long) code samples:

Friday, November 16, 2012

Pretty pictures

For a long time at the beginning of every software project, it seems like you can only really blog about technical stuff, which I'm sure gets boring for all the non-developers out there.

And while I could keep talking about maven modules and behavior-driven development until you fall asleep, we've finally arrived at the phase of the project where we'll start having actual pretty pictures to show. :-)

Here's our first pass at the login screen. Note that we're building in the idea of a "session location," mostly so when you start filling out forms, you don't have to keep saying where you are:


And here's our first pass at the homepage: (note that the list of apps a user actually sees depends on user privileges)


Since I can't end a blog post without some technical content, I should point out that these are much easier to develop now that we've started using Sass and Compass. If you've ever written CSS and you aren't using these tools, you've got to start, stat! (If you look at our omod/pom.xml and search for "compass" you'll see how we incorporated these into the build process.)

Thursday, November 8, 2012

Premature Optimization

Last week, we had a discussion on my project about what we should test. For sure there is a boundary for test, but where does this boundary live? I'm an advocate for tests, mostly unit tests. I think by using unit tests we have documentation about our classes and also good and quick feedback on what we're doing. It is a good safety net.

Ok, but everyone knows that. What happens when takes too much time to create a test? It happens in legacy systems when classes have big methods, use a lot of static methods, no dependency injection, etc. But everyone knows those classes are working, because the system is working. It is legacy code, but it is also working code. Should we test those classes? In this discussion, one of the members of my team Fernando Freire, told me about a presentation (only in Portuguese) from Lucas Cavalcanti at Agile Brazil. This presentation is about Premature Optimization and it is about how and when clean code and good design practices can help us to deliver. In this presentation Lucas uses a pseudo-graph from a Martin Fowler post. Design Stamin Graph Well, the graph shows the delivery curve using good design and also no design. In the beginning, the team that uses no design "practices" can delivery faster than the other team that uses good design practices. But this changes after some time and the team with good practices delivers more. The problem is how long is this "some time". It is very difficult to estimate. It depends of the project, on the team and when we have to deliver.

After this discussion, I thought a lot about this graph. When is it safe keep moving with fewer tests and when do we have to use all the good practices to slow down the team. I'm pretty sure that the answer to my question above, about to testing a class that is hard to test, is that it depends. If you have a tight deadline and you're not changing this class, well, you don't need to test it. But if you know you'll have to change this class a lot, then I think it is worth spending some time to cover it with tests and to refactor it to make the class more simple and readable.

Actually, I had to to take a decision like this. I was pairing with Darius and we were changing the patient registration module to be able to add two new fields in the check-in flow. The code wasn't that good and we didn't have any unit tests testing it, after some discussion, we realized that refactor everything to able to test it won't be a good decision. It would take too much time and it won't bring much business value since we didn't have plans to change that screen in our short period of time. After 1 month, I still think that was a good decision and we saved a lot of time.

My conclusion after this discussion is: we cannot compromise our delivery because of good practices. The good practices were created to help us to deliver more, to create more value for the client. If good practices are slowing the team down, something is wrong. I know its easier said than done. Sometimes this boundary about what and when to test is hard to see. Actually, most of the time this it is VERY hard to see. But this is what Agile is about right? If we focus on delivering value, we'll learn where this boundary lives and learn how to improve the team velocity.

BDD while doing OpenMRS development, thanks to Thoughtworks

I recorded a screencast for OpenMRS University a couple days ago, which was supposed to be about using the UI Framework module. I ended up doing an extra 25-minute tangent about behavior-driven development of API functionality, inspired by the process Thoughtworks has been helping us take up.

Watch on YouTube (over an hour long)

Monday, October 22, 2012

OpenMRS style tip: how you should expose module Global Properties

I've seen OpenMRS modules expose configurable functionality in about ten different ways. The most common is to expose a global property (e.g. "concept.weight") whose value you set to the primary key of an object in the database (e.g. "5089"), and then have the code littered with things like this:
Context.getConceptService().getConcept(Integer.valueOf(Context.getAdministrationService().getGlobalProperty("concept.weight")));
This is ugly for a few reasons:

  • a helper method would save a lot of typing, and allow consistent reporting of error messages if the GP is configured wrong.
  • proper unit-testing of any code that calls this requires you to use static mocks (thanks Mario and Alex for harping on this!)

Today Mark and I sat down (virtually) for a pairing session and wrote out our proposal for the "right way" to do this going forwards. Here's how it works in the EMR module:

  • An EmrProperties class
    • It has strongly-typed getter methods for the module's settings
    • These getters are not static. The class is annotated with @Component, so we can autowire it into services that need it.
    • This contains settings configurable via GP and hardcoded constants (so developers don't have to think about whether to look in a "properties" file or a "constants" file, and so if an initially-hardcoded setting is made configurable, this don't require structural refactoring)
  • A ModuleProperties helper class, so other modules that depend on the EMR module (which will be most PIH modules going forwards) don't have to reimplement code that fetches an object from the database based on a global property.
    • eventually we'll improve this to support PKs, UUIDs, names, etc.
The bolded point above is the key difference from things we've done before. So to use this class, we autowire an EmrProperties object (for production), providing a setter for testing. This lets us use the class in our code as conveniently as if we were making static calls, while letting us mock the EmrProperties object and inject it into our service, letting us write proper unit tests.

Thank you Keelhaul!

I wanted to highlight a cool little thing that we got from OpenMRS's open-source community.

Back in late 2008, when dinosaurs still walked the earth, Firefox was on version 3, and we had just elected a guy named Barack Obama president, we had an email thread around Location Hierarchies (at the time, there was no OpenMRS mechanism for saying that one location was inside another one) and we were debating whether we also needed location tags. (I said we did, though for the wrong reasons.)

So, along came a developer named Andrey Koshushkov (who goes by the nickname Keelhaul, and was at University of Göttingen, and I think worked with Ghislain Kouematchoua). Andrey implemented the location hierarchy and tags, which made it into OpenMRS 1.5.

Almost four years later we are finally getting to use the location tag feature at PIH for the first time. And we're very thankful that it's been sitting there, implemented well, just waiting for us. So, if you're reading this, thanks Andrey!

PS- Since I was just giving Burke props in my last post for having been very right about Visits, and how flexible they need to be, I wanted to balance that by pointing out something he was wrong about (I had to look back 4 years!); but when I read the whole conversation, I see that he wasn't wrong. :-)

Visits vs Visits

It's been a long time since I've posted anything. My apologies, I blame travel: two weeks in Porto Alegre doing a kickoff with the Thoughtworks developers, and a week at the OpenMRS Implementers meeting in Manila were pretty time consuming. But I promise to get back on track!

I've just spent a couple of days working on our API around Visits and ADT, and I was surprised to find that we're not going to be using visits on the Mirebalais project the way I'd assumed they would work when we built them in OpenMRS.

Scenario: a very sick patient shows up at the outpatient clinic, checks in, sees a doctor, who gets him admitted to the inpatient ward, where he stays for two days.

While we were building visits in OpenMRS, I was assuming that we'd represent this with two visits:

  1. visit type = "Outpatient", location = "Outpatient Clinic", start = 9am, stop = 10am
  2. visit type = "Inpatient", location = "Inpatient Ward A", start = 10am, stop = 2 days later
However the clinical and M&E teams really want us to represent this as just a single visit. Which makes sense if you're not wearing a developer hat. :-)

So instead we'll represent that scenario as:


  • One visit, with location = "Mirebalais Hospital", start = 9am, stop = 2 days later
  • An encounter of type = "Check In" at "Outpatient Clinic" at 9am
  • An encounter of type = "Outpatient consultation" at "Outpatient Clinic" at 9:30am
  • An encounter of type = "Hospital Admission" at "Inpatient Ward A" at 10am
  • An encounter of type = "Hospital Discharge" at "Inpatient Ward A" two days later
We've started to capture the business logic (up through check-in at time of writing) in an ADT Service (interface, implementation, unit tests) in the EMR module.

I actually think I like this better than the approach I'd originally assumed we'd take. And I'm really happy that our implementation of Visits in OpenMRS is flexible enough to let us model our business logic however we want. (Props to Burke for constantly reminding us of that!)

Friday, September 28, 2012

Build a complex OpenMRS system as a team

On this project we're building a set of integrated modules, and we expect all devs to be running nightly builds of them:

  • mirebalais
  • emr
  • pacsintegration
  • patientregistration

We're also depending on other OpenMRS modules that we usually use official released versions of, but occasionally we need nightly/snapshot builds. We're currently using snapshots of idgen (waiting for 2.4) and metadatasharing (waiting on META-264).

We also have quite a few devs working on this project. And this is the first time we really have multiple devs working on multiple interrelated modules. (We've had multiple devs working on a single module, and one dev working on overlapping modules, but this project combines the two!)

The lesson learned:
Don't ask every dev to always be get and build updated code for 4+ maven projects. Instead have the CI server deploy snapshot versions of those modules to a snapshot repo. (We're using the OpenMRS nexus one.)

So, if I'm working on one aspect of the code, I do a "mvn install" and it fails due to a change someone else has made in one of the modules I'm not working on at the moment, I would immediately just try "mvn install -U" (that forces an update of snapshots), and generally that will work.

Friday, September 21, 2012

More CI

Sorry for the lack of updates recently, we've been struggling with a bunch of software integration issues that are really not so interesting to blog about.

But finally, for the first time, we have all our modules in CI, and passing!


What we were hung up on:

We've got a showcase coming up on Tuesday where we want to show both the Patient Registration module, packaged up for Mirebalais, and a proof-of-concept of sending messages to a PACS server, so...back to work!

Thursday, September 13, 2012

CI!

One software development best-practice is that the first thing you do on a project is set up Continuous Integration or CI.

The idea is that every time a developer commits any code, all components of the project are built and "integrated" together, tests are run, and any failures are reported. As a result, if I make a change to one part of the project that happens to break a different part, I am notified within minutes, so I can fix it ASAP, before it inconveniences the other developers.

So, on this project we were slightly behind in getting this set up, but we're moving on it now:

Expect to see an automatically-updated demo server soon. It will initially be very boring, since the point is to do CI set up before you have any actual functionality. :-)

PS- As a dev-ops note, when you configure Bamboo to use maven on Ubuntu 12.04, you should set the path to "/usr/share/maven", and not to "/usr", because while both of these have the maven executable at the relative path of "bin/mvn", only the first is the proper maven home directory.

Tuesday, September 11, 2012

Distribution vs Single Installation

A random developer note:

While dicussing CI today, I noticed a key difference between the work I did on the Kenya EMR project, and what we're doing for Mirebalais: Kenya EMR was really about being an OpenMRS distribution, whereas Mirebalais is a single installation.

In Kenya EMR I did a bunch of work around having a maven target that fetched all module dependencies to produce a single zip file that could be distributed to a very large number of servers.

In Mirebalais the point is to use a CI/CD approach to maintain a small number of demo and production servers directly, without the need for a distributable artifact.

Nothing earth-shatteering, just this hadn't occurred to me before.

Saturday, September 8, 2012

Haiti Trip, August 20-24


Two weeks ago, Evan, Neil, Emerson, and I kicked off this project with a launch trip to Haiti.  While Evan has traveled to our Haiti sites many times, it was the first visit for the rest of us.  Brittany Eddy, our Haiti Informatics Project Coordinator, did a wonderful job of accompanying us throughout the visit – arranging tours and interesting conversations with a variety of stakeholders, and ensuring we were fed, rested, and transported around when needed.  Below, I’ve outlined a few of our primary objectives for the trip, explained how we accomplished these objectives, and added in some photos to paint a picture of the experience.

Seeing our sites in action drove home a lot of what I’ve learned at PIH about the immense challenges our colleagues face daily providing high-quality care in rural Haiti.  Observing the determination of our providers and the vibrancy of the communities with which we work set the stage for our team to return to Boston eager to jump into the Project Inception week (photos here, and described by Darius in an earlier post), communicate what we learned with the rest of the group, and begin building our - dare I say it - audacious EMR.

Objectives

What: Gather context on current service delivery in Haiti
How:
  • Visited two Zanmi Lasante sites - Lacolline Hospital and Cange.  Cange is PIH’s original site, and Lacolline is the site currently managing our patient registration module pilot (keep an eye out for future posts on this project).
  • Engaged in conversations with physicians and nurses about how care currently happens and how patients access our existing hospitals and clinics.

In Action:
Patient registration at Lacolline
Paper archives at Lacolline
Pharmacy window at Cange
Queue for registration at Lacolline

What: Understand expected Mirebalais Hospital patient flows
How:
  • Took an extensive hospital tour with Dr. David Walton.
  • Shot numerous photos and videos of the buildings.
  • Mapped standard patient visit processes to identify data collection points.

In Action
Mirebalais Hospital Tour
Mirebalais Ward

What: Gather clinical requirements
How:
  • Met with physicians, nurses, laboratory technicians, pharmacists, security managers, and administrators about their daily tasks, biggest challenges, and hopes for how an electronic system could support their responsibilities.
  • Considered important clinical indicators reported from our sites.
  • Discussed common scenarios during routine and emergency care in rural Haiti.

In Action:
Pharmacy at Lacolline
Lab at Lacolline

What: Begin prioritization of requirements
How:
  • Gathered feedback on our own synthesis of clinical requirements.
  • Asked stakeholders to choose requirements for our Minimum Viable Product – termed “MVP”
  • Encouraged collaboration and healthy debates over components of the MVP.

In Action:
Lauren Spahn, Naomie Marcelin, Pierre Paul, David Walton, Evan, Emerson, Neil

Thursday, September 6, 2012

Visas and Flights

Two things:

  1. I can't say enough good things about Kayak, but when you're trying to fly SEA-POA-MNL-SEA, you really need a travel agent. :-)
  2. Brazil's "reciprocity" approach to visas is endearing, and I support the concept. But I'd really like to get those hours of visa paperwork back.
Regularly scheduled programming will resume tomorrow. Hopefully we'll have something interesting to say about putting several dependent OpenMRS modules into CI.

Tuesday, September 4, 2012

Dev links

A few links for devs:

Inception (not the movie)

To give a quick picture of where the project stands...

Two weeks ago Evan, Renee, Emerson, and Neil spent a week in Haiti, visiting existing ZL hospitals, as well as the grounds of Mirebalais, meeting staff to develop user personas, and a tentative project road map. I'll let them share more details, including Renee's great video walkthroughs, showing the outpatient and women's health workflows from a patients-eye perspective.

Then we got our whole team together for a week in Boston, to do an "Inception" for the project. You could call it a "quick-start" or a "kick-off" if you prefer. Or you could think of it as...

These people...

Working by day...
And by night...


To produce a lot of sticky notes and index cards...

This week and next are "Iteration 0," as the BAs pull together all this information, and we get our development processes and continuous integration set up. So, expect to see lots of development ramp up in a couple of weeks.

If you really want all the details, watch PIH's github repositories. :-)

Hello, world!

Hello, world!*

Partners In Health and its sister organization Zanmi Lasante are building a state-of-the-art teaching hospital in Haiti, l'Hôpital Universitaire de Mirebalais. As part of our push to bring the highest level of care public facility in Haiti, we are building an OpenMRS-based Electronic Medical Record for the hospital.

The Mirebalais EMR will draw on the PIH informatics team's extensive history building medical record systems for developing countries, as well as recent work on packaging an EMR for I-TECH and the Kenyan Ministry of Health.

Further, to deliver the best possible software product, in the shortest possible time, we are partnering with the Porto Alegre, Brazil office of ThoughtWorks, experts at agile development of custom software with a passion for social justice.

This blog will narrate the software development collaboration, so you'll surely see mockups, screenshots, and code snippets. But we promise to have less nerdy content as well!


* = "Hello, world!" is the prototypical output of the first program you would ever write as a software developer in a new programming language, or framework.