|
|
@@ -9,6 +9,32 @@
|
|
|
import Foundation
|
|
|
|
|
|
extension MainViewController {
|
|
|
+
|
|
|
+ private func parseDate(_ rawString: String) -> Date? {
|
|
|
+ var mutableDate = rawString
|
|
|
+
|
|
|
+ if mutableDate.hasSuffix("Z") {
|
|
|
+ mutableDate = String(mutableDate.dropLast())
|
|
|
+ }
|
|
|
+ else if let offsetRange = mutableDate.range(of: "[\\+\\-]\\d{2}:\\d{2}$",
|
|
|
+ options: .regularExpression) {
|
|
|
+ mutableDate.removeSubrange(offsetRange)
|
|
|
+ }
|
|
|
+
|
|
|
+ mutableDate = mutableDate.replacingOccurrences(
|
|
|
+ of: "\\.\\d+",
|
|
|
+ with: "",
|
|
|
+ options: .regularExpression
|
|
|
+ )
|
|
|
+
|
|
|
+ let dateFormatter = DateFormatter()
|
|
|
+ dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
|
|
|
+ dateFormatter.locale = Locale(identifier: "en_US")
|
|
|
+ dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
|
|
|
+
|
|
|
+ return dateFormatter.date(from: mutableDate)
|
|
|
+ }
|
|
|
+
|
|
|
// NS Temp Basal Response Processor
|
|
|
func processNSBasals(entries: [[String:AnyObject]]) {
|
|
|
infoManager.clearInfoData(type: .basal)
|
|
|
@@ -17,180 +43,128 @@ extension MainViewController {
|
|
|
|
|
|
var lastEndDot = 0.0
|
|
|
|
|
|
- let isoFormatter = ISO8601DateFormatter()
|
|
|
- isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
|
-
|
|
|
var tempArray = entries
|
|
|
tempArray.reverse()
|
|
|
|
|
|
for i in 0..<tempArray.count {
|
|
|
- let currentEntry = tempArray[i] as [String : AnyObject]?
|
|
|
-
|
|
|
- var basalDateStr: String
|
|
|
- if let ts = currentEntry?["timestamp"] as? String {
|
|
|
- basalDateStr = ts
|
|
|
- } else if let ca = currentEntry?["created_at"] as? String {
|
|
|
- basalDateStr = ca
|
|
|
- } else {
|
|
|
- continue
|
|
|
- }
|
|
|
+ guard let currentEntry = tempArray[i] as [String : AnyObject]? else { continue }
|
|
|
|
|
|
- guard let dateParsed = isoFormatter.date(from: basalDateStr) else {
|
|
|
+ // Decide which field to parse
|
|
|
+ let dateString = currentEntry["timestamp"] as? String
|
|
|
+ ?? currentEntry["created_at"] as? String
|
|
|
+ guard let rawDateStr = dateString,
|
|
|
+ let dateParsed = parseDate(rawDateStr) else {
|
|
|
continue
|
|
|
}
|
|
|
- let dateTimeStamp = dateParsed.timeIntervalSince1970
|
|
|
|
|
|
- guard let basalRate = currentEntry?["absolute"] as? Double else {
|
|
|
- if UserDefaultsRepository.debugLog.value {
|
|
|
- self.writeDebugLog(value: "ERROR: Null Basal entry")
|
|
|
- }
|
|
|
+ let dateTimeStamp = dateParsed.timeIntervalSince1970
|
|
|
+ guard let basalRate = currentEntry["absolute"] as? Double else {
|
|
|
+ self.writeDebugLog(value: "ERROR: Null Basal entry")
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
- var duration = 0.0
|
|
|
- if let durationValue = currentEntry?["duration"] as? Double {
|
|
|
- duration = durationValue
|
|
|
- } else {
|
|
|
- print("No Duration Found")
|
|
|
- }
|
|
|
+ let duration = currentEntry["duration"] as? Double ?? 0.0
|
|
|
|
|
|
- // *** Check for any gap since the previous entry
|
|
|
if i > 0 {
|
|
|
let priorEntry = tempArray[i - 1] as [String : AnyObject]?
|
|
|
-
|
|
|
- var priorBasalDateStr: String
|
|
|
- if let ts = priorEntry?["timestamp"] as? String {
|
|
|
- priorBasalDateStr = ts
|
|
|
- } else if let ca = priorEntry?["created_at"] as? String {
|
|
|
- priorBasalDateStr = ca
|
|
|
- } else {
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- guard let priorDateParsed = isoFormatter.date(from: priorBasalDateStr) else {
|
|
|
- continue
|
|
|
- }
|
|
|
- let priorDateTimeStamp = priorDateParsed.timeIntervalSince1970
|
|
|
- let priorDuration = priorEntry?["duration"] as? Double ?? 0.0
|
|
|
-
|
|
|
- // if difference between time stamps is greater than the duration of the last entry, there is a gap. Give a 15 second leeway on the timestamp
|
|
|
- if Double(dateTimeStamp - priorDateTimeStamp) > Double((priorDuration * 60) + 15) {
|
|
|
-
|
|
|
- var scheduled = 0.0
|
|
|
- let midGap = false
|
|
|
- var midGapTime: TimeInterval = 0
|
|
|
- var midGapValue: Double = 0
|
|
|
-
|
|
|
- for b in 0..<self.basalScheduleData.count {
|
|
|
- let priorEnd = priorDateTimeStamp + (priorDuration * 60)
|
|
|
- if priorEnd >= basalScheduleData[b].date {
|
|
|
- scheduled = basalScheduleData[b].basalRate
|
|
|
-
|
|
|
- if b < self.basalScheduleData.count - 1 {
|
|
|
- if dateTimeStamp > self.basalScheduleData[b + 1].date {
|
|
|
- // midGap = true
|
|
|
- // TODO: finish this to handle mid-gap items without crashing from overlapping entries
|
|
|
- midGapTime = self.basalScheduleData[b + 1].date
|
|
|
- midGapValue = self.basalScheduleData[b + 1].basalRate
|
|
|
+ let priorDateStr = priorEntry?["timestamp"] as? String
|
|
|
+ ?? priorEntry?["created_at"] as? String
|
|
|
+ if let rawPrior = priorDateStr,
|
|
|
+ let priorDateParsed = parseDate(rawPrior) {
|
|
|
+
|
|
|
+ let priorDateTimeStamp = priorDateParsed.timeIntervalSince1970
|
|
|
+ let priorDuration = priorEntry?["duration"] as? Double ?? 0.0
|
|
|
+
|
|
|
+ if (dateTimeStamp - priorDateTimeStamp) > (priorDuration * 60) + 15 {
|
|
|
+ var scheduled = 0.0
|
|
|
+ var midGap = false
|
|
|
+ var midGapTime: TimeInterval = 0
|
|
|
+ var midGapValue: Double = 0
|
|
|
+
|
|
|
+ for b in 0..<basalScheduleData.count {
|
|
|
+ let priorEnd = priorDateTimeStamp + (priorDuration * 60)
|
|
|
+ if priorEnd >= basalScheduleData[b].date {
|
|
|
+ scheduled = basalScheduleData[b].basalRate
|
|
|
+ if b < basalScheduleData.count - 1 {
|
|
|
+ if dateTimeStamp > basalScheduleData[b + 1].date {
|
|
|
+ midGap = true
|
|
|
+ midGapTime = basalScheduleData[b + 1].date
|
|
|
+ midGapValue = basalScheduleData[b + 1].basalRate
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // Make the starting dot at the last ending dot
|
|
|
- let startDot = basalGraphStruct(
|
|
|
- basalRate: scheduled,
|
|
|
- date: priorDateTimeStamp + (priorDuration * 60)
|
|
|
- )
|
|
|
- basalData.append(startDot)
|
|
|
-
|
|
|
- if midGap {
|
|
|
- // Make the ending dot at the old scheduled basal
|
|
|
- let endDot1 = basalGraphStruct(basalRate: scheduled, date: midGapTime)
|
|
|
- basalData.append(endDot1)
|
|
|
- // Make the starting dot at the scheduled Time
|
|
|
- let startDot2 = basalGraphStruct(basalRate: midGapValue, date: midGapTime)
|
|
|
- basalData.append(startDot2)
|
|
|
- // Make the ending dot at the new basal value
|
|
|
- let endDot2 = basalGraphStruct(basalRate: midGapValue, date: dateTimeStamp)
|
|
|
- basalData.append(endDot2)
|
|
|
- } else {
|
|
|
- // Make the ending dot at the new starting dot
|
|
|
- let endDot = basalGraphStruct(basalRate: scheduled, date: dateTimeStamp)
|
|
|
- basalData.append(endDot)
|
|
|
+ let startDot = basalGraphStruct(basalRate: scheduled,
|
|
|
+ date: priorDateTimeStamp + (priorDuration * 60))
|
|
|
+ basalData.append(startDot)
|
|
|
+
|
|
|
+ if midGap {
|
|
|
+ let endDot1 = basalGraphStruct(basalRate: scheduled, date: midGapTime)
|
|
|
+ basalData.append(endDot1)
|
|
|
+ let startDot2 = basalGraphStruct(basalRate: midGapValue, date: midGapTime)
|
|
|
+ basalData.append(startDot2)
|
|
|
+ let endDot2 = basalGraphStruct(basalRate: midGapValue, date: dateTimeStamp)
|
|
|
+ basalData.append(endDot2)
|
|
|
+ } else {
|
|
|
+ let endDot = basalGraphStruct(basalRate: scheduled, date: dateTimeStamp)
|
|
|
+ basalData.append(endDot)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Make the starting dot
|
|
|
+ // Start dot
|
|
|
let startDot = basalGraphStruct(basalRate: basalRate, date: dateTimeStamp)
|
|
|
basalData.append(startDot)
|
|
|
|
|
|
- // Make the ending dot
|
|
|
- if i == tempArray.count - 1 && duration == 0.0 {
|
|
|
- // If it's the last one and has no duration
|
|
|
- lastEndDot = dateTimeStamp + (30 * 60)
|
|
|
- } else {
|
|
|
- lastEndDot = dateTimeStamp + (duration * 60)
|
|
|
+ // End dot
|
|
|
+ var lastDot = dateTimeStamp + (duration * 60)
|
|
|
+ if i == tempArray.count - 1, duration == 0.0 {
|
|
|
+ lastDot = dateTimeStamp + (30 * 60)
|
|
|
}
|
|
|
- latestBasal = Localizer.formatToLocalizedString(basalRate,
|
|
|
- maxFractionDigits: 2,
|
|
|
- minFractionDigits: 0)
|
|
|
+ latestBasal = Localizer.formatToLocalizedString(basalRate, maxFractionDigits: 2, minFractionDigits: 0)
|
|
|
|
|
|
- // *** Overlap check for next entry
|
|
|
+ // Overlap check
|
|
|
if i < tempArray.count - 1 {
|
|
|
let nextEntry = tempArray[i + 1] as [String : AnyObject]?
|
|
|
- var nextBasalDateStr: String
|
|
|
- if let ts = nextEntry?["timestamp"] as? String {
|
|
|
- nextBasalDateStr = ts
|
|
|
- } else if let ca = nextEntry?["created_at"] as? String {
|
|
|
- nextBasalDateStr = ca
|
|
|
- } else {
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- guard let nextDateParsed = isoFormatter.date(from: nextBasalDateStr) else {
|
|
|
- continue
|
|
|
- }
|
|
|
- let nextDateTimeStamp = nextDateParsed.timeIntervalSince1970
|
|
|
- if nextDateTimeStamp < (dateTimeStamp + (duration * 60)) {
|
|
|
- lastEndDot = nextDateTimeStamp
|
|
|
+ let nextDateStr = nextEntry?["timestamp"] as? String
|
|
|
+ ?? nextEntry?["created_at"] as? String
|
|
|
+ if let rawNext = nextDateStr,
|
|
|
+ let nextDateParsed = parseDate(rawNext) {
|
|
|
+
|
|
|
+ let nextDateTimeStamp = nextDateParsed.timeIntervalSince1970
|
|
|
+ if nextDateTimeStamp < (dateTimeStamp + (duration * 60)) {
|
|
|
+ lastDot = nextDateTimeStamp
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- let endDot = basalGraphStruct(basalRate: basalRate, date: lastEndDot)
|
|
|
+ let endDot = basalGraphStruct(basalRate: basalRate, date: lastDot)
|
|
|
basalData.append(endDot)
|
|
|
+ lastEndDot = lastDot
|
|
|
}
|
|
|
|
|
|
// If last basal was prior to right now, we need to create one last scheduled entry
|
|
|
if lastEndDot <= dateTimeUtils.getNowTimeIntervalUTC() {
|
|
|
var scheduled = 0.0
|
|
|
- for b in 0..<self.basalProfile.count {
|
|
|
- let scheduleTimeToday = self.basalProfile[b].timeAsSeconds
|
|
|
+ for b in 0..<basalProfile.count {
|
|
|
+ let scheduleTimeToday = basalProfile[b].timeAsSeconds
|
|
|
+ dateTimeUtils.getTimeIntervalMidnightToday()
|
|
|
if lastEndDot >= scheduleTimeToday {
|
|
|
scheduled = basalProfile[b].value
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- latestBasal = Localizer.formatToLocalizedString(
|
|
|
- scheduled,
|
|
|
- maxFractionDigits: 2,
|
|
|
- minFractionDigits: 0
|
|
|
- )
|
|
|
-
|
|
|
- // Make the starting dot at the last ending dot
|
|
|
- let startDot = basalGraphStruct(
|
|
|
- basalRate: scheduled,
|
|
|
- date: lastEndDot
|
|
|
- )
|
|
|
+ latestBasal = Localizer.formatToLocalizedString(scheduled,
|
|
|
+ maxFractionDigits: 2,
|
|
|
+ minFractionDigits: 0)
|
|
|
+
|
|
|
+ let startDot = basalGraphStruct(basalRate: scheduled, date: lastEndDot)
|
|
|
basalData.append(startDot)
|
|
|
|
|
|
- // Make the ending dot 10 minutes after now
|
|
|
- let endDot = basalGraphStruct(
|
|
|
- basalRate: scheduled,
|
|
|
- date: Date().timeIntervalSince1970 + (60 * 10)
|
|
|
- )
|
|
|
+ let endDot = basalGraphStruct(basalRate: scheduled,
|
|
|
+ date: Date().timeIntervalSince1970 + (60 * 10))
|
|
|
basalData.append(endDot)
|
|
|
}
|
|
|
|
|
|
@@ -198,7 +172,8 @@ extension MainViewController {
|
|
|
updateBasalGraph()
|
|
|
}
|
|
|
|
|
|
- if let profileBasal = profileManager.currentBasal(), profileBasal != latestBasal {
|
|
|
+ if let profileBasal = profileManager.currentBasal(),
|
|
|
+ profileBasal != latestBasal {
|
|
|
latestBasal = "\(profileBasal) → \(latestBasal)"
|
|
|
}
|
|
|
infoManager.updateInfoData(type: .basal, value: latestBasal)
|