Commit 72f94c1
Changed files (14)
lib
components
model
screens
subsettings
export_import
test
model
export_import
lib/model/export_import/column.dart
@@ -1,7 +1,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/import_field_type.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/csv_converter.dart
@@ -1,7 +1,7 @@
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/export_import/column.dart';
-import 'package:blood_pressure_app/model/export_import/legacy_column.dart' show RowDataFieldType;
+import 'package:blood_pressure_app/model/export_import/import_field_type.dart' show RowDataFieldType;
import 'package:blood_pressure_app/model/export_import/record_parsing_result.dart';
import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
import 'package:blood_pressure_app/model/storage/export_csv_settings_store.dart';
lib/model/export_import/import_field_type.dart
@@ -0,0 +1,44 @@
+import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+
+/// Type a [Formatter] can uses to indicate the kind of data returned.
+///
+/// The data types returned from the deprecated [LegacyExportColumn] may differ from the guarantees.
+enum RowDataFieldType {
+ /// 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?
+ /// Guarantees [Color] is returned.
+ color,
+ /// Guarantees that the returned type is of type [MeasurementNeedlePin].
+ needlePin; // TODO implement in ScriptedFormatter
+
+ String localize(AppLocalizations localizations) {
+ switch(this) {
+ case RowDataFieldType.timestamp:
+ return localizations.timestamp;
+ case RowDataFieldType.sys:
+ return localizations.sysLong;
+ case RowDataFieldType.dia:
+ return localizations.diaLong;
+ case pul:
+ return localizations.pulLong;
+ case RowDataFieldType.notes:
+ return localizations.notes;
+ case RowDataFieldType.color:
+ return localizations.color;
+ case RowDataFieldType.needlePin:
+ return localizations.color;
+ }
+ }
+}
\ No newline at end of file
lib/model/export_import/legacy_column.dart
@@ -1,121 +0,0 @@
-import 'package:blood_pressure_app/model/blood_pressure.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.
-@Deprecated("repaced by class in column.dart")
-class LegacyExportColumn { // TODO: delete
- /// Create object that turns data into strings.
- ///
- /// Example: ExportColumn(internalColumnName: 'pulsePressure', columnTitle: 'Pulse pressure', formatPattern: '{{$SYS-$DIA}}')
- LegacyExportColumn({required this.internalName, required this.columnTitle, required String formatPattern, this.editable = true, this.hidden = false}) {
- this.formatPattern = formatPattern.replaceAll('{{}}', '');
- _formatter = ScriptedFormatter(formatPattern);
- }
-
- late final Formatter _formatter;
-
- /// pure name as in the title of the csv file and for internal purposes. Should not contain special characters and spaces.
- late final String internalName;
-
- /// Display title of the column. Possibly localized
- late final String columnTitle;
-
- /// Pattern to create the field contents from:
- /// It supports inserting values for $TIMESTAMP, $SYS $DIA $PUL, $COLOR and $NOTE. Where $TIMESTAMP is the time since unix epoch in milliseconds.
- /// To format a timestamp in the same format as the $TIMESTAMP variable, $FORMAT(<timestamp>, <formatString>).
- /// It is supported to use basic mathematics inside of double brackets ("{{}}"). In case one of them is not present in the record, -1 is provided.
- /// The following math is supported:
- /// Operations: [+, -, *, /, %, ^]
- /// One-parameter functions [ abs, acos, asin, atan, ceil, cos, cosh, cot, coth, csc, csch, exp, floor, ln, log, round sec, sech, sin, sinh, sqrt, tan, tanh ]
- /// Two-parameter functions [ log, nrt, pow ]
- /// Constants [ e, pi, ln2, ln10, log2e, log10e, sqrt1_2, sqrt2 ]
- /// The full math interpreter specification can be found here: https://pub.dev/documentation/function_tree/latest#interpreter
- ///
- /// The String is processed in the following order:
- /// 1. variable replacement
- /// 2. Math
- /// 3. Date format
- late final String formatPattern;
-
- @Deprecated('will be replaced by the data structure the column is stored in')
- final bool editable; // TODO: remove
-
- /// doesn't show up as unused / hidden field in list
- final bool hidden;
-
- factory LegacyExportColumn.fromJson(Map<String, dynamic> json, [editable = true, hidden = false]) =>
- LegacyExportColumn(
- internalName: json['internalColumnName'],
- columnTitle: json['columnTitle'],
- formatPattern: json['formatPattern'],
- editable: editable,
- hidden: hidden
- );
-
- Map<String, dynamic> toJson() => {
- 'internalColumnName': internalName,
- 'columnTitle': columnTitle,
- 'formatPattern': formatPattern
- };
-
- /// Turns a [BloodPressureRecord] into a string as defined in the [formatPattern].
- String formatRecord(BloodPressureRecord record) => _formatter.encode(record);
-
- /// Parses records if [isReversible] is true else returns an empty list
- List<(RowDataFieldType, dynamic)> parseRecord(String formattedRecord) => [
- if (_formatter.decode(formattedRecord) != null)
- _formatter.decode(formattedRecord)!
- ];
-
- /// Checks if the pattern can be used to parse records. This is the case when the pattern contains variables without
- /// containing curly brackets or commas.
- bool get isReversible => _formatter.restoreAbleType != null;
-
- RowDataFieldType? get parsableFormat => _formatter.restoreAbleType;
-
- @override
- String toString() {
- return 'ExportColumn{internalColumnName: $internalName, columnTitle: $columnTitle, formatPattern: $formatPattern}';
- }
-}
-
-/// Type a [Formatter] can uses to indicate the kind of data returned.
-///
-/// The data types returned from the deprecated [LegacyExportColumn] may differ from the guarantees.
-enum RowDataFieldType {
- /// 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?
- /// Guarantees [Color] is returned.
- color,
- /// Guarantees that the returned type is of type [MeasurementNeedlePin].
- needlePin; // TODO implement in ScriptedFormatter
-
- String localize(AppLocalizations localizations) {
- switch(this) {
- case RowDataFieldType.timestamp:
- return localizations.timestamp;
- case RowDataFieldType.sys:
- return localizations.sysLong;
- case RowDataFieldType.dia:
- return localizations.diaLong;
- case pul:
- return localizations.pulLong;
- case RowDataFieldType.notes:
- return localizations.notes;
- case RowDataFieldType.color:
- return localizations.color;
- case RowDataFieldType.needlePin:
- return localizations.color;
- }
- }
-}
\ No newline at end of file
lib/model/export_import/record_formatter.dart
@@ -1,5 +1,5 @@
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/import_field_type.dart';
import 'package:flutter/material.dart';
import 'package:function_tree/function_tree.dart';
import 'package:intl/intl.dart';
lib/model/storage/db/config_dao.dart
@@ -1,4 +1,3 @@
-import 'package:blood_pressure_app/model/export_import/legacy_column.dart';
import 'package:blood_pressure_app/model/storage/db/config_db.dart';
import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
import 'package:blood_pressure_app/model/storage/export_csv_settings_store.dart';
@@ -17,7 +16,7 @@ import 'package:sqflite/sqflite.dart';
/// The load... methods have to schedule a initial save to db in case an migration / update of fields occurred.
class ConfigDao {
ConfigDao(this._configDB);
-
+
final ConfigDB _configDB;
/// Loads the profiles [Settings] object from the database.
@@ -28,10 +27,10 @@ class ConfigDao {
/// Changes to the database will not propagate to the object.
Future<Settings> loadSettings(int profileID) async {
final dbEntry = await _configDB.database.query(
- ConfigDB.settingsTable,
- columns: ['settings_json'],
- where: 'profile_id = ?',
- whereArgs: [profileID]
+ ConfigDB.settingsTable,
+ columns: ['settings_json'],
+ where: 'profile_id = ?',
+ whereArgs: [profileID]
);
late final Settings settings;
@@ -59,12 +58,12 @@ class ConfigDao {
Future<void> _updateSettings(int profileID, Settings settings) async {
if (!_configDB.database.isOpen) return;
await _configDB.database.insert(
- ConfigDB.settingsTable,
- {
- 'profile_id': profileID,
- 'settings_json': settings.toJson()
- },
- conflictAlgorithm: ConflictAlgorithm.replace
+ ConfigDB.settingsTable,
+ {
+ 'profile_id': profileID,
+ 'settings_json': settings.toJson()
+ },
+ conflictAlgorithm: ConflictAlgorithm.replace
);
}
@@ -261,7 +260,7 @@ class ConfigDao {
conflictAlgorithm: ConflictAlgorithm.replace
);
}
-
+
/// Loads the profiles [ExportColumnsManager] object from the database.
///
/// If any errors occur or the object is not present, a default one will be created. Changes in the object
@@ -309,44 +308,4 @@ class ConfigDao {
conflictAlgorithm: ConflictAlgorithm.replace
);
}
-
- /// Loads the current export columns from the database.
- ///
- /// Changes will *not* be saved automatically, see [updateExportColumn].
- Future<List<LegacyExportColumn>> loadExportColumns() async {
- final existingDbEntries = await _configDB.database.query(
- ConfigDB.exportStringsTable,
- columns: ['internalColumnName', 'columnTitle', 'formatPattern']
- );
- return [
- for (final e in existingDbEntries)
- LegacyExportColumn(
- internalName: e['internalColumnName'].toString(),
- columnTitle: e['columnTitle'].toString(),
- formatPattern: e['formatPattern'].toString()
- ),
- ];
- }
-
- /// Saves a [LegacyExportColumn] to the database.
- ///
- /// If one with the same [LegacyExportColumn.internalName] exists, it will get replaced by the new one regardless of content.
- Future<void> updateExportColumn(LegacyExportColumn exportColumn) async {
- if (!_configDB.database.isOpen) return;
- await _configDB.database.insert(
- ConfigDB.exportStringsTable,
- {
- 'internalColumnName': exportColumn.internalName,
- 'columnTitle': exportColumn.columnTitle,
- 'formatPattern': exportColumn.formatPattern
- },
- conflictAlgorithm: ConflictAlgorithm.replace
- );
- }
-
- /// Deletes the [LegacyExportColumn] where [LegacyExportColumn.internalName] matches [internalName] from the database.
- Future<void> deleteExportColumn(String internalName) async {
- if (!_configDB.database.isOpen) return;
- await _configDB.database.delete('exportStrings', where: 'internalColumnName = ?', whereArgs: [internalName]);
- }
}
\ No newline at end of file
lib/model/export_options.dart
@@ -1,113 +0,0 @@
-import 'dart:collection';
-
-import 'package:blood_pressure_app/main.dart';
-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/storage/common_settings_interfaces.dart';
-import 'package:blood_pressure_app/model/storage/db/config_dao.dart';
-import 'package:blood_pressure_app/model/storage/export_settings_store.dart';
-import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-
-class ExportFields {
- static const defaultCsv = ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes', 'color'];
- static const defaultPdf = ['formattedTimestamp','systolic','diastolic','pulse','notes'];
-}
-
-@Deprecated('will get replaced with ExportColumnsManager')
-class ExportConfigurationModel {
- // 2 sources.
- static ExportConfigurationModel? _instance;
-
- final AppLocalizations localizations;
- final ConfigDao _configDao; // TODO: remove after #181 is complete
-
- final List<LegacyExportColumn> _availableFormats = [];
-
- /// Format: (title, List<internalNameOfExportFormat>)
- List<(String, List<String>)> get exportConfigurations => [
- // Not fully localized, as potential user added configurations can't be localized as well
- // TODO: explain why check for pdf is not needed; write guides for modifying this code;
- (localizations.default_, ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes', 'color']),
- ('"My Heart" export', ['DATUM', 'SYSTOLE', 'DIASTOLE', 'PULS', 'Beschreibung', 'Tags', 'Gewicht', 'Sauerstoffsättigung']),
- ];
-
- ExportConfigurationModel._create(this.localizations, this._configDao);
- Future<void> _asyncInit() async {
- _availableFormats.addAll(getDefaultFormates());
- _availableFormats.addAll(await _configDao.loadExportColumns());
- }
- static Future<ExportConfigurationModel> get(AppLocalizations localizations) async {
- if (_instance == null) {
- _instance = ExportConfigurationModel._create(localizations, globalConfigDao);
- await _instance!._asyncInit();
- }
- return _instance!;
- }
-
- /// Determines which export columns should be used.
- ///
- /// The [fieldSettings] parameter describes the settings of the current export format and should be set accordingly.
- @Deprecated('not implemented anymore')
- List<LegacyExportColumn> getActiveExportColumns(ExportFormat format, CustomFieldsSettings fieldsSettings) {
- return [];
- }
-
- List<LegacyExportColumn> getDefaultFormates() => [
- LegacyExportColumn(internalName: 'timestampUnixMs', columnTitle: localizations.unixTimestamp, formatPattern: r'$TIMESTAMP', editable: false),
- LegacyExportColumn(internalName: 'formattedTimestamp', columnTitle: localizations.time, formatPattern: '\$FORMAT{\$TIMESTAMP,yyyy-MM-dd HH:mm:ss}', editable: false),
- LegacyExportColumn(internalName: 'systolic', columnTitle: localizations.sysLong, formatPattern: r'$SYS', editable: false),
- LegacyExportColumn(internalName: 'diastolic', columnTitle: localizations.diaLong, formatPattern: r'$DIA', editable: false),
- LegacyExportColumn(internalName: 'pulse', columnTitle: localizations.pulLong, formatPattern: r'$PUL', editable: false),
- LegacyExportColumn(internalName: 'notes', columnTitle: localizations.notes, formatPattern: r'$NOTE', editable: false),
- LegacyExportColumn(internalName: 'pulsePressure', columnTitle: localizations.pulsePressure, formatPattern: r'{{$SYS-$DIA}}', editable: false),
- LegacyExportColumn(internalName: 'color', columnTitle: localizations.color, formatPattern: r'$COLOR', editable: false),
-
- LegacyExportColumn(internalName: 'DATUM', columnTitle: '"My Heart" export time', formatPattern: r'$FORMAT{$TIMESTAMP,yyyy-MM-dd HH:mm:ss}', editable: false, hidden: true),
- LegacyExportColumn(internalName: 'SYSTOLE', columnTitle: '"My Heart" export sys', formatPattern: r'$SYS', editable: false, hidden: true),
- LegacyExportColumn(internalName: 'DIASTOLE', columnTitle: '"My Heart" export dia', formatPattern: r'$DIA', editable: false, hidden: true),
- LegacyExportColumn(internalName: 'PULS', columnTitle: '"My Heart" export pul', formatPattern: r'$PUL', editable: false, hidden: true),
- LegacyExportColumn(internalName: 'Beschreibung', columnTitle: '"My Heart" export description', formatPattern: r'null', editable: false, hidden: true),
- LegacyExportColumn(internalName: 'Tags', columnTitle: '"My Heart" export tags', formatPattern: r'', editable: false, hidden: true),
- LegacyExportColumn(internalName: 'Gewicht', columnTitle: '"My Heart" export weight', formatPattern: r'0.0', editable: false, hidden: true),
- LegacyExportColumn(internalName: 'Sauerstoffsättigung', columnTitle: '"My Heart" export oxygen', formatPattern: r'0', editable: false, hidden: true),
- ];
-
- /// Saves a new [LegacyExportColumn] to the list of the available columns.
- ///
- /// In case one with the same internal name exists it gets updated with the new values
- void addOrUpdate(LegacyExportColumn format) {
- _availableFormats.removeWhere((e) => e.internalName == format.internalName);
- _availableFormats.add(format);
- _configDao.updateExportColumn(format);
- }
-
- void delete(LegacyExportColumn format) {
- final existingEntries = _availableFormats.where((element) => (element.internalName == format.internalName) && element.editable);
- assert(existingEntries.isNotEmpty, r"Tried to delete entry that doesn't exist or is not editable.");
- _availableFormats.removeWhere((element) => element.internalName == format.internalName);
- _configDao.deleteExportColumn(format.internalName);
- }
-
- UnmodifiableListView<LegacyExportColumn> get availableFormats => UnmodifiableListView(_availableFormats);
- UnmodifiableMapView<String, LegacyExportColumn> get availableFormatsMap =>
- UnmodifiableMapView(Map.fromIterable(_availableFormats, key: (e) => e.internalName));
-
-
- /// Creates list of rows with that follow the order and format described by [activeExportColumns].
- ///
- /// The [createHeadline] option will create put a row at the start that contains [LegacyExportColumn.internalName] of the
- /// given [activeExportColumns].
- List<List<String>> createTable(Iterable<BloodPressureRecord> data, List<LegacyExportColumn> activeExportColumns,
- {bool createHeadline = true,}) {
- List<List<String>> items = [];
- if (createHeadline) {
- items.add(activeExportColumns.map((e) => e.internalName).toList());
- }
-
- final dataRows = data.map((record) =>
- activeExportColumns.map((attribute) =>
- attribute.formatRecord(record)).toList());
- items.addAll(dataRows);
- return items;
- }
-}
lib/screens/subsettings/export_import/export_column_data.dart
@@ -1,12 +1,14 @@
+/* TODO: rewrite
+
import 'package:blood_pressure_app/components/consistent_future_builder.dart';
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/import_field_type.dart';
import 'package:blood_pressure_app/model/export_options.dart';
import 'package:blood_pressure_app/screens/subsettings/export_import/export_field_format_documentation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-class EditExportColumnPage extends StatefulWidget {
+class EditExportColumnPage extends StatefulWidget {
final String? initialInternalName;
final String? initialDisplayName;
final String? initialFormatPattern;
@@ -190,4 +192,6 @@ class _EditExportColumnPageState extends State<EditExportColumnPage> {
),
);
}
-}
\ No newline at end of file
+}
+
+ */
\ No newline at end of file
lib/main.dart
@@ -12,9 +12,6 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
-@Deprecated('This should not be used for new code, but rather for migrating existing code.')
-late final ConfigDao globalConfigDao;
-
late final ConfigDB _database;
late final BloodPressureModel _bloodPressureModel;
@@ -44,8 +41,6 @@ Future<Widget> _loadApp() async {
await updateLegacySettings(settings, exportSettings, csvExportSettings, pdfExportSettings, intervalStorageManager);
- globalConfigDao = configDao;
-
// Reset the step size intervall to current on startup
intervalStorageManager.mainPage.setToMostRecentIntervall();
test/model/export_import/record_formatter_test.dart
@@ -1,5 +1,5 @@
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/import_field_type.dart';
import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
test/model/config_db_test.dart
@@ -1,5 +1,6 @@
import 'package:blood_pressure_app/model/storage/db/config_dao.dart';
import 'package:blood_pressure_app/model/storage/db/config_db.dart';
+import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
import 'package:blood_pressure_app/model/storage/export_csv_settings_store.dart';
import 'package:blood_pressure_app/model/storage/export_pdf_settings_store.dart';
import 'package:blood_pressure_app/model/storage/export_settings_store.dart';
@@ -30,7 +31,7 @@ void main() {
expect(tableNames.contains(ConfigDB.exportCsvSettingsTable), true);
expect(tableNames.contains(ConfigDB.exportPdfSettingsTable), true);
expect(tableNames.contains(ConfigDB.selectedIntervallStorageTable), true);
- expect(tableNames.contains(ConfigDB.exportStringsTable), true);
+ expect(tableNames.contains(ConfigDB.exportColumnsTable), true);
});
test('should save and load table entries', () async {
final db = await ConfigDB.open(dbPath: inMemoryDatabasePath, isFullPath: true);
@@ -55,13 +56,14 @@ void main() {
test('should create classes when no data is present', () async {
final rawDB = await ConfigDB.open(dbPath: inMemoryDatabasePath, isFullPath: true);
final dao = ConfigDao(rawDB);
-
- expect(await dao.loadExportColumns(), []);
+
expect((await dao.loadSettings(0)).toJson(), Settings().toJson());
expect((await dao.loadExportSettings(0)).toJson(), ExportSettings().toJson());
expect((await dao.loadCsvExportSettings(0)).toJson(), CsvExportSettings().toJson());
expect((await dao.loadPdfExportSettings(0)).toJson(), PdfExportSettings().toJson());
expect((await dao.loadIntervallStorage(0,0)).stepSize, IntervallStorage().stepSize);
+ expect((await dao.loadExportColumnsManager(0)).userColumns, ExportColumnsManager().userColumns);
+ expect((await dao.loadExportColumnsManager(0)), ExportColumnsManager().userColumns);
});
test('should save changes', () async {
final rawDB = await ConfigDB.open(dbPath: inMemoryDatabasePath, isFullPath: true);