Skip to content

Commit

Permalink
MVP done! added support for transaction parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
bradysalz committed Aug 4, 2018
1 parent 3f229f7 commit 66cab1e
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 53 deletions.
26 changes: 9 additions & 17 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
MIT License
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004

Copyright (c) 2018 Brady Salz
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
0. You just DO WHAT THE FUCK YOU WANT TO.
13 changes: 0 additions & 13 deletions LICENSE.md

This file was deleted.

157 changes: 139 additions & 18 deletions sankey-gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
See the README for more info.
"""
import csv
from typing import Dict, List
from datetime import datetime
from typing import Dict, List, TextIO

import toml

Expand Down Expand Up @@ -35,51 +36,171 @@ def parse_csv(fname: str) -> List[Transaction]:
return transactions


def calc_income_values(fname: str, earnings: float, pretax_vals: Dict) -> int:
def add_paystub(f: TextIO, earnings: float, pretax_vals: Dict, *,
scale: float) -> int:
"""Create SankeyMatic strings from configuration income+pretax info
Args:
fname: output file name
earnings: total earnings over the plotting period
f: output file
earnings: net income
pretax_vals: dictionary with all pretax items and their value
scale: scaling factor to apply to all values (based on time period)
The format is:
{Source} [{Amount}] {Type}
Returns:
total take home income over the plotting period
"""
with open(fname, 'w') as f:
take_home = earnings
for name, value in pretax_vals.items():
f.write(f'Wages [{value}] {name} \n')
take_home -= value

f.write(f'Wages [{take_home}] Take Home')
take_home = earnings * scale
sorted_pretax = sorted(pretax_vals.items(), key=lambda kv: kv[1])
sorted_pretax.reverse()
for name, value in sorted_pretax:
f.write(f'Wages [{value * scale}] {name}\n')
take_home -= value * scale

f.write(f'Wages [{take_home}] Take Home\n')
return take_home


def filter_transactions(transactions: List[Transaction], start_date: datetime,
end_date: datetime, vendors: List[str],
categories: List[str],
use_labels: bool) -> List[Transaction]:
"""Filter transactions based on date, vendor, and type
Args:
transactions: list of all transactions
start_date: ignore all transactions before this date
end_date: ignore all transactions after this date
vendors: ignore all transactions from these vendors
categories: ignore all transactions within these categories
use_labels: check labels in addition to categories
Returns:
Filtered list of transactions
"""

filt_trans = []
for t in transactions:
if t.date <= start_date or t.date >= end_date:
continue

if t.vendor in vendors:
continue

if use_labels and t.label in categories:
continue

if t.category in categories:
continue

filt_trans.append(t)
return filt_trans


def summarize_transactions(transactions: List[Transaction], use_labels: bool,
threshold: int) -> Dict[str, int]:
"""Bundle transactions up by category and calculate total amount per
Args:
transactions: list of all transactions
use_labels: if True, uses labels instead of categories if they exist
threshold: minimum amount for a category
if below the threshold, the categorys thrown into "Misc"
Returns:
dict of category name, category value pairs
"""
category_sums = {}
for t in transactions:
if use_labels and t.label != '':
category = t.label
else:
category = t.category

if category in category_sums:
category_sums[category] += t.amount
else:
category_sums[category] = t.amount

misc_amt = 0
for name in category_sums.copy():
if category_sums[name] < threshold:
misc_amt += category_sums.pop(name)

if misc_amt:
category_sums['Misc'] = misc_amt

return category_sums


def add_transactions(f: TextIO, transactions: List[Transaction],
take_home: int, config: Dict):
"""Generate SankeyMatic strings from filtered transactions
Args:
f: output file
transactions: list of all transactions
take_home: total take home pay for the period
config: config file
"""

start_date = datetime.strptime(config['time']['start_date'], '%m/%d/%Y')
end_date = datetime.strptime(config['time']['end_date'], '%m/%d/%Y')

filt_trans = filter_transactions(
transactions=transactions,
start_date=start_date,
end_date=end_date,
vendors=config['transactions']['ignore_vendors'],
categories=config['transactions']['ignore_categories'],
use_labels=config['transactions']['prefer_labels'])

summed_categories = summarize_transactions(
transactions=filt_trans,
use_labels=config['transactions']['prefer_labels'],
threshold=config['transactions']['category_threshold'])

expenditure = 0
sorted_cat = sorted(summed_categories.items(), key=lambda kv: kv[1])
sorted_cat.reverse()
for name, value in sorted_cat:
f.write(f'Take Home [{value}] {name}\n')
expenditure += value

savings = take_home - expenditure
f.write(f'Take Home [{savings}] Savings\n')


if __name__ == "__main__":
try:
config_file = open('config.toml', 'r')
except IOError:
config_file = open('config-sample.toml', 'r')

config = toml.load(config_file)
print(config)

if config['paths']['use_custom_input']:
transactions = parse_csv(config['paths']['input_file'])
else:
transactions = parse_csv('data/transactions.csv')

# generate input strings

if config['paths']['use_custom_output']:
fname = config['paths']['output_path']
else:
fname = 'output.txt'

take_home = calc_income_values(fname, config['paycheck']['net_earnings'],
config['paycheck']['pretax'])
# generate pretax strings
# generate transaction string
output_file = open(fname, 'w')

take_home = add_paystub(
output_file,
config['paycheck']['net_earnings'],
config['paycheck']['pretax'],
scale=2)

start_date = datetime.strptime('6/1/2018', '%m/%d/%Y')
end_date = datetime.strptime('7/1/2018', '%m/%d/%Y')
add_transactions(output_file, transactions, take_home, config)

output_file.close()
8 changes: 3 additions & 5 deletions transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,16 @@ def load_from_csv(self, data: List):
"""
self.date = datetime.strptime(data[0], "%m/%d/%Y")
self.vendor = data[1]
self.amount = data[3]
self.debit = data[4]
self.category = data[6]
self.amount = int(float(data[3]))
self.debit = True if data[4] == 'debit' else False
self.category = data[5]
self.label = data[7]

def make_sakey_string(self) -> str:
"""Create a string with the relevant SakeyMatic formatting
The format is:
{Source} [{Amount}] {Type}
More info can be found here: http://sankeymatic.com/build/
"""
if self.source:
return f"{self.source.category} [{self.amount}] {self.category}"
Expand Down

0 comments on commit 66cab1e

Please sign in to comment.