Bladeren bron

Handle parsing of CD pump history to JSON as flat object

dnzxy 2 jaren geleden
bovenliggende
commit
5ab248e20c
2 gewijzigde bestanden met toevoegingen van 179 en 64 verwijderingen
  1. 28 4
      FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift
  2. 151 60
      Model/Helper/PumpEvent+helper.swift

+ 28 - 4
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -146,12 +146,36 @@ final class OpenAPS {
                 self.storage.save(tempBasal, as: Monitor.tempBasal)
 
                 let a = self.loadFileFromStorage(name: OpenAPS.Monitor.pumpHistory)
+
                 let pumpHistory = self.fetchPumpHistory()
-                let pumpHistoryJSON = self.jsonConverter.convertToJSON(pumpHistory)
-//                print("pump history \(DebuggingIdentifiers.inProgress) \(String(describing: pumpHistory))")
+                var pumpHistoryJSON = ""
+
+                // TODO: maybe make this better and refactor the below loop logic to a util function?
+                if let events = pumpHistory {
+                    var dtos: [PumpEventDTO] = []
+
+                    for event in events {
+                        if let bolusDTO = event.toBolusDTOEnum() {
+                            dtos.append(bolusDTO)
+                        }
+                        if let tempBasalDTO = event.toTempBasalDTOEnum() {
+                            dtos.append(tempBasalDTO)
+                        }
+                        if let tempBasalDurationDTO = event.toTempBasalDurationDTOEnum() {
+                            dtos.append(tempBasalDurationDTO)
+                        }
+                    }
+
+                    let encoder = JSONEncoder()
+                    encoder.outputFormatting = .prettyPrinted
+                    if let jsonData = try? encoder.encode(dtos) {
+                        pumpHistoryJSON = String(data: jsonData, encoding: .utf8) ?? "[]"
+                    }
+                }
 
-                print("pump historyjson \(DebuggingIdentifiers.inProgress) \(pumpHistoryJSON)")
-                print("vorlage \(DebuggingIdentifiers.inProgress) \(a)")
+//                print("pump historyjson \(DebuggingIdentifiers.inProgress) \(pumpHistoryJSON)")
+//
+//                print("vorlage \(DebuggingIdentifiers.inProgress) \(a)")
 
                 // carbs
                 let carbs = self.fetchCarbs()

+ 151 - 60
Model/Helper/PumpEvent+helper.swift

@@ -57,75 +57,166 @@ extension NSPredicate {
     }
 }
 
