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

Create a simple computation endpoint #528

Merged
merged 50 commits into from
Jun 22, 2017
Merged

Create a simple computation endpoint #528

merged 50 commits into from
Jun 22, 2017

Conversation

fpagnoux
Copy link
Member

@fpagnoux fpagnoux commented Jun 21, 2017

Connected to #520

Sorry, I'm opening this PR late.

The spec has not yet been updated. If anyone has time to do it, that would be very appreciated.

Copy link
Contributor

@Anna-Livia Anna-Livia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for all of this work !

@@ -151,7 +198,7 @@ def get_array(self, column_name, period):
caller_input_variables_infos = calling_frame['input_variables_infos']
if variable_infos not in caller_input_variables_infos:
caller_input_variables_infos.append(variable_infos)
return self.get_or_new_holder(column_name).get_array(period)
return self.get_variable_entity(column_name).get_array(period).get_holder(column_name).get_array(period)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je ne comprends pas très bien cet enchainement d'objets. C'est l'array qui est dans le holder de l'array de la variable ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Il y a clairement un get_array de trop.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Et ce n'était pas la seule erreur de regexp ^^

@fpagnoux
Copy link
Member Author

fpagnoux commented Jun 21, 2017

If the response body doesn't show in case of error in Swagger UI, it's not our fault.

Upgrading Swagger UI may partially fix the problem in the legislation explorer.

@fpagnoux
Copy link
Member Author

fpagnoux commented Jun 21, 2017

To be done:

  • Complete the JSON schema
  • Refactor YAML spec (headers are defined n times)

Headers cannot be defined once for several responses:

Note that, currently, there is no way in Swagger to define common response headers for different response codes or different API operations. You need to define the headers for each response individually.

CHANGELOG.md Outdated
- Deprecate `simulation.holder_by_name`, `simulation.get_holder`, `get_or_new_holder`
- These functionalities are now provided by `entity.get_holder(name)`

- Deprecate constructor `Holder(simulation, column`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: missing )

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

('{"a" : "x", "b"}', BAD_REQUEST, 'error', 'Invalid JSON'),
('["An", "array"]', BAD_REQUEST, 'error', 'Invalid type'),
('{"persons": {}}', BAD_REQUEST, 'persons', 'At least one person'),
('{"persons": {"bob": {}}, "unknown_entity": {}}', BAD_REQUEST, 'unknown_entity', 'entity is not defined',),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BAD_REQUEST or NOT_FOUND ? What's the difference between an unknown entity and an unknown variable for a user ? Both come from openfisca world. NOT_FOUND for both ?

Copy link
Member Author

@fpagnoux fpagnoux Jun 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During the workshop on the API, we agreed to return a 400 in the case of an unexpected entity.

The main difference is that:

  • Unexpected entities rarely happen
  • Unexpected variables happen more often, for instance when the user requests a deprecated or renamed variable

We just want a specific error code for the last case, so that we can easily detect it when it happens.

CHANGELOG.md Outdated
- For instance `Simulation(simulation_json = {"persons": {...}, "households": {...}}, tax_benefit_system = tax_benefit_system)`

- Allows entities to be built from a JSON using their constructor
- For instance `Household(simulation, {"first_household": {...}})`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add entities_json = {"first_household": {...}} ?

Copy link
Member Author

@fpagnoux fpagnoux Jun 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A keyword argument is not needed in this case, as entities_json is the second argument.

The idea is:

  • There was a Household(simulation) constructor
  • Now there is also a Household(simulation, entities_json) constructor

type_map = {
dict: "Object",
list: "Array",
str: "String"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of types are they ? Json types ? Add the information in function name or in documentation ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

except VariableNotFound as e: # The variable doesn't exist
raise SituationParsingError([self.plural, entity_id, variable_name], e.message, code = 404)

holder = self.get_holder(variable_name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move holder init after if condition ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

for person_id in role_definition:
if person_id not in self.simulation.persons.ids:
raise SituationParsingError([self.plural, entity_id, role_id],
"Unexpected value: {0}. {0} has been declared in {1} {2}, but has not been declared in {3}.".format(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


def check_role_validity(self, role):
if role is not None and not type(role) == Role:
raise Exception("{} is not a valid role".format(role))
raise ValueError("{} is not a valid role".format(role))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catch the exception where check_role_validity is called ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_role_validity is not called in the entity initialization process.
This was just an opportunistic fix.

u"The Holder(simulation, column) constructor has been deprecated. "
u"Please use Holder(entity = entity, column = column) instead.",
Warning
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -255,12 +266,19 @@ def put_in_cache(self, value, period, extra_params = None):
raise ValueError('A period must be specified to put values in cache, except for variables with ETERNITY as as period_definition.')
if ((self.column.definition_period == MONTH and period.unit != periods.MONTH) or
(self.column.definition_period == YEAR and period.unit != periods.YEAR)):
raise ValueError(u'Unable to set a value for variable {0} for {1}-long period {2}. {0} is only defined for {3}s. Please adapt your input. If you are the maintainer of {0}, you can consider adding it a set_input attribute to enable automatic period casting.'.format(
error_message = u'Unable to set a value for variable {0} for {1}-long period {2}. {0} is only defined for {3}s. Please adapt your input. If you are the maintainer of {0}, you can consider adding it a set_input attribute to enable automatic period casting.'.format(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add new lines in message ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

entities = entity_class(self, entities_json)
else:
entities = entity_class(self)
self.entities[entity_class.key] = entities
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add entities = None ... at Simulation class first lines ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally, what you declare on the first lines of a class are class attributes (similar to static attributes in Java).

Using class attributes to define instances attributes default values technically works (only because the default attributes are not mutable), but it's not a good practise.

I know, we do this all over the codebase 😞

@fpagnoux fpagnoux requested a review from sandcha June 22, 2017 08:03
CHANGELOG.md Outdated
- Handle `500` errors in the preview API
- In this case, the API returns a JSON with details about the error.

- Allows simulations to be build from a JSON using their constructor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: builT

CHANGELOG.md Outdated
#### New features

- Introduce `/calculate` route in the preview API
- Allows to run calculations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain briefly that it handles calculations with json requests format ?

@@ -89,6 +194,22 @@ def filled_array(self, value, dtype = None):
warnings.simplefilter("ignore")
return np.full(self.count, value, dtype)

def get_holder(self, variable_name, init = True):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def holder_value(self, variable_name, init = True) ? Or holder_property.

@fpagnoux fpagnoux force-pushed the calculate-route branch 2 times, most recently from 5523d61 to 35ca745 Compare June 22, 2017 10:52
@fpagnoux fpagnoux merged commit d9ad655 into master Jun 22, 2017
@bonjourmauko bonjourmauko deleted the calculate-route branch April 10, 2018 18:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants