Sfoglia il codice sorgente

Merge branch 'dev' into chore/update-omnipodkit

marionbarker 23 ore fa
parent
commit
348ffff7f2

+ 1 - 1
Config.xcconfig

@@ -19,7 +19,7 @@ TRIO_APP_GROUP_ID = group.org.nightscout.$(DEVELOPMENT_TEAM).trio.trio-app-group
 
 // The developers set the version numbers, please leave them alone
 APP_VERSION = 0.7.0
-APP_DEV_VERSION = 0.7.0.15
+APP_DEV_VERSION = 0.7.0.17
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE =
 

+ 218 - 1
Trio.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -1,6 +1,169 @@
 {
+  "originHash" : "b271d5de8862534ec5ccf9ccfd7ce226afa7ed4c799b7066ccbe2281782402a1",
   "pins" : [
     {
+      "identity" : "abseil-cpp-binary",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/abseil-cpp-binary.git",
+      "state" : {
+        "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5",
+        "version" : "1.2024072200.0"
+      }
+    },
+    {
+      "identity" : "app-check",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/app-check.git",
+      "state" : {
+        "revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
+        "version" : "11.2.0"
+      }
+    },
+    {
+      "identity" : "bluecryptor",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/Kitura/BlueCryptor.git",
+      "state" : {
+        "revision" : "cec97c24b111351e70e448972a7d3fe68a756d6d",
+        "version" : "2.0.2"
+      }
+    },
+    {
+      "identity" : "blueecc",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/Kitura/BlueECC.git",
+      "state" : {
+        "revision" : "1485268a54f8135435a825a855e733f026fa6cc8",
+        "version" : "1.2.201"
+      }
+    },
+    {
+      "identity" : "bluersa",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/Kitura/BlueRSA.git",
+      "state" : {
+        "revision" : "f40325520344a966523b214394aa350132a6af68",
+        "version" : "1.0.203"
+      }
+    },
+    {
+      "identity" : "connectiq-companion-app-sdk-ios",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/garmin/connectiq-companion-app-sdk-ios",
+      "state" : {
+        "revision" : "f0d29ff691d700a132d86205ed9bb091e336c2f7",
+        "version" : "1.8.0"
+      }
+    },
+    {
+      "identity" : "cryptoswift",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/krzyzanowskim/CryptoSwift",
+      "state" : {
+        "revision" : "f2a627b84c1ff96f21ac2fcb623ab36142dd5512",
+        "version" : "1.10.0"
+      }
+    },
+    {
+      "identity" : "firebase-ios-sdk",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/firebase/firebase-ios-sdk.git",
+      "state" : {
+        "revision" : "fdc352fabaf5916e7faa1f96ad02b1957e93e5a5",
+        "version" : "11.15.0"
+      }
+    },
+    {
+      "identity" : "google-ads-on-device-conversion-ios-sdk",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk",
+      "state" : {
+        "revision" : "a2d0f1f1666de591eb1a811f40b1706f5c63a2ed",
+        "version" : "2.3.0"
+      }
+    },
+    {
+      "identity" : "googleappmeasurement",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/GoogleAppMeasurement.git",
+      "state" : {
+        "revision" : "45ce435e9406d3c674dd249a042b932bee006f60",
+        "version" : "11.15.0"
+      }
+    },
+    {
+      "identity" : "googledatatransport",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/GoogleDataTransport.git",
+      "state" : {
+        "revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
+        "version" : "10.1.0"
+      }
+    },
+    {
+      "identity" : "googleutilities",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/GoogleUtilities.git",
+      "state" : {
+        "revision" : "60da361632d0de02786f709bdc0c4df340f7613e",
+        "version" : "8.1.0"
+      }
+    },
+    {
+      "identity" : "grpc-binary",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/grpc-binary.git",
+      "state" : {
+        "revision" : "75b31c842f664a0f46a2e590a570e370249fd8f6",
+        "version" : "1.69.1"
+      }
+    },
+    {
+      "identity" : "gtm-session-fetcher",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/gtm-session-fetcher.git",
+      "state" : {
+        "revision" : "c756a29784521063b6a1202907e2cc47f41b667c",
+        "version" : "4.5.0"
+      }
+    },
+    {
+      "identity" : "interop-ios-for-google-sdks",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/interop-ios-for-google-sdks.git",
+      "state" : {
+        "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe",
+        "version" : "101.0.0"
+      }
+    },
+    {
+      "identity" : "kituracontracts",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/Kitura/KituraContracts.git",
+      "state" : {
+        "revision" : "6edf7ac3dd2b3a2c61284778d430bbad7d8a6f23",
+        "version" : "2.0.1"
+      }
+    },
+    {
+      "identity" : "leveldb",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/firebase/leveldb.git",
+      "state" : {
+        "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
+        "version" : "1.22.5"
+      }
+    },
+    {
+      "identity" : "loggerapi",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/Kitura/LoggerAPI.git",
+      "state" : {
+        "revision" : "4e6b45e850ffa275e8e26a24c6454fd709d5b6ac",
+        "version" : "2.0.0"
+      }
+    },
+    {
       "identity" : "mkringprogressview",
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/maxkonovalov/MKRingProgressView.git",
@@ -10,6 +173,33 @@
       }
     },
     {
+      "identity" : "nanopb",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/firebase/nanopb.git",
+      "state" : {
+        "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
+        "version" : "2.30910.0"
+      }
+    },
+    {
+      "identity" : "promises",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/promises.git",
+      "state" : {
+        "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
+        "version" : "2.4.0"
+      }
+    },
+    {
+      "identity" : "slidebutton",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/no-comment/SlideButton",
+      "state" : {
+        "branch" : "main",
+        "revision" : "5eacebba4d7deeb693592bc9a62ab2d2181e133b"
+      }
+    },
+    {
       "identity" : "swift-algorithms",
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/apple/swift-algorithms",
@@ -19,6 +209,24 @@
       }
     },
     {
+      "identity" : "swift-jwt",
+      "kind" : "remoteSourceControl",
+      "location" : "http://github.com/Kitura/Swift-JWT.git",
+      "state" : {
+        "revision" : "f68ec28fbd90a651597e9e825ea7f315f8d52a1f",
+        "version" : "4.0.1"
+      }
+    },
+    {
+      "identity" : "swift-log",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-log.git",
+      "state" : {
+        "revision" : "5073617dac96330a486245e4c0179cb0a6fd2256",
+        "version" : "1.12.0"
+      }
+    },
+    {
       "identity" : "swift-numerics",
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/apple/swift-numerics",
@@ -28,6 +236,15 @@
       }
     },
     {
+      "identity" : "swift-protobuf",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-protobuf.git",
+      "state" : {
+        "revision" : "81558271e243f8f47dfe8e9fdd55f3c2b5413f68",
+        "version" : "1.37.0"
+      }
+    },
+    {
       "identity" : "swiftcharts",
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/ivanschuetz/SwiftCharts.git",
@@ -64,5 +281,5 @@
       }
     }
   ],
-  "version" : 2
+  "version" : 3
 }

File diff suppressed because it is too large
+ 1006 - 382
Trio/Resources/InfoPlist.xcstrings


+ 4 - 2
Trio/Sources/APS/FetchGlucoseManager.swift

@@ -381,9 +381,11 @@ extension BaseFetchGlucoseManager {
             // Predicate must cover at least the full glucose horizon used by downstream algorithm consumers.
             // If autosens / oref / smoothing logic ever starts looking back further (e.g. 36h),
             // this fetch window must be expanded accordingly.
+            // Fetch descending (newest first) so the limit always keeps the most recent 350 readings.
+            // Reversed before return so callers receive oldest-first (chronological) order.
             predicate: compoundPredicate,
             key: "date",
-            ascending: true, // the first element is the oldest
+            ascending: false,
             fetchLimit: 350
         )
 
@@ -391,7 +393,7 @@ extension BaseFetchGlucoseManager {
             throw CoreDataError.fetchError(function: #function, file: #file)
         }
 
-        return glucoseArray.map(\.objectID)
+        return Array(glucoseArray.map(\.objectID).reversed())
     }
 
     /// CoreData-friendly AAPS exponential smoothing + storage.

File diff suppressed because it is too large
+ 24405 - 10142
Trio/Sources/Localizations/Main/Localizable.xcstrings


