Merge branch 'feature/uuid-for-formulas'

This commit is contained in:
Álvaro González 2026-03-04 19:53:08 +01:00
commit fe666fdcd6
5 changed files with 37 additions and 22 deletions

View file

@ -53,5 +53,9 @@
- _prettyPrintSet(Set s, int indent) - _prettyPrintSet(Set s, int indent)
- _prettyPrintArray(dynamic[] a, int indent) - _prettyPrintArray(dynamic[] a, int indent)
- _prettyPrintRawString(String s, int indent): Use _prettyPrintRawString when the string contains newlines, $, backlash... - _prettyPrintRawString(String s, int indent): Use _prettyPrintRawString when the string contains newlines, $, backlash...
- [X] Add a field to Formula: UUID.
- A constructor without UUID will generate a new random UUID. A constructor with UUID will use the provided UUID.
- The field should be used in database and everywhere instead of the name. The name is not unique anymore, but the UUID is.
- This will be used to identify formulas, instead of the name. This way, we can have formulas with the same name but different UUIDs. The name is not unique anymore. Corpus will be a list of UUIDs, instead of a list of formulas. The corpus.getFormula() method will return the first formula with that name.
- [ ] When _FormulaScreenState._evaluateFormula() detect an error, instead of show an SnackBar, show a ExpansionTile with "⚠️ There were an error. Show details..." with the details of the exception. The ExpansionTile will be invisible if there is no error. - [ ] When _FormulaScreenState._evaluateFormula() detect an error, instead of show an SnackBar, show a ExpansionTile with "⚠️ There were an error. Show details..." with the details of the exception. The ExpansionTile will be invisible if there is no error.
- [ ] Investigate https://pub.dev/packages/quantity - [ ] Investigate https://pub.dev/packages/quantity

View file

