Merge branch 'web-server'
This commit is contained in:
commit
6fe64c1d85
10 changed files with 109 additions and 18 deletions
10
Dockerfile
10
Dockerfile
|
|
@ -10,6 +10,16 @@ ENV PUB_CACHE=/cache/pub-cache
|
|||
ENV GRADLE_USER_HOME=/cache/gradle-cache
|
||||
RUN mkdir -p $PUB_CACHE $GRADLE_USER_HOME
|
||||
|
||||
# To avoid: fatal: detected dubious ownership in repository at '/sdks/flutter'
|
||||
# Pass this during build: --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)
|
||||
ARG USER_ID
|
||||
ARG GROUP_ID
|
||||
RUN echo "Using UID: $USER_ID and GID: $GROUP_ID"
|
||||
RUN chown -R $USER_ID:$GROUP_ID $PUB_CACHE $GRADLE_USER_HOME
|
||||
RUN chown -R $USER_ID:$GROUP_ID /sdks/flutter
|
||||
|
||||
USER $USER_ID:$GROUP_ID
|
||||
|
||||
# Copy pubspec files and get dependencies
|
||||
# Commented out to avoid building the app during image creation, this will be handled externally by makefile
|
||||
# COPY pubspec.yaml pubspec.lock ./
|
||||
|
|
|
|||
11
Makefile
11
Makefile
|
|
@ -19,11 +19,14 @@ build-linux-debug-container: pub-get-container
|
|||
build-web-debug-container: pub-get-container
|
||||
./docker-exec.sh exec flutter build web --debug
|
||||
|
||||
run-linux-debug-container: build-linux-debug-container
|
||||
run-linux-debug-container: pub-get-container
|
||||
./docker-exec.sh exec flutter run -d linux
|
||||
|
||||
run-linux-debug: build-linux-debug-container
|
||||
run-web-debug-container: pub-get-container
|
||||
./docker-exec.sh exec flutter run --web-port $${WEB_PORT:-8081} -d web-server
|
||||
|
||||
run-linux-debug-native: build-linux-debug-container
|
||||
./build/linux/x64/debug/bundle/d4rt_formulas
|
||||
|
||||
run-web-debug-container: build-web-debug-container
|
||||
cd build/web && python3 -m http.server 8080
|
||||
run-web-debug-native: build-web-debug-container
|
||||
cd build/web && python3 -m http.server $${WEB_PORT:-8081}
|
||||
|
|
|
|||
|
|
@ -132,9 +132,9 @@ Where:
|
|||
{"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"},
|
||||
"output": {"name": "Result", "unit": "string"},
|
||||
"d4rtCode": """
|
||||
var total = HeartRate + Breathing + MuscleTone + Reflexes + SkinColor;
|
||||
var total = indexOf("HeartRate") + indexOf("Breathing") + indexOf("MuscleTone") + indexOf("Reflexes") + indexOf("SkinColor");
|
||||
late var interpretation;
|
||||
if( total < 4 ) {
|
||||
interpretation = 'Critical condition';
|
||||
|
|
@ -154,12 +154,12 @@ Where:
|
|||
"name": "Compare price per mass",
|
||||
"description": "Compares two products by their price per mass and returns which is cheaper.",
|
||||
"input": [
|
||||
{"name": "price1", "unit": "scalar"},
|
||||
{"name": "price1", "unit": "currency"},
|
||||
{"name": "mass1", "unit": "kilogram"},
|
||||
{"name": "price2", "unit": "scalar"},
|
||||
{"name": "price2", "unit": "currency"},
|
||||
{"name": "mass2", "unit": "kilogram"}
|
||||
],
|
||||
"output": {"name": "Result", "unit": "scalar"},
|
||||
"output": {"name": "Result", "unit": "string"},
|
||||
"d4rtCode": """
|
||||
var p1 = price1 / mass1;
|
||||
var p2 = price2 / mass2;
|
||||
|
|
|
|||
3
assets/units/currency.d4rt.units
Normal file
3
assets/units/currency.d4rt.units
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[
|
||||
{"name": "currency", "symbol": "¤", "isBase": true},
|
||||
]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
[
|
||||
{"name": "scalar", "symbol": "", "isBase": true},
|
||||
{"name": "scalar", "symbol": "㊷", "isBase": true},
|
||||
{"name": "string", "symbol": "🔤", "isBase": true}
|
||||
]
|
||||
|
|
@ -19,9 +19,12 @@ detect_container(){
|
|||
fi
|
||||
}
|
||||
|
||||
clean_build_cache(){
|
||||
$DOCKER builder prune --all --force
|
||||
}
|
||||
|
||||
build_image(){
|
||||
$DOCKER build -t d4rt-formulas-builder -f Dockerfile .
|
||||
$DOCKER build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) --progress=plain -t d4rt-formulas-builder -f Dockerfile .
|
||||
}
|
||||
|
||||
graphic_options(){
|
||||
|
|
@ -45,19 +48,37 @@ graphic_options(){
|
|||
fi
|
||||
}
|
||||
|
||||
exec_in_container(){
|
||||
local SPIOPTIONS="--env AT_SPI_BUS=/run/user/$(id -u)/at-spi/bus_0 --volume=/run/user/$(id -u)/at-spi:/run/user/$(id -u)/at-spi --device=/dev/dri"
|
||||
spi_options(){
|
||||
if [ -e /run/user/$(id -u)/at-spi/bus_0 ]
|
||||
then
|
||||
printf " %s " "--env AT_SPI_BUS=/run/user/$(id -u)/at-spi/bus_0"
|
||||
fi
|
||||
|
||||
if [ -e /run/user/$(id -u)/at-spi ]
|
||||
then
|
||||
printf " %s " "--volume=/run/user/$(id -u)/at-spi:/run/user/$(id -u)/at-spi"
|
||||
fi
|
||||
if [ -e /dev/dri ]
|
||||
then
|
||||
printf " %s " "--device /dev/dri"
|
||||
fi
|
||||
}
|
||||
|
||||
exec_in_container(){
|
||||
SPIOPTIONS=$(spi_options)
|
||||
local GRAPHICOPTIONS=$(graphic_options)
|
||||
local BUILDCACHE=./.build-container-cache
|
||||
mkdir -p $BUILDCACHE
|
||||
|
||||
$DOCKER run \
|
||||
-it \
|
||||
--userns=keep-id \
|
||||
--user $(id -u):$(id -g) \
|
||||
--init \
|
||||
--rm \
|
||||
$GRAPHICOPTIONS \
|
||||
$SPIOPTIONS \
|
||||
-p ${WEBPORT:-8081}:8081 \
|
||||
-v $BUILDCACHE:/cache:z \
|
||||
-v .:/app:z \
|
||||
-e FLUTTER_FLAVOR=prod \
|
||||
|
|
@ -73,12 +94,18 @@ main(){
|
|||
return $?
|
||||
fi
|
||||
|
||||
if [ "$1" = "cleancache" ]; then
|
||||
clean_build_cache
|
||||
return $?
|
||||
fi
|
||||
|
||||
|
||||
if [ "$1" = "exec" ]; then
|
||||
exec_in_container ${@:2}
|
||||
return $?
|
||||
fi
|
||||
|
||||
echo "Usage: $0 {build|exec <command>}"
|
||||
echo "Usage: $0 {build|cleancache|exec <command>}"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,6 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
|||
|
||||
void _evaluateFormula() {
|
||||
print( "EVALUATE FORMULA");
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
try {
|
||||
final inputValues = <String, dynamic>{};
|
||||
|
|
@ -145,7 +144,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
|||
|
||||
// Convert output to selected unit if needed
|
||||
String? unit = widget.formula.output.unit;
|
||||
if (unit != null && unit is Number) {
|
||||
if (unit != null && result is Number) {
|
||||
final converted = widget.corpus.convert(result, unit, _selectedOutputUnit!);
|
||||
if (converted is num) {
|
||||
_result = converted.toStringAsFixed(2);
|
||||
|
|
@ -274,6 +273,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
|||
onUnitChanged: (unit) {
|
||||
_selectedOutputUnit = unit;
|
||||
_evaluateFormula();
|
||||
print( "En output unit changed to $unit: $_result");
|
||||
setState(() {
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ Future<Corpus> createDefaultCorpus() async{
|
|||
final unitResources = [
|
||||
"assets/units/angle.d4rt.units",
|
||||
"assets/units/area.d4rt.units",
|
||||
"assets/units/currency.d4rt.units",
|
||||
"assets/units/distance.d4rt.units",
|
||||
"assets/units/energy.d4rt.units",
|
||||
"assets/units/force.d4rt.units",
|
||||
|
|
@ -32,6 +33,7 @@ Future<Corpus> createDefaultCorpus() async{
|
|||
];
|
||||
|
||||
for (final unitRes in unitResources) {
|
||||
print( "Loading units from $unitRes");
|
||||
final literal = await loadResourceAsString(unitRes);
|
||||
final units = UnitSpec.fromArrayStringLiteral(literal);
|
||||
corpus.loadUnits(units);
|
||||
|
|
|
|||
|
|
@ -104,8 +104,9 @@ class FormulaEvaluator {
|
|||
final result = _interpreter.execute(source: completeSource);
|
||||
return result;
|
||||
}
|
||||
catch (e) {
|
||||
catch (e, stack) {
|
||||
print( "Error evaluating formula source:\n$completeSource" );
|
||||
print( stack );
|
||||
throw FormulaEvaluationException(
|
||||
'Error evaluating formula "${formula.name}": $e',
|
||||
e,
|
||||
|
|
@ -141,6 +142,8 @@ class FormulaEvaluator {
|
|||
import "package:d4rt_formulas.dart";
|
||||
""";
|
||||
|
||||
static const reservedVariableNames = { "variableValues", "indexOf", "variableAllowedValues"} ;
|
||||
|
||||
String _buildCompleteSource(Formula formula, Map<String, dynamic> inputValues) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
|
|
@ -168,6 +171,29 @@ class FormulaEvaluator {
|
|||
""");
|
||||
}
|
||||
}
|
||||
|
||||
buffer.writeln("""
|
||||
final variableValues = <String, dynamic>{
|
||||
""");
|
||||
for (final entry in inputValues.entries) {
|
||||
final varName = entry.key;
|
||||
final value = entry.value;
|
||||
|
||||
if (value is String) {
|
||||
final escapedValue = value.replaceAll('"', '\\"');
|
||||
buffer.writeln("""
|
||||
"$varName": "$escapedValue",
|
||||
""");
|
||||
} else {
|
||||
buffer.writeln("""
|
||||
"$varName": "$value",
|
||||
""");
|
||||
}
|
||||
}
|
||||
buffer.writeln("""
|
||||
};
|
||||
""");
|
||||
|
||||
// Build a Map<String, List<String>> named `variableValues` that exposes allowed values
|
||||
// for each VariableSpec (inputs and output) to the interpreted code. Values are
|
||||
// converted to strings and quoted in the produced d4rt source.
|
||||
|
|
@ -187,13 +213,24 @@ class FormulaEvaluator {
|
|||
}
|
||||
|
||||
// Write the variableValues map into the generated source without escaping names/values
|
||||
buffer.writeln("final variableValues = {");
|
||||
buffer.writeln("final variableAllowedValues = {");
|
||||
variableValuesMap.forEach((name, list) {
|
||||
final listLiteral = list.map((s) => '"' + s + '"').join(', ');
|
||||
buffer.writeln(' "' + name + '": [' + listLiteral + '],');
|
||||
});
|
||||
buffer.writeln('};');
|
||||
|
||||
// Some functions to deal with string values
|
||||
buffer.writeln("""
|
||||
// If return type is int, there is an error converting double to int 🤷
|
||||
dynamic indexOf(String inputName) {
|
||||
String value = variableValues[inputName];
|
||||
String allowedValues = variableAllowedValues[inputName];
|
||||
dynamic ret = allowedValues.indexOf(value) as int;
|
||||
return ret as int;
|
||||
}
|
||||
""");
|
||||
|
||||
buffer.writeln("""
|
||||
late var ${formula.output.name};
|
||||
${formula.d4rtCode}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:d4rt/d4rt.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:d4rt_formulas/d4rt_formulas.dart';
|
||||
|
||||
abstract class SetUtils {
|
||||
static Object safeGet(Map<Object?, Object?> map, String key) {
|
||||
|
|
@ -105,6 +106,13 @@ class VariableSpec {
|
|||
final List<dynamic>? values;
|
||||
|
||||
VariableSpec({required this.name, this.unit, this.values}){
|
||||
validate();
|
||||
}
|
||||
|
||||
void validate(){
|
||||
if( FormulaEvaluator.reservedVariableNames.contains(name) ){
|
||||
throw ArgumentError("$name: is a reserved variable name for FormulaEvaluator");
|
||||
}
|
||||
final valuesValid = values != null && values?.isNotEmpty == true;
|
||||
if( unit == null && !valuesValid ){
|
||||
throw ArgumentError("$name: at least unit or allowedValues should be valid");
|
||||
|
|
|
|||
Loading…
Reference in a new issue