Commit 0f3d92d
Changed files (7)
lib
components
screens
subsettings
test
model
lib/components/export_item_order.dart
@@ -10,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
+// TODO: save item order to pdf / csv depending on selection
class ExportItemsCustomizer extends StatefulWidget {
final List<ExportColumn> shownItems;
final List<ExportColumn> disabledItems;
@@ -122,7 +123,7 @@ class _ExportItemsCustomizerState extends State<ExportItemsCustomizer> {
},
onSelected: (value) {
final settings = Provider.of<Settings>(context, listen: false);
- settings.exportItems = exportConfigurations[exportConfigurationKeys[value]]!;
+ settings.exportItemsCsv = exportConfigurations[exportConfigurationKeys[value]]!;
},
),
child: child,
lib/model/export_import.dart
@@ -61,7 +61,7 @@ class ExportFileCreator {
}
Uint8List createCSVFile(List<BloodPressureRecord> records) {
- final items = exportColumnsConfig.createTable(records, settings.exportCsvHeadline);
+ final items = exportColumnsConfig.createTable(records, ExportFormat.csv, createHeadline: settings.exportCsvHeadline);
final converter = ListToCsvConverter(fieldDelimiter: settings.csvFieldDelimiter, textDelimiter: settings.csvTextDelimiter);
final csvData = converter.convert(items);
return Uint8List.fromList(utf8.encode(csvData));
@@ -179,7 +179,7 @@ class ExportFileCreator {
}
pw.Widget _buildPdfTable(List<BloodPressureRecord> data, DateFormat dateFormatter) {
- final tableData = exportColumnsConfig.createTable(data, true, true);
+ final tableData = exportColumnsConfig.createTable(data, ExportFormat.pdf, createHeadline: true);
return pw.TableHelper.fromTextArray(
border: null,
lib/model/export_options.dart
@@ -1,6 +1,7 @@
import 'dart:collection';
import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:blood_pressure_app/model/export_import.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:function_tree/function_tree.dart';
@@ -8,6 +9,11 @@ import 'package:intl/intl.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
+class ExportFields {
+ static const defaultCsv = ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes'];
+ static const defaultPdf = ['formattedTimestamp','systolic','diastolic','pulse','notes'];
+}
+
class ExportConfigurationModel {
static ExportConfigurationModel? _instance;
@@ -17,7 +23,7 @@ class ExportConfigurationModel {
final List<ExportColumn> _availableFormats = [];
- Map<String, List<String>> get exportConfigurations => {
+ Map<String, List<String>> get exportConfigurations => { // todo change type to List<String, List<String> and add pdf and csv seperately or remove entirely
// Not fully localized, as potemtial user added configurations can't be localized as well
localizations.default_: ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes'],
'"My Heart" export': ['DATUM', 'SYSTOLE', 'DIASTOLE', 'PULS', 'Beschreibung', 'Tags', 'Gewicht', 'Sauerstoffsättigung'],
@@ -54,12 +60,20 @@ class ExportConfigurationModel {
return _instance!;
}
- List<ExportColumn> getActiveExportColumns() {
- List<ExportColumn> activeFields = [];
- for (final internalName in settings.exportItems) {
- activeFields.add(availableFormats.singleWhere((e) => e.internalName == internalName));
+ List<ExportColumn> _getActiveExportColumns(ExportFormat format) {
+ switch (format) {
+ case ExportFormat.csv:
+ return availableFormats.where((e) =>
+ ((settings.exportCustomEntriesCsv) ? settings.exportItemsCsv : ExportFields.defaultCsv)
+ .contains(e.internalName)).toList();
+ case ExportFormat.pdf:
+ return availableFormats.where((e) =>
+ ((settings.exportCustomEntriesPdf) ? settings.exportItemsPdf : ExportFields.defaultPdf)
+ .contains(e.internalName)).toList();
+ default:
+ assert(false, 'no data selection for this one');
+ return [];
}
- return activeFields;
}
List<ExportColumn> getDefaultFormates() => [
@@ -118,35 +132,15 @@ class ExportConfigurationModel {
UnmodifiableMapView<String, ExportColumn> get availableFormatsMap =>
UnmodifiableMapView(Map.fromIterable(_availableFormats, key: (e) => e.internalName));
- List<List<String>> createTable(List<BloodPressureRecord> data, bool createHeadline, [bool shouldBeHumanReadable = false]) {
- List<ExportColumn> exportItems;
- if (settings.exportCustomEntries) {
- exportItems = getActiveExportColumns();
- } else {
- // https://github.com/NobodyForNothing/blood-pressure-monitor-fl/issues/131
- // currently doesn't default when setting to manual options, maybe save PDF columns separate from the CSV ones
- exportItems = getDefaultFormates().where((e) => [
- shouldBeHumanReadable ? 'formattedTimestamp' : 'timestampUnixMs'
- ,'systolic','diastolic','pulse','notes'
- ].contains(e.internalName)).toList();
- }
-
+ List<List<String>> createTable(List<BloodPressureRecord> data, ExportFormat format, {bool createHeadline = true,}) {
+ final exportItems = _getActiveExportColumns(format);
List<List<String>> items = [];
if (createHeadline) {
- List<String> headline = [];
- for (var i = 0; i<exportItems.length; i++) {
- headline.add(exportItems[i].internalName);
- }
- items.add(headline);
+ items.add(exportItems.map((e) => e.internalName).toList());
}
- for (var record in data) {
- List<String> row = [];
- for (var attribute in exportItems) {
- row.add(attribute.formatRecord(record));
- }
- items.add(row);
- }
+ final dataRows = data.map((record) => exportItems.map((attribute) => attribute.formatRecord(record)).toList());
+ items.addAll(dataRows);
return items;
}
}
lib/model/ram_only_implementations.dart
@@ -2,6 +2,7 @@ import 'dart:collection';
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/export_import.dart';
+import 'package:blood_pressure_app/model/export_options.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:file_saver/file_saver.dart' show MimeType;
import 'package:flutter/material.dart';
@@ -310,19 +311,19 @@ class RamSettings extends ChangeNotifier implements Settings {
}
@override
- bool get exportCustomEntries => _exportCustomEntries;
+ bool get exportCustomEntriesCsv => _exportCustomEntries;
@override
- set exportCustomEntries(bool value) {
+ set exportCustomEntriesCsv(bool value) {
_exportCustomEntries = value;
notifyListeners();
}
@override
- List<String> get exportItems => _exportItems;
+ List<String> get exportItemsCsv => _exportItems;
@override
- set exportItems(List<String> value) {
+ set exportItemsCsv(List<String> value) {
_exportItems = value;
notifyListeners();
}
@@ -481,6 +482,32 @@ class RamSettings extends ChangeNotifier implements Settings {
notifyListeners();
}
+ bool _exportCustomEntriesPdf = false;
+
+ @override
+ bool get exportCustomEntriesPdf {
+ return _exportCustomEntriesPdf;
+ }
+
+ @override
+ set exportCustomEntriesPdf(bool value) {
+ _exportCustomEntriesPdf = value;
+ notifyListeners();
+ }
+
+ List<String> _exportItemsPdf = ExportFields.defaultPdf;
+
+ @override
+ List<String> get exportItemsPdf {
+ return _exportItemsPdf;
+ }
+
+ @override
+ set exportItemsPdf(List<String> value) {
+ _exportItemsPdf = value;
+ notifyListeners();
+ }
+
@override
void changeStepSize(TimeStep value) {
graphStepSize = value;
lib/model/settings_store.dart
@@ -1,5 +1,6 @@
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/export_import.dart';
+import 'package:blood_pressure_app/model/export_options.dart';
import 'package:file_saver/file_saver.dart' show MimeType;
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -452,20 +453,20 @@ class Settings extends ChangeNotifier {
notifyListeners();
}
- bool get exportCustomEntries {
+ bool get exportCustomEntriesCsv {
return _prefs.getBool('exportCustomEntries') ?? false;
}
- set exportCustomEntries(bool value) {
+ set exportCustomEntriesCsv(bool value) {
_prefs.setBool('exportCustomEntries', value);
notifyListeners();
}
- List<String> get exportItems {
- return _prefs.getStringList('exportItems') ?? ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes'];
+ List<String> get exportItemsCsv {
+ return _prefs.getStringList('exportItems') ?? ExportFields.defaultCsv; // TODO migrate var name
}
- set exportItems(List<String> value) {
+ set exportItemsCsv(List<String> value) {
_prefs.setStringList('exportItems', value);
notifyListeners();
}
@@ -586,6 +587,24 @@ class Settings extends ChangeNotifier {
_prefs.setBool('startWithAddMeasurementPage', value);
notifyListeners();
}
+
+ bool get exportCustomEntriesPdf {
+ return _prefs.getBool('exportCustomEntriesPdf') ?? false;
+ }
+
+ set exportCustomEntriesPdf(bool value) {
+ _prefs.setBool('exportCustomEntriesPdf', value);
+ notifyListeners();
+ }
+
+ List<String> get exportItemsPdf {
+ return _prefs.getStringList('exportItemsPdf') ?? ExportFields.defaultPdf;
+ }
+
+ set exportItemsPdf(List<String> value) {
+ _prefs.setStringList('exportItemsPdf', value);
+ notifyListeners();
+ }
}
enum TimeStep {
lib/screens/subsettings/export_import_screen.dart
@@ -195,7 +195,7 @@ class _ExportFieldCustomisationSettingState extends State<ExportFieldCustomisati
final formats = result.availableFormats.toSet();
List<ExportColumn> activeFields = [];
List<ExportColumn> hiddenFields = [];
- for (final internalName in settings.exportItems) {
+ for (final internalName in settings.exportItemsCsv) {
activeFields.add(formats.singleWhere((e) => e.internalName == internalName));
formats.removeWhere((e) => e.internalName == internalName);
}
@@ -205,20 +205,20 @@ class _ExportFieldCustomisationSettingState extends State<ExportFieldCustomisati
children: [
SwitchSettingsTile(
title: Text(localizations.exportCustomEntries),
- initialValue: settings.exportCustomEntries,
+ initialValue: settings.exportCustomEntriesCsv,
disabled: !(settings.exportFormat == ExportFormat.csv || settings.exportFormat == ExportFormat.pdf &&
settings.exportPdfExportData),
onToggle: (value) {
- settings.exportCustomEntries = value;
+ settings.exportCustomEntriesCsv = value;
}
),
- (settings.exportCustomEntries && (settings.exportFormat == ExportFormat.csv || settings.exportFormat == ExportFormat.pdf &&
+ (settings.exportCustomEntriesCsv && (settings.exportFormat == ExportFormat.csv || settings.exportFormat == ExportFormat.pdf &&
settings.exportPdfExportData)) ?
ExportItemsCustomizer(
shownItems: activeFields,
disabledItems: hiddenFields,
onReorder: (exportItems, exportAddableItems) {
- settings.exportItems = exportItems.map((e) => e.internalName).toList();
+ settings.exportItemsCsv = exportItems.map((e) => e.internalName).toList();
},
) : const SizedBox.shrink()
],
@@ -286,15 +286,15 @@ class _ExportWarnBannerState extends State<ExportWarnBanner> {
return Consumer<Settings>(builder: (context, settings, child) {
if (_showWarnBanner && ![ExportFormat.csv, ExportFormat.db].contains(settings.exportFormat) ||
settings.exportCsvHeadline == false ||
- settings.exportCustomEntries && !(['timestampUnixMs'].any((i) => settings.exportItems.contains(i))) ||
+ settings.exportCustomEntriesCsv && !(['timestampUnixMs'].any((i) => settings.exportItemsCsv.contains(i))) ||
![',', '|'].contains(settings.csvFieldDelimiter) ||
!['"', '\''].contains(settings.csvTextDelimiter)
) {
message = localizations.exportWarnConfigNotImportable;
- } else if (_showWarnBanner && settings.exportCustomEntries &&
- !(['systolic','diastolic', 'pulse', 'notes'].every((i) => settings.exportItems.contains(i)))) {
+ } else if (_showWarnBanner && settings.exportCustomEntriesCsv &&
+ !(['systolic','diastolic', 'pulse', 'notes'].every((i) => settings.exportItemsCsv.contains(i)))) {
var missingAttributes = {'systolic','diastolic', 'pulse', 'notes'};
- missingAttributes.removeWhere((e) => settings.exportItems.contains(e));
+ missingAttributes.removeWhere((e) => settings.exportItemsCsv.contains(e));
message = localizations.exportWarnNotEveryFieldExported(missingAttributes.length, missingAttributes.toString());
}
test/model/settings_test.dart
@@ -44,7 +44,7 @@ void main() {
expect(s.graphTitlesCount, 5);
expect(s.csvFieldDelimiter, ',');
expect(s.csvTextDelimiter, '"');
- expect(s.exportItems, ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes']);
+ expect(s.exportItemsCsv, ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes']);
expect(s.exportCsvHeadline, true);
expect(s.exportMimeType, MimeType.csv);
expect(s.defaultExportDir.isEmpty, true);
@@ -88,7 +88,7 @@ void main() {
s.graphTitlesCount = 7;
s.csvFieldDelimiter = '|';
s.csvTextDelimiter = '\'';
- s.exportItems = ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime'];
+ s.exportItemsCsv = ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime'];
s.exportCsvHeadline = false;
s.exportMimeType = MimeType.pdf;
s.defaultExportDir = '/storage/emulated/0/Android/data/com.derdilla.bloodPressureApp/files/file.csv';
@@ -115,7 +115,7 @@ void main() {
expect(s.graphTitlesCount, 7);
expect(s.csvFieldDelimiter, '|');
expect(s.csvTextDelimiter, '\'');
- expect(s.exportItems, ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime']);
+ expect(s.exportItemsCsv, ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime']);
expect(s.exportCsvHeadline, false);
expect(s.exportMimeType, MimeType.pdf);
expect(s.defaultExportDir, '/storage/emulated/0/Android/data/com.derdilla.bloodPressureApp/files/file.csv');
@@ -153,7 +153,7 @@ void main() {
s.graphTitlesCount = 2;
s.csvFieldDelimiter = '|';
s.csvTextDelimiter = '\'';
- s.exportItems = ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime'];
+ s.exportItemsCsv = ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime'];
s.exportCsvHeadline = false;
s.exportMimeType = MimeType.pdf;
s.defaultExportDir = '/storage/emulated/0/Android/data/com.derdilla.bloodPressureApp/files/file.csv';
@@ -196,7 +196,7 @@ void main() {
expect(s.graphTitlesCount, 5);
expect(s.csvFieldDelimiter, ',');
expect(s.csvTextDelimiter, '"');
- expect(s.exportItems, ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes']);
+ expect(s.exportItemsCsv, ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes']);
expect(s.exportCsvHeadline, true);
expect(s.exportMimeType, MimeType.csv);
expect(s.defaultExportDir.isEmpty, true);
@@ -240,7 +240,7 @@ void main() {
s.graphTitlesCount = 7;
s.csvFieldDelimiter = '|';
s.csvTextDelimiter = '\'';
- s.exportItems = ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime'];
+ s.exportItemsCsv = ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime'];
s.exportCsvHeadline = false;
s.exportMimeType = MimeType.pdf;
s.defaultExportDir = '/storage/emulated/0/Android/data/com.derdilla.bloodPressureApp/files/file.csv';
@@ -267,7 +267,7 @@ void main() {
expect(s.graphTitlesCount, 7);
expect(s.csvFieldDelimiter, '|');
expect(s.csvTextDelimiter, '\'');
- expect(s.exportItems, ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime']);
+ expect(s.exportItemsCsv, ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime']);
expect(s.exportCsvHeadline, false);
expect(s.exportMimeType, MimeType.pdf);
expect(s.defaultExportDir, '/storage/emulated/0/Android/data/com.derdilla.bloodPressureApp/files/file.csv');
@@ -305,7 +305,7 @@ void main() {
s.graphTitlesCount = 2;
s.csvFieldDelimiter = '|';
s.csvTextDelimiter = '\'';
- s.exportItems = ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime'];
+ s.exportItemsCsv = ['systolic', 'diastolic', 'pulse', 'notes', 'isoUTCTime'];
s.exportCsvHeadline = false;
s.exportMimeType = MimeType.pdf;
s.defaultExportDir = '/storage/emulated/0/Android/data/com.derdilla.bloodPressureApp/files/file.csv';