Просмотр исходного кода

Merge remote-tracking branch 'refs/remotes/origin/bdb'

Conflicts:
	Config.xcconfig
	FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
avouspierre 3 лет назад
Родитель
Сommit
ad0374f3e5
41 измененных файлов с 430 добавлено и 377 удалено
  1. 2 2
      Config.xcconfig
  2. 40 40
      Dependencies/rileylink_ios/MinimedKitUI/tr.lproj/Localizable.strings
  3. 1 1
      FreeAPS/Resources/Info.plist
  4. 0 112
      FreeAPS/Resources/json/defaults/monitor/statistics.json
  5. 4 53
      FreeAPS/Sources/APS/APSManager.swift
  6. 1 1
      FreeAPS/Sources/APS/CGM/DexcomSource.swift
  7. 15 0
      FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift
  8. 8 2
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  9. 8 2
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  10. 8 2
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  11. 9 3
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  12. 5 2
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  13. 8 2
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  14. 8 2
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  15. 8 2
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  16. 8 2
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  17. 8 2
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  18. 8 2
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  19. 11 5
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  20. 8 2
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  21. 8 2
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  22. 8 2
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  23. 11 5
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  24. 8 2
      FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings
  25. 5 2
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  26. 39 33
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  27. 8 2
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  28. 8 2
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  29. 0 1
      FreeAPS/Sources/Models/Statistics.swift
  30. 1 0
      FreeAPS/Sources/Modules/DataTable/DataTableDataFlow.swift
  31. 4 0
      FreeAPS/Sources/Modules/DataTable/DataTableProvider.swift
  32. 11 0
      FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift
  33. 26 0
      FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift
  34. 0 1
      FreeAPS/Sources/Modules/Home/DurationButton.swift
  35. 47 16
      FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift
  36. 4 4
      FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift
  37. 13 10
      FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift
  38. 11 55
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  39. 1 1
      FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift
  40. 29 0
      FreeAPS/Sources/Services/Network/NightscoutAPI.swift
  41. 20 0
      FreeAPS/Sources/Services/Network/NightscoutManager.swift

+ 2 - 2
Config.xcconfig

@@ -1,7 +1,7 @@
 APP_DISPLAY_NAME = FreeAPS X
-APP_VERSION = 0.6.5
+APP_VERSION = 1.0.0
 APP_BUILD_NUMBER = 1
-COPYRIGHT_NOTICE = 
+BRANCH = 
 DEVELOPER_TEAM = ##TEAM_ID##
 BUNDLE_IDENTIFIER = ru.artpancreas.$(DEVELOPMENT_TEAM).FreeAPS
 APP_GROUP_ID = group.com.$(DEVELOPMENT_TEAM).loopkit.LoopGroup

Разница между файлами не показана из-за своего большого размера
+ 40 - 40
Dependencies/rileylink_ios/MinimedKitUI/tr.lproj/Localizable.strings


+ 1 - 1
FreeAPS/Resources/Info.plist

@@ -71,7 +71,7 @@
 	<key>NSHealthUpdateUsageDescription</key>
 	<string>Health App is used to store blood glucose data</string>
 	<key>NSHumanReadableCopyright</key>
-	<string>$(COPYRIGHT_NOTICE)</string>
+	<string>$(BRANCH)</string>
 	<key>UIApplicationSceneManifest</key>
 	<dict>
 		<key>UIApplicationSupportsMultipleScenes</key>

+ 0 - 112
FreeAPS/Resources/json/defaults/monitor/statistics.json

