Example #1
0
    def __init__(self, name, data, file_path):
        """
        :param name: name of the scale, eg "taxes.some_scale"
        :param data: Data loaded from a YAML file. In case of a reform, the data can also be created dynamically.
        :param file_path: File the parameter was loaded from.
        """
        self.name: str = name
        self.file_path: str = file_path
        helpers._validate_parameter(self,
                                    data,
                                    data_type=dict,
                                    allowed_keys=self._allowed_keys)
        self.description: str = data.get('description')
        self.metadata: typing.Dict = {}
        helpers._set_backward_compatibility_metadata(self, data)
        self.metadata.update(data.get('metadata', {}))

        if not isinstance(data.get('brackets', []), list):
            raise ParameterParsingError(
                "Property 'brackets' of scale '{}' must be of type array.".
                format(self.name), self.file_path)

        brackets = []
        for i, bracket_data in enumerate(data.get('brackets', [])):
            bracket_name = helpers._compose_name(name, item_name=i)
            bracket = parameters.ParameterScaleBracket(name=bracket_name,
                                                       data=bracket_data,
                                                       file_path=file_path)
            brackets.append(bracket)
        self.brackets: typing.List[parameters.ParameterScaleBracket] = brackets
    def __init__(self, name, data, file_path=None):
        self.name: str = name
        self.file_path: str = file_path
        helpers._validate_parameter(self, data, data_type=dict)
        self.description: str = None
        self.metadata: typing.Dict = {}
        self.documentation: str = None
        self.values_history = self  # Only for backward compatibility

        # Normal parameter declaration: the values are declared under the 'values' key: parse the description and metadata.
        if data.get('values'):
            # 'unit' and 'reference' are only listed here for backward compatibility
            helpers._validate_parameter(self,
                                        data,
                                        allowed_keys=config.COMMON_KEYS.union(
                                            {'values'}))
            self.description = data.get('description')

            helpers._set_backward_compatibility_metadata(self, data)
            self.metadata.update(data.get('metadata', {}))

            helpers._validate_parameter(self, data['values'], data_type=dict)
            values = data['values']

            self.documentation = data.get('documentation')

        else:  # Simplified parameter declaration: only values are provided
            values = data

        instants = sorted(values.keys(),
                          reverse=True)  # sort in reverse chronological order

        values_list = []
        for instant_str in instants:
            if not periods.INSTANT_PATTERN.match(instant_str):
                raise ParameterParsingError(
                    "Invalid property '{}' in '{}'. Properties must be valid YYYY-MM-DD instants, such as 2017-01-15."
                    .format(instant_str, self.name), file_path)

            instant_info = values[instant_str]

            #  Ignore expected values, as they are just metadata
            if instant_info == "expected" or isinstance(
                    instant_info, dict) and instant_info.get("expected"):
                continue

            value_name = helpers._compose_name(name, item_name=instant_str)
            value_at_instant = ParameterAtInstant(value_name,
                                                  instant_str,
                                                  data=instant_info,
                                                  file_path=self.file_path,
                                                  metadata=self.metadata)
            values_list.append(value_at_instant)

        self.values_list: typing.List[ParameterAtInstant] = values_list
Example #3
0
 def __getattr__(self, key):
     param_name = helpers._compose_name(self._name, item_name=key)
     raise ParameterNotFoundError(param_name, self._instant_str)
    def update(self, period=None, start=None, stop=None, value=None):
        """
        Change the value for a given period.

        :param period: Period where the value is modified. If set, `start` and `stop` should be `None`.
        :param start: Start of the period. Instance of `openfisca_core.periods.Instant`. If set, `period` should be `None`.
        :param stop: Stop of the period. Instance of `openfisca_core.periods.Instant`. If set, `period` should be `None`.
        :param value: New value. If `None`, the parameter is removed from the legislation parameters for the given period.
        """
        if period is not None:
            if start is not None or stop is not None:
                raise TypeError(
                    "Wrong input for 'update' method: use either 'update(period, value = value)' or 'update(start = start, stop = stop, value = value)'. You cannot both use 'period' and 'start' or 'stop'."
                )
            if isinstance(period, str):
                period = periods.period(period)
            start = period.start
            stop = period.stop
        if start is None:
            raise ValueError("You must provide either a start or a period")
        start_str = str(start)
        stop_str = str(stop.offset(1, 'day')) if stop else None

        old_values = self.values_list
        new_values = []
        n = len(old_values)
        i = 0

        # Future intervals : not affected
        if stop_str:
            while (i < n) and (old_values[i].instant_str >= stop_str):
                new_values.append(old_values[i])
                i += 1

        # Right-overlapped interval
        if stop_str:
            if new_values and (stop_str == new_values[-1].instant_str):
                pass  # such interval is empty
            else:
                if i < n:
                    overlapped_value = old_values[i].value
                    value_name = helpers._compose_name(self.name,
                                                       item_name=stop_str)
                    new_interval = ParameterAtInstant(
                        value_name, stop_str, data={'value': overlapped_value})
                    new_values.append(new_interval)
                else:
                    value_name = helpers._compose_name(self.name,
                                                       item_name=stop_str)
                    new_interval = ParameterAtInstant(value_name,
                                                      stop_str,
                                                      data={'value': None})
                    new_values.append(new_interval)

        # Insert new interval
        value_name = helpers._compose_name(self.name, item_name=start_str)
        new_interval = ParameterAtInstant(value_name,
                                          start_str,
                                          data={'value': value})
        new_values.append(new_interval)

        # Remove covered intervals
        while (i < n) and (old_values[i].instant_str >= start_str):
            i += 1

        # Past intervals : not affected
        while i < n:
            new_values.append(old_values[i])
            i += 1

        self.values_list = new_values
