def test_decomposition(print_decomposition=False):
    simulation = base.tax_benefit_system.new_scenario().init_single_entity(
        period="2013-01",
        parent1=dict(
            effectif_entreprise=3000,
            exposition_accident=3,
            code_postal_entreprise="75001",
            ratio_alternants=.025,
            salaire_de_base={"2013": 12 * 3000},
            taille_entreprise=3,
            categorie_salarie=0,
        ),
        menage=dict(zone_apl=1, ),
    ).new_simulation()

    xml_file_path = os.path.join(decompositions_directory,
                                 "fiche_de_paie_decomposition.xml")

    decomposition_json = decompositions.get_decomposition_json(
        base.tax_benefit_system, xml_file_path=xml_file_path)
    simulations = [simulation]
    response = decompositions.calculate(simulations, decomposition_json)
    if print_decomposition:
        print unicode(
            json.dumps(response,
                       encoding='utf-8',
                       ensure_ascii=False,
                       indent=2))
def test_decomposition(print_decomposition = False):
    simulation = tax_benefit_system.new_scenario().init_single_entity(
        period = "2013-01",
        parent1 = dict(
            effectif_entreprise = 3000,
            exposition_accident = 3,
            code_postal_entreprise = "75001",
            ratio_alternants = .025,
            salaire_de_base = {"2013": 12 * 3000},
            taille_entreprise = 3,
            type_sal = 0,
            ),
        menage = dict(
            zone_apl = 1,
            ),
        ).new_simulation(debug = True)

    xml_file_path = os.path.join(
        tax_benefit_system.DECOMP_DIR,
        "fiche_de_paie_decomposition.xml"
        )

    decomposition_json = decompositions.get_decomposition_json(tax_benefit_system, xml_file_path = xml_file_path)
    simulations = [simulation]
    response = decompositions.calculate(simulations, decomposition_json)
    if print_decomposition:
        print unicode(
            json.dumps(response, encoding = 'utf-8', ensure_ascii = False, indent = 2)
            )
def test_decomposition(print_decomposition = False):
    simulation = tax_benefit_system.new_scenario().init_single_entity(
        period = "2013-01",
        parent1 = dict(
            effectif_entreprise = 3000,
            exposition_accident = "tres_eleve",
            code_postal_entreprise = "75001",
            ratio_alternants = .025,
            salaire_de_base = {"2013": 12 * 3000},
            taille_entreprise = "de_20_a_249",
            categorie_salarie = "prive_non_cadre",
            ),
        menage = dict(
            zone_apl = "zone_1",
            ),
        ).new_simulation()

    xml_file_path = os.path.join(
        decompositions_directory,
        "fiche_de_paie_decomposition.xml"
        )

    decomposition_json = decompositions.get_decomposition_json(
        tax_benefit_system, xml_file_path = xml_file_path)
    simulations = [simulation]
    response = decompositions.calculate(simulations, decomposition_json)
    if print_decomposition:
        logger.debug(to_unicode(
            json.dumps(response, encoding = 'utf-8', ensure_ascii = False, indent = 2)
            ))
Ejemplo n.º 4
0
def test_decomposition(print_decomposition=False):
    from openfisca_core.decompositions import calculate, get_decomposition_json
    import json
    import os
    simulation = tax_benefit_system.new_scenario().init_single_entity(
        period="2013-01",
        parent1=dict(
            effectif_entreprise=3000,
            exposition_accident=3,
            localisation_entreprise="75001",
            ratio_alternants=.025,
            salaire_de_base={"2013": 12 * 3000},
            taille_entreprise=3,
            type_sal=0,
        ),
        menage=dict(zone_apl=1, ),
    ).new_simulation(debug=True)

    xml_file_path = os.path.join(tax_benefit_system.DECOMP_DIR,
                                 "fiche_de_paie_decomposition.xml")

    decomposition_json = get_decomposition_json(xml_file_path,
                                                tax_benefit_system)
    response = calculate(simulation, decomposition_json)
    if print_decomposition:
        print unicode(
            json.dumps(response,
                       encoding='utf-8',
                       ensure_ascii=False,
                       indent=2))
def test_decomposition_calculate():
    xml_file_path = tax_benefit_system.decomposition_file_path
    decomposition_json = decompositions.get_decomposition_json(
        tax_benefit_system, xml_file_path)
    year = 2013
    simulation = tax_benefit_system.new_scenario().init_single_entity(
        period=year,
        parent1={},
    ).new_simulation()
    decomposition = decompositions.calculate([simulation], decomposition_json)
    assert isinstance(decomposition, dict)
