Commit ef2aca9

derdilla <derdilla06@gmail.com>
2023-05-05 17:47:24
FEAT: import csv files
1 parent 9dbd46c
lib/model/blood_pressure.dart
@@ -1,5 +1,7 @@
 import 'dart:collection';
 import 'dart:io';
+import 'package:csv/csv.dart';
+import 'package:file_picker/file_picker.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:path/path.dart';
@@ -8,7 +10,6 @@ import 'package:sqflite/sqflite.dart';
 import 'package:file_saver/file_saver.dart';
 import 'package:share_plus/share_plus.dart';
 import 'dart:convert' show utf8;
-import 'dart:typed_data';
 
 class BloodPressureModel extends ChangeNotifier {
   static const maxEntries = 2E64; // https://www.sqlite.org/limits.html Nr.13
@@ -56,14 +57,27 @@ class BloodPressureModel extends ChangeNotifier {
   Future<void> add(BloodPressureRecord measurement) async {
     assert(_database.isOpen);
 
-    _database.rawInsert('INSERT INTO bloodPressureModel(timestamp, systolic, diastolic, pulse, notes) VALUES (?, ?, ?, ?, ?)', [
-      measurement.creationTime.millisecondsSinceEpoch,
-      measurement.systolic,
-      measurement.diastolic,
-      measurement.pulse,
-      measurement.notes]);
+    var existing = _database.query('bloodPressureModel', where: 'timestamp = ?',
+        whereArgs: [measurement.creationTime.millisecondsSinceEpoch]);
+    if ((await existing).isNotEmpty) {
+        await _database.update('bloodPressureModel', {
+          'systolic': measurement.systolic,
+          'diastolic': measurement.diastolic,
+          'pulse': measurement.pulse,
+          'notes': measurement.notes
+        }, where: 'timestamp = ?',
+            whereArgs: [measurement.creationTime.millisecondsSinceEpoch]);
+    } else {
+      await _database.insert('bloodPressureModel', {
+        'timestamp': measurement.creationTime.millisecondsSinceEpoch,
+        'systolic': measurement.systolic,
+        'diastolic': measurement.diastolic,
+        'pulse': measurement.pulse,
+        'notes': measurement.notes
+      });
+    }
 
-    _cacheLast(); // hast notifyListeners()
+    _cacheLast(); // contains notifyListeners()
   }
 
   /// Returns the last x BloodPressureRecords from new to old.
@@ -106,7 +120,8 @@ class BloodPressureModel extends ChangeNotifier {
     return UnmodifiableListView(recordsInRange);
   }
 
-  Future<void> save(BuildContext context) async { // TODO: passing context is not clean keep UI code out of model
+  Future<void> save(BuildContext context) async {
+    // TODO: passing context is not clean keep UI code out of model
     // create csv
     String csvData = 'timestampUnixMs, systolic, diastolic, pulse, notes\n';
     List<Map<String, Object?>> allEntries = await _database.query('bloodPressureModel',
@@ -132,10 +147,43 @@ class BloodPressureModel extends ChangeNotifier {
     } else {
 
     }
+  }
 
+  Future<void> import(void Function(bool, String?) callback) async {
+    var result = await FilePicker.platform.pickFiles(
+        allowMultiple: false,
+        withData: true,
+    );
 
+    if (result != null) {
+      var binaryContent = result.files.single.bytes;
+      print(binaryContent);
+      if (binaryContent != null) {
+        final csvContents = const CsvToListConverter().convert(
+            String.fromCharCodes(binaryContent),
+            fieldDelimiter: ',',
+            textDelimiter: '"',
+            eol: '\n'
+        );
+        for (var i = 1; i < csvContents.length; i++) {
+          var line = csvContents[i];
+          BloodPressureRecord record = BloodPressureRecord(
+              DateTime.fromMillisecondsSinceEpoch(line[0] as int),
+              (line[1] as int),
+              (line[2] as int),
+              (line[3] as int),
+              line[4]);
+          add(record);
+        }
+        return callback(true, null);
+
+      } else {
+        return callback(false, 'empty file');
+      }
+    } else {
+      return callback(false, 'no file opened');
+    }
   }
-  
 
 /* TODO:
   - bool deleteFromTime (timestamp)
@@ -166,4 +214,4 @@ class BloodPressureRecord {
       return 0;
     }
   }
-}
\ No newline at end of file
+}
lib/screens/settings.dart
@@ -75,6 +75,18 @@ class SettingsScreen extends StatelessWidget {
                     leading: const Icon(Icons.save),
                     onPressed: (context) =>  Provider.of<BloodPressureModel>(context, listen: false).save(context),
                   ),
+                  SettingsTile(
+                    title: const Text('import'),
+                    leading: const Icon(Icons.file_upload),
+                    onPressed: (context) =>  Provider.of<BloodPressureModel>(context, listen: false).import((res, String? err) {
+                      if (res) {
+
+                      } else {
+                        ScaffoldMessenger.of(context).showSnackBar(
+                            SnackBar(content: Text('Error: ${err ?? 'unknown error'}')));
+                      }
+                    }),
+                  ),
                 ],
               )
             ]);
pubspec.lock
@@ -57,6 +57,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "3.0.2"
+  csv:
+    dependency: "direct main"
+    description:
+      name: csv
+      sha256: "016b31a51a913744a0a1655c74ff13c9379e1200e246a03d96c81c5d9ed297b5"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.2"
   cupertino_icons:
     dependency: "direct main"
     description:
@@ -97,6 +105,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "6.1.4"
+  file_picker:
+    dependency: "direct main"
+    description:
+      name: file_picker
+      sha256: e6c7ad8e572379df86ea64ef0a5395889fba3954411d47ca021b888d79f8e798
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.2.11"
   file_saver:
     dependency: "direct main"
     description:
@@ -134,6 +150,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.1.0+2"
+  flutter_plugin_android_lifecycle:
+    dependency: transitive
+    description:
+      name: flutter_plugin_android_lifecycle
+      sha256: "8ffe990dac54a4a5492747added38571a5ab474c8e5d196809ea08849c69b1bb"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.13"
   flutter_test:
     dependency: "direct dev"
     description: flutter
pubspec.yaml
@@ -44,6 +44,8 @@ dependencies:
   share_plus: ^4.5.3
   settings_ui: ^2.0.2
   flutter_material_color_picker: ^1.1.0+2
+  file_picker: ^5.2.11
+  csv: ^5.0.2
 
 dev_dependencies:
   flutter_test: