Tuesday, June 28, 2016

A Review of the iPad Pro 12.9"

Every year I get myself a present with my annual bonus (in years that I get a bonus, which has been most of them in my career).  This year, I decided to replace my quite functional iPad 2 with an iPad Pro.  I had several reasons for upgrading, one of which was to make sure that my mother would be able to enjoy my wife's iPad (she got my old one, and hers went to my mother), and because I wanted the bigger display and the multitasking support.  I'm often trying to do two or three things at once.

What I like:  The bigger foot print means that it is easier to read and operate.  My high score on one of my video games went up because I can be more precise with the larger display.  And my aging eyes like the bigger screen for reading, which I use my iPad for quite a bit.  It's also easier to see the map while driving.  The multitasking isn't quite all I could ask for, but it beats what I used to have, which was nothing.  I also like what the bigger display does for helping me to manage my schedule.

What I dislike: The bigger foot print means that it is harder to carry around, and nearly impossible to use one handed.  I think I need a different case to be able to use it nearly the way I used to use my iPad while I was moving around on foot.  I use my iPhone now for mapping my way through the city while walking, and I don't carry it around with me everywhere.  That actually is a bonus, because I'm less likely to bury myself in it at the dinner table though.

I haven't spend any money on the pen or a keyboard case, I already have two blue-tooth keyboards that will work with it (I recently acquired a slim blue-tooth full sized keyboard for my travel set up). I may get the pen later just to play with it.

I put it in a Targus case very much like the one I use to have that I finally wore out, and am quite happy with the case, but am still getting used to the fact that the fold is about 1/3 the way in the back, which means I don't use it physically the same way as my old case.

The form-factor is different enough that my use of the device is different, but not substantially so for my needs.  Overall, I think I like it, but kinda wish I'd gotten the smaller iPad Pro in the original form factor that I've been accustomed to.

I'm probably going to spend some time playing with it as an external monitor for my laptop, to see how well that works for me.  I'm presently carrying around an AOC external USB display when I travel that has more screen, but less pixels.  Using my iPad for that could shorten my treck through airport security.  At least once over the past two weeks I had five bins filled through the TSA line, using my iPad for the same purpose could drop that by two, and get my travel computing back into one bag.


Wednesday, June 15, 2016

Apple IOS10 to support HL7 CDA in HealthKit

This is pretty big news from Apple for HL7, and something I'm rather feeling a bit proud of myself, even if I only played a small part in getting us to this stage.

Thanks to Andy Stechishin of the HL7 Mobile Health Workgroup for spotting this one and bringing it to the attention of HL7 Membership.


P.S. Now if we can get the technology giant to pay attention to FHIR, my day will be made.

Thursday, June 9, 2016

In the Rough

The thing I like about coding is that at its best, it is a fluid way to solve problems.  The best code to work on is the crux of a solution.  The scaffolding is boring.  The shell and outer layers is finish work.  I can do a craftsman-like job on it, but frankly it is boring.  The fun is in figuring out some new way to solve a tough problem, and what I really like to work on is the heart of the problem.

I think the most fun for me is teasing out the solution.  Often I feel as if I can see in my minds eye how the solution is hidden behind or within a rough crystaline structure.  I look at it, pick it up, turn it, shine light on it from different directions, see which way shadows are cast and more.  At some stage, I catch just a wee glimpse at the right angle, and that narrows down some options.

Eventually, I see just enough to know where to put the knife, and cut.  And cut again.  When I started I had only a small clue of the basic shape, but when I finish ...

Image via Smithsonian.com

Tuesday, June 7, 2016

Selecting Population Cohorts in FHIR using extension search parameters

The basic idea is pretty straightforward, right?  What you want to be able to do is identify a set of patients which meet a particular criteria.  Give me all 18 year-old or older men with a diagnosis of X, and a lab test Y where the result is > Z units.  The question is how to express it in FHIR.

If it were SQL, what you would want to say is something like this:

WHERE P.DOB <= EighteenYearsAgo 

Of course, I just made that database schema up, but the reality is that it probably in not all that far from what you already know.  However, we don't write FHIR queries in SQL, so how do we say this?

First of all, the resource we want to return is Patient, so the first thing we know is that we will start with:

GET [base]/Patient?

Because that will return patients.  If we had wanted observation, we'd start with
GET [base]/Observation?

Next, we can add the basic parameters on gender and age to the query.  These will be AND'd together, giving us:

GET [base]/Patient?gender=male&birthdate=ltEighteenYearsAgo

But the next bits are tricky.  In fact, not really possible in FHIR.  I can find those Conditions where the code is X, and I can find the results where the code is Y and the value is > Z, and I can even get the patients associated with each.
But I cannot get the patients AND'd with these two JOINs.

For example, finding the patients in addition to the conditions where condition.code = X:
GET [base]/Condition?code=X&_include=Condition:patient

And finding results where the code is Y and the value is > Z
GET [base]/Observation?code=Y&value-quantity=gtZ&_include=Observation:patient

But as I look at it, _include isn't a JOIN, instead it is a query with multiple results. Chaining doesn't cut it either.  It supports joins in the forward case, but I want the reverse.  I'm thinking about a syntax something like this:
GET [base]/Patient?gender=male&birthdate=ltEighteenYearsAgo&_join=Observation:patient,Condition:patient&Condition.code=X&Observation.code=Y&Observation.value=gtZ

In the above, the _join argument establishes the join critieria, Observation's patient search  path has to reference the found patient, and the same with Condition's patient search path. The reality is we could probably automatically figure out the join criteria in this case, since there's only one thing in Condition or Observation that would really point to a patient. So, Observation:patient, and Condition:patient are (or could be) implied.  I changed the search parameters a little bit and what I get is:
GET [base]/Patient?gender=male&birthdate=ltEighteenYearsAgo&patientCondition-code=X&patientObservation-code=Y&patientObservation-value=gtZ