@@ -1,114 +1,2 @@
 [
-  {
-    "createdAt" : "1978-02-22T11:43:54.659Z",
-    "iPhone" : "Default",
-    "iOS" : "Default",
-    "Build_Version" : "Default",
-    "Build_Number2" : "Default",
-    "Branch" : "Default",
-    "Build_Date" : "1978-02-22T11:59:59.659Z",
-    "Algorithm" : "Default",
-    "AdjustmentFactor" : 1,
-    "Pump" : "Default",
-    "CGM" : "Default",
-    "insulinType" : "Default",
-    "peakActivityTime" : 65,
-    "TDD" : 0,
-    "Carbs_24h":  0,
-    "GlucoseStorage_Days" : 0,
-    "Statistics" : {
-      "Distribution" : [
-        {
-          "TIR" : [
-            {
-              "day" : 0,
-              "sevenDays" : 0,
-              "thirtyDays" : 0,
-              "ninetyDays" : 0,
-              "totalDays" : 0
-            }
-          ],
-          "Hypos" : [
-            {
-              "day" : 0,
-              "sevenDays" : 0,
-              "thirtyDays" : 0,
-              "ninetyDays" : 0,
-              "totalDays" : 0
-            }
-          ],
-          "Hypers" : [
-            {
-              "day" : 0,
-              "sevenDays" : 0,
-              "thirtyDays" : 0,
-              "ninetyDays" : 0,
-              "totalDays" : 0
-            }
-          ]
-        }
-      ],
-      "Glucose" : [
-        {
-          "Average" : [
-            {
-              "oneDay_mmol" : 0,
-              "day" : 0,
-              "sevenDays_mmol" : 0,
-              "sevenDays" : 0,
-              "thirtyDays_mmol" : 0,
-              "thirtyDays" : 0,
-              "ninetyDays_mmol" : 0,
-              "ninetyDays" : 0,
-              "totalDays_mmol" : 0,
-              "totalDays" : 0
-            }
-          ],
-          "Median" : [
-            {
-              "oneDay_mmol" : 0,
-              "day" : 0,
-              "sevenDays_mmol" : 0,
-              "sevenDays" : 0,
-              "thirtyDays_mmol":  0,
-              "thirtyDays" : 0,
-              "ninetyDays_mmol" : 0,
-              "ninetyDays" : 0,
-              "totalDays_mmol" : 0,
-              "totalDays" : 0
-            }
-          ]
-        }
-      ],
-      "HbA1c" : [
-        {
-          "oneDay_mmolMol" : 0,
-          "day" : 0,
-          "sevenDays_mmolMol" : 0,
-          "sevenDays" : 0,
-          "thirtyDays_mmolMol" : 0,
-          "thirtyDays" : 0,
-          "ninetyDays_mmolMol" : 0,
-          "ninetyDays" : 0,
-          "totalDays_mmolMol" : 0,
-          "totalDays" : 0
-        }
-      ],
-      "LoopCycles" : [
-        {
-          "loops" : 0,
-          "errors" : 0,
-          "success_rate" : 0,
-          "avg_interval" : 0,
-          "median_interval" : 0,
-          "min_interval" : 0,
-          "max_interval" : 0,
-          "avg_duration" : 0,
-          "median_duration" : 0,
-          "min_duration" : 0,
-          "max_duration" : 0
-        }
-      ]
-    }
-  }
 ]

+ 4 - 53
FreeAPS/Sources/APS/APSManager.swift

@@ -240,7 +240,9 @@ final class BaseAPSManager: APSManager, Injectable {
         loopStats(loopStatRecord: loopStatRecord)
 
         // Create a statistics.json
-        statistics()
+        if settings.displayStatistics {
+            statistics()
+        }
 
         if settings.closedLoop {
             reportEnacted(received: error == nil)
@@ -763,7 +765,7 @@ final class BaseAPSManager: APSManager, Injectable {
 
         let updateThisOften = Int(settingsManager.preferences.updateInterval)
 
-        // Only run every 30 minutes or when pressing statPanel
+        // Only run every 30 minutesl
         if testIfEmpty != 0 {
             guard testFile[0].created_at.addingTimeInterval(updateThisOften.minutes.timeInterval) < Date()
             else {
@@ -905,18 +907,15 @@ final class BaseAPSManager: APSManager, Injectable {
         var bgArray_1_: [Double] = []
         var bgArray_7_: [Double] = []
         var bgArray_30_: [Double] = []
-        var bgArray_90_: [Double] = []
         var bgArrayForTIR: [(bg_: Double, date_: Date)] = []
         var bgArray_1: [(bg_: Double, date_: Date)] = []
         var bgArray_7: [(bg_: Double, date_: Date)] = []
         var bgArray_30: [(bg_: Double, date_: Date)] = []
-        var bgArray_90: [(bg_: Double, date_: Date)] = []
         var medianBG = 0.0
         var nr_bgs: Decimal = 0
         var nr_bgs_1: Decimal = 0
         var nr_bgs_7: Decimal = 0
         var nr_bgs_30: Decimal = 0
-        var nr_bgs_90: Decimal = 0
 
         var startDate = Date("1978-02-22T11:43:54.659Z")
         if endIndex >= 0 {
@@ -925,11 +924,9 @@ final class BaseAPSManager: APSManager, Injectable {
         var end1 = false
         var end7 = false
         var end30 = false
-        var end90 = false
         var bg_1: Decimal = 0
         var bg_7: Decimal = 0
         var bg_30: Decimal = 0
-        var bg_90: Decimal = 0
         var bg_total: Decimal = 0
         var j = -1
 
@@ -967,14 +964,6 @@ final class BaseAPSManager: APSManager, Injectable {
                         nr_bgs_30 = nr_bgs
                         // time_30 = ((startDate ?? Date()) - entry.date).timeInterval
                     }
-                    if (startDate! - entry.date).timeInterval >= 7.776E6, !end90 {
-                        end90 = true
-                        bg_90 = bg / nr_bgs
-                        bgArray_90 = bgArrayForTIR
-                        bgArray_90_ = bgArray
-                        nr_bgs_90 = nr_bgs
-                        // time_90 = ((startDate ?? Date()) - entry.date).timeInterval
-                    }
                 }
             }
         }
@@ -1067,14 +1056,6 @@ final class BaseAPSManager: APSManager, Injectable {
             IFCCa1CStatisticValue_30 = 10.929 *
                 (NGSPa1CStatisticValue_30 - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
         }
-        // 90 Days
-        var NGSPa1CStatisticValue_90: Decimal = 0.0
-        var IFCCa1CStatisticValue_90: Decimal = 0.0
-        if end90 {
-            NGSPa1CStatisticValue_90 = (46.7 + bg_90) / 28.7 // NGSP (%)
-            IFCCa1CStatisticValue_90 = 10.929 *
-                (NGSPa1CStatisticValue_90 - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
-        }
         // Total days
         var NGSPa1CStatisticValue_total: Decimal = 0.0
         var IFCCa1CStatisticValue_total: Decimal = 0.0
@@ -1088,7 +1069,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))), 1),
             week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.map(\.bg_))), 1),
             month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))), 1),
