Skip to content

Commit

Permalink
Improve ECG Mapping With a Precision Setting (#45)
Browse files Browse the repository at this point in the history
# Improve ECG Mapping With a Precision Setting

## ♻️ Current situation & Problem
- ECG voltage values are currently mapped to a string representation
discarding their precision and reasonable number of decimal values.

## ⚙️ Release Notes 
- Improve ECG Mapping With a Precision Setting


### Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
PSchmiedmayer authored Aug 26, 2024
1 parent b0cfe35 commit c24e316
Show file tree
Hide file tree
Showing 16 changed files with 104 additions and 45 deletions.
12 changes: 11 additions & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,27 @@ jobs:
name: Build and Test Swift Package
uses: StanfordBDHG/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
runsonlabels: '["macOS", "self-hosted"]'
scheme: HealthKitOnFHIR
artifactname: HealthKitOnFHIR.xcresult
buildandtestlatest:
name: Build and Test Swift Package Latest
uses: StanfordBDHG/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
runsonlabels: '["macOS", "self-hosted"]'
scheme: HealthKitOnFHIR
xcodeversion: latest
swiftVersion: 6
resultBundle: HealthKitOnFHIRLatest.xcresult
artifactname: HealthKitOnFHIRLatest.xcresult
buildandtestuitests:
name: Build and Test UI Tests
uses: StanfordBDHG/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: TestApp.xcresult
runsonlabels: '["macOS", "self-hosted"]'
path: Tests/UITests
scheme: TestApp
artifactname: TestApp.xcresult
uploadcoveragereport:
name: Upload Coverage Report
needs: [buildandtest, buildandtestuitests]
Expand Down
41 changes: 38 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.7
// swift-tools-version:5.10

//
// This source file is part of the HealthKitOnFHIR open source project
Expand All @@ -8,11 +8,20 @@
// SPDX-License-Identifier: MIT
//

import class Foundation.ProcessInfo
import PackageDescription


#if swift(<6)
let swiftConcurrency: SwiftSetting = .enableExperimentalFeature("StrictConcurrency")
#else
let swiftConcurrency: SwiftSetting = .enableUpcomingFeature("StrictConcurrency")
#endif


let package = Package(
name: "HealthKitOnFHIR",
defaultLocalization: "en",
platforms: [
.iOS(.v16)
],
Expand All @@ -30,13 +39,39 @@ let package = Package(
],
resources: [
.process("Resources")
]
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
),
.testTarget(
name: "HealthKitOnFHIRTests",
dependencies: [
.target(name: "HealthKitOnFHIR")
]
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
)
]
)