def test_decomposition_calculate():
    decompositions_directory = base.tax_benefit_system.DECOMP_DIR
    xml_file_path = os.path.join(decompositions_directory, base.tax_benefit_system.DEFAULT_DECOMP_FILE)
    decomposition_json = decompositions.get_decomposition_json(base.tax_benefit_system, xml_file_path)
    year = 2013
    simulation = base.tax_benefit_system.new_scenario().init_single_entity(
        period = year,
        parent1 = {},
        ).new_simulation(debug = True)
    decomposition = decompositions.calculate([simulation], decomposition_json)
    assert isinstance(decomposition, dict)
Ejemplo n.º 7
0
def test_decomposition_calculate():
    decompositions_directory = base.tax_benefit_system.DECOMP_DIR
    xml_file_path = os.path.join(decompositions_directory,
                                 base.tax_benefit_system.DEFAULT_DECOMP_FILE)
    decomposition_json = decompositions.get_decomposition_json(
        base.tax_benefit_system, xml_file_path)
    year = 2013
    simulation = base.tax_benefit_system.new_scenario().init_single_entity(
        period=year,
        parent1={},
    ).new_simulation()
    decomposition = decompositions.calculate([simulation], decomposition_json)
    assert isinstance(decomposition, dict)
Ejemplo n.º 8
0
    def init_from_decomposition_json(
            cls,
            scenario = None,
            simulation = None,
            decomposiiton_json = None,
            trace = False,
            ):
        assert scenario is not None or simulation is not None
        if simulation is None:
            simulation = scenario.new_simulation()

        # Compute for all decomposition nodes
        root_node = decompositions.calculate(simulation, decomposiiton_json)

        self = cls()
        convert_to_out_node(self, root_node)
        return self
Ejemplo n.º 9
0
def precalculate_decomposition_json(tbs):
    period = 2018

    scenario_params = {
        "period": period,
        "parent1": {
            "age": 30,
        },
        # "enfants": [
        #     {"age": 6},
        #     {"age": 8},
        #     {"age": 10}
        # ],
        "axes": [
            dict(
                count=COUNT,
                min=MIN,
                max=MAX,
                name='salaire_de_base',
            ),
        ],
    }

    scenario = tbs.new_scenario().init_single_entity(**scenario_params)
    simulation = scenario.new_simulation()

    decomposition_json = decompositions.get_decomposition_json(tbs)
    decomposition_tree = decompositions.calculate([simulation],
                                                  decomposition_json)

    # def serialize(x):
    #     if isinstance(x, collections.Iterable):
    #         return list(x)
    #     return x
    # with Path("decomposition.json").open('w') as fd:
    #     json.dump(decomposition_tree, fd, indent=2, default=serialize)

    return decomposition_tree
Ejemplo n.º 10
0
def api1_simulate(req):
    ctx = contexts.Ctx(req)
    headers = wsgihelpers.handle_cross_origin_resource_sharing(ctx)

    assert req.method == 'POST', req.method

    if conf['load_alert']:
        try:
            load_average = os.getloadavg()
        except (AttributeError, OSError):
            # When load average is not available, always accept request.
            pass
        else:
            if load_average[0] / cpu_count > 1:
                return wsgihelpers.respond_json(ctx,
                    collections.OrderedDict(sorted(dict(
                        apiVersion = 1,
                        error = collections.OrderedDict(sorted(dict(
                            code = 503,  # Service Unavailable
                            message = ctx._(u'Server is overloaded: {} {} {}').format(*load_average),
                            ).iteritems())),
                        method = req.script_name,
                        url = req.url.decode('utf-8'),
                        ).iteritems())),
                    headers = headers,
                    )

    content_type = req.content_type
    if content_type is not None:
        content_type = content_type.split(';', 1)[0].strip()
    if content_type != 'application/json':
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                error = collections.OrderedDict(sorted(dict(
                    code = 400,  # Bad Request
                    message = ctx._(u'Bad content-type: {}').format(content_type),
                    ).iteritems())),
                method = req.script_name,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )

    inputs, error = conv.pipe(
        conv.make_input_to_json(object_pairs_hook = collections.OrderedDict),
        conv.test_isinstance(dict),
        conv.not_none,
        )(req.body, state = ctx)
    if error is not None:
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                error = collections.OrderedDict(sorted(dict(
                    code = 400,  # Bad Request
                    errors = [conv.jsonify_value(error)],
                    message = ctx._(u'Invalid JSON in request POST body'),
                    ).iteritems())),
                method = req.script_name,
                params = req.body,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )

    str_to_reforms = conv.make_str_to_reforms()

    data, errors = conv.struct(
        dict(
            # api_key = conv.pipe(  # Shared secret between client and server
            #     conv.test_isinstance(basestring),
            #     conv.input_to_uuid_str,
            #     conv.not_none,
            #     ),
            base_reforms = str_to_reforms,
            context = conv.test_isinstance(basestring),  # For asynchronous calls
            reforms = str_to_reforms,
            scenarios = conv.pipe(
                conv.test_isinstance(list),
                conv.uniform_sequence(
                    conv.not_none,  # Real conversion is done once tax-benefit system is known.
                    ),
                conv.test(lambda scenarios: len(scenarios) >= 1, error = N_(u'At least one scenario is required')),
                conv.test(lambda scenarios: len(scenarios) <= 100,
                    error = N_(u"There can't be more than 100 scenarios")),
                conv.not_none,
                ),
            trace = conv.pipe(
                conv.test_isinstance((bool, int)),
                conv.anything_to_bool,
                conv.default(False),
                ),
            validate = conv.pipe(
                conv.test_isinstance((bool, int)),
                conv.anything_to_bool,
                conv.default(False),
                ),
            ),
        )(inputs, state = ctx)

    if errors is None:
        country_tax_benefit_system = model.tax_benefit_system
        base_tax_benefit_system = model.get_cached_composed_reform(
            reform_keys = data['base_reforms'],
            tax_benefit_system = country_tax_benefit_system,
            ) if data['base_reforms'] is not None else country_tax_benefit_system
        if data['reforms'] is not None:
            reform_tax_benefit_system = model.get_cached_composed_reform(
                reform_keys = data['reforms'],
                tax_benefit_system = base_tax_benefit_system,
                )

        base_scenarios, base_scenarios_errors = conv.uniform_sequence(
            base_tax_benefit_system.Scenario.make_json_to_cached_or_new_instance(
                ctx = ctx,
                repair = data['validate'],
                tax_benefit_system = base_tax_benefit_system,
                )
            )(data['scenarios'], state = ctx)
        errors = {'scenarios': base_scenarios_errors} if base_scenarios_errors is not None else None

        if errors is None and data['reforms'] is not None:
            reform_scenarios, reform_scenarios_errors = conv.uniform_sequence(
                reform_tax_benefit_system.Scenario.make_json_to_cached_or_new_instance(
                    ctx = ctx,
                    repair = data['validate'],
                    tax_benefit_system = reform_tax_benefit_system,
                    )
                )(data['scenarios'], state = ctx)
            errors = {'scenarios': reform_scenarios_errors} if reform_scenarios_errors is not None else None

    if errors is not None:
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                context = inputs.get('context'),
                error = collections.OrderedDict(sorted(dict(
                    code = 400,  # Bad Request
                    errors = [conv.jsonify_value(errors)],
                    message = ctx._(u'Bad parameters in request'),
                    ).iteritems())),
                method = req.script_name,
                params = inputs,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )

#    api_key = data['api_key']
#    account = model.Account.find_one(
#        dict(
#            api_key = api_key,
#            ),
#        as_class = collections.OrderedDict,
#        )
#    if account is None:
#        return wsgihelpers.respond_json(ctx,
#            collections.OrderedDict(sorted(dict(
#                apiVersion = 1,
#                context = data['context'],
#                error = collections.OrderedDict(sorted(dict(
#                    code = 401,  # Unauthorized
#                    message = ctx._('Unknown API Key: {}').format(api_key),
#                    ).iteritems())),
#                method = req.script_name,
#                params = inputs,
#                url = req.url.decode('utf-8'),
#                ).iteritems())),
#            headers = headers,
#            )

    scenarios = base_scenarios if data['reforms'] is None else reform_scenarios

    suggestions = {}
    for scenario_index, scenario in enumerate(scenarios):
        if data['validate']:
            original_test_case = scenario.test_case
            scenario.test_case = copy.deepcopy(original_test_case)
        suggestion = scenario.suggest()  # This modifies scenario.test_case!
        if data['validate']:
            scenario.test_case = original_test_case
        if suggestion is not None:
            suggestions.setdefault('scenarios', {})[scenario_index] = suggestion
    if not suggestions:
        suggestions = None

    if data['validate']:
        # Only a validation is requested. Don't launch simulation
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                context = inputs.get('context'),
                method = req.script_name,
                params = inputs,
                repaired_scenarios = [
                    scenario.to_json()
                    for scenario in scenarios
                    ],
                suggestions = suggestions,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )

    decomposition_json = model.get_cached_or_new_decomposition_json(tax_benefit_system = base_tax_benefit_system)
    base_simulations = [scenario.new_simulation(trace = data['trace']) for scenario in base_scenarios]
    base_response_json = decompositions.calculate(base_simulations, decomposition_json)

    if data['reforms'] is not None:
        reform_decomposition_json = model.get_cached_or_new_decomposition_json(
            tax_benefit_system = reform_tax_benefit_system,
            )
        reform_simulations = [scenario.new_simulation(trace = data['trace']) for scenario in reform_scenarios]
        reform_response_json = decompositions.calculate(reform_simulations, reform_decomposition_json)

    if data['trace']:
        simulations_variables_json = []
        tracebacks_json = []
        simulations = reform_simulations if data['reforms'] is not None else base_simulations
        for simulation in simulations:
            simulation_variables_json = {}
            traceback_json = []
            for (variable_name, period), step in simulation.traceback.iteritems():
                holder = step['holder']
                if variable_name not in simulation_variables_json:
                    variable_value_json = holder.to_value_json()
                    if variable_value_json is not None:
                        simulation_variables_json[variable_name] = variable_value_json
                input_variables_infos = step.get('input_variables_infos')
                column = holder.column
                traceback_json.append(dict(
                    cell_type = column.val_type,  # Unification with OpenFisca Julia name.
                    default_input_variables = step.get('default_input_variables', False),
                    entity = column.entity,
                    input_variables = [
                        (variable_name, str(variable_period))
                        for variable_name, variable_period in input_variables_infos
                        ] if input_variables_infos else None,
                    is_computed = step.get('is_computed', False),
                    label = column.label if column.label != variable_name else None,
                    name = variable_name,
                    period = str(period) if period is not None else None,
                    ))
            simulations_variables_json.append(simulation_variables_json)
            tracebacks_json.append(traceback_json)
    else:
        simulations_variables_json = None
        tracebacks_json = None

    response_data = dict(
        apiVersion = 1,
        context = data['context'],
        method = req.script_name,
        params = inputs,
        suggestions = suggestions,
        tracebacks = tracebacks_json,
        url = req.url.decode('utf-8'),
        value = reform_response_json if data['reforms'] is not None else base_response_json,
        variables = simulations_variables_json,
        )
    if data['reforms'] is not None:
        response_data['base_value'] = base_response_json
    return wsgihelpers.respond_json(ctx,
        collections.OrderedDict(sorted(response_data.iteritems())),
        headers = headers,
        )
