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 Timed Walk Test & Improve Rendering Possibilities for the QuestionnaireView #16

Merged
merged 32 commits into from Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f9bd2d0
initial commit
dguo8412 Oct 3, 2023
cb2423d
feat: implement functionality for timedWalk
dguo8412 Oct 4, 2023
7e872d5
feat: refactor to add dual functionality for QuestionnaireView and Fi…
dguo8412 Oct 5, 2023
bf8d5b9
fix: refactor code to account for ORKFileResult from FitnessCheck
dguo8412 Oct 5, 2023
3858189
test version
dguo8412 Oct 7, 2023
62cffc0
prints out data
dguo8412 Oct 10, 2023
b34770e
rough draft of manually coded walk test
dguo8412 Oct 11, 2023
53ac208
removed unnecessary location permissions
dguo8412 Oct 11, 2023
3dcbef0
enhanced UI and added timer functionality
dguo8412 Oct 17, 2023
49b6f66
fixed all functionality
dguo8412 Oct 18, 2023
12abf3a
refactor permissions
dguo8412 Oct 20, 2023
1666e5c
deleted TimedWalkView and refactored WalkTestDataSource, WalkTestCons…
dguo8412 Oct 23, 2023
e7b58a1
feat: added button enabling/disabling
dguo8412 Oct 23, 2023
c60424d
feat: added restart feature, reverted changes to questionnaire target…
dguo8412 Oct 27, 2023
ff3e916
Merge branch 'main' into daniel/timed-walk
dguo8412 Oct 30, 2023
802161d
extract walkTest to separate target + implement internal presentation…
dguo8412 Nov 2, 2023
696eb7e
added WalkTestError, refactored test app to support both WalkTest and…
dguo8412 Nov 2, 2023
28ee1d4
added WalkTestReponse extension to observation, added licenses.
dguo8412 Nov 3, 2023
3597d7a
refactored code with viewModel
dguo8412 Nov 7, 2023
2cd8300
edited bugs and added UITest for SpeziWalkTest
dguo8412 Nov 7, 2023
f5061ef
changed development team back
dguo8412 Nov 7, 2023
b87b084
edits to variable names and UI test comments
dguo8412 Nov 7, 2023
ae72798
create dummy pedometer results for UITests
dguo8412 Nov 8, 2023
92c39fa
edit UI and Response
dguo8412 Dec 1, 2023
34db5e0
Merge branch 'main' into daniel/timed-walk
PSchmiedmayer Mar 4, 2024
fe831b8
Update Walk Test
PSchmiedmayer Mar 4, 2024
480455f
Update Test
PSchmiedmayer Mar 4, 2024
cd0fbae
Update ResearchKit
PSchmiedmayer Mar 9, 2024
f352051
Remodel Results
PSchmiedmayer Mar 9, 2024
ecb86af
Improve Stability
PSchmiedmayer Mar 10, 2024
fc88f66
Update Documentation
PSchmiedmayer Mar 10, 2024
9be97d8
Add Documentation
PSchmiedmayer Mar 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build-and-test.yml
Expand Up @@ -20,9 +20,9 @@ jobs:
name: Build and Test Swift Package
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: SpeziQuestionnaire.xcresult
artifactname: SpeziQuestionnaire-Package.xcresult
runsonlabels: '["macOS", "self-hosted"]'
scheme: SpeziQuestionnaire
scheme: SpeziQuestionnaire-Package
buildandtestuitests:
name: Build and Test UI Tests
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
Expand All @@ -36,4 +36,4 @@ jobs:
needs: [buildandtest, buildandtestuitests]
uses: StanfordSpezi/.github/.github/workflows/create-and-upload-coverage-report.yml@v2
with:
coveragereports: SpeziQuestionnaire.xcresult TestApp.xcresult
coveragereports: SpeziQuestionnaire-Package.xcresult TestApp.xcresult
1 change: 1 addition & 0 deletions .spi.yml
Expand Up @@ -12,3 +12,4 @@ builder:
- platform: ios
documentation_targets:
- SpeziQuestionnaire
- SpeziTimedWalkTest
15 changes: 13 additions & 2 deletions Package.swift
Expand Up @@ -13,16 +13,19 @@ import PackageDescription

