Commit 0dda00d

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-01-23 17:51:24
implement foreign sql table selector
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent bb4267a
Changed files (2)
lib/screens/subsettings/foreign_db_import_screen.dart
@@ -0,0 +1,83 @@
+import 'package:blood_pressure_app/components/consistent_future_builder.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:sqflite/sqflite.dart';
+
+/// Screen to select the columns from a database and annotate types.
+class ForeignDBImportScreen extends StatefulWidget {
+  /// Create a screen to import data from a database with unknown structure.
+  const ForeignDBImportScreen({super.key, required this.db});
+
+  /// Database from which to import data.
+  final Database db;
+
+  @override
+  State<ForeignDBImportScreen> createState() => _ForeignDBImportScreenState();
+}
+
+class _ForeignDBImportScreenState extends State<ForeignDBImportScreen> {
+
+  /// The name of the selected column that contains the timestamps.
+  String? _activeTimeColumnName;
+
+  @override
+  Widget build(BuildContext context) => Scaffold(
+    appBar: AppBar(),
+    body: ConsistentFutureBuilder(
+      future: _ColumnImportData.loadFromDB(widget.db),
+      onData: (BuildContext context, data) {
+        final localizations = AppLocalizations.of(context);
+        return ListView(
+          children: [
+            ListTile(
+              title: Text(
+                'Select table:',
+                style: Theme.of(context).textTheme.titleLarge!,
+              ),
+            ),
+            for (final table in data.tableNames)
+              Card(
+                child: Padding(
+                  padding: const EdgeInsets.all(14),
+                  child: Text(table),
+                ),
+              ),
+            // TODO
+          ],
+        );
+      },
+    ),
+  );
+}
+
+
+class _ColumnImportData {
+  _ColumnImportData._create(this.columns);
+  
+  static Future<_ColumnImportData> loadFromDB(Database db) async {
+    final masterTable = await db.query('sqlite_master',
+      columns: ['name', 'sql'],
+      where: 'type = "table"'
+    );
+    final columns = <String, List<String>?>{};
+    for (final e in masterTable) {
+      final tableName = e['name']!.toString();
+      final creationSql = e['sql']!.toString();
+      final colNames = RegExp(r'CREATE\s+TABLE\s+[0-9\w]+\s*\(([\w\s()0-9,]+?)\)+')
+          .firstMatch(creationSql)
+          ?.group(0)
+          ?.split(',')
+          .map((e) => e.split(' ').first)
+          .toList();
+      assert(colNames != null);
+      columns[tableName] = colNames;
+    } 
+    return _ColumnImportData._create(columns);
+  }
+
+  /// Map of table names and their respective column names.
+  Map<String, List<String>?> columns;
+
+  /// Names of all tables.
+  Iterable<String> get tableNames => columns.keys;
+}
lib/screens/settings_screen.dart
@@ -10,6 +10,7 @@ import 'package:blood_pressure_app/model/storage/storage.dart';
 import 'package:blood_pressure_app/platform_integration/platform_client.dart';
 import 'package:blood_pressure_app/screens/subsettings/delete_data_screen.dart';
 import 'package:blood_pressure_app/screens/subsettings/export_import/export_import_screen.dart';
+import 'package:blood_pressure_app/screens/subsettings/foreign_db_import_screen.dart';
 import 'package:blood_pressure_app/screens/subsettings/graph_markings_screen.dart';
 import 'package:blood_pressure_app/screens/subsettings/medicine_manager_screen.dart';
 import 'package:blood_pressure_app/screens/subsettings/version_screen.dart';
@@ -307,9 +308,7 @@ class SettingsPage extends StatelessWidget {
                     leading: const Icon(Icons.settings_backup_restore),
                     onTap: () async {
                       final messenger = ScaffoldMessenger.of(context);
-                      final result = await FilePicker.platform.pickFiles(
-                        
-                      );
+                      final result = await FilePicker.platform.pickFiles();
                       if (result == null) {
                         messenger.showSnackBar(SnackBar(content: Text(localizations.errNoFileOpened)));
                         return;
@@ -341,6 +340,31 @@ class SettingsPage extends StatelessWidget {
                       );
                     },
                 ),
+                ListTile(
+                  title: Text('TODO'), // TODO
+                  leading: Icon(Icons.add_circle, color: Colors.red,),
+                  onTap: () async {
+                    final messenger = ScaffoldMessenger.of(context);
+                    final navigator = Navigator.of(context);
+                    final result = await FilePicker.platform.pickFiles();
+
+                    if (result == null) {
+                      messenger.showSnackBar(SnackBar(content: Text(localizations.errNoFileOpened)));
+                      return;
+                    }
+                    final path = result.files.single.path;
+                    if (path == null) {
+                      messenger.showSnackBar(SnackBar(content: Text(localizations.errCantReadFile)));
+                      return;
+                    } // TODO: stop duplicating file selection code
+
+                    final db = await openDatabase(path);
+
+                    navigator.push(MaterialPageRoute(builder: (context) =>
+                          ForeignDBImportScreen(db: db,),),
+                    );
+                  },
+                )
               ],
             ),
             TitledColumn(title: Text(localizations.aboutWarnValuesScreen), children: [