Commit 547609b

derdilla <derdilla06@gmail.com>
2023-07-31 14:58:29
add time format to export script language
1 parent b0e13f9
Changed files (2)
lib/components/export_item_order.dart
@@ -1,10 +1,8 @@
 
 import 'dart:async';
 
-import 'package:blood_pressure_app/model/blood_pressure.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:function_tree/function_tree.dart';
 
 class ExportItemsCustomizer extends StatelessWidget {
   final List<String> exportItems;
@@ -117,53 +115,5 @@ class ExportItemsCustomizer extends StatelessWidget {
   }
 }
 
-class ExportColumn {
-  static const _functionRegex = r'\{\{([^}]*)}}';
-
-  /// pure name as in the title of the csv file and for internal purposes. Should not contain special characters and spaces.
-  final String internalColumnName;
-  /// Display title of the column. Possibly localized
-  final String columnTitle;
-  /// Pattern to create the field contents from: TODO implement input and documentation
-  late final String formatPattern;
-  final List<MultiVariableFunction> _parsedFunctions = [];
-
-  /// Example: ExportColumn(internalColumnName: 'pulsePressure', columnTitle: 'Pulse pressure', formatPattern: '{{SYS-DIA}}')
-  ExportColumn({required this.internalColumnName, required this.columnTitle, required String formatPattern}) {
-    this.formatPattern = formatPattern.replaceAll('{{}}', '');
-
-    final mathSnippets = RegExp(_functionRegex).allMatches(this.formatPattern);
-    for (final m in mathSnippets) {
-      assert(m.groupCount == 1, 'If a math block is found content is expected');
-      final function = m.group(0)!.toMultiVariableFunction(['SYS', 'DIA', 'PUL']);
-      _parsedFunctions.add(function);
-    }
-  }
-
-  String formatRecord(BloodPressureRecord record) {
-    var fieldContents = formatPattern;
-
-    int matchIndex = 0;
-    fieldContents = fieldContents.replaceAllMapped(RegExp(_functionRegex), (m) {
-      assert (_parsedFunctions.length > matchIndex);
-      final result = _parsedFunctions[matchIndex].call({
-        'SYS' : record.systolic ?? -1,
-        'DIA': record.diastolic ?? -1,
-        'PUL': record.pulse ?? -1
-      }) as double;
-      matchIndex++;
-      return result.toString();
-    });
-
-    /*
-    fieldContents.replaceAll('\$SYS', record.systolic.toString());
-    fieldContents.replaceAll('\$DIA', record.diastolic.toString());
-    fieldContents.replaceAll('\$PUL', record.pulse.toString());
-    */
-
-    return fieldContents;
-  }
-}
-
 
 // 100 - 80 + (50 / 80 * (100 % 50))
\ No newline at end of file
lib/model/export_options.dart
@@ -0,0 +1,87 @@
+import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:blood_pressure_app/model/settings_store.dart';
+import 'package:function_tree/function_tree.dart';
+import 'package:intl/intl.dart';
+
+class ExportConfigurationModel {
+  List<ExportColumn> getDefaultFormates(Settings settings) => [
+    ExportColumn(internalColumnName: 'timestampUnixMs', columnTitle: 'Unix timestamp', formatPattern: r'$TIMESTAMP'),
+    ExportColumn(internalColumnName: 'formattedTimestamp', columnTitle: 'Time', formatPattern: '\$FORMAT{\$TIMESTAMP,${settings.dateFormatString}}'),
+    ExportColumn(internalColumnName: 'systolic', columnTitle: 'Systolic', formatPattern: r'$SYS'),
+    ExportColumn(internalColumnName: 'diastolic', columnTitle: 'Diastolic', formatPattern: r'$DIA'),
+    ExportColumn(internalColumnName: 'pulse', columnTitle: 'Pulse', formatPattern: r'$PUL'),
+    ExportColumn(internalColumnName: 'notes', columnTitle: 'Notes', formatPattern: r'$NOTE'),
+  ];
+}
+
+class ExportColumn {
+  /// pure name as in the title of the csv file and for internal purposes. Should not contain special characters and spaces.
+  late final String internalColumnName;
+  /// Display title of the column. Possibly localized
+  late final String columnTitle;
+  /// Pattern to create the field contents from: TODO implement user input and documentation
+  /// It supports inserting values for $TIMESTAMP, $SYS $DIA $PUL 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;
+
+  /// Example: ExportColumn(internalColumnName: 'pulsePressure', columnTitle: 'Pulse pressure', formatPattern: '{{$SYS-$DIA}}')
+  ExportColumn({required this.internalColumnName, required this.columnTitle, required String formatPattern}) {
+    this.formatPattern = formatPattern.replaceAll('{{}}', '');
+  }
+
+  ExportColumn.fromJson(Map<String, dynamic> json) {
+    ExportColumn(
+      internalColumnName: json['internalColumnName'],
+      columnTitle: json['columnTitle'],
+      formatPattern: json['formatPattern']
+    );
+  }
+
+  Map<String, dynamic> toJson() => {
+    'internalColumnName': internalColumnName,
+    'columnTitle': columnTitle,
+    'formatPattern': formatPattern
+  };
+
+  String formatRecord(BloodPressureRecord record) {
+    var fieldContents = formatPattern;
+
+    // variables
+    fieldContents = fieldContents.replaceAll(r'$TIMESTAMP', record.creationTime.millisecondsSinceEpoch.toString());
+    fieldContents = fieldContents.replaceAll(r'$SYS', record.systolic.toString());
+    fieldContents = fieldContents.replaceAll(r'$DIA', record.diastolic.toString());
+    fieldContents = fieldContents.replaceAll(r'$PUL', record.pulse.toString());
+    fieldContents = fieldContents.replaceAll(r'$NOTE', record.notes.toString());
+
+    // math
+    fieldContents = fieldContents.replaceAllMapped(RegExp(r'\{\{([^}]*)}}'), (m) {
+      assert(m.groupCount == 1, 'If a math block is found content is expected');
+      final result = m.group(0)!.interpret();
+      return result.toString();
+    });
+
+    // date format
+    fieldContents = fieldContents.replaceAllMapped(RegExp(r'\$FORMAT\{([^}]*)}'), (m) {
+      assert(m.groupCount == 1, 'If a FORMAT block is found a group is expected');
+      final bothArgs = m.group(1)!;
+      int separatorPosition = bothArgs.indexOf(",");
+      final timestamp = DateTime.fromMillisecondsSinceEpoch(int.parse(bothArgs.substring(0,separatorPosition)));
+      final formatPattern = bothArgs.substring(separatorPosition+1);
+      return DateFormat(formatPattern).format(timestamp);
+    });
+
+    return fieldContents;
+  }
+}
\ No newline at end of file