-            ninetyDays: roundDecimal(Decimal(medianCalculation(array: bgArray_90.map(\.bg_))), 1),
             total: roundDecimal(Decimal(medianBG), 1)
         )
 
@@ -1096,7 +1076,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(NGSPa1CStatisticValue, 1),
             week: roundDecimal(NGSPa1CStatisticValue_7, 1),
             month: roundDecimal(NGSPa1CStatisticValue_30, 1),
-            ninetyDays: roundDecimal(NGSPa1CStatisticValue_90, 1),
             total: roundDecimal(NGSPa1CStatisticValue_total, 1)
         )
 
@@ -1107,14 +1086,12 @@ final class BaseAPSManager: APSManager, Injectable {
             bg_1 = bg_1.asMmolL
             bg_7 = bg_7.asMmolL
             bg_30 = bg_30.asMmolL
-            bg_90 = bg_90.asMmolL
             bg_total = bg_total.asMmolL
 
             median = Durations(
                 day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))).asMmolL, 1),
                 week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.map(\.bg_))).asMmolL, 1),
                 month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))).asMmolL, 1),
-                ninetyDays: roundDecimal(Decimal(medianCalculation(array: bgArray_90.map(\.bg_))).asMmolL, 1),
                 total: roundDecimal(Decimal(medianBG).asMmolL, 1)
             )
 
@@ -1124,7 +1101,6 @@ final class BaseAPSManager: APSManager, Injectable {
                     day: roundDecimal(IFCCa1CStatisticValue, 1),
                     week: roundDecimal(IFCCa1CStatisticValue_7, 1),
                     month: roundDecimal(IFCCa1CStatisticValue_30, 1),
-                    ninetyDays: roundDecimal(IFCCa1CStatisticValue_90, 1),
                     total: roundDecimal(IFCCa1CStatisticValue_total, 1)
                 )
             }
@@ -1133,7 +1109,6 @@ final class BaseAPSManager: APSManager, Injectable {
                 day: roundDecimal(IFCCa1CStatisticValue, 1),
                 week: roundDecimal(IFCCa1CStatisticValue_7, 1),
                 month: roundDecimal(IFCCa1CStatisticValue_30, 1),
-                ninetyDays: roundDecimal(IFCCa1CStatisticValue_90, 1),
                 total: roundDecimal(IFCCa1CStatisticValue_total, 1)
             )
         }
@@ -1163,7 +1138,6 @@ final class BaseAPSManager: APSManager, Injectable {
         var oneDay_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var sevenDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var thirtyDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
-        var ninetyDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var totalDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
 
         // Get all TIR calcs for every case
@@ -1176,10 +1150,6 @@ final class BaseAPSManager: APSManager, Injectable {
         if end30 {
             thirtyDays_ = tir(bgArray_30)
         }
-        if end90 {
-            ninetyDays_ = tir(bgArray_90)
-        }
-
         if nr_bgs > 0 {
             totalDays_ = tir(bgArrayForTIR)
         }
@@ -1188,7 +1158,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(Decimal(oneDay_.TIR), 1),
             week: roundDecimal(Decimal(sevenDays_.TIR), 1),
             month: roundDecimal(Decimal(thirtyDays_.TIR), 1),
-            ninetyDays: roundDecimal(Decimal(ninetyDays_.TIR), 1),
             total: roundDecimal(Decimal(totalDays_.TIR), 1)
         )
 
@@ -1196,7 +1165,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: Decimal(oneDay_.hypos),
             week: Decimal(sevenDays_.hypos),
             month: Decimal(thirtyDays_.hypos),
-            ninetyDays: Decimal(ninetyDays_.hypos),
             total: Decimal(totalDays_.hypos)
         )
 