let package = Package(
name: "SpeziQuestionnaire",
defaultLocalization: "en",
platforms: [
.iOS(.v17)
],
products: [
.library(name: "SpeziQuestionnaire", targets: ["SpeziQuestionnaire"])
.library(name: "SpeziQuestionnaire", targets: ["SpeziQuestionnaire"]),
.library(name: "SpeziTimedWalkTest", targets: ["SpeziTimedWalkTest"])
],
dependencies: [
.package(url: "https://github.com/StanfordSpezi/Spezi", from: "1.0.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziViews", from: "1.0.0"),
.package(url: "https://github.com/apple/FHIRModels", .upToNextMinor(from: "0.5.0")),
.package(url: "https://github.com/StanfordBDHG/ResearchKit", from: "2.2.25"),
.package(url: "https://github.com/StanfordBDHG/ResearchKit", from: "2.2.28"),
.package(url: "https://github.com/StanfordBDHG/ResearchKitOnFHIR", from: "1.1.0")
],
targets: [
Expand All @@ -42,6 +45,14 @@ let package = Package(
dependencies: [
.target(name: "SpeziQuestionnaire")
]
),
.target(
name: "SpeziTimedWalkTest",
dependencies: [
.product(name: "Spezi", package: "Spezi"),
.product(name: "SpeziViews", package: "SpeziViews"),
.product(name: "ModelsR4", package: "FHIRModels")
]
)
]
)
12 changes: 8 additions & 4 deletions Sources/SpeziQuestionnaire/QuestionnaireView.swift
Expand Up @@ -21,7 +21,7 @@ import SwiftUI
/// ```swift
/// struct ExampleQuestionnaireView: View {
/// @State var displayQuestionnaire = false
///
///
///
/// var body: some View {
/// Button("Display Questionnaire") {
Expand All @@ -42,11 +42,12 @@ public struct QuestionnaireView: View {
private let questionnaire: Questionnaire
private let questionnaireResult: (QuestionnaireResult) async -> Void
private let completionStepMessage: String?
private let cancelBehavior: CancelBehavior


public var body: some View {
if let task = createTask(questionnaire: questionnaire) {
ORKOrderedTaskView(tasks: task, tintColor: .accentColor, result: handleResult)
ORKOrderedTaskView(tasks: task, tintColor: .accentColor, cancelBehavior: cancelBehavior, result: handleResult)
.ignoresSafeArea(.container, edges: .bottom)
.ignoresSafeArea(.keyboard, edges: .bottom)
.interactiveDismissDisabled()
Expand All @@ -59,22 +60,25 @@ public struct QuestionnaireView: View {
/// - Parameters:
/// - questionnaire: The `Questionnaire` that should be displayed.
/// - completionStepMessage: Optional completion message that can be appended at the end of the questionnaire.
/// - cancelBehavior: The cancel behavior of view. The default setting allows cancellation and asks for confirmation before the view is dismissed.
/// - questionnaireResult: Result closure that processes the ``QuestionnaireResult``.
public init(
questionnaire: Questionnaire,
completionStepMessage: String? = nil,
cancelBehavior: CancelBehavior = .shouldConfirmCancel,
questionnaireResult: @escaping @MainActor (QuestionnaireResult) async -> Void
) {
self.questionnaire = questionnaire
self.completionStepMessage = completionStepMessage
self.cancelBehavior = cancelBehavior
self.questionnaireResult = questionnaireResult
}



private func handleResult(_ result: TaskResult) async {
let questionnaireResult: QuestionnaireResult
switch result {
case let .completed(result):
let fhirResponse = result.fhirResponse
questionnaireResult = .completed(result.fhirResponse)
case .cancelled:
questionnaireResult = .cancelled
Expand Down
17 changes: 17 additions & 0 deletions Sources/SpeziQuestionnaire/Resources/Localizable.xcstrings
@@ -0,0 +1,17 @@
{
"sourceLanguage" : "en",
"strings" : {
"QUESTIONNAIRE_LOADING_ERROR_MESSAGE" : {
"comment" : "This is a string that is balh",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Questionnaire could not be loaded."
}
}
}
}
},
"version" : "1.0"
}
@@ -0,0 +1,5 @@
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT
101 changes: 101 additions & 0 deletions Sources/SpeziTimedWalkTest/ObservationExtension.swift
@@ -0,0 +1,101 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
// Based on https://github.com/StanfordBDHG/HealthKitOnFHIR/blob/main/Sources/HealthKitOnFHIR/Observation%20Extensions/Observation%2BCollections.swift
//
// SPDX-License-Identifier: MIT
//

import Foundation
import HealthKit
import ModelsR4


extension Observation {
private func appendElement<T>(_ element: T, to collection: ReferenceWritableKeyPath<Observation, [T]?>) {
// swiftlint:disable:previous discouraged_optional_collection
// Unfortunately we need to use an optional collection here as the ModelsR4 modules uses optional collections in the Observation type.

guard self[keyPath: collection] != nil else {
self[keyPath: collection] = [element]
return
}

self[keyPath: collection]?.append(element)
}

private func appendElements<T>(_ elements: [T], to collection: ReferenceWritableKeyPath<Observation, [T]?>) {
// swiftlint:disable:previous discouraged_optional_collection
// Unfortunately we need to use an optional collection here as the ModelsR4 modules uses optional collections in the Observation type.

if self[keyPath: collection] == nil {
self[keyPath: collection] = []
self[keyPath: collection]?.reserveCapacity(elements.count)
} else {
self[keyPath: collection]?.reserveCapacity((self[keyPath: collection]?.count ?? 0) + elements.count)
}

for element in elements {
appendElement(element, to: collection)
}
}


func appendIdentifier(_ identifier: Identifier) {
appendElement(identifier, to: \.identifier)
}

func appendIdentifiers(_ identifiers: [Identifier]) {
appendElements(identifiers, to: \.identifier)
}

func appendCategory(_ category: CodeableConcept) {
appendElement(category, to: \.category)
}

func appendCategories(_ categories: [CodeableConcept]) {
appendElements(categories, to: \.category)
}

func appendCoding(_ coding: Coding) {
appendElement(coding, to: \.code.coding)
}

func appendCodings(_ codings: [Coding]) {
appendElements(codings, to: \.code.coding)
}

func appendComponent(_ component: ObservationComponent) {
appendElement(component, to: \.component)
}

func appendComponents(_ components: [ObservationComponent]) {
appendElements(components, to: \.component)
}

func setEffective(startDate: Date, endDate: Date) {
if startDate == endDate {
effective = .dateTime(FHIRPrimitive(try? DateTime(date: startDate)))
} else {
effective = .period(
Period(
end: FHIRPrimitive(try? DateTime(date: endDate)),
start: FHIRPrimitive(try? DateTime(date: startDate))
)
)
}
}

func setIssued(on date: Date) {
issued = FHIRPrimitive(try? Instant(date: date))
}

func setValue(_ quantity: Quantity) {
value = .quantity(quantity)
}

func setValue(_ string: String) {
value = .string(string.asFHIRStringPrimitive())
}
}
99 changes: 99 additions & 0 deletions Sources/SpeziTimedWalkTest/Resources/Localizable.xcstrings
@@ -0,0 +1,99 @@
{
"sourceLanguage" : "en",
"strings" : {
"%lld" : {

},
"%lld m" : {

},
"Cancel" : {

},
"Cancel Timed Walk Test?" : {

},
"Cancel Walk Test" : {

},
"Distance:" : {

},
"Done" : {

},
"Invalid Data Error" : {

},
"Make yourself ready for the %@ minute walk test" : {

},
"Next" : {

},
"Pedometer access is not authorized" : {

},
"Pedometer data is invalid" : {

},
"Please go to the Settings App to authorize pedometer access for this application." : {

},
"Restart" : {

},
"Return" : {

},
"Start" : {

},
"Steps:" : {

},
"The %@ minute walk test will start in %@" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "The %1$@ minute walk test will start in %2$@"
}
}
}
},
"Timed Walk Test" : {

},
"Unauthorized Error" : {

},
"Unknown" : {

},
"Unknown Error" : {

},
"WALK_TEST_DEFAULT_COMPLETION_MESSAGE" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Great job completing the timed walk test. Please review your results and press \"Done\" to save your results."
}
}
}
},
"WALK_TEST_DEFAULT_TASK_DESCRIPTION %@" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Welcome to the timed minute walk test!\n\nPlease be sure that you have an enough space and time to walk for %@."
}
}
}
}
},
"version" : "1.0"
}
@@ -0,0 +1,5 @@
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,5 @@
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,5 @@
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,5 @@
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,5 @@
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,5 @@
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,5 @@
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT