@@ -30,14 +30,14 @@ public actor DataPointManager {
3030 }
3131 }
3232
33- private func updateDatapoint( goal : Goal , datapoint : DataPoint , datapointValue : NSNumber ) async throws {
33+ private func updateDatapoint( goal : Goal , datapoint : DataPoint , datapointValue : NSNumber , comment : String ) async throws {
3434 let val = datapoint. value
35- if datapointValue == val {
35+ if datapointValue == val && comment == datapoint . comment {
3636 return
3737 }
3838 let params = [
3939 " value " : " \( datapointValue) " ,
40- " comment " : " Auto-updated via Apple Health " ,
40+ " comment " : comment ,
4141 ]
4242 let _ = try await requestManager. put ( url: " api/v1/users/{username}/goals/ \( goal. slug) /datapoints/ \( datapoint. id) .json " , parameters: params)
4343 }
@@ -103,37 +103,52 @@ public actor DataPointManager {
103103 let datapoints = try await datapointsSince ( goal: goal, daystamp: try ! Daystamp ( fromString: firstDaystamp. description) )
104104 let realDatapoints = datapoints. filter { !$0. isDummy && !$0. isInitial }
105105
106- for newDataPoint in healthKitDataPoints {
107- try await self . updateToMatchDataPoint ( goal: goal, newDataPoint: newDataPoint, recentDatapoints: realDatapoints)
106+ let healthKitDataPointsByDay = Dictionary ( grouping: healthKitDataPoints) { $0. daystamp }
107+
108+ try await withThrowingTaskGroup ( of: Void . self) { group in
109+ for (daystamp, dayDataPoints) in healthKitDataPointsByDay {
110+ group. addTask {
111+ let existingDatapointsForDay = await self . datapointsMatchingDaystamp ( datapoints: realDatapoints, daystamp: daystamp)
112+ try await self . updateToMatchDataPointsForDay ( goal: goal, newDataPoints: dayDataPoints, existingDatapoints: existingDatapointsForDay)
113+ }
114+ }
108115 }
109116 }
110117
111- private func updateToMatchDataPoint( goal: Goal , newDataPoint : BeeDataPoint , recentDatapoints: [ DataPoint ] ) async throws {
112- var matchingDatapoints = datapointsMatchingDaystamp ( datapoints: recentDatapoints, daystamp: newDataPoint. daystamp)
113- if matchingDatapoints. count == 0 {
114- // If there are not already data points for this day, do not add points
115- // from before the creation of the goal. This avoids immediate derailment
116- //on do less goals, and excessive safety buffer on do-more goals.
117- if newDataPoint. daystamp < goal. initDaystamp {
118- return
118+ private func updateToMatchDataPointsForDay( goal: Goal , newDataPoints: [ BeeDataPoint ] , existingDatapoints: [ DataPoint ] ) async throws {
119+ try await withThrowingTaskGroup ( of: Void . self) { group in
120+ var processedDatapoints : Set < String > = [ ]
121+
122+ for newDataPoint in newDataPoints {
123+ let matchingDatapoint = existingDatapoints. first { $0. requestid == newDataPoint. requestid }
124+
125+ if let existingDatapoint = matchingDatapoint {
126+ if !isApproximatelyEqual( existingDatapoint. value. doubleValue, newDataPoint. value. doubleValue) || existingDatapoint. comment != newDataPoint. comment {
127+ group. addTask {
128+ self . logger. notice ( " Updating datapoint for \( goal. id) with requestId \( newDataPoint. requestid, privacy: . public) from \( existingDatapoint. value) to \( newDataPoint. value) " )
129+ try await self . updateDatapoint ( goal: goal, datapoint: existingDatapoint, datapointValue: newDataPoint. value, comment: newDataPoint. comment)
130+ }
131+ }
132+ processedDatapoints. insert ( existingDatapoint. requestid)
133+ } else if newDataPoint. daystamp >= goal. initDaystamp {
134+ // If there are not already data points for this requestId, do not add points
135+ // from before the creation of the goal. This avoids immediate derailment
136+ // on do less goals, and excessive safety buffer on do-more goals.
137+ group. addTask {
138+ let urText = " \( newDataPoint. daystamp. day) \( newDataPoint. value) \" \( newDataPoint. comment) \" "
139+ self . logger. notice ( " Creating new datapoint for \( goal. id, privacy: . public) with requestId \( newDataPoint. requestid, privacy: . public) : \( newDataPoint. value, privacy: . private) " )
140+ try await self . postDatapoint ( goal: goal, urText: urText, requestId: newDataPoint. requestid)
141+ }
142+ }
119143 }
120-
121- let urText = " \( newDataPoint. daystamp. day) \( newDataPoint. value) \" \( newDataPoint. comment) \" "
122- let requestId = newDataPoint. requestid
123-
124- logger. notice ( " Creating new datapoint for \( goal. id, privacy: . public) on \( newDataPoint. daystamp, privacy: . public) : \( newDataPoint. value, privacy: . private) " )
125-
126- try await postDatapoint ( goal: goal, urText: urText, requestId: requestId)
127- } else if matchingDatapoints. count >= 1 {
128- let firstDatapoint = matchingDatapoints. remove ( at: 0 )
129- for datapoint in matchingDatapoints {
130- try await deleteDatapoint ( goal: goal, datapoint: datapoint)
131- }
132-
133- if !isApproximatelyEqual( firstDatapoint. value. doubleValue, newDataPoint. value. doubleValue) {
134- logger. notice ( " Updating datapoint for \( goal. id) on \( firstDatapoint. daystamp, privacy: . public) from \( firstDatapoint. value) to \( newDataPoint. value) " )
135-
136- try await updateDatapoint ( goal: goal, datapoint: firstDatapoint, datapointValue: newDataPoint. value)
144+
145+ for existingDatapoint in existingDatapoints {
146+ if !processedDatapoints. contains ( existingDatapoint. requestid) {
147+ group. addTask {
148+ self . logger. notice ( " Deleting obsolete datapoint for \( goal. id) with requestId \( existingDatapoint. requestid, privacy: . public) " )
149+ try await self . deleteDatapoint ( goal: goal, datapoint: existingDatapoint)
150+ }
151+ }
137152 }
138153 }
139154 }
0 commit comments