Best Practice for Recursive/Nested Observation Results/Value

Hi together,

I have an app, where the users can start a program, in which they are guided through different steps.
In each Step they have to click on different items and while the time for that is measured.

I would like to convert these results into a FHIR Observation, but I came into the problem, that I don’t know how to work with these nested values. I thought about using a procedure for the program, but since the patient just uses the app, I thought it would not fit, since it was not performed on or for a patient.

My first approach was to have an Observation “Program” with an effective-period (starttime of the program until endtime of the program) which in turn hasMember another Observation of “Step” (which has an effective-period and some other component information).
Then I would have created an Observation “Item” which would have a valuePeriod (time the user needed to find the item) and some components describing the item (position, type of item, …).

My problem is that - as far as I understand it - observations are not “nestable”. So my question would be what are the best practice to approach such nested result structures?

Can steps repeat? Can the same step happen more than once in the flow? Do you need to compare timings for the same step across different program sessions? Do you care only about how long it took to complete the step, or are you gathering information for each step that you also need?

1 Like

Hi @lloyd, thank you for taking the time!

A step will always be unique, since there will be different items in it, which will always be at random positions.
So each run, has multiple different steps.
The timer runs from the beginning of the first step until the end of the last step and I collect the timing information when a step starts and ends as well as some other details for the step. I could drop the additional information and just keep the information how long it took to complete the step but I would be interested in the two options that I would have here!

It sounds like this is some sort of cognitive/response test and that the ‘value’ you’re observing is really the response time rather than the value, or do you also need the value in order to capture accuracy?

How are you planning to compare the observations across different runs?

1 Like

Yes I’m capturing the response time. Each item has some additional information (like position, and type) which would probably be components(?).

I want to compare e.g. the average search time throughout a run and I can compare based on the number of distractors and number of items which the user has to find, the type of those items and especially the position of those items.

Ok. I’d probably treat each item as a separate Observation with value being duration (number of seconds) with components for item position and type. I would then have an overall Observation capturing the sum of the timings for a particular step with “derivedFrom” relationships to the individual item measurements and an Observation with the overall time for the ‘run’ with derivedFrom relationships to the ‘step’ observations. The effectiveTime for each would be an effectiveDateTime that indicated the time of the start of the item, step or program respectively.

While you could use ‘member’ instead of ‘derivedFrom’, the fact that your roll-up Observations will have a value that’s calculated from the ‘children’ makes derivedFrom a better fit I think.

1 Like

Thank you so much, that I can use 'derivedFrom’ for such connections between Observations was not clear to me.

When you say “capturing the sum of the timings […] with ‘derviedFrom’ relationships” do you mean that the Observation “run” and “step” won’t have an ‘effective’ or ‘value’ on their own, but it will be implicated through the sum of the ‘effective’/‘value’ periods in the items? Or am I misunderstanding you here?

Two other questions remain for me now:

  1. As far as I understand from your text ‘member’ and ‘derivedFrom’ are not just the two sites of a connection between two Observations? My first idea would have been, to add steps as 'member’s to a run and add a run as ‘derivedFrom’ to each step. But as far as I understand you the best practice would be only to mark the run as ‘derivedFrom’ in each step and the corresponding step as ‘derivedFrom’ in each item.

  2. Would that approach also mean that each run, each step and each item need to be uniquely identifiable, correct? When I would describe a run, including its steps and items, in e.g. a json-format, I will probably not be able to use a nested structure but would have to do it as three different lists, somehow like this (pseudojson ahead):

        {
        "runs": [
            {
                "type": "observation",
                "id": "run_1"
            }
        ],
        "steps": [
            {
                "type": "observation",
                "id": "run_1_step_1",
                "derivedFrom": "run_1"
            }
        ],
        "items": [
            {
                "type": "observation",
                "id": "run_1_step_1_item_1",
                "derivedFrom": "run_1_step_1"
            }
        ]
    }

The “run” Observation will have a value that would be the duration of the whole run and would have an effectiveDateTime of the start of the run.
Observation.effectiveRange (with a start and end) is intended to convey “the period of time over which the observed value was true” - and that doesn’t really make much sense for you. The ‘observed value’ you care about is actually the elapsed time.

You don’t want to have both ‘member’ and ‘derivedFrom’ relationships. ‘member’ is usually used for panels where you’re just grouping things (and don’t have a value of your own).

You won’t have a nested structure. The same is true in lots of other places. Each Observation stands on its own and can be retrieved on its own. If you wanted to do a search and retrieve all time durations for items of type X or of positions in the lower-left quadrant, you could.

1 Like

Thank you very much for your help @lloyd, this solves all my questions about the structure.

Hi lloyd, one follow-up questions regarding the ID of the different Observations:

Do they need to be unique over different users? We don’t have a server or something that would be able to assign a real unique value but the ID’s would be unique on each export since they are unique on the device.

Is that sufficient? The standard says:

The value SHALL be unique within the defined system and have a consistent meaning wherever it appears
Datatypes - FHIR v4.0.1

What means “within the defined system” in this case? Could I define my system as a json file or does it need to be unique globally?

Observation.id needs to be unique in the context of a single server - i.e. all observations across all patients (labs, vitals, smoking history, psych assessments, water quality measurements, you name it) that are exposed from that server need to have unique ids. However, the same id might exist on different resource endpoints. I.e. you can’t have more than one Observation/1, but you’re allowed to have a Patient/1 and a Practitioner/1. Observation.identifier SHOULD be unique to that Observation across all systems in the world. It’s fine if multiple systems have an Observation with the same Observation.identifier, but if the identifiers match, both records should be talking about the same Observation.

Thank you lloyd, that means for a product with multiple instances that does not have a server to sync the IDs it will need UUIDs or something similar in order to archive this uniqueness across a single server.
Just for my clarification - with server in this case you mean a type of Observation, correct?

Simple Example:
Product A on Device X creates Observation “o1”
Product A on Device Y creates Observation “o2”

These two Observations o1 & o2 must have two different IDs, so they will need to use e.g. UUIDs to archive that since they do not have a way of communicating with each other, correct?
Would “Water Quality” be the server in this example or did I misunderstand that term?

Each server assigns identifiers to the resource it receives. The ‘server’ is the system that actually stores the resource and makes it available to other systems The identifier is assigned as part of the POST process where the client system asks for the Observation to be stored. Where does the Observation get stored in your scenario?

Currently, we are planning to export our resource only in a “FHIR format” on the device without sending it to a server. So currently, in our scenario, the client stores this observation only on the device itself and the file is not shared (yet) with any other server.

If you’re exporting a Bundle of resources, simplest thing to do is have them use UUIDs. Servers will assign resource ids when they consume the Bundle.

1 Like