Commit af0df43

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-06-22 11:56:45
implement update of old data
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 60ca2d1
app/lib/model/blood_pressure/medicine/intake_history.dart
@@ -9,6 +9,7 @@ import 'package:flutter/material.dart';
 ///
 /// Intake history does not implement database support, as operations performed
 /// are
+@Deprecated('use health_data_store')
 class IntakeHistory extends ChangeNotifier {
   /// Create a intake history from an unsorted list of intakes.
   IntakeHistory(List<OldMedicineIntake> medicineIntakes):
@@ -26,14 +27,11 @@ class IntakeHistory extends ChangeNotifier {
         .toList(),
     );
 
-  /// Serializes the current state of the object into a string.
-  String serialize() => _medicineIntakes.map((e) => e.serialize()).join('\n');
   /// List of all medicine intakes sorted in ascending order.
   ///
   /// Can contain multiple medicine intakes at the same time.
   final List<OldMedicineIntake> _medicineIntakes;
 
-
   /// Returns all intakes in a given range in ascending order.
   ///
   /// Binary searches the lower and the upper bound of stored intakes to create
@@ -50,36 +48,6 @@ class IntakeHistory extends ChangeNotifier {
     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(OldMedicineIntake intake) {
-    final 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(OldMedicineIntake 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<OldMedicineIntake> list, DateTime t) {
app/lib/model/blood_pressure/medicine/medicine.dart
@@ -5,7 +5,7 @@ import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine_intake
 import 'package:flutter/material.dart';
 
 /// Description of a specific medicine.
-@deprecated
+@Deprecated('use health_data_store')
 class Medicine {
   /// Create a instance from a map created by [toMap].
   factory Medicine.fromMap(Map<String, dynamic> map) => Medicine(
@@ -27,18 +27,6 @@ class Medicine {
     this.hidden = false,
   });
 
-  /// Serialize the object to a restoreable map.
-  Map<String, dynamic> toMap() => {
-    'id': id,
-    'designation': designation,
-    'color': color.value,
-    'defaultDosis': defaultDosis,
-    'hidden': hidden,
-  };
-
-  /// Serialize the object to a restoreable string.
-  String toJson() => jsonEncode(toMap());
-
   /// Unique id used to store the medicine in serialized objects.
   final int id;
 
@@ -57,7 +45,6 @@ class Medicine {
   /// data inconsistencies with existing intakes that use this medicine.
   bool hidden;
 
-
   @override
   bool operator ==(Object other) =>
       identical(this, other) ||
app/lib/model/blood_pressure/medicine/medicine_intake.dart
@@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 
 /// Instance of a medicine intake.
-@deprecated
+@Deprecated('use health_data_store')
 class OldMedicineIntake implements Comparable<Object> {
   /// Create a intake from a String created by [serialize].
   ///
@@ -42,8 +42,8 @@ class OldMedicineIntake implements Comparable<Object> {
   ///
   /// The string consists of the id of the medicine, the unix timestamp and the
   /// dosis. These values are seperated by a null byte
-  String serialize() =>
-      '${medicine.id}\x00${timestamp.millisecondsSinceEpoch}\x00$dosis';
+  /*String serialize() =>
+      '${medicine.id}\x00${timestamp.millisecondsSinceEpoch}\x00$dosis';*/
 
   /// Kind of medicine taken.
   final Medicine medicine;
app/lib/model/blood_pressure/model.dart
@@ -36,10 +36,7 @@ class BloodPressureModel extends ChangeNotifier {
     }
   }
 
-  /// Factory method to create a BloodPressureModel for a database file. This is needed to allow an async constructor.
-  ///
-  /// [dbPath] is the path to the folder the database is in. When [dbPath] is left empty the default database file is
-  /// used. The [isFullPath] option tells the constructor not to add the default filename at the end of [dbPath].
+  /// Construct a instance of [BloodPressureModel] if a db file still exists.
   static Future<BloodPressureModel?> create({String? dbPath, bool isFullPath = false}) async {
     final component = BloodPressureModel._create();
     dbPath ??= await getDatabasesPath();
app/lib/model/blood_pressure/update_legacy_entries.dart
@@ -0,0 +1,89 @@
+import 'dart:io';
+
+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/storage/settings_store.dart';
+import 'package:flutter/material.dart';
+import 'package:health_data_store/health_data_store.dart' as hds;
+import 'package:path/path.dart';
+import 'package:sqflite/sqflite.dart';
+
+import '../../screens/error_reporting_screen.dart';
+import 'medicine/intake_history.dart';
+import 'medicine/medicine.dart';
+
+/// Loads data from old storage locations, adds them to new repos and deletes old storage location on success.
+@Deprecated('Only use to migrate legacy data')
+Future<void> updateLegacyEntries(
+  Settings settings,
+  hds.BloodPressureRepository bpRepo,
+  hds.NoteRepository noteRepo,
+  hds.MedicineRepository medRepo,
+  hds.MedicineIntakeRepository intakeRepo,
+) async { // TODO: test
+  // Migrate old meds still in use and old intakes
+  try {
+    if (settings.medications.isNotEmpty) {
+      final intakeString = File(join(await getDatabasesPath(), 'medicine.intakes')).readAsStringSync();
+      final intakeHistory = IntakeHistory.deserialize(intakeString, settings.medications);
+      final addedMeds = <Medicine, hds.Medicine>{};
+      for (final i in intakeHistory.getIntakes(DateTimeRange(
+        start: DateTime.fromMillisecondsSinceEpoch(0),
+        end: DateTime.now(),
+      ))) {
+        hds.Medicine? med = addedMeds[i.medicine];
+        if (med == null) {
+          med = hds.Medicine(
+            designation: i.medicine.designation,
+            color: i.medicine.color.value,
+            dosis: i.medicine.defaultDosis == null ? null : hds.Weight.mg(i.medicine.defaultDosis!),
+          );
+          addedMeds[i.medicine] = med;
+          await medRepo.add(med);
+        }
+        final newI = hds.MedicineIntake(
+          time: i.timestamp,
+          medicine: med,
+          dosis: hds.Weight.mg(i.dosis),
+        );
+        await intakeRepo.add(newI);
+      }
+    }
+
+    File(join(await getDatabasesPath(), 'medicine.intakes')).deleteSync();
+  } on PathNotFoundException {
+    // pass
+  } catch (e, stack) {
+    await ErrorReporting.reportCriticalError('Error while migrating intakes to '
+      'new format', '$e\n$stack',);
+  }
+
+  // Migrating records and notes
+  try {
+    final oldBpModel = await BloodPressureModel.create();
+    for (final BloodPressureRecord r in await oldBpModel?.all ?? []) {
+      if (r.diastolic != null || r.systolic != null || r.pulse != null) {
+        await bpRepo.add(hds.BloodPressureRecord(
+          time: r.creationTime,
+          sys: r.systolic == null ? null : hds.Pressure.mmHg(r.systolic!),
+          dia: r.diastolic == null ? null : hds.Pressure.mmHg(r.diastolic!),
+          pul: r.pulse,
+        ));
+      }
+      if (r.notes.isNotEmpty || r.needlePin != null) {
+        await noteRepo.add(hds.Note(
+          time: r.creationTime,
+          note: r.notes.isEmpty ? null : r.notes,
+          color: r.needlePin?.color.value,
+        ));
+      }
+    }
+    await oldBpModel?.close();
+    File(join(await getDatabasesPath(), 'blood_pressure.db')).deleteSync();
+  } on PathNotFoundException {
+    // pass
+  } catch (e, stack)  {
+    await ErrorReporting.reportCriticalError('Error while migrating records to '
+      'new format', '$e\n$stack',);
+  }
+}
app/lib/model/blood_pressure/warn_values.dart
@@ -8,7 +8,7 @@ class BloodPressureWarnValues {
   static String source = 'https://pressbooks.library.torontomu.ca/vitalsign/chapter/blood-pressure-ranges/';
 
   /// Returns the default highest (safe) diastolic value for a specific age.
-  static int getUpperDiaWarnValue(int age) {
+  static int getUpperDiaWarnValue(int age) { // TODO: units
     if (age <= 2) {
       return 70;
     } else if (age <= 13) {
app/lib/model/storage/settings_store.dart
@@ -145,7 +145,6 @@ class Settings extends ChangeNotifier {
       'needlePinBarWidth': _needlePinBarWidth,
       'lastVersion': lastVersion,
       'bottomAppBars': bottomAppBars,
-      'medications': medications.map(jsonEncode).toList(),
       'highestMedIndex': highestMedIndex,
       'preferredPressureUnit': preferredPressureUnit.encode(),
     };
@@ -358,28 +357,9 @@ class Settings extends ChangeNotifier {
   ///
   /// This includes medications that got hidden. To obtain medications for a
   /// selection, do `settings.medications.where((e) => !e.hidden)`.
-  UnmodifiableListView<Medicine> get medications => 
-      UnmodifiableListView(_medications);
-
-  /// Adds a medication to the end of the medication list.
-  void addMedication(Medicine medication) {
-    _medications.add(medication);
-    _highestMedIndex += 1;
-    notifyListeners();
-  }
-
-  /// Marks a medication as deleted so it stops appearing in selections.
-  ///
-  /// Deleting the medication is not possible because this would invalidate all
-  /// entries that use it. In case the user forces medicine deletion in some way
-  /// intakes will be displayed with a deleted medicine text.
-  void removeMedicationAt(int index) {
-    assert(index >= 0 && index < _medications.length);
-    assert(!_medications[index].hidden, 'Removing a already hidden medication '
-        'indicates a bug.');
-    _medications[index].hidden = true;
-    notifyListeners();
-  }
+  @Deprecated('use health_data_store')
+  UnmodifiableListView<Medicine> get medications =>
+    UnmodifiableListView(_medications);
 
   int _highestMedIndex = 0;
   /// Total amount of medicines created.
app/lib/screens/error_reporting_screen.dart
@@ -14,7 +14,8 @@ class ErrorReporting {
   /// Whether there is already an critical error displayed.
   static bool isErrorState = false;
 
-  /// Replaces the application with an ErrorScreen
+  /// Replaces the application with an ErrorScreen.
+  ///
   /// This method can be used to avoid running any further code in your current function, by awaiting
   static Future<void> reportCriticalError(String title, String text) async {
     if (isErrorState) throw Exception('Tried to report another error:\n title = $title,\n text = $text');
@@ -83,51 +84,81 @@ class ErrorScreen extends StatelessWidget {
                     child: const Text('copy error message'),
                   ),
                   TextButton(
-                      onPressed: () async {
-                        try {
-                          final url = Uri.parse('https://github.com/NobodyForNothing/blood-pressure-monitor-fl/issues');
-                          if (await canLaunchUrl(url)) {
-                            await launchUrl(url, mode: LaunchMode.externalApplication);
-                          } else {
-                            scaffoldMessenger.showSnackBar(const SnackBar(
-                              content: Text('ERR: Please open this website: https://github.com/NobodyForNothing/blood-pressure-monitor-fl/issues'),),);
-                          }
-                        } catch (e) {
-                          scaffoldMessenger.showSnackBar(SnackBar(
-                              content: Text('ERR: $e'),),);
+                    onPressed: () async {
+                      try {
+                        final url = Uri.parse('https://github.com/NobodyForNothing/blood-pressure-monitor-fl/issues');
+                        if (await canLaunchUrl(url)) {
+                          await launchUrl(url, mode: LaunchMode.externalApplication);
+                        } else {
+                          scaffoldMessenger.showSnackBar(const SnackBar(
+                            content: Text('ERR: Please open this website: https://github.com/NobodyForNothing/blood-pressure-monitor-fl/issues'),),);
                         }
-                      },
-                      child: const Text('open issue reporting website'),
+                      } catch (e) {
+                        scaffoldMessenger.showSnackBar(SnackBar(
+                            content: Text('ERR: $e'),),);
+                      }
+                    },
+                    child: const Text('open issue reporting website'),
                   ),
                   TextButton(
-                      onPressed: () async {
-                        try {
-                          String dbPath = await getDatabasesPath();
+                    onPressed: () async {
+                      try {
+                        String dbPath = await getDatabasesPath();
 
-                          assert(dbPath != inMemoryDatabasePath);
-                          dbPath = join(dbPath, 'blood_pressure.db');
-                          PlatformClient.shareFile(dbPath, 'application/vnd.sqlite3');
-                        } catch(e) {
-                          scaffoldMessenger.showSnackBar(SnackBar(
-                              content: Text('ERR: $e'),),);
-                        }
-                      },
-                      child: const Text('rescue measurements'),
+                        assert(dbPath != inMemoryDatabasePath);
+                        dbPath = join(dbPath, 'blood_pressure.db');
+                        await PlatformClient.shareFile(dbPath, 'application/vnd.sqlite3');
+                      } catch(e) {
+                        scaffoldMessenger.showSnackBar(SnackBar(
+                            content: Text('ERR: $e'),),);
+                      }
+                    },
+                    child: const Text('rescue old measurements'),
                   ),
                   TextButton(
-                      onPressed: () async {
-                        try {
-                          String dbPath = await getDatabasesPath();
+                    onPressed: () async {
+                      try {
+                        String dbPath = await getDatabasesPath();
 
-                          assert(dbPath != inMemoryDatabasePath);
-                          dbPath = join(dbPath, 'config.db');
-                          PlatformClient.shareFile(dbPath, 'application/vnd.sqlite3');
-                        } catch(e) {
-                          scaffoldMessenger.showSnackBar(SnackBar(
-                              content: Text('ERR: $e'),),);
-                        }
-                      },
-                      child: const Text('rescue config.db'),
+                        assert(dbPath != inMemoryDatabasePath);
+                        dbPath = join(dbPath, 'config.db');
+                        await PlatformClient.shareFile(dbPath, 'application/vnd.sqlite3');
+                      } catch(e) {
+                        scaffoldMessenger.showSnackBar(SnackBar(
+                            content: Text('ERR: $e'),),);
+                      }
+                    },
+                    child: const Text('rescue config.db'),
+                  ),
+                  TextButton(
+                    onPressed: () async {
+                      try {
+                        String dbPath = await getDatabasesPath();
+
+                        assert(dbPath != inMemoryDatabasePath);
+                        dbPath = join(dbPath, 'medicine.intakes');
+                        await PlatformClient.shareFile(dbPath, 'application/octet-stream');
+                      } catch(e) {
+                        scaffoldMessenger.showSnackBar(SnackBar(
+                          content: Text('ERR: $e'),),);
+                      }
+                    },
+                    child: const Text('rescue old medicine intakes'),
+                  ),
+                  TextButton(
+                    onPressed: () async {
+                      try {
+                        String dbPath = await getDatabasesPath();
+
+                        assert(dbPath != inMemoryDatabasePath);
+                        dbPath = join(dbPath, 'bp.db');
+                        await PlatformClient.shareFile(dbPath, 'application/vnd.sqlite3');
+                      } catch(e) {
+                        scaffoldMessenger.showSnackBar(SnackBar(
+                          content: Text('ERR: $e'),),);
+                      }
+                    },
+                    child: const Text('rescue new db'),
                   ),
                 ],
               ),
app/lib/main.dart
@@ -1,7 +1,4 @@
-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/export_import/export_configuration.dart';
 import 'package:blood_pressure_app/model/storage/db/config_dao.dart';
 import 'package:blood_pressure_app/model/storage/db/config_db.dart';
@@ -20,6 +17,8 @@ import 'package:path/path.dart';
 import 'package:provider/provider.dart';
 import 'package:sqflite/sqflite.dart';
 
+import 'model/blood_pressure/update_legacy_entries.dart';
+
 late final ConfigDB _database;
 
 void main() async {
@@ -44,27 +43,22 @@ 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, stack) {
-    assert(e is PathNotFoundException, '$e\n$stack');
-    intakeHistory = IntakeHistory([]);
-  }
-  intakeHistory.addListener(() async {
-    File(join(await getDatabasesPath(), 'medicine.intakes')).writeAsStringSync(intakeHistory.serialize());
-  });
-
   final db = await HealthDataStore.load(await openDatabase(
     join(await getDatabasesPath(), 'bp.db'),
   ),);
-  // TODO: document
+  final bpRepo = db.bpRepo;
+  final noteRepo = db.noteRepo;
+  final medRepo = db.medRepo;
+  final intakeRepo = db.intakeRepo;
+
+  await updateLegacyEntries(
+    settings,
+    bpRepo,
+    noteRepo,
+    medRepo,
+    intakeRepo,
+  );
+  // TODO: document how data is stored in the app
 
   // update logic
   if (settings.lastVersion == 0) {
@@ -91,9 +85,6 @@ Future<Widget> _loadApp() async {
   // Reset the step size intervall to current on startup
   intervalStorageManager.mainPage.setToMostRecentIntervall();
 
-  // TODO: migrate old data and remove add methods
-  // TODO: fix navigation test failures
-
   return MultiProvider(providers: [
     ChangeNotifierProvider(create: (context) => settings),
     ChangeNotifierProvider(create: (context) => exportSettings),
@@ -101,14 +92,13 @@ Future<Widget> _loadApp() async {
     ChangeNotifierProvider(create: (context) => pdfExportSettings),
     ChangeNotifierProvider(create: (context) => intervalStorageManager),
     ChangeNotifierProvider(create: (context) => exportColumnsManager),
-    ChangeNotifierProvider(create: (context) => intakeHistory),
   ],
   child: MultiRepositoryProvider(
     providers: [
-      RepositoryProvider(create: (context) => db.bpRepo),
-      RepositoryProvider(create: (context) => db.noteRepo),
-      RepositoryProvider(create: (context) => db.medRepo),
-      RepositoryProvider(create: (context) => db.intakeRepo),
+      RepositoryProvider(create: (context) => bpRepo),
+      RepositoryProvider(create: (context) => noteRepo),
+      RepositoryProvider(create: (context) => medRepo),
+      RepositoryProvider(create: (context) => intakeRepo),
     ],
     child: const AppRoot(),
   ),);
app/test/model/medicine/intake_history_test.dart
@@ -1,170 +0,0 @@
-import 'package:blood_pressure_app/model/blood_pressure/medicine/intake_history.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import 'medicine_intake_test.dart';
-import 'medicine_test.dart';
-
-void main() {
-  test('should return all matching intakes in range', () {
-    final history = IntakeHistory([
-      mockIntake(timeMs: 2),
-      mockIntake(timeMs: 2),
-      mockIntake(timeMs: 4),
-      mockIntake(timeMs: 5),
-      mockIntake(timeMs: 6),
-      mockIntake(timeMs: 9),
-      mockIntake(timeMs: 9),
-      mockIntake(timeMs: 12),
-      mockIntake(timeMs: 15),
-      mockIntake(timeMs: 15),
-      mockIntake(timeMs: 16),
-      mockIntake(timeMs: 17),
-    ]);
-    final found = history.getIntakes(DateTimeRange(
-      start: DateTime.fromMillisecondsSinceEpoch(4),
-      end: DateTime.fromMillisecondsSinceEpoch(15),
-    ),);
-    expect(found.length, 8);
-    expect(found.map((e) => e.timestamp.millisecondsSinceEpoch), containsAllInOrder([4,5,6,9,9,12,15,15]));
-  });
-  test('should return all matching intakes when only few are in range', () {
-    final history = IntakeHistory([
-      mockIntake(timeMs: 2),
-      mockIntake(timeMs: 3),
-    ]);
-    final found = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(4),
-    ),);
-    expect(found.length, 2);
-    expect(found.map((e) => e.timestamp.millisecondsSinceEpoch), containsAllInOrder([2,3]));
-  });
-  test('should return nothing when no intakes are present', () {
-    final history = IntakeHistory([]);
-    final found = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(1000),
-    ),);
-    expect(found.length, 0);
-  });
-  test('should return nothing when intakes are out of range', () {
-    final history = IntakeHistory([
-      mockIntake(timeMs: 2),
-      mockIntake(timeMs: 3),
-    ]);
-    final found1 = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(4),
-        end: DateTime.fromMillisecondsSinceEpoch(10),
-    ),);
-    expect(found1.length, 0);
-    final found2 = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(1),
-    ),);
-    expect(found2.length, 0);
-  });
-  test('should add to the correct position', () {
-    final history = IntakeHistory([
-      mockIntake(timeMs: 2),
-      mockIntake(timeMs: 7),
-    ]);
-
-    history.addIntake(mockIntake(timeMs: 3));
-    final found = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(10),
-    ),);
-    expect(found.length, 3);
-    expect(found.map((e) => e.timestamp.millisecondsSinceEpoch), containsAllInOrder([2,3,7]));
-
-    history.addIntake(mockIntake(timeMs: 3));
-    final found2 = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(10),
-    ),);
-    expect(found2.length, 4);
-    expect(found2.map((e) => e.timestamp.millisecondsSinceEpoch), containsAllInOrder([2,3,3,7]));
-
-    history.addIntake(mockIntake(timeMs: 1));
-    final found3 = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(10),
-    ),);
-    expect(found3.length, 5);
-    expect(found3.map((e) => e.timestamp.millisecondsSinceEpoch), containsAllInOrder([1,2,3,3,7]));
-
-    history.addIntake(mockIntake(timeMs: 10));
-    final found4 = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(10),
-    ),);
-    expect(found4.length, 6);
-    expect(found4.map((e) => e.timestamp.millisecondsSinceEpoch), containsAllInOrder([1,2,3,3,7,10]));
-  });
-  test('should remove deleted intakes', () {
-    final history = IntakeHistory([
-      mockIntake(timeMs: 2),
-      mockIntake(timeMs: 2),
-      mockIntake(timeMs: 4),
-      mockIntake(timeMs: 5),
-      mockIntake(timeMs: 6),
-      mockIntake(timeMs: 9),
-      mockIntake(timeMs: 9, dosis: 2),
-      mockIntake(timeMs: 12),
-    ]);
-    history.deleteIntake(mockIntake(timeMs: 5));
-    final found = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(20),
-    ),);
-    expect(found.length, 7);
-    expect(found.map((e) => e.timestamp.millisecondsSinceEpoch),
-        containsAllInOrder([2,2,4,6,9,9,12]),);
-
-    history.deleteIntake(mockIntake(timeMs: 9));
-    final found3 = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(20),
-    ),);
-    expect(found3.length, 6);
-    expect(found3.map((e) => e.timestamp.millisecondsSinceEpoch),
-        containsAllInOrder([2,2,4,6,9,12]),);
-
-    history.deleteIntake(mockIntake(timeMs: 2));
-    final found4 = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(20),
-    ),);
-    expect(found4.length, 4);
-    expect(found4.map((e) => e.timestamp.millisecondsSinceEpoch),
-        containsAllInOrder([4,6,9,12]),);
-  });
-  test('should not fail on deleting non existent intake', () {
-    final history = IntakeHistory([]);
-    history.deleteIntake(mockIntake(timeMs: 5));
-    final found = history.getIntakes(DateTimeRange(
-        start: DateTime.fromMillisecondsSinceEpoch(0),
-        end: DateTime.fromMillisecondsSinceEpoch(20),
-    ),);
-    expect(found.length, 0);
-  });
-  test('should serialize and deserialize', () {
-    final meds = [mockMedicine(defaultDosis: 1), mockMedicine(defaultDosis: 2),mockMedicine(defaultDosis: 3), mockMedicine(defaultDosis: 4)];
-    final history = IntakeHistory([
-      mockIntake(dosis: 123, timeMs: 1235, medicine: meds[0]),
-      mockIntake(dosis: 123, timeMs: 1235, medicine: meds[0]),
-      mockIntake(dosis: 123, timeMs: 1235, medicine: meds[1]),
-      mockIntake(dosis: 123, timeMs: 1235, medicine: meds[2]),
-      mockIntake(dosis: 123, timeMs: 132, medicine: meds[3]),
-      mockIntake(dosis: 1232, timeMs: 132, medicine: meds[3]),
-      mockIntake(dosis: 1232, timeMs: 132, medicine: meds[3]),
-      mockIntake(dosis: 1232, timeMs: 132, medicine: meds[3]),
-      mockIntake(dosis: 123, timeMs: 1235, medicine: meds[3]),
-    ]);
-    final deserializedHistory = IntakeHistory.deserialize(history.serialize(), meds);
-
-    expect(deserializedHistory.serialize(), history.serialize());
-    expect(deserializedHistory, history);
-  });
-}
app/test/model/medicine/medicine_intake_test.dart
@@ -3,8 +3,6 @@ import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine_intake
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-import 'medicine_test.dart';
-
 void main() {
   test('should determine equality', () {
     final med = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 123);
@@ -23,39 +21,4 @@ void main() {
     final int4 = OldMedicineIntake(medicine: med1, dosis: 10, timestamp: DateTime.fromMillisecondsSinceEpoch(124));
     expect(int1, isNot(int4));
   });
-  test('should deserialize serialized intake', () {
-    final intake = mockIntake(timeMs: 543210);
-    expect(OldMedicineIntake.deserialize(intake.serialize(), [intake.medicine]), intake);
-
-    final intake2 = mockIntake(
-        timeMs: 543211,
-        dosis: 1000231,
-        medicine: mockMedicine(designation: 'tst'),
-    );
-    expect(OldMedicineIntake.deserialize(
-        intake2.serialize(),
-        [intake.medicine, intake2.medicine],),
-        intake2,);
-  });
-  test('should fail to deserialize serialized intake without exising med', () {
-    final intake = mockIntake(medicine: mockMedicine(designation: 'tst'));
-    expect(() => OldMedicineIntake.deserialize(
-        intake.serialize(),
-        [mockMedicine()],),
-        throwsArgumentError,);
-  });
 }
