Skip to content

Commit

Permalink
Merge pull request #655 from hdoupe/submit_quickcalc_bugfix
Browse files Browse the repository at this point in the history
Refactor submit_reform for quick-calc submission case
  • Loading branch information
brittainhard committed Sep 26, 2017
2 parents 6f37800 + b150792 commit 7911fa7
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 115 deletions.
12 changes: 0 additions & 12 deletions deploy/setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import os
import versioneer

versioneer.VCS = 'git'
versioneer.versionfile_source = os.path.join('taxbrain_server', '_version.py')
versioneer.versionfile_build = os.path.join('taxbrain_server', '_version.py')
versioneer.tag_prefix = '' # tags are like 1.2.0
versioneer.parentdir_prefix = 'taxbrain_server-' # dirname like 'taxcalc-1.2.0'

try:
from setuptools import setup
Expand All @@ -15,17 +8,12 @@
with open('README.md') as f:
longdesc = f.read()

version = versioneer.get_version()
cmdclass = versioneer.get_cmdclass()

config = {
'description': 'TaxBrain Server - Flask and Celery Workers for OSPC Models',
'url': 'https://github.com/OpenSourcePolicyCenter/webapp-public',
'download_url': 'https://github.com/OpenSourcePolicyCenter/webapp-public',
'description': 'taxbrain_server',
'long_description': longdesc,
'version': version,
'cmdclass': cmdclass,
'license': 'MIT',
'packages': ['taxbrain_server',
'taxbrain_server.scripts'],
Expand Down
274 changes: 171 additions & 103 deletions webapp/apps/taxbrain/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,16 +300,63 @@ def get_reform_from_gui(request, taxbrain_model=None, behavior_model=None,
return (reform_dict, assumptions_dict, "", "", errors_warnings)


def submit_reform(request, user=None):
def save_model(url, request, model, has_errors, start_year,
do_full_calc, is_file, reform_dict, assumptions_dict,
reform_text, assumptions_text, submitted_ids,
max_q_length, user):
"""
Submits TaxBrain reforms. This handles data from the GUI interface
and the file input interface. With some tweaks this model could be used
to submit reforms for all PolicyBrain models
Save user input data
returns OutputUrl object
"""
json_reform = JSONReformTaxCalculator()
json_reform.reform_text = json.dumps(reform_dict)
json_reform.assumption_text = json.dumps(assumptions_dict)
json_reform.raw_reform_text = reform_text
json_reform.raw_assumption_text = assumptions_text
json_reform.save()

returns OutputUrl object with parsed user input and warning/error messages
if necessary
boolean variable indicating whether this reform has errors or not
# create model for file_input case
if model is None:
model = TaxSaveInputs()
model.job_ids = denormalize(submitted_ids)
model.json_text = json_reform
model.first_year = int(start_year)
model.quick_calc = not do_full_calc
model.save()

# create OutputUrl object
if url is None:
unique_url = OutputUrl()
else:
unique_url = url
if user:
unique_url.user = user
elif request and request.user.is_authenticated():
current_user = User.objects.get(pk=request.user.id)
unique_url.user = current_user

if unique_url.taxcalc_vers is None:
unique_url.taxcalc_vers = TAXCALC_VERSION
if unique_url.webapp_vers is None:
unique_url.webapp_vers = WEBAPP_VERSION

unique_url.unique_inputs = model
unique_url.model_pk = model.pk
cur_dt = datetime.datetime.utcnow()
future_offset_seconds = ((2 + max_q_length) * JOB_PROC_TIME_IN_SECONDS)
future_offset = datetime.timedelta(seconds=future_offset_seconds)
expected_completion = cur_dt + future_offset
unique_url.exp_comp_datetime = expected_completion
unique_url.save()

return unique_url


def submit_reform(request, user=None):
"""
Parses user input data and submits reform
returns dictionary of arguments intended to be inputs for `save_model`
"""
start_year = START_YEAR
no_inputs = False
Expand All @@ -319,6 +366,16 @@ def submit_reform(request, user=None):
taxcalc_warnings = False
is_valid = True
has_parse_errors = False
errors_warnings = {'warnings': {}, 'errors': {}}
reform_dict = {}
assumptions_dict = {}
reform_text = ""
assumptions_text = ""
personal_inputs = None
model = None
is_file = False
submitted_ids = None
max_q_length = None
start_year = request.REQUEST['start_year']
fields = dict(request.REQUEST)
#TODO: use either first_year or start_year; validation error is thrown
Expand All @@ -330,21 +387,17 @@ def submit_reform(request, user=None):
del fields['full_calc']
elif 'quick_calc' in fields:
del fields['quick_calc']
errors_warnings = {'warnings': {}, 'errors': {}}
reform_dict = {}
personal_inputs = None
is_file = False
if 'docfile' in request.FILES:
is_file = True
model = None
(reform_dict, assumptions_dict, reform_text, assumptions_text,
errors_warnings) = get_reform_from_file(request)
else:
personal_inputs = PersonalExemptionForm(start_year, fields)
# If an attempt is made to post data we don't accept
# raise a 400
if personal_inputs.non_field_errors():
return HttpResponse("Bad Input!", status=400), True
return {'HttpResponse': HttpResponse("Bad Input!", status=400),
'has_errors': True}
is_valid = personal_inputs.is_valid()
if is_valid:
model = personal_inputs.save()
Expand All @@ -366,8 +419,8 @@ def submit_reform(request, user=None):

warn_msgs = errors_warnings['warnings'] != {}
stop_errors = no_inputs or not is_valid or errors_warnings['errors'] != {}

if stop_errors or (not has_errors and warn_msgs):
stop_submission = stop_errors or (not has_errors and warn_msgs)
if stop_submission:
taxcalc_errors = True if errors_warnings['errors'] else False
taxcalc_warnings = True if errors_warnings['warnings'] else False
if personal_inputs is not None:
Expand Down Expand Up @@ -399,92 +452,66 @@ def submit_reform(request, user=None):
msg = ("Some fields have unrecognized values. Enter comma "
"separated values for each input.")
personal_inputs.add_error(None, msg)

return (personal_inputs, any([taxcalc_errors, taxcalc_warnings,
no_inputs, not is_valid]))
# case where user has been warned and has fixed errors if necassary but may
# or may not have fixed warnings
if has_errors:
assert not taxcalc_errors
# try: # TODO: is try-catch necessary here?
log_ip(request)
# TODO: drop is_file and pack_up_user_mods keywords
if do_full_calc:
submitted_ids, max_q_length = dropq_compute.submit_dropq_calculation(
reform_dict,
int(start_year),
is_file=is_file,
additional_data=assumptions_dict,
pack_up_user_mods=False
)
else:
submitted_ids, max_q_length = dropq_compute.submit_dropq_small_calculation(
reform_dict,
int(start_year),
is_file=is_file,
additional_data=assumptions_dict,
pack_up_user_mods=False
)
job_ids = denormalize(submitted_ids)
json_reform = JSONReformTaxCalculator()
# save file_input user params
if is_file:
json_reform.reform_text = json.dumps(reform_dict)
json_reform.assumption_text = json.dumps(assumptions_dict)
json_reform.raw_reform_text = reform_text
json_reform.raw_assumption_text = assumptions_text
# save GUI user params
else:
job_ids = denormalize(submitted_ids)
model.job_ids = job_ids
model.first_year = int(start_year)
model.quick_calc = not do_full_calc
model.save()
unique_url = OutputUrl()

json_reform.reform_text = json.dumps(reform_dict)
json_reform.assumption_text = json.dumps(assumptions_dict)
json_reform.raw_reform_text = ""
json_reform.raw_assumption_text = ""

json_reform.save()

# create model for file_input case
if model is None:
model = TaxSaveInputs()
model.job_ids = job_ids
model.json_text = json_reform
model.first_year = int(start_year)
model.quick_calc = not do_full_calc
model.save()
log_ip(request)
if do_full_calc:
submitted_ids, max_q_length = dropq_compute.submit_dropq_calculation(
reform_dict,
int(start_year),
is_file=is_file,
additional_data=assumptions_dict,
pack_up_user_mods=False
)
else:
submitted_ids, max_q_length = dropq_compute.submit_dropq_small_calculation(
reform_dict,
int(start_year),
is_file=is_file,
additional_data=assumptions_dict,
pack_up_user_mods=False
)

return {'personal_inputs': personal_inputs,
'model': model,
'stop_submission': stop_submission,
'has_errors': any([taxcalc_errors, taxcalc_warnings,
no_inputs, not is_valid]),
'start_year': start_year,
'do_full_calc': do_full_calc,
'is_file': is_file,
'reform_dict': reform_dict,
'assumptions_dict': assumptions_dict,
'reform_text': reform_text,
'assumptions_text': assumptions_text,
'submitted_ids': submitted_ids,
'max_q_length': max_q_length}


def process_reform(request, user=None):
"""
Submits TaxBrain reforms. This handles data from the GUI interface
and the file input interface. With some tweaks this model could be used
to submit reforms for all PolicyBrain models
# create OutputUrl object
unique_url = OutputUrl()
if user:
unique_url.user = user
elif request and request.user.is_authenticated():
current_user = User.objects.get(pk=request.user.id)
unique_url.user = current_user
returns OutputUrl object with parsed user input and warning/error messages
if necessary
boolean variable indicating whether this reform has errors or not
if unique_url.taxcalc_vers is not None:
pass
else:
unique_url.taxcalc_vers = TAXCALC_VERSION
if unique_url.webapp_vers != None:
pass
"""
args = submit_reform(request, user=user)
if 'HttpResponse' in args:
return args['HttpResponse'], args['has_errors']

args['request'] = request
args['user'] = user
args['url'] = None
if args['stop_submission']:
return args['personal_inputs'], args['has_errors']
else:
unique_url.webapp_vers = WEBAPP_VERSION

unique_url.unique_inputs = model
unique_url.model_pk = model.pk
cur_dt = datetime.datetime.utcnow()
future_offset_seconds = ((2 + max_q_length) * JOB_PROC_TIME_IN_SECONDS)
future_offset = datetime.timedelta(seconds=future_offset_seconds)
expected_completion = cur_dt + future_offset
unique_url.exp_comp_datetime = expected_completion
unique_url.save()
return (unique_url, any([taxcalc_errors, taxcalc_warnings, no_inputs,
not is_valid]))
del args['stop_submission']
del args['personal_inputs']
url = save_model(**args)
return url, args['has_errors']


def file_input(request):
Expand All @@ -500,7 +527,7 @@ def file_input(request):
if 'docfile' not in dict(request.FILES):
errors = ["Please specify a tax-law change before submitting."]
else:
unique_url, has_errors = submit_reform(request)
unique_url, has_errors = process_reform(request)
# TODO: handle taxcalc_errors and taxcalc_warnings
return redirect(unique_url)
# GET request, load a default form
Expand Down Expand Up @@ -545,7 +572,7 @@ def personal_results(request):
elif 'quick_calc' in fields:
del fields['quick_calc']

obj, has_errors = submit_reform(request)
obj, has_errors = process_reform(request)

# case where validation failed in forms.PersonalExemptionForm
# TODO: assert HttpResponse status is 404
Expand Down Expand Up @@ -588,13 +615,14 @@ def submit_micro(request, pk):
Its primary purpose is to facilitate a mechanism to submit a full microsim
job after one has submitted parameters for a 'quick calculation'
"""
#TODO: get this function to work with submit_reform
#TODO: get this function to work with process_reform
try:
url = OutputUrl.objects.get(pk=pk)
except:
raise Http404

model = TaxSaveInputs.objects.get(pk=url.model_pk)
start_year = dict(model.__dict__)['first_year']
# This will be a new model instance so unset the primary key
model.pk = None
# Unset the computed results, set quick_calc to False
Expand All @@ -605,9 +633,49 @@ def submit_micro(request, pk):
model.tax_result = None

log_ip(request)
unique_url = process_model(model, start_year=str(model.first_year),
do_full_calc=True, user=url.user)
return redirect(unique_url)

#get microsim data
is_file = model.json_text is not None
# necessary for simulations before PR 641
if not is_file:
(reform_dict, _, _,
errors_warnings) = get_reform_from_gui(
request,
taxbrain_model=model,
behavior_model=None
)
else:
reform_dict = json.loads(model.json_text.reform_text)
assumptions_dict = json.loads(model.json_text.assumption_text)
# start calc job
submitted_ids, max_q_length = dropq_compute.submit_dropq_calculation(
reform_dict,
int(start_year),
is_file=is_file,
additional_data=assumptions_dict,
pack_up_user_mods=False
)

args = {'url': url,
'request': request,
'model': model,
'has_errors': False,
'start_year': start_year,
'do_full_calc': True,
'is_file': is_file,
'reform_dict': reform_dict,
'assumptions_dict': assumptions_dict,
'reform_text': (model.json_text.raw_reform_text
if model.json_text else ""),
'assumptions_text': (model.json_text.raw_assumption_text
if model.json_text else ""),
'submitted_ids': submitted_ids,
'max_q_length': max_q_length,
'user': None}

url = save_model(**args)

return redirect(url)


def edit_personal_results(request, pk):
Expand Down

0 comments on commit 7911fa7

Please sign in to comment.