def validate_slice_json(slice, state = None):
    if slice is None:
        return None, None
    state = conv.add_ancestor_to_state(state, slice)
    validated_slice, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                base = validate_values_holder_json,
                comment = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                rate = conv.pipe(
                    validate_values_holder_json,
                    conv.not_none,
                    ),
                threshold = conv.pipe(
                    validate_values_holder_json,
                    conv.not_none,
                    ),
                ),
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(slice, state = state)
    conv.remove_ancestor_from_state(state, slice)
    return validated_slice, errors
def validate_dated_legislation_json(dated_legislation_json, state = None):
    if dated_legislation_json is None:
        return None, None
    if state is None:
        state = conv.default_state

    dated_legislation_json, error = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                datesim = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.iso8601_input_to_date,
                    conv.date_to_iso8601_str,
                    conv.not_none,
                    ),
                ),
            constructor = collections.OrderedDict,
            default = conv.noop,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(dated_legislation_json, state = state)
    if error is not None:
        return dated_legislation_json, error

    datesim = dated_legislation_json.pop('datesim')
    dated_legislation_json, error = validate_dated_node_json(dated_legislation_json, state = state)
    dated_legislation_json['datesim'] = datesim
    return dated_legislation_json, error
def validate_dated_legislation_json(dated_legislation_json, state=None):
    if dated_legislation_json is None:
        return None, None
    if state is None:
        state = conv.default_state

    dated_legislation_json, error = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(datesim=conv.pipe(
                conv.test_isinstance(basestring),
                conv.iso8601_input_to_date,
                conv.date_to_iso8601_str,
                conv.not_none,
            ), ),
            constructor=collections.OrderedDict,
            default=conv.noop,
            drop_none_values='missing',
            keep_value_order=True,
        ),
    )(dated_legislation_json, state=state)
    if error is not None:
        return dated_legislation_json, error

    datesim = dated_legislation_json.pop('datesim')
    dated_legislation_json, error = validate_dated_node_json(
        dated_legislation_json, state=state)
    dated_legislation_json['datesim'] = datesim
    return dated_legislation_json, error
def input_to_json_data(value, state=None):
    return conv.pipe(
        conv.make_input_to_json(),
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                resultat_officiel=conv.pipe(
                    conv.test_isinstance(dict),
                    conv.uniform_mapping(
                        conv.pipe(
                            conv.test_isinstance(basestring),
                            conv.not_none,
                        ),
                        conv.pipe(
                            conv.test_isinstance(dict),
                            conv.struct(
                                dict(
                                    code=conv.pipe(
                                        conv.test_isinstance(basestring),
                                        conv.not_none,
                                    ),
                                    name=conv.pipe(
                                        conv.test_isinstance(basestring),
                                        conv.not_none,
                                    ),
                                    value=conv.pipe(
                                        conv.test_isinstance(float),
                                        conv.not_none,
                                    ),
                                ), ),
                            conv.not_none,
                        ),
                    ),
                    conv.not_none,
                ),
                scenario=conv.pipe(
                    conv.test_isinstance(dict),
                    conv.struct(
                        dict(
                            test_case=conv.pipe(
                                conv.test_isinstance(dict),
                                # For each entity convert its members from a dict to a list.
                                conv.uniform_mapping(
                                    conv.noop,
                                    conv.pipe(
                                        conv.test_isinstance(dict),
                                        conv.function(
                                            transform_entity_member_by_id_to_members
                                        ),
                                    ),
                                ),
                            ), ),
                        default=conv.noop,
                    ),
                    tax_benefit_system.Scenario.make_json_to_instance(
                        tax_benefit_system=tax_benefit_system),
                    conv.not_none,
                ),
            ), ),
    )(value, state=state or conv.default_state)
Esempio n. 5
0
 def make_json_to_array_by_period(self, period):
     return conv.condition(
         conv.test_isinstance(dict),
         conv.pipe(
             # Value is a dict of (period, value) couples.
             conv.uniform_mapping(
                 conv.pipe(
                     conv.function(periods.period),
                     conv.not_none,
                 ),
                 conv.pipe(
                     conv.make_item_to_singleton(),
                     conv.uniform_sequence(self.json_to_dated_python, ),
                     conv.empty_to_none,
                     conv.function(lambda cells_list: np.array(
                         cells_list, dtype=self.dtype)),
                 ),
                 drop_none_values=True,
             ),
             conv.empty_to_none,
         ),
         conv.pipe(
             conv.make_item_to_singleton(),
             conv.uniform_sequence(self.json_to_dated_python, ),
             conv.empty_to_none,
             conv.function(
                 lambda cells_list: np.array(cells_list, dtype=self.dtype)),
             conv.function(lambda array: {period: array}),
         ),
     )
def validate_slice_json(slice, state=None):
    if slice is None:
        return None, None
    state = conv.add_ancestor_to_state(state, slice)
    validated_slice, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                base=validate_values_holder_json,
                comment=conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                ),
                rate=conv.pipe(
                    validate_values_holder_json,
                    conv.not_none,
                ),
                threshold=conv.pipe(
                    validate_values_holder_json,
                    conv.not_none,
                ),
            ),
            constructor=collections.OrderedDict,
            drop_none_values='missing',
            keep_value_order=True,
        ),
    )(slice, state=state)
    conv.remove_ancestor_from_state(state, slice)
    return validated_slice, errors
    def validate_node_json(node, state=None):
        if node is None:
            return None, None
        if state is None:
            state = conv.default_state
        validated_node, errors = conv.pipe(
            conv.condition(
                conv.test_isinstance(list),
                conv.uniform_sequence(
                    validate_node_json,
                    drop_none_items=True,
                ),
                conv.pipe(
                    conv.condition(
                        conv.test_isinstance(basestring_type),
                        conv.function(lambda code: dict(code=code)),
                        conv.test_isinstance(dict),
                    ),
                    conv.struct(
                        dict(
                            children=conv.pipe(
                                conv.test_isinstance(list),
                                conv.uniform_sequence(
                                    validate_node_json,
                                    drop_none_items=True,
                                ),
                                conv.empty_to_none,
                            ),
                            code=conv.pipe(
                                conv.test_isinstance(basestring_type),
                                conv.cleanup_line,
                            ),
                        ),
                        constructor=collections.OrderedDict,
                        default=conv.noop,
                        drop_none_values='missing',
                        keep_value_order=True,
                    ),
                ),
            ),
            conv.empty_to_none,
        )(node, state=state)
        if validated_node is None or errors is not None:
            return validated_node, errors

        if isinstance(validated_node,
                      dict) and not validated_node.get('children'):
            validated_node, errors = conv.struct(
                dict(code=conv.pipe(
                    conv.test_in(tax_benefit_system.variables),
                    conv.not_none,
                ), ),
                default=conv.noop,
            )(validated_node, state=state)
        return validated_node, errors
def validate_parameter_xml_json(parameter, state = None):
    if parameter is None:
        return None, None
    state = conv.add_ancestor_to_state(state, parameter)
    validated_parameter, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                code = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_line,
                    conv.not_none,
                    ),
                description = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_line,
                    ),
                format = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.input_to_slug,
                    conv.test_in(xml_json_formats),
                    ),
                tail = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                taille = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.test_in([
                        'moinsde20',
                        'plusde20',
                        ]),
                    ),
                text = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                type = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.input_to_slug,
                    conv.test_in(json_unit_by_xml_json_type),
                    ),
                VALUE = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_value_xml_json,
                        drop_none_items = True,
                        ),
                    make_validate_values_xml_json_dates(require_consecutive_dates = True),
                    conv.empty_to_none,
                    conv.not_none,
                    ),
                ),
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(parameter, state = state)
    conv.remove_ancestor_from_state(state, parameter)
    return validated_parameter, errors
Esempio n. 9
0
    def json_or_python_to_input_variables(value, state=None):
        if value is None:
            return value, None
        if state is None:
            state = conv.default_state

        input_variables, errors = conv.pipe(
            conv.test_isinstance(dict),
            conv.uniform_mapping(
                conv.pipe(
                    conv.test_isinstance(basestring_type),
                    conv.not_none,
                ),
                conv.noop,
            ),
        )(value, state=state)

        if errors is not None:
            return input_variables, errors

        for input_variable_name in input_variables.keys():
            if input_variable_name not in variables_name:
                # We only import VariableNotFound here to avoid a circular dependency in imports
                from .taxbenefitsystems import VariableNotFound
                raise VariableNotFound(input_variable_name, tax_benefit_system)

        input_variables, errors = conv.pipe(
            make_json_or_python_to_array_by_period_by_variable_name(
                tax_benefit_system, period),
            conv.empty_to_none,
        )(value, state=state)
        if errors is not None:
            return input_variables, errors
        if input_variables is None:
            return input_variables, None

        count_by_entity_key = {}
        errors = {}
        for variable_name, array_by_period in input_variables.items():
            column = tax_benefit_system.get_variable(variable_name)
            entity_key = column.entity.key
            entity_count = count_by_entity_key.get(entity_key, 0)
            for variable_period, variable_array in array_by_period.items():
                if entity_count == 0:
                    count_by_entity_key[entity_key] = entity_count = len(
                        variable_array)
                elif len(variable_array) != entity_count:
                    errors[column.name] = state._(
                        "Array has not the same length as other variables of entity {}: {} instead of {}"
                    ).format(column.name, len(variable_array), entity_count)

        return input_variables, errors or None
Esempio n. 10
0
    def json_to_dated_python(self):
        enum = self.variable.possible_values
        possible_names = [item.name for item in list(enum)]

        if enum is None:
            return conv.pipe(conv.test_isinstance(basestring_type))
        return conv.pipe(
            conv.test_isinstance(basestring_type),
            conv.pipe(
                # Verify that item belongs to enumeration.
                conv.test_in(possible_names),
                # Transform that item into enum object.
                conv.function(lambda enum_name: enum[enum_name])))
Esempio n. 11
0
 def json_to_python(self):
     return conv.condition(
         conv.test_isinstance(dict),
         conv.pipe(
             # Value is a dict of (period, value) couples.
             conv.uniform_mapping(
                 conv.pipe(
                     conv.function(periods.period),
                     conv.not_none,
                 ),
                 self.json_to_dated_python,
             ), ),
         self.json_to_dated_python,
     )
def validate_scale_xml_json(scale, state = None):
    if scale is None:
        return None, None
    state = conv.add_ancestor_to_state(state, scale)
    validated_scale, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                code = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_line,
                    conv.not_none,
                    ),
                description = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_line,
                    ),
                option = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.input_to_slug,
                    conv.test_in((
                        'contrib',
                        'main-d-oeuvre',
                        'noncontrib',
                        )),
                    ),
                TRANCHE = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_slice_xml_json,
                        drop_none_items = True,
                        ),
                    validate_slices_xml_json_dates,
                    conv.empty_to_none,
                    conv.not_none,
                    ),
                tail = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                text = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                type = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.input_to_slug,
                    conv.test_in((
                        'monetary',
                        )),
                    ),
                ),
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(scale, state = state)
    conv.remove_ancestor_from_state(state, scale)
    return validated_scale, errors
def input_to_json_data(value, state=None):
    return conv.pipe(
        conv.make_input_to_json(),
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                resultat_officiel=conv.pipe(
                    conv.test_isinstance(dict),
                    conv.uniform_mapping(
                        conv.pipe(conv.test_isinstance(basestring), conv.not_none),
                        conv.pipe(
                            conv.test_isinstance(dict),
                            conv.struct(
                                dict(
                                    code=conv.pipe(conv.test_isinstance(basestring), conv.not_none),
                                    name=conv.pipe(conv.test_isinstance(basestring), conv.not_none),
                                    value=conv.pipe(conv.test_isinstance(float), conv.not_none),
                                )
                            ),
                            conv.not_none,
                        ),
                    ),
                    conv.not_none,
                ),
                scenario=conv.pipe(
                    conv.test_isinstance(dict),
                    conv.struct(
                        dict(
                            test_case=conv.pipe(
                                conv.test_isinstance(dict),
                                # For each entity convert its members from a dict to a list.
                                conv.uniform_mapping(
                                    conv.noop,
                                    conv.pipe(
                                        conv.test_isinstance(dict),
                                        conv.function(transform_entity_member_by_id_to_members),
                                    ),
                                ),
                            )
                        ),
                        default=conv.noop,
                    ),
                    tax_benefit_system.Scenario.make_json_to_instance(tax_benefit_system=tax_benefit_system),
                    conv.not_none,
                ),
            )
        ),
    )(value, state=state or conv.default_state)
def validate_dated_value_json(value, state = None):
    if value is None:
        return None, None
    container = state.ancestors[-1]
    value_converter = dict(
        boolean = conv.condition(
            conv.test_isinstance(int),
            conv.test_in((0, 1)),
            conv.test_isinstance(bool),
            ),
        float = conv.condition(
            conv.test_isinstance(int),
            conv.anything_to_float,
            conv.test_isinstance(float),
            ),
        integer = conv.condition(
            conv.test_isinstance(float),
            conv.pipe(
                conv.test(lambda number: round(number) == number),
                conv.function(int),
                ),
            conv.test_isinstance(int),
            ),
        rate = conv.condition(
            conv.test_isinstance(int),
            conv.anything_to_float,
            conv.test_isinstance(float),
            ),
        )[container.get('format') or 'float']  # Only parameters have a "format".
    return value_converter(value, state = state or conv.default_state)
Esempio n. 15
0
 def input_to_dated_python(self):
     enum = self.variable.possible_values
     if enum is None:
         return conv.test_isinstance(basestring_type)
     return conv.pipe(
         # Verify that item index belongs to enumeration.
         conv.test_in([item.name for item in list(enum)]))
def test():
    dir_path = os.path.join(os.path.dirname(__file__), 'formulas')
    for filename in sorted(os.listdir(dir_path)):
        if not filename.endswith('.yaml'):
            continue
        filename_core = os.path.splitext(filename)[0]
        with open(os.path.join(dir_path, filename)) as yaml_file:
            tests = yaml.load(yaml_file)
            tests, error = conv.pipe(
                conv.make_item_to_singleton(),
                conv.uniform_sequence(
                    conv.noop,
                    drop_none_items = True,
                    ),
                )(tests)
            if error is not None:
                embedding_error = conv.embed_error(tests, u'errors', error)
                assert embedding_error is None, embedding_error
                conv.check((tests, error))  # Generate an error.

            for test in tests:
                test, error = scenarios.make_json_or_python_to_test(tax_benefit_system)(test)
                if error is not None:
                    embedding_error = conv.embed_error(test, u'errors', error)
                    assert embedding_error is None, embedding_error
                    conv.check((test, error))  # Generate an error.

                if test.get(u'ignore', False):
                    continue
                yield check, test.get('name') or filename_core, unicode(test['scenario'].period), test
def validate_dated_value_json(value, state=None):
    if value is None:
        return None, None
    container = state.ancestors[-1]
    value_converter = dict(
        boolean=conv.condition(
            conv.test_isinstance(int),
            conv.test_in((0, 1)),
            conv.test_isinstance(bool),
        ),
        float=conv.condition(
            conv.test_isinstance(int),
            conv.anything_to_float,
            conv.test_isinstance(float),
        ),
        integer=conv.condition(
            conv.test_isinstance(float),
            conv.pipe(
                conv.test(lambda number: round(number) == number),
                conv.function(int),
            ),
            conv.test_isinstance(int),
        ),
        rate=conv.condition(
            conv.test_isinstance(int),
            conv.anything_to_float,
            conv.test_isinstance(float),
        ),
    )[container.get('format') or 'float']  # Only parameters have a "format".
    return value_converter(value, state=state or conv.default_state)
Esempio n. 18
0
def _parse_yaml_file(tax_benefit_system, yaml_path):
    filename = os.path.splitext(os.path.basename(yaml_path))[0]
    with open(yaml_path) as yaml_file:
        tests = yaml.load(yaml_file)

    tests, error = conv.pipe(
        conv.make_item_to_singleton(),
        conv.uniform_sequence(
            conv.noop,
            drop_none_items = True,
            ),
        )(tests)

    if error is not None:
        embedding_error = conv.embed_error(tests, u'errors', error)
        assert embedding_error is None, embedding_error
        raise ValueError("Error in test {}:\n{}".format(yaml_path, yaml.dump(tests, allow_unicode = True,
            default_flow_style = False, indent = 2, width = 120)))

    for test in tests:
        test, error = scenarios.make_json_or_python_to_test(
            tax_benefit_system = tax_benefit_system
            )(test)

        if error is not None:
            embedding_error = conv.embed_error(test, u'errors', error)
            assert embedding_error is None, embedding_error
            raise ValueError("Error in test {}:\n{}\nYaml test content: \n{}\n".format(
                yaml_path, error, yaml.dump(test, allow_unicode = True,
                default_flow_style = False, indent = 2, width = 120)))

        yield yaml_path, test.get('name') or filename, unicode(test['scenario'].period), test
Esempio n. 19
0
 def input_to_dated_python(self):
     return conv.pipe(
         conv.test(year_or_month_or_day_re.match, error=N_('Invalid date')),
         conv.function(lambda birth: '-'.join(
             (birth.split('-') + ['01', '01'])[:3])),
         conv.iso8601_input_to_date,
     )
Esempio n. 20
0
    def json_or_python_to_test(value, state=None):
        if value is None:
            return value, None
        if state is None:
            state = conv.default_state
        value, error = conv.pipe(
            conv.test_isinstance(dict),
            validate,
        )(value, state=state)
        if error is not None:
            return value, error

        value, error = conv.struct(
            dict(output_variables=make_json_or_python_to_input_variables(
                tax_benefit_system, value['period']), ),
            default=conv.noop,
        )(value, state=state)
        if error is not None:
            return value, error

        test_case = value.copy()
        absolute_error_margin = test_case.pop('absolute_error_margin')
        axes = test_case.pop('axes')
        description = test_case.pop('description')
        input_variables = test_case.pop('input_variables')
        keywords = test_case.pop('keywords')
        name = test_case.pop('name')
        output_variables = test_case.pop('output_variables')
        period = test_case.pop('period')
        relative_error_margin = test_case.pop('relative_error_margin')

        if test_case is not None and all(
                entity_members is None
                for entity_members in test_case.values()):
            test_case = None

        scenario, error = tax_benefit_system.Scenario.make_json_to_instance(
            repair=True, tax_benefit_system=tax_benefit_system)(dict(
                axes=axes,
                input_variables=input_variables,
                period=period,
                test_case=test_case,
            ),
                                                                state=state)
        if error is not None:
            return scenario, error

        return {
            key: value
            for key, value in dict(
                absolute_error_margin=absolute_error_margin,
                description=description,
                keywords=keywords,
                name=name,
                output_variables=output_variables,
                relative_error_margin=relative_error_margin,
                scenario=scenario,
            ).items() if value is not None
        }, None
 def json_to_instance(cls, value, state=None):
     attributes, error = conv.pipe(
         cls.json_to_attributes,
         conv.default({}),
     )(value, state=state or conv.default_state)
     if error is not None:
         return attributes, error
     return cls(**attributes), None
Esempio n. 22
0
 def json_to_dated_python(self):
     return conv.pipe(
         super(AgeCol, self).json_to_dated_python,
         conv.first_match(
             conv.test_greater_or_equal(0),
             conv.test_equals(-9999),
         ),
     )
Esempio n. 23
0
 def json_to_dated_python(self):
     return conv.pipe(
         conv.condition(
             conv.test_isinstance((float, int)),
             # YAML stores strings containing only digits as numbers.
             conv.function(str),
         ),
         conv.test_isinstance(basestring_type),
     )
Esempio n. 24
0
 def json_to_dated_python(self):
     return conv.pipe(
         conv.condition(
             conv.test_isinstance((float, int)),
             # YAML stores strings containing only digits as numbers.
             conv.function(str),
         ),
         conv.test_isinstance(basestring_type),
         conv.test(lambda value: len(value) <= self.variable.max_length),
     )
Esempio n. 25
0
def make_json_or_python_to_axes(tax_benefit_system):
    column_by_name = tax_benefit_system.variables
    return conv.pipe(
        conv.test_isinstance(list),
        conv.uniform_sequence(
            conv.pipe(
                conv.item_or_sequence(
                    conv.pipe(
                        conv.test_isinstance(dict),
                        conv.struct(
                            dict(
                                count=conv.pipe(
                                    conv.test_isinstance(int),
                                    conv.test_greater_or_equal(1),
                                    conv.not_none,
                                ),
                                index=conv.pipe(
                                    conv.test_isinstance(int),
                                    conv.test_greater_or_equal(0),
                                    conv.default(0),
                                ),
                                max=conv.pipe(
                                    conv.test_isinstance((float, int)),
                                    conv.not_none,
                                ),
                                min=conv.pipe(
                                    conv.test_isinstance((float, int)),
                                    conv.not_none,
                                ),
                                name=conv.pipe(
                                    conv.test_isinstance(basestring_type),
                                    conv.test_in(column_by_name),
                                    conv.test(
                                        lambda column_name: tax_benefit_system.
                                        get_variable(column_name).dtype in
                                        (np.float32, np.int16, np.int32),
                                        error=N_(
                                            'Invalid type for axe: integer or float expected'
                                        )),
                                    conv.not_none,
                                ),
                                period=conv.function(periods.period),
                            ), ),
                    ),
                    drop_none_items=True,
                ),
                conv.make_item_to_singleton(),
            ),
            drop_none_items=True,
        ),
        conv.empty_to_none,
    )
def test(force = False, name_filter = None, options_by_path = None):
    if isinstance(name_filter, str):
        name_filter = name_filter.decode('utf-8')
    if options_by_path is None:
        options_by_path = options_by_dir
    for path, options in options_by_path.iteritems():
        if not force and options.get('ignore', False):
            log.info(u'Ignoring {}'.format(path))
            continue
        if not os.path.exists(path):
            log.warning(u'Skipping missing {}'.format(path))
            continue
        if os.path.isdir(path):
            yaml_paths = [
                os.path.join(path, filename)
                for filename in sorted(os.listdir(path))
                if filename.endswith('.yaml')
                ]
        else:
            yaml_paths = [path]
        for yaml_path in yaml_paths:
            filename_core = os.path.splitext(os.path.basename(yaml_path))[0]
            with open(yaml_path) as yaml_file:
                tests = yaml.load(yaml_file)
            tests, error = conv.pipe(
                conv.make_item_to_singleton(),
                conv.uniform_sequence(
                    conv.noop,
                    drop_none_items = True,
                    ),
                )(tests)
            if error is not None:
                embedding_error = conv.embed_error(tests, u'errors', error)
                assert embedding_error is None, embedding_error
                raise ValueError("Error in test {}:\n{}".format(yaml_path, yaml.dump(tests, allow_unicode = True,
                    default_flow_style = False, indent = 2, width = 120)))

            for test in tests:
                test, error = scenarios.make_json_or_python_to_test(get_tax_benefit_system(options.get('reform')),
                    default_absolute_error_margin = options['default_absolute_error_margin'])(test)
                if error is not None:
                    embedding_error = conv.embed_error(test, u'errors', error)
                    assert embedding_error is None, embedding_error
                    raise ValueError("Error in test {}:\n{}".format(yaml_path, yaml.dump(test, allow_unicode = True,
                        default_flow_style = False, indent = 2, width = 120)))

                if not force and test.get(u'ignore', False):
                    continue
                if name_filter is not None and name_filter not in filename_core \
                        and name_filter not in (test.get('name', u'')) \
                        and name_filter not in (test.get('keywords', [])):
                    continue
                checker = check_any_period if options['accept_other_period'] else check
                yield checker, test.get('name') or filename_core, unicode(test['scenario'].period), test, force
Esempio n. 27
0
 def json_to_dated_python(self):
     return conv.pipe(
         conv.condition(
             conv.test_isinstance(datetime.date),
             conv.noop,
             conv.condition(
                 conv.test_isinstance(int),
                 conv.pipe(
                     conv.test_between(1870, 2099),
                     conv.function(lambda year: datetime.date(year, 1, 1)),
                     ),
                 conv.pipe(
                     conv.test_isinstance(basestring_type),
                     conv.test(year_or_month_or_day_re.match, error = N_('Invalid date')),
                     conv.function(lambda birth: '-'.join((birth.split('-') + ['01', '01'])[:3])),
                     conv.iso8601_input_to_date,
                     ),
                 ),
             ),
         conv.test_between(datetime.date(1870, 1, 1), datetime.date(2099, 12, 31)),
         )
Esempio n. 28
0
def check_entities_and_role(test_case, tax_benefit_system, state):
    """
        Check that the test_case describes entities consistent with the tax and benefit system.

        Will raise an error if :
            - An entity is not recognized
            - An entity role is not recognized
            - A variable is declared for an entity it is not defined for (e.g. salary for a family)
    """

    test_case = deepcopy(
        test_case)  # Avoid side-effects on other references to test_case
    entity_classes = {
        entity_class.plural: entity_class
        for entity_class in tax_benefit_system.entities
    }
    for entity_type_name, entities in test_case.items():
        if entity_classes.get(entity_type_name) is None:
            raise ValueError("Invalid entity name: {}".format(
                entity_type_name).encode('utf-8'))
        entities, error = conv.pipe(
            conv.make_item_to_singleton(),
            conv.test_isinstance(list),
            conv.uniform_sequence(
                conv.test_isinstance(dict),
                drop_none_items=True,
            ),
            conv.function(set_entities_json_id),
        )(entities)
        if error is not None:
            raise ValueError("Invalid list of {}: {}. Error: {}".format(
                entity_type_name, entities, error).encode('utf-8'))
        if entities is None:
            entities = test_case[entity_type_name] = [
            ]  # YAML test runner may set these values to None
        entity_class = entity_classes[entity_type_name]
        valid_roles = dict(
            (role.key, role) if (role.max == 1) else (role.plural, role) for
            role in entity_class.roles) if not entity_class.is_person else {}

        for entity_json in entities:
            check_entity_fields(entity_json, entity_class, valid_roles,
                                tax_benefit_system)

    for entity_class in entity_classes.values():
        if test_case.get(entity_class.plural) is None:
            test_case[entity_class.plural] = [
            ]  # by convention, all entities must be declared in the test_case

    return test_case
Esempio n. 29
0
def _parse_test_file(tax_benefit_system, yaml_path):
    filename = os.path.splitext(os.path.basename(yaml_path))[0]
    with open(yaml_path) as yaml_file:
        try:
            tests = yaml.load(yaml_file, Loader=Loader)
        except yaml.scanner.ScannerError:
            log.error("{} is not a valid YAML file".format(yaml_path).encode('utf-8'))
            raise

    tests, error = conv.pipe(
        conv.make_item_to_singleton(),
        conv.uniform_sequence(
            conv.noop,
            drop_none_items = True,
            ),
        )(tests)

    if error is not None:
        embedding_error = conv.embed_error(tests, 'errors', error)
        assert embedding_error is None, embedding_error
        raise ValueError("Error in test {}:\n{}".format(yaml_path, yaml.dump(tests, Dumper=Dumper, allow_unicode = True,
            default_flow_style = False, indent = 2, width = 120)))

    for test in tests:
        current_tax_benefit_system = tax_benefit_system
        if test.get('reforms'):
            reforms = test.pop('reforms')
            if not isinstance(reforms, list):
                reforms = [reforms]
            for reform_path in reforms:
                current_tax_benefit_system = current_tax_benefit_system.apply_reform(reform_path)

        try:
            test, error = scenarios.make_json_or_python_to_test(
                tax_benefit_system = current_tax_benefit_system
                )(test)
        except Exception:
            log.error("{} is not a valid OpenFisca test file".format(yaml_path).encode('utf-8'))
            raise

        if error is not None:
            embedding_error = conv.embed_error(test, 'errors', error)
            assert embedding_error is None, embedding_error
            raise ValueError("Error in test {}:\n{}\nYaml test content: \n{}\n".format(
                yaml_path, error, yaml.dump(test, Dumper=Dumper, allow_unicode = True,
                default_flow_style = False, indent = 2, width = 120)))

        yield yaml_path, test.get('name') or filename, to_unicode(test['scenario'].period), test
Esempio n. 30
0
    def check_role(value, key):
        role = valid_roles.get(key)
        if role.max == 1:
            value, error = conv.test_isinstance((basestring_type, int))(value)
        else:
            value, error = conv.pipe(
                conv.make_item_to_singleton(), conv.test_isinstance(list),
                conv.uniform_sequence(
                    conv.test_isinstance((basestring_type, int)),
                    drop_none_items=True,
                ))(value)

        if error is not None:
            raise ValueError("Invalid description of {}: {}. Error: {}".format(
                entity_class.key, entity_json, error).encode('utf-8'))
        entity_json[key] = value
Esempio n. 31
0
def test(current_options_by_dir = None, force = False, name_filter = None):
    if current_options_by_dir is None:
        current_options_by_dir = options_by_dir
    for dir, options in sorted(current_options_by_dir.iteritems()):
        if not force and options.get('ignore', False):
            log.info(u'Ignoring directory: {}'.format(dir))
            continue
        if not os.path.isdir(dir):
            log.warning(u'Skipping missing directory: {}'.format(dir))
            continue
        if isinstance(name_filter, str):
            name_filter = name_filter.decode('utf-8')
        for filename in sorted(os.listdir(dir)):
            if not filename.endswith('.yaml'):
                continue
            filename_core = os.path.splitext(filename)[0]
            with open(os.path.join(dir, filename)) as yaml_file:
                tests = yaml.load(yaml_file)
            tests, error = conv.pipe(
                conv.make_item_to_singleton(),
                conv.uniform_sequence(
                    conv.noop,
                    drop_none_items = True,
                    ),
                )(tests)
            if error is not None:
                embedding_error = conv.embed_error(tests, u'errors', error)
                assert embedding_error is None, embedding_error
                conv.check((tests, error))  # Generate an error.

            for test in tests:
                test, error = scenarios.make_json_or_python_to_test(get_tax_benefit_system(options.get('reform')),
                    default_absolute_error_margin = options['default_absolute_error_margin'])(test)
                if error is not None:
                    embedding_error = conv.embed_error(test, u'errors', error)
                    assert embedding_error is None, embedding_error
                    conv.check((test, error))  # Generate an error.

                if not force and test.get(u'ignore', False):
                    continue
                if name_filter is not None and name_filter not in filename_core \
                        and name_filter not in (test.get('name', u'')) \
                        and name_filter not in (test.get('keywords', [])):
                    continue
                checker = check_any_period if options['accept_other_period'] else check
                yield checker, test.get('name') or filename_core, unicode(test['scenario'].period), test, force
Esempio n. 32
0
def test(force = False, name_filter = None):
    if isinstance(name_filter, str):
        name_filter = name_filter.decode('utf-8')
    yaml_paths = [
        os.path.join(source_file_dir_name, filename)
        for filename in sorted(os.listdir(source_file_dir_name))
        if filename.endswith('.yaml')
        ]
    for yaml_path in yaml_paths:
        filename_core = os.path.splitext(os.path.basename(yaml_path))[0]
        with open(yaml_path) as yaml_file:
            tests = yaml.load(yaml_file)
        tests, error = conv.pipe(
            conv.make_item_to_singleton(),
            conv.uniform_sequence(
                conv.noop,
                drop_none_items = True,
                ),
            )(tests)
        if error is not None:
            embedding_error = conv.embed_error(tests, u'errors', error)
            assert embedding_error is None, embedding_error
            raise ValueError("Error in test {}:\n{}".format(yaml_path, yaml.dump(tests, allow_unicode = True,
                default_flow_style = False, indent = 2, width = 120)))

        for test in tests:
            test, error = scenarios.make_json_or_python_to_test(
                tax_benefit_system = tax_benefit_system,
                default_absolute_error_margin = default_absolute_error_margin,
                )(test)
            if error is not None:
                embedding_error = conv.embed_error(test, u'errors', error)
                assert embedding_error is None, embedding_error
                raise ValueError("Error in test {}:\n{}\nYaml test content: \n{}\n".format(
                    yaml_path, error, yaml.dump(test, allow_unicode = True,
                    default_flow_style = False, indent = 2, width = 120)))

            if not force and test.get(u'ignore', False):
                continue
            if name_filter is not None and name_filter not in filename_core \
                    and name_filter not in (test.get('name', u'')) \
                    and name_filter not in (test.get('keywords', [])):
                continue
            yield check, yaml_path, test.get('name') or filename_core, unicode(test['scenario'].period), test, force
