budget/reader.py
Luís Murta 1619ee8561
Initial commit
Working csv reader, which reorders the transactions on transaction.csv
based on date. Then it aggregates the information per month based on the
used categories, and prints that information.
2020-12-03 22:39:55 +00:00

256 lines
7.6 KiB
Python

from decimal import Decimal
import csv
import datetime
import sys
class Transaction:
def __init__(self, date, description, value, category):
self.id = id(self)
self.date = date
self.description = description
self.value = value
self.category = category
def __repr__(self):
return f"{self.date.date()} {self.description} {self.value}{self.category}"
class MonthlyTransactions:
def __init__(self, month, transactions):
self.month = datetime.datetime.strptime(str(month), "%m")
self.transactions = transactions
income_categories = [
"Income1",
"Income2",
"Income3",
]
fixed_expenses_categories = [
"Rent",
"Commmute",
"Utilities",
]
variable_expenses_categories = [
"Groceries",
"Eating Out",
"Entertainment",
"Pets",
"Travel",
"Miscellaneous",
]
self.income_per_cat = dict.fromkeys(income_categories, 0)
self.fixed_expenses_per_cat = dict.fromkeys(fixed_expenses_categories, 0)
self.variable_expenses_per_cat = dict.fromkeys(variable_expenses_categories, 0)
self.null = 0
self.investments = 0
self.separate_categories(self.transactions)
def separate_categories(self, transactions):
for transaction in transactions:
if transaction.category == "Null":
self.null += transaction.value
continue
if transaction.category == "Investment":
self.investments += transaction.value
continue
try:
self.income_per_cat[transaction.category] -= transaction.value
continue
except KeyError:
pass
try:
self.fixed_expenses_per_cat[transaction.category] += transaction.value
continue
except KeyError:
pass
try:
self.variable_expenses_per_cat[
transaction.category
] += transaction.value
continue
except KeyError as e:
if ", " in transaction.category:
categories = transaction.category.split(", ")
print(f"{transaction} has two categories. Allocate each.")
values = []
while transaction.value != sum(values):
for category in categories:
value = Decimal(input(f"Category {category}: "))
values.append(value)
new_transactions = []
for value, category in zip(values, categories):
new_transactions.append(
Transaction(
transaction.date,
transaction.description,
value,
category,
)
)
self.separate_categories(new_transactions)
else:
print(repr(e))
print(transaction)
sys.exit(2)
def income(self):
return sum(self.income_per_cat.values())
def fixed_expenses(self):
return sum(self.fixed_expenses_per_cat.values())
def variable_expenses(self):
return sum(self.variable_expenses_per_cat.values())
def expenses(self):
return self.fixed_expenses() + self.variable_expenses()
def __repr__(self):
info = []
for k, v in self.income_per_cat.items():
info.extend([k, v])
for k, v in self.fixed_expenses_per_cat.items():
info.extend([k, v])
for k, v in self.variable_expenses_per_cat.items():
info.extend([k, v])
p = """
\t\t\t\t{0} Report
Income\t\t\tFixed Expenses\t\tVariable Expenses
{1}\t{2}\t{11}\t\t{12}\t{25}\t\t{26}
{3}\t{4}\t{13}\t{14}\t{27}\t{28}
{5}\t{6}\t{15}\t{16}\t{29}\t{30}
{7}\t{8}\t{17}\t\t{18}\t{31}\t{32}
{9}\t{10}\t{19}\t\t{20}\t{33}\t{34}
\t\t\t{21}\t\t{22}\t{35}\t\t{36}
\t\t\t{23}\t{24}\t{37}\t\t{38}
\t\t\t\t\t\t{39}\t\t{40}
\t\t\t\t\t\t{41}\t\t{42}
\t\t\t\t\t\t{43}\t\t{44}
\t\t\t\t\t\t{45}\t\t{46}
\t\t\t\t\t\t{47}\t{48}
\t\t\t\t\t\t{49}\t\t{50}
\t\t\t\t\t\t{51}\t{52}
\t\t{53}\t\t\t{54}\t\t\t{55}
Expenses:\t{56}
Net:\t\t{57}
""".format(
self.month.strftime("%B"),
*info,
self.income(),
self.fixed_expenses(),
self.variable_expenses(),
self.expenses(),
self.income() - self.expenses(),
)
return p
def get_transactions(csvfile):
with open(csvfile, newline="") as fp:
reader = csv.reader(fp, delimiter="\t")
transactions = []
for transaction in reader:
try:
# date = datetime.datetime.strptime(transaction[0], "%Y-%m-%d")
date = datetime.datetime.strptime(transaction[0], "%d/%m/%Y")
description = transaction[1]
value = Decimal(transaction[2])
category = transaction[3]
transactions.append(Transaction(date, description, value, category))
except Exception as e:
print(repr(e))
print(transaction)
sys.exit(2)
return transactions
def reorder_transactions(transactions):
return sorted(transactions, key=lambda transaction: transaction.date)
def write_transactions(csvfile, transactions):
with open(csvfile, "w", newline="") as fp:
writer = csv.writer(fp, delimiter="\t")
for t in transactions:
writer.writerow([t.date.date(), t.description, t.value, t.category])
def get_month_transactions(transactions, month):
month_transactions = []
for transaction in transactions:
if transaction.date.month == month:
month_transactions.append(transaction)
return month_transactions
def get_value_per_category(transactions):
categories = dict()
for transaction in transactions:
try:
categories[transaction.category] += transaction.value
except KeyError:
categories[transaction.category] = transaction.value
return categories
def split_income_expenses(value_per_category):
income = dict()
expenses = dict()
for category, value in value_per_category.items():
if category.startswith("Income"):
income[category] = -value
elif category == "Investment":
pass
else:
expenses[category] = value
return income, expenses
if __name__ == "__main__":
transactions = get_transactions("transactions.csv")
transactions = reorder_transactions(transactions)
write_transactions("transactions_ordered.csv", transactions)
monthly_transactions = list()
monthly_categories = list()
incomes = list()
expenses = list()
for month in range(1, 7):
month_transactions = MonthlyTransactions(
month, get_month_transactions(transactions, month)
)
monthly_transactions.append(month_transactions)
print(month_transactions)
total_income = sum(month.income() for month in monthly_transactions)
total_expenses = sum(month.expenses() for month in monthly_transactions)
if total_income - total_expenses > 0:
print(f"\nWe're {total_income - total_expenses} richer!")
else:
print(f"We're {total_expenses - total_income} poorer :(")