Ejemplo n.º 11
0
def api1_simulate(req):
    wsgihelpers.track(req.url.decode('utf-8'))
    ctx = contexts.Ctx(req)
    headers = wsgihelpers.handle_cross_origin_resource_sharing(ctx)

    assert req.method == 'POST', req.method

    if conf['load_alert']:
        try:
            load_average = os.getloadavg()
        except (AttributeError, OSError):
            # When load average is not available, always accept request.
            pass
        else:
            if load_average[0] / environment.cpu_count > 1:
                return wsgihelpers.respond_json(
                    ctx,
                    collections.OrderedDict(
                        sorted(
                            dict(
                                apiVersion=1,
                                error=collections.OrderedDict(
                                    sorted(
                                        dict(
                                            code=503,  # Service Unavailable
                                            message=ctx.
                                            _(u'Server is overloaded: {} {} {}'
                                              ).format(*load_average),
                                        ).iteritems())),
                                method=req.script_name,
                                url=req.url.decode('utf-8'),
                            ).iteritems())),
                    headers=headers,
                )

    content_type = req.content_type
    if content_type is not None:
        content_type = content_type.split(';', 1)[0].strip()
    if content_type != 'application/json':
        return wsgihelpers.respond_json(
            ctx,
            collections.OrderedDict(
                sorted(
                    dict(
                        apiVersion=1,
                        error=collections.OrderedDict(
                            sorted(
                                dict(
                                    code=400,  # Bad Request
                                    message=ctx._(u'Bad content-type: {}').
                                    format(content_type),
                                ).iteritems())),
                        method=req.script_name,
                        url=req.url.decode('utf-8'),
                    ).iteritems())),
            headers=headers,
        )

    inputs, error = conv.pipe(
        conv.make_input_to_json(object_pairs_hook=collections.OrderedDict),
        conv.test_isinstance(dict),
        conv.not_none,
    )(req.body, state=ctx)
    if error is not None:
        return wsgihelpers.respond_json(
            ctx,
            collections.OrderedDict(
                sorted(
                    dict(
                        apiVersion=1,
                        error=collections.OrderedDict(
                            sorted(
                                dict(
                                    code=400,  # Bad Request
                                    errors=[conv.jsonify_value(error)],
                                    message=ctx._(
                                        u'Invalid JSON in request POST body'),
                                ).iteritems())),
                        method=req.script_name,
                        params=req.body,
                        url=req.url.decode('utf-8'),
                    ).iteritems())),
            headers=headers,
        )

    str_list_to_reforms = conv.make_str_list_to_reforms()

    data, errors = conv.struct(
        dict(
            base_reforms=str_list_to_reforms,
            context=conv.test_isinstance(basestring),  # For asynchronous calls
            reforms=str_list_to_reforms,
            scenarios=conv.pipe(
                conv.test_isinstance(list),
                conv.uniform_sequence(
                    conv.
                    not_none,  # Real conversion is done once tax-benefit system is known.
                ),
                conv.test(lambda scenarios: len(scenarios) >= 1,
                          error=N_(u'At least one scenario is required')),
                conv.test(lambda scenarios: len(scenarios) <= 100,
                          error=N_(u"There can't be more than 100 scenarios")),
                conv.not_none,
            ),
            trace=conv.pipe(
                conv.test_isinstance((bool, int)),
                conv.anything_to_bool,
                conv.default(False),
            ),
            validate=conv.pipe(
                conv.test_isinstance((bool, int)),
                conv.anything_to_bool,
                conv.default(False),
            ),
        ), )(inputs, state=ctx)

    if errors is None:
        country_tax_benefit_system = model.tax_benefit_system
        base_tax_benefit_system = model.get_cached_composed_reform(
            reform_keys=data['base_reforms'],
            tax_benefit_system=country_tax_benefit_system,
        ) if data['base_reforms'] is not None else country_tax_benefit_system
        if data['reforms'] is not None:
            reform_tax_benefit_system = model.get_cached_composed_reform(
                reform_keys=data['reforms'],
                tax_benefit_system=base_tax_benefit_system,
            )
        try:
            base_scenarios, base_scenarios_errors = conv.uniform_sequence(
                base_tax_benefit_system.Scenario.
                make_json_to_cached_or_new_instance(
                    ctx=ctx,
                    repair=data['validate'],
                    tax_benefit_system=base_tax_benefit_system,
                ))(data['scenarios'], state=ctx)
        except (ValueError, VariableNotFound) as exc:
            wsgihelpers.handle_error(exc, ctx, headers)

        errors = {
            'scenarios': base_scenarios_errors
        } if base_scenarios_errors is not None else None

        if errors is None and data['reforms'] is not None:
            try:
                reform_scenarios, reform_scenarios_errors = conv.uniform_sequence(
                    reform_tax_benefit_system.Scenario.
                    make_json_to_cached_or_new_instance(
                        ctx=ctx,
                        repair=data['validate'],
                        tax_benefit_system=reform_tax_benefit_system,
                    ))(data['scenarios'], state=ctx)
            except (ValueError, VariableNotFound) as exc:
                wsgihelpers.handle_error(exc, ctx, headers)
            errors = {
                'scenarios': reform_scenarios_errors
            } if reform_scenarios_errors is not None else None

    if errors is not None:
        return wsgihelpers.respond_json(
            ctx,
            collections.OrderedDict(
                sorted(
                    dict(
                        apiVersion=1,
                        context=inputs.get('context'),
                        error=collections.OrderedDict(
                            sorted(
                                dict(
                                    code=400,  # Bad Request
                                    errors=[conv.jsonify_value(errors)],
                                    message=ctx._(
                                        u'Bad parameters in request'),
                                ).iteritems())),
                        method=req.script_name,
                        params=inputs,
                        url=req.url.decode('utf-8'),
                    ).iteritems())),
            headers=headers,
        )

    scenarios = base_scenarios if data['reforms'] is None else reform_scenarios

    suggestions = {}
    for scenario_index, scenario in enumerate(scenarios):
        if data['validate']:
            original_test_case = scenario.test_case
            scenario.test_case = copy.deepcopy(original_test_case)
        suggestion = scenario.suggest()  # This modifies scenario.test_case!
        if data['validate']:
            scenario.test_case = original_test_case
        if suggestion is not None:
            suggestions.setdefault('scenarios',
                                   {})[scenario_index] = suggestion
    if not suggestions:
        suggestions = None

    if data['validate']:
        # Only a validation is requested. Don't launch simulation
        return wsgihelpers.respond_json(
            ctx,
            collections.OrderedDict(
                sorted(
                    dict(
                        apiVersion=1,
                        context=inputs.get('context'),
                        method=req.script_name,
                        params=inputs,
                        repaired_scenarios=[
                            scenario.to_json() for scenario in scenarios
                        ],
                        suggestions=suggestions,
                        url=req.url.decode('utf-8'),
                    ).iteritems())),
            headers=headers,
        )

    decomposition_json = model.get_cached_or_new_decomposition_json(
        base_tax_benefit_system)
    try:
        base_simulations = [
            scenario.new_simulation() for scenario in base_scenarios
        ]
    except ValueError as exc:
        wsgihelpers.handle_error(exc, ctx, headers)

    try:
        base_response_json = decompositions.calculate(base_simulations,
                                                      decomposition_json)
    except ParameterNotFound as exc:
        return wsgihelpers.respond_json(
            ctx,
            collections.OrderedDict(
                sorted(
                    dict(
                        apiVersion=1,
                        context=inputs.get('context'),
                        error=collections.OrderedDict(
                            sorted(
                                dict(
                                    code=500,
                                    errors=[{
                                        "scenarios": {
                                            exc.simulation_index:
                                            exc.to_json()
                                        }
                                    }],
                                    message=ctx._(u'Internal server error'),
                                ).iteritems())),
                        method=req.script_name,
                        params=inputs,
                        url=req.url.decode('utf-8'),
                    ).iteritems())),
            headers=headers,
        )
    except ValueError as exc:
        wsgihelpers.handle_error(exc, ctx, headers)

    if data['reforms'] is not None:
        reform_decomposition_json = model.get_cached_or_new_decomposition_json(
            reform_tax_benefit_system)
        try:
            reform_simulations = [
                scenario.new_simulation() for scenario in reform_scenarios
            ]
        except ValueError as exc:
            wsgihelpers.handle_error(exc, ctx, headers)

        try:
            reform_response_json = decompositions.calculate(
                reform_simulations, reform_decomposition_json)
        except ParameterNotFound as exc:
            return wsgihelpers.respond_json(
                ctx,
                collections.OrderedDict(
                    sorted(
                        dict(
                            apiVersion=1,
                            context=inputs.get('context'),
                            error=collections.OrderedDict(
                                sorted(
                                    dict(
                                        code=500,
                                        errors=[{
                                            "scenarios": {
                                                exc.simulation_index:
                                                exc.to_json()
                                            }
                                        }],
                                        message=ctx._(
                                            u'Internal server error'),
                                    ).iteritems())),
                            method=req.script_name,
                            params=inputs,
                            url=req.url.decode('utf-8'),
                        ).iteritems())),
                headers=headers,
            )
        except ValueError as exc:
            wsgihelpers.handle_error(exc, ctx, headers)

    simulations_variables_json = None
    tracebacks_json = None

    response_data = dict(
        apiVersion=1,
        context=data['context'],
        method=req.script_name,
        params=inputs,
        suggestions=suggestions,
        tracebacks=tracebacks_json,
        url=req.url.decode('utf-8'),
        value=reform_response_json
        if data['reforms'] is not None else base_response_json,
        variables=simulations_variables_json,
    )
    if data['reforms'] is not None:
        response_data['base_value'] = base_response_json
    return wsgihelpers.respond_json(
        ctx,
        collections.OrderedDict(sorted(response_data.iteritems())),
        headers=headers,
    )
