Commit af0df43
Changed files (13)
app
lib
model
blood_pressure
storage
screens
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());