Skip to content

Commit b2cb456

Browse files
committed
fix: import buddies as proper entities and fill sparse profile data
- Parse <buddy> and <divemaster> as unmatchedBuddyNames and unmatchedDiveGuideNames to create proper Buddy entities with correct roles (buddy/diveGuide) instead of plain text fields - Set diverId on inline-created buddies so they appear in buddy list - Fix updateBuddy() not persisting diverId to database - Invalidate buddyListNotifierProvider after import for immediate refresh - Count unique buddies in import summary, not buddy-dive links - Generalize sparse field interpolation to handle both pressure and temperature with interpolation between known points and forward/back fill at edges
1 parent 5d64277 commit b2cb456

2 files changed

Lines changed: 50 additions & 28 deletions

File tree

lib/features/universal_import/data/parsers/subsurface_xml_parser.dart

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -404,42 +404,64 @@ class SubsurfaceXmlParser implements ImportParser {
404404
if (pressure != null) point['pressure'] = pressure;
405405
points.add(point);
406406
}
407-
_interpolatePressures(points);
407+
_fillSparseField(points, 'pressure');
408+
_fillSparseField(points, 'temperature');
408409
return points;
409410
}
410411

411-
/// Linearly interpolates missing pressure values between known readings.
412+
/// Fills missing values for a sparse field using linear interpolation
413+
/// between known readings, with forward-fill after the last known value
414+
/// and back-fill before the first.
412415
///
413-
/// Subsurface dive computers only transmit tank pressure every few samples.
414-
/// This fills the gaps so the profile chart displays a smooth pressure curve
415-
/// instead of alternating between values and null.
416-
static void _interpolatePressures(List<Map<String, dynamic>> points) {
416+
/// Subsurface dive computers transmit pressure and temperature on a subset
417+
/// of samples. This fills the gaps so the profile chart displays smooth
418+
/// curves instead of alternating between values and null.
419+
static void _fillSparseField(
420+
List<Map<String, dynamic>> points,
421+
String field,
422+
) {
417423
if (points.length < 2) return;
418424

419-
// Find consecutive spans of missing pressure between known values
420-
int? lastKnownIndex;
425+
// Collect indices of points that have a value for this field
426+
final knownIndices = <int>[];
421427
for (var i = 0; i < points.length; i++) {
422-
if (points[i]['pressure'] == null) continue;
423-
424-
if (lastKnownIndex != null && i - lastKnownIndex > 1) {
425-
// Interpolate between lastKnownIndex and i
426-
final startPressure = points[lastKnownIndex]['pressure'] as double;
427-
final endPressure = points[i]['pressure'] as double;
428-
final startTime = points[lastKnownIndex]['timestamp'] as int;
429-
final endTime = points[i]['timestamp'] as int;
430-
final timeDelta = endTime - startTime;
431-
432-
if (timeDelta > 0) {
433-
for (var j = lastKnownIndex + 1; j < i; j++) {
434-
final t = points[j]['timestamp'] as int;
435-
final fraction = (t - startTime) / timeDelta;
436-
points[j]['pressure'] =
437-
startPressure + (endPressure - startPressure) * fraction;
438-
}
428+
if (points[i][field] != null) knownIndices.add(i);
429+
}
430+
if (knownIndices.isEmpty) return;
431+
432+
// Back-fill: set all points before the first known value
433+
final firstKnown = knownIndices.first;
434+
final firstValue = points[firstKnown][field] as double;
435+
for (var i = 0; i < firstKnown; i++) {
436+
points[i][field] = firstValue;
437+
}
438+
439+
// Interpolate between consecutive known values
440+
for (var k = 0; k < knownIndices.length - 1; k++) {
441+
final startIdx = knownIndices[k];
442+
final endIdx = knownIndices[k + 1];
443+
if (endIdx - startIdx <= 1) continue;
444+
445+
final startVal = points[startIdx][field] as double;
446+
final endVal = points[endIdx][field] as double;
447+
final startTime = points[startIdx]['timestamp'] as int;
448+
final endTime = points[endIdx]['timestamp'] as int;
449+
final timeDelta = endTime - startTime;
450+
451+
if (timeDelta > 0) {
452+
for (var j = startIdx + 1; j < endIdx; j++) {
453+
final t = points[j]['timestamp'] as int;
454+
final fraction = (t - startTime) / timeDelta;
455+
points[j][field] = startVal + (endVal - startVal) * fraction;
439456
}
440457
}
458+
}
441459

442-
lastKnownIndex = i;
460+
// Forward-fill: set all points after the last known value
461+
final lastKnown = knownIndices.last;
462+
final lastValue = points[lastKnown][field] as double;
463+
for (var i = lastKnown + 1; i < points.length; i++) {
464+
points[i][field] = lastValue;
443465
}
444466
}
445467

test/features/universal_import/data/parsers/subsurface_xml_parser_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,10 @@ void main() {
345345
expect(profile[0]['temperature'], 21.0);
346346
expect(profile[0]['pressure'], 196.9);
347347

348-
// Second sample: interpolated between 196.9 (t=0) and 180.0 (t=60)
348+
// Second sample: pressure interpolated, temperature forward-filled
349349
expect(profile[1]['timestamp'], 30);
350350
expect(profile[1]['depth'], 10.5);
351-
expect(profile[1].containsKey('temperature'), isFalse);
351+
expect(profile[1]['temperature'], 21.0);
352352
expect(profile[1]['pressure'], closeTo(188.45, 0.01));
353353

354354
// Third sample: explicit pressure

0 commit comments

Comments
 (0)