@ -25,12 +25,13 @@ class Multimap<K, V> extends DelegatingMap<K, List<V>> {
class Corpus{ class Corpus{
final Multimap<String, Formula> _tags = Multimap.create(); final Multimap<String, Formula> _tags = Multimap.create();
// Map formulas by uuid
final Map<String, Formula> _allFormulas = {}; final Map<String, Formula> _allFormulas = {};
void loadFormulas(List<Formula> formulas, {bool replaceOnDuplicates = true, bool checkUnits = true}) { void loadFormulas(List<Formula> formulas, {bool replaceOnDuplicates = true, bool checkUnits = true}) {
for (final formula in formulas) { for (final formula in formulas) {
if (!replaceOnDuplicates && _allFormulas.containsKey(formula.name)) { if (!replaceOnDuplicates && _allFormulas.containsKey(formula.uuid)) {
throw ArgumentError("Duplicate formula:$formula"); throw ArgumentError("Duplicate formula:${formula}");
} }
if( checkUnits ){ if( checkUnits ){
@ -41,7 +42,7 @@ class Corpus{
} }
} }
_allFormulas[formula.name] = formula; _allFormulas[formula.uuid] = formula;
for( final tag in formula.tags ){ for( final tag in formula.tags ){
_tags[tag]?.add(formula); _tags[tag]?.add(formula);
} }
@ -59,8 +60,18 @@ class Corpus{
return _allFormulas.values.toList(growable:false); return _allFormulas.values.toList(growable:false);
} }
/// Returns first formula with the given name (preserves old API semantics).
Formula? getFormula(String name) { Formula? getFormula(String name) {
return _allFormulas.get(name); try {
return _allFormulas.values.firstWhere((f) => f.name == name);
} catch (e) {
return null;
}
}
/// Returns formula by uuid
Formula? getFormulaByUuid(String uuid) {
return _allFormulas[uuid];
} }
final Multimap<String, String> _baseToUnits = Multimap.create(); final Multimap<String, String> _baseToUnits = Multimap.create();

View file

@ -1,6 +1,8 @@
import 'package:d4rt/d4rt.dart'; import 'package:d4rt/d4rt.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:d4rt_formulas/d4rt_formulas.dart'; import 'package:d4rt_formulas/d4rt_formulas.dart';
import 'dart:math';
import 'package:uuid/uuid.dart';
typedef Number = double; typedef Number = double;
@ -45,7 +47,7 @@ abstract class SetUtils {
.replaceAll('\n', r'\\n') .replaceAll('\n', r'\\n')
.replaceAll('\r', r'\\r') .replaceAll('\r', r'\\r')
.replaceAll('\t', r'\\t') .replaceAll('\t', r'\\t')
.replaceAll('"', r'\\"'); .replaceAll('"', r'\"');
} }
/// Parses corpus elements from an array string literal. /// Parses corpus elements from an array string literal.
@ -95,7 +97,7 @@ abstract class SetUtils {
// Check if the string needs raw string formatting (newlines, $, backslashes, quotes) // Check if the string needs raw string formatting (newlines, $, backslashes, quotes)
final needsRawString = s.contains('\n') || final needsRawString = s.contains('\n') ||
s.contains(r'$') || s.contains(r'$') ||
s.contains(r'\') || s.contains(r'\\') ||
s.contains('"'); s.contains('"');
if (needsRawString) { if (needsRawString) {
@ -160,7 +162,7 @@ abstract class SetUtils {
/// Uses Dart's raw string syntax r"""...""" /// Uses Dart's raw string syntax r"""..."""
static String _prettyPrintRawString(String s, int indent) { static String _prettyPrintRawString(String s, int indent) {
// Escape triple quotes by replacing """ with ""\" // Escape triple quotes by replacing """ with ""\"
final escaped = s.replaceAll('"""', r'""\"'); final escaped = s.replaceAll('"""', r'""\\"');
return 'r"""$escaped"""'; return 'r"""$escaped"""';
} }
} }
@ -296,7 +298,10 @@ class VariableSpec extends FormulaElement{
} }
String _generateUuidV4() => Uuid().v4();
class Formula extends FormulaElement { class Formula extends FormulaElement {
final String uuid;
final String name; final String name;
final String? description; final String? description;
final List<VariableSpec> input; final List<VariableSpec> input;
@ -306,6 +311,7 @@ class Formula extends FormulaElement {
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
// UUID NOT INCLUDED ON PURPOSE
return { return {
'name': name, 'name': name,
if (description != null) 'description': description, if (description != null) 'description': description,
@ -317,13 +323,14 @@ class Formula extends FormulaElement {
} }
Formula({ Formula({
String? uuid = null,
required this.name, required this.name,
this.description, this.description,
required this.input, required this.input,
required this.output, required this.output,
required this.d4rtCode, required this.d4rtCode,
this.tags = const [], this.tags = const [],
}) { }) : uuid = uuid ?? _generateUuidV4() {
validate(); validate();
} }
@ -337,25 +344,17 @@ class Formula extends FormulaElement {
@override @override
String toString() => String toString() =>
'Formula(name: $name, description: $description, input: $input, output: $output, d4rtCode: $d4rtCode, tags: $tags)'; 'Formula(uuid: $uuid, name: $name, description: $description, input: $input, output: $output, d4rtCode: $d4rtCode, tags: $tags)';
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is Formula && other is Formula &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
name == other.name && uuid == other.uuid;
description == other.description &&
output == other.output &&
ListEquality().equals(input, other.input) &&
d4rtCode == other.d4rtCode &&
ListEquality().equals(tags, other.tags);
@override @override
int get hashCode => int get hashCode => uuid.hashCode;
Object.hash(
name, description, ListEquality().hash(input), output, d4rtCode,
ListEquality().hash(tags));
List<String> inputVarNames() => List<String> inputVarNames() =>
input.map((v) => v.name).toList(growable: false); input.map((v) => v.name).toList(growable: false);
@ -405,6 +404,7 @@ class Formula extends FormulaElement {
); );
} }
String? uuid = theSet['uuid'] as String?;
String name = SetUtils.stringValue(theSet, "name"); String name = SetUtils.stringValue(theSet, "name");
String? description = theSet["description"] as String?; String? description = theSet["description"] as String?;
List<String> tags = (theSet["tags"] as List<Object?>? ?? []).map((t) => List<String> tags = (theSet["tags"] as List<Object?>? ?? []).map((t) =>
@ -417,6 +417,7 @@ class Formula extends FormulaElement {
String d4rtCode = SetUtils.stringValue(theSet, "d4rtCode"); String d4rtCode = SetUtils.stringValue(theSet, "d4rtCode");
return Formula( return Formula(
uuid: uuid,
name: name, name: name,
description: description, description: description,
tags: tags, tags: tags,

View file

@ -990,7 +990,7 @@ packages:
source: hosted source: hosted
version: "3.1.5" version: "3.1.5"
uuid: uuid:
dependency: transitive dependency: "direct main"
description: description:
name: uuid name: uuid
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"

View file

@ -41,8 +41,7 @@ dependencies:
flutter_markdown_plus: flutter_markdown_plus:
flutter_markdown_plus_latex: flutter_markdown_plus_latex:
flutter_code_editor: flutter_code_editor:
uuid:
# Drift dependencies for database support
drift: drift:
sqlite3_flutter_libs: sqlite3_flutter_libs:
path_provider: path_provider: