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

Resolve more backwards compatibility issues related to old taxcalc tables #814

Merged
merged 18 commits into from Feb 7, 2018

Conversation

hdoupe
Copy link
Collaborator

@hdoupe hdoupe commented Jan 25, 2018

Resolves #813

[EDIT] Also resolves #812

return self.tax_result
else:
return rename_keys(self.tax_result, PRE_TC_0130_RES_MAP)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@codekansas I would prefer to not copy this method from the taxbrain models code. Do you have any ideas for a cleaner solution?

Copy link
Contributor

Choose a reason for hiding this comment

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

You'll probably want to put this in a utils.py file in the parent directory. Hmm... If you do this, you'll probably also want to include

from __future__ import absolute_import

in the top of the file, so that importing is consistent.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@codekansas thanks for the advice. That makes sense.

Another solution would be to create an abstract model class with this method. Then, the TaxSaveInputs and DynamicSaveInputs classes could inherit this class. It is likely that we will have to add a get_fields method in a separate PR that resolves #811. This would be like the base class approach described here. An example that is similar to our situation is described here.

@hdoupe
Copy link
Collaborator Author

hdoupe commented Jan 26, 2018

#812 was caused because I added a decorator for the tax_result variable. In doing so, I added a field _tax_result that pointed to a column in the taxbrain_taxsaveinputs table, tax_result. However, in the migrations file, you can see that the field tax_result was removed from the table and then, added back later, effectively deleting all previous information.

This PR uses a method to do the table formatting instead of a decorator. I'd be interested in hearing feedback on this approach and ideas for other approaches.

Copy link
Contributor

@codekansas codekansas left a comment

Choose a reason for hiding this comment

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

See noted changes

"""
outputurl = OutputUrl.objects.get(unique_inputs__pk=self.pk)
taxcalc_vers = outputurl.taxcalc_vers
taxcalc_vers = taxcalc_vers.split('.')
Copy link
Contributor

Choose a reason for hiding this comment

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

Can simplify to

taxcalc_vers = outputurl.taxcalc_vers.split('.')

But probably the best solution would be to use some versioning comparison standard library, i.e. distutils.version.StrictVersion or distutils.version.LooseVersion so that you don't have to mess around with this version comparison stuff.

return self.tax_result
else:
return rename_keys(self.tax_result, PRE_TC_0130_RES_MAP)

Copy link
Contributor

Choose a reason for hiding this comment

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

You'll probably want to put this in a utils.py file in the parent directory. Hmm... If you do this, you'll probably also want to include

from __future__ import absolute_import

in the top of the file, so that importing is consistent.


@property
def tax_result(self):
def get_tax_result(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like a good idea - I think the best "logic" for deciding if something should be a property is if:

  1. It is computationally light, or
  2. It is cached (i.e. the result is only computed once)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok, that makes sense. Thanks for the info. I also couldn't get the property approach to work without deleting the column in the database.

"""
If taxcalc version is greater than or equal to 0.13.0, return table
If taxcalc version is less than 0.13.0, then rename keys to new names
and then return table
"""
outputurl = OutputUrl.objects.get(unique_inputs__pk=self.pk)
taxcalc_vers = outputurl.taxcalc_vers
taxcalc_vers = tuple(map(int, taxcalc_vers.split('.')))
taxcalc_vers = taxcalc_vers.split('.')
Copy link
Contributor

Choose a reason for hiding this comment

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

See earlier comment about using distutils for version parsing instead

@@ -1014,7 +1014,7 @@ def csv_output(request, pk):
filename = "taxbrain_outputs_" + suffix + ".csv"
response['Content-Disposition'] = 'attachment; filename="' + filename + '"'
Copy link
Contributor

Choose a reason for hiding this comment

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

It is probably better to use the following:

filename = 'taxbrain_outputs_{}.csv'.format(suffix)
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)

Also: For debugging purposes, it is a good idea to log things (the response filename, for example).

taxcalc_vers = outputurl.taxcalc_vers
# only the older (pre 0.13.0) taxcalc versions are null
if taxcalc_vers:
taxcalc_vers = LooseVersion(taxcalc_vers)
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like this should break something because LooseVersion isn't imported... Maybe I'm missing something

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nope, thanks for catching that. I added the import statement and a test for testing this method on the dynamic behavioral side.


# older PB versions stored commit reference too
# e.g. taxcalc_vers = "0.9.0.d79abf"
if taxcalc_vers >= LooseVersion("0.13.0"):
Copy link
Contributor

Choose a reason for hiding this comment

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

You could put this in a constant somewhere, like "taxcalc_min_compat_version" or something... Might be a bit clearer where this number comes from, so that people looking at it in the future are less confused.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done, thanks.

self._tax_result = result
# older PB versions stored commit reference too
# e.g. taxcalc_vers = "0.9.0.d79abf"
if taxcalc_vers >= LooseVersion("0.13.0"):
Copy link
Contributor

Choose a reason for hiding this comment

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

See comment above... Might also be a good idea to make a utils.py file for this commit anyway?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok, I moved this method to the bottom of taxbrain/helpers.py. The utility and helper functions need to be refactored and re-organzied. See issue #816

@hdoupe hdoupe changed the title Fix integer casting error Resolve more backwards compatibility issues related to old taxcalc tables Feb 2, 2018
assert (res["table_label_0"]["bin_0"] == [0, 1, 2] and
res["table_label_0"]["bin_1"] == [2, 1, 0] and
res["table_label_1"]["bin_0"] == [0, 1, 2] and
res["table_label_1"]["bin_1"] == [2, 1, 0])
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe this could be changed into four assert statements so it's clearer where something breaks

if error_msgs:
error_msg = error_msgs[0]
else:
error_msg = "Error: stack trace for this error is unavailable"
val_err_idx = error_msg.rfind("Error")
error = ErrorMessageTaxCalculator()
error_contents = error_msg[val_err_idx:].replace(" "," ")
Copy link
Contributor

Choose a reason for hiding this comment

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

This chunk of code seems pretty suspect... Ideally I guess there would be some easily-viewable logs, and you wouldn't need to store error messages in a database. Could to comment on the logic behind this? I.e. if the error messages aren't designed to be read by users easily, what's the purpose of storing them in the database / showing them to the user instead of logging them on the server side and presenting a more generic error message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This error message is supposed to be a stack trace that is returned by the policy model (e.g. Tax-Calculator). When you hit previous runs that did not complete, you see the saved error message:
screen shot 2018-02-03 at 10 25 40 am

The stack trace is logged on the server side, too.

So, saving the error message in the database is more for the user than for us to introspect. The logs are a better source of info for that.

@hdoupe
Copy link
Collaborator Author

hdoupe commented Feb 5, 2018

@codekansas do you have any more suggestions before #814 is merged?

@codekansas
Copy link
Contributor

LGTM

@hdoupe hdoupe merged commit 41642fe into ospc-org:master Feb 7, 2018
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.

None yet

2 participants