Introduction to HealthVault Development #8: Related items

Our current application allows users to track their weight, but some of our users also want to track the time they spend walking. In this episode, we’ll extend our application to store and display that information.

We will be storing that information in the Exercise type. First, we’ll need to head off to the Application Configuration Center (there’s a link at the bottom of the HealthVault Application Manager if you forget the URL…)

Locate your application, and look at the online access that is currently defined. We could add a new rule named “Exercise”, but that would get unwieldy pretty quickly with lots of types. Instead, we are are going to group our rules based upon the type of access required. What we are defining right now is known as the application’s “base auth”, so we’ll put that concept in our names to make it easier later, when we explore the other kind of auth (if you’re too excited to wait, it’s called “optional” auth…)

  1. Edit the Weight rule. Change the name to “Base_All”.
  2. Add access to Exercise to this rule.
  3. Rename the Height rule to be “Base_read”.

After you save the configuration (and perhaps wait a short time for the new configuration to be activated), run the application.

You will be redirected to re-authorize the application, since your application is now asking for more data access than you had previously authorized it to have.

That can make things a bit inconvenient (and confusing) for your users. You deploy a new version of your app that uses more types, and they have to reauthorize. The answer to that is the aforementioned optional auth, which I promise we’ll cover very soon.

Now we can modify our code to record that exercise information.

Housekeeping

A little bit of housekeeping first, to fix a bug that you might have noticed. If you enter a new weight value, it doesn’t show up right away. This is because of a mistake I made when I wrote the first version.

When the user enters a new weight, the data is posted back to the asp page, and Page_Load is called, then c_buttonSave_Click, then Page_Prerender, in that order (other stuff happens too). Which means that we’ve already rendered out the “current” set of information before we add new new value, which is why it doesn’t show up.

The fix is to take all of the code that is currently in Page_Load, and move it into Page_Prerender.

I assure you that this bug was in the code to provide a chance for you to debug your code, and certainly not an oversight from when I wrote the original code.

Saving Exercise Information

Go to default.aspx, and make label2 and c_textboxWalkingTime visible. This provides the UI that users use to enter their walking time.

We’ll start by creating an Exercise instance:

Exercise exercise = new Exercise();

Next, we need to record when the exercise happened. We do this with the following:

DateTime now = DateTime.Now;
ApproximateDateTime approximateDateTime = new ApproximateDateTime(
    new ApproximateDate(now.Year, now.Month, now.Day),
    new ApproximateTime(now.Hour, now.Minute, now.Second));

exercise.When = approximateDateTime;

That’s an ugly bit of code, but that’s what you have to write right now. Our next release will let you write this as:

exercise.When = new ApproximateDateTime(DateTime.Now);

so you might want to give that a try.

Now, we want to store the number of minutes that our user walked:

int minutes = Int32.Parse(c_textboxWalkingTime.Text);
exercise.Duration = minutes;