func swiftLintPlugin() -> [Target.PluginUsage] {
// Fully quit Xcode and open again with `open --env SPEZI_DEVELOPMENT_SWIFTLINT /Applications/Xcode.app`
if ProcessInfo.processInfo.environment["SPEZI_DEVELOPMENT_SWIFTLINT"] != nil {
[.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLint")]
} else {
[]
}
}

func swiftLintPackage() -> [PackageDescription.Package.Dependency] {
if ProcessInfo.processInfo.environment["SPEZI_DEVELOPMENT_SWIFTLINT"] != nil {
[.package(url: "https://github.com/realm/SwiftLint.git", from: "0.55.1")]
} else {
[]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import HealthKit


/// An ``HKCategorySampleMapping`` allows developers to customize the mapping of `HKCategorySample`s to FHIR observations.
public struct HKCategorySampleMapping: Decodable {
public struct HKCategorySampleMapping: Decodable, Sendable {
/// A default instance of an ``HKCategorySampleMapping`` instance allowing developers to customize the ``HKCategorySampleMapping``.
///
/// The default values are loaded from the `HKSampleMapping.json` resource in the ``HealthKitOnFHIR`` Swift Package.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import HealthKit


/// An ``HKCorrelationMapping`` allows developers to customize the mapping of `HKCorrelation`s to an FHIR observations.
public struct HKCorrelationMapping: Decodable {
public struct HKCorrelationMapping: Decodable, Sendable {
/// A default instance of an ``HKCorrelationMapping`` instance allowing developers to customize the ``HKCorrelationMapping``.
///
/// The default values are loaded from the `HKSampleMapping.json` resource in the ``HealthKitOnFHIR`` Swift Package.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//


/// An ``HKElectrocardiogramMapping`` allows developers to customize the mapping of an`HKElectrocardiogram` to a FHIR observation.
public struct HKElectrocardiogramMapping: Decodable {
/// An ``HKElectrocardiogramMapping`` allows developers to customize the mapping of an`HKElectrocardiogram` to an FHIR observation.
public struct HKElectrocardiogramMapping: Decodable, Sendable {
/// A default instance of an ``HKElectrocardiogramMapping`` instance allowing developers to customize the ``HKElectrocardiogramMapping``.
///
/// The default values are loaded from the `HKSampleMapping.json` resource in the ``HealthKitOnFHIR`` Swift Package.
Expand All @@ -19,39 +19,43 @@ public struct HKElectrocardiogramMapping: Decodable {
public var codings: [MappedCode]
/// The FHIR categories defined as ``MappedCode``s used for the `HKElectrocardiogram`.
public var categories: [MappedCode]
/// Defines the mapping of the `classification` category sample of an `HKElectrocardiogram` to a FHIR observation.
/// Defines the mapping of the `classification` category sample of an `HKElectrocardiogram` to an FHIR observation.
public var classification: HKCategorySampleMapping
/// Defines the mapping of the `symptomsStatus` category sample of an `HKElectrocardiogram` to a FHIR observation.
/// Defines the mapping of the `symptomsStatus` category sample of an `HKElectrocardiogram` to an FHIR observation.
public var symptomsStatus: HKCategorySampleMapping
/// Defines the mapping of the `numberOfVoltageMeasurements` quantity property of an `HKElectrocardiogram` to a FHIR observation.
/// Defines the mapping of the `numberOfVoltageMeasurements` quantity property of an `HKElectrocardiogram` to an FHIR observation.
public var numberOfVoltageMeasurements: HKQuantitySampleMapping
/// Defines the mapping of the `samplingFrequency` quantity property of an `HKElectrocardiogram` to a FHIR observation.
/// Defines the mapping of the `samplingFrequency` quantity property of an `HKElectrocardiogram` to an FHIR observation.
public var samplingFrequency: HKQuantitySampleMapping
/// Defines the mapping of the `averageHeartRate` quantity property of an `HKElectrocardiogram` to a FHIR observation.
/// Defines the mapping of the `averageHeartRate` quantity property of an `HKElectrocardiogram` to an FHIR observation.
public var averageHeartRate: HKQuantitySampleMapping
/// Defines the mapping of the `voltageMeasurements` of an `HKElectrocardiogram` to a FHIR observation.
/// Defines the mapping of the `voltageMeasurements` of an `HKElectrocardiogram` to an FHIR observation.
public var voltageMeasurements: HKQuantitySampleMapping
/// Defines the precision represented as the number of decimal values for the voltage measurement mapping of an `HKElectrocardiogram` to an FHIR observation.
public var voltagePrecision: UInt


/// An ``HKCorrelationMapping`` allows developers to customize the mapping of an`HKElectrocardiogram` to a FHIR observation.
/// An ``HKCorrelationMapping`` allows developers to customize the mapping of an`HKElectrocardiogram` to an FHIR observation.
/// - Parameters:
/// - codings: The FHIR codings defined as ``MappedCode``s used for the specified `HKElectrocardiogram`
/// - 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: 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.
/// - classification: Defines the mapping of the `classification` category sample of an `HKElectrocardiogram` to an FHIR observation.
/// - symptomsStatus: Defines the mapping of the `symptomsStatus` category sample of an `HKElectrocardiogram` to an FHIR observation.
/// - numberOfVoltageMeasurements: Defines the mapping of the `numberOfVoltageMeasurements` quantity property of an `HKElectrocardiogram` to an FHIR observation.
/// - samplingFrequency: Defines the mapping of the `samplingFrequency` quantity property of an `HKElectrocardiogram` to an FHIR observation.
/// - averageHeartRate: Defines the mapping of the `averageHeartRate` quantity property of an `HKElectrocardiogram` to an FHIR observation.
/// - voltageMeasurements: Defines the mapping of the `voltageMeasurements` of an `HKElectrocardiogram` to an FHIR observation.
/// - voltagePrecision: Defines the precision represented as the number of decimal values for the voltage measurement mapping of an `HKElectrocardiogram` to an FHIR observation.
public init(
codings: [MappedCode],
categories: [MappedCode],
classification: HKCategorySampleMapping,
symptomsStatus: HKCategorySampleMapping,
numberOfVoltageMeasurements: HKQuantitySampleMapping,
samplingFrequency: HKQuantitySampleMapping,
averageHeartRate: HKQuantitySampleMapping,
voltageMeasurements: HKQuantitySampleMapping
codings: [MappedCode] = Self.default.codings,
categories: [MappedCode] = Self.default.categories,
classification: HKCategorySampleMapping = Self.default.classification,
symptomsStatus: HKCategorySampleMapping = Self.default.symptomsStatus,
numberOfVoltageMeasurements: HKQuantitySampleMapping = Self.default.numberOfVoltageMeasurements,
samplingFrequency: HKQuantitySampleMapping = Self.default.samplingFrequency,
averageHeartRate: HKQuantitySampleMapping = Self.default.averageHeartRate,
voltageMeasurements: HKQuantitySampleMapping = Self.default.voltageMeasurements,
voltagePrecision: UInt = Self.default.voltagePrecision
) {
self.codings = codings
self.categories = categories
Expand All @@ -61,5 +65,6 @@ public struct HKElectrocardiogramMapping: Decodable {
self.samplingFrequency = samplingFrequency
self.averageHeartRate = averageHeartRate
self.voltageMeasurements = voltageMeasurements
self.voltagePrecision = voltagePrecision
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import HealthKit


/// An ``HKQuantitySampleMapping`` allows developers to customize the mapping of `HKQuantitySample`s to an FHIR observations.
public struct HKQuantitySampleMapping: Decodable {
public struct HKQuantitySampleMapping: Decodable, Sendable {
/// A default instance of an ``HKQuantitySampleMapping`` instance allowing developers to customize the ``HKQuantitySampleMapping``.
///
/// The default values are loaded from the `HKSampleMapping.json` resource in the ``HealthKitOnFHIR`` Swift Package.
Expand Down
6 changes: 3 additions & 3 deletions Sources/HealthKitOnFHIR/HKSampleMapping/HKSampleMapping.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import HealthKit


/// A ``HKSampleMapping`` instance is used to specify the mapping of `HKSample`s to FHIR observations allowing the customization of, e.g., codings and units.
public struct HKSampleMapping: Decodable {
public struct HKSampleMapping: Decodable, Sendable {
private enum CodingKeys: String, CodingKey {
case quantitySampleMapping = "HKQuantitySamples"
case categorySampleMapping = "HKCategorySamples"
Expand All @@ -34,9 +34,9 @@ public struct HKSampleMapping: Decodable {
public var categorySampleMapping: [HKCategoryType: HKCategorySampleMapping]
/// The ``HKSampleMapping/correlationMapping`` property defines the mapping of `HKCorrelationType`s to FHIR observations.
public var correlationMapping: [HKCorrelationType: HKCorrelationMapping]
/// The ``HKSampleMapping/electrocardiogramMapping`` property defines the mapping of an`HKElectrocardiogramMapping` to a FHIR observation.
/// The ``HKSampleMapping/electrocardiogramMapping`` property defines the mapping of an`HKElectrocardiogramMapping` to an FHIR observation.
public var electrocardiogramMapping: HKElectrocardiogramMapping
/// The ``HKSampleMapping/workoutMapping`` property defines the mapping of an `HKWorkout` to a FHIR Observation.
/// The ``HKSampleMapping/workoutMapping`` property defines the mapping of an `HKWorkout` to an FHIR Observation.
public var workoutSampleMapping: HKWorkoutSampleMapping


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import HealthKit


/// An ``HKWorkoutSampleMapping`` allows developers to customize the mapping of `HKWorkout` samples to FHIR Observations.
public struct HKWorkoutSampleMapping: Decodable {
public struct HKWorkoutSampleMapping: Decodable, Sendable {
/// 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
Expand All @@ -26,8 +26,8 @@ public struct HKWorkoutSampleMapping: Decodable {
/// - 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]
codings: [MappedCode] = Self.default.codings,
categories: [MappedCode] = Self.default.categories
) {
self.codings = codings
self.categories = categories
Expand Down
2 changes: 1 addition & 1 deletion Sources/HealthKitOnFHIR/HKSampleMapping/MappedCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ModelsR4


/// A ``MappedCode`` instance is used to specify codings for FHIR observations mapped from HealthKit's `HKSample`s.
public struct MappedCode: Decodable {
public struct MappedCode: Decodable, Sendable {
/// Symbol in syntax defined by the system.
public var code: String
/// Representation defined by the system.
Expand Down
2 changes: 1 addition & 1 deletion Sources/HealthKitOnFHIR/HKSampleMapping/MappedUnit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import HealthKit


/// A ``MappedUnit`` instance is used to specify a unit mapping for FHIR observations mapped from HealthKit's `HKUnit`s.
public struct MappedUnit: Decodable {
public struct MappedUnit: Decodable, Sendable {
private enum CodingKeys: String, CodingKey {
case hkunit
case unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ extension HKElectrocardiogram {
public typealias VoltageMeasurements = [(time: TimeInterval, value: HKQuantity)]


/// Creates a FHIR observation incorporating additional `Symptoms` and`VoltageMeasurements` collected in HealthKit.
/// Creates an FHIR observation incorporating additional `Symptoms` and`VoltageMeasurements` collected in HealthKit.
/// If you do not need `HKElectrocardiogram` specific context added you can use the generic `observation` extension on `HKSample`.
///
/// - Parameters:
Expand Down Expand Up @@ -249,11 +249,12 @@ extension HKElectrocardiogram {
// Check that we did not loose any data in the batching process.
assert(voltageMeasurements.count == voltageMeasurementBatches.reduce(0, { $0 + $1.count }))

let voltagePrecision = mappings.electrocardiogramMapping.voltagePrecision
for voltageMeasurementBatch in voltageMeasurementBatches {
// Create a space separated string of all the measurement values as defined by the mapping unit
let data = voltageMeasurementBatch
.map {
$0.value.doubleValue(for: mapping.unit.hkunit).description
String(format: "%.\(voltagePrecision)f", $0.value.doubleValue(for: mapping.unit.hkunit))
}
.joined(separator: " ")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ import ModelsR4


extension HKSample {
/// Converts an `HKSample` into a FHIR resource, encapsulated in a `ResourceProxy`
/// Converts an `HKSample` into an FHIR resource, encapsulated in a `ResourceProxy`
public var resource: ResourceProxy {
get throws {
try resource()
}
}


/// A `ResourceProxy` containing a FHIR `Observation` based on the concrete subclass of `HKSample`.
/// A `ResourceProxy` containing an FHIR `Observation` based on the concrete subclass of `HKSample`.
///
/// If a specific `HKSample` type is currently not supported the property returns an ``HealthKitOnFHIRError/notSupported`` error.
/// - Parameter withMapping: A mapping to map `HKSample`s to corresponding FHIR observations allowing the customization of, e.g., codings and units. See ``HKSampleMapping``.
/// - Returns: A `ResourceProxy`containing a FHIR `Observation` based on the concrete subclass of `HKSample`.
/// - Returns: A `ResourceProxy`containing an FHIR `Observation` based on the concrete subclass of `HKSample`.
public func resource(withMapping mapping: HKSampleMapping = HKSampleMapping.default) throws -> ResourceProxy {
var observation = Observation(
code: CodeableConcept(),
Expand Down
2 changes: 1 addition & 1 deletion Sources/HealthKitOnFHIR/HealthKitOnFHIRError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//


/// Error thrown by the HealthKitOnFHIR module if transforming a specific `HKSample` type to a FHIR resource was not possible.
/// Error thrown by the HealthKitOnFHIR module if transforming a specific `HKSample` type to an FHIR resource was not possible.
public enum HealthKitOnFHIRError: Error {
/// Indicates that a specific `HKSample` type is currently not supported by HealthKitOnFHIR.
case notSupported
Expand Down
3 changes: 2 additions & 1 deletion Sources/HealthKitOnFHIR/Resources/HKSampleMapping.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@
"system": "http://unitsofmeasure.org",
"unit": "uV"
}
}
},
"voltagePrecision": 3
},
"HKCategorySamples": {
"HKCategoryTypeIdentifierAppetiteChanges": {
Expand Down
3 changes: 3 additions & 0 deletions Tests/UITests/TestAppUITests/TestAppUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class TestAppUITests: XCTestCase {
}


@MainActor
func testHealthKitOnFHIR() throws {
let app = XCUIApplication()
app.launch()
Expand Down Expand Up @@ -49,6 +50,7 @@ class TestAppUITests: XCTestCase {
app.swipeDown(velocity: XCUIGestureVelocity.fast)
}

@MainActor
func testECGHealthKitMapping() throws {
let app = XCUIApplication()
app.launch()
Expand All @@ -71,6 +73,7 @@ class TestAppUITests: XCTestCase {
app.swipeDown(velocity: XCUIGestureVelocity.fast)
}

@MainActor
func testWorkoutMapping() throws {
let app = XCUIApplication()
app.launch()
Expand Down
4 changes: 4 additions & 0 deletions Tests/UITests/UITests.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
Expand Down Expand Up @@ -400,6 +402,8 @@
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
name = Release;
Expand Down

0 comments on commit c24e316

Please sign in to comment.