Commit 5a568f8

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2023-12-29 13:47:46
store intakes history on disk
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 7a2262d
Changed files (2)
lib
model
blood_pressure
lib/model/blood_pressure/medicine/intake_history.dart
@@ -1,3 +1,4 @@
+import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine.dart';
 import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine_intake.dart';
 import 'package:collection/collection.dart';
 import 'package:flutter/material.dart';
@@ -5,11 +6,30 @@ import 'package:flutter/material.dart';
 /// Model of all medicine intakes that allows fast access of data.
 ///
 /// Internally maintains a sorted list of intakes to allow for binary search.
-class IntakeHistory {
+///
+/// Intake history does not implement database support, as operations performed
+/// are
+class IntakeHistory extends ChangeNotifier {
   /// Create a intake history from an unsorted list of intakes.
   IntakeHistory(List<MedicineIntake> medicineIntakes):
     _medicineIntakes = medicineIntakes.sorted((p0, p1) => p0.compareTo(p1));
 
+  /// Creates a intake history from a sorted list of intakes.
+  IntakeHistory._sorted(this._medicineIntakes);
+
+  /// Restores a intake history from a [serialize] generated string.
+  factory IntakeHistory.deserialize(String serialized, List<Medicine> availableMedicines) =>
+    IntakeHistory._sorted(
+      serialized
+        .split('\n')
+        .map((e) => MedicineIntake.deserialize(e, availableMedicines))
+        .toList()
+    );
+
+  /// Serializes the current state of the object into a string.
+  String serialize() => _medicineIntakes.map((e) => e.serialize()).join('\n');
+  // TODO test serialization
+
   /// List of all medicine intakes sorted in ascending order.
   ///
   /// Can contain multiple medicine intakes at the same time.
@@ -32,6 +52,36 @@ class IntakeHistory {
     return UnmodifiableListView(_medicineIntakes.getRange(start, end));
   }
 
+  /// Save a medicine intake.
+  ///
+  /// Inserts the intake at the upper bound of intakes that are bigger or equal.
+  /// When no smaller bigger intake is available insert to the end of the list.
+  ///
+  /// Uses binary search to determine the bound.
+  void addIntake(MedicineIntake intake) {
+    int index = _findUpperBound(_medicineIntakes, intake.timestamp);
+
+    if (index == -1) {
+      _medicineIntakes.add(intake);
+    } else {
+      _medicineIntakes.insert(index, intake);
+    }
+    notifyListeners();
+  }
+
+  /// Attempts to delete a medicine intake.
+  ///
+  /// When finding multiple intakes with the same timestamp, medicine
+  /// and dosis all instances will get deleted.
+  void deleteIntake(MedicineIntake intake) {
+    int idx = binarySearch(_medicineIntakes, intake);
+    while (idx >= 0) {
+      _medicineIntakes.removeAt(idx);
+      idx = binarySearch(_medicineIntakes, intake);
+    }
+    notifyListeners();
+  }
+
   /// Use binary search to determine the first index in [list] before which all
   /// values that are before or at the same time as [t].
   int _findUpperBound(List<MedicineIntake> list, DateTime t) {
@@ -75,32 +125,4 @@ class IntakeHistory {
 
     return idx + 1;
   }
-
-  /// Save a medicine intake.
-  ///
-  /// Inserts the intake at the upper bound of intakes that are bigger or equal.
-  /// When no smaller bigger intake is available insert to the end of the list.
-  ///
-  /// Uses binary search to determine the bound.
-  void addIntake(MedicineIntake intake) {
-    int index = _findUpperBound(_medicineIntakes, intake.timestamp);
-
-    if (index == -1) {
-      _medicineIntakes.add(intake);
-    } else {
-      _medicineIntakes.insert(index, intake);
-    }
-  }
-
-  /// Attempts to delete a medicine intake.
-  ///
-  /// When finding multiple intakes with the same timestamp, medicine
-  /// and dosis all instances will get deleted.
-  void deleteIntake(MedicineIntake intake) {
-    int idx = binarySearch(_medicineIntakes, intake);
-    while (idx >= 0) {
-      _medicineIntakes.removeAt(idx);
-      idx = binarySearch(_medicineIntakes, intake);
-    }
-  }
 }
\ No newline at end of file
lib/main.dart
@@ -1,4 +1,7 @@
+import 'dart:io';
+
 import 'package:blood_pressure_app/components/consistent_future_builder.dart';
+import 'package:blood_pressure_app/model/blood_pressure/medicine/intake_history.dart';
 import 'package:blood_pressure_app/model/blood_pressure/model.dart';
 import 'package:blood_pressure_app/model/storage/db/config_dao.dart';
 import 'package:blood_pressure_app/model/storage/db/config_db.dart';
@@ -12,7 +15,9 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 import 'package:flutter_localizations/flutter_localizations.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:package_info_plus/package_info_plus.dart';
+import 'package:path/path.dart';
 import 'package:provider/provider.dart';
+import 'package:sqflite/sqflite.dart';
 
 late final ConfigDB _database;
 late final BloodPressureModel _bloodPressureModel;
@@ -41,6 +46,23 @@ Future<Widget> _loadApp() async {
   final intervalStorageManager = await IntervallStoreManager.load(configDao, 0);
   final exportColumnsManager = await configDao.loadExportColumnsManager(0);
 
+  // TODO: unify with blood pressure model (#257)
+  late final IntakeHistory intakeHistory;
+  try {
+    if (settings.medications.isNotEmpty) {
+      final intakeString = File(join(await getDatabasesPath(), 'medicine.intakes')).readAsStringSync();
+      intakeHistory = IntakeHistory.deserialize(intakeString, settings.medications);
+    } else {
+      intakeHistory = IntakeHistory([]);
+    }
+  } catch (e) {
+    assert(false, e.toString());
+    intakeHistory = IntakeHistory([]);
+  }
+  intakeHistory.addListener(() async {
+    File(join(await getDatabasesPath(), 'medicine.intakes')).writeAsStringSync(intakeHistory.serialize());
+  });
+
   // update logic
   if (settings.lastVersion == 0) {
     await updateLegacySettings(settings, exportSettings, csvExportSettings, pdfExportSettings, intervalStorageManager);
@@ -66,6 +88,7 @@ Future<Widget> _loadApp() async {
     ChangeNotifierProvider(create: (context) => pdfExportSettings),
     ChangeNotifierProvider(create: (context) => intervalStorageManager),
     ChangeNotifierProvider(create: (context) => exportColumnsManager),
+    ChangeNotifierProvider(create: (context) => intakeHistory),
   ], child: const AppRoot());
 }