Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Health Connect to FHIR converter module #15

Merged
merged 44 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8583dbc
Create Health Connect to FHIR converter module
vishnuravi May 22, 2024
b5d64c4
Update tests
vishnuravi May 22, 2024
e892419
Use build convention plugins
vishnuravi May 22, 2024
a6f1dbf
Move to modules folder and remove proguard rules
vishnuravi May 22, 2024
ea25dfc
Reorganize module files
vishnuravi May 22, 2024
43a9688
Support multiple codings and categories per observation
vishnuravi May 23, 2024
77b05b4
Add additional unit tests
vishnuravi May 23, 2024
9896b62
Update memory settings
vishnuravi May 23, 2024
5e24882
Improve observation creation
vishnuravi May 23, 2024
8c29c82
Increase heap size
vishnuravi May 23, 2024
aa13705
Update tests and dependencies
vishnuravi May 26, 2024
e5f2b78
Clean up manifest
vishnuravi May 26, 2024
3320580
Extract dependencies to version catalog
vishnuravi May 27, 2024
d277d90
Refactor tests to use truth library
vishnuravi May 27, 2024
c3f5871
Merge branch 'main' into healthconnectonfhir
vishnuravi Jun 3, 2024
7764a65
Create record to observation mapper
vishnuravi Jun 11, 2024
562ba6f
Merge branch 'main' into healthconnectonfhir
vishnuravi Jun 11, 2024
b55608f
Add heart rate mapping
vishnuravi Jun 11, 2024
6612e8d
Fix detekt errors
vishnuravi Jun 11, 2024
99983f8
Switch fhir dependencies
vishnuravi Jun 11, 2024
48fcdac
Update tests
vishnuravi Jun 11, 2024
fe14a1e
Remove unused import
vishnuravi Jun 11, 2024
de1302e
Fix dependency conflict
vishnuravi Jun 11, 2024
34025e9
Reduce heap size to default
vishnuravi Jun 12, 2024
8ad3d1f
Delete .DS_Store
PSchmiedmayer Jun 12, 2024
066e1ba
Alphabetize mapper functions
vishnuravi Jun 12, 2024
b914d9b
Add BodyFatRecord
vishnuravi Jun 12, 2024
e8e973c
Add OxygenSaturationRecord
vishnuravi Jun 12, 2024
4f65b0b
Add RespiratoryRateRecord
vishnuravi Jun 12, 2024
be80bce
Use effectiveDateTime instead of effectivePeriod if start and end tim…
vishnuravi Jun 13, 2024
0d2c49b
Add unique identifier from Record metadata to Observation
vishnuravi Jun 13, 2024
8c7e226
Adds issued time to observation to match HealthKitOnFHIR
vishnuravi Jun 13, 2024
178acbc
Fix detekt warning
vishnuravi Jun 13, 2024
ed133a0
Update identifier json structure to match HealthKitOnFHIR
vishnuravi Jun 13, 2024
a246c43
Switch to UCUM units and sync with HealthKitOnFHIR
vishnuravi Jun 13, 2024
66008f1
Fix detekt issues
vishnuravi Jun 13, 2024
3d9f12e
Use SI units
vishnuravi Jun 16, 2024
683594c
Add blood glucose
vishnuravi Jun 16, 2024
1c7a6c7
Update tests
vishnuravi Jun 16, 2024
1196b43
Add KDocs and improve test coverage
vishnuravi Jun 17, 2024
db9eb16
Add README
vishnuravi Jun 17, 2024
b86d51e
Update README
vishnuravi Jun 17, 2024
3a3c682
Fix dokka error
vishnuravi Jun 17, 2024
4217b3b
Update README
vishnuravi Jun 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ firebaseStorageKtx = "21.0.0"
foundation = "1.6.7"
googleGmsGoogleServices = "4.4.2"
googleid = "1.1.0"
hapiFhirVersion = "5.7.9"
healthConnectClient = "1.1.0-alpha07"
hiltNavigation = "1.2.0"
hiltVersion = "2.51"
junit = "4.13.2"
Expand All @@ -46,6 +48,7 @@ android-gradle = { group = "com.android.tools.build", name = "gradle", version.r
android-tools-common = { group = "com.android.tools", name = "common", version.ref = "androidTools" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-health-connect-client = { module = "androidx.health.connect:connect-client", version.ref = "healthConnectClient" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-core-testing = { group = "androidx.arch.core", name = "core-testing", version.ref = "coreTestingVersion" }
androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "credentialsPlayServicesAuth" }
Expand Down Expand Up @@ -73,6 +76,7 @@ firebase-functions-ktx = { group = "com.google.firebase", name = "firebase-funct
firebase-storage-ktx = { group = "com.google.firebase", name = "firebase-storage-ktx", version.ref = "firebaseStorageKtx" }
google-truth = { group = "com.google.truth", name = "truth", version.ref = "truth" }
googleid = { module = "com.google.android.libraries.identity.googleid:googleid", version.ref = "googleid" }
hapi-fhir-structures-r4 = { module = "ca.uhn.hapi.fhir:hapi-fhir-structures-r4", version.ref = "hapiFhirVersion"}
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hiltVersion" }
hilt-core = { group = "com.google.dagger", name = "hilt-android", version.ref = "hiltVersion" }
hilt-gradle = { group = "com.google.dagger", name = "hilt-android-gradle-plugin", version.ref = "hiltVersion" }
Expand Down
7 changes: 0 additions & 7 deletions modules/contact/src/androidTest/AndroidManifest.xml

This file was deleted.

4 changes: 1 addition & 3 deletions modules/contact/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>

</manifest>
<manifest />
1 change: 1 addition & 0 deletions modules/healthconnectonfhir/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
83 changes: 83 additions & 0 deletions modules/healthconnectonfhir/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Module healthconnectonfhir

The HealthConnectOnFHIR library provides a mapper that converts supported [Android Health Connect](https://health.google/health-connect-android/) Records to corresponding [HL7® FHIR® R4 Observations](https://hl7.org/fhir/r4/observation.html) with standardized codes (e.g. [LOINC](https://loinc.org/)).

For more information, please refer to the API documentation.

## Mapping Table

| Health Connect Record | FHIR Observation Category | LOINC Code | Unit | Display |
|------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------|------------|------------|--------------------------------------------|
| [ActiveCaloriesBurnedRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/ActiveCaloriesBurnedRecord) | Activity | 41981-2 | kcal | Calories burned |
| [BloodGlucoseRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/BloodGlucoseRecord) | | 41653-7 | mg/dL | Glucose Glucometer (BldC) [Mass/Vol] |
| [BloodPressureRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/BloodPressureRecord) | Vital Signs | 85354-9 | mmHg | Blood pressure panel with all children optional |
| [BodyFatRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/BodyFatRecord) | | 41982-0 | % | Percentage of body fat Measured |
| [BodyTemperatureRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/BodyTemperatureRecord) | Vital Signs | 8310-5 | Cel | Body temperature |
| [HeartRateRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/HeartRateRecord) | Vital Signs | 8867-4 | /min | Heart rate |
| [HeightRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/HeightRecord) | Vital Signs | 8302-2 | m | Body height |
| [OxygenSaturationRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/OxygenSaturationRecord) | Vital Signs | 59408-5 | % | Oxygen saturation in Arterial blood by Pulse oximetry |
| [RespiratoryRateRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/RespiratoryRateRecord) | Vital Signs | 9279-1 | /min | Respiratory rate |
| [StepsRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/StepsRecord) | Activity | 55423-8 | steps | Number of steps |
| [WeightRecord](https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/WeightRecord) | Vital Signs | 29463-7 | kg | Body weight |


## Installation

HealthConnectOnFHIR can be installed into your Android Studio project [via Jitpack](https://jitpack.io/#StanfordSpezi/SpeziKt/healthconnectonfhir-maven).

## Usage

```kotlin
// Initialize the mapper
val mapper = RecordToObservationMapperImpl()

// Query a `Record` from Health Connect
val record = // ..

// Map the record to an HL7 FHIR Observation
val observation = mapper.map(record)
```

## Example

First, you will need to configure your application to use Android Health Connect. For more information, please see the [official documentation](https://developer.android.com/health-and-fitness/guides/health-connect).

```kotlin
// Initialize a `HealthConnectClient` (see Health Connect docs for full details)
val healthConnectClient = HealthConnectClient.getOrCreate(context)

// Define a time range for the query
val startTime = Instant.parse("2023-05-01T00:00:00Z")
val endTime = Instant.parse("2023-06-01T00:00:00Z")

// Query a list of `WeightRecord`s from Health Connect
val result = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = WeightRecord::class,
timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
)
).records

// Initialize the mapper
val mapper = RecordToObservationMapperImpl()

// Convert each weight record to a FHIR Observation
result.forEach { weightRecord ->
val observations = mapper.map(weightRecord)
observations.forEach { observation ->
// Do something with the observation
}
}
```

## License

This project is licensed under the MIT license.

## Contributors

This project is developed as a part of the Stanford Biodesign for Digital Health projects at Stanford. See CONTRIBUTORS.md for a full list of all HealthConnectOnFHIR contributors.

## Notices

Health Connect is a registered trademark of Google. FHIR is a registered trademark of Health Level Seven International.
13 changes: 13 additions & 0 deletions modules/healthconnectonfhir/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
alias(libs.plugins.spezi.library)
alias(libs.plugins.spezi.hilt)
}

android {
namespace = "edu.stanford.spezi.modules.healthconnectonfhir"
}

dependencies {
api(libs.androidx.health.connect.client)
api(libs.hapi.fhir.structures.r4)
}
4 changes: 4 additions & 0 deletions modules/healthconnectonfhir/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
vishnuravi marked this conversation as resolved.
Show resolved Hide resolved
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package edu.stanford.healthconnectonfhir

data class MappedUnit(val unit: String, val system: String, val code: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package edu.stanford.healthconnectonfhir

import androidx.health.connect.client.records.Record
import org.hl7.fhir.r4.model.Observation

interface RecordToObservationMapper {
/**
* Maps a given Health Connect record to a list of HL7 FHIR Observations
*
* @param T the type of the Health Connect record, extending from `Record`
* @param record the record to be mapped
* @return a list of `Observation` objects derived from the provided health record
*/
fun <T : Record> map(record: T): List<Observation>
}
Loading
Loading