Skip to content

Commit

Permalink
Merge Variable and DatedVariable #371
Browse files Browse the repository at this point in the history
  • Loading branch information
sandcha authored and fpagnoux committed Jun 12, 2017
1 parent 05990e0 commit d5bad19
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 89 deletions.
154 changes: 77 additions & 77 deletions openfisca_core/formulas.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def __init__(self, holder = None):
)
for dated_formula_class in self.dated_formulas_class
]
assert self.dated_formulas
#assert self.dated_formulas

@classmethod
def at_instant(cls, instant, default = UnboundLocalError):
Expand Down Expand Up @@ -834,87 +834,87 @@ def new_filled_column(
if set_input is not None:
formula_class_attributes['set_input'] = set_input

# Turn Variable with a start or a stop into a DatedVariable
if (start_date or stop_date) and formula_class == SimpleFormula and specific_attributes.get('function'):
# Turn function into a decorated function
def is_decorated(function):
return hasattr(function, 'start_instant') or hasattr(function, 'stop_instant')

if specific_attributes.get('function') and not is_decorated(specific_attributes['function']):
specific_attributes['function'] = dated_function(start = start_date, stop = stop_date)(specific_attributes['function'])
formula_class = DatedFormula

if issubclass(formula_class, DatedFormula):



dated_formulas_class = []
for function_name, function in specific_attributes.copy().iteritems():
start_instant = getattr(function, 'start_instant', UnboundLocalError)
if start_instant is UnboundLocalError:
# Function is not dated (and may not even be a function). Skip it.
continue

# Do not accept dated formula with ETERNITY
assert column.definition_period != ETERNITY
dated_formulas_class = []
for function_name, function in specific_attributes.copy().iteritems():
start_instant = getattr(function, 'start_instant', UnboundLocalError)
if start_instant is UnboundLocalError:
# Function is not dated (and may not even be a function). Skip it.
continue
stop_instant = function.stop_instant
if stop_instant is not None:
assert start_instant <= stop_instant, 'Invalid instant interval for function {}: {} - {}'.format(
function_name, start_instant, stop_instant)

dated_formula_class_attributes = formula_class_attributes.copy()
dated_formula_class_attributes['function'] = function
dated_formula_class = type(name.encode('utf-8'), (SimpleFormula,), dated_formula_class_attributes)

del specific_attributes[function_name]
dated_formulas_class.append(dict(
formula_class = dated_formula_class,
start_instant = start_instant,
stop_instant = stop_instant,
))
# Sort dated formulas by start instant and add missing stop instants.
dated_formulas_class.sort(key = lambda dated_formula_class: dated_formula_class['start_instant'])
if start_date:
dated_formulas_class[0]['start_instant'] = max(dated_formulas_class[0]['start_instant'], instant(start_date))
if stop_date:
stop_instant = dated_formulas_class[-1]['stop_instant']
stop_instant = min(stop_instant, instant(stop_date)) if stop_instant else instant(stop_date)
dated_formulas_class[-1]['stop_instant'] = stop_instant
for dated_formula_class, next_dated_formula_class in itertools.izip(dated_formulas_class,
itertools.islice(dated_formulas_class, 1, None)):
if dated_formula_class['stop_instant'] is None:
dated_formula_class['stop_instant'] = next_dated_formula_class['start_instant'].offset(-1, 'day')
else:
assert dated_formula_class['stop_instant'] < next_dated_formula_class['start_instant'], \
"Dated formulas overlap: {} & {}".format(dated_formula_class, next_dated_formula_class)

# Add dated formulas defined in (optional) reference column when they are not overridden by new dated
# formulas.
if reference_column is not None and issubclass(reference_column.formula_class, DatedFormula):
for reference_dated_formula_class in reference_column.formula_class.dated_formulas_class:
reference_dated_formula_class = reference_dated_formula_class.copy()
for dated_formula_class in dated_formulas_class:
if reference_dated_formula_class['start_instant'] == dated_formula_class['start_instant'] \
and reference_dated_formula_class['stop_instant'] == dated_formula_class[
'stop_instant']:
break
if reference_dated_formula_class['start_instant'] >= dated_formula_class['start_instant'] \
and reference_dated_formula_class['start_instant'] < dated_formula_class[
'stop_instant']:
reference_dated_formula_class['start_instant'] = dated_formula_class['stop_instant'].offset(
1, 'day')
if reference_dated_formula_class['stop_instant'] > dated_formula_class['start_instant'] \
and reference_dated_formula_class['stop_instant'] <= dated_formula_class[
'stop_instant']:
reference_dated_formula_class['stop_instant'] = dated_formula_class['start_instant'].offset(
-1, 'day')
if reference_dated_formula_class['start_instant'] > reference_dated_formula_class[


stop_instant = function.stop_instant
if stop_instant is not None:
assert start_instant <= stop_instant, 'Invalid instant interval for function {}: {} - {}'.format(
function_name, start_instant, stop_instant)

dated_formula_class_attributes = formula_class_attributes.copy()
dated_formula_class_attributes['function'] = function
dated_formula_class = type(name.encode('utf-8'), (SimpleFormula,), dated_formula_class_attributes)

del specific_attributes[function_name]
dated_formulas_class.append(dict(
formula_class = dated_formula_class,
start_instant = start_instant,
stop_instant = stop_instant,
))
# Sort dated formulas by start instant and add missing stop instants.
dated_formulas_class.sort(key = lambda dated_formula_class: dated_formula_class['start_instant'])
if start_date:
dated_formulas_class[0]['start_instant'] = max(dated_formulas_class[0]['start_instant'], instant(start_date))
if stop_date:
stop_instant = dated_formulas_class[-1]['stop_instant']
stop_instant = min(stop_instant, instant(stop_date)) if stop_instant else instant(stop_date)
dated_formulas_class[-1]['stop_instant'] = stop_instant
for dated_formula_class, next_dated_formula_class in itertools.izip(dated_formulas_class,
itertools.islice(dated_formulas_class, 1, None)):
if dated_formula_class['stop_instant'] is None:
dated_formula_class['stop_instant'] = next_dated_formula_class['start_instant'].offset(-1, 'day')
else:
assert dated_formula_class['stop_instant'] < next_dated_formula_class['start_instant'], \
"Dated formulas overlap: {} & {}".format(dated_formula_class, next_dated_formula_class)

# Add dated formulas defined in (optional) reference column when they are not overridden by new dated
# formulas.
if reference_column is not None and issubclass(reference_column.formula_class, DatedFormula):
for reference_dated_formula_class in reference_column.formula_class.dated_formulas_class:
reference_dated_formula_class = reference_dated_formula_class.copy()
for dated_formula_class in dated_formulas_class:
if reference_dated_formula_class['start_instant'] == dated_formula_class['start_instant'] \
and reference_dated_formula_class['stop_instant'] == dated_formula_class[
'stop_instant']:
break
else:
dated_formulas_class.append(reference_dated_formula_class)
dated_formulas_class.sort(key = lambda dated_formula_class: dated_formula_class['start_instant'])
break
if reference_dated_formula_class['start_instant'] >= dated_formula_class['start_instant'] \
and reference_dated_formula_class['start_instant'] < dated_formula_class[
'stop_instant']:
reference_dated_formula_class['start_instant'] = dated_formula_class['stop_instant'].offset(
1, 'day')
if reference_dated_formula_class['stop_instant'] > dated_formula_class['start_instant'] \
and reference_dated_formula_class['stop_instant'] <= dated_formula_class[
'stop_instant']:
reference_dated_formula_class['stop_instant'] = dated_formula_class['start_instant'].offset(
-1, 'day')
if reference_dated_formula_class['start_instant'] > reference_dated_formula_class[
'stop_instant']:
break
else:
dated_formulas_class.append(reference_dated_formula_class)
dated_formulas_class.sort(key = lambda dated_formula_class: dated_formula_class['start_instant'])

formula_class_attributes['dated_formulas_class'] = dated_formulas_class
else:
assert issubclass(formula_class, SimpleFormula), formula_class

function = specific_attributes.pop('function', None)
if column.definition_period == ETERNITY:
assert function is None
if reference_column is not None and function is None:
function = reference_column.formula_class.function
formula_class_attributes['function'] = function
formula_class_attributes['dated_formulas_class'] = dated_formulas_class


# Ensure that all attributes defined in ConversionColumn class are used.
assert not specific_attributes, 'Unexpected attributes in definition of variable "{}": {!r}'.format(name,
Expand Down
2 changes: 1 addition & 1 deletion openfisca_core/model_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
requested_period_last_or_next_value,
requested_period_last_value,
)
from .variables import DatedVariable, Variable # noqa analysis:ignore
from .variables import Variable # noqa analysis:ignore
from .formula_helpers import apply_thresholds, switch # noqa analysis:ignore
from .periods import MONTH, YEAR, ETERNITY # noqa analysis:ignore
from .reforms import Reform # noqa analysis:ignore
4 changes: 2 additions & 2 deletions openfisca_core/scripts/measure_performances.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from openfisca_core.periods import ETERNITY
from openfisca_core.entities import build_entity
from openfisca_core.formulas import dated_function
from openfisca_core.variables import DatedVariable, Variable
from openfisca_core.variables import Variable
from openfisca_core.taxbenefitsystems import TaxBenefitSystem
from openfisca_core.tools import assert_near

Expand Down Expand Up @@ -139,7 +139,7 @@ def function(self, simulation, period):
return rsa + salaire_imposable * 0.7


class rsa(DatedVariable):
class rsa(Variable):
column = FloatCol
entity = Individu
label = u"RSA"
Expand Down
4 changes: 2 additions & 2 deletions openfisca_core/taxbenefitsystems.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def add_variable(self, variable):
"""
Adds an OpenFisca variable to the tax and benefit system.
:param variable: The variable to add. Must be a subclass of Variable or DatedVariable.
:param variable: The variable to add. Must be a subclass of Variable.
:raises: :any:`VariableNameConflict` if a variable with the same name have previously been added to the tax and benefit system.
"""
Expand All @@ -170,7 +170,7 @@ def update_variable(self, variable):
If no variable with the given name exists in the tax and benefit system, no error will be raised and the variable will be simply added.
:param variable: Variable to add. Must be a subclass of Variable or DatedVariable.
:param variable: Variable to add. Must be a subclass of Variable.
"""
return self.load_variable(variable, update = True)

Expand Down
7 changes: 2 additions & 5 deletions openfisca_core/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import inspect
import textwrap

from openfisca_core.formulas import SimpleFormula, DatedFormula, new_filled_column
from openfisca_core.formulas import DatedFormula, new_filled_column


class AbstractVariable(object):
Expand Down Expand Up @@ -62,8 +62,5 @@ def to_column(self, tax_benefit_system):


class Variable(AbstractVariable):
formula_class = SimpleFormula


class DatedVariable(AbstractVariable):
formula_class = DatedFormula

4 changes: 2 additions & 2 deletions tests/core/test_reforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from openfisca_core.periods import MONTH
from openfisca_core.reforms import Reform
from openfisca_core.formulas import dated_function
from openfisca_core.variables import Variable, DatedVariable
from openfisca_core.variables import Variable
from openfisca_core.periods import Instant
from openfisca_core.tools import assert_near
from openfisca_country_template.entities import Household
Expand Down Expand Up @@ -311,7 +311,7 @@ def apply(self):


def test_add_dated_variable():
class new_dated_variable(DatedVariable):
class new_dated_variable(Variable):
column = columns.IntCol
label = u"Nouvelle variable introduite par la réforme"
entity = Household
Expand Down

0 comments on commit d5bad19

Please sign in to comment.