@@ -1204,7 +1172,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: Decimal(oneDay_.hypers),
             week: Decimal(sevenDays_.hypers),
             month: Decimal(thirtyDays_.hypers),
-            ninetyDays: Decimal(ninetyDays_.hypers),
             total: Decimal(totalDays_.hypers)
         )
 
@@ -1214,7 +1181,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(bg_1, 1),
             week: roundDecimal(bg_7, 1),
             month: roundDecimal(bg_30, 1),
-            ninetyDays: roundDecimal(bg_90, 1),
             total: roundDecimal(bg_total, 1)
         )
 
@@ -1235,7 +1201,6 @@ final class BaseAPSManager: APSManager, Injectable {
         var sumOfSquares_1: Decimal = 0
         var sumOfSquares_7: Decimal = 0
         var sumOfSquares_30: Decimal = 0
-        var sumOfSquares_90: Decimal = 0
 
         // Total
         for array in bgArray {
@@ -1261,12 +1226,6 @@ final class BaseAPSManager: APSManager, Injectable {
                 sumOfSquares_30 += pow(Decimal(array_30).asMmolL - bg_30, 2)
             } else { sumOfSquares_30 += pow(Decimal(array_30) - bg_30, 2) }
         }
-        // 90 days
-        for array_90 in bgArray_90_ {
-            if units == .mmolL {
-                sumOfSquares_90 += pow(Decimal(array_90).asMmolL - bg_90, 2)
-            } else { sumOfSquares_90 += pow(Decimal(array_90) - bg_90, 2) }
-        }
 
         // Standard deviation and Coefficient of variation
         var sd_total = 0.0
@@ -1277,8 +1236,6 @@ final class BaseAPSManager: APSManager, Injectable {
         var cv_7 = 0.0
         var sd_30 = 0.0
         var cv_30 = 0.0
-        var sd_90 = 0.0
-        var cv_90 = 0.0
 
         // Avoid division by zero
         if avgs.total < 1 || nr_bgs < 1 { sd_total = 0
@@ -1304,17 +1261,12 @@ final class BaseAPSManager: APSManager, Injectable {
             cv_30 = 0 } else { sd_30 = sqrt(Double(sumOfSquares_30 / nr_bgs_30))
             cv_30 = sd_30 / Double(bg_30) * 100
         }
-        if avgs.ninetyDays < 1 || nr_bgs_90 < 1 { sd_90 = 0
-            cv_90 = 0 } else { sd_90 = sqrt(Double(sumOfSquares_90 / nr_bgs_90))
-            cv_90 = sd_90 / Double(bg_90) * 100
-        }
 
         // Standard Deviations
         let standardDeviations = Durations(
             day: roundDecimal(Decimal(sd_1), 1),
             week: roundDecimal(Decimal(sd_7), 1),
             month: roundDecimal(Decimal(sd_30), 1),
-            ninetyDays: roundDecimal(Decimal(sd_90), 1),
             total: roundDecimal(Decimal(sd_total), 1)
         )
 
@@ -1323,7 +1275,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(Decimal(cv_1), 1),
             week: roundDecimal(Decimal(cv_7), 1),
             month: roundDecimal(Decimal(cv_30), 1),
-            ninetyDays: roundDecimal(Decimal(cv_90), 1),
             total: roundDecimal(Decimal(cv_total), 1)
         )
 

+ 1 - 1
FreeAPS/Sources/APS/CGM/DexcomSource.swift

@@ -33,7 +33,7 @@ final class DexcomSource: GlucoseSource {
         return Future<[BloodGlucose], Error> { [weak self] promise in
             self?.promise = promise
         }
-        .timeout(60, scheduler: processQueue, options: nil, customError: nil)
+        .timeout(90, scheduler: processQueue, options: nil, customError: nil)
         .replaceError(with: [])
         .replaceEmpty(with: [])
         .eraseToAnyPublisher()

+ 15 - 0
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -14,6 +14,7 @@ protocol PumpHistoryStorage {
     func recent() -> [PumpHistoryEvent]
     func nightscoutTretmentsNotUploaded() -> [NigtscoutTreatment]
     func saveCancelTempEvents()
+    func deleteInsulin(at date: Date)
 }
 
 final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
@@ -194,6 +195,20 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
         storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self)?.reversed() ?? []
     }
 
+    func deleteInsulin(at date: Date) {
+        processQueue.sync {
+            var allValues = storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self) ?? []
+            guard let entryIndex = allValues.firstIndex(where: { $0.timestamp == date }) else {
+                return
+            }
+            allValues.remove(at: entryIndex)
+            storage.save(allValues, as: OpenAPS.Monitor.pumpHistory)
+            broadcaster.notify(PumpHistoryObserver.self, on: processQueue) {
+                $0.pumpHistoryDidUpdate(allValues)
+            }
+        }
+    }
+
     func nightscoutTretmentsNotUploaded() -> [NigtscoutTreatment] {
         let events = recent()
         guard !events.isEmpty else { return [] }

Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 9 - 3
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 5 - 2
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 11 - 5
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 11 - 5
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 5 - 2
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 39 - 33
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 8 - 2
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


+ 0 - 1
FreeAPS/Sources/Models/Statistics.swift

@@ -108,7 +108,6 @@ struct Durations: JSON, Equatable {
     var day: Decimal
     var week: Decimal
     var month: Decimal
-    var ninetyDays: Decimal
     var total: Decimal
 }
 

+ 1 - 0
FreeAPS/Sources/Modules/DataTable/DataTableDataFlow.swift

@@ -173,5 +173,6 @@ protocol DataTableProvider: Provider {
     func carbs() -> [CarbsEntry]
     func glucose() -> [BloodGlucose]
     func deleteCarbs(at date: Date)
+    func deleteInsulin(at date: Date)
     func deleteGlucose(id: String)
 }

+ 4 - 0
FreeAPS/Sources/Modules/DataTable/DataTableProvider.swift

@@ -25,6 +25,10 @@ extension DataTable {
             nightscoutManager.deleteCarbs(at: date)
         }
 
+        func deleteInsulin(at date: Date) {
+            nightscoutManager.deleteInsulin(at: date)
+        }
+
         func glucose() -> [BloodGlucose] {
             glucoseStorage.recent().sorted { $0.date > $1.date }
         }

+ 11 - 0
FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift

@@ -3,6 +3,8 @@ import SwiftUI
 extension DataTable {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() var broadcaster: Broadcaster!
+        @Injected() var unlockmanager: UnlockManager!
+
         @Published var mode: Mode = .treatments
         @Published var treatments: [Treatment] = []
         @Published var glucose: [Glucose] = []
@@ -92,6 +94,15 @@ extension DataTable {
             provider.deleteCarbs(at: date)
         }
 
+        func deleteInsulin(at date: Date) {
+            unlockmanager.unlock()
+                .sink { _ in } receiveValue: { [weak self] _ in
+                    guard let self = self else { return }
+                    self.provider.deleteInsulin(at: date)
+                }
+                .store(in: &lifetime)
+        }
+
         func deleteGlucose(at index: Int) {
             let id = glucose[index].id
             provider.deleteGlucose(id: id)

+ 26 - 0
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -5,9 +5,13 @@ extension DataTable {
     struct RootView: BaseView {
         let resolver: Resolver
         @StateObject var state = StateModel()
+
         @State private var isRemoveCarbsAlertPresented = false
         @State private var removeCarbsAlert: Alert?
 
+        @State private var isRemoveInsulinAlertPresented = false
+        @State private var removeInsulinAlert: Alert?
+
         private var glucoseFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
@@ -100,6 +104,28 @@ extension DataTable {
                             removeCarbsAlert!
                         }
                 }
+
+                if item.type == .bolus {
+                    Spacer()
+                    Image(systemName: "xmark.circle").foregroundColor(.secondary)
+                        .contentShape(Rectangle())
+                        .padding(.vertical)
+                        .onTapGesture {
+                            removeInsulinAlert = Alert(
+                                title: Text("Delete insulin?"),
+                                message: Text(item.amountText),
+                                primaryButton: .destructive(
+                                    Text("Delete"),
+                                    action: { state.deleteInsulin(at: item.date) }
+                                ),
+                                secondaryButton: .cancel()
+                            )
+                            isRemoveInsulinAlertPresented = true
+                        }
+                        .alert(isPresented: $isRemoveInsulinAlertPresented) {
+                            removeInsulinAlert!
+                        }
+                }
             }
         }
 

+ 0 - 1
FreeAPS/Sources/Modules/Home/DurationButton.swift

@@ -14,7 +14,6 @@ enum durationState: String, DurationButton {
     case day = "Past 24 Hours "
     case week = "Past Week "
     case month = "Past Month "
-    case ninetyDays = "Past 90 Days "
     case total = "All Past Days of Data "
 }
 

+ 47 - 16
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -27,6 +27,14 @@ struct CurrentGlucoseView: View {
         return formatter
     }
 
+    private var timaAgoFormatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumFractionDigits = 0
+        formatter.negativePrefix = ""
+        return formatter
+    }
+
     private var dateFormatter: DateFormatter {
         let formatter = DateFormatter()
         formatter.timeStyle = .short
@@ -34,8 +42,8 @@ struct CurrentGlucoseView: View {
     }
 
     var body: some View {
-        VStack(alignment: .center, spacing: 6) {
-            HStack(spacing: 8) {
+        VStack(alignment: .center) {
+            HStack {
                 Text(
                     recentGlucose?.glucose
                         .map {
@@ -43,24 +51,30 @@ struct CurrentGlucoseView: View {
                                 .string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)! }
                         ?? "--"
                 )
-                .font(.system(size: 24, weight: .bold))
-                .fixedSize()
-                .foregroundColor(alarm == nil ? .primary : .loopRed)
-                image.padding(.bottom, 2)
+                .font(.title).fontWeight(.bold)
+                .foregroundColor(alarm == nil ? colorOfGlucose : .loopRed)
 
-            }.padding(.leading, 4)
-            HStack(alignment: .lastTextBaseline, spacing: 2) {
+                image
+            }
+            HStack {
+                let minutes = (recentGlucose?.dateString.timeIntervalSinceNow ?? 0) / 60
+                let text = timaAgoFormatter.string(for: Double(minutes)) ?? ""
                 Text(
-                    recentGlucose.map { dateFormatter.string(from: $0.dateString) } ?? "--"
-                ).font(.caption2).foregroundColor(.secondary)
+                    text == "0" ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
+                        text + " " +
+                            NSLocalizedString("min", comment: "Short form for minutes") + " "
+                    )
+                )
+                .font(.caption2).foregroundColor(.secondary)
+
                 Text(
                     delta
-                        .map { deltaFormatter.string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)!
-                        } ??
-                        "--"
-
-                ).font(.system(size: 12, weight: .bold))
-            }
+                        .map {
+                            deltaFormatter.string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)!
+                        } ?? "--"
+                )
+                .font(.caption2).foregroundColor(.secondary)
+            }.frame(alignment: .top)
         }
     }
 
