Skip to content
Open
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
Empty file modified .github/.keep
100644 → 100755
Empty file.
Empty file modified .github/workflows/ci.yml
100644 → 100755
Empty file.
Empty file modified .github/workflows/stale.yml
100644 → 100755
Empty file.
3 changes: 2 additions & 1 deletion .gitignore
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
## Build generated
build/
DerivedData/
#Package.resolved
ResearchKit/
ParseCareKit-heroku.plist

## Various settings
.DS_Store
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "ResearchKit"]
path = ResearchKit
url = https://github.com/netreconlab/ResearchKit.git
2 changes: 2 additions & 0 deletions .swiftlint.yml
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
excluded: # paths to ignore during linting. Takes precedence over `included`.
- DerivedData
- ResearchKit
disabled_rules:
- multiple_closures_with_trailing_closure
- weak_delegate
- file_length
- function_body_length
Expand Down
Empty file modified CONTRIBUTING.md
100644 → 100755
Empty file.
274 changes: 264 additions & 10 deletions OCKSample.xcodeproj/project.pbxproj
100644 → 100755

Large diffs are not rendered by default.

Empty file.
Empty file.
Empty file.
Empty file modified OCKSample.xcodeproj/xcshareddata/xcschemes/OCKSample.xcscheme
100644 → 100755
Empty file.
Empty file modified OCKSample.xctestplan
100644 → 100755
Empty file.
Empty file modified OCKSample/AppDelegate.swift
100644 → 100755
Empty file.
34 changes: 32 additions & 2 deletions OCKSample/Constants.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ extension AppError: LocalizedError {
}

