Skip to content

Commit

Permalink
VIT-7028: Add data count and first asked to SyncProgressLog (#216)
Browse files Browse the repository at this point in the history
  • Loading branch information
andersio authored Aug 1, 2024
1 parent 43b4234 commit a7cb333
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ public enum ProcessedResourceData: Equatable, Encodable {
return timeSeriesData.payload
}
}

public var shouldSkipPost: Bool {
dataCount == 0
}

public var dataCount: Int {
switch self {
case let .summary(summaryData):
return summaryData.shouldSkipPost
return summaryData.dataCount
case let .timeSeries(timeSeriesData):
return timeSeriesData.shouldSkipPost
return timeSeriesData.dataCount
}
}

public var name: String {
switch self {
case let .summary(summaryData):
Expand Down Expand Up @@ -95,7 +99,7 @@ public enum TimeSeriesData: Equatable, Encodable {
}
}

public var shouldSkipPost: Bool {
public var dataCount: Int {
switch self {
case
let .glucose(samples), let .bloodOxygen(samples), let .heartRate(samples),
Expand All @@ -104,10 +108,10 @@ public enum TimeSeriesData: Equatable, Encodable {
let .caloriesActive(samples), let .caloriesBasal(samples), let .distance(samples),
let .floorsClimbed(samples), let .steps(samples), let .vo2Max(samples),
let .respiratoryRate(samples), let .temperature(samples):
return samples.isEmpty
return samples.count

case let .bloodPressure(samples):
return samples.isEmpty
return samples.count
}
}

Expand Down Expand Up @@ -174,20 +178,20 @@ public enum SummaryData: Equatable, Encodable {
}
}

public var shouldSkipPost: Bool {
public var dataCount: Int {
switch self {
case let .profile(patch):
return patch.dateOfBirth.isNil && patch.height.isNil && patch.biologicalSex.isNil
return patch.dateOfBirth.isNil && patch.height.isNil && patch.biologicalSex.isNil ? 0 : 1
case let .body(patch):
return patch.bodyFatPercentage.isEmpty && patch.bodyMass.isEmpty
return patch.bodyFatPercentage.count + patch.bodyMass.count
case let .workout(patch):
return patch.workouts.isEmpty
return patch.workouts.count
case let .activity(patch):
return patch.activities.isEmpty
return patch.activities.count
case let .sleep(patch):
return patch.sleep.isEmpty
return patch.sleep.count
case let .menstrualCycle(patch):
return patch.cycles.isEmpty
return patch.cycles.count
}
}

Expand Down
21 changes: 17 additions & 4 deletions Sources/VitalHealthKit/HealthKit/Storage/SyncProgressStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,19 @@ public struct SyncProgress: Codable {
public var end: Date?
public var tags: Set<SyncContextTag>
public private(set) var statuses: [Event<SyncStatus>]
public var dataCount: Int = 0

public var lastStatus: SyncStatus {
statuses.last!.type
}

public var id: Date { start }

public init(start: Date, status: SyncStatus, tags: Set<SyncContextTag>) {
public init(start: Date, status: SyncStatus, tags: Set<SyncContextTag>, dataCount: Int = 0) {
self.start = start
self.statuses = [Event(timestamp: start, type: status)]
self.tags = tags
self.dataCount = dataCount
}

public mutating func append(_ status: SyncStatus, at timestamp: Date = Date()) {
Expand Down Expand Up @@ -116,7 +118,7 @@ public struct SyncProgress: Codable {
public struct Resource: Codable {
public var syncs: [Sync] = []
public var systemEvents: [Event<SystemEventType>] = []
public var uploadedChunks: Int = 0
public var dataCount: Int = 0
public var firstAsked: Date? = nil

public var latestSync: Sync? {
Expand Down Expand Up @@ -178,7 +180,7 @@ final class SyncProgressStore {
}
}

func recordSync(_ id: SyncProgress.SyncID, _ status: SyncProgress.SyncStatus) {
func recordSync(_ id: SyncProgress.SyncID, _ status: SyncProgress.SyncStatus, dataCount: Int = 0) {
mutate(CollectionOfOne(id.resource.resourceToBackfillType())) {
let now = Date()

Expand All @@ -195,6 +197,7 @@ final class SyncProgressStore {
let index = $0.syncs.count - 1
$0.syncs[index].append(status, at: now)
$0.syncs[index].tags = id.tags
$0.syncs[index].dataCount += dataCount

switch status {
case .completed, .error, .cancelled, .noData:
Expand All @@ -214,9 +217,19 @@ final class SyncProgressStore {
}

$0.syncs.append(
SyncProgress.Sync(start: id.start, status: status, tags: id.tags)
SyncProgress.Sync(start: id.start, status: status, tags: id.tags, dataCount: dataCount)
)
}

$0.dataCount += dataCount
}
}

func recordAsk(_ resources: some Sequence<RemappedVitalResource>) {
let date = Date()

mutate(resources.map { $0.wrapped.resourceToBackfillType() }) {
$0.firstAsked = date
}
}

Expand Down
14 changes: 9 additions & 5 deletions Sources/VitalHealthKit/HealthKit/VitalHealthKitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ extension VitalHealthKitClient {
data: ProcessedResourceData?,
anchors: [StoredAnchor],
hasMore: Bool
) async throws -> Bool {
) async throws -> Int {

// We skip empty POST only in daily stage.
// Empty POST is sent for historical stage, so we would consistently emit
Expand All @@ -915,7 +915,7 @@ extension VitalHealthKitClient {
VitalLogger.healthKit.info("[\(description)] no data to upload", source: "Sync")
_status.send(.nothingToSync(resource))

return false
return 0
}

if configuration.mode.isAutomatic {
Expand Down Expand Up @@ -944,7 +944,7 @@ extension VitalHealthKitClient {
_status.send(.successSyncing(resource, data))

VitalLogger.healthKit.info("[\(description)] completed: \(hasMore ? "hasMore" : "noMore")", source: "Sync")
return true
return data.dataCount
}

// Overlap read and upload, so we maximize the use of limited background execution time.
Expand Down Expand Up @@ -1005,8 +1005,8 @@ extension VitalHealthKitClient {
pipelineScheduler.yield(.read(uncommittedAnchors: anchors))
}

let hasPosted = try await uploadStep(data: data, anchors: anchors, hasMore: hasMore)
progressStore.recordSync(syncID, hasPosted ? .uploadedChunk : .noData)
let dataCount = try await uploadStep(data: data, anchors: anchors, hasMore: hasMore)
progressStore.recordSync(syncID, dataCount >= 0 ? .uploadedChunk : .noData, dataCount: dataCount)

if !hasMore {
pipelineScheduler.yield(.success)
Expand Down Expand Up @@ -1092,6 +1092,10 @@ extension VitalHealthKitClient {
do {
try await store.requestReadWriteAuthorization(readResources, writeResource)

let state = authorizationState(store: store)
SyncProgressStore.shared.recordAsk(state.activeResources)
SyncProgressStore.shared.flush()

// We have gone through Ask successfully. Check if a connected source has been created.
do {
try await VitalClient.shared.checkConnectedSource(for: .appleHealthKit)
Expand Down
29 changes: 26 additions & 3 deletions Sources/VitalHealthKit/UI/ForEachVitalResource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public struct ForEachVitalResource: View {
VStack(alignment: .leading) {
Text("\(key.rawValue)")
if let lastUpdated = resource.latestSync?.statuses.last?.timestamp {
Text(verbatim: dateFormatter.string(from: lastUpdated))
Text(verbatim: "\(dateFormatter.string(from: lastUpdated)) \(timeFormatter.string(from: lastUpdated))")
.foregroundColor(Color.secondary)
.font(Font.subheadline)
}
Expand All @@ -70,7 +70,11 @@ public struct ForEachVitalResource: View {
}
.isDetailLink(false)
}
.onReceive(VitalHealthKitClient.shared.syncProgressPublisher().receive(on: RunLoop.main)) { progress in
.onReceive(
VitalHealthKitClient.shared.syncProgressPublisher()
.throttle(for: .milliseconds(100), scheduler: RunLoop.main, latest: true)
.receive(on: RunLoop.main)
) { progress in
self.items = progress.backfillTypes
.sorted(by: { $0.key.rawValue.compare($1.key.rawValue) == .orderedAscending })
self.nextSchedule = SyncProgressReporter.shared.nextSchedule()
Expand Down Expand Up @@ -135,6 +139,20 @@ private struct ResourceProgressDetailView: View {
Text("System Events")
}
.isDetailLink(false)

if let firstAsked = resource.firstAsked {
HStack(alignment: .center) {
Text("First Asked")
Spacer()
Text("\(dateFormatter.string(from: firstAsked)) \(timeFormatter.string(from: firstAsked))")
}
}

HStack(alignment: .center) {
Text("Total Data Count")
Spacer()
Text("\(resource.dataCount)")
}
}

ForEach(sections) { section in
Expand Down Expand Up @@ -169,7 +187,12 @@ private struct ResourceProgressDetailView: View {
icon(for: sync.lastStatus)

VStack(alignment: .leading) {
Text(verbatim: String(describing: sync.lastStatus))
if sync.lastStatus.isInProgress {
Text(verbatim: String(describing: sync.lastStatus))
} else {
Text(verbatim: "\(String(describing: sync.lastStatus)) (\(sync.dataCount) uploaded)")
}

Text(verbatim: sync.tags.map(String.init(describing:)).sorted().joined(separator: ", "))
.foregroundColor(Color.secondary)
.font(.subheadline)
Expand Down

0 comments on commit a7cb333

Please sign in to comment.