Commit 08d29db

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2023-09-24 15:50:15
add more insightful import error messages
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent ab4f6a2
Changed files (3)
lib/model/export_import.dart
@@ -51,7 +51,7 @@ class ExportFileCreator {
         try {
           return parseCSVFile(data);
         } catch (e) {
-          return null;
+          return Future.error(e);
         }
       case ExportFormat.pdf:
         return null;
@@ -84,6 +84,7 @@ class ExportFileCreator {
     for (var lineIndex = 0; lineIndex < csvLines.length; lineIndex++) {
       // get values from columns
       int? timestamp, sys, dia, pul;
+      Color? color;
       String? notes;
       for (var attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++) {
         if (timestamp != null && sys != null && dia !=null && pul != null) continue; // optimization
@@ -119,6 +120,8 @@ class ExportFileCreator {
               assert(parsedRecordDataType.$2 is int?);
               timestamp ??= parsedRecordDataType.$2 as int?;
               break;
+            case RowDataFieldType.color:
+              color ??= parsedRecordDataType.$2 as Color?;
           }
         }
       }
@@ -127,7 +130,8 @@ class ExportFileCreator {
       if (timestamp == null) {
         throw ArgumentError('File didn\'t save timestamps');
       }
-      records.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(timestamp), sys, dia, pul, notes ?? ''));
+      records.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(timestamp), sys, dia, pul, notes ?? '',
+          needlePin: (color == null) ? null : MeasurementNeedlePin(color)));
     }
     return records;
   }