+ 67 - 0
TrioTests/GlucoseSmoothingTests.swift

@@ -217,6 +217,73 @@ import Testing
         }
     }
 
+    // MARK: - fetchGlucose Window Tests
+
+    @Test(
+        "fetchGlucose retains the most recent 350 readings (not the oldest) when 24h holds more than 350"
+    ) func testFetchGlucoseKeepsMostRecentWhenOverLimit() async throws {
+        // GIVEN: 360 readings within the last 24h (3 min spacing => ~18h span).
+        // Each reading carries a unique glucose value so we can verify which subset survives the limit.
+        let count = 360
+        let values: [Int16] = (0 ..< count).map { Int16(100 + $0) }
+        await createGlucoseSequence(values: values, interval: 3 * 60, isManual: false)
+
+        // WHEN
+        let objectIDs = try await fetchGlucoseManager.fetchGlucose(context: testContext)
+
+        // THEN
+        #expect(objectIDs.count == 350, "fetchGlucose should respect the 350 limit, got \(objectIDs.count).")
+
+        await testContext.perform {
+            let fetched = objectIDs.compactMap { self.testContext.object(with: $0) as? GlucoseStored }
+            #expect(fetched.count == 350, "All returned object IDs must resolve to GlucoseStored instances.")
+
+            // Returned order must be oldest-first (chronological) — the smoother walks the array this way.
+            let dates = fetched.compactMap(\.date)
+            #expect(dates == dates.sorted(), "fetchGlucose must return readings in chronological (ascending) order.")
+
+            // The most recent reading (current BG) must be the LAST element after the chronological reverse.
+            #expect(
+                fetched.last?.glucose == Int16(100 + count - 1),
+                "Most recent reading (current BG) must be retained after the 350-limit truncation."
+            )
+
+            // The oldest 10 readings must be dropped — verify the limit cut from the OLD end, not the recent end.
+            let returnedGlucoseValues = Set(fetched.map(\.glucose))
+            #expect(
+                !returnedGlucoseValues.contains(Int16(100)),
+                "Oldest reading must be excluded by the limit (truncation should cut old, not recent)."
+            )
+            #expect(
+                returnedGlucoseValues.contains(Int16(100 + count - 1)),
+                "Newest reading must be included after truncation."
+            )
+        }
+    }
+
+    @Test(
+        "Exponential smoothing writes a smoothed value for the current BG when 24h holds more than 350 readings"
+    ) func testExponentialSmoothingCoversCurrentBGAboveLimit() async throws {
+        // GIVEN: 360 contiguous CGM readings within the last 24h (3 min spacing, no gaps).
+        let count = 360
+        let values: [Int16] = (0 ..< count).map { _ in Int16(120) }
+        await createGlucoseSequence(values: values, interval: 3 * 60, isManual: false)
+
+        // WHEN
+        await fetchGlucoseManager.exponentialSmoothingGlucose(context: testContext)
+
+        // THEN: the most recent reading must have received a smoothed value.
+        // Regression test for the bug where ascending+fetchLimit kept the OLDEST 350 readings,
+        // so the current BG fell outside the smoothing window and was never written.
+        let ascending = try await fetchAndSortGlucose()
+        #expect(ascending.count == count)
+
+        #expect(
+            ascending.last?.smoothedGlucose != nil,
+            "Most recent reading (current BG) must receive a smoothed value when over the 350-row limit."
+        )
+    }
+
     // MARK: - OpenAPS Glucose Selection Tests
 
     @Test("Algorithm uses smoothed glucose when enabled") func testAlgorithmUsesSmoothedGlucose() async throws {