Skip to content

Commit

Permalink
Merge pull request nightscout#55 from dnzxy/tidepool
Browse files Browse the repository at this point in the history
  • Loading branch information
polscm32 authored Sep 30, 2024
2 parents d733543 + 2cf03c5 commit 14dbbbd
Show file tree
Hide file tree
Showing 25 changed files with 1,678 additions and 903 deletions.
47 changes: 13 additions & 34 deletions FreeAPS/Sources/APS/FetchGlucoseManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extension FetchGlucoseManager {

final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
private let processQueue = DispatchQueue(label: "BaseGlucoseManager.processQueue")

@Injected() var glucoseStorage: GlucoseStorage!
@Injected() var nightscoutManager: NightscoutManager!
@Injected() var tidepoolService: TidepoolManager!
Expand Down Expand Up @@ -165,18 +166,16 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
/// function to try to force the refresh of the CGM - generally provide by the pump heartbeat
public func refreshCGM() {
debug(.deviceManager, "refreshCGM by pump")
// updateGlucoseSource(cgmGlucoseSourceType: settingsManager.settings.cgm, cgmGlucosePluginId: settingsManager.settings.cgmPluginIdentifier)

Publishers.CombineLatest3(
Publishers.CombineLatest(
Just(glucoseStorage.syncDate()),
healthKitManager.fetch(nil),
glucoseSource.fetchIfNeeded()
)
.eraseToAnyPublisher()
.receive(on: processQueue)
.sink { syncDate, glucoseFromHealth, glucose in
.sink { syncDate, glucose in
debug(.nightscout, "refreshCGM FETCHGLUCOSE : SyncDate is \(syncDate)")
self.glucoseStoreAndHeartDecision(syncDate: syncDate, glucose: glucose, glucoseFromHealth: glucoseFromHealth)
self.glucoseStoreAndHeartDecision(syncDate: syncDate, glucose: glucose)
}
.store(in: &lifetime)
}
Expand Down Expand Up @@ -210,11 +209,10 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
}
}

private func glucoseStoreAndHeartDecision(syncDate: Date, glucose: [BloodGlucose], glucoseFromHealth: [BloodGlucose] = []) {
private func glucoseStoreAndHeartDecision(syncDate: Date, glucose: [BloodGlucose]) {
// calibration add if required only for sensor
let newGlucose = overcalibrate(entries: glucose)

let allGlucose = newGlucose + glucoseFromHealth
var filteredByDate: [BloodGlucose] = []
var filtered: [BloodGlucose] = []

Expand All @@ -226,19 +224,19 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
backGroundFetchBGTaskID = .invalid
}

guard allGlucose.isNotEmpty else {
guard newGlucose.isNotEmpty else {
if let backgroundTask = backGroundFetchBGTaskID {
UIApplication.shared.endBackgroundTask(backgroundTask)
backGroundFetchBGTaskID = .invalid
}
return
}

filteredByDate = allGlucose.filter { $0.dateString > syncDate }
filteredByDate = newGlucose.filter { $0.dateString > syncDate }
filtered = glucoseStorage.filterTooFrequentGlucose(filteredByDate, at: syncDate)

guard filtered.isNotEmpty else {
// end of the BG tasks
// end of the Background tasks
if let backgroundTask = backGroundFetchBGTaskID {
UIApplication.shared.endBackgroundTask(backgroundTask)
backGroundFetchBGTaskID = .invalid
Expand All @@ -265,24 +263,7 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {

deviceDataManager.heartbeat(date: Date())

Task.detached {
await self.nightscoutManager.uploadGlucose()
await self.tidepoolService.uploadGlucose(device: self.cgmManager?.cgmManagerStatus.device)
}

let glucoseForHealth = filteredByDate.filter { !glucoseFromHealth.contains($0) }

guard glucoseForHealth.isNotEmpty else {
// end of the BG tasks
if let backgroundTask = backGroundFetchBGTaskID {
UIApplication.shared.endBackgroundTask(backgroundTask)
backGroundFetchBGTaskID = .invalid
}
return
}
healthKitManager.saveIfNeeded(bloodGlucose: glucoseForHealth)

// end of the BG tasks
// End of the Background tasks
if let backgroundTask = backGroundFetchBGTaskID {
UIApplication.shared.endBackgroundTask(backgroundTask)
backGroundFetchBGTaskID = .invalid
Expand All @@ -303,17 +284,15 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
}
.sink { glucose in
debug(.nightscout, "FetchGlucoseManager callback sensor")
Publishers.CombineLatest3(
Publishers.CombineLatest(
Just(glucose),
Just(self.glucoseStorage.syncDate()),
self.healthKitManager.fetch(nil)
Just(self.glucoseStorage.syncDate())
)
.eraseToAnyPublisher()
.sink { newGlucose, syncDate, glucoseFromHealth in
.sink { newGlucose, syncDate in
self.glucoseStoreAndHeartDecision(
syncDate: syncDate,
glucose: newGlucose,
glucoseFromHealth: glucoseFromHealth
glucose: newGlucose
)
}
.store(in: &self.lifetime)
Expand Down
87 changes: 81 additions & 6 deletions FreeAPS/Sources/APS/Storage/CarbsStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ protocol CarbsStorage {
func getCarbsNotYetUploadedToNightscout() async -> [NightscoutTreatment]
func getFPUsNotYetUploadedToNightscout() async -> [NightscoutTreatment]
func deleteCarbs(at uniqueID: String, fpuID: String, complex: Bool)
func getCarbsNotYetUploadedToHealth() async -> [CarbsEntry]
func getCarbsNotYetUploadedToTidepool() async -> [CarbsEntry]
}

final class BaseCarbsStorage: CarbsStorage, Injectable {
Expand Down Expand Up @@ -44,8 +46,9 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
entriesToStore = await filterRemoteEntries(entries: entriesToStore)
}

await saveCarbEquivalents(entries: entriesToStore, areFetchedFromRemote: areFetchedFromRemote)
await saveCarbsToCoreData(entries: entriesToStore, areFetchedFromRemote: areFetchedFromRemote)

await saveCarbEquivalents(entries: entriesToStore, areFetchedFromRemote: areFetchedFromRemote)
}

private func filterRemoteEntries(entries: [CarbsEntry]) async -> [CarbsEntry] {
Expand Down Expand Up @@ -116,7 +119,7 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
- Returns: A tuple containing the array of future carb entries and the total carb equivalents.
*/
private func processFPU(
entries _: [CarbsEntry],
entries: [CarbsEntry],
fat: Decimal,
protein: Decimal,
createdAt: Date,
Expand Down Expand Up @@ -145,7 +148,7 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
var numberOfEquivalents = carbEquivalents / carbEquivalentSize

var useDate = actualDate ?? createdAt
let fpuID = UUID().uuidString
let fpuID = entries.first?.fpuID ?? UUID().uuidString
var futureCarbArray = [CarbsEntry]()
var firstIndex = true

Expand All @@ -162,7 +165,8 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
fat: 0,
protein: 0,
note: nil,
enteredBy: CarbsEntry.manual, isFPU: true,
enteredBy: CarbsEntry.manual,
isFPU: true,
fpuID: fpuID
)
futureCarbArray.append(eachCarbEntry)
Expand Down Expand Up @@ -203,6 +207,12 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
newItem.id = UUID()
newItem.isFPU = false
newItem.isUploadedToNS = areFetchedFromRemote ? true : false
newItem.isUploadedToHealth = false
newItem.isUploadedToTidepool = false

if entry.fat != nil, entry.protein != nil, let fpuId = entry.fpuID {
newItem.fpuID = UUID(uuidString: fpuId)
}

do {
guard self.coredataContext.hasChanges else { return }
Expand All @@ -214,8 +224,10 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
}

private func saveFPUToCoreDataAsBatchInsert(entries: [CarbsEntry], areFetchedFromRemote: Bool) async {
let commonFPUID =
UUID() // all fpus should only get ONE id per batch insert to be able to delete them referencing the fpuID
let commonFPUID = UUID(
uuidString: entries.first?.fpuID ?? UUID()
.uuidString
) // all fpus should only get ONE id per batch insert to be able to delete them referencing the fpuID
var entrySlice = ArraySlice(entries) // convert to ArraySlice
let batchInsert = NSBatchInsertRequest(entity: CarbEntryStored.entity()) { (managedObject: NSManagedObject) -> Bool in
guard let carbEntry = managedObject as? CarbEntryStored, let entry = entrySlice.popFirst(),
Expand All @@ -229,6 +241,7 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
carbEntry.fpuID = commonFPUID
carbEntry.isFPU = true
carbEntry.isUploadedToNS = areFetchedFromRemote ? true : false
// do NOT set Health and Tidepool flags to ensure they will NOT be uploaded
return false // return false to continue
}
await coredataContext.perform {
Expand Down Expand Up @@ -403,4 +416,66 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
}
}
}

func getCarbsNotYetUploadedToHealth() async -> [CarbsEntry] {
let results = await CoreDataStack.shared.fetchEntitiesAsync(
ofType: CarbEntryStored.self,
onContext: coredataContext,
predicate: NSPredicate.carbsNotYetUploadedToHealth,
key: "date",
ascending: false
)

guard let carbEntries = results as? [CarbEntryStored] else {
return []
}

return await coredataContext.perform {
return carbEntries.map { result in
CarbsEntry(
id: result.id?.uuidString,
createdAt: result.date ?? Date(),
actualDate: result.date,
carbs: Decimal(result.carbs),
fat: Decimal(result.fat),
protein: Decimal(result.protein),
note: result.note,
enteredBy: CarbsEntry.manual,
isFPU: result.isFPU,
fpuID: result.fpuID?.uuidString
)
}
}
}

func getCarbsNotYetUploadedToTidepool() async -> [CarbsEntry] {
let results = await CoreDataStack.shared.fetchEntitiesAsync(
ofType: CarbEntryStored.self,
onContext: coredataContext,
predicate: NSPredicate.carbsNotYetUploadedToTidepool,
key: "date",
ascending: false
)

guard let carbEntries = results as? [CarbEntryStored] else {
return []
}

return await coredataContext.perform {
return carbEntries.map { result in
CarbsEntry(
id: result.id?.uuidString,
createdAt: result.date ?? Date(),
actualDate: result.date,
carbs: Decimal(result.carbs),
fat: nil,
protein: nil,
note: result.note,
enteredBy: CarbsEntry.manual,
isFPU: nil,
fpuID: nil
)
}
}
}
}
Loading

0 comments on commit 14dbbbd

Please sign in to comment.