Commit 896ae7a
Changed files (5)
lib
model
export_import
screens
subsettings
lib/model/export_import/csv_converter.dart
@@ -39,7 +39,7 @@ class CsvConverter {
return csvCreator.convert(table);
}
- /// Attempts to parse a csv string.
+ /// Attempts to parse a csv string automatically.
///
/// Validates that the first line of the file contains columns present
/// in [availableColumns]. When a column is present multiple times only
@@ -60,25 +60,27 @@ class CsvConverter {
}
// Convert data to records.
- return parseRecords(lines.map((e) => e.cast<String>()).toList(), titles, columns);
+ return parseRecords(lines, titles, columns);
}
- /// Returns
- List<List<dynamic>> getCsvLines(String csvString) {
+ /// Parses lines from csv files according to settings.
+ ///
+ /// Works around different EOL types n
+ List<List<String>> getCsvLines(String csvString) {
final converter = CsvToListConverter(
fieldDelimiter: settings.fieldDelimiter,
textDelimiter: settings.textDelimiter,
shouldParseNumbers: false,
);
- final csvLines = converter.convert(csvString, eol: '\r\n');
- if (csvLines.length < 2) return converter.convert(csvString, eol: '\n');
+ final csvLines = converter.convert<String>(csvString, eol: '\r\n');
+ if (csvLines.length < 2) return converter.convert<String>(csvString, eol: '\n');
return csvLines;
}
/// Map column names in the first csv-line to matching [ExportColumn].
- Map<String, ExportColumn> getColumns(List<String> firstLine) {
+ Map<String, ExportColumn> getColumns(List<String> headline) {
final Map<String, ExportColumn> columns = {};
- for (final titleText in firstLine) {
+ for (final titleText in headline) {
final formattedTitleText = titleText.trim();
final column = availableColumns.firstWhere(
(c) => c.csvTitle == formattedTitleText
lib/model/export_import/csv_record_parsing_actor.dart
@@ -1,27 +1,50 @@
import 'package:blood_pressure_app/model/export_import/column.dart';
+import 'package:blood_pressure_app/model/export_import/csv_converter.dart';
+import 'package:blood_pressure_app/model/export_import/record_parsing_result.dart';
-/// A intermediate class that allows to manage record parsing.
+/// A intermediate class usable by UI components to drive csv parsing.
class CsvRecordParsingActor {
/// Create an intermediate object to manage a record parsing process.
- CsvRecordParsingActor(this.csvString) {
-
+ CsvRecordParsingActor(this._converter, String csvString) {
+ final lines = _converter.getCsvLines(csvString);
+ _headline = lines.removeAt(0);
+ dataLines = lines;
+ _columnNames = _headline ?? [];
+ _columnParsers = _converter.getColumns(_columnNames);
}
- /// The initial csv String provided to the parser
- final String csvString;
+ final CsvConverter _converter;
+
+ /// All lines without the first line.
+ late final List<List<String>> dataLines;
+
+ List<String>? _headline;
+
+ /// The first line in the csv file.
+ List<String>? get headline => _headline;
+
+ late List<String> _columnNames;
/// All columns defined in the csv headline.
- List<String> columnNames;
+ List<String> get columnNames => _columnNames;
+
+ late Map<String, ExportColumn> _columnParsers;
/// The current interpretation of columns in the csv data.
///
/// There is no guarantee that every column in [columnNames] has a parser.
- Map<String, ExportColumn> columnParsers;
+ Map<String, ExportColumn> get columnParsers => _columnParsers;
- final List<void Function(CsvRecordParsingActor)> _listeners = [];
+ /// Override a columns with a custom one.
+ void changeColumnParser(String columnName, ExportColumn parser) {
+ assert(_columnNames.contains(columnName));
+ _columnParsers[columnName] = parser;
+ }
- /// Register a listener that gets called once a value gets changed.
- void addListener(void Function(CsvRecordParsingActor state) listener)
- => _listeners.add(listener);
+ /// Try to parse the data with the current configuration.
+ RecordParsingResult attemptParse() =>
+ _converter.parseRecords(dataLines, columnNames, columnParsers, false);
}
+
+// TODO: consider joining headline and columnNames OR support no headline.
lib/screens/subsettings/export_import/export_import_screen.dart
@@ -14,6 +14,7 @@ import 'package:provider/provider.dart';
/// Screen to configure and perform exports and imports of blood pressure values.
class ExportImportScreen extends StatelessWidget {
+ /// Create a screen that shows options for ex- and importing data.
const ExportImportScreen({super.key});
@override
lib/screens/subsettings/foreign_db_import_screen.dart
@@ -19,8 +19,8 @@ class ForeignDBImportScreen extends StatefulWidget {
/// Create a screen to import data from a database with unknown structure.
///
/// Parses selected data to a [BloodPressureRecord] list.
- const ForeignDBImportScreen({super.key,
- required this.db,
+ const ForeignDBImportScreen({super.key,
+ required this.db,
required this.bottomAppBars,
});
@@ -56,7 +56,7 @@ class _ForeignDBImportScreenState extends State<ForeignDBImportScreen> {
} else {
return RowDataFieldType.values
.whereNot((e) => e == RowDataFieldType.timestamp
- || selections.contains(e),)
+ || selections.contains(e),)
.map((e) => e.localize(localizations))
.toList();
}
@@ -149,7 +149,7 @@ class _ForeignDBImportScreenState extends State<ForeignDBImportScreen> {
class _ColumnImportData {
_ColumnImportData._create(this.columns);
-
+
static Future<_ColumnImportData> loadFromDB(Database db) async {
final engine = SqlEngine();
@@ -169,7 +169,7 @@ class _ColumnImportData {
if (colNames.isNotEmpty) columns[rootNode.tableName] = colNames;
}
- }
+ }
return _ColumnImportData._create(columns);
}
@@ -184,7 +184,7 @@ class _ColumnImportData {
Future<List<BloodPressureRecord>?> showForeignDBImportDialoge(
BuildContext context,
bool bottomAppBars,
- Database db) =>
+ Database db,) =>
showDialog<List<BloodPressureRecord>>(
context: context, builder: (context) =>
Dialog.fullscreen(
pubspec.lock
@@ -337,26 +337,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
- sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
+ sha256: f8cdf1383f5b4672a2693d875f1f239af6bd7e4a8925a17ef7219226db932624
url: "https://pub.dev"
source: hosted
- version: "10.0.0"
+ version: "10.0.1"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
- sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
+ sha256: a2055640bf5bc903475e4bbdb34e04f8bf698542bee41edec47d337a5939e1ae
url: "https://pub.dev"
source: hosted
- version: "2.0.1"
+ version: "2.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
- sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
+ sha256: e62042d479c4c139dd774125ed4dfbde646b8f07ac228e3c1b57a3d91d6d9df4
url: "https://pub.dev"
source: hosted
- version: "2.0.1"
+ version: "2.0.2"
lints:
dependency: transitive
description:
@@ -868,4 +868,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.2.0 <4.0.0"
- flutter: ">=3.16.0"
+ flutter: ">=3.18.0-18.0.pre.54"