def validate_dated_slice_json(slice, state=None):
    if slice is None:
        return None, None
    state = conv.add_ancestor_to_state(state, slice)
    validated_slice, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                base=conv.pipe(
                    validate_dated_value_json,
                    conv.test_greater_or_equal(0),
                ),
                constant_amount=validate_dated_value_json,
                comment=conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                ),
                rate=conv.pipe(
                    validate_dated_value_json,
                    conv.test_between(0, 1),
                ),
                threshold=conv.pipe(
                    validate_dated_value_json,
                    conv.test_greater_or_equal(0),
                ),
                date=conv.pipe(
                    validate_dated_value_json,
                    conv.test_between(0, 1),  #TODO: changer
                ),
            ),
            constructor=collections.OrderedDict,
            drop_none_values='missing',
            keep_value_order=True,
        ),
    )(slice, state=state)
    conv.remove_ancestor_from_state(state, slice)
    return validated_slice, errors
def validate_dated_slice_json(slice, state = None):
    if slice is None:
        return None, None
    state = conv.add_ancestor_to_state(state, slice)
    validated_slice, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                base = conv.pipe(
                    validate_dated_value_json,
                    conv.test_greater_or_equal(0),
                    ),
                constant_amount = validate_dated_value_json,
                comment = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                rate = conv.pipe(
                    validate_dated_value_json,
                    conv.test_between(0, 1),
                    ),
                threshold = conv.pipe(
                    validate_dated_value_json,
                    conv.test_greater_or_equal(0),
                    ),
                 date = conv.pipe(
                    validate_dated_value_json,
                    conv.test_between(0, 1), #TODO: changer
                    ),
                ),
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(slice, state = state)
    conv.remove_ancestor_from_state(state, slice)
    return validated_slice, errors
def validate_value_json(value, state = None):
    if value is None:
        return None, None
    container = state.ancestors[-1]
    value_converter = dict(
        boolean = conv.condition(
            conv.test_isinstance(int),
            conv.test_in((0, 1)),
            conv.test_isinstance(bool),
            ),
        float = conv.condition(
            conv.test_isinstance(int),
            conv.anything_to_float,
            conv.test_isinstance(float),
            ),
        integer = conv.condition(
            conv.test_isinstance(float),
            conv.pipe(
                conv.test(lambda number: round(number) == number),
                conv.function(int),
                ),
            conv.test_isinstance(int),
            ),
        rate = conv.condition(
            conv.test_isinstance(int),
            conv.anything_to_float,
            conv.test_isinstance(float),
            ),
        )[container.get('format') or 'float']  # Only parameters have a "format".
    state = conv.add_ancestor_to_state(state, value)
    validated_value, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            {
                u'comment': conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                u'from': conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.iso8601_input_to_date,
                    conv.date_to_iso8601_str,
                    conv.not_none,
                    ),
                u'to': conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.iso8601_input_to_date,
                    conv.date_to_iso8601_str,
                    conv.not_none,
                    ),
                u'value': conv.pipe(
                    value_converter,
                    conv.not_none,
                    ),
                },
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(value, state = state)
    conv.remove_ancestor_from_state(state, value)
    return validated_value, errors
Esempio n. 36
0
def make_json_or_python_to_test(tax_benefit_system):
    validate = conv.struct(
        dict(
            itertools.chain(
                dict(
                    absolute_error_margin=conv.pipe(
                        conv.test_isinstance((float, int)),
                        conv.test_greater_or_equal(0),
                    ),
                    axes=make_json_or_python_to_axes(tax_benefit_system),
                    description=conv.pipe(
                        conv.test_isinstance(basestring_type),
                        conv.cleanup_line,
                    ),
                    input_variables=conv.pipe(
                        conv.test_isinstance(dict),
                        conv.uniform_mapping(
                            conv.pipe(
                                conv.test_isinstance(basestring_type),
                                conv.not_none,
                            ),
                            conv.noop,
                        ),
                        conv.empty_to_none,
                    ),
                    keywords=conv.pipe(
                        conv.make_item_to_singleton(),
                        conv.test_isinstance(list),
                        conv.uniform_sequence(
                            conv.pipe(
                                conv.test_isinstance(basestring_type),
                                conv.cleanup_line,
                            ),
                            drop_none_items=True,
                        ),
                        conv.empty_to_none,
                    ),
                    name=conv.pipe(
                        conv.test_isinstance(basestring_type),
                        conv.cleanup_line,
                    ),
                    output_variables=conv.test_isinstance(dict),
                    period=conv.pipe(
                        conv.function(periods.period),
                        conv.not_none,
                    ),
                    relative_error_margin=conv.pipe(
                        conv.test_isinstance((float, int)),
                        conv.test_greater_or_equal(0),
                    ),
                ).items(),
                ((entity.plural,
                  conv.pipe(
                      conv.make_item_to_singleton(),
                      conv.test_isinstance(list),
                  )) for entity in tax_benefit_system.entities),
            )), )

    def json_or_python_to_test(value, state=None):
        if value is None:
            return value, None
        if state is None:
            state = conv.default_state
        value, error = conv.pipe(
            conv.test_isinstance(dict),
            validate,
        )(value, state=state)
        if error is not None:
            return value, error

        value, error = conv.struct(
            dict(output_variables=make_json_or_python_to_input_variables(
                tax_benefit_system, value['period']), ),
            default=conv.noop,
        )(value, state=state)
        if error is not None:
            return value, error

        test_case = value.copy()
        absolute_error_margin = test_case.pop('absolute_error_margin')
        axes = test_case.pop('axes')
        description = test_case.pop('description')
        input_variables = test_case.pop('input_variables')
        keywords = test_case.pop('keywords')
        name = test_case.pop('name')
        output_variables = test_case.pop('output_variables')
        period = test_case.pop('period')
        relative_error_margin = test_case.pop('relative_error_margin')

        if test_case is not None and all(
                entity_members is None
                for entity_members in test_case.values()):
            test_case = None

        scenario, error = tax_benefit_system.Scenario.make_json_to_instance(
            repair=True, tax_benefit_system=tax_benefit_system)(dict(
                axes=axes,
                input_variables=input_variables,
                period=period,
                test_case=test_case,
            ),
                                                                state=state)
        if error is not None:
            return scenario, error

        return {
            key: value
            for key, value in dict(
                absolute_error_margin=absolute_error_margin,
                description=description,
                keywords=keywords,
                name=name,
                output_variables=output_variables,
                relative_error_margin=relative_error_margin,
                scenario=scenario,
            ).items() if value is not None
        }, None

    return json_or_python_to_test
Esempio n. 37
0
        def json_or_python_to_attributes(value, state=None):
            if value is None:
                return value, None
            if state is None:
                state = conv.default_state

            # First validation and conversion step
            data, error = conv.pipe(
                conv.test_isinstance(dict),
                # TODO: Remove condition below, once every calls uses "period" instead of "date" & "year".
                self.cleanup_period_in_json_or_python,
                conv.struct(
                    dict(
                        axes=make_json_or_python_to_axes(
                            self.tax_benefit_system),
                        input_variables=conv.test_isinstance(
                            dict
                        ),  # Real test is done below, once period is known.
                        period=conv.pipe(
                            conv.function(periods.period),
                            conv.not_none,
                        ),
                        test_case=conv.test_isinstance(
                            dict
                        ),  # Real test is done below, once period is known.
                    ), ),
            )(value, state=state)
            if error is not None:
                return data, error

            # Second validation and conversion step
            data, error = conv.struct(
                dict(
                    input_variables=make_json_or_python_to_input_variables(
                        self.tax_benefit_system, data['period']),
                    test_case=self.make_json_or_python_to_test_case(
                        period=data['period'], repair=repair),
                ),
                default=conv.noop,
            )(data, state=state)
            if error is not None:
                return data, error

            # Third validation and conversion step
            errors = {}
            if data['input_variables'] is not None and data[
                    'test_case'] is not None:
                errors['input_variables'] = state._(
                    "Items input_variables and test_case can't both exist")
                errors['test_case'] = state._(
                    "Items input_variables and test_case can't both exist")
            elif data['axes'] is not None and data["test_case"] is None:
                errors['axes'] = state._(
                    "Axes can't be used with input_variables.")
            if errors:
                return data, errors

            if data['axes'] is not None:
                for parallel_axes_index, parallel_axes in enumerate(
                        data['axes']):
                    first_axis = parallel_axes[0]
                    axis_count = first_axis['count']
                    axis_entity_key = tbs.get_variable(
                        first_axis['name']).entity.key
                    first_axis_period = first_axis['period'] or data['period']
                    for axis_index, axis in enumerate(parallel_axes):
                        if axis['min'] >= axis['max']:
                            errors.setdefault('axes', {}).setdefault(
                                parallel_axes_index, {}
                            ).setdefault(axis_index, {})['max'] = state._(
                                "Max value must be greater than min value")
                        column = tbs.get_variable(axis['name'])
                        if axis['index'] >= len(
                                data['test_case'][column.entity.plural]):
                            errors.setdefault('axes', {}).setdefault(
                                parallel_axes_index, {}).setdefault(
                                    axis_index, {})['index'] = state._(
                                        "Index must be lower than {}").format(
                                            len(data['test_case'][
                                                column.entity.plural]))
                        if axis_index > 0:
                            if axis['count'] != axis_count:
                                errors.setdefault('axes', {}).setdefault(
                                    parallel_axes_index, {}
                                ).setdefault(
                                    axis_index, {}
                                )['count'] = state._(
                                    "Parallel indexes must have the same count"
                                )
                            if column.entity.key != axis_entity_key:
                                errors.setdefault('axes', {}).setdefault(
                                    parallel_axes_index, {}
                                ).setdefault(
                                    axis_index, {}
                                )['period'] = state._(
                                    "Parallel indexes must belong to the same entity"
                                )
                            axis_period = axis['period'] or data['period']
                            if axis_period.unit != first_axis_period.unit:
                                errors.setdefault('axes', {}).setdefault(
                                    parallel_axes_index, {}
                                ).setdefault(
                                    axis_index, {}
                                )['period'] = state._(
                                    "Parallel indexes must have the same period unit"
                                )
                            elif axis_period.size != first_axis_period.size:
                                errors.setdefault('axes', {}).setdefault(
                                    parallel_axes_index, {}
                                ).setdefault(
                                    axis_index, {}
                                )['period'] = state._(
                                    "Parallel indexes must have the same period size"
                                )
                if errors:
                    return data, errors

            self.axes = data['axes']
            if data['input_variables'] is not None:
                self.input_variables = data['input_variables']
            self.period = data['period']
            if data['test_case'] is not None:
                self.test_case = data['test_case']
            return self, None
                    ),
                },
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(value, state = state)
    conv.remove_ancestor_from_state(state, value)
    return validated_value, errors


validate_values_holder_json = conv.pipe(
    conv.test_isinstance(list),
    conv.uniform_sequence(
        validate_value_json,
        drop_none_items = True,
        ),
    make_validate_values_json_dates(require_consecutive_dates = False),
    conv.empty_to_none,
    )


# Level-2 Converters


validate_any_legislation_json = conv.pipe(
    conv.test_isinstance(dict),
    conv.condition(
        conv.test(lambda legislation_json: 'datesim' in legislation_json),
        validate_dated_legislation_json,
        validate_node_json,
        def json_or_python_to_test_case(value, state = None):
            if value is None:
                return value, None
            if state is None:
                state = conv.default_state

            column_by_name = self.tax_benefit_system.column_by_name

            # First validation and conversion step
            test_case, error = conv.pipe(
                conv.test_isinstance(dict),
                conv.struct(
                    dict(
                        familles = conv.pipe(
                            conv.condition(
                                conv.test_isinstance(list),
                                conv.pipe(
                                    conv.uniform_sequence(
                                        conv.test_isinstance(dict),
                                        drop_none_items = True,
                                        ),
                                    conv.function(lambda values: collections.OrderedDict(
                                        (value.pop('id', index), value)
                                        for index, value in enumerate(values)
                                        )),
                                    ),
                                ),
                            conv.test_isinstance(dict),
                            conv.uniform_mapping(
                                conv.pipe(
                                    conv.test_isinstance((basestring, int)),
                                    conv.not_none,
                                    ),
                                conv.pipe(
                                    conv.test_isinstance(dict),
                                    conv.struct(
                                        dict(itertools.chain(
                                            dict(
                                                enfants = conv.pipe(
                                                    conv.test_isinstance(list),
                                                    conv.uniform_sequence(
                                                        conv.test_isinstance((basestring, int)),
                                                        drop_none_items = True,
                                                        ),
                                                    conv.default([]),
                                                    ),
                                                parents = conv.pipe(
                                                    conv.test_isinstance(list),
                                                    conv.uniform_sequence(
                                                        conv.test_isinstance((basestring, int)),
                                                        drop_none_items = True,
                                                        ),
                                                    conv.default([]),
                                                    ),
                                                ).iteritems(),
                                            (
                                                (column.name, column.json_to_python)
                                                for column in column_by_name.itervalues()
                                                if column.entity == 'fam'
                                                ),
                                            )),
                                        drop_none_values = True,
                                        ),
                                    ),
                                drop_none_values = True,
                                ),
                            conv.default({}),
                            ),
                        individus = conv.pipe(
                            conv.condition(
                                conv.test_isinstance(list),
                                conv.pipe(
                                    conv.uniform_sequence(
                                        conv.test_isinstance(dict),
                                        drop_none_items = True,
                                        ),
                                    conv.function(lambda values: collections.OrderedDict(
                                        (value.pop('id', index), value)
                                        for index, value in enumerate(values)
                                        )),
                                    ),
                                ),
                            conv.test_isinstance(dict),
                            conv.uniform_mapping(
                                conv.pipe(
                                    conv.test_isinstance((basestring, int)),
                                    conv.not_none,
                                    ),
                                conv.pipe(
                                    conv.test_isinstance(dict),
                                    conv.struct(
                                        dict(
                                            (column.name, column.json_to_python)
                                            for column in column_by_name.itervalues()
                                            if column.entity == 'ind' and column.name not in (
                                                'idfam', 'idfoy', 'idmen', 'quifam', 'quifoy', 'quimen')
                                            ),
                                        drop_none_values = True,
                                        ),
                                    ),
                                drop_none_values = True,
                                ),
                            conv.empty_to_none,
                            conv.not_none,
                            ),
                        ),
                    ),
                )(value, state = state)
            if error is not None:
                return test_case, error

            # Second validation step
            familles_individus_id = list(test_case['individus'].iterkeys())
            test_case, error = conv.struct(
                dict(
                    familles = conv.uniform_mapping(
                        conv.noop,
                        conv.struct(
                            dict(
                                enfants = conv.uniform_sequence(conv.test_in_pop(familles_individus_id)),
                                parents = conv.uniform_sequence(conv.test_in_pop(familles_individus_id)),
                                ),
                            default = conv.noop,
                            ),
                        ),
                    ),
                default = conv.noop,
                )(test_case, state = state)

            remaining_individus_id = set(familles_individus_id)
            if remaining_individus_id:
                if error is None:
                    error = {}
                for individu_id in remaining_individus_id:
                    error.setdefault('individus', {})[individu_id] = state._(u"Individual is missing from {}").format(
                        state._(u' & ').join(
                            word
                            for word in [
                                u'familles' if individu_id in familles_individus_id else None,
                                ]
                            if word is not None
                            ))
            if error is not None:
                return test_case, error

            return test_case, error
def validate_value_xml_json(value, state = None):
    if value is None:
        return None, None
    container = state.ancestors[-1]
    value_converter = dict(
        bool = conv.pipe(
            conv.test_isinstance(basestring),
            conv.cleanup_line,
            conv.test_in([u'0', u'1']),
            ),
        float = conv.pipe(
            conv.test_isinstance(basestring),
            conv.cleanup_line,
            conv.test_conv(conv.anything_to_float),
            ),
        integer = conv.pipe(
            conv.test_isinstance(basestring),
            conv.cleanup_line,
            conv.test_conv(conv.anything_to_strict_int),
            ),
        percent = conv.pipe(
            conv.test_isinstance(basestring),
            conv.cleanup_line,
            conv.test_conv(conv.anything_to_float),
            ),
        date = conv.pipe(
            conv.test_isinstance(basestring),
            conv.cleanup_line,
            #TODO: add a new conv ?
            conv.test_conv(conv.anything_to_strict_int),
            ),
        )[container.get('format') or 'float']  # Only CODE have a "format".
    state = conv.add_ancestor_to_state(state, value)
    validated_value, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                code = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_line,
                    ),
                deb = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.iso8601_input_to_date,
                    conv.date_to_iso8601_str,
                    conv.not_none,
                    ),
                fin = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.iso8601_input_to_date,
                    conv.date_to_iso8601_str,
                    conv.not_none,
                    ),
                format = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.input_to_slug,
                    conv.test_in(xml_json_formats),
                    conv.test_equals(container.get('format')),
                    ),
                tail = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                text = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                type = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.input_to_slug,
                    conv.test_in([
                        'age',
                        'days',
                        'hours',
                        'monetary',
                        'months',
                        ]),
                    conv.test_equals(container.get('type')),
                    ),
                valeur = conv.pipe(
                    value_converter,
                    conv.not_none,
                    ),
                ),
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(value, state = state)
    conv.remove_ancestor_from_state(state, value)
    return validated_value, errors
Esempio n. 41
0
def test(force = False, name_filter = None, options_by_path = None):
    if isinstance(name_filter, str):
        name_filter = name_filter.decode('utf-8')
    if options_by_path is None:
        options_by_path = options_by_dir
    for path, options in options_by_path.iteritems():
        if not force and options.get('ignore', False):
            log.info(u'Ignoring {}'.format(path))
            continue
        if not os.path.exists(path):
            log.warning(u'Skipping missing {}'.format(path))
            continue
        if os.path.isdir(path):
            yaml_paths = [
                os.path.join(path, filename)
                for filename in sorted(os.listdir(path))
                if filename.endswith('.yaml')
                ]
        else:
            yaml_paths = [path]

        if options.get('requires'):
            # Check if the required package was successfully imported in tests/base.py
            if getattr(base, options.get('requires')) is None:
                continue

        reform_keys = options.get('reforms')
        tax_benefit_system_for_path = base.get_cached_composed_reform(
            reform_keys = reform_keys,
            tax_benefit_system = base.tax_benefit_system,
            ) if reform_keys is not None else base.tax_benefit_system

        for yaml_path in yaml_paths:
            filename_core = os.path.splitext(os.path.basename(yaml_path))[0]
            with open(yaml_path) as yaml_file:
                tests = yaml.load(yaml_file)
            tests, error = conv.pipe(
                conv.make_item_to_singleton(),
                conv.uniform_sequence(
                    conv.noop,
                    drop_none_items = True,
                    ),
                )(tests)
            if error is not None:
                embedding_error = conv.embed_error(tests, u'errors', error)
                assert embedding_error is None, embedding_error
                raise ValueError("Error in test {}:\n{}".format(yaml_path, yaml.dump(tests, allow_unicode = True,
                    default_flow_style = False, indent = 2, width = 120)))

            for test in tests:
                test, error = scenarios.make_json_or_python_to_test(
                    tax_benefit_system = tax_benefit_system_for_path,
                    default_absolute_error_margin = options.get('default_absolute_error_margin'),
                    default_relative_error_margin = options.get('default_relative_error_margin'),
                    )(test)
                if error is not None:
                    embedding_error = conv.embed_error(test, u'errors', error)
                    assert embedding_error is None, embedding_error
                    raise ValueError("Error in test {}:\n{}\nYaml test content: \n{}\n".format(
                        yaml_path, error, yaml.dump(test, allow_unicode = True,
                        default_flow_style = False, indent = 2, width = 120)))

                if not force and test.get(u'ignore', False):
                    continue
                if name_filter is not None and name_filter not in filename_core \
                        and name_filter not in (test.get('name', u'')) \
                        and name_filter not in (test.get('keywords', [])):
                    continue
                checker = check_calculate_output if options['calculate_output'] else check
                yield checker, yaml_path, test.get('name') or filename_core, unicode(test['scenario'].period), test, \
                    force
Esempio n. 42
0
    def post_process_test_case(self, test_case, period, state):

        individu_by_id = {
            individu['id']: individu
            for individu in test_case['individus']
            }

        parents_id = set(  # noqa F841
            parent_id
            for famille in test_case['familles']
            for parent_id in famille['parents']
            )
        test_case, error = conv.struct(
            dict(
                familles = conv.pipe(
                    conv.uniform_sequence(
                        conv.struct(
                            dict(
                                enfants = conv.uniform_sequence(
                                    conv.test(
                                        lambda individu_id:
                                            individu_by_id[individu_id].get('handicap', False)
                                            or find_age(individu_by_id[individu_id], period.start.date,
                                                default = 0) <= 25,
                                        error = u"Une personne à charge d'un foyer fiscal doit avoir moins de"
                                                u" 25 ans ou être handicapée",
                                        ),
                                    ),
                                parents = conv.pipe(
                                    conv.empty_to_none,
                                    conv.not_none,
                                    conv.test(lambda parents: len(parents) <= 2,
                                        error = N_(u'A "famille" must have at most 2 "parents"'))
                                    ),
                                ),
                            default = conv.noop,
                            ),
                        ),
                    conv.empty_to_none,
                    conv.not_none,
                    ),
                foyers_fiscaux = conv.pipe(
                    conv.uniform_sequence(
                        conv.struct(
                            dict(
                                declarants = conv.pipe(
                                    conv.empty_to_none,
                                    conv.not_none,
                                    conv.test(
                                        lambda declarants: len(declarants) <= 2,
                                        error = N_(u'A "foyer_fiscal" must have at most 2 "declarants"'),
                                        ),
                                    conv.uniform_sequence(conv.pipe(
                                        )),
                                    ),
                                personnes_a_charge = conv.uniform_sequence(
                                    conv.test(
                                        lambda individu_id:
                                            individu_by_id[individu_id].get('handicap', False)
                                            or find_age(individu_by_id[individu_id], period.start.date,
                                                default = 0) <= 25,
                                        error = u"Une personne à charge d'un foyer fiscal doit avoir moins de"
                                                u" 25 ans ou être handicapée",
                                        ),
                                    ),
                                ),
                            default = conv.noop,
                            ),
                        ),
                    conv.empty_to_none,
                    conv.not_none,
                    ),
                menages = conv.pipe(
                    conv.uniform_sequence(
                        conv.struct(
                            dict(
                                personne_de_reference = conv.not_none,
                                ),
                            default = conv.noop,
                            ),
                        ),
                    conv.empty_to_none,
                    conv.not_none,
                    ),
                ),
            default = conv.noop,
            )(test_case, state = state)

        return test_case, error
