Tuesday 17 December 2013

Visual Studio stops loading after attempting to open a solution




Ok, so, sometimes when you've been screwing around with a solution (e.g. taking a copy of an offline solution that is normally attached to TFS) and you open it you'll find that Visual Studio chokes and never finishes loading:

And you see the balloon we all love so much

I seem to encounter this the second time I open a solution, the first time it opens fine.  Visual Studio can sit in this state forever.  Or at least for so long that I can't be bothered waiting any longer. Then it needs to be killed via Task Manager.  This kind of sucks.

To fix this you can try deleting the .suo files for the solution.  These are Visual Studios solution user options files.  They are hidden files so you might need to tell explorer to show them:


Send them to the trash and next time you open the solution it might load a bit better.


Monday 9 December 2013

HINZ 2013 Conference - a review in bullet points so I don't have to write coherently or in quantity




  • HINZ = Health Informatics New Zealand. What is an informatic? 
  • Rotorua is pretty cool.  (this is not sarcasm)
  • So many TrendCare dolls my colleagues accused me of acquiring them through illicit means
  • QualIT have really nice free pens, they have a solid, well made feel to them.  Would probably draw blood if thrown at a colleague.
  • Vendors are generally not very good at software innovation.  Wow look at the CSC & Orion booths.
  • There were a lot of managers talking to other managers about manager things. Where my dev buddies at?
  • It was jolly nice to talk to some folk from the other DHBs and to share some knowledge and maybe some code.
  • Conclusion: this is not a conference for developers, you won't learn anything practical, but the food is ok and if you want you can stare at the hot bubbling mud and imagine throwing vendor software in there.

From Wikipedia:  Health informatics is a discipline at the intersection of information science, computer science, and health care. It deals with the resources, devices, and methods required to optimize the acquisition, storage, retrieval blah blah blah no one cares 

Monday 14 October 2013

Professional Achievements - we all like our victories noticed and recorded

I was recovering data off a semi failed disk and found some crap treasure that I had forgotten about. In a previous job I created a 'Professional Achievements' application, which is basically a rip-off of steam achievements but for work.

This is the front page. Observant people may notice that the default MVC template has been hardly changed.



Here's some of the achievements that were earned by me after a lot of dedicated effort:





The app would let you create your own achievements and assign them (or other previously created achievements) to anyone.


You could pick different pre-loaded icons or upload your own.  Most people used the team fortress 2 ones. I suspect this is because they were lazy. I also suspect this is a stunning copyright breach. I think I included any achievement icons that were easily found from a Google search! Please don't sue me Valve!

Here's some other random achievements that could be given out:





Spelling 'achievements' correctly in this post has really been a challenge. The More You Know.
Also, I've made the pictures full size even though it will blow out the layout cause I'm the boss round these parts.

NHI number validation or whatever


Yeah NHI number validation!  There is an exciting document here that the Ministry of Health have supplied which specifies what makes an NHI number valid or not.

Here's a method y'all.

public bool ValidateNhi(string nhi)
{
    bool valid = false;
    const string validNhiChars = "ABCDEFGHJKLMNPQRSTUVWXYZ";
                
    nhi = nhi.ToUpper();
    // must be seven chars long
    if (nhi.Length == 7)
    {
        // must not contain I or O
        if (!nhi.Contains("I") && !nhi.Contains("O"))
        {
            // last four must be integer
            int parsed;
            if (int.TryParse(nhi.Substring(3), out parsed))
            {
                // multiply each character by 8 minus its index and then sum em all together
                // e.g. first char * 7 + second char * 6 + third char * 5 etc
                int result = 0;
                int counter = 7;
                foreach (var character in nhi.Substring(0, 6)) // exclude last character
                {
                    int parsedCharacter = counter > 4 ? validNhiChars.IndexOf(character) + 1 : (int)Char.GetNumericValue(character);
                    result = result + (counter * parsedCharacter);
                    counter--;
                }
                // create a checksum by mod 11
                int checksum = result % 11;
                int checkDidgit = 11 - checksum;
                if (checkDidgit == 10)
                    checkDidgit = 0;

                // last value in nhi number must equal check digit
                valid = (int)Char.GetNumericValue(nhi.Last()) == checkDidgit;
            }
        }
    }

    return valid;
}