@@ -91,4 +105,21 @@ struct CurrentGlucoseView: View {
             return Image(systemName: "arrow.left.and.right")
         }
     }
+
+    var colorOfGlucose: Color {
+        let whichGlucose = recentGlucose?.glucose ?? 0
+
+        switch whichGlucose {
+        case 71 ... 145:
+            return .loopGreen
+        case 1 ... 55,
+             217...:
+            return .loopRed
+        case 56 ... 70,
+             146 ... 216:
+            return .loopYellow
+        default:
+            return .primary
+        }
+    }
 }

+ 4 - 4
FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift

@@ -21,13 +21,13 @@ struct LoopView: View {
         return formatter
     }
 
-    private let rect = CGRect(x: 0, y: 0, width: 32, height: 32)
+    private let rect = CGRect(x: 0, y: 0, width: 28, height: 28)
     var body: some View {
         VStack(alignment: .center) {
             ZStack {
                 Circle()
-                    .strokeBorder(color, lineWidth: 6)
-                    .frame(width: rect.width, height: rect.height)
+                    .strokeBorder(color, lineWidth: 5)
+                    .frame(width: rect.width, height: rect.height, alignment: .bottom)
                     .mask(mask(in: rect).fill(style: FillStyle(eoFill: true)))
                 if isLooping {
                     ProgressView()
@@ -51,7 +51,7 @@ struct LoopView: View {
         if minAgo > 1440 {
             return "--"
         }
-        return "\(minAgo) " + NSLocalizedString("min ago", comment: "Minutes ago since last loop")
+        return "\(minAgo) " + NSLocalizedString("min", comment: "Minutes ago since last loop")
     }
 
     private var color: Color {

+ 13 - 10
FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift

@@ -27,28 +27,30 @@ struct PumpView: View {
                     Image(systemName: "drop.fill")
                         .resizable()
                         .aspectRatio(contentMode: .fit)
-                        .frame(height: 8)
+                        .frame(maxHeight: 10)
                         .foregroundColor(reservoirColor)
                     if reservoir == 0xDEAD_BEEF {
-                        Text("50+ " + NSLocalizedString("U", comment: "Insulin unit")).font(.system(size: 12, weight: .bold))
+                        Text("50+ " + NSLocalizedString("U", comment: "Insulin unit")).font(.footnote)
+                            .fontWeight(.bold)
                     } else {
                         Text(
                             reservoirFormatter
                                 .string(from: reservoir as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit")
                         )
-                        .font(.system(size: 12, weight: .bold))
+                        .font(.footnote).fontWeight(.bold)
                     }
-                }
+                }.frame(alignment: .top)
             }
             if let battery = battery, battery.display ?? false, expiresAtDate == nil {
                 HStack {
                     Image(systemName: "battery.100")
                         .resizable()
                         .aspectRatio(contentMode: .fit)
-                        .frame(height: 8)
+                        .frame(maxHeight: 10)
                         .foregroundColor(batteryColor)
-                    Text("\(Int(battery.percent ?? 100)) %").font(.system(size: 12, weight: .bold))
-                }
+                    Text("\(Int(battery.percent ?? 100)) %").font(.footnote)
+                        .fontWeight(.bold)
+                }.frame(alignment: .bottom)
             }
 
             if let date = expiresAtDate {
@@ -56,10 +58,11 @@ struct PumpView: View {
                     Image(systemName: "stopwatch.fill")
                         .resizable()
                         .aspectRatio(contentMode: .fit)
-                        .frame(height: 8)
+                        .frame(maxHeight: 10)
                         .foregroundColor(timerColor)
-                    Text(remainingTimeString(time: date.timeIntervalSince(timerDate))).font(.system(size: 12, weight: .bold))
-                }
+                    Text(remainingTimeString(time: date.timeIntervalSince(timerDate))).font(.footnote)
+                        .fontWeight(.bold)
+                }.frame(alignment: .bottom)
             }
         }
     }

