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

Add --sqldb option to tc CLI #1312

Merged
merged 4 commits into from
Apr 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions taxcalc/cli/tc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def main():
usage_str = 'tc INPUT TAXYEAR {}{}{}'.format(
'[--reform REFORM] [--assump ASSUMP]\n',
' ',
'[--exact] [--tables] [--graphs] [--ceeu] [--dump] [--test]')
'[--exact] [--tables] [--graphs] [--ceeu] [--dump] [-sqldb] [--test]')
parser = argparse.ArgumentParser(
prog='',
usage=usage_str,
Expand Down Expand Up @@ -89,6 +89,11 @@ def main():
'output.'),
default=False,
action="store_true")
parser.add_argument('--sqldb',
help=('optional flag that writes SQLite database with '
'dump table containing same output as --dump.'),
default=False,
action="store_true")
parser.add_argument('--test',
help=('optional flag that conducts installation '
'test.'),
Expand Down Expand Up @@ -124,12 +129,15 @@ def main():
output_tables=args.tables,
output_graphs=args.graphs,
output_ceeu=args.ceeu,
output_dump=args.dump)
output_dump=args.dump,
output_sqldb=args.sqldb)
# compare test output with expected test output if --test option specified
if args.test:
compare_test_output_files()
# return no-error exit code
return 0
retcode = compare_test_output_files()
else:
retcode = 0
# return exit code
return retcode
# end of main function code


Expand Down Expand Up @@ -159,18 +167,22 @@ def write_test_input_output_files():

def compare_test_output_files():
"""
Compare expected and actual test output files.
Compare expected and actual test output files;
returns 0 if pass test, otherwise returns 1.
"""
explines = open(EXPECTED_TEST_OUTPUT_FILENAME, 'U').readlines()
actlines = open(ACTUAL_TEST_OUTPUT_FILENAME, 'U').readlines()
if ''.join(explines) == ''.join(actlines):
sys.stdout.write('PASSED TEST\n')
retcode = 0
else:
retcode = 1
sys.stdout.write('FAILED TEST\n')
diff = difflib.unified_diff(explines, actlines,
fromfile=EXPECTED_TEST_OUTPUT_FILENAME,
tofile=ACTUAL_TEST_OUTPUT_FILENAME, n=0)
sys.stdout.writelines(diff)
return retcode


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion taxcalc/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ def AMT(e07300, dwks13, standard, f6251, c00100, c18300, taxbc,
AMT function computes Alternative Minimum Tax taxable income and liability:
c62100 is AMT taxable income
c09600 is AMT tax liability
c05800 is total (reg + AMT) income tax liability before credits
c05800 is total (regular + AMT) income tax liability before credits

Note that line-number variable names refer to (2015) Form 6251.
"""
Expand Down
2 changes: 1 addition & 1 deletion taxcalc/records_variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@
},
"c05800": {
"type": "float",
"desc": "",
"desc": "Total (regular + AMT) income tax liability before credits",
"form": {}
},
"c07100": {
Expand Down
36 changes: 30 additions & 6 deletions taxcalc/taxcalcio.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import os
import copy
import sqlite3
import six
import numpy as np
import pandas as pd
Expand Down Expand Up @@ -264,7 +265,8 @@ def analyze(self, writing_output_file=False,
output_tables=False,
output_graphs=False,
output_ceeu=False,
output_dump=False):
output_dump=False,
output_sqldb=False):
"""
Conduct tax analysis.

Expand All @@ -289,13 +291,17 @@ def analyze(self, writing_output_file=False,
whether or not to replace standard output with all input and
calculated variables using their Tax-Calculator names

output_sqldb: boolean
whether or not to write SQLite3 database with dump table
containing same output as written by output_dump to a csv file

Returns
-------
Nothing
"""
# pylint: disable=too-many-arguments,too-many-branches
calc_clp_calculated = False
if output_dump:
if output_dump or output_sqldb:
(mtr_paytax, mtr_inctax,
_) = self.calc.mtr(wrt_full_compensation=False)
else: # do not need marginal tax rates
Expand Down Expand Up @@ -325,6 +331,9 @@ def analyze(self, writing_output_file=False,
# extract output if writing_output_file
if writing_output_file:
self.write_output_file(output_dump, mtr_paytax, mtr_inctax)
# optionally write --sqldb output to SQLite3 database
if output_sqldb:
self.write_sqldb_file(mtr_paytax, mtr_inctax)
# optionally write --tables output to text file
if output_tables:
if not calc_clp_calculated:
Expand Down Expand Up @@ -352,6 +361,17 @@ def write_output_file(self, output_dump, mtr_paytax, mtr_inctax):
assert len(outdf.index) == self.calc.records.dim
outdf.to_csv(self._output_filename, index=False, float_format='%.2f')

def write_sqldb_file(self, mtr_paytax, mtr_inctax):
"""
Write dump output to SQLite3 database table dump.
"""
outdf = self.dump_output(mtr_inctax, mtr_paytax)
assert len(outdf.index) == self.calc.records.dim
dbfilename = '{}.db'.format(self._output_filename[:-4])
dbcon = sqlite3.connect(dbfilename)
outdf.to_sql('dump', dbcon, if_exists='replace', index=False)
dbcon.close()

def write_tables_file(self):
"""
Write tables to text file.
Expand Down Expand Up @@ -531,16 +551,20 @@ def ceeu_output(cedict):

def dump_output(self, mtr_inctax, mtr_paytax):
"""
Extract --dump output and return as pandas DataFrame.
Extract dump output and return it as pandas DataFrame.
"""
odf = pd.DataFrame()
varset = Records.USABLE_READ_VARS | Records.CALCULATED_VARS
for varname in varset:
vardata = getattr(self.calc.records, varname)
odf[varname] = vardata
if varname in Records.INTEGER_VARS:
odf[varname] = vardata
else:
odf[varname] = vardata.round(2) # rounded to nearest cent
odf['FLPDYR'] = self.tax_year() # tax calculation year
odf['mtr_inctax'] = mtr_inctax
odf['mtr_paytax'] = mtr_paytax
# mtr values expressed in rounded percentage terms
odf['mtr_inctax'] = (mtr_inctax * 100.0).round(2)
odf['mtr_paytax'] = (mtr_paytax * 100.0).round(2)
return odf

@staticmethod
Expand Down
4 changes: 2 additions & 2 deletions taxcalc/tests/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import six
import numpy as np
import pytest
from taxcalc import ParametersBase # pylint: disable=import-error
from taxcalc import Policy, Consumption # pylint: disable=import-error
# pylint: disable=import-error
from taxcalc import ParametersBase, Policy, Consumption


def test_instantiation_and_usage():
Expand Down
46 changes: 39 additions & 7 deletions taxcalc/tests/test_taxcalcio.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ def test_output_otions(rawinputfile, reformfile1, assumpfile1):
os.remove(outfilepath)
except OSError:
pass # sometimes we can't remove a generated temporary file
assert 'TaxCalcIO.calculate(ceeu)_ok' == 'no'
assert 'TaxCalcIO.analyze(ceeu)_ok' == 'no'
# --dump output
try:
tcio.analyze(writing_output_file=True, output_dump=True)
Expand All @@ -276,13 +276,45 @@ def test_output_otions(rawinputfile, reformfile1, assumpfile1):
os.remove(outfilepath)
except OSError:
pass # sometimes we can't remove a generated temporary file
assert 'TaxCalcIO.calculate(dump)_ok' == 'no'
# if tries were successful, try to remove the output file
assert 'TaxCalcIO.analyze(dump)_ok' == 'no'
# if tries were successful, remove the output file
if os.path.isfile(outfilepath):
try:
os.remove(outfilepath)
except OSError:
pass # sometimes we can't remove a generated temporary file
os.remove(outfilepath)


def test_sqldb_option(rawinputfile, reformfile1, assumpfile1):
"""
Test TaxCalcIO output_sqldb option when not writing_output_file.
"""
taxyear = 2021
tcio = TaxCalcIO(input_data=rawinputfile.name,
tax_year=taxyear,
reform=reformfile1.name,
assump=assumpfile1.name)
assert len(tcio.errmsg) == 0
tcio.init(input_data=rawinputfile.name,
tax_year=taxyear,
reform=reformfile1.name,
assump=assumpfile1.name,
growdiff_response=None,
aging_input_data=False,
exact_calculations=False)
assert len(tcio.errmsg) == 0
outfilepath = tcio.output_filepath()
dbfilepath = outfilepath.replace('.csv', '.db')
# --sqldb output
try:
tcio.analyze(writing_output_file=False, output_sqldb=True)
except: # pylint: disable=bare-except
if os.path.isfile(dbfilepath):
try:
os.remove(dbfilepath)
except OSError:
pass # sometimes we can't remove a generated temporary file
assert 'TaxCalcIO.analyze(sqldb)_ok' == 'no'
# if try was successful, remove the db file
if os.path.isfile(dbfilepath):
os.remove(dbfilepath)


def test_no_tables_or_graphs(reformfile1):
Expand Down