-extension PumpEventStored: Encodable {
-    enum CodingKeys: String, CodingKey {
-        // pump event CD entitiy
+// extension PumpEventStored: Encodable {
+//    enum CodingKeys: String, CodingKey {
+//        // pump event CD entitiy
+//        case id
+//        case timestamp
+//        case type
+//        // bolus CD entitity
+//        case amount
+//        case isSMB
+//        case isExternal
+//        // temp basal CD entity
+//        case duration
+//        case rate
+//        case temp
+//    }
+//
+//    public func encode(to encoder: Encoder) throws {
+//        var containers = encoder.unkeyedContainer()
+//
+//        let dateFormatter = ISO8601DateFormatter()
+//        let formattedDate = dateFormatter.string(from: timestamp ?? Date())
+//
+//        if let tempBasal = self.tempBasal {
+//            // TempBasalDuration
+//            var tempBasalDurationContainer = containers.nestedContainer(keyedBy: CodingKeys.self)
+//            try tempBasalDurationContainer.encode("TempBasalDuration", forKey: .type)
+//            try tempBasalDurationContainer.encode(tempBasal.duration, forKey: .duration)
+//            try tempBasalDurationContainer.encode(formattedDate, forKey: .timestamp)
+//            try tempBasalDurationContainer.encode(id ?? UUID().uuidString, forKey: .id)
+//
+//            // TempBasal
+//            var tempBasalContainer = containers.nestedContainer(keyedBy: CodingKeys.self)
+//            try tempBasalContainer.encode("TempBasal", forKey: .type)
+//            if let rate = tempBasal.rate as Decimal? {
+//                try tempBasalContainer.encode(rate, forKey: .rate)
+//            } else {
+//                try tempBasalContainer.encode(0, forKey: .rate)
+//            }
+//            // its called "temp" in the json thats passed into determineBasal hence the undescriptive name of this coding key
+//            if let tempType = tempBasal.tempType {
+//                try tempBasalContainer.encode(tempType, forKey: .temp)
+//            } else {
+//                try tempBasalContainer.encode("absolute", forKey: .temp)
+//            }
+//            try tempBasalContainer.encode(formattedDate, forKey: .timestamp)
+//            // TempBasal and TempBasalDuration need to "relate" in the JSON, use same ID and prepemd with "_" here
+//            try tempBasalContainer.encode("_\(id ?? UUID().uuidString)", forKey: .id)
+//        }
+//
+//        // Encode specific to Bolus
+//        if let bolus = self.bolus {
+//            var bolusContainer = containers.nestedContainer(keyedBy: CodingKeys.self)
+//            try bolusContainer.encode("Bolus", forKey: .type)
+//            if let bolusAmount = bolus.amount as Decimal? {
+//                try bolusContainer.encode(bolusAmount, forKey: .amount)
+//            } else {
+//                // Default value
+//                try bolusContainer.encode(Decimal(0), forKey: .amount)
+//            }
+//            try bolusContainer.encode(bolus.isSMB, forKey: .isSMB)
+//            try bolusContainer.encode(bolus.isExternal, forKey: .isExternal)
+//            try bolusContainer.encode(formattedDate, forKey: .timestamp)
+//            try bolusContainer.encode(id ?? UUID().uuidString, forKey: .id)
+//        }
+//    }
+// }
+
+// Declare helper structs ("data transfer objects" = DTO) to utilize parsing a flattened pump history
+struct BolusDTO: Codable {
+    var id: String
+    var timestamp: String
+    var amount: Double
+    var isExternal: Bool
+    var isSMB: Bool
+    var duration: Int
+    var _type: String = "Bolus"
+}
+
+struct TempBasalDTO: Codable {
+    var id: String
+    var timestamp: String
+    var temp: String
+    var rate: Double
+    var _type: String = "TempBasal"
+}
+
+struct TempBasalDurationDTO: Codable {
+    var id: String
+    var timestamp: String
+    var duration: Int
+    var _type: String = "TempBasalDuration"
+
+    private enum CodingKeys: String, CodingKey {
         case id
         case timestamp
-        case type
-        // bolus CD entitity
-        case amount
-        case isSMB
-        case isExternal
-        // temp basal CD entity
-        case duration
-        case rate
-        case temp
+        case duration = "duration (min)"
+        case _type
     }
+}
 
-    public func encode(to encoder: Encoder) throws {
-        var container = encoder.container(keyedBy: CodingKeys.self)
-
-        let dateFormatter = ISO8601DateFormatter()
-        let formattedDate = dateFormatter.string(from: timestamp ?? Date())
-
-        // PumpEventStored
-        try container.encode(id, forKey: .id)
-        try container.encode(formattedDate, forKey: .timestamp)
-        try container.encode(type, forKey: .type)
+// Mask distinct DTO subtypes with a common enum that conforms to Encodable
+enum PumpEventDTO: Encodable {
+    case bolus(BolusDTO)
+    case tempBasal(TempBasalDTO)
+    case tempBasalDuration(TempBasalDurationDTO)
 
-        // access to BolusStored entity
-        //
-        // amount
-        if let bolusAmount = bolus?.amount as Decimal? {
-            try container.encode(bolusAmount, forKey: .amount)
-        } else {
-            // Default value
-            try container.encode(Decimal(0), forKey: .amount)
-        }
-        // isSMB
-        if let isSMB = bolus?.isSMB {
-            try container.encode(isSMB, forKey: .isSMB)
-        } else {
-            try container.encode(true, forKey: .amount)
-        }
-        // isExternal
-        if let isExternal = bolus?.isExternal {
-            try container.encode(isExternal, forKey: .isExternal)
-        } else {
-            try container.encode(false, forKey: .isExternal)
+    func encode(to encoder: Encoder) throws {
+        switch self {
+        case let .bolus(bolus):
+            try bolus.encode(to: encoder)
+        case let .tempBasal(tempBasal):
+            try tempBasal.encode(to: encoder)
+        case let .tempBasalDuration(tempBasalDuration):
+            try tempBasalDuration.encode(to: encoder)
         }
+    }
+}
 
-        // access to TempBasalStored entity
-        //
-        // duration
-        if let duration = tempBasal?.duration {
-            try container.encode(duration, forKey: .duration)
-        } else {
-            try container.encode(0, forKey: .duration)
+// Extension with helper functions to map pump events to DTO objects via uniform masking enum
+extension PumpEventStored {
+    func toBolusDTOEnum() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let bolus = bolus, let amount = bolus.amount else {
+            return nil
         }
-        // rate
-        if let rate = tempBasal?.rate as Decimal? {
-            try container.encode(rate, forKey: .rate)
-        } else {
-            try container.encode(0, forKey: .rate)
+        let dateFormatter = ISO8601DateFormatter()
+        let bolusDTO = BolusDTO(
+            id: id,
+            timestamp: dateFormatter.string(from: timestamp),
+            amount: amount.doubleValue,
+            isExternal: bolus.isExternal,
+            isSMB: bolus.isSMB,
+            duration: 0
+        )
+        return .bolus(bolusDTO)
+    }
+
+    func toTempBasalDTOEnum() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let tempBasal = tempBasal, let rate = tempBasal.rate else {
+            return nil
         }
-        // temp type
-        // its called "temp" in the json thats passed into determineBasal hence the undescriptive name of this coding key
-        if let tempType = tempBasal?.tempType {
-            try container.encode(tempType, forKey: .temp)
-        } else {
-            try container.encode("absolute", forKey: .temp)
+        let dateFormatter = ISO8601DateFormatter()
+        let tempBasalDTO = TempBasalDTO(
+            id: "_\(id)",
+            timestamp: dateFormatter.string(from: timestamp),
+            temp: tempBasal.tempType ?? "unknown",
+            rate: rate.doubleValue
+        )
+        return .tempBasal(tempBasalDTO)
+    }
+
+    func toTempBasalDurationDTOEnum() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let tempBasal = tempBasal else {
+            return nil
         }
+        let dateFormatter = ISO8601DateFormatter()
+        let tempBasalDurationDTO = TempBasalDurationDTO(
+            id: id,
+            timestamp: dateFormatter.string(from: timestamp),
+            duration: Int(tempBasal.duration)
+        )
+        return .tempBasalDuration(tempBasalDurationDTO)
     }
 }