+ 11 - 55
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -16,12 +16,13 @@ extension Home {
         @State var median_ = ""
         @State var average_ = ""
         @State var readings = ""
+
         @State var averageOrmedian = ""
         @State var CV_or_SD_Title = NSLocalizedString("CV", comment: "CV")
         @State var cv_ = ""
         @State var sd_ = ""
         @State var CVorSD = ""
-        // Switch between Loops and Errors when tapping in ststPanel
+        // Switch between Loops and Errors when tapping in statPanel
         @State var loopStatTitle = NSLocalizedString("Loops", comment: "Nr of Loops in statPanel")
 
         public let paddingSpace: CGFloat = 15
@@ -73,29 +74,29 @@ extension Home {
                 Spacer()
             }
             .frame(maxWidth: .infinity)
-            .frame(maxHeight: 70)
             .padding(.top, geo.safeAreaInsets.top)
+            .padding(.bottom)
             .background(Color.gray.opacity(0.2))
         }
 
         var cobIobView: some View {
             VStack(alignment: .leading, spacing: 12) {
                 HStack {
-                    Text("IOB").font(.caption2).foregroundColor(.secondary)
+                    Text("IOB").font(.footnote).foregroundColor(.secondary)
                     Text(
                         (numberFormatter.string(from: (state.suggestion?.iob ?? 0) as NSNumber) ?? "0") +
                             NSLocalizedString(" U", comment: "Insulin unit")
                     )
-                    .font(.system(size: 12, weight: .bold))
-                }
+                    .font(.footnote).fontWeight(.bold)
+                }.frame(alignment: .top)
                 HStack {
-                    Text("COB").font(.caption2).foregroundColor(.secondary)
+                    Text("COB").font(.footnote).foregroundColor(.secondary)
                     Text(
                         (numberFormatter.string(from: (state.suggestion?.cob ?? 0) as NSNumber) ?? "0") +
                             NSLocalizedString(" g", comment: "gram of carbs")
                     )
-                    .font(.system(size: 12, weight: .bold))
-                }
+                    .font(.footnote).fontWeight(.bold)
+                }.frame(alignment: .bottom)
             }
         }
 
