Commit 0f3d92d

derdilla <derdilla06@gmail.com>
2023-08-26 16:48:39
refactor export order storage
1 parent 852144f
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';