Posts
  • Eric Gunnerson's Compendium

    A brief, incomplete, and mostly wrong history of programming languages

    • 0 Comments

    A brief, incomplete, and mostly wrong history of programming languages

  • Eric Gunnerson's Compendium

    Thoughts on “Thoughts on TDD”…

    • 0 Comments

    Brian Harry wrote a post entitled “Thoughts on TDD” that I thought I was going to let lie, but I find that I need to write a response.

    I find myself in agreement with Brian on many points in the post, but I disagree with his conclusion.

    Not surprisingly, I agree with the things that he likes about TDD. Focusing on the usage rather than the implementation is really important, and this is important whether you use TDD or not. And YAGNI was a big theme in my “Seven Deadly Sins of Programming” series.

    Now, on to what he doesn’t like.

    He says that he finds it inefficient to have tests that he has to change every time he refactors.

    Here is where we part company.

    If you are having to do a lot of test rewriting (say, more than a couple of minutes work to get back to green) *often* when you are refactoring your code, I submit that either you are testing things that you don’t need to test (internal details rather than external implementation), your code perhaps isn’t as decoupled as it could be, or maybe you need a visit to refactorers anonymous.

    I also like to refactor like crazy, but as we all know, the huge downside of refactoring is that we often break things. Important things. Subtle things. Which makes refactoring risky.

    *Unless* we have a set of tests that have great coverage. And TDD (or “Example-based Design”, which I prefer as a term) gives those to us. Now, I don’t know what sort of coverage Brian gets with the unit tests that he writes, but I do know that for the majority of the developers I’ve worked with – and I count myself in that bucket – the coverage of unit tests written afterwards is considerably inferior to the coverage of unit tests that come from TDD.

    For me, it all comes down to the answer to the following question:

    How do you ensure that your code works now and will continue to work in the future?

    I’m willing to put up with a little efficiency on the front side to get that benefit later. It’s not the writing of the code that’s the expensive part, it’s everything else that comes after.

    I don’t think that stepping through test cases in the debugger gets you what you want. You can verify what the current behavior is, sure, and do it fairly cheaply, but you don’t help the guy in the future who doesn’t know what conditions were important if he has to change your code.

    His second part that he doesn’t like backing into an architecture (go read to see what he means).

    I’ve certainly had to work with code that was like this before, and it’s a nightmare – the code that nobody wants to touch. But that’s not at all the kind of code that you get with TDD, because – if you’re doing it right – you’re doing the “write a failing tests, make it pass, refactor” approach. Now, you may miss some useful refactorings and generalizations for this, but if you do, you can refactor later because you have the tests that make it safe to do so, and your code tends to be easy to refactor because the same things that make code easy to write unit tests for make it easy to refactor.

    I also think Brian is missing an important point.

    We aren’t all as smart as he is.

    I’m reminded a bit of the lesson of Intentional Programming, Charles Simonyi’s paradigm for making programming easier. I played around with Intentional Programming when it was young, and came to the conclusion that it was a pretty good thing if you were as smart as Simonyi is, but it was pretty much a disaster if you were an average developer.

    In this case, TDD gives you a way to work your way into a good, flexible, and functional architecture when you don’t have somebody of Brian’s talents to help you out. And that’s a good thing.

  • Eric Gunnerson's Compendium

    101110.11

    • 9 Comments
    101110.11
  • Eric Gunnerson's Compendium

    The power of "ouch"...

    • 0 Comments

    From my bicycle blog...

    Three of them

  • Eric Gunnerson's Compendium

    The power of "no"...

    • 3 Comments

    Eric Brechner wrote an interesting post titled "Don't panic", about how to deal with requests. I sometimes agree and sometimes disagree with what Eric writes, but it's usually a pretty good read.

    In this case, I agree with his approach, but disagree with his advice.

    He advocates that when anybody comes with you for a request, your first response should always be "Yes, I'd be happy to help".  Which is wrong.

    Way back when my daughter was 4, I was sitting on the couch and she came and asked me for something. My memory is a bit hazy on what she asked for, but I think it was some sort of snack. I didn't think deeply about it, and made a quick decision and answered "no". She got pretty upset and started crying and asked again.

    At that point I had a quandry. I thought about it a bit more, and realized that her request was a reasonable one (lunch was a long time ago) and that there was really no reason to grant it, but I knew that I couldn't because at that point is wasn't about the request but instead was about the way it was asked and the pattern me changing my mind would set. I didn't want to set up a behavior where getting upset and crying is expected to make your dad change his mind. So I held firm.

    And felt really bad about it, because I made the wrong initial call.

    The whole point of the story - assuming that there is a point - is that you need to look not at the current interaction but instead at the meta-level. What message is your response sending? What pattern of interaction is it reinforcing?

    If somebody comes and asks you to do something and you say, "yes", once that word leaves your lips you can assume that the person making the request will think that they are getting everything they want from you. They have in mind a big feature delivered on an impossible date, and your job then becomes trying to scope their expectations down to something manageable, but even if you succeed they'll still be stuck on what they originally had in mind, and will be disappointed with you.

    Not to mention the fact that if you immediately say "yes", it seems like you're either working on unimportant stuff and/or not working hard enough.

    The right thing to do is to say "no", but in the right way. Something like "I'm/we're currently booked and have a lot of high-priority work planned, so I think that would be hard to fit in, but let me understand exactly what you're asking". That puts the requester in the position of having to convince you of the importance of what they want to do and be able to explain the details coherently.

    At that point, you can start the nuanced discussions that Eric talks about - talking about the details of what they want, why they think it's important, etc. It becomes very clear very fast whether the requester has done his homework, and if they haven't you can point out the additional things he needs to figure out before you talk more. If he has done her homework, you can discuss where you think the feature ranks (always subject to the approval of whoever approves features), and how it might be broken apart/modified to bring it in earlier.

    At that point, the answer usually becomes, "yes, we can do <x> in timeframe <y>", which makes the requester happy - you've spent the time to explain to them why you put a specific priority on the request and you've made your life harder by rearranging things to slot their request into your current schedule and (probably) taking on the task of explaining to everybody who's below the new feature's priority why they aren't getting what they expected.

    And, you've set up a healthy pattern of behavior. Requesters are likely to do a bit of homework before they talk to you, and you are busy but willing to take on new work if it makes sense to do so.

    And that's the power of "no".

  • Eric Gunnerson's Compendium

    Lucky

    • 0 Comments
    Sometimes you're in the right place at the right time...
  • Eric Gunnerson's Compendium

    HealthVault 0908 SDK Highlights…

    • 0 Comments

    The 0908 SDK has dropped, and I’d like to talk about some of the highlights of this release.

    SODA

    The big one is SODA, our name for the ability to write non-web-based HealthVault applications. SODA leverages our master/child application infrastructure in a different way, and will provide some nice additional capabilities (I have an app or two that I want to write using it). More details will follow in the near future.

    I should note that our current plan is to require SODA apps to deploy with a HealthVault redist package, so that it will be possible for us to service the SDK assemblies through Microsoft Update. We’re working on that but it’s not ready quite yet.

    Application Creation

    In previous releases, it was a bit cumbersome to create a new application – you had to create the certificate, upload it, copy helloworld, and update the web.config appropriately. We have extended the HealthVault Application Manager to make this easier.

    If you just want to clone HelloWorld to try something out, you can choose “Create New HelloWorld Sample”, select VB or C# as your language, decide where your project should live, and you’ll be ready to hit F5 in VS to run the project.

    Or, if you want to get fancy, choose “Create New Application”. This does the same thing as the HelloWorld approach, except that it will also create a new application certificate, start the registration process to the application configuration center. Just hit F5, and you’re up and running with your new application.

    Certificate Storage

    Both of these approaches leverage the new ability to put an application’s certificate on the file store instead of in the certificate store. This should be more convenient in some situations. This is done by adding a key in the web.config file:

    <add key="ApplicationCertificateFileName" value="g:\mshealth\Nice App\cert\WildcatApp-3cdc0cea-6008-4c76-9169-36d44c3d63b4.pfx" />

    If the certificate has a password, that can be specified with the ApplicationCertificatePassword key.

    We do caution that applications should take care not to put the certificate in a web-accessible directory.

  • Eric Gunnerson's Compendium

    Blog refactoring 2.0

    • 0 Comments

    In the beginning, I started this blog just to write. I wrote a lot of C# stuff, some regex stuff, some HealthVault stuff, and a bunch of irrelevant stuff.

    Then, at some point, I started the RiderX blog to write about some more cycling-specific stuff that I – in a rare moment of discernment – didn’t want to put on this blog.

    Since that time, I picked up a HealthVault team blog (well, two team blogs, actually), and most of the time I wanted to write, I didn’t want to write anything work related, and wanted a place to do a bit less self-editing that I do here.

    Yes, I know, it does boggle the mind a bit, that what I post here is self-edited…

    So, anyway, to make things short(er), I went domain name hunting, found to my surprise that “thegunnersons.com” was available, and spun up a personal blog there.

  • Eric Gunnerson's Compendium

    Question of the day

    • 1 Comments
    Is Cinderella related to Mozzarella?
  • Eric Gunnerson's Compendium

    Ratings from Eric's Vacation

    • 0 Comments

    I've decided to be totally derivative (and likely considerable less good) than "The Book of Ratings".  A great book.

    State highway signs

    California

    It's really a bit sad. They spend all that time putting up "enforced by radar" or "enforced by aircraft" signs, but deep down they know that nobody is going to pay any attention.

    To distract themselves, they amuse themselves by putting "80" at the end of all the freeway names around San Francisco.

    Rating: C

    Washington

    Boring signs, though the state highway signs do have a nice outline of George's head on them.

    Or perhaps I'm just jaded from my long association, and am desperately seeking my midlife crisis of signs.

    Rating: C

    Oregon

    Perhaps the Oregon highway department was beaten up by the other highway departments when it was little, but for whatever reason, Oregon likes to do things big, with an outsize "55" telling you that they really mean business, and colossal "do not enter" signs at least 10' on a side. Definitely signs with an attitude.

    On the minus side, they insist on putting up "end of speed zone" signs, forcing me to try to remember how fast I was allowed to go at some point in the past.

    Rating: B

    Vacation Vehicles

    Mom's Old Car

    Mom's old car - chosen so the offspring could drive in WA and OR - is a sweet nicely-preserved 1998 Honda Accord, returning a bit over 30 MPG for the trip.

    Rating: B-

    ATVs

    5 minutes of recorded safety lecture, and we're out in the largest sandbox on the west coast, 32000 acres of duney bliss. The last off-road experience I had was a 50cc Honda when I was 12, but I do remember the most important rules - a) You get it stuck, you dig it out, b) don't cross over the unmarked boundaries, and c) you break it, you pay us.

    Not really useful for getting from here to there, but a hecka lot of fun (as the kids would say).

    Rating: A

    Sand Rail

    First off, "Sand Rail" is a killer name, beating up on "Dune Buggy", and taking the lunch money of "ATV" ("Hey, I know! People love names that are acronyms").

    Four point racing harnesses, goggles, and a suggestion to keep our hands off the top rail "in case we roll", and the first pass is a 30 mph trip down a 45 degree slope and up another. Great fun once I pried my eyes open.

    Sand Dunes Frontier

    A++

    Aquatic Creatures

    Hippocampinae

    Sometimes confused with the hippocampus. Yeah, we get it, they're shaped like horses, and there are tiny fish who act as jockeys, riding them around the tanks is daily races.

    Sure, the dudes are the ones that give birth, guaranteed to make all the guys in the audience wince a bit.

    Rating: C

    Cephalopods

    The 007 of the water, with adaptive camo, water-jet propulsion, super grippers, and a built-in smoke screen.

    Rating: B

    Pinnipeds

    Cute and cuddly, intelligent, mischievous - everything you want in an aquatic animal. Except for the fact that they mostly swim under the water and when outside the water spend their time doing impressions of jumbo furry sausages.

    Rating: B+

    Missing mountains

    Mount St. Helens

    A bit less known as its larger and better-behaved brother to the north, St. Helens is widely held up as the epitome of a missing mountain.

    2/3 of a cubic mile of mountain disappeared in 30 seconds, killing 57 humans, 7,000 big game animals and 12 million hatchery salmon. It inconvenienced people throughout the state, causing millions of people to have to wash their cars ahead of schedule.

    The glowing growing dome is a nice touch if you can see it at night, but it could really use some daytime pizzazz.

    Rating: B

    Mazama

    First of all, erupting 3000 years before the advent of mass media is a bad career choice, and the PR is largely non-existent. You still see Jordan's name all over the place, so I think Mazama needs a new agent.

    But it's hard to fault it on execution. 25 cubic miles of mountain vanish. What do you put in it's place? A 2000' deep lake.

    Brilliant.

    Rating: A

  • Eric Gunnerson's Compendium

    Introduction to HealthVault Development #13: More more than one person

    • 1 Comments

    In the last installment, we modified our application so that it could switch between family members for data display and entry. This time, we’re going to add a table at the top that shows the current weight for all family members.

    We add the table right after the <h1> title:

    Family Summary <br />
    <asp:Table ID="c_tableSummary" runat="server" BorderWidth="1px" CellPadding="2" CellSpacing="2" GridLines="Both"/>
    <br />

    Then we need a method to walk through the records and fetch the current weight from each of them. The following will do that:

    void GenerateSummaryTable()
    {
        TableHeaderRow headerRow = new TableHeaderRow();
        TableHeaderCell headerCell = new TableHeaderCell();
        headerCell.Text = "Name";
        headerRow.Cells.Add(headerCell);

        headerCell = new TableHeaderCell();
        headerCell.Text = "Weight";
        headerRow.Cells.Add(headerCell);

        c_tableSummary.Rows.Add(headerRow);

        foreach (HealthRecordInfo record in PersonInfo.AuthorizedRecords.Values)
        {
            HealthRecordSearcher searcher = record.CreateSearcher();

            HealthRecordFilter filter = new HealthRecordFilter(Weight.TypeId);
            filter.MaxItemsReturned = 1;

            searcher.Filters.Add(filter);

            HealthRecordItemCollection weights = searcher.GetMatchingItems()[0];

            if (weights.Count == 1)
            {
                TableRow row = new TableRow();

                TableCell nameCell = new TableCell();
                nameCell.Text = record.Name;
                row.Cells.Add(nameCell);

                Weight weight = weights[0] as Weight;

                TableCell weightCell = new TableCell();
                weightCell.Text = weight.Value.DisplayValue.ToString();
                row.Cells.Add(weightCell);

                c_tableSummary.Rows.Add(row);
            }
        }

    Currently, there is no way to make a single request that fetches data for more than one record, so we need to create an execute a separate query for each one.

    Next Time

    With all the operations that we are performing, the application is running a little bit slowly. We’ll do some investigation into what’s going on, and see if we can’t make some improvements.
  • Eric Gunnerson's Compendium

    Thoughts on agile design and platforms

    • 3 Comments

    I started by writing a broad post about design, and it got away from me (apparently I should have spent some time designing the post first…), so I deleted it all and decided to write something shorter, and, with any luck, more understandable and useful.

    I’ve been reading some discussions about how design relates to Agile. Some teams get into trouble because they think that agile means “no design”, when in fact it means “right design”.

    The whole point of design in my mind is to save you work later on. There’s a sweet spot between no design and big design that makes sense in a particular situation. I don’t think that’s a unique insight at all, though I have seen groups how use the “design document” approach where there’s a document with 18 sections in it that everybody has to use.

    The two areas I would like to talk about are about what you are building and the scope of what you are doing right now. I’ll talk about scope first.

    The amount of design you should do depends on the scope of what you are doing, and scope in this situation doesn’t mean “amount of code change” (though it often correlates somewhat with that), it means “impact of code change on the customer”. This is obviously different for every change you make to the code, and my general guideline is that spending 5 minutes bouncing your thoughts off of somebody else generally gives you a good enough conclusion about how much design is required. Notice that I said “guideline” – I expect that developers can use their best judgement about when they can make changes without consulting with others.

    Some people would say that not having the opportunity to make a wrong choice about when to consult with others is a strength of pair programming. I think that’s probably true, but obviously only works for teams that do pair, and that’s not that common in my neck of the woods (and, I suspect in others).

    So, anyway, that’s what I think about scope, and, once again, I don’t think it’s a unique insight.

    My second point – that the amount of design depends on what you are building – is something I haven’t heard talked about much, especially in agile circles. Because most software developers deliver applications, the agile processes are described in that context.

    And in that context, I think that many developers do far too much design up front. You can spend 3 days writing something up that covers how you will do something, or you can spend 2 days doing early implementations and then know which one works better, and have real code to show people. Trying to figure out how things should work before you write them is often less efficient than just writing them when you need them.

    Given that I consider premature generalization to be the #1 sin of developers, no surprises there.

    But – and I think I may be finally getting to the point – that perspective comes from applications, where you own the code that you’re building, and refactorings can be done when you learn more.

    Platforms are different.

    If you are building a platform, things get a bit schizophrenic. Internally, you are an application – you can refactor the internals without a lot of impact elsewhere, and therefore the amount of design you do should keep that in mind.

    But externally, people depend on your APIs to stay the same. This means that, for a given feature, you need to get it right (or as close to right as you can) on your first release. It also means that you need to think about how the feature that you’re doing right now might be extended for things you might do in the future.

    Which is exactly the thing that you shouldn’t be doing if you’re building an app, because it’s a pain, it’s expensive, you’ll make the wrong choices, and you’ll have to throw work away.

    In my current team, we own both applications and platform API, so we get to spend time in both of these areas.

    And now it’s lunchtime. I may write a future post on how I think you should do platform design.

  • Eric Gunnerson's Compendium

    RAMROD 2009 ride report

    • 0 Comments
    http://riderx.info/blogs/riderx/archive/2009/08/06/ramrod-2009.aspx
  • Eric Gunnerson's Compendium

    NASA images lunar descent stage and astronaut path on moon...

    • 0 Comments

    Just in time for the 40th anniversary of Apollo 11, a NASA orbiter images Apollo landing sites.

    The apollo 14 site is very impressive.

  • Eric Gunnerson's Compendium

    21st Century Breakdown - show review

    • 0 Comments

    Green Day remains a very entertaining group to watch, and we had a great time.

    After a dinner at the traditional Seattle eatery 13 coins, we headed over the the Key for the show. Got there fairly early, and headed to the West side to buy some Merch (far less crowded than the east side), and the found our seats and waited.

    Opening group was "The Bravery". I cut opening bands a lot of slack because it's a hard crowd and the sound usually doesn't work well for you, but it wasn't to my taste. The vocals were muddy, and the songs sounded all the same to me. Happy they didn't play for very long.

    After a bunch of listening, 21st Century Breakdown hasn't caught on for me the way that American Idiot did, so I was hoping that they would play a lot of other stuff besides the new album. Which they did - opened with a few cuts of the new album, then a few off of idiot, then some old stuff (Dookie and newer, so not old old), some more new stuff, etc. Nice variety, and you can tell from the crowd reaction that they like the old stuff quite a bit.

    Billie Joe played a lot less lead guitar than in the idiot show, concentrating more on vocals and rhythm guitar, especially on the new stuff. It seemed to work pretty well and the mix between his guitar and the backup guy was pretty good.

    A few issues since this was the first show. Crew isn't quite used to how things work yet (leading to a pretty funny 2-minute segment when the guitar tech keeps trying to hand Billie Joe his guitar and Billie Joe keeps singing), and a notably disappointing t-shirt gun, which despite several attempts couldn't launch the t-shirt more than about 30'.

    They played for just a bit over two hours, with only perhaps a 1 minute break before the encore. Billie Joe spends the whole two hours running around and singing, and it's pretty impressive.

    Great show.

    Partial setlist:

    Dookie:

    • Longview
    • Basket Case

    Nimrod:

    • Hitchin' a ride
    • King for a day
    • Good riddance (time of your life) (closing song, acoustical)

    Warning:

    • Minority

    American idiot:

    • American Idiot
    • Jesus of suburbia
    • Boulevard of broken dreams
    • Holiday
    • Are we the waiting
    • St. Jimmy
    • Homecoming

    21st Century Breakdown:

    • Song of the Century
    • 21st Century breakdown
    • Know your enemy
    • Before the lobotomy
    • 21 guns
    • (think I missed a couple here...)

     

  • Eric Gunnerson's Compendium

    Is programming a generic skill?

    • 7 Comments

    Came across a post by Justin Etheredge discussion whether changing between languages is just a matter of syntax.

    Or, to pick a specific example, can a Java programmer quickly and easily learn to write C# code?

    The answer is obviously "yes". Development is about a way of thinking and approaching problems, and given the similarity between Java and C#, a good Java developer should take a minimal amount of time to learn how to write functional code in C#. The biggest barrier is libraries, which are more different than the languages are.

    The answer is equally as obviously "no". Sure, you can write functional code, but you will not be able to write idiomatic code. Like a high school senior with 4 years of French class on a trip to Paris, you can make yourself understood, but you aren't going to be mistaken as a native. You ask a question, somebody replies, "Ce ne sont pas vos oignons", and you just end thinking of soup.

    So, yeah, you can write C# code, but it's going to be Java written in C#. Given the closeness of the languages, it may be sufficient, but you're going to force some refactoring on any idiomatic C# speakers who inherit your code.

    It can be worse - when I first started writing in Perl, I wrote C code in Perl, which just doesn't work very well. And over time, I became at least functional, though perhaps not idiomatic in Perl (though, because of TMTOWTDI, it's hard to judge that in Perl).

    However, if you can become idiomatic in multiple languages, your toolset broadens, and you become more useful in all your langauges.

  • Eric Gunnerson's Compendium

    Livestrong Seattle Century report

    • 0 Comments
    From my cycling blog...
  • Eric Gunnerson's Compendium

    Introduction to HealthVault Development #12: More than one person…

    • 0 Comments

    WeightTracker has gotten popular, and we have a new scenario. Our users would like us to extend our application so that they can easily enter weights for each member of the family.

    This will require us to delve a bit more deeply into users, records, and authentication, and we’ll also explore the HealthVault shell a bit more…

    Accounts and records

    HealthVault separates the concept of accounts and records. An account is associated with a specific set of credentials, and a record contains the health information for a specific person.

    It’s not uncommon for an account to have access to more than one record. A person might have access to their own record and the records of their children, spouse, or parents.

    What data does your application use?

    HealthVault supports two ways of dealing with records.

    A single-record application – which is what WeightTracker is right now – is intended to work with only one person’s data (ie one record) at a time. The platform keeps track of which record the application is using, and when writing code, you just need to use PersonInfo.SelectedRecord.

    A single-record application can switch between records by redirecting the user back to the HealthVault shell to choose the new record, and authorize it if necessary.

    The alternate way of dealing with applications is to work with multiple records simultaneously.

    We do this by adding the following entry to our web.config:

    <add key="WCPage_IsMRA" value="true"/>

    And we also need to tell the shell that we’re an MRA application whenever we ask it to do something. We do this in AuthorizeEmotion.aspx.cs, and we’ll modify the code there to the following:

    string TargetQuery = "appid=cee3e0fc-03c6-40b4-9550-a151901b4a27&onopt1=Opt_Emotion&ismra=true";

    Now, we can go back to the HealthVault PPE shell, create a second record, make sure our existing record no longer allows WeightTracker to use it (via the “sharing” tab). And when we run WeightTracker, we’ll see the following:

     

    Note that there are now checkboxes next to the records. We select all of them, hit “continue”, and then we will need to authorize the application to access each record.

    Displaying the list of authorized records

    The list of records that the application is authorized is stored in PersonInfo.AuthorizedRecords. We’ll add a dropdown control to default.aspx:

    Welcomes&nbsp;
        <asp:DropDownList ID="c_dropDownCurrentRecord" runat="server" Width="262px" OnSelectedIndexChanged="c_dropDownCurrentRecord_SelectedIndexChanged" AutoPostBack="True" />
        <br /><br />

    and remove the references to c_labelUser from the code.

    We will use the drop-down list to display the current list of authorized records and to select the one that we want. We’ll add the following code, and call PopulateRecordDropDown() at the beginning of Page_Prerender().

    protected void c_dropDownCurrentRecord_SelectedIndexChanged(object sender, EventArgs e)
    {
        foreach (Guid recordId in PersonInfo.AuthorizedRecords.Keys)
        {
            if (recordId.ToString() == c_dropDownCurrentRecord.SelectedValue)
            {
                PersonInfo.SelectedRecord = PersonInfo.AuthorizedRecords[recordId];
            }
        }
    }

    void PopulateRecordDropDown()
    {
        c_dropDownCurrentRecord.Items.Clear();

        foreach (Guid recordId in PersonInfo.AuthorizedRecords.Keys)
        {
            HealthRecordInfo healthRecordInfo = PersonInfo.AuthorizedRecords[recordId];

            ListItem listItem = new ListItem(healthRecordInfo.Name, recordId.ToString());

            if (recordId == PersonInfo.SelectedRecord.Id)
            {
                listItem.Selected = true;
            }

            c_dropDownCurrentRecord.Items.Add(listItem);
        }
    }

    We then need to update our height code to deal with cases where the height isn’t there (or perhaps you already did this…)

    Height height = GetSingleValue<Height>(Height.TypeId);
    if (height != null)
    {
        if (height.Value.DisplayValue != null)
        {
            c_labelHeight.Text = height.Value.DisplayValue.ToString();
        }
        else
        {
            c_labelHeight.Text = height.Value.ToString();
        }
    }

    and

    string bmiString = String.Empty;
    if (height != null)
    {
        double bmi = weight.Value.Kilograms / (height.Value.Meters * height.Value.Meters);
        bmiString = String.Format("{0:F2}", bmi);
    }

    After all of that, if you run the application, is should now allow you to select between the records. However, we still need to save the information about which record is current.

    Saving the current record

    To store the identifier of the current record, what we would like is a storage place that is associated with the person using the application rather than the a specific record. HealthVault provides that through the GetApplicationsSettings() and SetApplicationSettings() methods on the application connection. We could use those directly by putting some XML in there and then pulling it out, but we’re going to encapsulate that in a class. Here’s what we need to do:

    First, we’ll create a new class named “WeightTrackerApplicationSettings”.

    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Xml;
    using System.Xml.XPath;
    using System.Text;

    using Microsoft.Health;

    public class WeightTrackerApplicationSettings
    {
        private Guid _selectedRecordId;

        public Guid SelectedRecordId
        {
            get { return _selectedRecordId; }
            set { _selectedRecordId = value; }
        }

        public void LoadFromHealthVault(AuthenticatedConnection authenticatedConnection)
        {
            IXPathNavigable settingsNavigable = authenticatedConnection.GetApplicationSettings();
            if (settingsNavigable == null)
            {
                return;
            }

            XPathNavigator navigator = settingsNavigable.CreateNavigator();

            XPathNavigator appSettingsNode = navigator.SelectSingleNode("app-settings");

            XPathNavigator selectedRecordNode = appSettingsNode.SelectSingleNode("selected-record-id");

            string value = selectedRecordNode.Value;
            _selectedRecordId = new Guid(value);
        }

        public void SaveToHealthVault(AuthenticatedConnection authenticatedConnection)
        {
            XmlDocument document = new XmlDocument();

            XmlElement nodeWeightTrackerSettings = document.CreateElement("app-settings");
            document.AppendChild(nodeWeightTrackerSettings);

            XmlElement nodeSelectedRecordId = document.CreateElement("selected-record-id");
            nodeWeightTrackerSettings.AppendChild(nodeSelectedRecordId);
            nodeSelectedRecordId.InnerText = _selectedRecordId.ToString();

            authenticatedConnection.SetApplicationSettings(document);
        }
    }

    Then, we add the code to load in the settings at the beginning of Page_Prerender():

    _weightTrackerApplicationSettings.LoadFromHealthVault(AuthenticatedConnection);

    and modify our methods:

    protected void c_dropDownCurrentRecord_SelectedIndexChanged(object sender, EventArgs e)
    {
        foreach (Guid recordId in PersonInfo.AuthorizedRecords.Keys)
        {
            if (recordId.ToString() == c_dropDownCurrentRecord.SelectedValue)
            {
                PersonInfo.SelectedRecord = PersonInfo.AuthorizedRecords[recordId];

                _weightTrackerApplicationSettings.SelectedRecordId = recordId;
                _weightTrackerApplicationSettings.SaveToHealthVault(AuthenticatedConnection);
            }
        }
    }

    void PopulateRecordDropDown()
    {
        c_dropDownCurrentRecord.Items.Clear();

        foreach (Guid recordId in PersonInfo.AuthorizedRecords.Keys)
        {
            HealthRecordInfo healthRecordInfo = PersonInfo.AuthorizedRecords[recordId];

            ListItem listItem = new ListItem(healthRecordInfo.Name, recordId.ToString());

            if (recordId == PersonInfo.SelectedRecord.Id)
            {
                listItem.Selected = true;
            }

            c_dropDownCurrentRecord.Items.Add(listItem);
        }
    }

    That stores the currently selected record away and then restores it when the page starts up.

    Next Time

    Next time, we’ll display values from multiple records.

  • Eric Gunnerson's Compendium

    Rekeying the HealthVault/Amalga USB Key

    • 0 Comments

    If you attended the connected health conference last week, you got a USB key with a combination lock on it, so you could protect your important data against those with less than 10 minutes of free time, or those with very little imagination.

    But to get this protection, you'll need to change the combination to some other number. Here's how you do it:

    1. Set the key to 0-0-0
    2. On the non-usb end, there's a small round button. Press it in with a pen.
    3. Change the combination
    4. Press the button on the USB end back in.

     

     

  • Eric Gunnerson's Compendium

    Suggest a HealthVault topic...

    • 1 Comments

    Is there something about HealthVault that you find confusing? If so, add a comment to this post, and I'll try to cover it in a future blog post.

  • Eric Gunnerson's Compendium

    7 Hills of Kirkland 2009

    • 1 Comments
    7 Hills of Kirkland 2009
  • Eric Gunnerson's Compendium

    21st Century Breakdown

    • 2 Comments

    I picked up a copy of Green Day's newest this morning at Fred Meyer, paying a princely $12.99 for the priviledge to do so. I missed picking it up last Saturday because of being busy and all, so I missed 3 days of listening.

    I've listened through a couple of times now. Musically, it's pretty wide-ranging - there are songs that are traditional Green Day, one song that sounds like it's directly from Money Money 2020 (a nice album if you like punk/new wave fusion), and one that's a little bit country (and more than a little bit rock and rock).

    They manage to sound both very much like Green Day and not like Green Day at the same time, if that makes any sense. I think it's a nice album, perhaps not as strong as American Idiot, but we'll see how it holds up to repeated listening

     And yes, I'll be at Key arena with the wife and (now old enough for a Green Day concert) daughter on July 3rd.

     

     

  • Eric Gunnerson's Compendium

    Microsoft Connected Health Conference

    • 1 Comments

    The HealthVault Partner Conference is a year older and has a new name.

    The Microsoft Connected Health Conference will be held on June 10-12th at Meydenbauer Center in Bellevue, WA.

    If you enter the registration code on the page and choose "submit", you will get access to the details of the conference.

  • Eric Gunnerson's Compendium

    Progressio per Patientia

    • 2 Comments

    "Progress through suffering"

    I have a friend (perhaps "riding acquaintance" is a more accurate term) named "Elden Fat Cyclist". Actually, that's his stage name. His real name is "Harold Fat Cyclist".

    Harold has been involved in Livestrong for a while and - for reasons that will become obvious if you read his blog - this year has decided to organize "Team Fat Cyclist", to try to win the team competition of the Livestrong challenge.

    This year, Livestrong has decided to hold a ride in Seattle. Their goal is apparently to do a shortened version of a Tour de France mountain stage, with an advertised 7900' of elevation gain over the 101 miles. That would, AFAIK, set a new standard for centuries in these parts, with the ever-popular (many would say "over-popular") Flying Wheels Century clocking in at 3500', and even the difficult 7 Hills century at only 7000'.

    Or, to put it another way, this ride will involve a significant amount of pain and suffering. Since signing up for rides that are harder than I should is a bit of a tradition for me (not to mention being a tradition for Harold), I signed up last week.

    That's where you come in.

    The event is a fundraiser for cancer research, and I'm looking for people to sponsor me in this undertaking.

    For the first $250 that's donated, I will match every dollar, and then Microsoft will match my dollar. So, if you donate $10, the total will be... well, it will be more than I can calculate on Sunny Seattle Spring Sunday.

    If you can spare some money to help out a worthy cause (cancer research, not me suffering), please head on over to my fundraising page.

  • Eric Gunnerson's Compendium

    Introduction to HealthvaultDevelopment #11: Data filtering

    • 2 Comments

    The users are unhappy. The weight tracking application worked pretty well initially, but now that they’ve been using it daily for a few months, it’s showing them too much information. They would like it to show the recent results.

    That will take us into filtering…

    Filtering is done using the aptly-named HealthRecordFilter class. Thus far, we have only been using this class to filter to data that has the weight type id. In this episode we will explore additional ways to perform filtering.

    Common Data Filtering

    There are some properties that are shared across all data types. This section discusses filtering on those items.

    Filtering based on the number of items

    The simplest thing we can do is to only fetch a specific number of items rather than all of them. First, we add the following checkbox before the c_tableWeight table in default.aspx:

    <asp:CheckBox ID="c_CheckboxLast5Items" runat="server" Text="Last 5 items only" AutoPostBack="True" /><br />

    and then we change the filter definition in default.aspx.cs to:

    filter.View.Sections |= HealthRecordItemSections.Audits;
    if (c_CheckboxLast5Items.Checked)
    {
        filter.MaxItemsReturned = 5;
    }

    If you run this, when you click the checkbox you will only see the last 5 weight measurements.

    <aside>

    The HealthRecordItemCollection class implements lazy fetching of results. It will fetch chunks of instances at a time, going back to the server when it needs another chunk. The chunk size is set by the MaxFullItemsReturnedPerRequest property of the filter, and is 30 by default.

    This is transparent to the calling application, and the default value is usually fine.  Decreasing the value reduces the amount of work that the server has to do when processing each chunk, and might be required if your application deals objects that have significant amounts of data in them. This requires more trips to the server to fetch data, however, which will have performance implications.

    Increasing the value reduces the number of trips to the server to fetch all the data, but there is a possibility that the longer operations may result in a timeout.

    </aside>

    Filtering based on dates

    We can also filter based on the 

    First, add a checkbox above the table that allows the user to toggle this setting. You can do that through the designer or by adding the following before the c_tableWeight table:

    <asp:CheckBox ID="c_CheckboxLast3MonthsOnly" runat="server" Text="Last 3 months only" AutoPostBack="True" /><br />

    and the following is added to the filter definition:

    if (c_CheckboxLast3MonthsOnly.Checked)
    {
        filter.UpdatedDateMin = DateTime.Now - new TimeSpan(30, 0, 0, 0);
    }

    Filtering based on application id

    In a previous episode, we added some code to fetch the name of the application that last updated the data in HealthVault. We can filter on the application that last updated this data item so we only retrieve that items that our application retrieved (in general, apps should try to read all the data a user has, but it may make sense to only read this subset at times).

    This is done with the following:

    filter.UpdatedApplication = ApplicationConnection.ApplicationId;

    Adding the checkbox and the appropriate logic is left as an exercise to the reader.

    Type-Specific Filtering

    Filtering on a value that is specific to a type – the value of a weight measurement, the name of a medication is a bit more complex. The type-specific data for an instance is stored in HealthVault XML, so a filter needs to be able to find a specific piece of data inside of a larger chunk of XML and then compare it to a desired value.

    This is done using a query language that is known as XPath. To write an xpath expression for a specific HealthVault data type requires that you understand that XML format that is uses to store the information, which either requires getting the XML for a specific instance (by calling GetItemXml() on the instance), or understanding the XSD schema for the data type.

    To make this easier, I wrote the HealthVault XPath Explorer. This utility will let you easily walk the XML for instances that are in a specific HealthVault record, though you will have to figure out how to write the condition yourself. There are a number of good XPath tutorial available on the net.

    For our application, if we want to only return items where the weight value is less than 50 kilograms, we can use the following:

    filter.XPath = "/thing/data-xml/weight/value/kg[.> 50]";

    In the “[. > 50]”, the “.” refers to the current node, so it means “kg > 50”.

    Automatic user sign in

    You may have noticed that our demo app requires us to sign in every time we run it. This behavior is configurable:

    1. Go to the application configuration center
    2. Select the demo application
    3. Go to the “Misc” tab
    4. Toggle the “automatic user sign-in” radio button to “yes”
    5. Set a sign-in duration in seconds (I chose 3600).
    6. Click “Make changes”.

    That will start the configuration change process. Within 30 minutes, the update will make it to the servers, and a new “stay signed in” checkbox will appear on the log in page for the application.

    Next Time

    Next time we'll modify our application to work for the whole family.

Page 2 of 46 (1,129 items) 12345»