Ejemplo n.º 12
0
 def init_from_decomposition_json(cls, simulation, decomposition_json):
     simulations = [simulation]
     root_node = decompositions.calculate(simulations, decomposition_json)
     self = cls()
     convert_to_out_node(self, root_node)
     return self
Ejemplo n.º 13
0
def api1_simulate(req):
    wsgihelpers.track(req.url.decode('utf-8'))
    ctx = contexts.Ctx(req)
    headers = wsgihelpers.handle_cross_origin_resource_sharing(ctx)

    assert req.method == 'POST', req.method

    if conf['load_alert']:
        try:
            load_average = os.getloadavg()
        except (AttributeError, OSError):
            # When load average is not available, always accept request.
            pass
        else:
            if load_average[0] / environment.cpu_count > 1:
                return wsgihelpers.respond_json(ctx,
                    collections.OrderedDict(sorted(dict(
                        apiVersion = 1,
                        error = collections.OrderedDict(sorted(dict(
                            code = 503,  # Service Unavailable
                            message = ctx._(u'Server is overloaded: {} {} {}').format(*load_average),
                            ).iteritems())),
                        method = req.script_name,
                        url = req.url.decode('utf-8'),
                        ).iteritems())),
                    headers = headers,
                    )

    content_type = req.content_type
    if content_type is not None:
        content_type = content_type.split(';', 1)[0].strip()
    if content_type != 'application/json':
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                error = collections.OrderedDict(sorted(dict(
                    code = 400,  # Bad Request
                    message = ctx._(u'Bad content-type: {}').format(content_type),
                    ).iteritems())),
                method = req.script_name,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )

    inputs, error = conv.pipe(
        conv.make_input_to_json(object_pairs_hook = collections.OrderedDict),
        conv.test_isinstance(dict),
        conv.not_none,
        )(req.body, state = ctx)
    if error is not None:
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                error = collections.OrderedDict(sorted(dict(
                    code = 400,  # Bad Request
                    errors = [conv.jsonify_value(error)],
                    message = ctx._(u'Invalid JSON in request POST body'),
                    ).iteritems())),
                method = req.script_name,
                params = req.body,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )

    str_list_to_reforms = conv.make_str_list_to_reforms()

    data, errors = conv.struct(
        dict(
            base_reforms = str_list_to_reforms,
            context = conv.test_isinstance(basestring),  # For asynchronous calls
            reforms = str_list_to_reforms,
            scenarios = conv.pipe(
                conv.test_isinstance(list),
                conv.uniform_sequence(
                    conv.not_none,  # Real conversion is done once tax-benefit system is known.
                    ),
                conv.test(lambda scenarios: len(scenarios) >= 1, error = N_(u'At least one scenario is required')),
                conv.test(lambda scenarios: len(scenarios) <= 100,
                    error = N_(u"There can't be more than 100 scenarios")),
                conv.not_none,
                ),
            trace = conv.pipe(
                conv.test_isinstance((bool, int)),
                conv.anything_to_bool,
                conv.default(False),
                ),
            validate = conv.pipe(
                conv.test_isinstance((bool, int)),
                conv.anything_to_bool,
                conv.default(False),
                ),
            ),
        )(inputs, state = ctx)

    if errors is None:
        country_tax_benefit_system = model.tax_benefit_system
        base_tax_benefit_system = model.get_cached_composed_reform(
            reform_keys = data['base_reforms'],
            tax_benefit_system = country_tax_benefit_system,
            ) if data['base_reforms'] is not None else country_tax_benefit_system
        if data['reforms'] is not None:
            reform_tax_benefit_system = model.get_cached_composed_reform(
                reform_keys = data['reforms'],
                tax_benefit_system = base_tax_benefit_system,
                )
        try:
            base_scenarios, base_scenarios_errors = conv.uniform_sequence(
                base_tax_benefit_system.Scenario.make_json_to_cached_or_new_instance(
                    ctx = ctx,
                    repair = data['validate'],
                    tax_benefit_system = base_tax_benefit_system,
                    )
                )(data['scenarios'], state = ctx)
        except (ValueError, VariableNotFound) as exc:
            wsgihelpers.handle_error(exc, ctx, headers)

        errors = {'scenarios': base_scenarios_errors} if base_scenarios_errors is not None else None

        if errors is None and data['reforms'] is not None:
            try:
                reform_scenarios, reform_scenarios_errors = conv.uniform_sequence(
                    reform_tax_benefit_system.Scenario.make_json_to_cached_or_new_instance(
                        ctx = ctx,
                        repair = data['validate'],
                        tax_benefit_system = reform_tax_benefit_system,
                        )
                    )(data['scenarios'], state = ctx)
            except (ValueError, VariableNotFound) as exc:
                wsgihelpers.handle_error(exc, ctx, headers)
            errors = {'scenarios': reform_scenarios_errors} if reform_scenarios_errors is not None else None

    if errors is not None:
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                context = inputs.get('context'),
                error = collections.OrderedDict(sorted(dict(
                    code = 400,  # Bad Request
                    errors = [conv.jsonify_value(errors)],
                    message = ctx._(u'Bad parameters in request'),
                    ).iteritems())),
                method = req.script_name,
                params = inputs,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )

    scenarios = base_scenarios if data['reforms'] is None else reform_scenarios

    suggestions = {}
    for scenario_index, scenario in enumerate(scenarios):
        if data['validate']:
            original_test_case = scenario.test_case
            scenario.test_case = copy.deepcopy(original_test_case)
        suggestion = scenario.suggest()  # This modifies scenario.test_case!
        if data['validate']:
            scenario.test_case = original_test_case
        if suggestion is not None:
            suggestions.setdefault('scenarios', {})[scenario_index] = suggestion
    if not suggestions:
        suggestions = None

    if data['validate']:
        # Only a validation is requested. Don't launch simulation
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                context = inputs.get('context'),
                method = req.script_name,
                params = inputs,
                repaired_scenarios = [
                    scenario.to_json()
                    for scenario in scenarios
                    ],
                suggestions = suggestions,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )

    decomposition_json = model.get_cached_or_new_decomposition_json(base_tax_benefit_system)
    try:
        base_simulations = [scenario.new_simulation() for scenario in base_scenarios]
    except ValueError as exc:
        wsgihelpers.handle_error(exc, ctx, headers)

    try:
        base_response_json = decompositions.calculate(base_simulations, decomposition_json)
    except ParameterNotFound as exc:
        return wsgihelpers.respond_json(ctx,
            collections.OrderedDict(sorted(dict(
                apiVersion = 1,
                context = inputs.get('context'),
                error = collections.OrderedDict(sorted(dict(
                    code = 500,
                    errors = [{"scenarios": {exc.simulation_index: exc.to_json()}}],
                    message = ctx._(u'Internal server error'),
                    ).iteritems())),
                method = req.script_name,
                params = inputs,
                url = req.url.decode('utf-8'),
                ).iteritems())),
            headers = headers,
            )
    except ValueError as exc:
        wsgihelpers.handle_error(exc, ctx, headers)

    if data['reforms'] is not None:
        reform_decomposition_json = model.get_cached_or_new_decomposition_json(reform_tax_benefit_system)
        try:
            reform_simulations = [scenario.new_simulation() for scenario in reform_scenarios]
        except ValueError as exc:
            wsgihelpers.handle_error(exc, ctx, headers)

        try:
            reform_response_json = decompositions.calculate(reform_simulations, reform_decomposition_json)
        except ParameterNotFound as exc:
            return wsgihelpers.respond_json(ctx,
                collections.OrderedDict(sorted(dict(
                    apiVersion = 1,
                    context = inputs.get('context'),
                    error = collections.OrderedDict(sorted(dict(
                        code = 500,
                        errors = [{"scenarios": {exc.simulation_index: exc.to_json()}}],
                        message = ctx._(u'Internal server error'),
                        ).iteritems())),
                    method = req.script_name,
                    params = inputs,
                    url = req.url.decode('utf-8'),
                    ).iteritems())),
                headers = headers,
                )
        except ValueError as exc:
            wsgihelpers.handle_error(exc, ctx, headers)

    simulations_variables_json = None
    tracebacks_json = None

    response_data = dict(
        apiVersion = 1,
        context = data['context'],
        method = req.script_name,
        params = inputs,
        suggestions = suggestions,
        tracebacks = tracebacks_json,
        url = req.url.decode('utf-8'),
        value = reform_response_json if data['reforms'] is not None else base_response_json,
        variables = simulations_variables_json,
        )
    if data['reforms'] is not None:
        response_data['base_value'] = base_response_json
    return wsgihelpers.respond_json(ctx,
        collections.OrderedDict(sorted(response_data.iteritems())),
        headers = headers,
        )