Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose AmountTaxScale by Web API #756

Merged
merged 8 commits into from
Nov 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

### 24.7.0 [#756](https://github.com/openfisca/openfisca-core/pull/756)

- Exposes `amount` scales through the Web API
- Allows for [AmountTaxScale](https://github.com/openfisca/openfisca-core/blob/2b76b2ae5f684832411694c7c763b2d84c521c3c/openfisca_core/taxscales.py#L119) parameters to be consumed through the Web API
- See for an example this [parameter in openfisca-tunisia](https://github.com/openfisca/openfisca-tunisia/blob/10a15168e0aab5000f6850ad2f7779eba5da0fe0/openfisca_tunisia/parameters/impot_revenu/contribution_budget_etat.yaml)

### 24.6.8 [#753](https://github.com/openfisca/openfisca-core/pull/753)

- Always allow `documentation` property for `ParameterNode`
Expand Down
21 changes: 14 additions & 7 deletions openfisca_web_api/loader/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ def get_value(date, values):
return None


def build_api_scale(scale):
# preprocess brackets
def build_api_scale(scale, value_key_name):
# preprocess brackets for a scale with 'rates' or 'amounts'
brackets = [{
'thresholds': build_api_values_history(bracket.threshold),
'rates': build_api_values_history(bracket.rate),
'values': build_api_values_history(getattr(bracket, value_key_name))
} for bracket in scale.brackets]

dates = set(sum(
[list(bracket['thresholds'].keys()) + list(bracket['rates'].keys()) for bracket in brackets],
[list(bracket['thresholds'].keys())
+ list(bracket['values'].keys()) for bracket in brackets],
[])) # flatten the dates and remove duplicates

# We iterate on all dates as we need to build the whole scale for each of them
Expand All @@ -42,13 +43,14 @@ def build_api_scale(scale):
for bracket in brackets:
threshold_value = get_value(date, bracket['thresholds'])
if threshold_value is not None:
rate_value = get_value(date, bracket['rates'])
rate_or_amount_value = get_value(date, bracket['values'])
api_scale[date] = api_scale.get(date) or {}
api_scale[date][threshold_value] = rate_value
api_scale[date][threshold_value] = rate_or_amount_value

# Handle stopped parameters: a parameter is stopped if its first bracket is stopped
latest_date_first_threshold = max(brackets[0]['thresholds'].keys())
latest_value_first_threshold = brackets[0]['thresholds'][latest_date_first_threshold]

if latest_value_first_threshold is None:
api_scale[latest_date_first_threshold] = None

Expand Down Expand Up @@ -80,7 +82,10 @@ def walk_node(node, parameters, path_fragments, country_package_metadata):
api_parameter['documentation'] = child.documentation.strip()
api_parameter['values'] = build_api_values_history(child)
elif isinstance(child, Scale):
api_parameter['brackets'] = build_api_scale(child)
if 'rate' in child.brackets[0].children:
api_parameter['brackets'] = build_api_scale(child, 'rate')
elif 'amount' in child.brackets[0].children:
api_parameter['brackets'] = build_api_scale(child, 'amount')
elif isinstance(child, ParameterNode):
if child.documentation:
api_parameter['documentation'] = child.documentation.strip()
Expand All @@ -93,6 +98,8 @@ def walk_node(node, parameters, path_fragments, country_package_metadata):
walk_node(child, parameters, path_fragments + [child_name], country_package_metadata)
parameters.append(api_parameter)

return parameters


def build_parameters(tax_benefit_system, country_package_metadata):
original_parameters = tax_benefit_system.parameters
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

setup(
name = 'OpenFisca-Core',
version = '24.6.8',
version = '24.7.0',
bonjourmauko marked this conversation as resolved.
Show resolved Hide resolved
author = 'OpenFisca Team',
author_email = 'contact@openfisca.org',
classifiers = [
Expand Down
Empty file.
50 changes: 50 additions & 0 deletions tests/web_api/loader/test_parameters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-

import pytest

from openfisca_core.parameters import ParameterNode, Scale

from openfisca_web_api.loader.parameters import build_api_scale, walk_node


def test_build_rate_scale():
'''Extracts a 'rate' children from a bracket collection'''
data = {'brackets': [{'rate': {'2014-01-01': {'value': 0.5}}, 'threshold': {'2014-01-01': {'value': 1}}}]}
rate = Scale('this rate', data, None)
assert build_api_scale(rate, 'rate') == {'2014-01-01': {1: 0.5}}


def test_build_amount_scale():
'''Extracts an 'amount' children from a bracket collection'''
data = {'brackets': [{'amount': {'2014-01-01': {'value': 0}}, 'threshold': {'2014-01-01': {'value': 1}}}]}
rate = Scale('that amount', data, None)
assert build_api_scale(rate, 'amount') == {'2014-01-01': {1: 0}}


@pytest.fixture
def walk_node_args():
parameters = []
path_fragments = []
country_package_metadata = {'location': 'foo', 'version': '1', 'repository_url': 'foo'}

return parameters, path_fragments, country_package_metadata


def test_walk_node_rate_scale(walk_node_args):
'''Serializes a 'rate' parameter node'''
root_node = ParameterNode(data = {})
data = {'brackets': [{'rate': {'2014-01-01': {'value': 0.5}}, 'threshold': {'2014-01-01': {'value': 1}}}]}
scale = Scale('this rate', data, None)
root_node.children['rate'] = scale
parameters = walk_node(root_node, *walk_node_args)
assert parameters == [{'description': None, 'id': 'rate', 'metadata': {}, 'brackets': {'2014-01-01': {1: 0.5}}}]


def test_walk_node_amount_scale(walk_node_args):
'''Serializes an 'amount' parameter node'''
root_node = ParameterNode(data = {})
data = {'brackets': [{'amount': {'2014-01-01': {'value': 0}}, 'threshold': {'2014-01-01': {'value': 1}}}]}
scale = Scale('that amount', data, None)
root_node.children['amount'] = scale
parameters = walk_node(root_node, *walk_node_args)
assert parameters == [{'description': None, 'id': 'amount', 'metadata': {}, 'brackets': {'2014-01-01': {1: 0}}}]