Skip to content

Commit

Permalink
Merge pull request #136 from NobodyForNothing/FEAT-readable-pdf-date
Browse files Browse the repository at this point in the history
Make the default record date in PDF readable
  • Loading branch information
derdilla authored Aug 27, 2023
2 parents aa0b53a + fba9ef0 commit 141854e
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 68 deletions.
13 changes: 9 additions & 4 deletions lib/components/export_item_order.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:async';

import 'package:badges/badges.dart' as badges;
import 'package:blood_pressure_app/components/consistent_future_builder.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:blood_pressure_app/screens/subsettings/export_column_data.dart';
Expand Down Expand Up @@ -105,7 +106,6 @@ class _ExportItemsCustomizerState extends State<ExportItemsCustomizer> {

Widget _buildManagePresetsBadge(BuildContext context, ExportConfigurationModel result, {required Widget child}) {
final exportConfigurations = result.exportConfigurations;
final exportConfigurationKeys = exportConfigurations.keys.toList();
return badges.Badge(
position: badges.BadgePosition.topEnd(top: 3, end: 3),
badgeStyle: badges.BadgeStyle(
Expand All @@ -116,13 +116,18 @@ class _ExportItemsCustomizerState extends State<ExportItemsCustomizer> {
icon: const Icon(Icons.collections_bookmark),
itemBuilder: (BuildContext context) {
return [
for (var i = 0; i< exportConfigurationKeys.length; i++)
PopupMenuItem<int>(value: i, child: Text(exportConfigurationKeys[i])),
for (var i = 0; i< exportConfigurations.length; i++)
PopupMenuItem<int>(value: i, child: Text(exportConfigurations[i].$1)),
];
},
onSelected: (value) {
final settings = Provider.of<Settings>(context, listen: false);
settings.exportItems = exportConfigurations[exportConfigurationKeys[value]]!;
if (settings.exportFormat == ExportFormat.csv) {
settings.exportItemsCsv = exportConfigurations[value].$2;
} else {
assert(settings.exportFormat == ExportFormat.pdf);
settings.exportItemsPdf = exportConfigurations[value].$2;
}
},
),
child: child,
Expand Down
4 changes: 2 additions & 2 deletions lib/model/export_import.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -179,7 +179,7 @@ class ExportFileCreator {
}

pw.Widget _buildPdfTable(List<BloodPressureRecord> data, DateFormat dateFormatter) {
final tableData = exportColumnsConfig.createTable(data, true);
final tableData = exportColumnsConfig.createTable(data, ExportFormat.pdf, createHeadline: true);

return pw.TableHelper.fromTextArray(
border: null,
Expand Down
60 changes: 30 additions & 30 deletions lib/model/export_options.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
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';
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;

Expand All @@ -17,11 +23,12 @@ class ExportConfigurationModel {

final List<ExportColumn> _availableFormats = [];

Map<String, List<String>> get exportConfigurations => {
// 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'],
};
/// Format: (title, List<internalNameOfExportFormat>)
List<(String, List<String>)> get exportConfigurations => [
// Not fully localized, as potential 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']),
];

ExportConfigurationModel._create(this.settings, this.localizations);
Future<void> _asyncInit(String? dbPath, bool isFullPath) async {
Expand Down Expand Up @@ -54,12 +61,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() => [
Expand Down Expand Up @@ -118,30 +133,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) {
List<ExportColumn> exportItems;
if (settings.exportCustomEntries) {
exportItems = getActiveExportColumns();
} else {
exportItems = getDefaultFormates().where((e) => ['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;
}
}
Expand Down
35 changes: 31 additions & 4 deletions lib/model/ram_only_implementations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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;
Expand Down
44 changes: 36 additions & 8 deletions lib/model/settings_store.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -48,6 +49,14 @@ class Settings extends ChangeNotifier {
if (keys.contains('exportAddableItems')) {
toAwait.add(_prefs.remove('exportAddableItems'));
}
if (keys.contains('exportCustomEntries')) {
await _prefs.setBool('exportCustomEntriesCsv', _prefs.getBool('exportCustomEntries') ?? false);
toAwait.add(_prefs.remove('exportCustomEntries'));
}
if (keys.contains('exportItems')) {
await _prefs.setStringList('exportItemsCsv', _prefs.getStringList('exportItems') ?? ExportFields.defaultCsv);
toAwait.add(_prefs.remove('exportItems'));
}

// reset variables for new version. Necessary for reusing variable names in new version and avoid having unexpected
// breaking values in the preferences
Expand Down Expand Up @@ -452,21 +461,21 @@ class Settings extends ChangeNotifier {
notifyListeners();
}

bool get exportCustomEntries {
return _prefs.getBool('exportCustomEntries') ?? false;
bool get exportCustomEntriesCsv {
return _prefs.getBool('exportCustomEntriesCsv') ?? false;
}

set exportCustomEntries(bool value) {
_prefs.setBool('exportCustomEntries', value);
set exportCustomEntriesCsv(bool value) {
_prefs.setBool('exportCustomEntriesCsv', value);
notifyListeners();
}

List<String> get exportItems {
return _prefs.getStringList('exportItems') ?? ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes'];
List<String> get exportItemsCsv {
return _prefs.getStringList('exportItemsCsv') ?? ExportFields.defaultCsv;
}

set exportItems(List<String> value) {
_prefs.setStringList('exportItems', value);
set exportItemsCsv(List<String> value) {
_prefs.setStringList('exportItemsCsv', value);
notifyListeners();
}

Expand Down Expand Up @@ -569,6 +578,7 @@ class Settings extends ChangeNotifier {
notifyListeners();
}

/// whether to add a section with all entries to pdf export
bool get exportPdfExportData {
return _prefs.getBool('exportPdfExportData') ?? true;
}
Expand All @@ -586,6 +596,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 {
Expand Down
45 changes: 33 additions & 12 deletions lib/screens/subsettings/export_import_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,17 @@ class _ExportFieldCustomisationSettingState extends State<ExportFieldCustomisati
future: _future!,
onData: (context, result) {
return Consumer<Settings>(builder: (context, settings, child) {
/// whether or not the currently selected export format supports field customization
final isApplicable = (settings.exportFormat == ExportFormat.csv || settings.exportFormat == ExportFormat.pdf &&
settings.exportPdfExportData);
final exportCustomEntries = (settings.exportFormat == ExportFormat.csv) ?
settings.exportCustomEntriesCsv : settings.exportCustomEntriesPdf;
final exportItems = (settings.exportFormat == ExportFormat.csv) ? settings.exportItemsCsv : settings.exportItemsPdf;

final formats = result.availableFormats.toSet();
List<ExportColumn> activeFields = [];
List<ExportColumn> hiddenFields = [];
for (final internalName in settings.exportItems) {
for (final internalName in exportItems) {
activeFields.add(formats.singleWhere((e) => e.internalName == internalName));
formats.removeWhere((e) => e.internalName == internalName);
}
Expand All @@ -205,20 +212,30 @@ class _ExportFieldCustomisationSettingState extends State<ExportFieldCustomisati
children: [
SwitchSettingsTile(
title: Text(localizations.exportCustomEntries),
initialValue: settings.exportCustomEntries,
disabled: !(settings.exportFormat == ExportFormat.csv || settings.exportFormat == ExportFormat.pdf &&
settings.exportPdfExportData),
initialValue: exportCustomEntries,
disabled: !isApplicable,
onToggle: (value) {
settings.exportCustomEntries = value;
if (settings.exportFormat == ExportFormat.csv) {
settings.exportCustomEntriesCsv = value;
} else {
assert(settings.exportFormat == ExportFormat.pdf);
settings.exportCustomEntriesPdf = value;
}

}
),
(settings.exportCustomEntries && (settings.exportFormat == ExportFormat.csv || settings.exportFormat == ExportFormat.pdf &&
settings.exportPdfExportData)) ?
(exportCustomEntries && isApplicable) ?
ExportItemsCustomizer(
shownItems: activeFields,
disabledItems: hiddenFields,
onReorder: (exportItems, exportAddableItems) {
settings.exportItems = exportItems.map((e) => e.internalName).toList();
if (settings.exportFormat == ExportFormat.csv) {
settings.exportItemsCsv = exportItems.map((e) => e.internalName).toList();
} else {
assert(settings.exportFormat == ExportFormat.pdf);
settings.exportItemsPdf = exportItems.map((e) => e.internalName).toList();
}

},
) : const SizedBox.shrink()
],
Expand Down Expand Up @@ -284,17 +301,21 @@ class _ExportWarnBannerState extends State<ExportWarnBanner> {
final localizations = AppLocalizations.of(context)!;
String? message;
return Consumer<Settings>(builder: (context, settings, child) {
final exportItems = (settings.exportFormat == ExportFormat.csv) ? settings.exportItemsCsv : settings.exportItemsPdf;
final exportCustomEntries = (settings.exportFormat == ExportFormat.csv) ?
settings.exportCustomEntriesCsv : settings.exportCustomEntriesPdf;

if (_showWarnBanner && ![ExportFormat.csv, ExportFormat.db].contains(settings.exportFormat) ||
settings.exportCsvHeadline == false ||
settings.exportCustomEntries && !(['timestampUnixMs'].any((i) => settings.exportItems.contains(i))) ||
exportCustomEntries && !(['timestampUnixMs'].any((i) => exportItems.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 && exportCustomEntries &&
!(['systolic','diastolic', 'pulse', 'notes'].every((i) => exportItems.contains(i)))) {
var missingAttributes = {'systolic','diastolic', 'pulse', 'notes'};
missingAttributes.removeWhere((e) => settings.exportItems.contains(e));
missingAttributes.removeWhere((e) => exportItems.contains(e));

message = localizations.exportWarnNotEveryFieldExported(missingAttributes.length, missingAttributes.toString());
}
Expand Down
Loading

0 comments on commit 141854e

Please sign in to comment.