-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathMockDoseStore.swift
More file actions
171 lines (133 loc) · 6.64 KB
/
MockDoseStore.swift
File metadata and controls
171 lines (133 loc) · 6.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//
// MockDoseStore.swift
// LoopTests
//
// Created by Anna Quinlan on 8/7/20.
// Copyright © 2020 LoopKit Authors. All rights reserved.
//
import HealthKit
import LoopKit
@testable import Loop
class MockDoseStore: DoseStoreProtocol {
var doseHistory: [DoseEntry]?
var sensitivitySchedule: InsulinSensitivitySchedule?
init(for scenario: DosingTestScenario = .flatAndStable) {
self.scenario = scenario // The store returns different effect values based on the scenario
self.pumpEventQueryAfterDate = scenario.currentDate
self.lastAddedPumpData = scenario.currentDate
self.doseHistory = loadHistoricDoses(scenario: scenario)
}
static let dateFormatter = ISO8601DateFormatter.localTimeDate()
var scenario: DosingTestScenario
var basalProfileApplyingOverrideHistory: BasalRateSchedule?
var delegate: DoseStoreDelegate?
var device: HKDevice?
var pumpRecordsBasalProfileStartEvents: Bool = false
var pumpEventQueryAfterDate: Date
var basalProfile: BasalRateSchedule?
// Default to the adult exponential insulin model
var insulinModelProvider: InsulinModelProvider = StaticInsulinModelProvider(ExponentialInsulinModelPreset.rapidActingAdult)
var longestEffectDuration: TimeInterval = ExponentialInsulinModelPreset.rapidActingAdult.effectDuration
var insulinSensitivitySchedule: InsulinSensitivitySchedule?
var sampleType: HKSampleType = HKQuantityType.quantityType(forIdentifier: .insulinDelivery)!
var authorizationRequired: Bool = false
var sharingDenied: Bool = false
var lastReservoirValue: ReservoirValue?
var lastAddedPumpData: Date
func addPumpEvents(_ events: [NewPumpEvent], lastReconciliation: Date?, replacePendingEvents: Bool, completion: @escaping (DoseStore.DoseStoreError?) -> Void) {
completion(nil)
}
func addReservoirValue(_ unitVolume: Double, at date: Date, completion: @escaping (ReservoirValue?, ReservoirValue?, Bool, DoseStore.DoseStoreError?) -> Void) {
completion(nil, nil, false, nil)
}
func insulinOnBoard(at date: Date, basalDosingEnd: Date? = nil, completion: @escaping (DoseStoreResult<InsulinValue>) -> Void) {
completion(.success(.init(startDate: scenario.currentDate, value: 9.5)))
}
func generateDiagnosticReport(_ completion: @escaping (String) -> Void) {
completion("")
}
func addDoses(_ doses: [DoseEntry], from device: HKDevice?, completion: @escaping (Error?) -> Void) {
completion(nil)
}
func getInsulinOnBoardValues(start: Date, end: Date?, basalDosingEnd: Date?, completion: @escaping (DoseStoreResult<[InsulinValue]>) -> Void) {
completion(.failure(.configurationError))
}
func getNormalizedDoseEntries(start: Date, end: Date?, completion: @escaping (DoseStoreResult<[DoseEntry]>) -> Void) {
completion(.failure(.configurationError))
}
func executePumpEventQuery(fromQueryAnchor queryAnchor: DoseStore.QueryAnchor?, limit: Int, completion: @escaping (DoseStore.PumpEventQueryResult) -> Void) {
completion(.failure(DoseStore.DoseStoreError.configurationError))
}
func getTotalUnitsDelivered(since startDate: Date, completion: @escaping (DoseStoreResult<InsulinValue>) -> Void) {
completion(.failure(.configurationError))
}
func getGlucoseEffects(start: Date, end: Date? = nil, basalDosingEnd: Date? = Date(), completion: @escaping (_ result: DoseStoreResult<[GlucoseEffect]>) -> Void) {
if let doseHistory, let sensitivitySchedule, let basalProfile = basalProfileApplyingOverrideHistory {
// To properly know glucose effects at startDate, we need to go back another DIA hours
let doseStart = start.addingTimeInterval(-longestEffectDuration)
let doses = doseHistory.filterDateRange(doseStart, end)
let trimmedDoses = doses.map { (dose) -> DoseEntry in
guard dose.type != .bolus else {
return dose
}
return dose.trimmed(to: basalDosingEnd)
}
let annotatedDoses = trimmedDoses.annotated(with: basalProfile)
let glucoseEffects = annotatedDoses.glucoseEffects(insulinModelProvider: self.insulinModelProvider, longestEffectDuration: self.longestEffectDuration, insulinSensitivity: sensitivitySchedule, from: start, to: end)
completion(.success(glucoseEffects.filterDateRange(start, end)))
} else {
return completion(.success(getCannedGlucoseEffects()))
}
}
func getCannedGlucoseEffects() -> [GlucoseEffect] {
let fixture: [JSONDictionary] = loadFixture(fixtureToLoad)
let dateFormatter = ISO8601DateFormatter.localTimeDate()
return fixture.map {
return GlucoseEffect(
startDate: dateFormatter.date(from: $0["date"] as! String)!,
quantity: HKQuantity(
unit: HKUnit(from: $0["unit"] as! String),
doubleValue: $0["amount"] as! Double
)
)
}
}
}
extension MockDoseStore {
public var bundle: Bundle {
return Bundle(for: type(of: self))
}
public func loadFixture<T>(_ resourceName: String) -> T {
let path = bundle.path(forResource: resourceName, ofType: "json")!
return try! JSONSerialization.jsonObject(with: Data(contentsOf: URL(fileURLWithPath: path)), options: []) as! T
}
var fixtureToLoad: String {
switch scenario {
case .liveCapture:
fatalError("live capture scenario computes effects from doses, does not used pre-canned effects")
case .flatAndStable:
return "flat_and_stable_insulin_effect"
case .highAndStable:
return "high_and_stable_insulin_effect"
case .highAndRisingWithCOB:
return "high_and_rising_with_cob_insulin_effect"
case .lowAndFallingWithCOB:
return "low_and_falling_insulin_effect"
case .lowWithLowTreatment:
return "low_with_low_treatment_insulin_effect"
case .highAndFalling:
return "high_and_falling_insulin_effect"
}
}
public func loadHistoricDoses(scenario: DosingTestScenario) -> [DoseEntry]? {
if let url = bundle.url(forResource: scenario.fixturePrefix + "doses", withExtension: "json"),
let data = try? Data(contentsOf: url)
{
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return try? decoder.decode([DoseEntry].self, from: data)
} else {
return nil
}
}
}