-
-/// Create a mock intake.
-///
-/// [timeMs] creates the intake timestamp through [DateTime.fromMillisecondsSinceEpoch].
-/// When is null [DateTime.now] is used.
-OldMedicineIntake mockIntake({
-  double dosis = 0,
-  int? timeMs,
-  Medicine? medicine,
-}) => OldMedicineIntake(
-    medicine: medicine ?? mockMedicine(),
-    dosis: dosis,
-    timestamp: timeMs == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(timeMs),
-);
app/test/model/medicine/medicine_test.dart
@@ -17,49 +17,4 @@ void main() {
     final med4 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 11);
     expect(med1, isNot(med4));
   });
-  test('should restore after encoded to map', () {
-    final med1 = mockMedicine();
-    final med1Restored = Medicine.fromMap(med1.toMap());
-    expect(med1Restored, med1);
-
-    final med2 = mockMedicine(color: Colors.red, designation: 'designation', defaultDosis: 15);
-    final med2Restored = Medicine.fromMap(med2.toMap());
-    expect(med2Restored, med2);
-  });
-  test('should restore after encoded to json', () {
-    final med1 = mockMedicine();
-    final med1Restored = Medicine.fromJson(med1.toJson());
-    expect(med1Restored, med1);
-
-    final med2 = mockMedicine(color: Colors.red, designation: 'designation', defaultDosis: 15);
-    final med2Restored = Medicine.fromJson(med2.toJson());
-    expect(med2Restored, med2);
-  });
-  test('should generate the same json after restoration', () {
-    final med1 = mockMedicine();
-    final med1Restored = Medicine.fromJson(med1.toJson());
-    expect(med1Restored.toJson(), med1.toJson());
-
-    final med2 = mockMedicine(color: Colors.red, designation: 'designation', defaultDosis: 15);
-    final med2Restored = Medicine.fromJson(med2.toJson());
-    expect(med2Restored.toJson(), med2.toJson());
-  }); // not in a json serialization test as this is not a setting like file.
-}
-
-
-final List<Medicine> _meds = [];
-
-/// Creates mock medicine.
-///
-/// Medicines with the same properties will keep the correct id.
-Medicine mockMedicine({
-  Color color = Colors.black,
-  String designation = '',
-  double? defaultDosis,
-}) {
-  final matchingMeds = _meds.where((med) => med.defaultDosis == defaultDosis && med.color == color && med.designation == designation);
-  if (matchingMeds.isNotEmpty) return matchingMeds.first;
-  final med = Medicine(_meds.length, designation: designation, color: color, defaultDosis: defaultDosis);
-  _meds.add(med);
-  return med;
 }
app/test/model/json_serialization_test.dart
@@ -12,8 +12,6 @@ import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:health_data_store/health_data_store.dart';
 
-import 'medicine/medicine_test.dart';
-
 void main() {
   group('IntervallStorage', () {
     test('should create json without error', () {
@@ -96,7 +94,6 @@ void main() {
         useLegacyList: false,
         horizontalGraphLines: [HorizontalGraphLine(Colors.blue, 1230)],
         bottomAppBars: true,
-        medications: [mockMedicine(), mockMedicine(defaultDosis: 42)],
       );
       final fromJson = Settings.fromJson(initial.toJson());