Example #5
0
    def __init__(self,
                 name="",
                 directory_path=None,
                 data=None,
                 file_path=None):
        """
        Instantiate a ParameterNode either from a dict, (using `data`), or from a directory containing YAML files (using `directory_path`).

        :param string name: Name of the node, eg "taxes.some_tax".
        :param string directory_path: Directory containing YAML files describing the node.
        :param dict data: Object representing the parameter node. It usually has been extracted from a YAML file.
        :param string file_path: YAML file from which the `data` has been extracted from.


        Instantiate a ParameterNode from a dict:

        >>> node = ParameterNode('basic_income', data = {
            'amount': {
              'values': {
                "2015-01-01": {'value': 550},
                "2016-01-01": {'value': 600}
                }
              },
            'min_age': {
              'values': {
                "2015-01-01": {'value': 25},
                "2016-01-01": {'value': 18}
                }
              },
            })

        Instantiate a ParameterNode from a directory containing YAML parameter files:

        >>> node = ParameterNode('benefits', directory_path = '/path/to/country_package/parameters/benefits')
        """
        self.name: str = name
        self.children: typing.Dict[str, typing.Union[
            ParameterNode, Parameter, parameters.ParameterScale]] = {}
        self.description: str = None
        self.documentation: str = None
        self.file_path: str = None
        self.metadata: typing.Dict = {}

        if directory_path:
            self.file_path = directory_path
            for child_name in os.listdir(directory_path):
                child_path = os.path.join(directory_path, child_name)
                if os.path.isfile(child_path):
                    child_name, ext = os.path.splitext(child_name)

                    # We ignore non-YAML files
                    if ext not in config.FILE_EXTENSIONS:
                        continue

                    if child_name == 'index':
                        data = helpers._load_yaml_file(child_path) or {}
                        helpers._validate_parameter(
                            self, data, allowed_keys=config.COMMON_KEYS)
                        self.description = data.get('description')
                        self.documentation = data.get('documentation')
                        helpers._set_backward_compatibility_metadata(
                            self, data)
                        self.metadata.update(data.get('metadata', {}))
                    else:
                        child_name_expanded = helpers._compose_name(
                            name, child_name)
                        child = helpers.load_parameter_file(
                            child_path, child_name_expanded)
                        self.add_child(child_name, child)

                elif os.path.isdir(child_path):
                    child_name = os.path.basename(child_path)
                    child_name_expanded = helpers._compose_name(
                        name, child_name)
                    child = ParameterNode(child_name_expanded,
                                          directory_path=child_path)
                    self.add_child(child_name, child)

        else:
            self.file_path = file_path
            helpers._validate_parameter(self,
                                        data,
                                        data_type=dict,
                                        allowed_keys=self._allowed_keys)
            self.description = data.get('description')
            self.documentation = data.get('documentation')
            helpers._set_backward_compatibility_metadata(self, data)
            self.metadata.update(data.get('metadata', {}))
            for child_name, child in data.items():
                if child_name in config.COMMON_KEYS:
                    continue  # do not treat reserved keys as subparameters.

                child_name = str(child_name)
                child_name_expanded = helpers._compose_name(name, child_name)
                child = helpers._parse_child(child_name_expanded, child,
                                             file_path)
                self.add_child(child_name, child)