Now I have three search parameter extentions: patientCondition-code, patientObservation-code and patientObservation-value which I can use in my profile for patient, and with these, I can do the necessary joins in my happy little server.

I might prefer a little less verbose syntax for these parameters (e.g., condition-code rather than patientCondition-code), but for the most part, the principles are still the same.


Monday, June 6, 2016

The Nightly Dumb

There's a design pattern that is often used in healthcare, and in other enterprise integrations with legacy that involves a nightly update.  What often happens is that a days transactions get aggregated into a single big file (by taking a dump of the days work -- which is why it is called the nightly dump).  Then this single big file is sent off somewhere else to be processed.

It's a convenient design.  It doesn't have to deal with transactions or locks .. the days work  is already done and nobody is using the transactional system.  It won't impact your users, because the day's work is already done and nobody is ...
Batches of stuff can be processed very quickly because the days work is ...

You get the picture.

This is such an often used design pattern, and for some reason, it just feels wrong to be so widely used.  There's a heuristic being applied ... that at the end of the day, the day's work is done, which we know isn't really the case.  There's always that one record or two which we never got back to which needs to be updated.  Yet we often act as if the heuristic is in fact a law of physics, only to discover to our chagrin that it is not, and we need to account for that in our processing of the nightly dump.

Often times when I see this pattern in use, I question it.  The answer I typically get is "this is more efficient".  And yet, I wonder, is it really?  Is it more efficient to wait to send something?  Or is that a premature optimization?  Is the delay between sending it, and subsequently finding a problem with it that needs to be corrected really the best way to handle this situation.

One thing I can tell you. The nightly dump is interfacing, not integration. When you have to wait 24 or 48 hours for the nightly dump to have taken place before another system can act on what was done elsewhere, someone is bound to notice somewhere.  And when they do, they are sure to start swearing.  Because at that point, the nightly dump will basically feel as if someone just took a dump on them.


P.S.  In case you were wondering, yeah, I just got dumped on.

Friday, June 3, 2016

Boundaries around Task, MessageHeader and Operation in FHIR

On this weeks FHIR Workflow call, we discussed the similarities between the task and message header structures in FHIR.  The notions we associate with messaging, services and tasks have a lot of overlap, and Lloyd asked us to consider these for next week.

Taking a crack at this:
Task as a resource is a record of things to do or what has been done.
Messages are a record of things that have been communicated.
Operations are a record of services to perform.  We don't really have a resource that records the invocation, what we have is a resource (OperationDefinition) that describes HOW and invocation can be performed.

Communications are part of "things that can be done", and communications can initiate other activity.

The focus of the MessageHeader resource is to ensure that a message gets where it needs to go in a timely, consistent and predictable fashion.  To do that, it needs to know the source and destination of the message, the content of the message, and other critical details needed to ensure appropriate routing with respect to timeliness, consistency and predictability.

The focus of the Task resource is to capture the status of activity in providing a service.  This enables the steps required to to perform a service to be monitored, managed and adjusted as necessary to optimize service delivery.  To do this, the task resource needs to know how a task is composed, what actors may be engaged in or affected by the task, and the parameters that may affect the performance of the task.

An operation initiates a service, and the OperationDefinition resource describes how that service is called.  To do this, it describes what the service does, and the parameters that might affect its behavior.

In system automation, we should realize that ANY detectable event can be used as a trigger to invoke activity.  Thus, FHIR based systems might use any of three ways above to automate the delivery services.  Receipt of a message could trigger activity.  Creation (or update) of a task could trigger activity.  Invoking an operation could trigger activity.  And even creating or updating a resource could be a way to invoke activity.

So, we should be looking at design patterns for the invocation of services. Messaging, services, and workflow are all design patterns that can support this.  Invocation of a service is performed by "binding" that service to an event, where event could be described as the "discovery" of a resource meeting certain criteria.

  1. In Message oriented system, the arrival of a message is (or leads directly to) the invocation of a service.
  2. In a Task oriented system, the update of a task can lead to the invocation of a service.
  3. In an operation, the invocation of an operation leads to (or simply is) the invocation of a service.

All three can be used together.  A message can be sent from one system to another saying "Please perform this service".  Upon receipt of the message, the receiving system can create a set of tasks which must be performed to manage the activity.  Upon creation of one of these tasks, another component monitoring its task queue can invoke activity directly using one or more operations.

One of those invocations could then involve communication with another system, requiring messaging to communicate a request to perform additional services (e.g., a lab, on obtaining a positive identification of Type A Influenza might request subtype evaluation from a reference lab).

On boundaries, messaging is used when what needs to be monitored or ensured is the communication activity.  Tasks are used when what needs to monitored or managed is the workflow activity. Operations are used to invoke behaviors directly.

With RESTful approaches, the essentials of messaging are already addressed within the HTTP or HTTPS layer in terms of routing and communications.  However, more complex messaging scenarios can use FHIR Messaging to support more complex communication management.

There also may be cases where where a system needs to both ensure communications, and manage workflows.  In these cases, messaging can be to communicate tasks which reference the activity to be performed.


Thursday, June 2, 2016

Why do we put up with it?

I've so often heard "Why do people put up with bad EHR systems".  Before you go there, for those of you who read this blog, answer me this:

Why do you put up with a system for storing meeting invitations that doesn't have a place to put in a teleconference number and passcode, and a web URL in a way that makes it easy for you to dial into the teleconference, and connect to the screen sharing service?

When you get to that answer, you'll have the answer to your first question.