Skip to content

Commit

Permalink
Relax rules on allocation to group entities
Browse files Browse the repository at this point in the history
  • Loading branch information
Morendil authored and sandcha committed Mar 13, 2019
1 parent 08e8216 commit 7d622d7
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 18 deletions.
32 changes: 23 additions & 9 deletions openfisca_core/simulation_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ def build_from_entities(self, tax_benefit_system, input_dict, **kwargs):
for entity_class in tax_benefit_system.group_entities:
entity = simulation.entities[entity_class.key]
instances_json = input_dict.get(entity_class.plural)
self.add_group_entity(simulation.persons.plural, persons_ids, entity, instances_json)
if instances_json is not None:
self.add_group_entity(simulation.persons.plural, persons_ids, entity, instances_json)
else:
self.add_default_group_entity(persons_ids, entity)

if axes:
self.axes = axes
Expand Down Expand Up @@ -206,20 +209,27 @@ def add_person_entity(self, entity, instances_json):

return self.get_ids(entity.plural)

def add_default_group_entity(self, persons_ids, entity):
persons_count = len(persons_ids)
self.entity_ids[entity.plural] = persons_ids
self.entity_counts[entity.plural] = persons_count
self.memberships[entity.plural] = np.arange(0, persons_count - 1, dtype = np.int32)

def add_group_entity(self, persons_plural, persons_ids, entity, instances_json):
"""
Add all instances of one of the model's entities as described in ``instances_json``.
"""
check_type(instances_json, dict, [entity.plural])
entity_ids = list(map(str, instances_json.keys()))
self.entity_ids[entity.plural] = entity_ids
self.entity_counts[entity.plural] = len(entity_ids)

persons_count = len(persons_ids)
persons_to_allocate = set(persons_ids)
self.memberships[entity.plural] = np.empty(persons_count, dtype = np.int32)
self.roles[entity.plural] = np.empty(persons_count, dtype = object)

self.entity_ids[entity.plural] = entity_ids
self.entity_counts[entity.plural] = len(entity_ids)

for instance_id, instance_object in instances_json.items():
check_type(instance_object, dict, [entity.plural, instance_id])

Expand Down Expand Up @@ -249,16 +259,20 @@ def add_group_entity(self, persons_plural, persons_ids, entity, instances_json):

self.init_variable_values(entity, variables_json, instance_id)

if persons_to_allocate:
entity_ids = entity_ids + list(persons_to_allocate)
for person_id in persons_to_allocate:
person_index = persons_ids.index(person_id)
self.memberships[entity.plural][person_index] = entity_ids.index(person_id)
self.roles[entity.plural][person_index] = entity.flattened_roles[0]
# Adjust previously computed ids and counts
self.entity_ids[entity.plural] = entity_ids
self.entity_counts[entity.plural] = len(entity_ids)

# Convert back to Python array
self.roles[entity.plural] = self.roles[entity.plural].tolist()
self.memberships[entity.plural] = self.memberships[entity.plural].tolist()

if persons_to_allocate:
raise SituationParsingError([entity.plural],
'{0} have been declared in {1}, but are not members of any {2}. All {1} must be allocated to a {2}.'.format(
persons_to_allocate, persons_plural, entity.key)
)

def set_default_period(self, period_str):
if period_str:
self.default_period = str(period(period_str))
Expand Down
22 changes: 15 additions & 7 deletions tests/core/test_simulation_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,13 +340,21 @@ def test_allocate_person_twice(simulation_builder):
assert exception.value.error == {'familles': {'famille1': {'parents': 'Alicia has been declared more than once in familles'}}}


def test_unallocated_person(simulation_builder, group_entity):
with raises(SituationParsingError) as exception:
simulation_builder.add_group_entity('persons', ['Alicia', 'Javier', 'Sarah', 'Tom'], group_entity, {
'Household_1': {'parents': ['Alicia', 'Javier']},
'Household_2': {'parents': ['Tom']},
})
assert exception.value.error == {'households': "{'Sarah'} have been declared in persons, but are not members of any household. All persons must be allocated to a household."}
def test_one_person_without_household(simulation_builder):
simulation_dict = {'persons': {'Alicia': {}}}
simulation = simulation_builder.build_from_dict(tax_benefit_system, simulation_dict)
assert simulation.household.count == 1


def test_some_person_without_household(simulation_builder):
input_yaml = """
persons: {'Alicia': {}, 'Bob': {}}
household: {'parents': ['Alicia']}
"""
simulation = simulation_builder.build_from_dict(tax_benefit_system, yaml.load(input_yaml))
assert simulation.household.count == 2
parents_in_households = simulation.household.nb_persons(role = simulation.household.PARENT)
assert parents_in_households.tolist() == [1, 1] # household member default role is first_parent


# Test Intégration
Expand Down
3 changes: 1 addition & 2 deletions tests/web_api/test_calculate.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ def check_response(data, expected_error_code, path_to_check, content_to_check):
('{"persons": {"bob": {"salary": {"invalid period": 2000 }}}}', BAD_REQUEST, 'persons/bob/salary', 'Expected a period',),
('{"persons": {"bob": {"salary": {"invalid period": null }}}}', BAD_REQUEST, 'persons/bob/salary', 'Expected a period',),
('{"persons": {"bob": {"basic_income": {"2017": 2000 }}}, "households": {"household": {"parents": ["bob"]}}}', BAD_REQUEST, 'persons/bob/basic_income/2017', 'basic_income is only defined for months',),
('{"persons": {"bob": {"salary": {"ETERNITY": 2000 }}}, "households": {"household": {"parents": ["bob"]}}}', BAD_REQUEST, 'persons/bob/salary/ETERNITY', 'salary is only defined for months',),
('{"persons": {"bob": {"birth": {"ETERNITY": "1980-01-01"} }}, "households": {}}', BAD_REQUEST, 'households', 'not members of any household',),
('{"persons": {"bob": {"salary": {"ETERNITY": 2000 }}}, "households": {"household": {"parents": ["bob"]}}}', BAD_REQUEST, 'persons/bob/salary/ETERNITY', 'salary is only defined for months',)
])
def test_responses(test):
check_response(*test)
Expand Down

0 comments on commit 7d622d7

Please sign in to comment.