Commit d71d328e authored by Christophe Benz's avatar Christophe Benz

Update transpiler

parent d4079236
......@@ -5,8 +5,8 @@ from toolz import get_in, keyfilter
def find_restituee_variables():
from .generated.variables_definitions import variable_definition_by_name
return keyfilter(is_restituee_variable, variable_definition_by_name).keys()
from .generated.variables_definitions import definition_by_variable_name
return list(keyfilter(is_restituee_variable, definition_by_variable_name).keys())
def get_variable_applications(variable_name):
......@@ -14,8 +14,8 @@ def get_variable_applications(variable_name):
def get_variable_definition(variable_name, default=None):
from .generated.variables_definitions import variable_definition_by_name
return variable_definition_by_name.get(variable_name, default)
from .generated.variables_definitions import definition_by_variable_name
return definition_by_variable_name.get(variable_name, default)
def get_variable_description(variable_name):
......@@ -52,5 +52,20 @@ def is_saisie_variable(variable_name):
return get_variable_type(variable_name) == 'variable_saisie'
# Applications tests
def is_batch(variable_name):
return is_variable_in_application(variable_name, 'batch')
def is_variable_in_application(variable_name, application_name):
return application_name in get_variable_definition(variable_name, {}).get('applications', [])
# Variable name sanitization
def sanitized_variable_name(value):
# Python variables must not begin with a digit.
return '_' + value if value[0].isdigit() else value
......@@ -7,6 +7,7 @@ from m_language_parser import dependencies_helpers
from toolz.curried import first
from . import core
from .generated.variables_definitions import definition_by_variable_name
"""Functions used by formulas"""
......@@ -51,6 +52,9 @@ def cache_result(formula_function):
def new_formula_function():
if formula_name not in result_by_formula_name_cache:
result = formula_function()
# if result == 10561.16:
# print(formula_name + ' == 10561.16')
# import ipdb; ipdb.set_trace()
result_by_formula_name_cache[formula_name] = result
return result_by_formula_name_cache[formula_name]
......@@ -59,13 +63,23 @@ def cache_result(formula_function):
def inspect():
ok_variable_tuples = inspect_cache(predicate=lambda x: x == 2461)
ok_variable_names = map(first, ok_variable_tuples)
first_ok_variable_name = first(ok_variable_names)
# import ipdb; ipdb.set_trace()
return find_restituee_variables_depending_on(first_ok_variable_name)
def inspect_cache(predicate=None, restituee=False):
ok_variable_names = list(map(first, ok_variable_tuples))
print('restituee variables in batch depending on one of {}: {}'.format(
ok_variable_names,
'\n'.join(
'{} {!r}'.format(
formula_name,
core.get_variable_description(formula_name),
)
for formula_name in filter(
core.is_batch,
find_restituee_variables_depending_on(ok_variable_names),
)
) + '\n',
))
def inspect_cache(predicate=None):
return [
(
formula_name,
......@@ -73,19 +87,46 @@ def inspect_cache(predicate=None, restituee=False):
core.get_variable_description(formula_name),
core.get_variable_applications(formula_name),
)
for formula_name, result in result_by_formula_name_cache.items()
if ((not restituee or core.is_restituee_variable(formula_name)) and
# core.is_variable_in_application(formula_name, 'batch') and
(predicate(result) if predicate is not None else True))
for formula_name, result in sorted(result_by_formula_name_cache.items())
if (predicate(result) if predicate is not None else True)
]
def find_restituee_variables_depending_on(formula_name):
restituee_variables = core.find_restituee_variables()
for restituee_variable in restituee_variables:
formula_dependencies = []
dependencies_helpers.build_formula_dependencies(
formula_dependencies=formula_dependencies,
formula_name=formula_name,
)
return formula_dependencies
def has_dependency(formula_name, dependency_formula_names, dependencies_by_formula_name):
def walk_dependencies(formula_name, dependencies):
if formula_name in dependencies:
return False
dependencies.add(formula_name)
if formula_name in dependency_formula_names:
return True
formula_dependencies = dependencies_by_formula_name.get(formula_name)
if formula_dependencies is not None:
for dependency_name in formula_dependencies:
if walk_dependencies(
dependencies=dependencies,
formula_name=dependency_name,
):
return True
return False
return walk_dependencies(
dependencies=set(),
formula_name=formula_name,
)
def find_restituee_variables_depending_on(formula_names):
dependencies_by_formula_name = dependencies_helpers.load_dependencies_by_formula_name()
# formulas_dependencies_by_formula_name = dependencies_helpers.filter_formulas_dependencies(
# definition_by_variable_name=definition_by_variable_name,
# dependencies_by_formula_name=dependencies_by_formula_name,
# )
restituee_variable_names = core.find_restituee_variables()
return list(filter(
lambda restituee_variable_name: has_dependency(
formula_name=restituee_variable_name,
dependency_formula_names=formula_names,
dependencies_by_formula_name=dependencies_by_formula_name,
),
restituee_variable_names,
))
......@@ -26,11 +26,6 @@ log = logging.getLogger(__name__)
# Helpers
def sanitized_variable_name(value):
# Python variables must not begin with a digit.
return '_' + value if value[0].isdigit() else value
def visit_infix_expression(node, operators={}):
def interleave(*iterables):
for values in itertools.zip_longest(*iterables, fillvalue=UnboundLocalError):
......@@ -141,7 +136,7 @@ def visit_float(node):
def visit_formula(node):
formula_name = node['name']
sanitized_formula_name = sanitized_variable_name(formula_name)
sanitized_formula_name = core.sanitized_variable_name(formula_name)
expression_source = visit_node(node['expression'])
source = """\
@cache_result
......@@ -222,9 +217,9 @@ def visit_symbol(node):
'base_variables[{!r}]'.format(symbol)
if core.is_base_variable(symbol)
else (
'{}()'.format(sanitized_variable_name(symbol))
'{}()'.format(core.sanitized_variable_name(symbol))
if core.is_calculee_variable(symbol)
else sanitized_variable_name(symbol)
else core.sanitized_variable_name(symbol)
)
)
......
......@@ -12,7 +12,7 @@ from toolz.curried.operator import attrgetter
from calculateur_impots import core
from calculateur_impots.generated import formulas
from calculateur_impots.generated.variables_definitions import variable_definition_by_name
from calculateur_impots.generated.variables_definitions import definition_by_variable_name
# from calculateur_impots.generated.verif_regles import verif_regles
......@@ -48,17 +48,21 @@ def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Increase output verbosity')
parser.add_argument('-d', '--debug', action='store_true', default=False, help='Display debug messages')
parser.add_argument('--calculee', dest='calculee_variable', default='IINETIR', metavar='variable',
parser.add_argument('--calculees', dest='calculee_variables', metavar='variable', nargs='+',
help='Variables calculées')
parser.add_argument('--saisie', dest='saisie_variables', metavar='nom=valeur', nargs='+', help='Variables saisies')
parser.add_argument('--saisies', dest='saisie_variables', metavar='nom=valeur', nargs='+', help='Variables saisies')
args = parser.parse_args()
logging.basicConfig(
level=logging.DEBUG if args.debug else (logging.INFO if args.verbose else logging.WARNING),
stream=sys.stdout,
)
if not core.is_restituee_variable(args.calculee_variable):
parser.error('Variable "{}" is not a variable of type "calculee restituee"'.format(args.calculee_variable))
for calculee_variable in args.calculee_variables:
if not core.is_restituee_variable(calculee_variable):
not_restituee_message = 'Variable "{}" is not a variable of type "calculee restituee"'.format(
calculee_variable)
log.warning(not_restituee_message)
# parser.error(not_restituee_message)
# Required variables_saisies: V_ANREV, V_REGCO (tag "contexte"?)
......@@ -106,7 +110,7 @@ def main():
base_variables_value_by_name = {} # Override base variables values
base_variables = {
variable_name: base_variables_value_by_name.get(variable_name, 0)
for variable_name, variable_definition in variable_definition_by_name.items()
for variable_name, variable_definition in definition_by_variable_name.items()
if core.is_base_variable(variable_name)
}
......@@ -115,60 +119,32 @@ def main():
# saisie_variables=saisie_variables,
# )
# From ABPRi
# ABPRV = arr((((1 - IND_APBV) * min(APBV, (
# PL_PB and PL_PB * APBV / somme([
# (APBV * (1 - IND_APBV)), (APBC * (1 - IND_APBC)), (APB1 * (1 - IND_APB1)), (APB2 * (1 - IND_APB2)),
# (APB3 * (1 - IND_APB3)), (APB4 * (1 - IND_APB4))
# ])
# ))) + (IND_APBV * APBV)))
# ABPRV : calculee : "Abattement brut avant ajustement (pensions retraites 10%)" ;
# IND_APBV : calculee : "booleen application deduction minimale pensions" ;
# APBV : calculee : "Minimum d'abattement 10% (PR)" ;
# PL_PB : calculee : "plafond pensions a repartir" ;
formulas_results = formulas.compute(
# FIXME Rename "compute"
formulas_functions = formulas.compute(
base_variables=base_variables,
saisie_variables=saisie_variables,
)
# log.debug('formulas_results: {}'.format(formulas_results))
try:
requested_formula_result = formulas_results[args.calculee_variable]()
except:
# import json
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
frames = traceback.extract_tb(exc_traceback)
function_names = pipe(frames, map(attrgetter('name')), take_nth(2), drop(1), list)
print(function_names)
# name_and_dependencies_pairs = list(zip(
# function_names,
# map(
# lambda formula_name: list(map(
# lambda dependency_name: (
# dependency_name,
# formulas_helpers.result_by_formula_name_cache.get(dependency_name),
# ),
# variables_dependencies_by_name[formula_name],
# )),
# function_names,
# ),
# ))
# from pprint import pprint
# pprint(name_and_dependencies_pairs)
# function_values = map(
# lambda formula_name: formulas_helpers.result_by_formula_name_cache[formula_name],
# function_names,
# )
# function_name_and_value_pairs = list(zip(function_names, function_values))
# print(function_name_and_value_pairs)
raise
else:
print('{} = {}'.format(args.calculee_variable, requested_formula_result))
for calculee_variable_name in args.calculee_variables:
sanitized_calculee_variable = core.sanitized_variable_name(calculee_variable_name)
requested_formula_result = formulas_functions[sanitized_calculee_variable]()
# from calculateur_impots import formulas_helpers
# from pprint import pformat
# try:
# requested_formula_result = formulas_functions[sanitized_calculee_variable]()
# except:
# import traceback
# exc_type, exc_value, exc_traceback = sys.exc_info()
# frames = traceback.extract_tb(exc_traceback)
# raise Exception(pformat({
# 'stack_functions': pipe(frames, map(attrgetter('name')), take_nth(2), drop(1), list),
# 'eq_2641': formulas_helpers.inspect_cache(predicate=lambda x: x == 2461),
# }, width=200))
# print(pformat({
# 'eq_2641': formulas_helpers.inspect_cache(predicate=lambda x: x == 2461),
# }, width=200))
print('{} = {} ({})'.format(calculee_variable_name, requested_formula_result,
core.get_variable_description(calculee_variable_name)))
return 0
......
......@@ -19,9 +19,10 @@ import pprint
import sys
import textwrap
from m_language_parser import dependencies_helpers
from toolz.curried import concat, concatv, filter, map, mapcat, pipe, sorted, valmap
from calculateur_impots import python_source_visitors
from calculateur_impots import core, python_source_visitors
# Globals
......@@ -157,10 +158,10 @@ def main():
# Transpile variables definitions
variables_definitions_file_name = os.path.join('data', 'variables_definitions.json')
variable_definition_by_name = read_json_file(json_file_name=variables_definitions_file_name)
definition_by_variable_name = read_json_file(json_file_name=variables_definitions_file_name)
write_source_file(
file_name='variables_definitions.py',
source='variable_definition_by_name = {}\n'.format(pprint.pformat(variable_definition_by_name, width=120)),
source='definition_by_variable_name = {}\n'.format(pprint.pformat(definition_by_variable_name, width=120)),
)
# # Transpile verification functions
......@@ -185,7 +186,7 @@ def main():
# Read formulas order
ordered_formulas_names = read_json_file(json_file_name=os.path.join('data', 'ordered_formulas.json'))
# ordered_formulas_names = read_json_file(json_file_name=os.path.join('data', 'ordered_formulas.json'))
# Transpile formulas
......@@ -195,10 +196,10 @@ def main():
))
source_by_formula_name = {}
for formula_name, source in formula_name_and_source_pairs:
if formula_name not in variable_definition_by_name:
if formula_name not in definition_by_variable_name:
log.warning('Formula "{}" has no definition.'.format(formula_name))
else:
applications = variable_definition_by_name[formula_name]['applications']
applications = definition_by_variable_name[formula_name]['applications']
if formula_name not in source_by_formula_name or 'batch' in applications:
if formula_name in source_by_formula_name and 'batch' in applications:
log.warning('Formula "{}" already met from another application, '
......@@ -208,15 +209,24 @@ def main():
def get_formula_source(formula_name):
sanitized_formula_name = python_source_visitors.sanitized_variable_name(formula_name)
sanitized_formula_name = core.sanitized_variable_name(formula_name)
return source_by_formula_name[sanitized_formula_name] \
if sanitized_formula_name in source_by_formula_name \
else ('# WARNING: the variable "{name}" is used in a formula at least, but is not defined.\n' +
'def {name}(): return 0').format(name=sanitized_formula_name)
'def {name}(): return 0\n').format(name=sanitized_formula_name)
# write_source_file(
# file_name='formulas.py',
# source=formulas_file_source(map(get_formula_source, ordered_formulas_names)),
# )
dependencies_by_formula_name = dependencies_helpers.load_dependencies_by_formula_name()
formula_names = set(dependencies_by_formula_name.keys()) | set(filter(
lambda variable_name: core.is_calculee_variable(variable_name) and not core.has_tag(variable_name, 'base'),
definition_by_variable_name,
))
write_source_file(
file_name='formulas.py',
source=formulas_file_source(map(get_formula_source, ordered_formulas_names)),
source=formulas_file_source(map(get_formula_source, formula_names)),
)
return 0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment