Skip to content
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
41 changes: 1 addition & 40 deletions TidepoolServiceKit/Extensions/StoredSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import TidepoolKit
- preMealTargetRange ClosedRange<HKQuantity>? TPumpSettingsDatum.bloodGlucoseTargetPreprandial
- workoutTargetRange ClosedRange<HKQuantity>? TPumpSettingsDatum.bloodGlucoseTargetPhysicalActivity
- overridePresets [TemporaryScheduleOverridePreset]? TPumpSettingsDatum.overridePresets
- scheduleOverride TemporaryScheduleOverride? TPumpSettingsOverrideDeviceEventDatum.*
- preMealOverride TemporaryScheduleOverride? TPumpSettingsOverrideDeviceEventDatum.*
- maximumBasalRatePerHour Double? TPumpSettingsDatum.basal.rateMaximum.value
- maximumBolus Double? TPumpSettingsDatum.bolus.amountMaximum.value
- suspendThreshold GlucoseThreshold? TPumpSettingsDatum.bloodGlucoseSafetyLimit
Expand All @@ -38,7 +36,6 @@ import TidepoolKit
- syncIdentifier UUID .id, .origin, .payload["syncIdentifier"]

Notes:
- The active override (scheduleOverride or preMealOverride) are stored in TPumpSettingsOverrideDeviceEventDatum.
- Assumes same time zone for basalRateSchedule, glucoseTargetRangeSchedule, carbRatioSchedule, insulinSensitivitySchedule.
- StoredSettings.notificationSettings.carPlaySetting is not included as it is unneeded by backend.
- StoredSettings.notificationSettings.showPreviewsSetting is not included as it is unneeded by backend.
Expand Down Expand Up @@ -116,29 +113,6 @@ extension StoredSettings: IdentifiableDatum {
origin: origin)
}

func datumPumpSettingsOverrideDeviceEvent(for userId: String, hostIdentifier: String, hostVersion: String) -> TPumpSettingsOverrideDeviceEventDatum? {
guard let activeOverride = activeOverride else {
return nil
}
let datum = TPumpSettingsOverrideDeviceEventDatum(time: activeOverride.datumTime,
overrideType: activeOverride.datumOverrideType,
overridePreset: activeOverride.datumOverridePreset,
method: activeOverride.datumMethod,
duration: activeOverride.datumDuration,
expectedDuration: activeOverride.datumExpectedDuration,
bloodGlucoseTarget: activeOverride.datumBloodGlucoseTarget,
basalRateScaleFactor: activeOverride.datumBasalRateScaleFactor,
carbohydrateRatioScaleFactor: activeOverride.datumCarbohydrateRatioScaleFactor,
insulinSensitivityScaleFactor: activeOverride.datumInsulinSensitivityScaleFactor,
units: activeOverride.datumUnits)
let origin = datumOrigin(for: resolvedIdentifier(for: TPumpSettingsOverrideDeviceEventDatum.self), hostIdentifier: hostIdentifier, hostVersion: hostVersion)
return datum.adornWith(id: datumId(for: userId, type: TPumpSettingsOverrideDeviceEventDatum.self),
timeZone: datumTimeZone,
timeZoneOffset: datumTimeZoneOffset,
payload: datumPayload,
origin: origin)
}

var syncIdentifierAsString: String { syncIdentifier.uuidString }

