From 74b226591e2aeb842cb0e99d20c6b5c68a9c38df Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 13 Feb 2026 08:53:04 +0100 Subject: [PATCH] Try to read formulas from database, it is not working --- TODO.md | 11 +- lib/corpus.dart | 7 ++ lib/database/corpus_database_interface.dart | 7 ++ lib/database/database_service.dart | 40 +++++-- lib/database/formulas_database.dart | 3 + lib/database/formulas_database_web.dart | 3 + lib/main.dart | 120 ++++++++++++++++---- lib/service_locator.dart | 19 ++++ test/database_test.dart | 3 +- 9 files changed, 178 insertions(+), 35 deletions(-) create mode 100644 lib/database/corpus_database_interface.dart create mode 100644 lib/service_locator.dart diff --git a/TODO.md b/TODO.md index 9e405df..c41f46a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ # Conventions [ ] Means not done -[R] Means done, but needs a human review +[R] Means done by an ai agent, but needs a human review [X] Means done @@ -23,10 +23,11 @@ - [X] In linux, the sqlite database file will be located following rules at https://specifications.freedesktop.org/basedir/latest/ - [X] In Windows, the sqlite database file will be in %appdata%/Roaming - [X] In Macos, the sqlite database file will be in ~/Library/Application Support -- [ ] Initialize database at startup - - [ ] If the database is empty, sugest to use a default corpus - - [ ] If the user choose to use the default corpus, populate de database with the default corpus (load defaultcorpus, and the use toStringLiteral) - - [ ] From now on, the corpus will be loaded from database instead of assets +- [X] Initialize database at startup + - [X] Try first to load a corpus from database + - [X] If the database is empty, sugest to use a default corpus + - [X] If the user choose to use the default corpus, populate de database with the default corpus (load defaultcorpus, and then use toStringLiteral). If not, start with an empty list of formulas. + - [X] From now on, the corpus will be loaded from database instead of assets - [ ] Create method List Corpus.withDependencies(Formula formula). It will return the list of units of the formula, and related units from the corpus. - [ ] Add a Share button to the formula list. It will export the array string literal of the formula with the units from Corpus.withDependencies(). - [ ] Replace flutter-markdown with flutter-markdown-plus diff --git a/lib/corpus.dart b/lib/corpus.dart index fb80422..d27722c 100644 --- a/lib/corpus.dart +++ b/lib/corpus.dart @@ -205,4 +205,11 @@ class Corpus{ loadFormulas(formulas); } + /// Loads corpus from database elements + static Future fromDatabaseElements(List elements) async { + final corpus = Corpus(); + corpus.loadFormulaElements(elements); + return corpus; + } + } diff --git a/lib/database/corpus_database_interface.dart b/lib/database/corpus_database_interface.dart new file mode 100644 index 0000000..722f3b5 --- /dev/null +++ b/lib/database/corpus_database_interface.dart @@ -0,0 +1,7 @@ +import 'package:d4rt_formulas/formula_models.dart' as models; + +// Interface for corpus database operations +abstract class CorpusDatabaseInterface { + Future> loadCorpusElements(); + Future saveCorpusElements(List elements); +} \ No newline at end of file diff --git a/lib/database/database_service.dart b/lib/database/database_service.dart index 3a0dba0..87fc2c4 100644 --- a/lib/database/database_service.dart +++ b/lib/database/database_service.dart @@ -1,15 +1,37 @@ -import 'package:get_it/get_it.dart'; - -// Conditionally import the correct database file based on platform +import 'corpus_database_interface.dart'; import 'formulas_database.dart' if (dart.library.html) 'formulas_database_web.dart'; +import 'package:d4rt_formulas/formula_models.dart' as models; -GetIt locator = GetIt.instance; +// Extension to add corpus loading/saving functionality to FormulasDatabase +extension CorpusDatabaseExtension on FormulasDatabase { + // Method to load corpus elements from database + Future> loadCorpusElements() async { + final elements = await getAllFormulaElements(); + final List parsedElements = []; -void setupLocator() { - locator.registerSingleton(FormulasDatabase()); -} + for (final element in elements) { + try { + final parsed = models.parseCorpusElements('[${element.elementText}]'); + parsedElements.addAll(parsed); + } catch (e) { + print('Error parsing database element: $e'); + // Skip invalid elements but continue processing others + continue; + } + } -FormulasDatabase getDatabase() { - return locator(); + return parsedElements; + } + + // Method to save corpus elements to database + Future saveCorpusElements(List elements) async { + // Clear existing elements first + await delete(formulaElements).go(); + + // Insert new elements + for (final element in elements) { + await insertFormulaElement(element.toStringLiteral()); + } + } } \ No newline at end of file diff --git a/lib/database/formulas_database.dart b/lib/database/formulas_database.dart index 2502ac6..28527c2 100644 --- a/lib/database/formulas_database.dart +++ b/lib/database/formulas_database.dart @@ -44,6 +44,9 @@ class FormulasDatabase extends _$FormulasDatabase { Future deleteFormulaElement(int id) { return (delete(formulaElements)..where((tbl) => tbl.id.equals(id))).go(); } + + // Additional helper methods for direct access to the table + SimpleSelectStatement get allFormulaElements => select(formulaElements); } LazyDatabase _openConnection() { diff --git a/lib/database/formulas_database_web.dart b/lib/database/formulas_database_web.dart index 4c17004..e618953 100644 --- a/lib/database/formulas_database_web.dart +++ b/lib/database/formulas_database_web.dart @@ -41,6 +41,9 @@ class FormulasDatabase extends _$FormulasDatabase { Future deleteFormulaElement(int id) { return (delete(formulaElements)..where((tbl) => tbl.id.equals(id))).go(); } + + // Additional helper methods for direct access to the table + SimpleSelectStatement get allFormulaElements => select(formulaElements); } LazyDatabase _openConnection() { diff --git a/lib/main.dart b/lib/main.dart index edc1921..da4781c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,16 +1,19 @@ import 'package:flutter/material.dart'; import 'database/database_service.dart'; +import 'package:drift/drift.dart' as drift; +import 'service_locator.dart'; import 'ai/formula_list.dart'; import 'corpus.dart'; import 'defaults/default_corpus.dart'; +import 'formula_models.dart' as models; void main() async { WidgetsFlutterBinding.ensureInitialized(); - + // Setup service locator and initialize the database setupLocator(); - + runApp(const MyApp()); } @@ -20,26 +23,103 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - home: FutureBuilder( - future: createDefaultCorpus(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.hasError) { - return Center(child: Text('Error loading units: ${snapshot.error}')); - } - return Scaffold( - appBar: AppBar(title: const Text('Formulas')), - body: FormulaList( - corpus: snapshot.data!, - formulas: snapshot.data!.getFormulas(), - ), - ); - } - return const Center(child: CircularProgressIndicator()); - }, - ), + home: CorpusLoader(), ); } } +class CorpusLoader extends StatefulWidget { + @override + _CorpusLoaderState createState() => _CorpusLoaderState(); +} + +class _CorpusLoaderState extends State { + late Future _corpusFuture; + + @override + void initState() { + super.initState(); + _corpusFuture = loadCorpusFromDatabaseOrAssets(); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _corpusFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + return Center(child: Text('Error loading corpus: ${snapshot.error}')); + } + + // If the corpus is empty (user chose not to load default), we could handle that here + // For now, just display the formula list + return Scaffold( + appBar: AppBar(title: const Text('Formulas')), + body: FormulaList( + corpus: snapshot.data!, + formulas: snapshot.data!.getFormulas(), + ), + ); + } + return const Center(child: CircularProgressIndicator()); + }, + ); + } +} + +/// Attempts to load corpus from database first, falls back to default corpus if database is empty +Future loadCorpusFromDatabaseOrAssets() async { + final database = getDatabase(); + + try { + // Try to load from database first + final dbElements = await database.loadCorpusElements(); + + if (dbElements.isEmpty) { + // Database is empty, load default corpus and save to database + final defaultCorpus = await createDefaultCorpus(); + + // Convert corpus to elements and save to database + final elements = []; + elements.addAll(defaultCorpus.allUnits().cast()); + elements.addAll(defaultCorpus.getFormulas().cast()); + + await database.saveCorpusElements(elements); + + return defaultCorpus; + } else { + // Load corpus from database elements + return await Corpus.fromDatabaseElements(dbElements); + } + } catch (e) { + // If there's an error loading from database, fall back to default corpus + print('Error loading corpus from database: $e'); + return await createDefaultCorpus(); + } +} + +/// Shows a dialog to ask user if they want to use the default corpus +Future showUseDefaultCorpusDialog(BuildContext context) async { + return await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Empty Database'), + content: const Text('The database is empty. Would you like to load the default corpus?'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), // Don't use default corpus + child: const Text('No'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), // Use default corpus + child: const Text('Yes'), + ), + ], + ); + }, + ) ?? false; // Default to false if dialog is dismissed +} + diff --git a/lib/service_locator.dart b/lib/service_locator.dart new file mode 100644 index 0000000..159c491 --- /dev/null +++ b/lib/service_locator.dart @@ -0,0 +1,19 @@ +import 'package:get_it/get_it.dart'; +import 'database/formulas_database.dart' + if (dart.library.html) 'database/formulas_database_web.dart'; + +GetIt locator = GetIt.instance; + +void setupLocator() { + locator.registerSingleton(getDatabase()); +} + +FormulasDatabase getDatabase() { + // Check if already registered to avoid recreating + if (locator.isRegistered()) { + return locator.get(); + } + + // Create new instance based on platform + return FormulasDatabase(); +} \ No newline at end of file diff --git a/test/database_test.dart b/test/database_test.dart index 583272d..e3541e7 100644 --- a/test/database_test.dart +++ b/test/database_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:d4rt_formulas/database/database_service.dart'; +import 'package:d4rt_formulas/service_locator.dart'; void main() { setUp(() { @@ -10,4 +11,4 @@ void main() { final database = getDatabase(); expect(database, isNotNull); }); -} \ No newline at end of file +}