From ffe13266292b606fdd0478167fb7d24610643f65 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 7 Feb 2026 11:45:25 +0100 Subject: [PATCH] Used qwen to validate allowed values --- CLAUDE.md | 163 +++---------------------------- Makefile | 2 +- README.md | 126 ------------------------ flutterw | 4 +- lib/formula_evaluator.dart | 24 ++++- test/formula_evaluator_test.dart | 16 +++ test/formula_models_test.dart | 72 +++++++++++++- units-and-formulas-definition.md | 111 --------------------- 8 files changed, 119 insertions(+), 399 deletions(-) delete mode 100644 units-and-formulas-definition.md diff --git a/CLAUDE.md b/CLAUDE.md index 3a99ade..e2d61c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,154 +1,13 @@ -# Development Guidelines +# Tools available +- This is a flutter app. +- Flutter and Dart are not installed locally. They are executed via a flutter wrapper script, like gradlew (gradle wrapper) for gradle. +- Flutter and Dart are installed in a podman container. This container is invoked via `./flutterw` bash script. + - `./flutterw --build-container` to build the image and the container with flutter tools + - `./flutterw --exec ` to execute bash commands inside the container + - `./flutterw ` is a shorthand for `./flutter --exec flutter `, so `./flutterw` and `flutter` are somewhat equivalent. +- Examples: + - `flutter pub get` --> `./flutterw pub get` + - `flutter run -d linux` --> `./flutterw run -d linux` +- See `./Makefile` for more use examples. -## Philosophy -### Core Beliefs - -- **Incremental progress over big bangs** - Small changes that compile and pass tests -- **Learning from existing code** - Study and plan before implementing -- **Pragmatic over dogmatic** - Adapt to project reality -- **Clear intent over clever code** - Be boring and obvious - -### Simplicity Means - -- Single responsibility per function/class -- Avoid premature abstractions -- No clever tricks - choose the boring solution -- If you need to explain it, it's too complex - -## Process - -### 1. Planning & Staging - -Break complex work into 3-5 stages. Document in `IMPLEMENTATION_PLAN.md`: - -```markdown -## Stage N: [Name] -**Goal**: [Specific deliverable] -**Success Criteria**: [Testable outcomes] -**Tests**: [Specific test cases] -**Status**: [Not Started|In Progress|Complete] -``` -- Update status as you progress -- Remove file when all stages are done - -### 2. Implementation Flow - -1. **Understand** - Study existing patterns in codebase -2. **Branch** - Create a new branch from current branch, asing a memorable name to the new branch -3. **Test** - Write test first (red) -4. **Implement** - Minimal code to pass (green) -5. **Refactor** - Clean up with tests passing -6. **Commit** - With clear message linking to plan - -### 3. When Stuck (After 3 Attempts) - -**CRITICAL**: Maximum 3 attempts per issue, then STOP. - -1. **Document what failed**: - - What you tried - - Specific error messages - - Why you think it failed - -2. **Research alternatives**: - - Find 2-3 similar implementations - - Note different approaches used - -3. **Question fundamentals**: - - Is this the right abstraction level? - - Can this be split into smaller problems? - - Is there a simpler approach entirely? - -4. **Try different angle**: - - Different library/framework feature? - - Different architectural pattern? - - Remove abstraction instead of adding? - -## Technical Standards - -### Architecture Principles - -- **Composition over inheritance** - Use dependency injection -- **Interfaces over singletons** - Enable testing and flexibility -- **Explicit over implicit** - Clear data flow and dependencies -- **Test-driven when possible** - Never disable tests, fix them - -### Code Quality - -- **Every commit must**: - - Compile successfully - - Pass all existing tests - - Include tests for new functionality - - Follow project formatting/linting - -- **Before committing**: - - Run formatters/linters - - Self-review changes - - Ensure commit message explains "why" - -### Error Handling - -- Fail fast with descriptive messages -- Include context for debugging -- Handle errors at appropriate level -- Never silently swallow exceptions - -## Decision Framework - -When multiple valid approaches exist, choose based on: - -1. **Testability** - Can I easily test this? -2. **Readability** - Will someone understand this in 6 months? -3. **Consistency** - Does this match project patterns? -4. **Simplicity** - Is this the simplest solution that works? -5. **Reversibility** - How hard to change later? - -## Project Integration - -### Learning the Codebase - -- Find 3 similar features/components -- Identify common patterns and conventions -- Use same libraries/utilities when possible -- Follow existing test patterns - -### Tooling - -- Use project's existing build system -- Use project's test framework -- Use project's formatter/linter settings -- Don't introduce new tools without strong justification - -## Quality Gates - -### Definition of Done - -- [ ] Tests written and passing -- [ ] Code follows project conventions -- [ ] No linter/formatter warnings -- [ ] Commit messages are clear -- [ ] Implementation matches plan -- [ ] No TODOs without issue numbers - -### Test Guidelines - -- Test behavior, not implementation -- One assertion per test when possible -- Clear test names describing scenario -- Use existing test utilities/helpers -- Tests should be deterministic - -## Important Reminders - -**NEVER**: -- Use `--no-verify` to bypass commit hooks -- Disable tests instead of fixing them -- *Commit code yourself* -- Make assumptions - verify with existing code - -**ALWAYS**: -- Commit working code incrementally -- Update plan documentation as you go -- Learn from existing implementations -- Stop after 3 failed attempts and reassess -- *Let the user commit changes* diff --git a/Makefile b/Makefile index 4d3a443..70f3169 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ all: clean-container build-linux-debug-container build-container: - ./flutterw --build + ./flutterw --build-container clean-container: build-container ./flutterw clean diff --git a/README.md b/README.md index 7c627a7..b43514a 100644 --- a/README.md +++ b/README.md @@ -2,135 +2,9 @@ https://github.com/Shahxad-Akram/flutter_tex/blob/master/example/lib/tex_view_ma # Math Formulae Manager -A comprehensive command-line application for managing and computing mathematical formulas across various disciplines including mathematics, physics, medicine, and engineering. - This project uses dart language, and flutter framework. It leverages d4rt library to execute formulas. # Development guidelines If you are a contributor or an agent, please follow [CLAUDE.md](./CLAUDE.md) for development guidelines. -# Formula file description - -The file is a dart array of formulas. Each formula is a dart set literal - -```dart -[ - { - "name": "Newton's second law (scalar)", - "input": { - "m" : { - "magnitude": "mass" - }, - "a" : { - "magnitude": "acceleration" - } - }, - "output": { - "F" : { - "magnitude" : "Force" - } - }, - "d4rt_code": "F = m*a;" - - }, - { - "name": 'Triangle rectangle', - "input": [ - 'b':{ "magnitude": 'meter'}, - 'c':{ "magnitude": 'meter'}, - ], - "output": { 'a': { "magnitude": 'meter' } }, - "d4rtCode": ''' - a = Math.sqrt(b*b + c*c); - ''', - } -] -``` - -# Unit file description - -```dart -[ - { - "name": 'meter', - "symbol": 'm', - "isBase": true - }, - { - "name": 'inch', - "symbol" 'in', - "baseUnit": 'meter', - "factor": 0.0254 - }, - { - "name": 'nautical mile', - "symbol": 'Nm', - "baseUnit": 'meter', - "factor": 1852 - }, - { - "name": 'Kelvin', - "symbol": "Kº", - "isBase": true, - }, - { - "name": 'Celsius', - "symbol": "Cº", - "baseUnit" : "Kelvin", - "toBase": "x + 273.15", - "fromBase": "x - 273.15" - }, - { - "name": 'Fahrenheit', - "symbol": "Fº", - "baseUnit" : "Kelvin", - "toBase": "(x - 32) × 5/9 + 273.15", - "fromBase": "x - 273.15) * 9/5 + 32" - } -] -``` - -## Features - -### Formula Search and Computation -- Search through a vast collection of formulas from multiple domains: - - Mathematics - - Physics - - Medical sciences - - Engineering - - And more! -- Input values for formula variables -- Get computed results with proper units - -### Unit Management -- Each data value includes its magnitude and unit -- Convert between different units seamlessly -- Automatic unit validation and conversion - -### Formula Editor -- Built-in formula editor using the d4rt interpreter -- Create and modify formulas with ease -- Syntax highlighting and validation - -### Formula Sharing -- Share formulas with other users -- Import formulas from the community -- Collaborative formula database - -### Rich Formula Documentation -Each formula includes: -- **The formula itself** - Mathematical expression -- **Explanation** - Detailed description in Markdown format -- **Images** - Visual diagrams, graphs, or illustrations -- **Examples** - Sample calculations and use cases - -## Getting Started - -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 - -[Contribution guidelines to be added] diff --git a/flutterw b/flutterw index 89b83bc..f72b07a 100755 --- a/flutterw +++ b/flutterw @@ -113,7 +113,7 @@ exec_in_container(){ main(){ detect_container - if [ "$1" = "--build" ]; then + if [ "$1" = "--build-container" ]; then build_image return $? elif [ "$1" = "--cleancache" ]; then @@ -123,7 +123,7 @@ main(){ exec_in_container ${@:2} return $? elif [ "$1" = "" ] || [ "$1" = "--help" ] || [ "$1" = "-h" ]; then - echo "Usage: $0 {build|cleancache|exec }" + echo "Usage: $0 {--help|--build-container|--cleancache|--exec |}" return 1 fi diff --git a/lib/formula_evaluator.dart b/lib/formula_evaluator.dart index 40e67f7..67dc45c 100644 --- a/lib/formula_evaluator.dart +++ b/lib/formula_evaluator.dart @@ -116,19 +116,39 @@ class FormulaEvaluator { void _validateInputValues(Formula formula, Map inputValues) { final missingVars = []; - + for (final inputVar in formula.inputVarNames()) { if (!inputValues.containsKey(inputVar)) { missingVars.add(inputVar); } } - + if (missingVars.isNotEmpty) { throw FormulaEvaluationException( 'Missing required input variables for formula "${formula.name}": ' '${missingVars.join(', ')}', ); } + + // Validate that input values are in the allowed values list if specified + for (final vs in formula.input) { + final values = vs.values; + if (values != null && values.isNotEmpty) { + final inputValue = inputValues[vs.name]; + if (inputValue != null) { + // Convert input value to string for comparison since allowed values are stored as strings + final inputValueAsString = inputValue.toString(); + final containsValue = values.any((allowedValue) => allowedValue.toString() == inputValueAsString); + + if (!containsValue) { + throw FormulaEvaluationException( + 'Invalid value for variable "${vs.name}" in formula "${formula.name}". ' + 'Expected one of: [${values.join(', ')}], but got: $inputValue', + ); + } + } + } + } } diff --git a/test/formula_evaluator_test.dart b/test/formula_evaluator_test.dart index 0b4c04d..8d69b99 100644 --- a/test/formula_evaluator_test.dart +++ b/test/formula_evaluator_test.dart @@ -141,6 +141,22 @@ void main() { ); }); + test('throws exception for invalid values when allowed values are specified', () { + final formula = Formula( + name: 'Test formula with allowed values', + input: [ + VariableSpec(name: 'status', unit: 'string', values: ['active', 'inactive']), + ], + output: VariableSpec(name: 'result', unit: 'string'), + d4rtCode: 'result = status;', + ); + + expect( + () => evaluator.evaluate(formula, {'status': 'invalid_value'}), // Not in allowed values + throwsA(isA()), + ); + }); + }); group('Utility methods', () { diff --git a/test/formula_models_test.dart b/test/formula_models_test.dart index 1456b3a..7b25cb4 100644 --- a/test/formula_models_test.dart +++ b/test/formula_models_test.dart @@ -120,15 +120,77 @@ void main() { final evaluator = FormulaEvaluator(); final result = evaluator.evaluate(formula, { - 'HeartRate': 2, - 'Breathing': 2, - 'MuscleTone': 2, - 'Reflexes': 2, - 'SkinColor': 2 + 'HeartRate': '> 100 bpm', + 'Breathing': 'Strong, robust cry', + 'MuscleTone': 'Flexed arms/leg, resists extension', + 'Reflexes': 'Cry on stimulation', + 'SkinColor': 'Pink' }); expect(result, 'Score: 10 - Normal'); }); + + test('evaluates APGAR score formula - Good condition case', () async { + final corpus = await testCorpus; + final formula = corpus.getFormula("Apgar Score")!; + final evaluator = FormulaEvaluator(); + + final result = evaluator.evaluate(formula, { + 'HeartRate': '> 100 bpm', // 2 + 'Breathing': 'Strong, robust cry', // 2 + 'MuscleTone': 'Some', // 1 + 'Reflexes': 'Grimace on aggressive stimulation', // 1 + 'SkinColor': 'Blue extremities, pink body' // 1 + }); + + expect(result, 'Score: 7 - Normal'); + }); + + test('evaluates APGAR score formula - Needs assistance case', () async { + final corpus = await testCorpus; + final formula = corpus.getFormula("Apgar Score")!; + final evaluator = FormulaEvaluator(); + + final result = evaluator.evaluate(formula, { + 'HeartRate': '> 100 bpm', // 2 + 'Breathing': 'Weak, irregular', // 1 + 'MuscleTone': 'Some', // 1 + 'Reflexes': 'Grimace on aggressive stimulation', // 1 + 'SkinColor': 'Blue extremities, pink body' // 1 + }); + + expect(result, 'Score: 6 - Needs assistance'); + }); + + test('evaluates APGAR score formula - Critical condition case', () async { + final corpus = await testCorpus; + final formula = corpus.getFormula("Apgar Score")!; + final evaluator = FormulaEvaluator(); + + final result = evaluator.evaluate(formula, { + 'HeartRate': 'Absent', // 0 + 'Breathing': 'Absent', // 0 + 'MuscleTone': 'None', // 0 + 'Reflexes': 'No response', // 0 + 'SkinColor': 'Blue or pale' // 0 + }); + + expect(result, 'Score: 0 - Critical condition'); + }); + + test('evaluates APGAR score formula - Invalid value throws exception', () async { + final corpus = await testCorpus; + final formula = corpus.getFormula("Apgar Score")!; + final evaluator = FormulaEvaluator(); + + expect(() => evaluator.evaluate(formula, { + 'HeartRate': 'Invalid Value', // Not in allowed values + 'Breathing': 'Absent', // 0 + 'MuscleTone': 'None', // 0 + 'Reflexes': 'No response', // 0 + 'SkinColor': 'Blue or pale' // 0 + }), throwsA(isA())); + }); }); } diff --git a/units-and-formulas-definition.md b/units-and-formulas-definition.md deleted file mode 100644 index edb1c87..0000000 --- a/units-and-formulas-definition.md +++ /dev/null @@ -1,111 +0,0 @@ -# Units and Formulas Definition Format - -## Units Specification - -Units are defined in `.d4rt.units` files using JSON arrays. Each unit can have: - -```dart -{ - "name": "unit name", // Full name (required) - "symbol": "abbr", // Symbol (required) - "isBase": true, // Mark as base unit (exclusive with baseUnit) - "baseUnit": "parent", // Reference unit for conversions - "factor": 1.0, // Conversion factor to base unit - "toBase": "x * 1000", // Conversion code to base (expression) - "fromBase": "x / 1000" // Conversion code from base (expression) -} -``` - -### Key Rules: -1. Use either `isBase` OR `baseUnit`+conversion (factor or code) -2. Factor-based units use simple multiplicative conversions -3. Code-based units require both `toBase` and `fromBase` Dart expressions -4. Code expressions: - - Use `x` as input variable - - Must return a numeric value - - Can use math functions via `dart:math` - -### Examples: -**Simple Factor-based:** -```json -{ - "name": "kilometer", - "symbol": "km", - "baseUnit": "meter", - "factor": 1000 -} -``` - -**Code-based Conversion:** -```json -{ - "name": "Fahrenheit", - "symbol": "°F", - "baseUnit": "Kelvin", - "toBase": "(x - 32) * 5/9 + 273.15", - "fromBase": "(x - 273.15) * 9/5 + 32" -} -``` - -## Formulas Specification - -Formulas are defined in `.d4rt` files using JSON arrays. Each formula has: - -```dart -{ - "name": "Formula Name", // Required - "description": "Markdown", // Optional - "input": [ // List of input variables - { - "name": "varName", // Variable identifier - "unit": "unitName" // Base unit for calculations - } - ], - "output": { // Single output variable - "name": "resultVar", - "unit": "outputUnit" - }, - "d4rtCode": "Dart code", // Calculation logic - "tags": ["physics", "energy"] // Categories -} -``` - -### Formula Code Rules: -1. Input variables are declared as `final` -2. Output variable must be assigned -3. Can use: - - Basic math operators - - `dart:math` functions - - Custom functions from FormulaEvaluator -4. Example: -```dart -// Inputs: m (kg), v (m/s) -d4rtCode: "KE = 0.5 * m * pow(v, 2);" -``` - -### Conversion Code Examples - -**Expression-style:** -```dart -// Simple factor conversion -final x = 5; // Value in source unit -main() => x * 1000; // Convert to base unit -``` - -**Statement-style:** -```dart -// Complex conversion with multiple steps -final x = 212; // Fahrenheit -main() { - var celsius = (x - 32) * 5/9; - return celsius + 273.15; // Convert to Kelvin -} -``` - -## Validation Rules - -1. Units must form a DAG (no circular dependencies) -2. Formulas must declare all input variables -3. Output variable must be assigned in d4rtCode -4. Unit conversion code must handle numeric inputs -5. Base units must exist before derived units