diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4552cb9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# Use the official Flutter SDK image +FROM ghcr.io/cirruslabs/flutter:stable + +# Install cmake, ninja, clang, pkg-config for flutter linux +RUN apt-get update && apt-get install -y cmake ninja-build clang pkg-config libgtk-3-dev liblzma-dev + +WORKDIR /app + +# Configure cache directories +ENV PUB_CACHE=/cache/pub-cache +ENV GRADLE_USER_HOME=/cache/gradle-cache +RUN mkdir -p $PUB_CACHE $GRADLE_USER_HOME + +# Copy pubspec files and get dependencies +# COPY pubspec.yaml pubspec.lock ./ +# RUN flutter pub get + +# Copy the rest of the application code and build +# Commented out to avoid building the app during image creation, this will be handled externally by makefile +# COPY . . +# RUN flutter build apk --release diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..613c3cb --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ + +flutter-container-exec = podman-compose run --entrypoint "$(1)" flutter + +all: clean-podman build-linux-debug-podman build-linux-debug-podman + +build-podman: + podman-compose build + +clean-podman: build-podman + $(call flutter-container-exec, flutter clean) + +pub-get-podman: build-podman + $(call flutter-container-exec, flutter pub get) + +build-android-release-podman: pub-get-podman + $(call flutter-container-exec, flutter build apk --release) + +build-linux-debug-podman: pub-get-podman + $(call flutter-container-exec, flutter build linux --debug) + +run-linux-debug: build-linux-debug-podman + build/linux/x64/debug/bundle/d4rt_formulas + +run-web-release-podman: build-web-release-podman + cd build/web && python3 -m http.server 8080 diff --git a/README.md b/README.md index 00d6931..7c627a7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +https://github.com/Shahxad-Akram/flutter_tex/blob/master/example/lib/tex_view_markdown_example.dart + # Math Formulae Manager A comprehensive command-line application for managing and computing mathematical formulas across various disciplines including mathematics, physics, medicine, and engineering. @@ -123,15 +125,11 @@ Each formula includes: - **Images** - Visual diagrams, graphs, or illustrations - **Examples** - Sample calculations and use cases -## Project Structure - -- `bin/` - Main executable and entry point -- `lib/` - Core library code and formula engine -- `test/` - Unit tests and formula validation tests - ## Getting Started -[Installation and usage instructions to be added] +This project uses `flutter`, so a valid installation is needed in order to build it. + +For convenience, a containerized build is provided. It is based on `podman` and `podman-compose`. See [Makefile](Makefile) for details. ## Contributing diff --git a/lib/defaults/formulas.d4rt b/assets/formulas/formulas.d4rt similarity index 76% rename from lib/defaults/formulas.d4rt rename to assets/formulas/formulas.d4rt index 00bfd74..a6dc9db 100644 --- a/lib/defaults/formulas.d4rt +++ b/assets/formulas/formulas.d4rt @@ -120,4 +120,33 @@ Where: "d4rtCode": "F = m * a;", "tags": ["physics", "mechanics", "newton"] }, + + // Apgar Score + { + "name": "Apgar Score", + "description": "Newborn health assessment scoring system\n\nScores 0-2 for:\n1. Heart rate\n2. Breathing\n3. Muscle tone\n4. Reflexes\n5. Skin color\nTotal score 0-10", + "input": [ + {"name": "HeartRate", "values": ["Absent", "< 100 bpm>", "> 100 bpm"] }, + {"name": "Breathing", "values": ["Absent", "Weak, irregular", "Strong, robust cry"] }, + {"name": "MuscleTone", "values": ["None", "Some", "Flexed arms/leg, resists extension"] }, + {"name": "Reflexes", "values": ["No response", "Grimace on aggressive stimulation", "Cry on stimulation"] }, + {"name": "SkinColor", "values": ["Blue or pale", "Blue extremities, pink body", "Pink"] } + ], + "output": {"name": "Result", "unit": "scalar"}, + "d4rtCode": """ + var total = HeartRate + Breathing + MuscleTone + Reflexes + SkinColor; + late var interpretation; + if( total < 4 ) { + interpretation = 'Critical condition'; + } + else if( total < 7 ){ + interpretation = 'Needs assistance'; + } + else { + interpretation = 'Normal'; + } + Result = 'Score: \$total - \$interpretation'; + """, + "tags": ["medical", "pediatrics", "assessment"] + } ] diff --git a/lib/defaults/units/angle.d4rt.units b/assets/units/angle.d4rt.units similarity index 100% rename from lib/defaults/units/angle.d4rt.units rename to assets/units/angle.d4rt.units diff --git a/lib/defaults/units/area.d4rt.units b/assets/units/area.d4rt.units similarity index 100% rename from lib/defaults/units/area.d4rt.units rename to assets/units/area.d4rt.units diff --git a/lib/defaults/units/distance.d4rt.units b/assets/units/distance.d4rt.units similarity index 100% rename from lib/defaults/units/distance.d4rt.units rename to assets/units/distance.d4rt.units diff --git a/lib/defaults/units/energy.d4rt.units b/assets/units/energy.d4rt.units similarity index 100% rename from lib/defaults/units/energy.d4rt.units rename to assets/units/energy.d4rt.units diff --git a/lib/defaults/units/force.d4rt.units b/assets/units/force.d4rt.units similarity index 100% rename from lib/defaults/units/force.d4rt.units rename to assets/units/force.d4rt.units diff --git a/lib/defaults/units/mass.d4rt.units b/assets/units/mass.d4rt.units similarity index 100% rename from lib/defaults/units/mass.d4rt.units rename to assets/units/mass.d4rt.units diff --git a/lib/defaults/units/pressure.d4rt.units b/assets/units/pressure.d4rt.units similarity index 100% rename from lib/defaults/units/pressure.d4rt.units rename to assets/units/pressure.d4rt.units diff --git a/assets/units/scalar.d4rt.units b/assets/units/scalar.d4rt.units new file mode 100644 index 0000000..0533d87 --- /dev/null +++ b/assets/units/scalar.d4rt.units @@ -0,0 +1,3 @@ +[ + {"name": "scalar", "symbol": "", "isBase": true}, +] \ No newline at end of file diff --git a/lib/defaults/units/temperature.d4rt.units b/assets/units/temperature.d4rt.units similarity index 100% rename from lib/defaults/units/temperature.d4rt.units rename to assets/units/temperature.d4rt.units diff --git a/lib/defaults/units/time.d4rt.units b/assets/units/time.d4rt.units similarity index 100% rename from lib/defaults/units/time.d4rt.units rename to assets/units/time.d4rt.units diff --git a/lib/defaults/units/velocity.d4rt.units b/assets/units/velocity.d4rt.units similarity index 100% rename from lib/defaults/units/velocity.d4rt.units rename to assets/units/velocity.d4rt.units diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1dcbe96 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.8' + +services: + flutter: + build: + context: . + dockerfile: Dockerfile + image: d4rt-formulas-builder + volumes: + - ./.build-container-cache:/cache:z + - .:/app:z # Link the current directory to /app in the container + environment: + - FLUTTER_FLAVOR=prod # Example environment variable, adjust as needed diff --git a/example/formula_evaluation_example.dart b/example/formula_evaluation_example.dart index 068c699..0b60770 100644 --- a/example/formula_evaluation_example.dart +++ b/example/formula_evaluation_example.dart @@ -37,8 +37,8 @@ void main() { print(' Mass: 10.0 kg'); print(' Acceleration: 9.8 m/s²'); print(' Calculated Force: $force N'); - print(' Output variable: ${evaluator.getOutputVariableName(newtonFormula)}'); - print(' Output magnitude: ${evaluator.getOutputVariableMagnitude(newtonFormula)}'); + print(' Output variable: ${newtonFormula.output.name}'); + print(' Output magnitude: ${newtonFormula.output.unit}'); } catch (e) { print(' Error: $e'); } diff --git a/firejail-warp-terminal.profile b/firejail-warp-terminal.profile deleted file mode 100644 index 5406c7b..0000000 --- a/firejail-warp-terminal.profile +++ /dev/null @@ -1,11 +0,0 @@ -# launch as: firejail --profile=firejail-warp-terminal.profile /opt/warpdotdev/warp-terminal/warp -private /home/alvaro/repos/d4rt_formulas/ -blacklist /datos-1T -blacklist /datos-luks/ -blacklist /media - -# net none # disable network -# noroot # don't run as root -caps.drop all # drop Linux capabilities -# seccomp # enable syscall filtering -First version of a formula widget \ No newline at end of file diff --git a/lib/ai/formula_screen.dart b/lib/ai/formula_screen.dart index 81d82ad..eb91e75 100644 --- a/lib/ai/formula_screen.dart +++ b/lib/ai/formula_screen.dart @@ -1,6 +1,4 @@ - import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import '../formula_models.dart'; import '../formula_evaluator.dart'; @@ -11,24 +9,22 @@ class FormulaScreen extends StatefulWidget { final Formula formula; final Corpus corpus; - const FormulaScreen({ - super.key, - required this.formula, - required this.corpus, - }); + const FormulaScreen({super.key, required this.formula, required this.corpus}); @override State createState() => _FormulaScreenState(); } +// TODO: Create VariableWidget. Depending on VariableSpec.values, it can be a ValueDropdown or a D4rtEditingController +// The d4rtValue will be FormulaResult? + //// Start of D4rtEditingController class //// class D4rtEditingController extends TextEditingController { String? _lastError; - String? get lastError => _lastError; FormulaResult? _lastValue; - D4rtEditingController({String? text}) : super(text: text); + D4rtEditingController({super.text}); bool validate() { try { @@ -44,9 +40,8 @@ class D4rtEditingController extends TextEditingController { } } - get d4rtValue => _lastValue; + FormulaResult? get d4rtValue => _lastValue; - @override set text(String newText) { super.text = newText; validate(); @@ -97,38 +92,51 @@ class _FormulaScreenState extends State { final inputValues = {}; for (final input in widget.formula.input) { final controller = _inputControllers[input.name]!; - if( controller.d4rtValue == null ){ - throw FormulaEvaluationException( "Field ${input.name} is invalid" ); + if (controller.d4rtValue == null) { + //throw FormulaEvaluationException("Field ${input.name} is invalid"); + _result = ""; + return; } - final value = controller.d4rtValue.value; - // Convert input to base unit if needed - // Always convert from dropdown unit to variable's base unit - late final convertedValue; - if( value is Number ) { - convertedValue = widget.corpus.convert( - value, - _selectedUnits[input.name]!, - input.unit, - ); - } - else{ - convertedValue = value; + late final dynamic convertedValue; + + switch (controller.d4rtValue) { + case NumberResult nr: + // Convert input to base unit if needed + // Always convert from dropdown unit to variable's base unit + if (input.unit != null) { + convertedValue = widget.corpus.convert( + nr.value, + _selectedUnits[input.name]!, + input.unit as String, + ); + } else { + convertedValue = nr.value; + } + + case StringResult sr: + convertedValue = sr.value; + default: + throw FormulaEvaluationException( + "Field ${input.name} has unsupported type ${controller.d4rtValue!.runtimeType}", + ); } inputValues[input.name] = convertedValue; - } final evaluator = FormulaEvaluator(); final result = evaluator.evaluate(widget.formula, inputValues); // Convert output to selected unit if needed - _result = widget.corpus.convert( - result, - widget.formula.output.unit, - _selectedOutputUnit!, - ).toStringAsFixed(2); + String? unit = widget.formula.output.unit; + if (unit != null) { + _result = widget.corpus + .convert(result, unit, _selectedOutputUnit!) + .toStringAsFixed(2); + } else { + _result = result; + } //print( "_evaluateFormula: result:${result} _result:${_result}"); @@ -136,7 +144,7 @@ class _FormulaScreenState extends State { } catch (e, stack) { debugPrint('Formula evaluation error: $e'); debugPrint('Stack trace: $stack'); - + ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Error: ${e.toString()}\n${stack.toString()}'), @@ -149,9 +157,7 @@ class _FormulaScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(widget.formula.name), - ), + appBar: AppBar(title: Text(widget.formula.name)), body: Form( key: _formKey, child: Padding( @@ -170,7 +176,7 @@ class _FormulaScreenState extends State { } Widget _buildDescriptionSection() { - if (widget.formula.description == null || + if (widget.formula.description == null || widget.formula.description!.isEmpty) { return const SizedBox.shrink(); } @@ -180,9 +186,9 @@ class _FormulaScreenState extends State { children: [ Text( 'Description', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Container( @@ -207,9 +213,9 @@ class _FormulaScreenState extends State { children: [ Text( 'Input Variables', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), ...widget.formula.input.map((variable) => _buildVariableRow(variable)), @@ -223,9 +229,9 @@ class _FormulaScreenState extends State { children: [ Text( 'Result', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Row( @@ -280,6 +286,7 @@ class _FormulaScreenState extends State { decoration: const InputDecoration( border: UnderlineInputBorder(), ), + autovalidateMode: AutovalidateMode.always, validator: (value) { if (value == null || value.isEmpty) { return 'Required'; diff --git a/lib/corpus.dart b/lib/corpus.dart index c9c724f..7ad4f50 100644 --- a/lib/corpus.dart +++ b/lib/corpus.dart @@ -34,7 +34,7 @@ class Corpus{ if( checkUnits ){ for( final inputVar in formula.input + [formula.output] ){ - if( !_allUnits.containsKey(inputVar.unit) ){ + if( inputVar.unit != null && !_allUnits.containsKey(inputVar.unit) ){ throw ArgumentError( "Unit not found: ${inputVar.unit}"); } } @@ -58,6 +58,10 @@ class Corpus{ return _allFormulas.values.toList(growable:false); } + Formula? getFormula(String name) { + return _allFormulas.get(name); + } + final Multimap _baseToUnits = Multimap.create(); final Map _allUnits = {}; @@ -71,7 +75,10 @@ class Corpus{ } } - List unitsOfSameMagnitude(String unit){ + List unitsOfSameMagnitude(String? unit){ + if( unit == null ){ + return ["scalar"]; + } final base = getUnit(unit).baseUnit; return _baseToUnits[base] as List; } @@ -87,7 +94,7 @@ class Corpus{ String _converterFromCodeStringAsExpression(Number x, String codeString) { final buffer = StringBuffer(); - buffer.writeln("final x = ${x};"); + buffer.writeln("final x = $x;"); buffer.writeln("main(){return $codeString;}"); final code = buffer.toString(); return code; @@ -95,7 +102,7 @@ class Corpus{ String _converterFromCodeStringAsStatement(Number x, String codeString) { final buffer = StringBuffer(); - buffer.writeln("final x = ${x};"); + buffer.writeln("final x = $x;"); buffer.writeln("main(){ $codeString; return x; }"); final code = buffer.toString(); return code; @@ -113,7 +120,7 @@ class Corpus{ } final ret = _convertUsingCode(x, unit.codeFromUnitToBase as String); - return ret as Number; + return ret; } Number _convertFromBase(Number x, String toUnit) { @@ -128,7 +135,7 @@ class Corpus{ } final ret = _convertUsingCode(x, unit.codeFromBaseToUnit as String); - return ret as Number; + return ret; } @@ -140,17 +147,19 @@ class Corpus{ final ret = _evaluate(completeSourceExpression); return ret; } - catch(e1,stack){ + catch(e1, stack1){ try{ completeSourceStatement = _converterFromCodeStringAsStatement(x, code); final ret = _evaluate(completeSourceStatement); return ret; } - catch( e2, stack ){ + catch( e2, stack2 ){ print(completeSourceExpression); print(e1); + print(stack1); print(completeSourceStatement); print(e2); + print(stack2); throw FormulaEvaluationException( "Evaluation as statement and expression failed" ); } } diff --git a/lib/defaults/default_corpus.dart b/lib/defaults/default_corpus.dart index 7df8d82..af7f983 100644 --- a/lib/defaults/default_corpus.dart +++ b/lib/defaults/default_corpus.dart @@ -1,41 +1,48 @@ +import 'dart:async'; import 'dart:convert' show utf8; +import 'package:flutter/services.dart' show rootBundle; import 'package:resource_portable/resource_portable.dart' show Resource; import '../corpus.dart'; import '../formula_models.dart'; + Future createDefaultCorpus() async{ final corpus = Corpus(); + Future loadResourceAsString(String path) async { + return await rootBundle.loadString(path, cache: false); + } + + Future loadUnits() async { final unitResources = [ - "lib/defaults/units/angle.d4rt.units", - "lib/defaults/units/area.d4rt.units", - "lib/defaults/units/distance.d4rt.units", - "lib/defaults/units/energy.d4rt.units", - "lib/defaults/units/force.d4rt.units", - "lib/defaults/units/mass.d4rt.units", - "lib/defaults/units/pressure.d4rt.units", - "lib/defaults/units/temperature.d4rt.units", - "lib/defaults/units/time.d4rt.units", - "lib/defaults/units/velocity.d4rt.units", + "assets/units/angle.d4rt.units", + "assets/units/area.d4rt.units", + "assets/units/distance.d4rt.units", + "assets/units/energy.d4rt.units", + "assets/units/force.d4rt.units", + "assets/units/mass.d4rt.units", + "assets/units/pressure.d4rt.units", + "assets/units/scalar.d4rt.units", + "assets/units/temperature.d4rt.units", + "assets/units/time.d4rt.units", + "assets/units/velocity.d4rt.units", ]; for (final unitRes in unitResources) { - final resource = Resource(unitRes); - final literal = await resource.readAsString(encoding: utf8); + final literal = await loadResourceAsString(unitRes); final units = UnitSpec.fromArrayStringLiteral(literal); corpus.loadUnits(units); } } Future loadFormulas() async { - final formulaResources = ["lib/defaults/formulas.d4rt"]; + final formulaResources = ["assets/formulas/formulas.d4rt"]; for (final formRes in formulaResources) { - final resource = Resource(formRes); - final literal = await resource.readAsString(encoding: utf8); + final literal = await loadResourceAsString(formRes); final formulas = Formula.fromArrayStringLiteral(literal); corpus.loadFormulas(formulas); } diff --git a/lib/formula_evaluator.dart b/lib/formula_evaluator.dart index 0665b38..acc978d 100644 --- a/lib/formula_evaluator.dart +++ b/lib/formula_evaluator.dart @@ -76,14 +76,14 @@ class FormulaEvaluator { final d4rtInterpreter = interpreter ?? createDefaultInterpreter(); prepareInterpreter(d4rtInterpreter); final d4rtCode = """ - ${d4rtImports} + $d4rtImports main() { late var result; result = $code; return result; }"""; - //print("evaluateExpression:\n$d4rtCode"); + print("evaluateExpression:\n$d4rtCode"); final result = d4rtInterpreter.execute(source: d4rtCode); switch ( result ){ case int value: @@ -100,8 +100,17 @@ class FormulaEvaluator { dynamic evaluate(Formula formula, Map inputValues) { _validateInputValues(formula, inputValues); final completeSource = _buildCompleteSource(formula, inputValues); - final result = _interpreter.execute(source: completeSource); - return result; + try { + final result = _interpreter.execute(source: completeSource); + return result; + } + catch (e) { + print( "Error evaluating formula source:\n$completeSource" ); + throw FormulaEvaluationException( + 'Error evaluating formula "${formula.name}": $e', + e, + ); + } } void _validateInputValues(Formula formula, Map inputValues) { @@ -121,13 +130,7 @@ class FormulaEvaluator { } } - String getOutputVariableName(Formula formula) { - return formula.output.name; - } - String getOutputVariableMagnitude(Formula formula) { - return formula.output.unit; - } List getInputVariableOrder(Formula formula) { return formula.inputVarNames()..sort(); @@ -166,9 +169,9 @@ class FormulaEvaluator { } } buffer.writeln(""" - late var ${getOutputVariableName(formula)}; + late var ${formula.output.name}; ${formula.d4rtCode} - return ${getOutputVariableName(formula)}; + return ${formula.output.name}; } """ ); diff --git a/lib/formula_models.dart b/lib/formula_models.dart index ebdf047..9a0c5f6 100644 --- a/lib/formula_models.dart +++ b/lib/formula_models.dart @@ -101,12 +101,18 @@ class UnitSpec { class VariableSpec { final String name; - final String unit; + final String? unit; + final List? values; - VariableSpec({required this.name, required this.unit}); + VariableSpec({required this.name, this.unit, this.values}){ + final valuesValid = values != null && values?.isNotEmpty == true; + if( unit == null && !valuesValid ){ + throw ArgumentError("$name: at least unit or allowedValues should be valid"); + } + } @override - String toString() => 'var($name: $unit)'; + String toString() => 'var($name: $unit${values != null ? ' allowed: $values' : ''})'; @override bool operator ==(Object other) => @@ -114,10 +120,11 @@ class VariableSpec { other is VariableSpec && runtimeType == other.runtimeType && unit == other.unit && - name == other.name; + name == other.name && + const DeepCollectionEquality().equals(values, other.values); @override - int get hashCode => Object.hash(unit, name); + int get hashCode => Object.hash(unit, name, values != null ? const DeepCollectionEquality().hash(values!) : 0); } class Formula { @@ -139,7 +146,7 @@ class Formula { validate(); } - validate() { + void validate() { if (name.trim().isEmpty) { throw ArgumentError('Formula name cannot be empty'); } @@ -196,8 +203,25 @@ class Formula { factory Formula.fromSet(Map theSet) { VariableSpec parseVar(Map varSpec) { String name = SetUtils.stringValue(varSpec, "name"); - String unit = SetUtils.stringValue(varSpec, "unit"); - return VariableSpec(name: name, unit: unit); + String? unit; + if (varSpec.containsKey("unit")) { + unit = SetUtils.stringValue(varSpec, "unit"); + } + final allowed = varSpec['values'] as List?; + if (allowed != null) { + final types = allowed.map((v) => v.runtimeType).toSet(); + if (types.length > 1) { + throw ArgumentError('Allowed values must be all Strings or all Numbers'); + } + if (!types.contains(String) && !types.contains(double) && !types.contains(int)) { + throw ArgumentError('Allowed values must be Strings or Numbers'); + } + } + return VariableSpec( + name: name, + unit: unit, + values: allowed?.toList(growable: false), + ); } String name = SetUtils.stringValue(theSet, "name"); diff --git a/lib/main.dart b/lib/main.dart index f21f76c..0f44288 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'corpus.dart'; import 'defaults/default_corpus.dart'; void main() { + WidgetsFlutterBinding.ensureInitialized(); runApp(MaterialApp( home: FutureBuilder( future: createDefaultCorpus(), diff --git a/pubspec.lock b/pubspec.lock index 9ee6b4b..78823f5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" cupertino_icons: dependency: "direct main" description: @@ -125,18 +125,18 @@ packages: dependency: "direct main" description: name: d4rt - sha256: "1eb626145e2ed97332f9e6e842e626f973d3969ce30e2794efb4744bd8aeba63" + sha256: eff6a10f31e9e5b60b99146a33204c5f2d74e20ac3eeb14132d8a8ed0921c6e1 url: "https://pub.dev" source: hosted - version: "0.1.7" + version: "0.1.9" equatable: dependency: transitive description: name: equatable - sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.8" fake_async: dependency: transitive description: @@ -244,10 +244,10 @@ packages: dependency: transitive description: name: http - sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -356,10 +356,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -585,34 +585,34 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9" + sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611" url: "https://pub.dev" source: hosted - version: "6.3.24" + version: "6.3.28" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7 + sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad url: "https://pub.dev" source: hosted - version: "6.3.4" + version: "6.3.6" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f + sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" url: "https://pub.dev" source: hosted - version: "3.2.3" + version: "3.2.5" url_launcher_platform_interface: dependency: transitive description: @@ -625,18 +625,18 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" vector_math: dependency: transitive description: @@ -657,10 +657,10 @@ packages: dependency: transitive description: name: watcher - sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.2.1" web: dependency: transitive description: @@ -702,5 +702,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.9.0 <4.0.0" - flutter: ">=3.35.0" + dart: ">=3.10.7 <4.0.0" + flutter: ">=3.38.0" diff --git a/pubspec.yaml b/pubspec.yaml index b314177..d70efdb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ^3.8.1 + sdk: ^3.10.7 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -50,8 +50,8 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^6.0.0 - test: ^1.26.3 + flutter_lints: + test: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -68,6 +68,9 @@ flutter: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg + assets: + - assets/units/ + - assets/formulas/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images diff --git a/test/d4rt_test.dart b/test/d4rt_test.dart index bc68e6c..0e948a8 100644 --- a/test/d4rt_test.dart +++ b/test/d4rt_test.dart @@ -3,7 +3,7 @@ import 'package:d4rt/d4rt.dart'; import 'dart:math' as Math; -main(){ +void main(){ test('Access to Math', () { final completeSource = """ @@ -29,6 +29,7 @@ main(){ } """; final interpreter = D4rt(); + interpreter.grant(FilesystemPermission.readPath("/etc/passwd")); final result = interpreter.execute(source: completeSource); expect(result, contains("root")); diff --git a/test/dart_test.dart b/test/dart_test.dart index 9777d46..9e78acc 100644 --- a/test/dart_test.dart +++ b/test/dart_test.dart @@ -3,7 +3,7 @@ import 'package:d4rt/d4rt.dart'; import 'dart:math' as Math; -main(){ +void main(){ test('for dart grammar tests', () { }); diff --git a/test/formula_evaluator_test.dart b/test/formula_evaluator_test.dart index 727759f..0b4c04d 100644 --- a/test/formula_evaluator_test.dart +++ b/test/formula_evaluator_test.dart @@ -152,7 +152,7 @@ void main() { d4rtCode: 'force = x;', ); - expect(evaluator.getOutputVariableName(formula), 'force'); + expect(formula.output.name, 'force'); }); test( @@ -165,7 +165,7 @@ void main() { d4rtCode: 'force = x;', ); - expect(evaluator.getOutputVariableMagnitude(formula), 'Newton'); + expect(formula.output.unit, 'Newton'); }, ); @@ -177,8 +177,8 @@ void main() { d4rtCode: 'result = x;', ); - expect(evaluator.getOutputVariableName(validFormula), 'result'); - expect(evaluator.getOutputVariableMagnitude(validFormula), 'Newton'); + expect(validFormula.output.name, 'result'); + expect(validFormula.output.unit, 'Newton'); }); }); diff --git a/test/formula_models_test.dart b/test/formula_models_test.dart index 3979a2b..1456b3a 100644 --- a/test/formula_models_test.dart +++ b/test/formula_models_test.dart @@ -1,16 +1,21 @@ import 'package:d4rt_formulas/corpus.dart'; import 'package:d4rt_formulas/defaults/default_corpus.dart'; import 'package:d4rt_formulas/formula_evaluator.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:d4rt_formulas/formula_models.dart'; void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + Future createTestCorpus() async { return createDefaultCorpus(); } + Future testCorpus = createTestCorpus(); + + test("Parses unit", () { final setLiteral = {"name": "kilometer", "symbol": "km", "baseUnit": "meter", "factor": 1000}; final unit = UnitSpec.fromSet(setLiteral); @@ -23,37 +28,37 @@ void main() { }); test("From km to in", () async { - final corpus = await createTestCorpus(); + final corpus = await testCorpus; final inches = corpus.convert(1, "kilometer", "inch"); expect( inches, closeTo(39370.078,0.001) ); }); test("From furlong to base", () async { - final corpus = await createTestCorpus(); + final corpus = await testCorpus; final m = corpus.convert(1, "furlong", "meter"); expect(m,closeTo(201.168,0.001)); }); test("From base to furlong", () async { - final corpus = await createTestCorpus(); + final corpus = await testCorpus; final m = corpus.convert(201.168, "meter", "furlong"); expect(m,closeTo(1,0.001)); }); test("From C to F", () async { - final corpus = await createTestCorpus(); + final corpus = await testCorpus; final m = corpus.convert(37, "Celsius", "Fahrenheit"); expect(m,closeTo(98.6,0.001)); }); test("From K to F", () async { - final corpus = await createTestCorpus(); + final corpus = await testCorpus; final m = corpus.convert(37, "Kelvin", "Fahrenheit"); expect(m,closeTo(-393.07,0.001)); }); test("From C to K", () async { - final corpus = await createTestCorpus(); + final corpus = await testCorpus; final m = corpus.convert(100, "Celsius", "Kelvin"); expect(m,closeTo(373.15,0.001)); }); @@ -108,5 +113,22 @@ void main() { expect(result, 98.0); // F = m * a = 10 * 9.8 = 98 N }); + group('APGAR Score', () { + test('evaluates APGAR score formula - Normal case', () async { + final corpus = await testCorpus; + final formula = corpus.getFormula("Apgar Score")!; + final evaluator = FormulaEvaluator(); + + final result = evaluator.evaluate(formula, { + 'HeartRate': 2, + 'Breathing': 2, + 'MuscleTone': 2, + 'Reflexes': 2, + 'SkinColor': 2 + }); + + expect(result, 'Score: 10 - Normal'); + }); + }); } diff --git a/warp-terminal.sh b/warp-terminal.sh deleted file mode 100755 index 8471c2f..0000000 --- a/warp-terminal.sh +++ /dev/null @@ -1 +0,0 @@ -firejail --profile=firejail-warp-terminal.profile warp-terminal