Commit ef2aca9
Changed files (4)
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: