-
Notifications
You must be signed in to change notification settings - Fork 76
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
Add editorial content on parameter and variable #717
Changes from all commits
1171b46
c934de6
f356ec3
7ec3f0a
b56d700
731e8c3
3a5d2bb
b24eb76
a4f0769
fe70fb9
5a8748b
722f702
7bd9937
d3f5fc0
6e3bfbd
ebe5b78
383be19
493122e
f194591
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,3 +16,4 @@ dist/ | |
.tags* | ||
.noseids | ||
.pytest_cache | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,9 +88,10 @@ class Parameter(object): | |
""" | ||
A parameter of the legislation. Parameters can change over time. | ||
|
||
:param name: name of the parameter, e.g. "taxes.some_tax.some_param" | ||
:param data: Data loaded from a YAML file. | ||
:param file_path: File the parameter was loaded from. | ||
:param string name: Name of the parameter, e.g. "taxes.some_tax.some_param" | ||
:param dict data: Data loaded from a YAML file. | ||
:param string file_path: File the parameter was loaded from. | ||
:param string documentation: Documentation describing parameter usage and context. | ||
|
||
|
||
Instantiate a parameter without metadata: | ||
|
@@ -121,19 +122,23 @@ def __init__(self, name, data, file_path = None): | |
_validate_parameter(self, data, data_type = dict) | ||
self.description = None | ||
self.metadata = {} | ||
self.documentation = 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 | ||
_validate_parameter(self, data, allowed_keys = set(['values', 'description', 'metadata', 'unit', 'reference'])) | ||
_validate_parameter(self, data, allowed_keys = set(['values', 'description', 'metadata', 'unit', 'reference', 'documentation'])) | ||
self.description = data.get('description') | ||
|
||
_set_backward_compatibility_metadata(self, data) | ||
self.metadata.update(data.get('metadata', {})) | ||
|
||
_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 | ||
|
||
|
@@ -254,9 +259,9 @@ class ParameterAtInstant(object): | |
|
||
def __init__(self, name, instant_str, data = None, file_path = None, metadata = None): | ||
""" | ||
:param name: name of the parameter, e.g. "taxes.some_tax.some_param" | ||
:param instant_str: Date of the value in the format `YYYY-MM-DD`. | ||
:param data: Data, usually loaded from a YAML file. | ||
:param string name: name of the parameter, e.g. "taxes.some_tax.some_param" | ||
:param string instant_str: Date of the value in the format `YYYY-MM-DD`. | ||
:param dict data: Data, usually loaded from a YAML file. | ||
""" | ||
self.name = name | ||
self.instant_str = instant_str | ||
|
@@ -316,6 +321,7 @@ def __init__(self, name = "", directory_path = None, data = None, file_path = No | |
: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 documentation: Documentation describing parameter node usage and context. | ||
:param string file_path: YAML file from which the `data` has been extracted from. | ||
|
||
|
||
|
@@ -343,6 +349,7 @@ def __init__(self, name = "", directory_path = None, data = None, file_path = No | |
self.name = name | ||
self.children = {} | ||
self.description = None | ||
self.documentation = None | ||
self.file_path = None | ||
self.metadata = {} | ||
|
||
|
@@ -359,8 +366,9 @@ def __init__(self, name = "", directory_path = None, data = None, file_path = No | |
|
||
if child_name == 'index': | ||
data = _load_yaml_file(child_path) | ||
_validate_parameter(self, data, allowed_keys = ['metadata', 'description', 'reference']) | ||
self.description = data.get('description', None) | ||
_validate_parameter(self, data, allowed_keys = ['metadata', 'description', 'documentation', 'reference']) | ||
self.description = data.get('description') | ||
self.documentation = data.get('documentation') | ||
_set_backward_compatibility_metadata(self, data) | ||
self.metadata.update(data.get('metadata', {})) | ||
else: | ||
|
@@ -379,8 +387,8 @@ def __init__(self, name = "", directory_path = None, data = None, file_path = No | |
else: | ||
self.file_path = file_path | ||
_validate_parameter(self, data, data_type = dict, allowed_keys = self._allowed_keys) | ||
# We allow to set a reference and a description for a node. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment seems outdated (we don't set a reference around it). I'd just remove it 🙂 |
||
self.description = data.get('description', None) | ||
self.description = data.get('description') | ||
self.documentation = data.get('documentation') | ||
_set_backward_compatibility_metadata(self, data) | ||
self.metadata.update(data.get('metadata', {})) | ||
for child_name, child in data.items(): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,8 +43,10 @@ def build_formula(formula, country_package_metadata, source_file_path, tax_benef | |
# Python 2 backward compatibility | ||
if isinstance(source_code[0], bytes): | ||
source_code = [source_line.decode('utf-8') for source_line in source_code] | ||
|
||
source_code = textwrap.dedent(''.join(source_code)) | ||
return { | ||
|
||
api_formula = { | ||
'source': build_source_url( | ||
country_package_metadata, | ||
source_file_path, | ||
|
@@ -54,6 +56,11 @@ def build_formula(formula, country_package_metadata, source_file_path, tax_benef | |
'content': to_unicode(source_code), | ||
} | ||
|
||
if formula.__doc__: | ||
api_formula['documentation'] = to_unicode(textwrap.dedent(formula.__doc__)) | ||
|
||
return api_formula | ||
|
||
|
||
def build_formulas(formulas, country_package_metadata, source_file_path, tax_benefit_system): | ||
return { | ||
|
@@ -79,6 +86,9 @@ def build_variable(variable, country_package_metadata, tax_benefit_system): | |
), | ||
} | ||
|
||
if variable.documentation: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at OpenFisca France, a lot of doc string is added at the formula level. This PR is not exposing this doc though the API. We should probably do that too :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way we handle unspecified documentation in the Web API is currently inconsistent:
I'd be in favor of generalizing the 3rd option, as it makes the JSON more readable, lighter, and doesn't change anything for JS clients. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed for Web API : a documentation attribute is exposed only when it exists and its value is cleaned from start and end spaces.
|
||
result['documentation'] = variable.documentation.strip() | ||
|
||
if variable.reference: | ||
result['references'] = variable.reference | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,16 +61,28 @@ def test_parameter_values(): | |
assert_regexp_matches(parameter['source'], GITHUB_URL_REGEX) | ||
assert_in('taxes/income_tax_rate.yaml', parameter['source']) | ||
|
||
# 'documentation' attribute exists only when a value is defined | ||
response = subject.get('/parameter/benefits/housing_allowance') | ||
parameter = json.loads(response.data) | ||
assert_equal(sorted(list(parameter.keys())), ['description', 'documentation', 'id', 'metadata', 'source', 'values']) | ||
assert_equal(parameter['documentation'], | ||
'A fraction of the rent.\nFrom the 1st of Dec 2016, the housing allowance no longer exists.') | ||
|
||
|
||
def test_parameter_node(): | ||
response = subject.get('/parameter/benefits') | ||
assert_equal(response.status_code, OK) | ||
parameter = json.loads(response.data) | ||
assert_equal(sorted(list(parameter.keys())), ['description', 'id', 'metadata', 'source', 'subparams']) | ||
assert_equal(sorted(list(parameter.keys())), ['description', 'documentation', 'id', 'metadata', 'source', 'subparams']) | ||
assert_equal(parameter['documentation'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
"Government support for the citizens and residents of society. " | ||
"\nThey may be provided to people of any income level, as with social security, " | ||
"\nbut usually it is intended to ensure that everyone can meet their basic human needs " | ||
"\nsuch as food and shelter.\n(See https://en.wikipedia.org/wiki/Welfare)") | ||
assert_equal(parameter['subparams'], { | ||
'housing_allowance': {'description': 'Housing allowance amount (as a fraction of the rent)'}, | ||
'basic_income': {'description': 'Amount of the basic income'} | ||
}) | ||
}, parameter['subparams']) | ||
|
||
|
||
def test_stopped_parameter_values(): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also add this new attributes to parameter nodes? I'm not sure there is a good reason to do it only for leads.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you see some use case for documentation on
ParameterNode
objects?As I see it, for now, we wouldn't use it so it would be over-engineering it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, imagine something like:
rsa.plafonds.1_enfant
,rsa.plafonds.2_enfants
,rsa.plafonds.par_enfant_supp
.In that case, it's probably more interesting to document the
rsa.plafonds
rather than the specific values.In many case, parameters work by group and are not semantically fully independent. In that case, the nodes may be the thing we want to document.