@@ -313,37 +314,6 @@ extension Home {
 
                         averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
 
-                    case .ninetyDays:
-                        let hba1c_all = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
-                        let average_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Average.ninetyDays ?? 0) as NSNumber) ??
-                            ""
-                        let median_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Median.ninetyDays ?? 0) as NSNumber) ??
-                            ""
-                        let tir_low = tirFormatter
-                            .string(
-                                from: (state.statistics?.Statistics.Distribution.Hypos.ninetyDays ?? 0) as NSNumber
-                            ) ??
-                            ""
-                        let tir_high = tirFormatter
-                            .string(
-                                from: (state.statistics?.Statistics.Distribution.Hypers.ninetyDays ?? 0) as NSNumber
-                            ) ??
-                            ""
-                        let tir_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.TIR.ninetyDays ?? 0) as NSNumber) ??
-                            ""
-                        let hba1c_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.ninetyDays ?? 0) as NSNumber) ?? ""
-                        let sd_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.SD.ninetyDays ?? 0) as NSNumber) ?? ""
-                        let cv_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.CV.ninetyDays ?? 0) as NSNumber) ?? ""
-
-                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
-
                     case .total:
                         let hba1c_all = numberFormatter
                             .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
@@ -617,7 +587,7 @@ extension Home {
                                 .renderingMode(.template)
                                 .resizable()
                                 .frame(width: 24, height: 24)
-                                .foregroundColor(.loopGreen)
+                                .foregroundColor(.loopYellow)
                                 .padding(8)
                             if let carbsReq = state.carbsRequired {
                                 Text(numberFormatter.string(from: carbsReq as NSNumber)!)
@@ -636,7 +606,7 @@ extension Home {
                             .resizable()
                             .frame(width: 24, height: 24)
                             .padding(8)
-                    }.foregroundColor(.loopYellow)
+                    }.foregroundColor(.loopGreen)
                     Spacer()
                     Button { state.showModal(for: .bolus(waitForSuggestion: false)) }
                     label: {
@@ -732,19 +702,5 @@ extension Home {
                 }
             }
         }