I can't deal with the excitement!

Tuesday 1 October 2013

Extracting Data from the Patient Administration System (it's the PAS yo)


You want patient data out of the system?  Sure, sure, let me connect in and write some quick SQL for you.  Let's see what tables we have here.



Ok. This might take a little while. Lets have a look at the field names.



Hmmm.  Well, we'll try the data dictionary.



Ummm, hey look it's home time, let's do this tomorrow.

Tuesday 10 September 2013

Support Queue Monitor


We have a Support II triage system where generally all the issues being sent to level 2 support go into a big (metaphorical) support 2 bucket and then the peeps on support 2 that week look at the issues and either assign them to themselves to resolve or reassign them to someone else (another support 2 person, or level 3 support).

Our issue tracking system is 'not very good'.  That is a direct quote from me when I was using non-cuss-words. To see what issues were sitting in our metaphorical level 2 support bucket you had to load up the issue system, click on a menu, select an item, wait a bit, scroll down, click the next page arrow, expand a thing and ta-da, there they were.  Visibility on what issues were there was lacking. The goal is to keep as few issues in the bucket as possible, they should all be assigned to someone to work on as soon as possible but it was always a bit of a challenge for people to know what was in there.

So I made an auto-refresh dash-board type page.
 

It refreshes every minute and indicates how many issues are there that need to be assigned out.  At the bottom is a list of issues, which link off to the incident tracking system so the issue can be reassigned.  The colour changes depending on how bad the situation is, typically we never get over 5 issues in the queue, we've never seen the red page yet! Go team!


This is an MVC application with a tiny bit of javascript on the front end.  It reads the data out of the issue tracking system from a custom view created on the db.

There is a cat hanging out at the top left of the page, when you hover over the cat it will slide out and give you a link to click on:

This takes you to a statistics page.  You get basic information around how many incidents have been assigned to Support 2 over the last week and what the current status of those incidents is:

Obviously, green/happy cat is best because that mean the incident has been resolved. Blue/ambivalent cat is ok but not ideal; this means the incident has been allocated to someone but it's still open.  You want to avoid red/wet cat which means the incident is sitting there waiting to be assigned to someone - it won't get fixed if no one is looking at it! It's depressing to see a lot of wet cats around.

If you mouse over an incident in the graph you'll get some pretty basic details:

There is a second tab which shows a list of peeps with the count of issues currently assigned to them; clicking on a person will give you a basic break down of issues they have:


And that's enough writing for one day.





Tuesday 3 September 2013

A change is as good as a rest

We have a change control document that needs to be filled in before changes can be done to produciton systems.  This is not a technical document, but since the PMs throw their hands in the air and say "I don't know how to do those!" the technical peeps end up filling in fields in a non-technical document, trying to answer questions they don't know the answers too. We all know the best thing to do in that situation is to either guess, or be a smart ass.








A slightly more in-depth review of Rhapsody 5.4

We recently moved from Rhapsody version 3 to version 5. Here's some tedious info about that.

From what I can tell Rhapsody is made-up of 3 components

  1. The engine that does the message processing - Windows service
  2. The IDE (development environment) - Windows application
  3. The Management Console - web page
The engine is a black box so who knows what improvements have been done there.  It's always been pretty good, so let's just assume it hasn't got worse.

The IDE looks and feels the same, so has continued with the kind-of-clunky-user-interface and random-weird-errors way of working.  This is kind of expected because it's just developers that use this and no one cares about the development experience because:
  1. This stuff is complicated & hard to get right
  2. No one listens to developers as they will generally complain anyway, because they are basically the high tech version of the boy who cried wolf
There is some new functionality here (e.g. webservices communication points) but whatever, this is pretty much the same as version 3.  It's still pretty buggy; in version 3 I have had deployments to production fail and leave components in weird states, and then had the IDE refuse to restore backups (giving random errors). I haven't encountered bugs that big yet but have seen some along the lines of:
  • When changing the value of a variable an error occurs and the new value is not pushed out to the components (restarting the service fixed this)
  • An altered property on a checked out component is forgotten as soon as the component loses focus (restarting the IDE fixed this)
Yep, still looks like crap


The Management Console is where a lot of improvements are noticeable.  
  • When looking at com points/routes you can collapse folders and apply filters - no more scrolling around like an idiot (well, less of that at least).  The state of this (whether a folder is collapsed) is remember when coming back to the page.
  • In the old version the refresh on com point front page was a bit rubbish and would scroll you to the top of the page and make you lose your place etc.  It is now very seamless and continuous (seems to bring in updated data every few seconds via ajax calls).
  • There's a heap of automated (but configurable) warnings and errors that give you a good idea of the current state of the engine.
Makes it easy to see problems so that I can then pretend I haven't seen them and hope my colleagues fix 'em
  • When you log in it never tells you "Your security key session has expired; please log-in again. " and makes you log in again!
  • The component details view is pretty cool now. Mostly the same information but better organised and accessible.

  • When looking at a message, navigating to different messages is a lot easier e.g. you can now do a single click to go to the next or previous message. Yeah that's right. You don't need to mash that back button till you get back to the search then clown around trying to find the message id you were just looking at and then click the one before/after it. 
The expertly placed red triangles indicate where you can click to move to different messages
  • It doesn't look like total pants anymore.

Lessons learnt from migration

  • If you have communication points that are polling a database, when you move them to a new machine they will not remember the last polled value.  This is bad!  This means they basically start from the default value again.  You can update this from the management console under the Database Key Values menu.
  • There will be some small errors generated when moving to the new version (like some directory com points will need the configuration updated - I think additional validation has been added) but all can typically be easily worked through.
  • It will take you eternity to move all your crap because no one knows what it does, how to test it, or how to gain access to the systems that are sending/receiving the messages to change their configuration.
  • You're going to need some training in the new version.  This is kind of a guess, because I haven't had any training yet and there's a whole heap of stuff happening that I don't understand (like com points have input and output queues now) - I am assuming training would fix that.

Monday 2 September 2013

Monday 5 August 2013

Hospital at a Glance - making it kind of different


Everybody seems pretty keen to get their own hospital at a glance system - if you can quickly see the state of the hospital then you can see any problems and you can juggle your resources (um, you know, nurses) as needed.  We know of a few hospitals that have recently implemented a hospital at a glance system, so we visited them and stole all their ideas.

Typically the main page of the system is a bar graph where each bar is a ward in the hospital, each level within the bar is a bed.  You can then colour code the bed based on the speciality and maybe add overlays and things for other information (e.g. is the patient past their expected discharge date). So we ended up with something like this (based on development data - so not good data):

Isn't it pretty? Aren't we clever? Everyone was pretty happy with this, until my collegue said that most of the page was taken up with occupancy stats (they are not that important) and all the important information (like hours and resourcing variance (e.g. are the peeps working to hard and do we have the right kind and enough of the peeps)) was crammed down the bottom and not that visible/accessible.  It also kind of skews things because some wards are bigger than others; just because they have more patients in the ward doesn't really mean anything as they also have more available beds and resourcing.

Pssssh, whatever. What a jerk. Well, hmmm, maybe he's right.

So then I got to have some fun with CSS.


We threw the graph away and we ended up with tiles.  Interestingly, pretty much everyone who looked at it initially hated it, but after a while using it changed their minds and decided it was the bee's knees.  There are a few variations of tiles depending on the type of ward (e.g. emergency departments have their own tile).

Each tile represents a ward; this is the tile for ward W2B:


The coloured bars along the top and the bottom (the two purple horizontal strips) indicate the over-all health of ward based on simple questionnaires that can be filled in by clinical staff - so this is basically the opnion of the people on the ward as to how their ward is doing. Each question they answer has a weighting and can move the ward through these colours:


The above screenshot is purple, so they have extra capacity. This indicates resources could be taken from the ward and moved to another ward that is struggling.
Questionnaires are entered using the + and past quesionnaires can be viewed using the ?.


The hours variance comes from TrendCare.  A positive amount is good meaning the nurses are under allocated and have some spare time, a red negative amount means the nurses are overallocated and need assistance.


When you hover over the specialties it will give you a breakdown of the bed count for that specialty:


And there is a key for the specialties at the bottom of the page





Tuesday 18 June 2013

The Rhapsody 'Database Message Extraction' filter is a bit pants but you have to use it anyway

We were in a situation where we needed generic patient information to be displayed in an application. How do we get this info and maybe make it so the process can be reused by other applications? Everything else in the hospital goes through Rhapsody, so can we do this in Rhapsody? Seems easy enough; send a message to Rhapsody and say 'hey, I want data for ward ABC', then Rhapsody runs a query against the database and sends back all the required information as an XML message. Man, that was so easy, this integration stuff is a piece of cake.

The Rhapsody Database Components Best Practice Guide says "If you want to retrieve data from a database table for every message it receives, use the Database Message Extraction filter."

I'm a good little developer. I read the documentation. I believe in the documentation. So, when we use the Database Message Extraction filter we are following best practice. Good for us!



Unfortunately the Database Message Extraction filter has some problems. It seems that when the Rhapsody route that holds the DB Message Extraction filter starts up it connects to the database at that point. It then holds that connection open. If that connection is disrupted for some reason (network issues, database goes offline then comes back online) the DB Message Extraction filter WILL NOT be able to recover that connection. It will remain in a broken state until the Rhapsody route is restarted. When this happens you will see errors like this:



We have a lot of outages. We have upgrades, we have server moves, we had a server room move. The connect gets broken between the Rhapsody server and the database server a lot. We have backups that can potentially make servers unavailable for very short periods and so for a while we were in a situation where every night the Rhapsody route was breaking and having to be restarted. The system engineers managed to solve the problem with the backups but we still have the problem every time the database becomes unavailable.

So, I don't have a good solution to this. If you want to follow the pattern of extracting and returning data for each message sent then you have no choice but to use the DB Message Extraction filter (according to Rhapsody support). I reckon you should avoid using it if at all possible. If not, you might be able to work around the problem by scheduling a route restart (if your version of Rhapsody will allow this - ours doesn't). If you have this problem I don't really have any help for you, I'm just letting you know I share your pain and advising everyone else not to use this filter.

