8000 fix(cbio): disclose the positions the stored history never received by shroominic · Pull Request #188 · shroominic/OpenGlucose · GitHub
[go: up one dir, main page]

Skip to content
Dismiss alert
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions openhealth/lib/src/session_presentation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -610,8 +610,27 @@ String? cbioProvisionalValueText(CgmReading? reading) {
return (raw / 10).toStringAsFixed(1);
}

/// What the app actually stored for this sensor: how many records and which
/// sensor positions they cover. Positions are the protocol's own `index`
/// Sensor positions inside the stored span that this phone never received.
///
/// The first and last stored positions bound an *envelope*. The protocol's own
/// `index` counter advances one per stored minute, so a stored span of 1-7 with
/// five records holds two holes - positions the sensor moved past that were
/// never delivered to this app.
int cbioMissingPositions(Iterable<CgmReading> readings) {
final positions = <int>{
for (final reading in readings)
if (reading.sensorMinute != null) reading.sensorMinute!,
};
if (positions.isEmpty) {
return 0;
}
final sorted = positions.toList()..sort();
return (sorted.last - sorted.first + 1) - sorted.length;
}

/// What the app actually stored for this sensor: how many records, which sensor
/// positions they cover, and - when the envelope is not full - how many
/// positions inside it never arrived. Positions are the protocol's own `index`
/// counter, which advances one per stored minute; it is never a wall clock.
String cbioStoredRangeText(Iterable<CgmReading> readings) {
final positions = <int>[
Expand All @@ -622,8 +641,14 @@ String cbioStoredRangeText(Iterable<CgmReading> readings) {
return '${readings.length} readings stored';
}
positions.sort();
return '${readings.length} readings stored · '
final stored =
'${readings.length} readings stored · '
'sensor minutes ${positions.first}–${positions.last}';
final missing = cbioMissingPositions(readings);
if (missing <= 0) {
return stored;
}
return '$stored · $missing positions not received';
}

/// The index-to-clock anchor a GS1 session published, or null when it has none.
Expand Down
67 changes: 67 additions & 0 deletions openhealth/test/cbio_app_surface_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,73 @@ void main() {
}
});

test('the stored range names a hole instead of printing an envelope', () {
List<CgmReading> readingsAt(List<int> positions) => <CgmReading>[
for (final position in positions)
CgmReading(
valueMgdl: 100,
source: CgmRecordSource.raw,
sensorMinute: position,
rawValue: 55,
isDisplayProvisional: true,
),
];

expect(
cbioStoredRangeText(readingsAt(<int>[1, 2, 3])),
'3 readings stored · sensor minutes 1–3',
);
expect(
cbioStoredRangeText(readingsAt(<int>[1, 2, 3, 6, 7])),
'5 readings stored · sensor minutes 1–7 · 2 positions not received',
reason: '1-7 is an envelope, not what the app stored',
);
expect(
cbioStoredRangeText(readingsAt(<int>[4])),
'1 readings stored · sensor minutes 4–4',
);
});

testWidgets('the dashboard names a hole in the stored history', (
tester,
) async {
final readings = <CgmReading>[
for (final position in <int>[10, 11, 12, 15, 16])
CgmReading(
valueMgdl: 100 + position.toDouble(),
source: CgmRecordSource.raw,
sensorMinute: position,
recordedAt: null,
rawValue: 55,
isDisplayProvisional: true,
),
];
final (controller, preferences) = await _controllerFor(
() => _snapshot(
stage: CgmSyncStage.ready,
statusText: 'Live. Reading every minute.',
history: readings,
historySync: const CgmHistorySyncState(
storedCount: 5,
totalAvailable: 16,
latestStoredOffset: 16,
),
),
);
await _pumpApp(tester, controller, preferences);

expect(
tester
.widget<Text>(find.byKey(const ValueKey<String>('cbioStoredRange')))
.data,
'5 readings stored · sensor minutes 10–16 · 2 positions not received',
);

await tester.pumpWidget(const SizedBox.shrink());
controller.dispose();
await tester.pump();
});

test('history progress wording reports the fetched count', () {
expect(
historySyncProgressText(
Expand Down
Loading
0