microsoft / healthkit-on-fhir

HealthKitOnFhir is a Swift library that automates the export of Apple HealthKit Data to a FHIR Server.
MIT License
106 stars 27 forks source link

Where do SampledData values get stored #22

Closed agigary closed 2 years ago

agigary commented 2 years ago

Hi Again...

So I have got all this all setup and the app is working correctly and sending data, it's great btw !

My recipient FHIR server is correctly receiving, transforming and persisting the data as I can see some of it via PowerApps and PowerBI, when connected using the FHIR connector.

However for the life of me I cannot see the apple health values sent via the high frequency IomtFhirDelegate, such as heart rate, steps etc, I can find the low frequency data such as height and body mass etc.

The only difference I can see is that the high frequency data is being sent as sampled, you can see the json that is being sent for each time period "EEEEEE 56 EEEE" within the app on the device, however when trying to access the data in the FHIR server via the observation table all that I can find is empty observations for "steps" with no actual measurement values.

Low frequency data is able to be found via the "value.Quantity.value" fields, and in theory the high frequency data should be available via "Component.Value" however there is nothing recorded.

Where in theory should this data be available, Appreciate this may be more of a question for the iomt-fhir git!

Many Thanks

namalu commented 2 years ago

High frequency data is sent to an IoT Connector, transformed into an Observation and stored in the value.SampledData property of the Observation, not the Component.Value property.

SampledData has a data property that is of type string. The reason for this is to reduce the number of Observations created. For example, when an Apple Watch is collecting exercise data, it will measure heart rate every 5 seconds - This would generate 720 Observations in a one hour period if the data were not sent to the IoT Connector to transform the measurement and store it in a single Observation representing a 1 hour period.

The data is stored in a string that will have space separated values for every measurement received. If there is no data for a given period, E is used to represent Empty. In the example above, because there could be data measured every 5 seconds, an Observation is created with a SampledData.Data string that contains 720 space separated values. If there is only one measurement, you end up with 719 Es and 1 measurement - In your example, a single 56 in a sea of Es.

Example:

E E E E E E 56 E E E E E ...

This partial SampledData.Data string represents just 1 minute of data (again, the Observations represent one hour of data, so this string in a real world scenario will have 720 values). The first 30 seconds, there are no measurements, so there are 6 space-separated Es. At 35 seconds, there is a single measurement of 56 and so on...

What if I don't need/want to use the SampledData?

Below is an IoT Connector Mapping template used to transform heart rate data into a FHIR Observation that uses SampledData to create an Observation representing 1 hour. You can see that the periodInterval is set to create a 1 hour Observation (currently there are only 2 options 1 hour and 24 hours) and the defaultPeriod is used to set the period each value in the SampledData.Data string represents. In this case 5 seconds.

{
  "templateType":"CodeValueFhir",
  "template":{
    "codes":[
      {
        "code":"8867-4",
        "system":"http://loinc.org",
        "display":"Heart rate"
      },
      {
        "system":"com.apple.health",
        "code":"HKQuantityTypeIdentifierHeartRate",
        "display":"Heart rate"
      }
    ],
    "periodInterval":60, // The Observation will represent 1 hour of data (60 minutes)
    "typeName":"heartrate",
    "value":{
      "defaultPeriod":5000, // Each space-separated value represents a 5 second period (5000 milliseconds)
      "unit":"count/min",
      "valueName":"hr",
      "valueType":"SampledData"
    }
  }
}

If you do not expect to encounter high frequency data, you can still send data through the IoT Connector, by deleting the periodInterval and defaultPeriod, changing the valueType to Quantity, and adding the system the code code belongs to. This will create an Observation with the measurement stored in the value.Quantity.value property.

{
  "templateType":"CodeValueFhir",
  "template":{
    "codes":[
      {
        "code":"8867-4",
        "system":"http://loinc.org",
        "display":"Heart rate"
      },
      {
        "system":"com.apple.health",
        "code":"HKQuantityTypeIdentifierHeartRate",
        "display":"Heart rate"
      }
    ],
    "typeName":"heartrate",
    "value":{
      "system": "http://unitsofmeasure.org", // Add the system (optional)
      "unit":"count/min",
      "valueName":"hr",
      "valueType":"Quantity" // Set value type to Quantity
    }
  }
}
namalu commented 2 years ago

@agigary - Just checking in to see if this issue has been addressed?

agigary commented 2 years ago

So partially; I have had to change them all to Quantity as the SampledData doesnt seem to be storing in my FHIR instance not sure if its something on my side; but there is no value stored in value.SampledData at all. Quantity comes across fine. it's not a problem in my current usecase which is a PoC but a live solution may require a sample.