Tuesday 4 June 2013

Calling the command line from Rhapsody - the Execute Process Filter


Also known as 'why can't my version of Rhapsody manipulate files'.

We had a situation where we needed Rhapsody to move a file from one location to another location. There does not appear to be any file system manipulation components available in Rhapsody so we settled on this guy:


So the Execute Process Filter (what is it filtering? heck if I know) seems to have been designed to run programs or scripts. Which is cool. You could write custom apps and then trigger them from Rhapsody. But I'm far too lazy to write my own exe if I don't have to. I'd rather just pass cmd.exe the appropriate arguments and make it do the work for me.

There are a couple of questions that need to be answered to make this filter more useful:

  1. How do you access the command line and get it to run a given command?
  2. What if the command is dynamic? e.g. you're building the command in a javascript filter and then want to run it in the command prompt.

Getting a cmd.exe to run a command


This is easy(ish), the executable you want to call is just cmd.exe.  So you can enter the full path to it, along the lines of c:\windows\system32\cmd.exe
But, we want to pass a specific command to cmd.exe so that cmd.exe will run it.  Do this using the /C argument that cmd.exe accepts:

c:\windows\system32\cmd.exe /C [your command here]

Cmd.exe will treat the string that you pass in as a command and run it in the same manner it would if you had typed it in a command line window.

Building a dynamic command and then using that in the Execute Process Filter 


In a Javascript filter that runs before the Execute Process Filter you need to build your command and then save it so the Execute Process Filter can use it later:


We build up our command in the moveCommand variable, and then we save it into the moveCommand property against the message.

Then in the Execute Process Filter properties we use that moveCommand property by surrounding it with $ signs.