Commit 5daf379

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2023-12-27 10:26:21
make medicine_intake serializable
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 38db6e1
Changed files (5)
lib/model/blood_pressure/medicine/medicine.dart
@@ -23,10 +23,10 @@ class Medicine {
   String toJson() => jsonEncode(toMap());
 
   factory Medicine.fromMap(Map<String, dynamic> map) => Medicine(
-      map['id'],
-      designation: map['designation'],
-      color: Color(map['color']),
-      defaultDosis: map['defaultDosis']
+    map['id'],
+    designation: map['designation'],
+    color: Color(map['color']),
+    defaultDosis: map['defaultDosis']
   );
 
   factory Medicine.fromJson(String json) => Medicine.fromMap(jsonDecode(json));
lib/model/blood_pressure/medicine/medicine_intake.dart
@@ -10,6 +10,22 @@ class MedicineIntake implements Comparable<Object> {
     required this.timestamp
   });
 
+  /// Serialize the object to a deserializable string.
+  ///
+  /// 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';
+
+  factory MedicineIntake.deserialize(String string, List<Medicine> availableMeds) {
+    final elements = string.split('\x00');
+    return MedicineIntake(
+      medicine: availableMeds.firstWhere((e) => e.id == int.parse(elements[0])),
+      timestamp: DateTime.fromMillisecondsSinceEpoch(int.parse(elements[1])),
+      dosis: double.parse(elements[2]),
+    );
+  }
+
   /// Kind of medicine taken.
   final Medicine medicine;
 
@@ -41,4 +57,9 @@ class MedicineIntake implements Comparable<Object> {
 
     return dosis.compareTo(other.dosis);
   }
+
+  @override
+  String toString() {
+    return 'MedicineIntake{medicine: $medicine, dosis: $dosis, timestamp: $timestamp}';
+  }
 }
\ No newline at end of file
test/model/medicine/intake_history_test.dart
@@ -1,10 +1,8 @@
 import 'package:blood_pressure_app/model/blood_pressure/medicine/intake_history.dart';
-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:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-import 'medicine_test.dart';
+import 'medicine_intake_test.dart';
 
 void main() {
   group('IntakeHistory', () { 
@@ -152,18 +150,4 @@ void main() {
       expect(found.length, 0);
     });
   });
-}
-
-/// Create a mock intake.
-/// 
-/// [timeMs] creates the intake timestamp through [DateTime.fromMillisecondsSinceEpoch].
-/// When is null [DateTime.now] is used.
-MedicineIntake mockIntake({
-  double dosis = 0,
-  int? timeMs,
-  Medicine? medicine
-}) => MedicineIntake(
-  medicine: medicine ?? mockMedicine(),
-  dosis: dosis, 
-  timestamp: timeMs == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(timeMs)
-);
\ No newline at end of file
+}
\ No newline at end of file
test/model/medicine/medicine_intake_test.dart
@@ -0,0 +1,63 @@
+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:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'medicine_test.dart';
+
+void main() {
+  group('MedicineIntake', () {
+    test('should determine equality', () {
+      const med = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 123);
+      final int1 = MedicineIntake(medicine: med, dosis: 10, timestamp: DateTime.fromMillisecondsSinceEpoch(123));
+      final int2 = MedicineIntake(medicine: med, dosis: 10, timestamp: DateTime.fromMillisecondsSinceEpoch(123));
+      expect(int1, int2);
+    });
+    test('should determine inequality', () {
+      const med1 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 123);
+      const med2 = Medicine(2, designation: 'designation', color: Colors.red, defaultDosis: 123);
+      final int1 = MedicineIntake(medicine: med1, dosis: 10, timestamp: DateTime.fromMillisecondsSinceEpoch(123));
+      final int2 = MedicineIntake(medicine: med2, dosis: 10, timestamp: DateTime.fromMillisecondsSinceEpoch(123));
+      expect(int1, isNot(int2));
+      final int3 = MedicineIntake(medicine: med1, dosis: 11, timestamp: DateTime.fromMillisecondsSinceEpoch(123));
+      expect(int1, isNot(int3));
+      final int4 = MedicineIntake(medicine: med1, dosis: 10, timestamp: DateTime.fromMillisecondsSinceEpoch(124));
+      expect(int1, isNot(int4));
+    });
+    test('should deserialize serialized intake', () {
+      final intake = mockIntake(timeMs: 543210);
+      expect(MedicineIntake.deserialize(intake.serialize(), [intake.medicine]), intake);
+
+      final intake2 = mockIntake(
+          timeMs: 543211,
+          dosis: 1000231,
+          medicine: mockMedicine(designation: 'tst')
+      );
+      expect(MedicineIntake.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(() => MedicineIntake.deserialize(
+          intake.serialize(),
+          [mockMedicine()]),
+          throwsStateError);
+    });
+  });
+}
+
+/// Create a mock intake.
+///
+/// [timeMs] creates the intake timestamp through [DateTime.fromMillisecondsSinceEpoch].
+/// When is null [DateTime.now] is used.
+MedicineIntake mockIntake({
+  double dosis = 0,
+  int? timeMs,
+  Medicine? medicine
+}) => MedicineIntake(
+    medicine: medicine ?? mockMedicine(),
+    dosis: dosis,
+    timestamp: timeMs == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(timeMs)
+);
\ No newline at end of file
test/model/medicine/medicine_test.dart
@@ -17,7 +17,6 @@ void main() {
       expect(med1, isNot(med3));
       const med4 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 11);
       expect(med1, isNot(med4));
-
     });
     test('should restore after encoded to map', () {
       final med1 = mockMedicine();
@@ -52,6 +51,9 @@ void main() {
 
 final List<Medicine> _meds = [];
 
+/// Creates mock intake.
+///
+/// Medicines with the same properties will keep the correct id.
 Medicine mockMedicine({
   Color color = Colors.black,
   String designation = '',
@@ -60,5 +62,6 @@ Medicine mockMedicine({
   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;
 }
\ No newline at end of file