We’re still missing an important piece of data – the kind of exercise. We would like to store this information so that programs can make decisions based on the specific activity. That can easily be done by defining a set of different activities (which is known as a “vocabulary” in HealthVault, and referencing one of those with the exercise.

But that would miss an important piece of data – what the user said the activity was. Perhaps they want to enter “race walking” or “fitness walking”, or “walk in the park” and have the system show that as the activity when it displays the exercise information.

To accommodate both of those uses, HealthVault uses a concept of a codable value, which is similar to the measurement type discussed in an earlier episode. It stores a Text value – which is whatever the user or program thinks it should be – and one or more coded values, which provide the specifics that programs would read.

We’ll start by defining the CodedValue part. Looking at the docs for Exercise.Activity, we find it recommends the exercise-activities vocabulary (we could also have looked in the list of vocabularies).

Each vocabulary item has a code, a display text, and an abbreviation. So, to code to walking, we use the following coded value:

CodedValue codedValue = new CodedValue(“Walking”, “exercise-activities”);

Where "Walking” is the code and “exercise-activities” is the vocabulary.

We’ll use that to create the whole codable value:

exercise.Activity =
    new CodableValue("Walking", new CodedValue("Walking", "exercise-activities"));

In this case the Text and the codable value are the same, but in most cases the code is going to be considerably more terse than the Text value, or it may be a numeric value.

If it was important to our application to store more information, or if we wanted to refer to more than one vocabulary, we could add additional coded values to the codable value. This is useful if there is more than one standard set of codes for a particular property.

The codable values that are stored are not validated by the platform, which gives applications the flexibility to code to vocabularies that are not on the platform. We’d appreciate it, however, that you let us know if you’re coding to a vocabulary that you think should be on the platform.

After all of that, we can save the Exercise instance to the record:

PersonInfo.SelectedRecord.NewItem(exercise);

Constructors

In the last bit of code, I created the Exercise instance and then set the properties. That works fine, but all of the types in HealthVault declare constructors that take all the required values as parameters, and using that constructor is a good way of making sure you set all the required properties. Using that approach, our code would look like this:

DateTime now = DateTime.Now;
ApproximateDateTime approximateDateTime = new ApproximateDateTime(
    new ApproximateDate(now.Year, now.Month, now.Day),
    new ApproximateTime(now.Hour, now.Minute, now.Second));

Exercise exercise = new Exercise(approximateDateTime, 
                 new CodableValue("Walking",
                                            new CodedValue("Walking", "exercise-activities")));

int minutes = Int32.Parse(c_textboxWalkingTime.Text);
exercise.Duration = minutes;

PersonInfo.SelectedRecord.NewItem(exercise);

There’s a problem in the code that I just wrote. The Exercise instance and the Weight instance were entered at the same time by the user, but that relationship isn’t stored in HealthVault. This is going to complicate our display code considerably, as we have no easy way of finding the Exercise instance that goes with a specific weight.

We’ll use the Related items feature to connect them together.  We’ll simply add the Exercise instance to the related items list of the weight before we save it to the database:

weight.CommonData.RelatedItems.Add(new HealthRecordItemRelationship(weight.Key));
PersonInfo.SelectedRecord.NewItem(weight);

Related items is pretty low-tech – the id of the related object is merely saved on the object. The platform doesn’t provide referential integrity or any of that database stuff, so applications must be prepared to come across related item links that refer to items that aren’t accessible to the application.

Displaying the Exercise information in the table

Whenever we fetch a weight, we want to see if there is a related exercise instance. Here’s the code that we’ll use:

string minutes = String.Empty;
foreach (HealthRecordItemRelationship relationship in weight.CommonData.RelatedItems)
{
    if (relationship.ItemKey != null)
    {
        HealthRecordItem relatedItem =
            PersonInfo.SelectedRecord.GetItem(relationship.ItemKey.Id,
            HealthRecordItemSections.Core | HealthRecordItemSections.Xml);

        Exercise exercise = relatedItem as Exercise;
        if (exercise != null)
        {
            minutes = exercise.Duration.ToString();
        }
    }
}

This code walks through all the related items for an instance, and if there’s a key, it fetches the related item instance. It checks to see if the item fetched is an exercise, and if it is, formats the duration.

The task of adding a new column named “Minutes” to the table and the minutes value to the body of the table is left as an exercise to the reader.

Other Stuff

There are a few other topics that out of scope of this episode, but worth mentioning anyway.

Exercise is the new version of the AerobicSession type. Because of the type of data they store and the differences between the types, applications that will process AerobicSession data will likely want to handle it separately from Exercise data.  There is more information on this issue here.

There is a second method of relating items together known as client ID, which is used by HealthVault connection center when it needs to link instances together (related items doesn’t work for it because of some architectural issues). I’ll cover client IDs in the future.

Next Time

Next time, we’ll do something with optional auth.