Commit 992bf72

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-06-26 13:13:17
fix export and import
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent bb5d9bc
Changed files (8)
app
health_data_store
lib
src
app/lib/model/blood_pressure/model.dart
@@ -56,18 +56,18 @@ class BloodPressureModel extends ChangeNotifier {
   }
 
   /// Returns all recordings in saved in a range in ascending order
-  Future<UnmodifiableListView<BloodPressureRecord>> getInTimeRange(DateTime from, DateTime to) async {
+  Future<UnmodifiableListView<OldBloodPressureRecord>> getInTimeRange(DateTime from, DateTime to) async {
     if (!_database.isOpen) return UnmodifiableListView([]);
     final dbEntries = await _database.query('bloodPressureModel',
         orderBy: 'timestamp DESC',
         where: 'timestamp BETWEEN ? AND ?',
         whereArgs: [from.millisecondsSinceEpoch, to.millisecondsSinceEpoch],); // descending
-    final List<BloodPressureRecord> recordsInRange = _convert(dbEntries);
+    final List<OldBloodPressureRecord> recordsInRange = _convert(dbEntries);
     return UnmodifiableListView(recordsInRange);
   }
 
   /// Querries all measurements saved in the database.
-  Future<UnmodifiableListView<BloodPressureRecord>> get all async {
+  Future<UnmodifiableListView<OldBloodPressureRecord>> get all async {
     if (!_database.isOpen) return UnmodifiableListView([]);
     return UnmodifiableListView(_convert(await _database.query('bloodPressureModel', columns: ['*'])));
   }
@@ -77,12 +77,12 @@ class BloodPressureModel extends ChangeNotifier {
   /// Cannot be accessed anymore.
   Future<void> close() => _database.close();
 
-  List<BloodPressureRecord> _convert(List<Map<String, Object?>> dbResult) {
-    final List<BloodPressureRecord> records = [];
+  List<OldBloodPressureRecord> _convert(List<Map<String, Object?>> dbResult) {
+    final List<OldBloodPressureRecord> records = [];
     for (final e in dbResult) {
       final needlePinJson = e['needlePin'] as String?;
       final needlePin = (needlePinJson != null) ? jsonDecode(needlePinJson) : null;
-      records.add(BloodPressureRecord(
+      records.add(OldBloodPressureRecord(
           DateTime.fromMillisecondsSinceEpoch(e['timestamp'] as int),
           e['systolic'] as int?,
           e['diastolic'] as int?,
app/lib/model/blood_pressure/needle_pin.dart
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
 
 @immutable
 @Deprecated('only maintained for imports, use health_data_store')
-/// Metadata and secondary information for a [BloodPressureRecord].
+/// Metadata and secondary information for a [OldBloodPressureRecord].
 class MeasurementNeedlePin {
   /// Create a instance from a map created in older versions.
   MeasurementNeedlePin.fromMap(Map<String, dynamic> json)
app/lib/model/blood_pressure/record.dart
@@ -4,9 +4,9 @@ import 'package:flutter/material.dart';
 /// Immutable data representation of a saved measurement.
 @immutable
 @Deprecated('use health data store')
-class BloodPressureRecord {
+class OldBloodPressureRecord {
   /// Create a measurement.
-  BloodPressureRecord(DateTime creationTime, this.systolic, this.diastolic, this.pulse, this.notes, {
+  OldBloodPressureRecord(DateTime creationTime, this.systolic, this.diastolic, this.pulse, this.notes, {
     this.needlePin,
   }) {
     if (creationTime.millisecondsSinceEpoch > 0) {
@@ -36,14 +36,14 @@ class BloodPressureRecord {
   final MeasurementNeedlePin? needlePin;
 
   /// Creates a new record from this one by updating individual properties.
-  BloodPressureRecord copyWith({
+  OldBloodPressureRecord copyWith({
     DateTime? creationTime,
     int? systolic,
     int? diastolic,
     int? pulse,
     String? notes,
     MeasurementNeedlePin? needlePin,
-  }) => BloodPressureRecord(
+  }) => OldBloodPressureRecord(
     creationTime ?? this.creationTime,
     systolic ?? this.systolic,
     diastolic ?? this.diastolic,
app/lib/model/blood_pressure/update_legacy_entries.dart
@@ -61,7 +61,7 @@ Future<void> updateLegacyEntries(
   // Migrating records and notes
   try {
     final oldBpModel = await BloodPressureModel.create();
-    for (final BloodPressureRecord r in await oldBpModel?.all ?? []) {
+    for (final OldBloodPressureRecord r in await oldBpModel?.all ?? []) {
       if (r.diastolic != null || r.systolic != null || r.pulse != null) {
         await bpRepo.add(hds.BloodPressureRecord(
           time: r.creationTime,
app/lib/screens/subsettings/export_import/export_button_bar.dart
@@ -5,6 +5,8 @@ import 'dart:io';
 import 'dart:typed_data';
 
 import 'package:blood_pressure_app/components/dialoges/import_preview_dialoge.dart';
+import 'package:blood_pressure_app/model/blood_pressure/model.dart';
+import 'package:blood_pressure_app/model/blood_pressure/record.dart';
 import 'package:blood_pressure_app/model/export_import/csv_converter.dart';
 import 'package:blood_pressure_app/model/export_import/csv_record_parsing_actor.dart';
 import 'package:blood_pressure_app/model/export_import/pdf_converter.dart';
@@ -106,19 +108,51 @@ class ExportButtonBar extends StatelessWidget {
                     final bpRepo = RepositoryProvider.of<BloodPressureRepository>(context);
                     final noteRepo = RepositoryProvider.of<NoteRepository>(context);
                     final intakeRepo = RepositoryProvider.of<MedicineIntakeRepository>(context);
-                    final importedDB = await HealthDataStore.load(await openReadOnlyDatabase(file.path!));
-                    await Future.forEach(
-                      await importedDB.bpRepo.get(DateRange.all()),
-                      bpRepo.add,
-                    );
-                    await Future.forEach(
-                      await importedDB.noteRepo.get(DateRange.all()),
-                      noteRepo.add,
-                    );
-                    await Future.forEach(
-                      await importedDB.intakeRepo.get(DateRange.all()),
-                      intakeRepo.add,
-                    );
+
+                    final List<BloodPressureRecord> records = [];
+                    final List<Note> notes = [];
+                    final List<MedicineIntake> intakes = [];
+                    try {
+                      final db = await openReadOnlyDatabase(file.path!);
+                      final importedDB = await HealthDataStore.load(db);
+                      records.addAll(await importedDB.bpRepo.get(DateRange.all()));
+                      notes.addAll(await importedDB.noteRepo.get(DateRange.all()));
+                      intakes.addAll(await importedDB.intakeRepo.get(DateRange.all()));
+                      await db.close();
+                    } catch (e) {
+                      // DB doesn't conform new format
+                    }
+
+                    try { // Update legacy format
+                      final model = (records.isNotEmpty || notes.isNotEmpty || intakes.isNotEmpty)
+                        ? null
+                        : await BloodPressureModel.create(dbPath: file.path!, isFullPath: true);
+                      for (final OldBloodPressureRecord oldR in (await model?.all) ?? []) {
+                        if (oldR.systolic != null || oldR.diastolic != null || oldR.pulse != null) {
+                          records.add(BloodPressureRecord(
+                            time: oldR.creationTime,
+                            sys: oldR.systolic == null ? null :Pressure.mmHg(oldR.systolic!),
+                            dia: oldR.diastolic == null ? null :Pressure.mmHg(oldR.diastolic!),
+                            pul: oldR.pulse,
+                          ));
+                        }
+                        if (oldR.notes.isNotEmpty || oldR.needlePin != null) {
+                          notes.add(Note(
+                            time: oldR.creationTime,
+                            note: oldR.notes.isEmpty ? null : oldR.notes,
+                            color: oldR.needlePin?.color.value,
+                          ));
+                        }
+                      }
+                    } catch (e) {
+                      // DB not importable
+                    }
+
+                    await Future.forEach(records, bpRepo.add);
+                    await Future.forEach(notes, noteRepo.add);
+                    await Future.forEach(intakes, intakeRepo.add);
+                    // TODO: show success
+
                     break;
                   default:
                     _showError(messenger, localizations.errWrongImportFormat);
@@ -143,15 +177,16 @@ void performExport(BuildContext context, [AppLocalizations? localizations]) asyn
   final filename = 'blood_press_${DateTime.now().toIso8601String()}';
   switch (exportSettings.exportFormat) {
     case ExportFormat.db:
-      final path = join(await getDatabasesPath(), 'blood_pressure.db');
+      final path = join(await getDatabasesPath(), 'bp.db');
 
-      if (context.mounted) await _exportFile(context, path, '$filename.db', 'text/sqlite');
+      if (context.mounted) await _exportFile(context, path, '$filename.db', 'application/vnd.sqlite3');
       break;
     case ExportFormat.csv:
       final csvConverter = CsvConverter(
         Provider.of<CsvExportSettings>(context, listen: false),
         Provider.of<ExportColumnsManager>(context, listen: false),
       );
+      // TODO: update color serialization
       final csvString = csvConverter.create(await _getEntries(context));
       final data = Uint8List.fromList(utf8.encode(csvString));
       if (context.mounted) await _exportData(context, data, '$filename.csv', 'text/csv');
app/lib/screens/error_reporting_screen.dart
@@ -113,7 +113,7 @@ class ErrorScreen extends StatelessWidget {
                             content: Text('ERR: $e'),),);
                       }
                     },
-                    child: const Text('rescue old measurements'),
+                    child: const Text('rescue legacy blood_pressure.db'),
                   ),
                   TextButton(
                     onPressed: () async {
app/test/model/bood_pressure_test.dart
@@ -8,7 +8,7 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart';
 void main() {
   group('BloodPressureRecord', () {
     test('should initialize with all values supported by dart', () {
-      final BloodPressureRecord record = BloodPressureRecord(DateTime.fromMicrosecondsSinceEpoch(1582991592), 0, -50, 1000,
+      final OldBloodPressureRecord record = OldBloodPressureRecord(DateTime.fromMicrosecondsSinceEpoch(1582991592), 0, -50, 1000,
           '((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა',);
 
       expect(record.creationTime, DateTime.fromMicrosecondsSinceEpoch(1582991592));
@@ -19,7 +19,7 @@ void main() {
           '((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა',);
     });
     test('should not save times at or before epoch', () {
-      expect(() => BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(0), 0, 0, 0, ''), throwsAssertionError);
+      expect(() => OldBloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(0), 0, 0, 0, ''), throwsAssertionError);
     });
   });
 
health_data_store/lib/src/types/full_entry.dart
@@ -65,6 +65,7 @@ extension FullEntryList on List<FullEntry> {
     List<Note> notes,
     List<MedicineIntake> intakes,
   ) {
+    // TODO: make these asserts unneccessary
     assert(!records
       .any((rOuter) => records
         .where((rInner) => rOuter.time == rInner.time).length != 1,