enum Constants {
static let parseConfigFileName = "ParseCareKit"
static let parseConfigFileName = "ParseCareKit" // -heroku
static let iOSParseCareStoreName = "iOSParseStore"
static let iOSLocalCareStoreName = "iOSLocalStore"
static let watchOSParseCareStoreName = "watchOSParseStore"
Expand All @@ -73,23 +73,53 @@ enum Constants {
static let parseUserSessionTokenKey = "requestParseSessionToken"
static let requestSync = "requestSync"
static let progressUpdate = "progressUpdate"
static let finishedAskingForPermission = "finishedAskingForPermission"
static let completedFirstSyncAfterLogin = "completedFirstSyncAfterLogin"
static let finishedAskingForPermission = "finishedAskingForPermission"
static let shouldRefreshView = "shouldRefreshView"
static let userLoggedIn = "userLoggedIn"
static let storeInitialized = "storeInitialized"
static let userTypeKey = "userType"
static let card = "card"
static let survey = "survey"
}

enum MainViewPath {
case tabs
}

enum CareKitCard: String, CaseIterable, Identifiable {
var id: Self { self }
case button = "Button"
case checklist = "Checklist"
case featured = "Featured"
case grid = "Grid"
case instruction = "Instruction"
case labeledValue = "Labeled Value"
case link = "Link"
case numericProgress = "Numeric Progress"
case simple = "Simple"
case survey = "Survey"
case custom = "Custom" // xTODO: Should add any custom card you make to this enum.
}

enum CarePlanID: String, CaseIterable, Identifiable {
var id: Self { self }
case health // Add custom id's for your Care Plans, these are examples
case checkIn
}

enum TaskID {
static let doxylamine = "doxylamine"
static let nausea = "nausea"
static let stretch = "stretch"
static let kegels = "kegels"
static let steps = "steps"
static let repetition = "repetition"
static let washFace = "washFace"
static let dirtiness = "dirtiness"
static let shower = "shower"
static let shampoo = "shampoo"
static let towels = "towels"

static var ordered: [String] {
[Self.steps, Self.doxylamine, Self.kegels, Self.stretch, Self.nausea]
Expand Down
Empty file modified OCKSample/Environment/AppDelegateKey.swift
100644 → 100755
Empty file.
Empty file modified OCKSample/Environment/CustomStylerKey.swift
100644 → 100755
Empty file.
Empty file modified OCKSample/Environment/FontColorKey.swift
100644 → 100755
Empty file.
Empty file modified OCKSample/Environment/StoreManagerKey.swift
100644 → 100755
Empty file.
2 changes: 1 addition & 1 deletion OCKSample/Environment/TintColorFlipKey.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SwiftUI
struct TintColorFlipKey: EnvironmentKey {
static var defaultValue: UIColor {
#if os(iOS)
return UIColor { $0.userInterfaceStyle == .light ? #colorLiteral(red: 0.06253327429, green: 0.6597633362, blue: 0.8644603491, alpha: 1) : #colorLiteral(red: 0, green: 0.2858072221, blue: 0.6897063851, alpha: 1) }
return UIColor { $0.userInterfaceStyle == .light ? #colorLiteral(red: 1, green: 0.622412324, blue: 0.7744814754, alpha: 1) : #colorLiteral(red: 0.721568644, green: 0.8862745166, blue: 0.5921568871, alpha: 1) }
#else
return #colorLiteral(red: 0.06253327429, green: 0.6597633362, blue: 0.8644603491, alpha: 1)
#endif
Expand Down
2 changes: 1 addition & 1 deletion OCKSample/Environment/TintColorKey.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SwiftUI
struct TintColorKey: EnvironmentKey {
static var defaultValue: UIColor {
#if os(iOS)
return UIColor { $0.userInterfaceStyle == .light ? #colorLiteral(red: 0, green: 0.2858072221, blue: 0.6897063851, alpha: 1) : #colorLiteral(red: 0.06253327429, green: 0.6597633362, blue: 0.8644603491, alpha: 1) }
return UIColor { $0.userInterfaceStyle == .light ? #colorLiteral(red: 0.721568644, green: 0.8862745166, blue: 0.5921568871, alpha: 1) : #colorLiteral(red: 1, green: 0.6348608732, blue: 0.8093153834, alpha: 1) }
#else
return #colorLiteral(red: 0, green: 0.2855202556, blue: 0.6887390018, alpha: 1)
#endif
Expand Down
Empty file modified OCKSample/Extensions/AppDelegate+ParseRemoteDelegate.swift
100644 → 100755
Empty file.
Empty file modified OCKSample/Extensions/AppDelegate+UIApplicationDelegate.swift
100644 → 100755
Empty file.
Empty file modified OCKSample/Extensions/Calendar+Dates.swift
100644 → 100755
Empty file.
1 change: 1 addition & 0 deletions OCKSample/Extensions/Logger.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extension Logger {
static let localSessionDelegate = Logger(subsystem: subsystem, category: "LocalSessionDelegate")
static let utility = Logger(subsystem: subsystem, category: "Utility")
static let contact = Logger(subsystem: subsystem, category: "Contact")
static let myContact = Logger(subsystem: subsystem, category: "MyContact")
static let login = Logger(subsystem: subsystem, category: "Login")
static let feed = Logger(subsystem: subsystem, category: "Feed")
static let watch = Logger(subsystem: subsystem, category: "Watch")
Expand Down
Empty file modified OCKSample/Extensions/OCKAnyEvent+CustomStringConvertable.swift
100644 → 100755
Empty file.
17 changes: 17 additions & 0 deletions OCKSample/Extensions/OCKAnyEvent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// OCKAnyEvent.swift
// OCKSample
//
// Created by Corey Baker on 11/11/22.
// Copyright © 2022 Network Reconnaissance Lab. All rights reserved.
//
import CareKitStore

extension OCKAnyEvent {

func answer(kind: String) -> Double {
let values = outcome?.values ?? []
let match = values.first(where: { $0.kind == kind })
return match?.doubleValue ?? 0
}
}
68 changes: 68 additions & 0 deletions OCKSample/Extensions/OCKAnyOutcome.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// OCKAnyOutcome.swift
// OCKSample
//
// Created by Corey Baker on 12/3/22.
// Copyright © 2022 Network Reconnaissance Lab. All rights reserved.
//

import CareKitStore

extension OCKAnyOutcome {

func answerDouble(kind: String) -> [Double] {
let doubleValues = values.compactMap({ value -> Double? in
guard value.kind == kind else {
return nil
}
guard let intValue = value.integerValue else {
return value.doubleValue
}
return Double(intValue)
})
return doubleValues
}

func answerString(kind: String) -> [String] {
let stringValues = values.compactMap { value -> String? in
guard value.kind == kind else {
return nil
}
return value.stringValue
}
return stringValues
}

func sortedOutcomeValuesByRecency() -> Self {
guard !self.values.isEmpty else { return self }
var newOutcome = self
let sortedValues = newOutcome.values.sorted {
$0.createdDate > $1.createdDate
}

newOutcome.values = sortedValues
return newOutcome
}

func sortedOutcomeValues() -> Self {
guard !self.values.isEmpty else { return self }
var newOutcome = self
let sortedValues = newOutcome.values.sorted {
if let value0 = $0.dateValue,
let value1 = $1.dateValue {
return value0 > value1
} else if let value0 = $0.integerValue,
let value1 = $1.integerValue {
return value0 > value1
} else if let value0 = $0.doubleValue,
let value1 = $1.doubleValue {
return value0 > value1
} else {
return false
}
}

newOutcome.values = sortedValues
return newOutcome
}
}
13 changes: 13 additions & 0 deletions OCKSample/Extensions/OCKBiologicalSex+Hashable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// OCKBiologicalSex+Hashable.swift
// OCKSample
//
// Created by Corey Baker on 11/7/22.
// Copyright © 2022 Network Reconnaissance Lab. All rights reserved.
//

import CareKitStore

// Needed to use OCKBiologicalSex in a Picker.
// Simple conformance to hashable protocol.
extension OCKBiologicalSex: Hashable { }
109 changes: 109 additions & 0 deletions OCKSample/Extensions/OCKEventAggregator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//
// OCKEventAggregator.swift
// OCKSample
//
// Created by Corey Baker on 12/3/22.
// Copyright © 2022 Network Reconnaissance Lab. All rights reserved.
//
import CareKitStore
import Foundation

extension OCKEventAggregator {

static func aggregatorMean(_ kind: String? = nil) -> OCKEventAggregator {
guard let kind = kind else {
return OCKEventAggregator.custom { events -> Double in

let totalCompleted = Double(events.map { $0.outcome?.values.count ?? 0 }.reduce(0, +))
let tempSumOfCompleted = events.compactMap {
$0.outcome?.values.compactMap { value -> Double in
guard let intValue = value.integerValue else {
guard let boolValue = value.booleanValue else {
return value.doubleValue ?? 0.0
}
return boolValue ? 1.0 : 0.0
}
return Double(intValue)
}.reduce(0, +)
}
let sumOfCompleted = Double(tempSumOfCompleted.compactMap { $0 }.reduce(0, +))

if totalCompleted == 0 {
return 0.0
} else {
return sumOfCompleted / totalCompleted
}
}
}

return OCKEventAggregator.custom { events -> Double in

let completed = events.compactMap { event -> [Double]? in
event.outcome?.answerDouble(kind: kind)
}.flatMap { $0 }

let totalCompleted = Double(completed.count)
let sumOfCompleted = Double(completed.compactMap { $0 }.reduce(0, +))

if totalCompleted == 0 {
return 0.0
} else {
return sumOfCompleted / totalCompleted
}
}
}

static func aggregatorMedian(_ kind: String? = nil) -> OCKEventAggregator {
guard let kind = kind else {
return OCKEventAggregator.custom { events -> Double in

let tempAllValues = events.compactMap {
$0.outcome?.values.compactMap { value -> Double in
guard let intValue = value.integerValue else {
guard let boolValue = value.booleanValue else {
return value.doubleValue ?? 0.0
}
return boolValue ? 1.0 : 0.0
}
return Double(intValue)
}
}
var sortedAllValues = tempAllValues.flatMap { $0 }
sortedAllValues.sort()

if sortedAllValues.isEmpty {
return 0.0
} else if (sortedAllValues.count % 2) == 0 {
let index = sortedAllValues.count / 2
return (sortedAllValues[index] + sortedAllValues[index - 1]) / 2.0
} else {
return sortedAllValues[sortedAllValues.count / 2]
}
}
}

return OCKEventAggregator.custom { events -> Double in

var completed = events.compactMap { event -> [Double]? in
event.outcome?.answerDouble(kind: kind)
}.flatMap { $0 }

completed.sort()

if completed.isEmpty {
return 0.0
} else if (completed.count % 2) == 0 {
let index = completed.count / 2
return (completed[index] + completed[index - 1]) / 2.0
} else {
return completed[completed.count / 2]
}
}
}

static func aggregatorStreak(_ kind: String? = nil) -> OCKEventAggregator {
return OCKEventAggregator.custom { events -> Double in
Double(events.map { $0.outcome?.values.first != nil ? 1 : 0 }.reduce(0, +))
}
}
}
3 changes: 3 additions & 0 deletions OCKSample/Extensions/OCKHealthKitPassthroughStore.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ extension OCKHealthKitPassthroughStore {
}
}

/*
xTODO: You need to tie an OCPatient and CarePlan to these tasks,
*/
func populateSampleData() async throws {

let schedule = OCKSchedule.dailyAtTime(
Expand Down
Empty file modified OCKSample/Extensions/OCKOutcome.swift
100644 → 100755
Empty file.
Empty file modified OCKSample/Extensions/OCKOutcomeValue+Identifiable.swift
100644 → 100755
Empty file.
Empty file modified OCKSample/Extensions/OCKPatient+Parse.swift
100644 → 100755
Empty file.
Loading