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

Creates daily survey task with ResearchKit #6

Merged
merged 3 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 24 additions & 4 deletions StrokeCog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
2F4E23872989DB360013F3D9 /* ContactsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4E23862989DB360013F3D9 /* ContactsTests.swift */; };
2F4FC8D729EE69D300BFFE26 /* MockUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4FC8D629EE69D300BFFE26 /* MockUpload.swift */; };
2F5E32BD297E05EA003432F8 /* StrokeCogDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F5E32BC297E05EA003432F8 /* StrokeCogDelegate.swift */; };
2F6025CB29BBE70F0045459E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2F6025CA29BBE70F0045459E /* GoogleService-Info.plist */; };
2F65B44E2A3B8B0600A36932 /* NotificationPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F65B44D2A3B8B0600A36932 /* NotificationPermissions.swift */; };
2FA0BFED2ACC977500E0EF83 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2FA0BFEC2ACC977500E0EF83 /* Localizable.xcstrings */; };
2FB099AF2A875DF100B20952 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 2FB099AE2A875DF100B20952 /* FirebaseAuth */; };
Expand Down Expand Up @@ -73,6 +72,10 @@
63497B732BBF855E001F8419 /* OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63497B722BBF855E001F8419 /* OptionsPanel.swift */; };
63BBF8162BB8993B006890CE /* StudyIDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63BBF8152BB8993B006890CE /* StudyIDView.swift */; };
63BBF8192BB89CF7006890CE /* studyIDs.csv in Resources */ = {isa = PBXBuildFile; fileRef = 63BBF8182BB89CF7006890CE /* studyIDs.csv */; };
63EA5F7B2BC04F8400A48590 /* DailySurveyTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63EA5F7A2BC04F8400A48590 /* DailySurveyTask.swift */; };
63EA5F892BC78ADD00A48590 /* DailySurveyTaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63EA5F882BC78ADD00A48590 /* DailySurveyTaskView.swift */; };
63EA5F8C2BC78F8400A48590 /* DailySurveyResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63EA5F8B2BC78F8400A48590 /* DailySurveyResponse.swift */; };
63EA5F902BC7943F00A48590 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 63EA5F8F2BC7943F00A48590 /* GoogleService-Info.plist */; };
63F4C3992BBCCC300033D985 /* MapboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F4C3982BBCCC300033D985 /* MapboxView.swift */; };
63F4C39B2BBCCCF80033D985 /* LocationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F4C39A2BBCCCF80033D985 /* LocationModule.swift */; };
63F4C39D2BBCCD200033D985 /* LocationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F4C39C2BBCCD200033D985 /* LocationUtils.swift */; };
Expand Down Expand Up @@ -118,7 +121,6 @@
2F4E23862989DB360013F3D9 /* ContactsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsTests.swift; sourceTree = "<group>"; };
2F4FC8D629EE69D300BFFE26 /* MockUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUpload.swift; sourceTree = "<group>"; };
2F5E32BC297E05EA003432F8 /* StrokeCogDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StrokeCogDelegate.swift; sourceTree = "<group>"; };
2F6025CA29BBE70F0045459E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
2F65B44D2A3B8B0600A36932 /* NotificationPermissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissions.swift; sourceTree = "<group>"; };
2FA0BFEC2ACC977500E0EF83 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
2FAEC07F297F583900C11C42 /* StrokeCog.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = StrokeCog.entitlements; sourceTree = "<group>"; };
Expand Down Expand Up @@ -156,6 +158,10 @@
63497B722BBF855E001F8419 /* OptionsPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsPanel.swift; sourceTree = "<group>"; };
63BBF8152BB8993B006890CE /* StudyIDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyIDView.swift; sourceTree = "<group>"; };
63BBF8182BB89CF7006890CE /* studyIDs.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = studyIDs.csv; sourceTree = "<group>"; };
63EA5F7A2BC04F8400A48590 /* DailySurveyTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailySurveyTask.swift; sourceTree = "<group>"; };
63EA5F882BC78ADD00A48590 /* DailySurveyTaskView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailySurveyTaskView.swift; sourceTree = "<group>"; };
63EA5F8B2BC78F8400A48590 /* DailySurveyResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailySurveyResponse.swift; sourceTree = "<group>"; };
63EA5F8F2BC7943F00A48590 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
63F4C3982BBCCC300033D985 /* MapboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapboxView.swift; sourceTree = "<group>"; };
63F4C39A2BBCCCF80033D985 /* LocationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationModule.swift; sourceTree = "<group>"; };
63F4C39C2BBCCD200033D985 /* LocationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationUtils.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -239,7 +245,7 @@
children = (
2FAEC07F297F583900C11C42 /* StrokeCog.entitlements */,
653A258928339462005D4D48 /* Info.plist */,
2F6025CA29BBE70F0045459E /* GoogleService-Info.plist */,
63EA5F8F2BC7943F00A48590 /* GoogleService-Info.plist */,
2F1AC9DE2B4E840E00C24973 /* StrokeCog.docc */,
);
path = "Supporting Files";
Expand Down Expand Up @@ -347,6 +353,16 @@
path = Location;
sourceTree = "<group>";
};
63EA5F792BC04F6E00A48590 /* Survey */ = {
isa = PBXGroup;
children = (
63EA5F7A2BC04F8400A48590 /* DailySurveyTask.swift */,
63EA5F882BC78ADD00A48590 /* DailySurveyTaskView.swift */,
63EA5F8B2BC78F8400A48590 /* DailySurveyResponse.swift */,
);
path = Survey;
sourceTree = "<group>";
};
653A2544283387FE005D4D48 = {
isa = PBXGroup;
children = (
Expand All @@ -372,6 +388,7 @@
653A254F283387FE005D4D48 /* StrokeCog */ = {
isa = PBXGroup;
children = (
63EA5F792BC04F6E00A48590 /* Survey */,
653A2550283387FE005D4D48 /* StrokeCog.swift */,
2F5E32BC297E05EA003432F8 /* StrokeCogDelegate.swift */,
2FF53D8C2A8729D600042B76 /* StrokeCogStandard.swift */,
Expand Down Expand Up @@ -593,7 +610,7 @@
653A255528338800005D4D48 /* Assets.xcassets in Resources */,
2FC3439029EE6346002D773C /* SocialSupportQuestionnaire.json in Resources */,
2FA0BFED2ACC977500E0EF83 /* Localizable.xcstrings in Resources */,
2F6025CB29BBE70F0045459E /* GoogleService-Info.plist in Resources */,
63EA5F902BC7943F00A48590 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -645,6 +662,7 @@
2FE5DC3A29EDD7CA004B9AB4 /* Welcome.swift in Sources */,
2FE5DC3829EDD7CA004B9AB4 /* InterestingModules.swift in Sources */,
2FE5DC3529EDD7CA004B9AB4 /* Consent.swift in Sources */,
63EA5F8C2BC78F8400A48590 /* DailySurveyResponse.swift in Sources */,
2FE5DC4529EDD7F2004B9AB4 /* Binding+Negate.swift in Sources */,
63497B732BBF855E001F8419 /* OptionsPanel.swift in Sources */,
2FC975A82978F11A00BA99FE /* Home.swift in Sources */,
Expand Down Expand Up @@ -672,6 +690,8 @@
56F6F2A02AB441930022FE5A /* ContributionsList.swift in Sources */,
566155292AB8447C00209B80 /* Package+LicenseType.swift in Sources */,
5680DD392AB8983D004E6D4A /* PackageCell.swift in Sources */,
63EA5F7B2BC04F8400A48590 /* DailySurveyTask.swift in Sources */,
63EA5F892BC78ADD00A48590 /* DailySurveyTaskView.swift in Sources */,
2F5E32BD297E05EA003432F8 /* StrokeCogDelegate.swift in Sources */,
2FE5DC5229EDD7FA004B9AB4 /* StrokeCogScheduler.swift in Sources */,
63BBF8162BB8993B006890CE /* StudyIDView.swift in Sources */,
Expand Down
10 changes: 0 additions & 10 deletions StrokeCog/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,10 @@ import SwiftUI


struct HomeView: View {
enum Tabs: String {
case schedule
case contact
case mockUpload
case map
case profile
}

static var accountEnabled: Bool {
!FeatureFlags.disableFirebase && !FeatureFlags.skipOnboarding
}


@AppStorage(StorageKeys.homeTabSelection) private var selectedTab = Tabs.schedule
@State private var presentingAccount = false


Expand Down
5 changes: 3 additions & 2 deletions StrokeCog/Map/OptionsPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// Created by Vishnu Ravi on 4/4/24.
//


import ResearchKit
import ResearchKitSwiftUI
import Spezi
import SwiftUI

Expand Down Expand Up @@ -33,7 +34,7 @@ struct OptionsPanel: View {
)
}
.sheet(isPresented: $showingSurvey) {
// TODO: Launch StrokeCog Survey
DailySurveyTaskView(showingSurvey: $showingSurvey)
}
}

Expand Down
11 changes: 11 additions & 0 deletions StrokeCog/StrokeCogStandard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,17 @@ actor StrokeCogStandard: Standard, EnvironmentAccessible, HealthKitConstraint, O
.setData(from: dataPoint)
}

func add(response: DailySurveyResponse) async throws {
guard let details = await account.details else {
throw StrokeCogStandardError.userNotAuthenticatedYet
}

try await userDocumentReference
.collection("surveys")
.document(UUID().uuidString)
.setData(from: response)
}


private func healthKitDocument(id uuid: UUID) async throws -> DocumentReference {
try await userDocumentReference
Expand Down
16 changes: 16 additions & 0 deletions StrokeCog/Survey/DailySurveyResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// DailySurveyResponse.swift
// StrokeCog
//
// Created by Vishnu Ravi on 4/10/24.
//

import Foundation


struct DailySurveyResponse: Codable {
var socialInteractionQuestion: Int?
var leavingTheHouseQuestion: String?
var emotionalWellBeingQuestion: Int?
var physicalWellBeingQuestion: Int?
}
74 changes: 74 additions & 0 deletions StrokeCog/Survey/DailySurveyTask.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// DailySurveyTask.swift
// StrokeCog
//
// Created by Vishnu Ravi on 4/5/24.
//

import Foundation
import ResearchKit

class DailySurveyTask: ORKOrderedTask {
convenience init(identifier: String) {
// Initialize the array to hold the steps of the survey
var steps = [ORKStep]()

// Question 1: Close friends or family seen today
// swiftlint:disable legacy_objc_type
let answerFormat1 = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: [
ORKTextChoice(text: "0", value: 0 as NSNumber),
ORKTextChoice(text: "1-4", value: 1 as NSNumber),
ORKTextChoice(text: "5-10", value: 2 as NSNumber),
ORKTextChoice(text: "10 or more", value: 3 as NSNumber)
])
let question1Step = ORKQuestionStep(
identifier: "SocialInteractionQuestion",
title: "Social Interaction",
question: "How many close friends or family did you see today face to face?",
answer: answerFormat1
)
steps.append(question1Step)

// Question 2: Times left house
let answerFormat2 = ORKAnswerFormat.integerAnswerFormat(withUnit: nil)
answerFormat2.minimum = 0
let question2Step = ORKQuestionStep(
identifier: "LeavingTheHouseQuestion",
title: "Leaving the House",
question: "How many times today did you leave your house and engage meaningfully with others?",
answer: answerFormat2
)
steps.append(question2Step)

// Question 3: Happiness
let answerFormat3 = ORKAnswerFormat.booleanAnswerFormat()
let question3Step = ORKQuestionStep(
identifier: "EmotionalWellBeingQuestion",
title: "Emotional Well-being",
question: "I was happy",
answer: answerFormat3
)
steps.append(question3Step)

// Question 4: Fatigue
let answerFormat4 = ORKAnswerFormat.scale(
withMaximumValue: 4,
minimumValue: 0,
defaultValue: 0,
step: 1,
vertical: false,
maximumValueDescription: "Very much",
minimumValueDescription: "Not at all"
)
let question4Step = ORKQuestionStep(
identifier: "PhysicalWellBeingQuestion",
title: "Physical Well-being",
question: "I feel fatigued",
answer: answerFormat4
)
steps.append(question4Step)

// Initialize the ORKOrderedTask with the steps array
self.init(identifier: identifier, steps: steps)
}
}
64 changes: 64 additions & 0 deletions StrokeCog/Survey/DailySurveyTaskView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// DailySurveyTaskView.swift
// StrokeCog
//
// Created by Vishnu Ravi on 4/10/24.
//

import ResearchKit
import ResearchKitSwiftUI
import SwiftUI


struct DailySurveyTaskView: View {
@Environment(StrokeCogStandard.self) private var standard
@Binding var showingSurvey: Bool


var body: some View {
ORKOrderedTaskView(tasks: DailySurveyTask(identifier: "DailySurveyTask")) { result in
guard case let .completed(taskResult) = result else {
showingSurvey.toggle()
return
}

var response = DailySurveyResponse()

if let socialInteractionQuestion = taskResult.stepResult(forStepIdentifier: "SocialInteractionQuestion")?.results {
let answer = socialInteractionQuestion[0] as? ORKScaleQuestionResult
let result = answer?.scaleAnswer
response.socialInteractionQuestion = result?.intValue ?? -1
}

if let leavingTheHouseQuestion = taskResult.stepResult(forStepIdentifier: "LeavingTheHouseQuestion")?.results {
let answer = leavingTheHouseQuestion[0] as? ORKTextQuestionResult
let result = answer?.textAnswer
response.leavingTheHouseQuestion = result ?? "-1"
}

if let emotionalWellBeingQuestion = taskResult.stepResult(forStepIdentifier: "EmotionalWellBeingQuestion")?.results {
let answer = emotionalWellBeingQuestion[0] as? ORKBooleanQuestionResult
let result = answer?.booleanAnswer
response.emotionalWellBeingQuestion = result?.boolValue
}

if let physicalWellBeingQuestion = taskResult.stepResult(forStepIdentifier: "PhysicalWellBeingQuestion")?.results {
let answer = physicalWellBeingQuestion[0] as? ORKScaleQuestionResult
let result = answer?.scaleAnswer
response.physicalWellBeingQuestion = result?.intValue ?? -1
}

do {
try await standard.add(response: response)
} catch {
print("Error: \(error.localizedDescription)")
}

showingSurvey.toggle()
}
}
}

#Preview {
DailySurveyTaskView(showingSurvey: .constant(true))
}
Loading