def validate_node_json(node, state = None):
    if node is None:
        return None, None
    state = conv.add_ancestor_to_state(state, node)

    validated_node, error = conv.test_isinstance(dict)(node, state = state)
    if error is not None:
        conv.remove_ancestor_from_state(state, node)
        return validated_node, error

    validated_node, errors = conv.struct(
        {
            '@context': conv.pipe(
                conv.test_isinstance(basestring),
                conv.make_input_to_url(full = True),
                conv.test_equals(u'http://openfisca.fr/contexts/legislation.jsonld'),
                ),
            '@type': conv.pipe(
                conv.test_isinstance(basestring),
                conv.cleanup_line,
                conv.test_in((u'Node', u'Parameter', u'Scale')),
                conv.not_none,
                ),
            'comment': conv.pipe(
                conv.test_isinstance(basestring),
                conv.cleanup_text,
                ),
            'description': conv.pipe(
                conv.test_isinstance(basestring),
                conv.cleanup_line,
                ),
            },
        constructor = collections.OrderedDict,
        default = conv.noop,
        drop_none_values = 'missing',
        keep_value_order = True,
        )(validated_node, state = state)
    if errors is not None:
        conv.remove_ancestor_from_state(state, node)
        return validated_node, errors

    validated_node.pop('@context', None)  # Remove optional @context everywhere. It will be added to root node later.
    node_converters = {
        '@type': conv.noop,
        'comment': conv.noop,
        'description': conv.noop,
        }
    node_type = validated_node['@type']
    if node_type == u'Node':
        node_converters.update(dict(
            children = conv.pipe(
                conv.test_isinstance(dict),
                conv.uniform_mapping(
                    conv.pipe(
                        conv.test_isinstance(basestring),
                        conv.cleanup_line,
                        conv.not_none,
                        ),
                    conv.pipe(
                        validate_node_json,
                        conv.not_none,
                        ),
                    ),
                conv.empty_to_none,
                conv.not_none,
                ),
            ))
    elif node_type == u'Parameter':
        node_converters.update(dict(
            format = conv.pipe(
                conv.test_isinstance(basestring),
                conv.input_to_slug,
                conv.test_in([
                    'boolean',
                    'float',
                    'integer',
                    'rate',
                    ]),
                ),
            unit = conv.pipe(
                conv.test_isinstance(basestring),
                conv.input_to_slug,
                conv.test_in(units),
                ),
            values = conv.pipe(
                conv.test_isinstance(list),
                conv.uniform_sequence(
                    validate_value_json,
                    drop_none_items = True,
                    ),
                make_validate_values_json_dates(require_consecutive_dates = True),
                conv.empty_to_none,
                conv.not_none,
                ),
            ))
    else:
        assert node_type == u'Scale'
        node_converters.update(dict(
            option = conv.pipe(
                conv.test_isinstance(basestring),
                conv.input_to_slug,
                conv.test_in((
                    'contrib',
                    'main-d-oeuvre',
                    'noncontrib',
                    )),
                ),
            slices = conv.pipe(
                conv.test_isinstance(list),
                conv.uniform_sequence(
                    validate_slice_json,
                    drop_none_items = True,
                    ),
                validate_slices_json_dates,
                conv.empty_to_none,
                conv.not_none,
                ),
            unit = conv.pipe(
                conv.test_isinstance(basestring),
                conv.input_to_slug,
                conv.test_in((
                    'currency',
                    )),
                ),
            ))
    validated_node, errors = conv.struct(
        node_converters,
        constructor = collections.OrderedDict,
        drop_none_values = 'missing',
        keep_value_order = True,
        )(validated_node, state = state)

    conv.remove_ancestor_from_state(state, node)
    return validated_node, errors
    def attribute_groupless_persons_to_entities(self, test_case, period, groupless_individus):
        individus_without_menage = groupless_individus['menages']
        individus_without_foyer_fiscal = groupless_individus['foyers_fiscaux']

        individu_by_id = {
            individu['id']: individu
            for individu in test_case['individus']
            }

        # Affecte à un foyer fiscal chaque individu qui n'appartient à aucun d'entre eux.
        new_foyer_fiscal = dict(
            declarants = [],
            personnes_a_charge = [],
            )
        new_foyer_fiscal_id = None
        for individu_id in individus_without_foyer_fiscal[:]:
            # Tente d'affecter l'individu à un foyer fiscal d'après son ménage.
            menage, menage_role = find_menage_and_role(test_case, individu_id)
            if menage_role == u'personne_de_reference':
                conjoint_id = menage[u'conjoint']
                if conjoint_id is not None:
                    foyer_fiscal, other_role = find_foyer_fiscal_and_role(test_case, conjoint_id)
                    if other_role == u'declarants' and len(foyer_fiscal[u'declarants']) == 1:
                        # Quand l'individu n'est pas encore dans un foyer fiscal, mais qu'il est personne de
                        # référence dans un ménage, qu'il y a un conjoint dans ce ménage et que ce
                        # conjoint est seul déclarant dans un foyer fiscal, alors ajoute l'individu comme
                        # autre déclarant de ce foyer fiscal.
                        foyer_fiscal[u'declarants'].append(individu_id)
                        individus_without_foyer_fiscal.remove(individu_id)
            elif menage_role == u'conjoint':
                personne_de_reference_id = menage[u'personne_de_reference']
                if personne_de_reference_id is not None:
                    foyer_fiscal, other_role = find_foyer_fiscal_and_role(test_case, personne_de_reference_id)
                    if other_role == u'declarants' and len(foyer_fiscal[u'declarants']) == 1:
                        # Quand l'individu n'est pas encore dans un foyer fiscal, mais qu'il est conjoint
                        # dans un ménage, qu'il y a une personne de référence dans ce ménage et que
                        # cette personne est seul déclarant dans un foyer fiscal, alors ajoute l'individu
                        # comme autre déclarant de ce foyer fiscal.
                        foyer_fiscal[u'declarants'].append(individu_id)
                        individus_without_foyer_fiscal.remove(individu_id)
            elif menage_role == u'enfants' and (
                    menage['personne_de_reference'] is not None or menage[u'conjoint'] is not None):
                for other_id in (menage['personne_de_reference'], menage[u'conjoint']):
                    if other_id is None:
                        continue
                    foyer_fiscal, other_role = find_foyer_fiscal_and_role(test_case, other_id)
                    if other_role == u'declarants':
                        # Quand l'individu n'est pas encore dans un foyer fiscal, mais qu'il est enfant dans
                        # un ménage, qu'il y a une personne à charge ou un conjoint dans ce ménage et que
                        # celui-ci est déclarant dans un foyer fiscal, alors ajoute l'individu comme
                        # personne à charge de ce foyer fiscal.
                        foyer_fiscal[u'personnes_a_charge'].append(individu_id)
                        individus_without_foyer_fiscal.remove(individu_id)
                        break

            if individu_id in individus_without_foyer_fiscal:
                # L'individu n'est toujours pas affecté à un foyer fiscal.
                individu = individu_by_id[individu_id]
                age = find_age(individu, period.start.date)
                if len(new_foyer_fiscal[u'declarants']) < 2 and (age is None or age >= 18):
                    new_foyer_fiscal[u'declarants'].append(individu_id)
                else:
                    new_foyer_fiscal[u'personnes_a_charge'].append(individu_id)
                if new_foyer_fiscal_id is None:
                    new_foyer_fiscal[u'id'] = new_foyer_fiscal_id = unicode(uuid.uuid4())
                    test_case[u'foyers_fiscaux'].append(new_foyer_fiscal)
                individus_without_foyer_fiscal.remove(individu_id)

            # Affecte à un ménage chaque individu qui n'appartient à aucun d'entre eux.
            new_menage = dict(
                autres = [],
                conjoint = None,
                enfants = [],
                personne_de_reference = None,
                )
            new_menage_id = None
            for individu_id in menages_individus_id[:]:
                # Tente d'affecter l'individu à un ménage d'après son foyer fiscal.
                foyer_fiscal, foyer_fiscal_role = find_foyer_fiscal_and_role(test_case, individu_id)
                if foyer_fiscal_role == u'declarants' and len(foyer_fiscal[u'declarants']) == 2:
                    for declarant_id in foyer_fiscal[u'declarants']:
                        if declarant_id != individu_id:
                            menage, other_role = find_menage_and_role(test_case, declarant_id)
                            if other_role == u'personne_de_reference' and menage[u'conjoint'] is None:
                                # Quand l'individu n'est pas encore dans un ménage, mais qu'il est déclarant
                                # dans un foyer fiscal, qu'il y a un autre déclarant dans ce foyer fiscal et que
                                # cet autre déclarant est personne de référence dans un ménage et qu'il n'y a
                                # pas de conjoint dans ce ménage, alors ajoute l'individu comme conjoint de ce
                                # ménage.
                                menage[u'conjoint'] = individu_id
                                menages_individus_id.remove(individu_id)
                            elif other_role == u'conjoint' and menage[u'personne_de_reference'] is None:
                                # Quand l'individu n'est pas encore dans un ménage, mais qu'il est déclarant
                                # dans une foyer fiscal, qu'il y a un autre déclarant dans ce foyer fiscal et
                                # que cet autre déclarant est conjoint dans un ménage et qu'il n'y a pas de
                                # personne de référence dans ce ménage, alors ajoute l'individu comme personne
                                # de référence de ce ménage.
                                menage[u'personne_de_reference'] = individu_id
                                menages_individus_id.remove(individu_id)
                            break
                elif foyer_fiscal_role == u'personnes_a_charge' and foyer_fiscal[u'declarants']:
                    for declarant_id in foyer_fiscal[u'declarants']:
                        menage, other_role = find_menage_and_role(test_case, declarant_id)
                        if other_role in (u'personne_de_reference', u'conjoint'):
                            # Quand l'individu n'est pas encore dans un ménage, mais qu'il est personne à charge
                            # dans un foyer fiscal, qu'il y a un déclarant dans ce foyer fiscal et que ce
                            # déclarant est personne de référence ou conjoint dans un ménage, alors ajoute
                            # l'individu comme enfant de ce ménage.
                            menage[u'enfants'].append(individu_id)
                            menages_individus_id.remove(individu_id)
                            break

                if individu_id in menages_individus_id:
                    # L'individu n'est toujours pas affecté à un ménage.
                    if new_menage[u'personne_de_reference'] is None:
                        new_menage[u'personne_de_reference'] = individu_id
                    elif new_menage[u'conjoint'] is None:
                        new_menage[u'conjoint'] = individu_id
                    else:
                        new_menage[u'enfants'].append(individu_id)
                    if new_menage_id is None:
                        new_menage[u'id'] = new_menage_id = unicode(uuid.uuid4())
                        test_case[u'menages'].append(new_menage)
                    menages_individus_id.remove(individu_id)

        remaining_individus_id = set(individus_without_foyer_fiscal).union(menages_individus_id)
        if remaining_individus_id:
            individu_index_by_id = {
                individu[u'id']: individu_index
                for individu_index, individu in enumerate(test_case[u'individus'])
                }
            if error is None:
                error = {}
            for individu_id in remaining_individus_id:
                error.setdefault('individus', {})[individu_index_by_id[individu_id]] = state._(
                    u"Individual is missing from {}").format(
                        state._(u' & ').join(
                            word
                            for word in [
                                u'foyers_fiscaux' if individu_id in individus_without_foyer_fiscal else None,
                                u'menages' if individu_id in menages_individus_id else None,
                                ]
                            if word is not None
                            ))
        if error is not None:
            return test_case, error

            # Third validation step
            individu_by_id = test_case['individus']
            test_case, error = conv.struct(
                dict(
                    foyers_fiscaux = conv.pipe(
                        conv.uniform_sequence(
                            conv.struct(
                                dict(
                                    declarants = conv.pipe(
                                        conv.empty_to_none,
                                        conv.not_none,
                                        conv.test(
                                            lambda declarants: len(declarants) <= 2,
                                            error = N_(u'A "foyer_fiscal" must have at most 2 "declarants"'),
                                            ),
                                        # conv.uniform_sequence(conv.pipe(
                                            # conv.test(lambda individu_id:
                                            #     find_age(individu_by_id[individu_id], period.start.date,
                                            #         default = 100) >= 18,
                                            #     error = u"Un déclarant d'un foyer fiscal doit être agé d'au moins 18"
                                            #         u" ans",
                                            #     ),
                                            # conv.test(
                                                # lambda individu_id: individu_id in parents_id,
                                                # error = u"Un déclarant ou un conjoint sur la déclaration d'impôt, doit"
                                                        # u" être un parent dans sa famille",
                                                # ),
                                            # )),
                                        ),
                                    personnes_a_charge = conv.uniform_sequence(
                                        conv.test(
                                            lambda individu_id:
                                                individu_by_id[individu_id].get('invalide', False)
                                                or find_age(individu_by_id[individu_id], period.start.date,
                                                    default = 0) < 25,
                                            error = u"Une personne à charge d'un foyer fiscal doit avoir moins de"
                                                    u" 25 ans ou être invalide",
                                            ),
                                        ),
                                    ),
                                default = conv.noop,
                                ),
                            ),
                        conv.empty_to_none,
                        conv.not_none,
                        ),
                    # individus = conv.uniform_sequence(
                    #     conv.struct(
                    #         dict(
                    #             date_naissance = conv.test(
                    #                 lambda date_naissance: period.start.date - date_naissance >= datetime.timedelta(0),
                    #                 error = u"L'individu doit être né au plus tard le jour de la simulation",
                    #                 ),
                    #             ),
                    #         default = conv.noop,
                    #         drop_none_values = 'missing',
                    #         ),
                    #     ),
                    menages = conv.pipe(
                        conv.uniform_sequence(
                            conv.struct(
                                dict(
                                    personne_de_reference = conv.not_none,
                                    ),
                                default = conv.noop,
                                ),
                            ),
                        conv.empty_to_none,
                        conv.not_none,
                        ),
                    ),
                default = conv.noop,
                )(test_case, state = state)

            return test_case, error

        return json_or_python_to_test_case
        def json_or_python_to_test_case(value, state=None):
            if value is None:
                return value, None
            if state is None:
                state = conv.default_state

            column_by_name = self.tax_benefit_system.column_by_name

            # First validation and conversion step
            test_case, error = conv.pipe(
                conv.test_isinstance(dict),
                conv.struct(
                    dict(
                        individus=conv.pipe(
                            conv.make_item_to_singleton(),
                            conv.test_isinstance(list),
                            conv.uniform_sequence(conv.test_isinstance(dict), drop_none_items=True),
                            conv.function(scenarios.set_entities_json_id),
                            conv.uniform_sequence(
                                conv.struct(
                                    dict(
                                        itertools.chain(
                                            dict(
                                                id=conv.pipe(conv.test_isinstance((basestring, int)), conv.not_none)
                                            ).iteritems(),
                                            (
                                                (column.name, column.json_to_python)
                                                for column in column_by_name.itervalues()
                                                if column.entity == "ind" and column.name not in ("idmen", "quimen")
                                            ),
                                        )
                                    ),
                                    drop_none_values=True,
                                ),
                                drop_none_items=True,
                            ),
                            conv.empty_to_none,
                            conv.not_none,
                        ),
                        menages=conv.pipe(
                            conv.make_item_to_singleton(),
                            conv.test_isinstance(list),
                            conv.uniform_sequence(conv.test_isinstance(dict), drop_none_items=True),
                            conv.function(scenarios.set_entities_json_id),
                            conv.uniform_sequence(
                                conv.struct(
                                    dict(
                                        itertools.chain(
                                            dict(
                                                autres=conv.pipe(
                                                    conv.test_isinstance(list),
                                                    conv.uniform_sequence(
                                                        conv.test_isinstance((basestring, int)), drop_none_items=True
                                                    ),
                                                    conv.default([]),
                                                ),
                                                conjoint=conv.pipe(
                                                    conv.test_isinstance(basestring, int), conv.default(None)
                                                ),
                                                enfants=conv.pipe(
                                                    conv.test_isinstance(list),
                                                    conv.uniform_sequence(
                                                        conv.test_isinstance((basestring, int)), drop_none_items=True
                                                    ),
                                                    conv.default([]),
                                                ),
                                                id=conv.pipe(conv.test_isinstance((basestring, int)), conv.not_none),
                                                personne_de_reference=conv.pipe(
                                                    conv.test_isinstance(basestring, int), conv.default(None)
                                                ),
                                            ).iteritems(),
                                            (
                                                (column.name, column.json_to_python)
                                                for column in column_by_name.itervalues()
                                                if column.entity == "men"
                                            ),
                                        )
                                    ),
                                    drop_none_values=True,
                                ),
                                drop_none_items=True,
                            ),
                            conv.default({}),
                        ),
                    )
                ),
            )(value, state=state)
            if error is not None:
                return test_case, error

            # Second validation step
            menages_individus_id = [individu["id"] for individu in test_case["individus"]]
            test_case, error = conv.struct(
                dict(
                    menages=conv.uniform_sequence(
                        conv.struct(
                            dict(
                                autres=conv.uniform_sequence(conv.test_in_pop(menages_individus_id)),
                                conjoint=conv.test_in_pop(menages_individus_id),
                                enfants=conv.uniform_sequence(conv.test_in_pop(menages_individus_id)),
                                personne_de_reference=conv.test_in_pop(menages_individus_id),
                            ),
                            default=conv.noop,
                        )
                    )
                ),
                default=conv.noop,
            )(test_case, state=state)

            remaining_individus_id = set(menages_individus_id)
            if remaining_individus_id:
                individu_index_by_id = {
                    individu[u"id"]: individu_index for individu_index, individu in enumerate(test_case[u"individus"])
                }
                if error is None:
                    error = {}
                for individu_id in remaining_individus_id:
                    error.setdefault("individus", {})[individu_index_by_id[individu_id]] = state._(
                        u"Individual is missing from {}"
                    ).format(
                        state._(u" & ").join(
                            word
                            for word in [u"menages" if individu_id in menages_individus_id else None]
                            if word is not None
                        )
                    )
            if error is not None:
                return test_case, error

            return test_case, error
def validate_slice_xml_json(slice, state = None):
    if slice is None:
        return None, None
    state = conv.add_ancestor_to_state(state, slice)
    validated_slice, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                ASSIETTE = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_values_holder_xml_json,
                        drop_none_items = True,
                        ),
                    conv.empty_to_none,
                    conv.test(lambda l: len(l) == 1, error = N_(u"List must contain one and only one item")),
                    ),
                code = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_line,
                    ),
                MONTANT = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_values_holder_xml_json,
                        drop_none_items = True,
                        ),
                    conv.empty_to_none,
                    conv.test(lambda l: len(l) == 1, error = N_(u"List must contain one and only one item")),
                    ),
                SEUIL = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_values_holder_xml_json,
                        drop_none_items = True,
                        ),
                    conv.empty_to_none,
                    conv.test(lambda l: len(l) == 1, error = N_(u"List must contain one and only one item")),
                    conv.not_none,
                    ),
                tail = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                TAUX = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_values_holder_xml_json,
                        drop_none_items = True,
                        ),
                    conv.empty_to_none,
                    conv.test(lambda l: len(l) == 1, error = N_(u"List must contain one and only one item")),
                    ),
                text = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                ),
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        conv.test(lambda slice: bool(slice.get('MONTANT')) ^ bool(slice.get('TAUX')),
            error = N_(u"Either MONTANT or TAUX must be provided")),
        )(slice, state = state)
    conv.remove_ancestor_from_state(state, slice)
    return validated_slice, errors
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(value, state = state)
    conv.remove_ancestor_from_state(state, value)
    return validated_value, errors


validate_values_holder_xml_json = conv.struct(
    dict(
        VALUE = conv.pipe(
            conv.test_isinstance(list),
            conv.uniform_sequence(
                validate_value_xml_json,
                drop_none_items = True,
                ),
            make_validate_values_xml_json_dates(require_consecutive_dates = False),
            conv.empty_to_none,
            conv.not_none,
            ),
        ),
    constructor = collections.OrderedDict,
    drop_none_values = 'missing',
    keep_value_order = True,
    )


def xml_legislation_to_json(xml_element, state = None):
    if xml_element is None:
        return None, None
    json_key, json_element = translate_xml_element_to_json_item(xml_element)
Esempio n. 48
0
 def json_to_dated_python(self):
     return conv.pipe(conv.make_anything_to_int(accept_expression=True), )
def validate_node_xml_json(node, state = None):
    if node is None:
        return None, None
    state = conv.add_ancestor_to_state(state, node)
    validated_node, errors = conv.pipe(
        conv.test_isinstance(dict),
        conv.struct(
            dict(
                BAREME = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_scale_xml_json,
                        drop_none_items = True,
                        ),
                    conv.empty_to_none,
                    ),
                CODE = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_parameter_xml_json,
                        drop_none_items = True,
                        ),
                    conv.empty_to_none,
                    ),
                code = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_line,
                    conv.not_none,
                    ),
                description = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_line,
                    ),
                NODE = conv.pipe(
                    conv.test_isinstance(list),
                    conv.uniform_sequence(
                        validate_node_xml_json,
                        drop_none_items = True,
                        ),
                    conv.empty_to_none,
                    ),
                tail = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                text = conv.pipe(
                    conv.test_isinstance(basestring),
                    conv.cleanup_text,
                    ),
                ),
            constructor = collections.OrderedDict,
            drop_none_values = 'missing',
            keep_value_order = True,
            ),
        )(node, state = state)
    if errors is None:
        children_groups_key = ('BAREME', 'CODE', 'NODE', 'VALBYTRANCHES')
        if all(
                validated_node.get(key) is None
                for key in children_groups_key
                ):
            error = state._(u"At least one of the following items must be present: {}").format(state._(u', ').join(
                u'"{}"'.format(key)
                for key in children_groups_key
                ))
            errors = dict(
                (key, error)
                for key in children_groups_key
                )
        else:
            errors = {}
        children_code = set()
        for key in children_groups_key:
            for child_index, child in enumerate(validated_node.get(key) or []):
                child_code = child['code']
                if child_code in children_code:
                    errors.setdefault(key, {}).setdefault(child_index, {})['code'] = state._(u"Duplicate value")
                else:
                    children_code.add(child_code)
    conv.remove_ancestor_from_state(state, node)
    return validated_node, errors or None
