Used qwen to validate allowed values

This commit is contained in:
Your Name 2026-02-07 11:45:25 +01:00
parent 6f352a945c
commit ffe1326629
8 changed files with 119 additions and 399 deletions

163
CLAUDE.md
View file

@ -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 <args>` to execute bash commands inside the container
- `./flutterw <args>` is a shorthand for `./flutter --exec flutter <args>`, 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*

View file

@ -2,7 +2,7 @@
all: clean-container build-linux-debug-container all: clean-container build-linux-debug-container
build-container: build-container:
./flutterw --build ./flutterw --build-container
clean-container: build-container clean-container: build-container
./flutterw clean ./flutterw clean

126
README.md
View file

@ -2,135 +2,9 @@ https://github.com/Shahxad-Akram/flutter_tex/blob/master/example/lib/tex_view_ma
# Math Formulae Manager # 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. This project uses dart language, and flutter framework. It leverages d4rt library to execute formulas.
# Development guidelines # Development guidelines
If you are a contributor or an agent, please follow [CLAUDE.md](./CLAUDE.md) for 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]

View file

@ -113,7 +113,7 @@ exec_in_container(){
main(){ main(){
detect_container detect_container
if [ "$1" = "--build" ]; then if [ "$1" = "--build-container" ]; then
build_image build_image
return $? return $?
elif [ "$1" = "--cleancache" ]; then elif [ "$1" = "--cleancache" ]; then
@ -123,7 +123,7 @@ main(){
exec_in_container ${@:2} exec_in_container ${@:2}
return $? return $?
elif [ "$1" = "" ] || [ "$1" = "--help" ] || [ "$1" = "-h" ]; then elif [ "$1" = "" ] || [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
echo "Usage: $0 {build|cleancache|exec <command>}" echo "Usage: $0 {--help|--build-container|--cleancache|--exec <command>|<flutter args>}"
return 1 return 1
fi fi

View file

@ -116,19 +116,39 @@ class FormulaEvaluator {
void _validateInputValues(Formula formula, Map<String, dynamic> inputValues) { void _validateInputValues(Formula formula, Map<String, dynamic> inputValues) {
final missingVars = <String>[]; final missingVars = <String>[];
for (final inputVar in formula.inputVarNames()) { for (final inputVar in formula.inputVarNames()) {
if (!inputValues.containsKey(inputVar)) { if (!inputValues.containsKey(inputVar)) {
missingVars.add(inputVar); missingVars.add(inputVar);
} }
} }
if (missingVars.isNotEmpty) { if (missingVars.isNotEmpty) {
throw FormulaEvaluationException( throw FormulaEvaluationException(
'Missing required input variables for formula "${formula.name}": ' 'Missing required input variables for formula "${formula.name}": '
'${missingVars.join(', ')}', '${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',
);
}
}
}
}
} }

View file

@ -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<FormulaEvaluationException>()),
);
});
}); });
group('Utility methods', () { group('Utility methods', () {

View file

@ -120,15 +120,77 @@ void main() {
final evaluator = FormulaEvaluator(); final evaluator = FormulaEvaluator();
final result = evaluator.evaluate(formula, { final result = evaluator.evaluate(formula, {
'HeartRate': 2, 'HeartRate': '> 100 bpm',
'Breathing': 2, 'Breathing': 'Strong, robust cry',
'MuscleTone': 2, 'MuscleTone': 'Flexed arms/leg, resists extension',
'Reflexes': 2, 'Reflexes': 'Cry on stimulation',
'SkinColor': 2 'SkinColor': 'Pink'
}); });
expect(result, 'Score: 10 - Normal'); 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<FormulaEvaluationException>()));
});
}); });
} }

View file

@ -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