-
-        private func colorOfGlucose(_ glucose: Decimal) -> Color {
-            switch glucose {
-            case 4 ... 8,
-                 30 ... 46,
-                 72 ... 144:
-                return .loopGreen
-            case 0 ... 4,
-                 20 ... 71:
-                return .loopRed
-            default:
-                return .loopYellow
-            }
-        }
     }
 }

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift


+ 29 - 0
FreeAPS/Sources/Services/Network/NightscoutAPI.swift

@@ -166,6 +166,35 @@ extension NightscoutAPI {
             .eraseToAnyPublisher()
     }
 
+    func deleteInsulin(at date: Date) -> AnyPublisher<Void, Swift.Error> {
+        var components = URLComponents()
+        components.scheme = url.scheme
+        components.host = url.host
+        components.port = url.port
+        components.path = Config.treatmentsPath
+        components.queryItems = [
+            URLQueryItem(name: "find[bolus][$exists]", value: "true"),
+            URLQueryItem(
+                name: "find[created_at][$eq]",
+                value: Formatter.iso8601withFractionalSeconds.string(from: date)
+            )
+        ]
+
+        var request = URLRequest(url: components.url!)
+        request.allowsConstrainedNetworkAccess = false
+        request.timeoutInterval = Config.timeout
+        request.httpMethod = "DELETE"
+
+        if let secret = secret {
+            request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
+        }
+
+        return service.run(request)
+            .retry(Config.retryCount)
+            .map { _ in () }
+            .eraseToAnyPublisher()
+    }
+
     func fetchTempTargets(sinceDate: Date? = nil) -> AnyPublisher<[TempTarget], Swift.Error> {
         var components = URLComponents()
         components.scheme = url.scheme

+ 20 - 0
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -9,6 +9,7 @@ protocol NightscoutManager: GlucoseSource {
     func fetchTempTargets() -> AnyPublisher<[TempTarget], Never>
     func fetchAnnouncements() -> AnyPublisher<[Announcement], Never>
     func deleteCarbs(at date: Date)
+    func deleteInsulin(at date: Date)
     func uploadStatus()
     func uploadStatistics(dailystat: Statistics)
     func uploadPreferences()
@@ -180,6 +181,25 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
             .store(in: &lifetime)
     }
 
+    func deleteInsulin(at date: Date) {
+        guard let nightscout = nightscoutAPI, isUploadEnabled else {
+            pumpHistoryStorage.deleteInsulin(at: date)
+            return
+        }
+
+        nightscout.deleteInsulin(at: date)
+            .sink { completion in
+                switch completion {
+                case .finished:
+                    self.pumpHistoryStorage.deleteInsulin(at: date)
+                    debug(.nightscout, "Carbs deleted")
+                case let .failure(error):
+                    debug(.nightscout, error.localizedDescription)
+                }
+            } receiveValue: {}
+            .store(in: &lifetime)
+    }
+
     func uploadStatistics(dailystat: Statistics) {
         let stats = NightscoutStatistics(
             dailystats: dailystat