Commit 38a51e6
Changed files (8)
health_data_store
lib
health_data_store/lib/src/repositories/blood_pressure_repository.dart
@@ -27,7 +27,6 @@ class BloodPressureRepository extends Repository<BloodPressureRecord> {
timeSec,
['Systolic', 'Diastolic', 'Pulse'],
);
- print(record);
if (record.sys != null) {
await txn.insert('Systolic', {
'entryID': entryID,
@@ -57,7 +56,7 @@ class BloodPressureRepository extends Repository<BloodPressureRecord> {
'LEFT JOIN Systolic AS s ON t.entryID = s.entryID '
'LEFT JOIN Diastolic AS d ON t.entryID = d.entryID '
'LEFT JOIN Pulse AS p ON t.entryID = p.entryID '
- 'WHERE timestampUnixS BETWEEN ? AND ?;',
+ 'WHERE timestampUnixS BETWEEN ? AND ?',
[range.startStamp, range.endStamp]
);
final records = <BloodPressureRecord>[];
health_data_store/lib/src/repositories/medicine_intake_repository.dart
@@ -0,0 +1,105 @@
+import 'package:health_data_store/health_data_store.dart';
+import 'package:health_data_store/src/database_helper.dart';
+import 'package:health_data_store/src/database_manager.dart';
+import 'package:health_data_store/src/repositories/repository.dart';
+import 'package:sqflite_common/sqflite.dart';
+
+/// Repository for [MedicineIntake]s.
+///
+/// Provides high level access on intakes saved in a [DatabaseManager] managed
+/// database.
+class MedicineIntakeRepository extends Repository<MedicineIntake> {
+ /// Create a repository for medicine intakes.
+ MedicineIntakeRepository(this._db);
+
+ /// The [DatabaseManager] managed database
+ final Database _db;
+
+ @override
+ Future<void> add(MedicineIntake intake) => _db.transaction((txn) async {
+ // obtain medicine id
+ final medIDRes = await txn.query('Medicine',
+ columns: ['medID'],
+ where: 'designation = ? '
+ 'AND color ' +((intake.medicine.color != null) ? '= ?' : 'IS NULL')
+ + ' AND defaultDose ' +((intake.medicine.dosis != null) ? '= ?' : 'IS '
+ 'NULL'),
+ whereArgs: [
+ intake.medicine.designation,
+ if (intake.medicine.color != null)
+ intake.medicine.color,
+ if (intake.medicine.dosis != null)
+ intake.medicine.dosis,
+ ],
+ );
+ assert(medIDRes.isNotEmpty);
+ // Assuming intakes only contain medications that have been added
+ final medID = medIDRes.first['medID'];
+
+ // obtain free entry id
+ final id = await DBHelper.getEntryID(
+ txn, intake.time.millisecondsSinceEpoch ~/ 1000,
+ ['Intake'],
+ );
+
+ // store to db
+ await txn.insert('Intake', {
+ 'entryID': id,
+ 'medID': medID,
+ 'dosis': intake.dosis,
+ });
+ });
+
+ @override
+ Future<List<MedicineIntake>> get(DateRange range) async {
+ final results = await _db.rawQuery(
+ 'SELECT t.timestampUnixS, dosis, defaultDose, designation, color '
+ 'FROM Timestamps AS t '
+ 'LEFT JOIN Intake AS i ON t.entryID = i.entryID '
+ 'LEFT JOIN Medicine AS m ON m.medID = i.medID '
+ 'WHERE t.timestampUnixS BETWEEN ? AND ?'
+ 'AND i.dosis IS NOT NULL', // deleted intakes
+ [range.startStamp, range.endStamp]
+ );
+ final intakes = <MedicineIntake>[];
+ for (final r in results) {
+ final timeS = r['timestampUnixS'] as int;
+ intakes.add(MedicineIntake(
+ time: DateTime.fromMillisecondsSinceEpoch(timeS * 1000),
+ dosis: r['dosis'] as double,
+ medicine: Medicine(
+ designation: r['designation'] as String,
+ dosis: r['defaultDose'] as double?,
+ color: r['color'] as int?,
+ ),
+ ));
+ }
+ return intakes;
+ }
+
+ @override
+ Future<void> remove(MedicineIntake intake) => _db.rawDelete(
+ 'DELETE FROM Intake WHERE entryID IN ('
+ 'SELECT entryID FROM Timestamps '
+ 'WHERE timestampUnixS = ?'
+ ') AND dosis = ? '
+ 'AND medID IN ('
+ 'SELECT medID FROM Medicine '
+ 'WHERE designation = ?'
+ 'AND color '
+ + ((intake.medicine.color != null) ? '= ?' : 'IS NULL') +
+ ' AND defaultDose '
+ + ((intake.medicine.dosis != null) ? '= ?' : 'IS NULL') +
+ ')',
+ [
+ intake.time.millisecondsSinceEpoch ~/ 1000,
+ intake.dosis,
+ intake.medicine.designation,
+ if (intake.medicine.color != null)
+ intake.medicine.color,
+ if (intake.medicine.dosis != null)
+ intake.medicine.dosis,
+ ]
+ );
+
+}
health_data_store/lib/src/types/medicine_intake.dart
@@ -16,6 +16,6 @@ class MedicineIntake with _$MedicineIntake {
///
/// When the medication has a default value, this must be set to that value,
/// as it may change.
- required int dosis,
+ required double dosis,
}) = _MedicineIntake;
}
health_data_store/lib/src/database_manager.dart
@@ -47,6 +47,7 @@ class DatabaseManager {
Database get db => _db.database;
Future<void> _setUpTables() async {
+ // TODO: IF NOT EXISTS ?
await _db.execute('CREATE TABLE "Medicine" ('
'"medID" INTEGER NOT NULL UNIQUE,'
'"designation" TEXT NOT NULL,'
@@ -58,12 +59,12 @@ class DatabaseManager {
await _db.execute('CREATE TABLE "Timestamps" ('
'"entryID" INTEGER NOT NULL UNIQUE,'
'"timestampUnixS" INTEGER NOT NULL,'
- 'PRIMARY KEY("entryID")'
+ 'PRIMARY KEY("entryID")' // TODO: add timezone to determine morning, evening
');');
await _db.execute('CREATE TABLE "Intake" ('
'"entryID" INTEGER NOT NULL,'
'"medID" INTEGER NOT NULL,'
- '"dosis" INTEGER NOT NULL,'
+ '"dosis" REAL NOT NULL,'
'PRIMARY KEY("entryID"),'
'FOREIGN KEY("entryID") REFERENCES "Timestamps"("entryID"),'
'FOREIGN KEY("medID") REFERENCES "Medicine"("medID")'
health_data_store/test/src/repositories/blood_pressure_repository_test.dart
@@ -1,6 +1,5 @@
import 'package:health_data_store/health_data_store.dart';
-import 'package:health_data_store/src/repositories/blood_pressure_repository.dart';
import 'package:test/test.dart';
import '../database_manager_test.dart';
@@ -84,7 +83,6 @@ void main() {
start: DateTime.fromMillisecondsSinceEpoch(0),
end: DateTime.fromMillisecondsSinceEpoch(80000),
));
- print(values1);
expect(values1, hasLength(2));
expect(values1, containsAll([r2,r3]));
@@ -93,7 +91,6 @@ void main() {
start: DateTime.fromMillisecondsSinceEpoch(0),
end: DateTime.fromMillisecondsSinceEpoch(80000),
));
- print(values2);
expect(values2, hasLength(1));
expect(values2, containsAll([r3]));
@@ -134,4 +131,6 @@ void main() {
));
expect(values, isEmpty);
});
+
+ // TODO: not return records out of range
}
health_data_store/test/src/repositories/medicine_intake_repository_test.dart
@@ -0,0 +1,119 @@
+
+import 'package:health_data_store/health_data_store.dart';
+import 'package:health_data_store/src/repositories/medicine_intake_repository.dart';
+import 'package:test/test.dart';
+
+import '../database_manager_test.dart';
+import '../types/medicine_intake_test.dart';
+import '../types/medicine_test.dart';
+
+void main() {
+ sqfliteTestInit();
+ test('should initialize', () async {
+ final db = await mockDBManager();
+ addTearDown(db.close);
+ MedicineIntakeRepository(db.db);
+ });
+ test('should store intakes without errors', () async {
+ final db = await mockDBManager();
+ addTearDown(db.close);
+ final med1 = mockMedicine(designation: 'med1', dosis: 2.4);
+ final med2 = mockMedicine(designation: 'med2',);
+ final medRepo = MedicineRepository(db.db);
+ await medRepo.add(med1);
+ await medRepo.add(med2);
+
+ final repo = MedicineIntakeRepository(db.db);
+ await repo.add(mockIntake(med1));
+ await repo.add(mockIntake(med2));
+ await repo.add(mockIntake(med1, dosis: 123));
+ // This medicine is not added to med repo
+ expect(() async => repo.add(mockIntake(mockMedicine())),
+ throwsA(isA<AssertionError>()));
+ });
+ test('should return stored intakes', () async {
+ final db = await mockDBManager();
+ addTearDown(db.close);
+ final med1 = mockMedicine(dosis: 2.4);
+ final med2 = mockMedicine();
+ final medRepo = MedicineRepository(db.db);
+ await medRepo.add(med1);
+ await medRepo.add(med2);
+
+ final repo = MedicineIntakeRepository(db.db);
+ final t1 = mockIntake(med1, time: 20000);
+ final t2 = mockIntake(med2, time: 76000);
+ final t3 = mockIntake(med1, dosis: 123, time: 50000,);
+ await repo.add(t1);
+ await repo.add(t2);
+ await repo.add(t3);
+ await repo.add(mockIntake(med1));
+
+ final values = await repo.get(DateRange(
+ start: DateTime.fromMillisecondsSinceEpoch(20000),
+ end: DateTime.fromMillisecondsSinceEpoch(80000),
+ ));
+ expect(values, hasLength(3));
+ expect(values, containsAll([t1, t2, t3,]));
+ });
+ test('should remove intakes', () async {
+ final db = await mockDBManager();
+ addTearDown(db.close);
+ final medRepo = MedicineRepository(db.db);
+ final med = mockMedicine();
+ await medRepo.add(med);
+ final repo = MedicineIntakeRepository(db.db);
+ final i1 = mockIntake(med, time: 5000);
+ await repo.add(i1);
+
+ final values1 = await repo.get(DateRange(
+ start: DateTime.fromMillisecondsSinceEpoch(0),
+ end: DateTime.fromMillisecondsSinceEpoch(10000),
+ ));
+ expect(values1, hasLength(1));
+ expect(values1, contains(i1));
+
+ await repo.remove(i1);
+ final values2 = await repo.get(DateRange(
+ start: DateTime.fromMillisecondsSinceEpoch(0),
+ end: DateTime.fromMillisecondsSinceEpoch(10000),
+ ));
+ expect(values2, isEmpty);
+ });
+ test('should remove correct intake when multiple are at same time', () async {
+ final db = await mockDBManager();
+ addTearDown(db.close);
+ final repo = MedicineIntakeRepository(db.db);
+ final medRepo = MedicineRepository(db.db);
+ final med = mockMedicine();
+ await medRepo.add(med);
+ final i1 = mockIntake(med, time: 10000);
+ final i2 = mockIntake(med, time: 10000, dosis: 458);
+ await repo.add(i1);
+ await repo.add(i2);
+
+ await repo.remove(i1);
+ final values2 = await repo.get(DateRange(
+ start: DateTime.fromMillisecondsSinceEpoch(0),
+ end: DateTime.fromMillisecondsSinceEpoch(80000),
+ ));
+ expect(values2, hasLength(1));
+ expect(values2, contains(i2));
+ });
+ test('should not throw when removing non existent record', () async {
+ final db = await mockDBManager();
+ addTearDown(db.close);
+ final medRepo = MedicineRepository(db.db);
+ final med = mockMedicine();
+ await medRepo.add(med);
+ final repo = MedicineIntakeRepository(db.db);
+ final i1 = mockIntake(med);
+
+ await repo.remove(i1);
+ final values = await repo.get(DateRange(
+ start: DateTime.fromMillisecondsSinceEpoch(0),
+ end: DateTime.fromMillisecondsSinceEpoch(80000),
+ ));
+ expect(values, isEmpty);
+ });
+}
health_data_store/test/src/types/medicine_intake_test.dart
@@ -13,3 +13,12 @@ void main() {
expect(intake.dosis, equals(42));
});
}
+
+MedicineIntake mockIntake(Medicine medicine, {
+ int? time,
+ double? dosis,
+}) => MedicineIntake(
+ time: time!=null ? DateTime.fromMillisecondsSinceEpoch(time) : DateTime.now(),
+ medicine: medicine,
+ dosis: dosis ?? medicine.dosis ?? 42,
+);
health_data_store/test/src/types/medicine_test.dart
@@ -1,3 +1,5 @@
+import 'dart:math';
+
import 'package:health_data_store/src/types/medicine.dart';
import 'package:test/test.dart';
@@ -8,3 +10,12 @@ void main() {
expect(med.dosis, equals(42));
});
}
+
+Medicine mockMedicine({
+ String? designation,
+ double? dosis,
+}) => Medicine(
+ designation: designation ??
+ 'med'+(Random().nextInt(899999) + 100000).toString(),
+ dosis: dosis,
+);