private var datumTime: Date { date }
Expand Down Expand Up @@ -304,7 +278,7 @@ extension StoredSettings: IdentifiableDatum {
private var datumPumpName: String? { pumpDevice?.name }

private var datumPumpOverridePresets: [String: TPumpSettingsDatum.OverridePreset]? {
guard let overridePresets = overridePresets, !overridePresets.isEmpty else {
guard !overridePresets.isEmpty else {
return nil
}
return overridePresets.reduce(into: [:]) { $0[$1.name] = $1.datum }
Expand Down Expand Up @@ -333,19 +307,6 @@ extension StoredSettings: IdentifiableDatum {
return dictionary
}

private var activeOverride: TemporaryScheduleOverride? {
switch (preMealOverride, scheduleOverride) {
case (let preMealOverride?, nil):
return preMealOverride
case (nil, let scheduleOverride?):
return scheduleOverride
case (let preMealOverride?, let scheduleOverride?):
return preMealOverride.scheduledEndDate > date ? preMealOverride : scheduleOverride
case (nil, nil):
return nil
}
}

public static var activeScheduleNameDefault: String { "Default" }
}

Expand Down
116 changes: 29 additions & 87 deletions TidepoolServiceKit/TidepoolService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ public final class TidepoolService: Service, TAPIObserver, ObservableObject {

private var lastPumpSettingsDatum: TPumpSettingsDatum?

private var lastPumpSettingsOverrideDeviceEventDatum: TPumpSettingsOverrideDeviceEventDatum?

private var hostIdentifier: String?
private var hostVersion: String?

Expand Down Expand Up @@ -95,7 +93,6 @@ public final class TidepoolService: Service, TAPIObserver, ObservableObject {
self.lastControllerSettingsDatum = (rawState["lastControllerSettingsDatum"] as? Data).flatMap { try? Self.decoder.decode(TControllerSettingsDatum.self, from: $0) }
self.lastCGMSettingsDatum = (rawState["lastCGMSettingsDatum"] as? Data).flatMap { try? Self.decoder.decode(TCGMSettingsDatum.self, from: $0) }
self.lastPumpSettingsDatum = (rawState["lastPumpSettingsDatum"] as? Data).flatMap { try? Self.decoder.decode(TPumpSettingsDatum.self, from: $0) }
self.lastPumpSettingsOverrideDeviceEventDatum = (rawState["lastPumpSettingsOverrideDeviceEventDatum"] as? Data).flatMap { try? Self.decoder.decode(TPumpSettingsOverrideDeviceEventDatum.self, from: $0) }
self.session = try sessionStorage.getSession(for: sessionService)
Task {
await tapi.setSession(session)
Expand All @@ -118,7 +115,6 @@ public final class TidepoolService: Service, TAPIObserver, ObservableObject {
rawValue["lastControllerSettingsDatum"] = lastControllerSettingsDatum.flatMap { try? Self.encoder.encode($0) }
rawValue["lastCGMSettingsDatum"] = lastCGMSettingsDatum.flatMap { try? Self.encoder.encode($0) }
rawValue["lastPumpSettingsDatum"] = lastPumpSettingsDatum.flatMap { try? Self.encoder.encode($0) }
rawValue["lastPumpSettingsOverrideDeviceEventDatum"] = lastPumpSettingsOverrideDeviceEventDatum.flatMap { try? Self.encoder.encode($0) }
return rawValue
}

Expand Down Expand Up @@ -280,7 +276,32 @@ extension TidepoolService: TLogging {
extension TidepoolService: RemoteDataService {

public func uploadTemporaryOverrideData(updated: [TemporaryScheduleOverride], deleted: [TemporaryScheduleOverride], completion: @escaping (Result<Bool, Error>) -> Void) {
// TODO: Implement
// TODO: https://tidepool.atlassian.net/browse/LOOP-4769

// The following code is taken from previous upload code when override events where stored in settings
// To be implemented with

// guard let activeOverride = activeOverride else {
// return nil
// }
// let datum = TPumpSettingsOverrideDeviceEventDatum(time: activeOverride.datumTime,
// overrideType: activeOverride.datumOverrideType,
// overridePreset: activeOverride.datumOverridePreset,
// method: activeOverride.datumMethod,
// duration: activeOverride.datumDuration,
// expectedDuration: activeOverride.datumExpectedDuration,
// bloodGlucoseTarget: activeOverride.datumBloodGlucoseTarget,
// basalRateScaleFactor: activeOverride.datumBasalRateScaleFactor,
// carbohydrateRatioScaleFactor: activeOverride.datumCarbohydrateRatioScaleFactor,
// insulinSensitivityScaleFactor: activeOverride.datumInsulinSensitivityScaleFactor,
// units: activeOverride.datumUnits)
// let origin = datumOrigin(for: resolvedIdentifier(for: TPumpSettingsOverrideDeviceEventDatum.self), hostIdentifier: hostIdentifier, hostVersion: hostVersion)
// return datum.adornWith(id: datumId(for: userId, type: TPumpSettingsOverrideDeviceEventDatum.self),
// timeZone: datumTimeZone,
// timeZoneOffset: datumTimeZoneOffset,
// payload: datumPayload,
// origin: origin)

completion(.success(true))
}

Expand Down Expand Up @@ -448,7 +469,7 @@ extension TidepoolService: RemoteDataService {
return
}

let (created, updated, lastControllerSettingsDatum, lastCGMSettingsDatum, lastPumpSettingsDatum, lastPumpSettingsOverrideDeviceEventDatum) = calculateSettingsData(stored, for: userId, hostIdentifier: hostIdentifier, hostVersion: hostVersion)
let (created, updated, lastControllerSettingsDatum, lastCGMSettingsDatum, lastPumpSettingsDatum) = calculateSettingsData(stored, for: userId, hostIdentifier: hostIdentifier, hostVersion: hostVersion)

Task {
do {
Expand All @@ -457,7 +478,6 @@ extension TidepoolService: RemoteDataService {
self.lastControllerSettingsDatum = lastControllerSettingsDatum
self.lastCGMSettingsDatum = lastCGMSettingsDatum
self.lastPumpSettingsDatum = lastPumpSettingsDatum
self.lastPumpSettingsOverrideDeviceEventDatum = lastPumpSettingsOverrideDeviceEventDatum
self.completeUpdate()
completion(.success(createdUploaded || updatedUploaded))
} catch {
Expand All @@ -466,19 +486,12 @@ extension TidepoolService: RemoteDataService {
}
}

func calculateSettingsData(_ stored: [StoredSettings], for userId: String, hostIdentifier: String, hostVersion: String) -> ([TDatum], [TDatum], TControllerSettingsDatum?, TCGMSettingsDatum?, TPumpSettingsDatum?, TPumpSettingsOverrideDeviceEventDatum?) {
func calculateSettingsData(_ stored: [StoredSettings], for userId: String, hostIdentifier: String, hostVersion: String) -> ([TDatum], [TDatum], TControllerSettingsDatum?, TCGMSettingsDatum?, TPumpSettingsDatum?) {
var created: [TDatum] = []
var updated: [TDatum] = []
var lastControllerSettingsDatum = lastControllerSettingsDatum
var lastCGMSettingsDatum = lastCGMSettingsDatum
var lastPumpSettingsDatum = lastPumpSettingsDatum
var lastPumpSettingsOverrideDeviceEventDatum = lastPumpSettingsOverrideDeviceEventDatum

// A StoredSettings can generate a TPumpSettingsDatum and an optional TPumpSettingsOverrideDeviceEventDatum if there is an
// enabled override. Only upload the TPumpSettingsDatum or TPumpSettingsOverrideDeviceEventDatum if they have CHANGED.
// If the TPumpSettingsOverrideDeviceEventDatum has changed, then also re-upload the previous uploaded
// TPumpSettingsOverrideDeviceEventDatum with an updated duration and potentially expected duration, but only if the
// duration is calculated to be ended early.

stored.forEach {

Expand All @@ -493,15 +506,11 @@ extension TidepoolService: RemoteDataService {
let pumpSettingsDatum = $0.datumPumpSettings(for: userId, hostIdentifier: hostIdentifier, hostVersion: hostVersion)
let pumpSettingsDatumIsEffectivelyEquivalent = TPumpSettingsDatum.areEffectivelyEquivalent(old: lastPumpSettingsDatum, new: pumpSettingsDatum)

let pumpSettingsOverrideDeviceEventDatum = $0.datumPumpSettingsOverrideDeviceEvent(for: userId, hostIdentifier: hostIdentifier, hostVersion: hostVersion)
let pumpSettingsOverrideDeviceEventDatumIsEffectivelyEquivalent = TPumpSettingsOverrideDeviceEventDatum.areEffectivelyEquivalent(old: lastPumpSettingsOverrideDeviceEventDatum, new: pumpSettingsOverrideDeviceEventDatum)

// Associate the data

var controllerSettingsAssociations: [TAssociation] = []
var cgmSettingsAssociations: [TAssociation] = []
var pumpSettingsAssociations: [TAssociation] = []
var pumpSettingsOverrideDeviceEventAssociations: [TAssociation] = []

if let controllerSettingsDatum = controllerSettingsDatumIsEffectivelyEquivalent ? lastControllerSettingsDatum : controllerSettingsDatum {
let association = TAssociation(type: .datum, id: controllerSettingsDatum.id!, reason: "controllerSettings")
Expand All @@ -517,13 +526,11 @@ extension TidepoolService: RemoteDataService {
let association = TAssociation(type: .datum, id: pumpSettingsDatum.id!, reason: "pumpSettings")
controllerSettingsAssociations.append(association)
cgmSettingsAssociations.append(association)
pumpSettingsOverrideDeviceEventAssociations.append(association)
}

controllerSettingsDatum.append(associations: controllerSettingsAssociations)
cgmSettingsDatum.append(associations: cgmSettingsAssociations)
pumpSettingsDatum.append(associations: pumpSettingsAssociations)
pumpSettingsOverrideDeviceEventDatum?.append(associations: pumpSettingsOverrideDeviceEventAssociations)

// Upload and update the data, if necessary

Expand All @@ -541,27 +548,9 @@ extension TidepoolService: RemoteDataService {
created.append(pumpSettingsDatum)
lastPumpSettingsDatum = pumpSettingsDatum
}

if !pumpSettingsOverrideDeviceEventDatumIsEffectivelyEquivalent {

// If we need to update the duration of the last override, then do so
if let lastPumpSettingsOverrideDeviceEventDatum = lastPumpSettingsOverrideDeviceEventDatum,
lastPumpSettingsOverrideDeviceEventDatum.updateDuration(basedUpon: pumpSettingsOverrideDeviceEventDatum?.time ?? pumpSettingsDatum.time) {

// If it isn't already being created, then update it
if !created.contains(where: { $0 === lastPumpSettingsOverrideDeviceEventDatum }) {
updated.append(lastPumpSettingsOverrideDeviceEventDatum)
}
}

if let pumpSettingsOverrideDeviceEventDatum = pumpSettingsOverrideDeviceEventDatum {
created.append(pumpSettingsOverrideDeviceEventDatum)
}
lastPumpSettingsOverrideDeviceEventDatum = pumpSettingsOverrideDeviceEventDatum
}
}

return (created, updated, lastControllerSettingsDatum, lastCGMSettingsDatum, lastPumpSettingsDatum, lastPumpSettingsOverrideDeviceEventDatum)
return (created, updated, lastControllerSettingsDatum, lastCGMSettingsDatum, lastPumpSettingsDatum)
}

private func createData(_ data: [TDatum]) async throws -> Bool {
Expand Down Expand Up @@ -782,53 +771,6 @@ extension TPumpSettingsDatum: EffectivelyEquivalent {
}
}

extension TPumpSettingsOverrideDeviceEventDatum: EffectivelyEquivalent {

// All TDatum properties can be ignored EXCEPT time for this datum type
// Time is gather from the actual scheduled override and NOT the StoredSettings so it is valid and necessary for comparison
func isEffectivelyEquivalent(to other: TPumpSettingsOverrideDeviceEventDatum) -> Bool {
return self.time == other.time &&
self.overrideType == other.overrideType &&
self.overridePreset == other.overridePreset &&
self.method == other.method &&
self.duration == other.duration &&
self.expectedDuration == other.expectedDuration &&
self.bloodGlucoseTarget == other.bloodGlucoseTarget &&
self.basalRateScaleFactor == other.basalRateScaleFactor &&
self.carbohydrateRatioScaleFactor == other.carbohydrateRatioScaleFactor &&
self.insulinSensitivityScaleFactor == other.insulinSensitivityScaleFactor &&
self.units == other.units
}

var isEffectivelyEmpty: Bool {
return overrideType == nil &&
overridePreset == nil &&
method == nil &&
duration == nil &&
expectedDuration == nil &&
bloodGlucoseTarget == nil &&
basalRateScaleFactor == nil &&
carbohydrateRatioScaleFactor == nil &&
insulinSensitivityScaleFactor == nil &&
units == nil
}

func updateDuration(basedUpon endTime: Date?) -> Bool {
guard let endTime = endTime, let time = time, endTime > time else {
return false
}

let updatedDuration = time.distance(to: endTime)
guard duration == nil || updatedDuration < duration! else {
return false
}

self.expectedDuration = duration
self.duration = updatedDuration
return true
}
}

fileprivate extension TDosingDecisionDatum {

// Ignore reason and units as they are always specified
Expand Down
38 changes: 0 additions & 38 deletions TidepoolServiceKitTests/Extensions/StoredSettingsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -247,42 +247,6 @@ class StoredSettingsTests: XCTestCase {
)
}

func testDatumPumpSettingsOverrideDeviceEvent() {
let data = try! Self.encoder.encode(StoredSettings.test.datumPumpSettingsOverrideDeviceEvent(for: "1234567890", hostIdentifier: "Loop", hostVersion: "1.2.3"))
XCTAssertEqual(String(data: data, encoding: .utf8), """
{
"basalRateScaleFactor" : 0.5,
"bgTarget" : {
"high" : 90,
"low" : 80
},
"carbRatioScaleFactor" : 2,
"id" : "f89ad59a42430ab89dd2eab3a3e4df84",
"insulinSensitivityScaleFactor" : 2,
"method" : "manual",
"origin" : {
"id" : "2A67A303-1234-4CB8-1234-79498265368E:deviceEvent/pumpSettingsOverride",
"name" : "Loop",
"type" : "application",
"version" : "1.2.3"
},
"overrideType" : "preprandial",
"payload" : {
"syncIdentifier" : "2A67A303-1234-4CB8-1234-79498265368E"
},
"subType" : "pumpSettingsOverride",
"time" : "2020-05-14T14:38:39.000Z",
"timezone" : "America/Los_Angeles",
"timezoneOffset" : -420,
"type" : "deviceEvent",
"units" : {
"bg" : "mg/dL"
}
}
"""
)
}

private static let encoder: JSONEncoder = {
let encoder = JSONEncoder.tidepool
encoder.outputFormatting.insert(.prettyPrinted)
Expand Down Expand Up @@ -392,8 +356,6 @@ fileprivate extension StoredSettings {
preMealTargetRange: preMealTargetRange,
workoutTargetRange: workoutTargetRange,
overridePresets: overridePresets,
scheduleOverride: scheduleOverride,
preMealOverride: preMealOverride,
maximumBasalRatePerHour: maximumBasalRatePerHour,
maximumBolus: maximumBolus,
suspendThreshold: suspendThreshold,
Expand Down
Loading