Commit 942b096

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-03-28 11:35:18
implement note repository
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 586d733
Changed files (3)
health_data_store
lib
src
test
health_data_store/lib/src/repositories/note_repository.dart
@@ -0,0 +1,78 @@
+
+import 'package:health_data_store/src/database_helper.dart';
+import 'package:health_data_store/src/database_manager.dart';
+import 'package:health_data_store/src/extensions/datetime_seconds.dart';
+import 'package:health_data_store/src/repositories/repository.dart';
+import 'package:health_data_store/src/types/date_range.dart';
+import 'package:health_data_store/src/types/note.dart';
+import 'package:sqflite_common/sqflite.dart';
+
+/// Repository for accessing [Note]s stored in a [DatabaseManager] managed db.
+class NoteRepository extends Repository<Note> {
+  /// Create a repository for notes.
+  NoteRepository(this._db);
+
+  /// The [DatabaseManager] managed database.
+  final Database _db;
+
+  @override
+  Future<void> add(Note note) async {
+    if (note.note == null && note.color == null) {
+      assert(false);
+      return;
+    }
+    await _db.transaction((txn) async {
+      final id = await DBHelper.getEntryID(
+        txn, note.time.secondsSinceEpoch,
+        ['Notes'],
+      );
+      await txn.insert('Notes', {
+        'entryID': id,
+        if (note.note != null)
+          'note': note.note,
+        if (note.color != null)
+          'color': note.color,
+      });
+    });
+  }
+
+  @override
+  Future<List<Note>> get(DateRange range) async {
+    final result = await _db.rawQuery(
+      'SELECT t.timestampUnixS AS time, note, color '
+        'FROM Timestamps AS t '
+        'JOIN Notes AS n ON t.entryID = n.entryID '
+      'WHERE t.timestampUnixS BETWEEN ? AND ?'
+      'AND (n.note IS NOT NULL OR n.color IS NOT NULL)', [
+      range.startStamp,
+      range.endStamp,
+    ]
+    );
+    final notes = <Note>[];
+    for (final row in result) {
+      notes.add(Note(
+        time: DateTimeS.fromSecondsSinceEpoch(row['time'] as int),
+        note: row['note'] as String?,
+        color: row['color'] as int?,
+      ));
+    }
+    return notes;
+  }
+
+  @override
+  Future<void> remove(Note value) => _db.rawDelete(
+    'DELETE FROM Notes WHERE entryID IN ('
+      'SELECT entryID FROM Timestamps '
+      'WHERE timestampUnixS = ?'
+    ') AND note '
+    + ((value.note == null) ? 'IS NULL' : '= ?')
+    + ' AND color '
+    + ((value.color == null) ? 'IS NULL' : '= ?'), [
+   value.time.secondsSinceEpoch,
+   if (value.note != null)
+     value.note,
+   if (value.color != null)
+     value.color,
+  ]);
+
+}
health_data_store/test/src/repositories/note_repository_test.dart
@@ -0,0 +1,78 @@
+import 'package:health_data_store/health_data_store.dart';
+import 'package:health_data_store/src/repositories/note_repository.dart';
+import 'package:test/test.dart';
+
+import '../database_manager_test.dart';
+import '../types/blood_pressure_record_test.dart';
+import '../types/note_test.dart';
+
+void main() {
+  sqfliteTestInit();
+  test('should initialize', () async {
+    final db = await mockDBManager();
+    addTearDown(db.close);
+    NoteRepository(db.db);
+  });
+  test('should store notes', () async {
+    final db = await mockDBManager();
+    addTearDown(db.close);
+    final repo = NoteRepository(db.db);
+    await repo.add(mockNote(color: 0x00FF00));
+    await repo.add(mockNote(note: 'test'));
+    expect(() async => repo.add(mockNote()), throwsA(isA<AssertionError>()));
+  });
+  test('should return stored notes', () async {
+    final db = await mockDBManager();
+    addTearDown(db.close);
+    final repo = NoteRepository(db.db);
+    final note1 = mockNote(time: 100000, color: 0x00FF00);
+    await repo.add(note1);
+    final note2 = mockNote(time: 700000, note: 'test');
+    await repo.add(note2);
+    final note3 = mockNote(time: 530000, note: 'test2');
+    await repo.add(note3);
+
+    final values = await repo.get(DateRange(
+      start: DateTime.fromMillisecondsSinceEpoch(100000),
+      end: DateTime.fromMillisecondsSinceEpoch(800000),
+    ));
+
+    expect(values, hasLength(3));
+    expect(values, containsAll([note1, note2, note3]));
+  });
+  test('should remove notes', () async {
+    final db = await mockDBManager();
+    addTearDown(db.close);
+    final repo = NoteRepository(db.db);
+    final note1 = mockNote(time: 100000, color: 0x00FF00);
+    await repo.add(note1);
+    final note2 = mockNote(time: 700000, note: 'test');
+    await repo.add(note2);
+    final note3 = mockNote(time: 530000, note: 'test2');
+    await repo.add(note3);
+
+    await repo.remove(note1);
+    await repo.remove(note3);
+    await repo.remove(mockNote());
+
+    final values = await repo.get(DateRange(
+      start: DateTime.fromMillisecondsSinceEpoch(100000),
+      end: DateTime.fromMillisecondsSinceEpoch(800000),
+    ));
+    expect(values, hasLength(1));
+    expect(values, contains(note2));
+  });
+  test('should not return notes when only other data is present', () async {
+    final db = await mockDBManager();
+    addTearDown(db.close);
+    final bpRepo = BloodPressureRepository(db.db);
+    final r1 = mockRecord(time: 200000, sys: 456, dia: 457, pul: 458);
+    await bpRepo.add(r1);
+    final repo = NoteRepository(db.db);
+    final values = await repo.get(DateRange(
+      start: DateTime.fromMillisecondsSinceEpoch(100000),
+      end: DateTime.fromMillisecondsSinceEpoch(800000),
+    ));
+    expect(values, isEmpty);
+  });
+}
health_data_store/test/src/types/note_test.dart
@@ -19,3 +19,13 @@ void main() {
     )));
   });
 }
+
+Note mockNote({
+  int? time,
+  String? note,
+  int? color,
+}) => Note(
+  time: time!=null ? DateTime.fromMillisecondsSinceEpoch(time) : DateTime.now(),
+  note: note,
+  color: color,
+);