Commit 647028b
Changed files (5)
lib
model
export_import
test
model
export_import
lib/model/export_import/column.dart
@@ -2,7 +2,7 @@ import 'dart:convert';
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/export_import/legacy_column.dart';
-import 'package:blood_pressure_app/model/export_import/reocord_formatter.dart';
+import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
lib/model/export_import/legacy_column.dart
@@ -1,5 +1,5 @@
import 'package:blood_pressure_app/model/blood_pressure.dart';
-import 'package:blood_pressure_app/model/export_import/reocord_formatter.dart';
+import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
/// Convert [BloodPressureRecord]s from and to strings and provide metadata about the conversion.
@@ -80,12 +80,24 @@ class ExportColumn { // TODO: change this class so it implements the interface.
}
}
-/// Type a [ExportColumn] can be parsed as.
+/// Type a [Formatter] can uses to indicate the kind of data returned.
+///
+/// The data types returned from the deprecated [ExportColumn] may differ from the guarantees.
enum RowDataFieldType {
- timestamp, sys, dia, pul, notes,
- @Deprecated('use needlePin instead')
+ /// Guarantees [DateTime] is returned.
+ timestamp,
+ /// Guarantees [int] is returned.
+ sys,
+ /// Guarantees [int] is returned.
+ dia,
+ /// Guarantees [int] is returned.
+ pul,
+ /// Guarantees [String] is returned.
+ notes,
+ @Deprecated('use needlePin instead') // TODO: implement conversion to needle pin?
color,
- needlePin;
+ /// Guarantees that the returned type is of type [MeasurementNeedlePin].
+ needlePin; // TODO implement in ScriptedFormatter
String localize(AppLocalizations localizations) {
switch(this) {
lib/model/export_import/reocord_formatter.dart → lib/model/export_import/record_formatter.dart
@@ -38,40 +38,36 @@ class ScriptedFormatter implements Formatter {
@override
(RowDataFieldType, dynamic)? decode(String formattedRecord) {
- // TODO: rewrite contents
+ print('$pattern.decode($formattedRecord)');
if (restoreAbleType == null) return null;
- if (pattern == r'$NOTE') return (RowDataFieldType.notes, formattedRecord);
- if (pattern == r'$COLOR') {
- final value = int.tryParse(formattedRecord);
- return value == null ? null : (RowDataFieldType.color, Color(value));
- }
-
- // records are parse by replacing the values with capture groups
- final types = RegExp(r'\$(TIMESTAMP|SYS|DIA|PUL)').allMatches(pattern).map((e) => e.group(0)).toList();
- final numRegex = pattern.replaceAll(RegExp(r'\$(TIMESTAMP|SYS|DIA|PUL)'), '([0-9]+.?[0-9]*)'); // ints and doubles
- final numMatches = RegExp(numRegex).allMatches(formattedRecord);
- final numbers = [];
- if (numMatches.isNotEmpty) {
- for (var i = 1; i <= numMatches.first.groupCount; i++) {
- numbers.add(numMatches.first[i]);
- }
- }
-
- for (var i = 0; i < types.length; i++) {
- switch (types[i]) {
- case r'$TIMESTAMP':
- return (RowDataFieldType.timestamp, int.tryParse(numbers[i] ?? ''));
- case r'$SYS':
- return (RowDataFieldType.sys, double.tryParse(numbers[i] ?? ''));
- case r'$DIA':
- return (RowDataFieldType.dia, double.tryParse(numbers[i] ?? ''));
- case r'$PUL':
- return (RowDataFieldType.pul, double.tryParse(numbers[i] ?? ''));
- }
- }
-
- assert(false);
+ final valueRegex = RegExp(pattern.replaceAll(RegExp(r'\$(TIMESTAMP|COLOR|SYS|DIA|PUL|NOTE)'), '(?<value>.*)'),);
+ final match = valueRegex.firstMatch(formattedRecord);
+ if (match == null) return null;
+ var text = match.namedGroup('value');
+ if (text == null) return null;
+
+ final value = (){switch(restoreAbleType!) {
+ case RowDataFieldType.timestamp:
+ final num = int.tryParse(text);
+ if (num != null) return DateTime.fromMillisecondsSinceEpoch(num);
+ return null;
+ case RowDataFieldType.sys:
+ case RowDataFieldType.dia:
+ case RowDataFieldType.pul:
+ return int.tryParse(text);
+ case RowDataFieldType.notes:
+ return text;
+ case RowDataFieldType.color:
+ final num = int.tryParse(text);
+ if (num != null) return Color(num);
+ return null;
+ case RowDataFieldType.needlePin:
+ final num = int.tryParse(text);
+ if (num == null) return null;
+ return MeasurementNeedlePin(Color(num));
+ }}();
+ if (value != null) return (restoreAbleType!, value);
return null;
}
@@ -114,7 +110,6 @@ class ScriptedFormatter implements Formatter {
RowDataFieldType? _restoreAbleType;
@override
- // TODO: rewrite (logic)
RowDataFieldType? get restoreAbleType {
if (_hasRestoreableType == false) {
if (pattern.contains(RegExp(r'[{},]'))) {
@@ -123,14 +118,14 @@ class ScriptedFormatter implements Formatter {
_restoreAbleType = RowDataFieldType.timestamp;
} else if (pattern == r'$COLOR') {
_restoreAbleType = RowDataFieldType.color;
- } else if (pattern.contains(RegExp(r'[^{},$]*\$(SYS)[^{},$]*'))) {
+ } else if (pattern == r'$NOTE') {
+ _restoreAbleType = RowDataFieldType.notes;
+ } else if (pattern.contains(RegExp(r'[^{},$]*\$(SYS)[^{},$]*'))) {
_restoreAbleType = RowDataFieldType.sys;
} else if (pattern.contains(RegExp(r'[^{},$]*\$(DIA)[^{},$]*'))) {
_restoreAbleType = RowDataFieldType.dia;
} else if (pattern.contains(RegExp(r'[^{},$]*\$(PUL)[^{},$]*'))) {
_restoreAbleType = RowDataFieldType.pul;
- } else if (pattern.contains(RegExp(r'[^{},$]*\$(NOTE)[^{},$]*'))) {
- _restoreAbleType = RowDataFieldType.notes;
} else { _restoreAbleType = null; }
_hasRestoreableType = true;
}
test/model/export_import/column_test.dart
@@ -1,79 +0,0 @@
-
-import 'package:blood_pressure_app/model/blood_pressure.dart';
-import 'package:blood_pressure_app/model/export_import/legacy_column.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:intl/intl.dart';
-
-void main() {
- group('ExportColumn', () {
- test('should not throw errors', () async {
- final c = ExportColumn(
- internalName: 'testColumn',
- columnTitle: 'Test',
- formatPattern: r'$SYS'
- );
-
- expect(c.internalName, 'testColumn');
- expect(c.columnTitle, 'Test');
- expect(c.formatPattern, r'$SYS');
- c.formatRecord(BloodPressureRecord(DateTime.now(), 123, 45, 67, 'notes'));
- c.formatRecord(BloodPressureRecord(DateTime.now(), null, null, null, ''));
- c.parseRecord('122');
- });
- test('should not change during json conversion', () {
- final original = ExportColumn(
- internalName: 'testColumn',
- columnTitle: 'Test',
- formatPattern: r'{{$SYS-$DIA}}',
- );
- final fromJson = ExportColumn.fromJson(original.toJson());
- expect(original.internalName, fromJson.internalName);
- expect(original.columnTitle, fromJson.columnTitle);
- expect(original.formatPattern, fromJson.formatPattern);
- expect(original.isReversible, fromJson.isReversible);
- expect(original.formatPattern, fromJson.formatPattern);
- expect(original.toJson(), fromJson.toJson());
- });
- test('should create correct strings', () {
- final testRecord = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), 123, 45, 67, 'Test',
- needlePin: const MeasurementNeedlePin(Colors.red));
-
- expect(_testColumn(r'$SYS',).formatRecord(testRecord), testRecord.systolic.toString());
- expect(_testColumn(r'$DIA',).formatRecord(testRecord), testRecord.diastolic.toString());
- expect(_testColumn(r'$PUL',).formatRecord(testRecord), testRecord.pulse.toString());
- expect(_testColumn(r'$COLOR',).formatRecord(testRecord), testRecord.needlePin!.color.value.toString());
- expect(_testColumn(r'$NOTE',).formatRecord(testRecord), testRecord.notes);
- expect(_testColumn(r'$TIMESTAMP',).formatRecord(testRecord), testRecord.creationTime.millisecondsSinceEpoch.toString());
- expect(_testColumn(r'{{$SYS-$DIA}}',).formatRecord(testRecord),
- (testRecord.systolic! - testRecord.diastolic!).toDouble().toString());
- expect(_testColumn(r'{{$SYS*$DIA-$PUL}}',).formatRecord(testRecord),
- (testRecord.systolic! * testRecord.diastolic! - testRecord.pulse!).toDouble().toString());
- expect(_testColumn(r'$SYS-$DIA',).formatRecord(testRecord), ('${testRecord.systolic}-${testRecord.diastolic}'));
-
- final formatter = DateFormat.yMMMMEEEEd();
- expect(_testColumn('\$FORMAT{\$TIMESTAMP,${formatter.pattern}}',).formatRecord(testRecord),
- formatter.format(testRecord.creationTime));
- });
- test('should report correct reversibility', () {
- expect(_testColumn(r'$SYS',).isReversible, true);
- expect(_testColumn(r'$DIA',).isReversible, true);
- expect(_testColumn(r'$PUL',).isReversible, true);
- expect(_testColumn(r'$TIMESTAMP',).isReversible, true);
- expect(_testColumn(r'$NOTE',).isReversible, true);
- expect(_testColumn(r'$COLOR',).isReversible, true);
- expect(_testColumn(r'test$NOTE',).isReversible, true);
- expect(_testColumn(r'test$NOTE123',).isReversible, true);
- expect(_testColumn(r'test$SYS123',).isReversible, true);
- expect(_testColumn(r'test$DIA123',).isReversible, true);
- expect(_testColumn(r'test$PUL123',).isReversible, true);
-
- //expect(_testColumn(r'$PUL$SYS',).isReversible, false);
- expect(_testColumn(r'{{$PUL-$SYS}}',).isReversible, false);
- });
- // TODO: test parsing
- });
-}
-
-ExportColumn _testColumn(String formatPattern) =>
- ExportColumn(internalName: '', columnTitle: '', formatPattern: formatPattern,);
\ No newline at end of file
test/model/export_import/record_formatter_test.dart
@@ -0,0 +1,73 @@
+import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:blood_pressure_app/model/export_import/legacy_column.dart';
+import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:intl/intl.dart';
+
+void main() {
+ group('ScriptedFormatter', () {
+ test('should not throw errors', () async {
+ final f = ScriptedFormatter(r'$SYS');
+
+ expect(f.formatPattern, r'$SYS');
+ f.encode(BloodPressureRecord(DateTime.now(), 123, 456, 789, 'test text'));
+ f.encode(BloodPressureRecord(DateTime.now(), null, null, null, ''));
+ f.decode('123');
+ });
+ test('should create correct strings', () {
+ final testRecord = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), 123, 45, 67, 'Test',
+ needlePin: const MeasurementNeedlePin(Colors.red));
+
+ expect(ScriptedFormatter(r'constant text',).encode(testRecord), 'constant text');
+ expect(ScriptedFormatter(r'$SYS',).encode(testRecord), testRecord.systolic.toString());
+ expect(ScriptedFormatter(r'$DIA',).encode(testRecord), testRecord.diastolic.toString());
+ expect(ScriptedFormatter(r'$PUL',).encode(testRecord), testRecord.pulse.toString());
+ expect(ScriptedFormatter(r'$COLOR',).encode(testRecord), testRecord.needlePin!.color.value.toString());
+ expect(ScriptedFormatter(r'$NOTE',).encode(testRecord), testRecord.notes);
+ expect(ScriptedFormatter(r'$TIMESTAMP',).encode(testRecord), testRecord.creationTime.millisecondsSinceEpoch.toString());
+ expect(ScriptedFormatter(r'{{$SYS-$DIA}}',).encode(testRecord),
+ (testRecord.systolic! - testRecord.diastolic!).toDouble().toString());
+ expect(ScriptedFormatter(r'{{$SYS*$DIA-$PUL}}',).encode(testRecord),
+ (testRecord.systolic! * testRecord.diastolic! - testRecord.pulse!).toDouble().toString());
+ expect(ScriptedFormatter(r'$SYS-$DIA',).encode(testRecord), ('${testRecord.systolic}-${testRecord.diastolic}'));
+
+ final formatter = DateFormat.yMMMMEEEEd();
+ expect(ScriptedFormatter('\$FORMAT{\$TIMESTAMP,${formatter.pattern}}',).encode(testRecord),
+ formatter.format(testRecord.creationTime));
+ });
+ test('should report correct reversibility', () {
+ expect(ScriptedFormatter(r'$SYS',).restoreAbleType, RowDataFieldType.sys);
+ expect(ScriptedFormatter(r'$DIA',).restoreAbleType, RowDataFieldType.dia);
+ expect(ScriptedFormatter(r'$PUL',).restoreAbleType, RowDataFieldType.pul);
+ expect(ScriptedFormatter(r'$TIMESTAMP',).restoreAbleType, RowDataFieldType.timestamp);
+ expect(ScriptedFormatter(r'$NOTE',).restoreAbleType, RowDataFieldType.notes);
+ expect(ScriptedFormatter(r'$COLOR',).restoreAbleType, RowDataFieldType.color);
+ expect(ScriptedFormatter(r'test$SYS123',).restoreAbleType, RowDataFieldType.sys);
+ expect(ScriptedFormatter(r'test$DIA123',).restoreAbleType, RowDataFieldType.dia);
+ expect(ScriptedFormatter(r'test$PUL123',).restoreAbleType, RowDataFieldType.pul);
+
+ //expect(ScriptedFormatter(r'$PUL$SYS',).isReversible, false);
+ expect(ScriptedFormatter(r'test$NOTE',).restoreAbleType, null);
+ expect(ScriptedFormatter(r'test$NOTE123',).restoreAbleType, null);
+ expect(ScriptedFormatter(r'{{$PUL-$SYS}}',).restoreAbleType, null);
+ });
+ test('should correctly decode reversible patterns', () {
+ expect(ScriptedFormatter(r'$SYS',).decode('123'), (RowDataFieldType.sys, 123));
+ expect(ScriptedFormatter(r'$DIA',).decode('456'), (RowDataFieldType.dia, 456));
+ expect(ScriptedFormatter(r'$PUL',).decode('789'), (RowDataFieldType.pul, 789));
+ expect(ScriptedFormatter(r'$TIMESTAMP',).decode('12345678'), (RowDataFieldType.timestamp, DateTime.fromMillisecondsSinceEpoch(12345678)));
+ expect(ScriptedFormatter(r'$NOTE',).decode('test note'), (RowDataFieldType.notes, 'test note'));
+ final encodedPurple = ScriptedFormatter(r'$COLOR',)
+ .encode(BloodPressureRecord(DateTime.now(), null, null, null, '',
+ needlePin: const MeasurementNeedlePin(Colors.purple)));
+ expect(ScriptedFormatter(r'$COLOR',).decode(encodedPurple)?.$1, RowDataFieldType.color);
+ expect(ScriptedFormatter(r'$COLOR',).decode(encodedPurple)?.$2, isA<Color>()
+ .having((p0) => p0.value, 'color', Colors.purple.value));
+ expect(ScriptedFormatter(r'test$SYS',).decode('test567'), (RowDataFieldType.sys, 567));
+ expect(ScriptedFormatter(r'test$SYS123',).decode('test567123'), (RowDataFieldType.sys, 567));
+ expect(ScriptedFormatter(r'test$DIA123',).decode('test567123'), (RowDataFieldType.dia, 567));
+ expect(ScriptedFormatter(r'test$PUL123',).decode('test567123'), (RowDataFieldType.pul, 567));
+ });
+ });
+}
\ No newline at end of file