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

Add basic support for HKWorkout samples #38

Merged
merged 13 commits into from
Nov 20, 2023
12 changes: 0 additions & 12 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,6 @@ jobs:
artifactname: HealthKitOnFHIR.xcresult
runsonlabels: '["macOS", "self-hosted"]'
scheme: HealthKitOnFHIR
build:
name: Build for CodeQL & Swift 5.7
permissions:
contents: read
security-events: write
uses: StanfordBDHG/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
runsonlabels: '["macos-13"]'
xcodeversion: 14.2.0
scheme: HealthKitOnFHIR
test: false
codeql: true
buildandtestuitests:
name: Build and Test UI Tests
uses: StanfordBDHG/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ jobs:
swiftlint:
name: SwiftLint
uses: StanfordBDHG/.github/.github/workflows/swiftlint.yml@v2
markdown_link_check:
name: Markdown Link Check
uses: StanfordBDHG/.github/.github/workflows/markdown-link-check.yml@v2
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ let resource = try sample.resource

### Observations

`HKQuantitySample`, `HKCategorySample`, `HKCorrelationSample`, and `HKElectrocardiogram` will be converted into FHIR [Observation](https://hl7.org/fhir/R4/observation.html) resources encapsulated in a [ResourceProxy](https://github.com/apple/FHIRModels/blob/main/HowTo/Instantiation.md#1-use-resourceproxy).
`HKQuantitySample`, `HKCategorySample`, `HKCorrelationSample`, `HKElectrocardiogram`, and `HKWorkout` will be converted into FHIR [Observation](https://hl7.org/fhir/R4/observation.html) resources encapsulated in a [ResourceProxy](https://github.com/apple/FHIRModels/blob/main/HowTo/Instantiation.md#1-use-resourceproxy).

```swift
let sample: HKQuantitySample = // ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public struct HKElectrocardiogramMapping: Decodable {
/// - categories: The FHIR categories defined as ``MappedCode``s used for the specified `HKElectrocardiogram`
/// - classification: Defines the mapping of the `classification` category sample of an `HKElectrocardiogram` to a FHIR observation.
/// - symptomsStatus: Defines the mapping of the `symptomsStatus` category sample of an `HKElectrocardiogram` to a FHIR observation.
/// - numberOfVoltageMeasurements: efines the mapping of the `numberOfVoltageMeasurements` quantity property of an `HKElectrocardiogram` to a FHIR observation.
/// - numberOfVoltageMeasurements: Defines the mapping of the `numberOfVoltageMeasurements` quantity property of an `HKElectrocardiogram` to a FHIR observation.
/// - samplingFrequency: Defines the mapping of the `samplingFrequency` quantity property of an `HKElectrocardiogram` to a FHIR observation.
/// - averageHeartRate: Defines the mapping of the `averageHeartRate` quantity property of an `HKElectrocardiogram` to a FHIR observation.
/// - voltageMeasurements: Defines the mapping of the `voltageMeasurements` of an `HKElectrocardiogram` to a FHIR observation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public struct HKQuantitySampleMapping: Decodable {
public var unit: MappedUnit


/// An ``HKQuantitySampleMapping`` allows developers to customize the mapping of `HKQuantitySample`s to an FHIR observations.
/// An ``HKQuantitySampleMapping`` allows developers to customize the mapping of `HKQuantitySample`s to FHIR observations.
/// - Parameters:
/// - codings: The FHIR codings defined as ``MappedCode``s used for the specified `HKQuantitySample` type
/// - unit: The FHIR units defined as ``MappedUnit``s used for the specified `HKQuantitySample` type
Expand Down
16 changes: 12 additions & 4 deletions Sources/HealthKitOnFHIR/HKSampleMapping/HKSampleMapping.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public struct HKSampleMapping: Decodable {
case categorySampleMapping = "HKCategorySamples"
case correlationMapping = "HKCorrelations"
case electrocardiogramMapping = "HKElectrocardiogram"
case workoutSampleMapping = "HKWorkout"
}


Expand All @@ -35,7 +36,9 @@ public struct HKSampleMapping: Decodable {
public var correlationMapping: [HKCorrelationType: HKCorrelationMapping]
/// The ``HKSampleMapping/electrocardiogramMapping`` property defines the mapping of an`HKElectrocardiogramMapping` to a FHIR observation.
public var electrocardiogramMapping: HKElectrocardiogramMapping

/// The ``HKSampleMapping/workoutMapping`` property defines the mapping of an `HKWorkout` to a FHIR Observation.
public var workoutSampleMapping: HKWorkoutSampleMapping


public init(from decoder: Decoder) throws {
let mappings = try decoder.container(keyedBy: CodingKeys.self)
Expand Down Expand Up @@ -82,12 +85,15 @@ public struct HKSampleMapping: Decodable {
)

let electrocardiogramMapping = try mappings.decode(HKElectrocardiogramMapping.self, forKey: .electrocardiogramMapping)


let workoutSampleMapping = try mappings.decode(HKWorkoutSampleMapping.self, forKey: .workoutSampleMapping)

self.init(
quantitySampleMapping: quantitySampleMapping,
categorySampleMapping: categorySampleMapping,
correlationMapping: correlationMapping,
electrocardiogramMapping: electrocardiogramMapping
electrocardiogramMapping: electrocardiogramMapping,
workoutSampleMapping: workoutSampleMapping
)
}

Expand All @@ -99,11 +105,13 @@ public struct HKSampleMapping: Decodable {
quantitySampleMapping: [HKQuantityType: HKQuantitySampleMapping] = HKQuantitySampleMapping.default,
categorySampleMapping: [HKCategoryType: HKCategorySampleMapping] = HKCategorySampleMapping.default,
correlationMapping: [HKCorrelationType: HKCorrelationMapping] = HKCorrelationMapping.default,
electrocardiogramMapping: HKElectrocardiogramMapping = HKElectrocardiogramMapping.default
electrocardiogramMapping: HKElectrocardiogramMapping = HKElectrocardiogramMapping.default,
workoutSampleMapping: HKWorkoutSampleMapping = HKWorkoutSampleMapping.default
) {
self.quantitySampleMapping = quantitySampleMapping
self.categorySampleMapping = categorySampleMapping
self.correlationMapping = correlationMapping
self.electrocardiogramMapping = electrocardiogramMapping
self.workoutSampleMapping = workoutSampleMapping
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// This source file is part of the HealthKitOnFHIR open source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import HealthKit


/// An ``HKWorkoutSampleMapping`` allows developers to customize the mapping of `HKWorkout` samples to FHIR Observations.
public struct HKWorkoutSampleMapping: Decodable {
/// A default instance of an ``HKWorkoutSampleMapping`` allowing developers to customize the ``HKWorkoutSampleMapping``
/// The default values are loaded from the `HKSampleMapping.json` resource in the ``HealthKitOnFHIR`` Swift Package.
public static let `default` = HKSampleMapping.default.workoutSampleMapping

/// The FHIR codings defined as ``MappedCode``s to be used for `HKWorkout` samples
public var codings: [MappedCode]
/// The FHIR categories defined as ``MappedCode``s to be used for `HKWorkout` samples
public var categories: [MappedCode]


/// An ``HKWorkoutSampleMapping`` allows developers to customize the mapping of `HKWorkout`s to FHIR observations.
/// - Parameters:
/// - codings: The FHIR codings defined as ``MappedCode``s used for the `HKWorkout` sample
/// - categories: The FHIR categories defined as ``MappedCode``s used for the `HKWorkout` sample
public init(
codings: [MappedCode],
categories: [MappedCode]
) {
self.codings = codings
self.categories = categories
}

Check warning on line 34 in Sources/HealthKitOnFHIR/HKSampleMapping/HKWorkoutSampleMapping.swift

View check run for this annotation

Codecov / codecov/patch

Sources/HealthKitOnFHIR/HKSampleMapping/HKWorkoutSampleMapping.swift#L31-L34

Added lines #L31 - L34 were not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
//
// SPDX-License-Identifier: MIT
//

vishnuravi marked this conversation as resolved.
Show resolved Hide resolved
import HealthKit


protocol HKCategoryValueDescription: CustomStringConvertible {
var categoryValueDescription: String { get throws }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ extension HKSample {
try electrocardiogram.buildObservation(&observation, mappings: mapping)
case let clinicalRecord as HKClinicalRecord:
return try clinicalRecord.resource()
case let workout as HKWorkout:
try workout.buildWorkoutObservation(&observation)
default:
throw HealthKitOnFHIRError.notSupported
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import ModelsR4

extension HKSampleType {
/// Converts an `HKSampleType` into the corresponding FHIR resource type, defined as a `ResourceType`
public var resourceTyoe: ResourceType {
public var resourceType: ResourceType {
vishnuravi marked this conversation as resolved.
Show resolved Hide resolved
get throws {
switch self {
case is HKQuantityType, is HKCorrelationType, is HKCategoryType:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// This source file is part of the HealthKitOnFHIR open source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import HealthKit
import ModelsR4


extension HKWorkout {
/// Generates an observation that captures the type of physical activity performed for a single instance of physical activity, based on https://build.fhir.org/ig/HL7/physical-activity/StructureDefinition-pa-observation-activity-measure.html
/// Note: An `HKWorkout` object can also act as a container for other `HKSample` objects, which will need to be converted to observations individually.
func buildWorkoutObservation(
_ observation: inout Observation,
mappings: HKSampleMapping = HKSampleMapping.default
) throws {
let mapping = mappings.workoutSampleMapping

for code in mapping.codings {
observation.appendCoding(code.coding)
}

for category in mapping.categories {
observation.appendCategory(
CodeableConcept(coding: [category.coding])
)
}

let activityTypeString = self.workoutActivityType.description.asFHIRStringPrimitive()

let valueCodeableConcept = CodeableConcept(
coding: [
Coding(
code: activityTypeString,
system: "http://developer.apple.com/documentation/healthkit".asFHIRURIPrimitive()
)
]
)

observation.value = .codeableConcept(valueCodeableConcept)
}
}
Loading