Commit 38db6e1

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2023-12-27 09:45:36
mad medicine serializable
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent a187ea6
Changed files (4)
lib
model
blood_pressure
test
lib/model/blood_pressure/medicine/intake_history.dart
@@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
 ///
 /// Internally maintains a sorted list of intakes to allow for binary search.
 class IntakeHistory {
-
+  /// Create a intake history from an unsorted list of intakes.
   IntakeHistory(List<MedicineIntake> medicineIntakes):
     _medicineIntakes = medicineIntakes.sorted((p0, p1) => p0.compareTo(p1));
 
lib/model/blood_pressure/medicine/medicine.dart
@@ -1,3 +1,4 @@
+import 'dart:convert';
 import 'dart:ui';
 
 import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine_intake.dart';
@@ -6,12 +7,33 @@ import 'package:flutter/material.dart';
 /// Description of a specific medicine.
 class Medicine {
   /// Create a new medicine.
-  const Medicine({
+  const Medicine(this.id, {
     required this.designation,
     required this.color,
     required this.defaultDosis
   });
 
+  Map<String, dynamic> toMap() => {
+    'id': id,
+    'designation': designation,
+    'color': color.value,
+    'defaultDosis': defaultDosis
+  };
+
+  String toJson() => jsonEncode(toMap());
+
+  factory Medicine.fromMap(Map<String, dynamic> map) => Medicine(
+      map['id'],
+      designation: map['designation'],
+      color: Color(map['color']),
+      defaultDosis: map['defaultDosis']
+  );
+
+  factory Medicine.fromJson(String json) => Medicine.fromMap(jsonDecode(json));
+
+  /// Unique id used to store the medicine in serialized objects.
+  final int id;
+
   /// Name of the medicine.
   final String designation;
 
@@ -21,15 +43,22 @@ class Medicine {
   /// Default dosis used to autofill [MedicineIntake].
   final double? defaultDosis;
 
+
   @override
   bool operator ==(Object other) =>
       identical(this, other) ||
       other is Medicine &&
           runtimeType == other.runtimeType &&
+          id == other.id &&
           designation == other.designation &&
-          color == other.color &&
+          color.value == other.color.value &&
           defaultDosis == other.defaultDosis;
 
   @override
-  int get hashCode => designation.hashCode ^ color.hashCode ^ defaultDosis.hashCode;
+  int get hashCode => id.hashCode ^ designation.hashCode ^ color.hashCode ^ defaultDosis.hashCode;
+
+  @override
+  String toString() {
+    return 'Medicine{id: $id, designation: $designation, color: $color, defaultDosis: $defaultDosis}';
+  }
 }
test/model/medicine/intake_history_test.dart
@@ -4,6 +4,8 @@ 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() {
   group('IntakeHistory', () { 
     test('should return all matching intakes in range', () {
@@ -157,17 +159,11 @@ void main() {
 /// [timeMs] creates the intake timestamp through [DateTime.fromMillisecondsSinceEpoch].
 /// When is null [DateTime.now] is used.
 MedicineIntake mockIntake({
-  Color medicineColor = Colors.black,
-  String medicineDesignation = '',
-  double? medicineDefaultDosis,
   double dosis = 0,
-  int? timeMs
+  int? timeMs,
+  Medicine? medicine
 }) => MedicineIntake(
-  medicine: Medicine(
-    color: medicineColor, 
-    designation: medicineDesignation,
-    defaultDosis: medicineDefaultDosis
-  ), 
+  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
@@ -0,0 +1,64 @@
+import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  group('Medicine', () {
+    test('should determine equality', () {
+      const med1 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 10);
+      const med2 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 10);
+      expect(med1, med2);
+    });
+    test('should determine inequality', () {
+      const med1 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 10);
+      const med2 = Medicine(1, designation: 'designatio', color: Colors.red, defaultDosis: 10);
+      expect(med1, isNot(med2));
+      const med3 = Medicine(1, designation: 'designation', color: Colors.blue, defaultDosis: 10);
+      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();
+      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 = [];
+
+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);
+  return med;
+}
\ No newline at end of file