Commit 37affaa
Changed files (15)
.github
workflows
health_data_store
lib
test
.github/workflows/app-CI.yml
@@ -22,6 +22,17 @@ jobs:
sparse-checkout: |
app
health_data_store
+ - name: Setup dart
+ uses: dart-lang/setup-dart@v1
+ with:
+ sdk: 'beta'
+ - name: Generate code
+ run: dart pub get
+ working-directory: ./health_data_store
+ - name: Generate code
+ run: dart run build_runner build
+ working-directory: ./health_data_store
+
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
@@ -45,6 +56,17 @@ jobs:
with:
distribution: 'zulu'
java-version: '11'
+ - name: Setup dart
+ uses: dart-lang/setup-dart@v1
+ with:
+ sdk: 'beta'
+ - name: Generate code
+ run: dart pub get
+ working-directory: ./health_data_store
+ - name: Generate code
+ run: dart run build_runner build
+ working-directory: ./health_data_store
+
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
.github/workflows/pkg-CI.yml
@@ -28,6 +28,9 @@ jobs:
- name: Get dependencies
run: dart pub get
working-directory: ./health_data_store
+ - name: Generate code
+ run: dart run build_runner build
+ working-directory: ./health_data_store
- name: Analyze code
run: dart analyze
working-directory: ./health_data_store
health_data_store/lib/src/repositories/blood_pressure_repository_impl.dart
@@ -4,6 +4,7 @@ import 'package:health_data_store/src/extensions/datetime_seconds.dart';
import 'package:health_data_store/src/repositories/blood_pressure_repository.dart';
import 'package:health_data_store/src/types/blood_pressure_record.dart';
import 'package:health_data_store/src/types/date_range.dart';
+import 'package:health_data_store/src/types/units/pressure.dart';
import 'package:sqflite_common/sqflite.dart';
/// Implementation of repository for [BloodPressureRecord]s.
@@ -32,19 +33,19 @@ class BloodPressureRepositoryImpl extends BloodPressureRepository {
if (record.sys != null) {
await txn.insert('Systolic', {
'entryID': entryID,
- 'sys': record.sys,
+ 'sys': record.sys!.kPa,
});
}
if (record.dia != null) {
await txn.insert('Diastolic', {
'entryID': entryID,
- 'dia': record.dia,
+ 'dia': record.dia!.kPa,
});
}
if (record.pul != null) {
await txn.insert('Pulse', {
'entryID': entryID,
- 'pul': record.pul,
+ 'pul': record.pul!.kPa,
});
}
});
@@ -66,9 +67,9 @@ class BloodPressureRepositoryImpl extends BloodPressureRepository {
final timeS = r['timestampUnixS'] as int;
final newRec = BloodPressureRecord(
time: DateTimeS.fromSecondsSinceEpoch(timeS),
- sys: r['sys'] as int?,
- dia: r['dia'] as int?,
- pul: r['pul'] as int?,
+ sys: _decode(r['sys']),
+ dia: _decode(r['dia']),
+ pul: _decode(r['pul']),
);
if (newRec.sys !=null || newRec.dia != null || newRec.pul != null) {
records.add(newRec);
@@ -79,41 +80,44 @@ class BloodPressureRepositoryImpl extends BloodPressureRepository {
}
@override
- Future<void> remove(BloodPressureRecord value) async {
- await _db.transaction((txn) async {
- String query = 'SELECT t.entryID FROM Timestamps AS t ';
- if (value.sys != null)
- query += 'LEFT JOIN Systolic AS s ON t.entryID = s.entryID ';
- if (value.dia != null)
- query += 'LEFT JOIN Diastolic AS d ON t.entryID = d.entryID ';
- if (value.pul != null)
- query += 'LEFT JOIN Pulse AS p ON t.entryID = p.entryID ';
- query += 'WHERE timestampUnixS = ? ';
- if (value.sys != null)
- query += 'AND sys = ? ';
- if (value.dia != null)
- query += 'AND dia = ? ';
- if (value.pul != null)
- query += 'AND pul = ? ';
+ Future<void> remove(BloodPressureRecord value) => _db.transaction((txn) async{
+ String query = 'SELECT t.entryID FROM Timestamps AS t ';
+ if (value.sys != null)
+ query += 'LEFT JOIN Systolic AS s ON t.entryID = s.entryID ';
+ if (value.dia != null)
+ query += 'LEFT JOIN Diastolic AS d ON t.entryID = d.entryID ';
+ if (value.pul != null)
+ query += 'LEFT JOIN Pulse AS p ON t.entryID = p.entryID ';
+ query += 'WHERE timestampUnixS = ? ';
+ if (value.sys != null)
+ query += 'AND sys = ? ';
+ if (value.dia != null)
+ query += 'AND dia = ? ';
+ if (value.pul != null)
+ query += 'AND pul = ? ';
- final entryResult = await txn.rawQuery(query, [
- value.time.secondsSinceEpoch,
- if (value.sys != null)
- value.sys,
- if (value.dia != null)
- value.dia,
- if (value.pul != null)
- value.pul,
- ]);
- if (entryResult.isEmpty) return;
- final entryID = entryResult.first['entryID'];
+ final entryResult = await txn.rawQuery(query, [
+ value.time.secondsSinceEpoch,
if (value.sys != null)
- await txn.delete('Systolic', where: 'entryID = ?', whereArgs:[entryID]);
+ value.sys!.kPa,
if (value.dia != null)
- await txn.delete('Diastolic', where:'entryID = ?', whereArgs:[entryID]);
+ value.dia!.kPa,
if (value.pul != null)
- await txn.delete('Pulse', where: 'entryID = ?', whereArgs: [entryID]);
- });
+ value.pul!.kPa,
+ ]);
+ if (entryResult.isEmpty) return;
+ final entryID = entryResult.first['entryID'];
+ if (value.sys != null)
+ await txn.delete('Systolic', where: 'entryID = ?', whereArgs:[entryID]);
+ if (value.dia != null)
+ await txn.delete('Diastolic', where:'entryID = ?', whereArgs:[entryID]);
+ if (value.pul != null)
+ await txn.delete('Pulse', where: 'entryID = ?', whereArgs: [entryID]);
+ });
+
+ Pressure? _decode(Object? value) {
+ if (value is! double) return null;
+ return Pressure.kPa(value);
}
}
health_data_store/lib/src/repositories/medicine_intake_repository_impl.dart
@@ -5,6 +5,7 @@ import 'package:health_data_store/src/repositories/medicine_intake_repository.da
import 'package:health_data_store/src/types/date_range.dart';
import 'package:health_data_store/src/types/medicine.dart';
import 'package:health_data_store/src/types/medicine_intake.dart';
+import 'package:health_data_store/src/types/units/weight.dart';
import 'package:sqflite_common/sqflite.dart';
/// Implementation of a repository for [MedicineIntake]s.
@@ -32,7 +33,7 @@ class MedicineIntakeRepositoryImpl extends MedicineIntakeRepository {
if (intake.medicine.color != null)
intake.medicine.color,
if (intake.medicine.dosis != null)
- intake.medicine.dosis,
+ intake.medicine.dosis!.mg,
],
);
assert(medIDRes.isNotEmpty);
@@ -49,7 +50,7 @@ class MedicineIntakeRepositoryImpl extends MedicineIntakeRepository {
await txn.insert('Intake', {
'entryID': id,
'medID': medID,
- 'dosis': intake.dosis,
+ 'dosis': intake.dosis.mg,
});
});
@@ -69,10 +70,10 @@ class MedicineIntakeRepositoryImpl extends MedicineIntakeRepository {
final timeS = r['timestampUnixS'] as int;
intakes.add(MedicineIntake(
time: DateTimeS.fromSecondsSinceEpoch(timeS),
- dosis: r['dosis'] as double,
+ dosis: _decode(r['dosis'])!,
medicine: Medicine(
designation: r['designation'] as String,
- dosis: r['defaultDose'] as double?,
+ dosis: _decode(r['defaultDose']),
color: r['color'] as int?,
),
));
@@ -96,13 +97,18 @@ class MedicineIntakeRepositoryImpl extends MedicineIntakeRepository {
')',
[
intake.time.secondsSinceEpoch,
- intake.dosis,
+ intake.dosis.mg,
intake.medicine.designation,
if (intake.medicine.color != null)
intake.medicine.color,
if (intake.medicine.dosis != null)
- intake.medicine.dosis,
+ intake.medicine.dosis?.mg,
]
);
+ Weight? _decode(Object? value) {
+ if (value is! double) return null;
+ return Weight.mg(value);
+ }
+
}
health_data_store/lib/src/repositories/medicine_repository_impl.dart
@@ -2,6 +2,7 @@ import 'package:health_data_store/src/database_manager.dart';
import 'package:health_data_store/src/extensions/castable.dart';
import 'package:health_data_store/src/repositories/medicine_repository.dart';
import 'package:health_data_store/src/types/medicine.dart';
+import 'package:health_data_store/src/types/units/weight.dart';
import 'package:sqflite_common/sqflite.dart';
/// Implementation of repository for medicines that are taken by the user.
@@ -19,7 +20,7 @@ class MedicineRepositoryImpl extends MedicineRepository {
await txn.insert('Medicine', {
'medID': id,
'designation': medicine.designation,
- 'defaultDose': medicine.dosis,
+ 'defaultDose': medicine.dosis?.mg,
'color': medicine.color,
'removed': 0,
});
@@ -35,7 +36,7 @@ class MedicineRepositoryImpl extends MedicineRepository {
for (final m in medData) {
meds.add(Medicine(
designation: m['designation'].toString(),
- dosis: m['defaultDose'] as double?,
+ dosis: _decode(m['defaultDose']),
color: m['color'] as int?,
));
}
@@ -56,8 +57,13 @@ class MedicineRepositoryImpl extends MedicineRepository {
if (value.color != null)
value.color,
if (value.dosis != null)
- value.dosis,
+ value.dosis!.mg,
],
);
+ Weight? _decode(Object? value) {
+ if (value is! double) return null;
+ return Weight.mg(value);
+ }
+
}
health_data_store/lib/src/types/units/pressure.dart
@@ -0,0 +1,27 @@
+/// Class representing and converting [pressure](https://en.wikipedia.org/wiki/Pressure).
+class Pressure {
+ /// Create pressure from kilopascal.
+ Pressure.kPa(double value): _valPa = value / 1000;
+
+ /// Create pressure from [Millimetre of mercury](https://en.wikipedia.org/wiki/Millimetre_of_mercury).
+ Pressure.mmHg(int value): _valPa = value / 133.322;
+
+ /// Currently stored value in pascal.
+ double _valPa;
+
+ /// Get value in kilopascal.
+ double get kPa => _valPa * 1000;
+
+ /// Get value in [Millimetre of mercury](https://en.wikipedia.org/wiki/Millimetre_of_mercury).
+ int get mmHg => (_valPa * 133.322).round();
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other)
+ || other is Pressure
+ && runtimeType == other.runtimeType
+ && _valPa == other._valPa;
+
+ @override
+ int get hashCode => _valPa.hashCode;
+}
health_data_store/lib/src/types/units/weight.dart
@@ -0,0 +1,27 @@
+/// Class representing and converting weight.
+class Weight {
+ /// Create a weight from milligrams.
+ Weight.mg(this._value);
+
+ /// Create a weight from [grain](https://en.wikipedia.org/wiki/Grain_(unit)).
+ Weight.gr(double value): _value = value * 64.79891;
+
+ /// Currently stored weight in milligrams.
+ double _value;
+
+ /// The weight in milligrams.
+ double get mg => _value;
+
+ /// Get the value in [grain](https://en.wikipedia.org/wiki/Grain_(unit)).
+ double get gr => mg / 64.79891;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is Weight
+ && runtimeType == other.runtimeType
+ && _value == other._value;
+
+ @override
+ int get hashCode => _value.hashCode;
+}
health_data_store/lib/src/types/blood_pressure_record.dart
@@ -1,4 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:health_data_store/src/types/units/pressure.dart';
part 'blood_pressure_record.freezed.dart';
@@ -10,10 +11,10 @@ class BloodPressureRecord with _$BloodPressureRecord {
/// Timestamp when the measurement was taken.
required DateTime time,
/// Systolic value of the measurement.
- int? sys,
+ Pressure? sys,
/// Diastolic value of the measurement.
- int? dia,
+ Pressure? dia,
/// Pulse value of the measurement.
- int? pul,
+ Pressure? pul,
}) = _BloodPressureRecord;
}
health_data_store/lib/src/types/medicine.dart
@@ -1,4 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:health_data_store/src/types/units/weight.dart';
part 'medicine.freezed.dart';
@@ -15,6 +16,6 @@ class Medicine with _$Medicine {
/// Sample value: `0xFF42A5F5`
int? color,
/// Default dosis of medication.
- double? dosis,
+ Weight? dosis,
}) = _Medicine;
}
health_data_store/lib/src/types/medicine_intake.dart
@@ -1,5 +1,6 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:health_data_store/src/types/medicine.dart';
+import 'package:health_data_store/src/types/units/weight.dart';
part 'medicine_intake.freezed.dart';
@@ -16,6 +17,6 @@ class MedicineIntake with _$MedicineIntake {
///
/// When the medication has a default value, this must be set to that value,
/// as it may change.
- required double dosis,
+ required Weight dosis,
}) = _MedicineIntake;
}
health_data_store/lib/src/database_manager.dart
@@ -73,8 +73,8 @@ class DatabaseManager {
('Pulse','pul')
]) {
await txn.execute('CREATE TABLE "${info.$1}" ('
- '"entryID" INTEGER NOT NULL,'
- '"${info.$2}" INTEGER,'
+ '"entryID" INTEGER NOT NULL,'
+ '"${info.$2}" REAL,'
'FOREIGN KEY("entryID") REFERENCES "Timestamps"("entryID"),'
'PRIMARY KEY("entryID")'
');');
health_data_store/test/src/repositories/medicine_repository_test.dart
@@ -4,6 +4,7 @@ import 'package:test/expect.dart';
import 'package:test/scaffolding.dart';
import '../database_manager_test.dart';
+import '../types/medicine_test.dart';
void main() {
sqfliteTestInit();
@@ -23,19 +24,19 @@ void main() {
final db = await mockDBManager();
addTearDown(db.close);
final repo = MedicineRepositoryImpl(db.db);
- await repo.add(Medicine(designation: 'med1', color: 0xFF226A, dosis: 42));
- await repo.add(Medicine(designation: 'med2', color: 0xAF226B, dosis: 43));
+ await repo.add(mockMedicine(designation:'med1', color:0xFF226A, dosis:42));
+ await repo.add(mockMedicine(designation:'med2', color:0xAF226B, dosis:43));
final all = await repo.getAll();
expect(all, hasLength(2));
expect(all, containsAll([
isA<Medicine>()
.having((p0) => p0.designation, 'designation', 'med1')
.having((p0) => p0.color, 'color', 0xFF226A)
- .having((p0) => p0.dosis, 'dosis', 42),
+ .having((p0) => p0.dosis?.mg, 'dosis', 42),
isA<Medicine>()
.having((p0) => p0.designation, 'designation', 'med2')
.having((p0) => p0.color, 'color', 0xAF226B)
- .having((p0) => p0.dosis, 'dosis', 43),
+ .having((p0) => p0.dosis?.mg, 'dosis', 43),
]));
});
test('should store all incomplete medicines', () async {
@@ -43,7 +44,7 @@ void main() {
addTearDown(db.close);
final repo = MedicineRepositoryImpl(db.db);
await repo.add(Medicine(designation: 'med1', color: 0xFF226A,));
- await repo.add(Medicine(designation: 'med2', dosis: 43));
+ await repo.add(mockMedicine(designation: 'med2', dosis: 43));
await repo.add(Medicine(designation: 'med3',));
final all = await repo.getAll();
expect(all, hasLength(3));
@@ -51,24 +52,24 @@ void main() {
isA<Medicine>()
.having((p0) => p0.designation, 'designation', 'med1')
.having((p0) => p0.color, 'color', 0xFF226A)
- .having((p0) => p0.dosis, 'dosis', null),
+ .having((p0) => p0.dosis?.mg, 'dosis', null),
isA<Medicine>()
.having((p0) => p0.designation, 'designation', 'med2')
.having((p0) => p0.color, 'color', null)
- .having((p0) => p0.dosis, 'dosis', 43),
+ .having((p0) => p0.dosis?.mg, 'dosis', 43),
isA<Medicine>()
.having((p0) => p0.designation, 'designation', 'med3')
.having((p0) => p0.color, 'color', null)
- .having((p0) => p0.dosis, 'dosis', null),
+ .having((p0) => p0.dosis?.mg, 'dosis', null),
]));
});
test('should mark medicines as deleted', () async {
final db = await mockDBManager();
addTearDown(db.close);
final repo = MedicineRepositoryImpl(db.db);
- final med1= Medicine(designation: 'med1', color: 0xFF226A, dosis: 42);
+ final med1= mockMedicine(designation: 'med1', color: 0xFF226A, dosis: 42);
await repo.add(med1);
- await repo.add(Medicine(designation: 'med2', color: 0xAF226B, dosis: 43));
+ await repo.add(mockMedicine(designation:'med2', color:0xAF226B, dosis:43));
expect(await repo.getAll(), hasLength(2));
await repo.remove(med1);
expect(await repo.getAll(), hasLength(1));
@@ -79,7 +80,7 @@ void main() {
final repo = MedicineRepositoryImpl(db.db);
final med1 = Medicine(designation: 'med1', color: 0xFF226A,);
await repo.add(med1);
- final med2 = Medicine(designation: 'med2', dosis: 43);
+ final med2 = mockMedicine(designation: 'med2', dosis: 43);
await repo.add(med2);
final med3 = Medicine(designation: 'med3',);
await repo.add(med3);
health_data_store/test/src/types/blood_pressure_record_test.dart
@@ -1,4 +1,5 @@
import 'package:health_data_store/src/types/blood_pressure_record.dart';
+import 'package:health_data_store/src/types/units/pressure.dart';
import 'package:test/test.dart';
void main() {
@@ -6,36 +7,36 @@ void main() {
final time = DateTime.now();
final record = BloodPressureRecord(
time: time,
- sys: 123,
- dia: 56,
- pul: 78,
+ sys: Pressure.mmHg(123),
+ dia: Pressure.mmHg(56),
+ pul: Pressure.mmHg(78),
);
expect(record.time, time);
- expect(record.sys, 123);
- expect(record.dia, 56);
- expect(record.pul, 78);
+ expect(record.sys?.mmHg, 123);
+ expect(record.dia?.mmHg, 56);
+ expect(record.pul?.mmHg, 78);
expect(record, equals(BloodPressureRecord(
time: time,
- sys: 123,
- dia: 56,
- pul: 78,
+ sys: Pressure.mmHg(123),
+ dia: Pressure.mmHg(56),
+ pul: Pressure.mmHg(78),
)));
});
test('should initialize with partial data', () {
final time = DateTime.now();
final record = BloodPressureRecord(
time: time,
- dia: 56,
+ dia: Pressure.mmHg(56),
);
expect(record.time, time);
- expect(record.sys, null);
- expect(record.dia, 56);
- expect(record.pul, null);
+ expect(record.sys?.mmHg, null);
+ expect(record.dia?.mmHg, 56);
+ expect(record.pul?.mmHg, null);
expect(record, isNot(equals(BloodPressureRecord(
time: time,
- sys: 123,
- dia: 56,
- pul: 78,
+ sys: Pressure.mmHg(123),
+ dia: Pressure.mmHg(56),
+ pul: Pressure.mmHg(78),
))));
});
}
@@ -47,7 +48,7 @@ BloodPressureRecord mockRecord({
int? pul,
}) => BloodPressureRecord(
time: time!=null ? DateTime.fromMillisecondsSinceEpoch(time) : DateTime.now(),
- sys: sys,
- dia: dia,
- pul: pul,
+ sys: sys == null ? null : Pressure.mmHg(sys),
+ dia: dia == null ? null : Pressure.mmHg(dia),
+ pul: pul == null ? null : Pressure.mmHg(pul),
);
health_data_store/test/src/types/medicine_intake_test.dart
@@ -1,16 +1,20 @@
import 'package:health_data_store/src/types/medicine.dart';
import 'package:health_data_store/src/types/medicine_intake.dart';
+import 'package:health_data_store/src/types/units/weight.dart';
import 'package:test/test.dart';
void main() {
test('should initialize', () {
final intake = MedicineIntake(
time: DateTime.now(),
- medicine: Medicine(designation: 'test', dosis: 42),
- dosis: 42,
+ medicine: Medicine(designation: 'test', dosis: Weight.mg(42)),
+ dosis: Weight.mg(42),
);
- expect(intake.medicine, equals(Medicine(designation: 'test', dosis: 42)));
- expect(intake.dosis, equals(42));
+ expect(intake.medicine, equals(Medicine(
+ designation: 'test',
+ dosis: Weight.mg(42),
+ )));
+ expect(intake.dosis.mg, equals(42));
});
}
@@ -20,5 +24,5 @@ MedicineIntake mockIntake(Medicine medicine, {
}) => MedicineIntake(
time: time!=null ? DateTime.fromMillisecondsSinceEpoch(time) : DateTime.now(),
medicine: medicine,
- dosis: dosis ?? medicine.dosis ?? 42,
+ dosis: Weight.mg(dosis ?? medicine.dosis?.mg ?? 42.0),
);
health_data_store/test/src/types/medicine_test.dart
@@ -1,21 +1,24 @@
import 'dart:math';
import 'package:health_data_store/src/types/medicine.dart';
+import 'package:health_data_store/src/types/units/weight.dart';
import 'package:test/test.dart';
void main() {
test('should initialize', () {
- final med = Medicine(designation: 'test', dosis: 42);
+ final med = Medicine(designation: 'test', dosis: Weight.mg(42));
expect(med.designation, equals('test'));
- expect(med.dosis, equals(42));
+ expect(med.dosis?.mg, equals(42));
});
}
Medicine mockMedicine({
String? designation,
double? dosis,
+ int? color
}) => Medicine(
designation: designation ??
'med'+(Random().nextInt(899999) + 100000).toString(),
- dosis: dosis,
+ dosis: dosis == null ? null : Weight.mg(dosis),
+ color: color,
);