def input_to_period_tuple(value, state=None):
    """Convert an input string to a period tuple.

    .. note:: This function doesn't return a period, but a tuple that allows to construct a period.

    >>> input_to_period_tuple('2014')
    (('year', 2014), None)
    >>> input_to_period_tuple('2014:2')
    (('year', 2014, 2), None)
    >>> input_to_period_tuple('2014-2')
    (('month', (2014, 2)), None)
    >>> input_to_period_tuple('2014-3:12')
    (('month', (2014, 3), 12), None)
    >>> input_to_period_tuple('2014-2-3')
    (('day', (2014, 2, 3)), None)
    >>> input_to_period_tuple('2014-3-4:2')
    (('day', (2014, 3, 4), 2), None)

    >>> input_to_period_tuple('year:2014')
    (('year', '2014'), None)
    >>> input_to_period_tuple('year:2014:2')
    (('year', '2014', '2'), None)
    >>> input_to_period_tuple('year:2014-2:2')
    (('year', '2014-2', '2'), None)
    """
    if value is None:
        return value, None
    if state is None:
        state = conv.default_state
    split_value = tuple(
        clean_fragment for clean_fragment in (fragment.strip()
                                              for fragment in value.split(':'))
        if clean_fragment)
    if not split_value:
        return None, None
    if len(split_value) == 1:
        split_value = tuple(
            clean_fragment
            for clean_fragment in (fragment.strip()
                                   for fragment in split_value[0].split('-'))
            if clean_fragment)
        if len(split_value) == 1:
            return conv.pipe(
                conv.input_to_strict_int,
                conv.test_greater_or_equal(0),
                conv.function(lambda year: ('year', year)),
            )(split_value[0], state=state)
        if len(split_value) == 2:
            return conv.pipe(
                conv.struct((
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_greater_or_equal(0),
                    ),
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_between(1, 12),
                    ),
                ), ),
                conv.function(lambda month_tuple: ('month', month_tuple)),
            )(split_value, state=state)
        if len(split_value) == 3:
            return conv.pipe(
                conv.struct((
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_greater_or_equal(0),
                    ),
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_between(1, 12),
                    ),
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_between(1, 31),
                    ),
                ), ),
                conv.function(lambda day_tuple: ('day', day_tuple)),
            )(split_value, state=state)
        return split_value, state._(
            'Instant string contains too much "-" for a year, a month or a day'
        )
    if len(split_value) == 2:
        split_start = tuple(
            clean_fragment
            for clean_fragment in (fragment.strip()
                                   for fragment in split_value[0].split('-'))
            if clean_fragment)
        size, error = conv.input_to_int(split_value[1], state=state)
        if error is None:
            if len(split_start) == 1:
                start, error = conv.pipe(
                    conv.input_to_strict_int,
                    conv.test_greater_or_equal(0),
                )(split_start[0], state=state)
                if error is None:
                    return ('year', start, size), None
            elif len(split_start) == 2:
                start, error = conv.struct((
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_greater_or_equal(0),
                    ),
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_between(1, 12),
                    ),
                ), )(split_start, state=state)
                if error is None:
                    return ('month', start, size), None
            elif len(split_start) == 3:
                start, error = conv.struct((
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_greater_or_equal(0),
                    ),
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_between(1, 12),
                    ),
                    conv.pipe(
                        conv.input_to_strict_int,
                        conv.test_between(1, 31),
                    ),
                ), )(split_start, state=state)
                if error is None:
                    return ('day', start, size), None
    return split_value, None
    def post_process_test_case(self, test_case, period, state):

        individu_by_id = {
            individu['id']: individu
            for individu in test_case['individus']
            }

        parents_id = set(
            parent_id
            for foyer_fiscal in test_case['foyers_fiscaux']
            for parent_id in foyer_fiscal['declarants']
            )
        test_case, error = conv.struct(
            dict(
                # foyers_fiscaux = conv.pipe(
                #     conv.uniform_sequence(
                #         conv.struct(
                #             dict(
                #                 enfants = conv.uniform_sequence(
                #                     conv.test(
                #                         lambda individu_id:
                #                             individu_by_id[individu_id].get('invalide', False)
                #                             or find_age(individu_by_id[individu_id], period.start.date,
                #                                 default = 0) <= 25,
                #                         error = u"Une personne à charge d'un foyer fiscal doit avoir moins de"
                #                                 u" 25 ans ou être handicapée",
                #                         ),
                #                     ),
                #                 parents = conv.pipe(
                #                     conv.empty_to_none,
                #                     conv.not_none,
                #                     conv.test(lambda parents: len(parents) <= 2,
                #                         error = N_(u'A "famille" must have at most 2 "parents"'))
                #                     ),
                #                 ),
                #             default = conv.noop,
                #             ),
                #         ),
                #     conv.empty_to_none,
                #     conv.not_none,
                #     ),
                foyers_fiscaux = conv.pipe(
                    conv.uniform_sequence(
                        conv.struct(
                            dict(
                                declarants = conv.pipe(
                                    conv.empty_to_none,
                                    conv.not_none,
                                    conv.test(
                                        lambda declarants: len(declarants) <= 2,
                                        error = N_(u'A "foyer_fiscal" must have at most 2 "declarants"'),
                                        ),
                                    conv.uniform_sequence(conv.pipe(
                                        )),
                                    ),
                                personnes_a_charge = conv.uniform_sequence(
                                    conv.test(
                                        lambda individu_id:
                                            individu_by_id[individu_id].get('handicap', False)
                                            or find_age(individu_by_id[individu_id], period.start.date,
                                                default = 0) <= 25,
                                        error = u"Une personne à charge d'un foyer fiscal doit avoir moins de"
                                                u" 25 ans ou être handicapée",
                                        ),
                                    ),
                                ),
                            default = conv.noop,
                            ),
                        ),
                    conv.empty_to_none,
                    conv.not_none,
                    ),
                menages = conv.pipe(
                    conv.uniform_sequence(
                        conv.struct(
                            dict(
                                personne_de_reference = conv.not_none,
                                ),
                            default = conv.noop,
                            ),
                        ),
                    conv.empty_to_none,
                    conv.not_none,
                    ),
                ),
            default = conv.noop,
            )(test_case, state = state)

        return test_case, error
        # First validation and conversion step
        test_case, error = conv.pipe(
            conv.test_isinstance(dict),
            conv.struct(
                dict(
                    foyers_fiscaux = conv.pipe(
                        conv.make_item_to_singleton(),
                        conv.test_isinstance(list),
                        conv.uniform_sequence(
                            conv.test_isinstance(dict),
                            drop_none_items = True,
                            ),
                        conv.uniform_sequence(
                            conv.struct(
                                dict(itertools.chain(
                                    dict(
                                        declarants = conv.pipe(
                                            conv.make_item_to_singleton(),
                                            conv.test_isinstance(list),
                                            conv.uniform_sequence(
                                                conv.test_isinstance((basestring, int)),
                                                drop_none_items = True,
                                                ),
                                            conv.default([]),
                                            ),
                                        id = conv.pipe(
                                            conv.test_isinstance((basestring, int)),
                                            conv.not_none,
                                            ),
                                        personnes_a_charge = conv.pipe(
                                            conv.make_item_to_singleton(),
                                            conv.test_isinstance(list),
                                            conv.uniform_sequence(
                                                conv.test_isinstance((basestring, int)),
                                                drop_none_items = True,
                                                ),
                                            conv.default([]),
                                            ),
                                        ).iteritems(),
                                    )),
                                drop_none_values = True,
                                ),
                            drop_none_items = True,
                            ),
                        conv.default([]),
                        ),
                    menages = conv.pipe(
                        conv.make_item_to_singleton(),
                        conv.test_isinstance(list),
                        conv.uniform_sequence(
                            conv.test_isinstance(dict),
                            drop_none_items = True,
                            ),
                        conv.uniform_sequence(
                            conv.struct(
                                dict(itertools.chain(
                                    dict(
                                        autres = conv.pipe(
                                            # personnes ayant un lien autre avec la personne de référence
                                            conv.make_item_to_singleton(),
                                            conv.test_isinstance(list),
                                            conv.uniform_sequence(
                                                conv.test_isinstance((basestring, int)),
                                                drop_none_items = True,
                                                ),
                                            conv.default([]),
                                            ),
                                        # conjoint de la personne de référence
                                        conjoint = conv.test_isinstance((basestring, int)),
                                        enfants = conv.pipe(
                                            # enfants de la personne de référence ou de son conjoint
                                            conv.make_item_to_singleton(),
                                            conv.test_isinstance(list),
                                            conv.uniform_sequence(
                                                conv.test_isinstance((basestring, int)),
                                                drop_none_items = True,
                                                ),
                                            conv.default([]),
                                            ),
                                        id = conv.pipe(
                                            conv.test_isinstance((basestring, int)),
                                            conv.not_none,
                                            ),
                                        personne_de_reference = conv.test_isinstance((basestring, int)),
                                        ).iteritems(),
                                    )),
                                drop_none_values = True,
                                ),
                            drop_none_items = True,
                            ),
                        conv.default([]),
                        ),
                    ),
                ),
            )(test_case, state = state)

        # test_case, error = conv.struct(
        #     dict(
        #         foyers_fiscaux = conv.uniform_sequence(
        #             conv.struct(
        #                 dict(
        #                     declarants = conv.uniform_sequence(conv.test_in_pop(foyers_fiscaux_individus_id)),
        #                     personnes_a_charge = conv.uniform_sequence(conv.test_in_pop(
        #                         foyers_fiscaux_individus_id)),
        #                     ),
        #                 default = conv.noop,
        #                 ),
        #             ),
        #         menages = conv.uniform_sequence(
        #             conv.struct(
        #                 dict(
        #                     autres = conv.uniform_sequence(conv.test_in_pop(menages_individus_id)),
        #                     conjoint = conv.test_in_pop(menages_individus_id),
        #                     enfants = conv.uniform_sequence(conv.test_in_pop(menages_individus_id)),
        #                     personne_de_reference = conv.test_in_pop(menages_individus_id),
        #                     ),
        #                 default = conv.noop,
        #                 ),
        #             ),
        #         ),
        #     default = conv.noop,
        #     )(test_case, state = state)

        return test_case, error