@@ -307,8 +311,17 @@ class Exporter {
     var path = result.files.single.path;
     assert(path != null); // null state directly linked to binary content
 
-    var fileContents = await ExportFileCreator(settings, localizations, theme, exportColumnsConfig).parseFile(path! ,binaryContent);
-    if (fileContents == null) {
+    final fileContentsFuture = ExportFileCreator(settings, localizations, theme, exportColumnsConfig).parseFile(path! ,binaryContent);
+    Object? fileContentsError;
+    fileContentsFuture.onError((error, stackTrace) {
+      fileContentsError = error;
+      return null;
+    });
+    var fileContents = await fileContentsFuture;
+    if (fileContentsError != null) {
+      messenger.showSnackBar(SnackBar(content: Text(localizations.error(fileContentsError.toString()))));
+      return;
+    } else if (fileContents == null) {
       messenger.showSnackBar(SnackBar(content: Text(localizations.errNotImportable)));
       return;
     }
lib/model/export_options.dart
@@ -4,6 +4,7 @@ import 'package:blood_pressure_app/main.dart';
 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/material.dart';
 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 import 'package:function_tree/function_tree.dart';
 import 'package:intl/intl.dart';
@@ -27,7 +28,7 @@ class ExportConfigurationModel {
   /// 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']),
+    (localizations.default_, ['timestampUnixMs', 'systolic', 'diastolic', 'pulse', 'notes', 'color']),
     ('"My Heart" export', ['DATUM', 'SYSTOLE', 'DIASTOLE', 'PULS', 'Beschreibung', 'Tags', 'Gewicht', 'Sauerstoffsättigung']),
   ];
 
@@ -86,6 +87,7 @@ class ExportConfigurationModel {
     ExportColumn(internalName: 'pulse', columnTitle: localizations.pulLong, formatPattern: r'$PUL', editable: false),
     ExportColumn(internalName: 'notes', columnTitle: localizations.notes, formatPattern: r'$NOTE', editable: false),
     ExportColumn(internalName: 'pulsePressure', columnTitle: localizations.pulsePressure, formatPattern: r'{{$SYS-$DIA}}', editable: false),
+    ExportColumn(internalName: 'color', columnTitle: localizations.color, formatPattern: r'$COLOR', editable: false),
 
     ExportColumn(internalName: 'DATUM', columnTitle: '"My Heart" export time', formatPattern: r'$FORMAT{$TIMESTAMP,yyyy-MM-dd HH:mm:ss}', editable: false, hidden: true),
     ExportColumn(internalName: 'SYSTOLE', columnTitle: '"My Heart" export sys', formatPattern: r'$SYS', editable: false, hidden: true),
@@ -201,6 +203,7 @@ class ExportColumn {
     fieldContents = fieldContents.replaceAll(r'$DIA', record.diastolic.toString());
     fieldContents = fieldContents.replaceAll(r'$PUL', record.pulse.toString());
     fieldContents = fieldContents.replaceAll(r'$NOTE', record.notes.toString());
+    fieldContents = fieldContents.replaceAll(r'$COLOR', record.needlePin?.color.value.toString() ?? '');
 
     // math
     fieldContents = fieldContents.replaceAllMapped(RegExp(r'\{\{([^}]*)}}'), (m) {
@@ -227,6 +230,12 @@ class ExportColumn {
     if (!isReversible || formattedRecord == 'null') return [];
 
     if (formatPattern == r'$NOTE') return [(RowDataFieldType.notes, formattedRecord)];
+    if (formatPattern == r'$COLOR') {
+      final value = int.tryParse(formattedRecord);
+      print(value);
+      print(formattedRecord);
+      return value == null ? [] : [(RowDataFieldType.color, Color(value))];
+    }
 
     // records are parse by replacing the values with capture groups
     final types = RegExp(r'\$(TIMESTAMP|SYS|DIA|PUL)').allMatches(formatPattern).map((e) => e.group(0)).toList();
@@ -262,13 +271,14 @@ class ExportColumn {
   /// 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 {
-    return (formatPattern == r'$TIMESTAMP') ||
+    return (formatPattern == r'$TIMESTAMP') || (formatPattern == r'$COLOR') ||
         formatPattern.contains(RegExp(r'\$(SYS|DIA|PUL|NOTE)')) && !formatPattern.contains(RegExp(r'[{},]'));
   }
 
   RowDataFieldType? get parsableFormat {
     if (formatPattern.contains(RegExp(r'[{},]'))) return null;
     if (formatPattern == r'$TIMESTAMP') return RowDataFieldType.timestamp;
+    if (formatPattern == r'$COLOR') return RowDataFieldType.color;
     if (formatPattern.contains(RegExp(r'\$(SYS)'))) return RowDataFieldType.sys;
     if (formatPattern.contains(RegExp(r'\$(DIA)'))) return RowDataFieldType.dia;
     if (formatPattern.contains(RegExp(r'\$(PUL)'))) return RowDataFieldType.pul;
@@ -283,7 +293,7 @@ class ExportColumn {
 }
 
 enum RowDataFieldType {
-  timestamp, sys, dia, pul, notes;
+  timestamp, sys, dia, pul, notes, color;
 
   @override
   String toString() {
@@ -298,6 +308,8 @@ enum RowDataFieldType {
         return gLocalizations.pulLong;
       case 4:
         return gLocalizations.notes;
+      case 5:
+        return gLocalizations.color;
       default:
         return "unknown";
     };
lib/screens/subsettings/export_import_screen.dart
@@ -303,8 +303,7 @@ class ExportImportButtons extends StatelessWidget {
                           messenger,
                           localizations,
                           theme,
-                          await ExportConfigurationModel.get(
-                              settings, localizations))
+                          await ExportConfigurationModel.get(settings, localizations))
                       .import(),
                 )),
           ],
@@ -347,7 +346,8 @@ class _ExportWarnBannerState extends State<ExportWarnBanner> {
               RowDataFieldType.sys,
               RowDataFieldType.dia,
               RowDataFieldType.pul,
-              RowDataFieldType.notes
+              RowDataFieldType.notes,
+              RowDataFieldType.color
             };
             missingAttributes.removeWhere((e) => exportFormats.contains(e));
             if (_showWarnBanner &&