157 lines
6.6 KiB
Python
157 lines
6.6 KiB
Python
from actual import Actual
|
|
import os
|
|
from dotenv import load_dotenv
|
|
from actual.queries import get_budgets, get_categories, get_category, get_transactions
|
|
from datetime import datetime
|
|
from moneyed import Money, USD
|
|
from moneyed.l10n import format_money
|
|
import json
|
|
import re
|
|
import pprint
|
|
|
|
load_dotenv()
|
|
pp=pprint.PrettyPrinter(indent=4)
|
|
TODAY = datetime.now()
|
|
MONTH_DAY = TODAY.strftime("%Y%m")
|
|
MONTHSTR = TODAY.strftime("%B")
|
|
MONTHDAYDATE = datetime.strptime(MONTH_DAY, "%Y%m")
|
|
ONBUDGETACCOUNTS = os.getenv('ONBUDGETACCOUNTS')
|
|
|
|
def simple_track(actual):
|
|
main_dict = {}
|
|
main_list = []
|
|
budget = get_budgets(actual.session)
|
|
for b in budget:
|
|
if b.month == int(MONTH_DAY) and b.amount > 0:
|
|
cats = get_categories(actual.session)
|
|
for cat in cats:
|
|
if cat.id == b.category_id:
|
|
if cat.name == "Income":
|
|
formatted_amount = str(b.amount)[:-2] + "." + str(b.amount)[-2:]
|
|
expected_income = Money(formatted_amount, USD)
|
|
main_dict[cat.name] = {"Expected Income": expected_income, "Actual Income": Money(0, USD), "Total Spent": Money(0, USD), "Left Against Budget": Money(0, USD), "Left Against Actual": Money(0, USD)}
|
|
category_transcations(sesh=actual.session, main_list=main_dict, origin="simple")
|
|
|
|
def main(actual):
|
|
main_dict = {}
|
|
main_list = []
|
|
budget = get_budgets(actual.session)
|
|
for b in budget:
|
|
if b.month == int(MONTH_DAY) and b.amount > 0:
|
|
cats = get_categories(actual.session)
|
|
for cat in cats:
|
|
if cat.id == b.category_id:
|
|
# if cat.name == "Kid's Activities":
|
|
formatted_amount = str(b.amount)[:-2] + "." + str(b.amount)[-2:]
|
|
budgeted_amount = Money(formatted_amount, USD)
|
|
main_dict[cat.name] = {"budgeted_amount": budgeted_amount, "amount_spent": "", "amount_left": ""}
|
|
# main_dict = {"category": cat.name, "month": MONTHSTR, "budgeted_amount": budgeted_amount, "amount_spent": "", "amount_left": ""}
|
|
print(main_dict)
|
|
category_transcations(sesh=actual.session, main_list=main_dict, origin="all_cats")
|
|
|
|
|
|
def category_transcations(sesh, main_list, origin):
|
|
if origin == "simple":
|
|
this_monhth_income = get_transactions(sesh, category='Income', start_date=MONTHDAYDATE)
|
|
income_sum = Money(0, USD)
|
|
for ti in this_monhth_income:
|
|
income = str(ti.amount)[:-2] + "." + str(ti.amount)[-2:]
|
|
total_income = Money(income, USD)
|
|
income_sum += total_income
|
|
|
|
this_month_trans = get_transactions(sesh, account='onbudget', start_date=MONTHDAYDATE)
|
|
curr_sum = Money(0, USD)
|
|
count = 0
|
|
for ta in this_month_trans:
|
|
if ta.category_id != '3c1699a5-522a-435e-86dc-93d900a14f0e' and ta.account.id in ONBUDGETACCOUNTS:
|
|
tamount = str(ta.amount)[:-2] + "." + str(ta.amount)[-2:]
|
|
total_sum = Money(tamount, USD)
|
|
curr_sum += total_sum
|
|
count += 1
|
|
|
|
amount_left = curr_sum + main_list['Income']['Expected Income']
|
|
main_list['Income']['Expected Income'] = main_list['Income']['Expected Income']
|
|
main_list['Income']['Actual Income'] = income_sum
|
|
main_list['Income']['Left Against Budget'] = amount_left
|
|
main_list['Income']['Total Spent'] = curr_sum
|
|
xx = curr_sum.amount
|
|
if xx.is_zero():
|
|
main_list['Income']['Left Against Actual'] = income_sum+curr_sum
|
|
else:
|
|
main_list['Income']['Left Against Actual'] = income_sum+curr_sum
|
|
|
|
else:
|
|
for keys, vals in main_list.items():
|
|
this_month_trans = get_transactions(sesh, category=keys, start_date=MONTHDAYDATE)
|
|
curr_sum = Money(0, USD)
|
|
for ta in this_month_trans:
|
|
tamount = str(ta.amount)[:-2] + "." + str(ta.amount)[-2:]
|
|
total_sum = Money(tamount, USD)
|
|
curr_sum += total_sum
|
|
amount_list = curr_sum + vals["budgeted_amount"]
|
|
vals["budgeted_amount"] = vals['budgeted_amount']
|
|
vals["amount_spent"] = curr_sum
|
|
vals["amount_left"] = amount_list
|
|
|
|
if origin == "simple":
|
|
x = format_dicts(main_list)
|
|
simple_notifications(x)
|
|
else:
|
|
all_budgets_for_notifications(main_list)
|
|
|
|
def simple_notifications(x):
|
|
print(f"""Here's where you stand this month!\n {x}""")
|
|
|
|
def all_budgets_for_notifications(sorted_data):
|
|
negative_budget = {}
|
|
no_budget_left = {}
|
|
some_budget_left = {}
|
|
final_tuple = ()
|
|
for categories, values in sorted_data.items():
|
|
# for budget_type, amount in values.items():
|
|
intmoney = values['amount_left'].amount
|
|
if intmoney == 0:
|
|
# no_budget_left.append((categories, values) )
|
|
no_budget_left[categories] = values
|
|
elif intmoney < 0:
|
|
# negative_budget.append((categories, values))
|
|
negative_budget[categories] = values
|
|
else:
|
|
# some_budget_left.append(( categories, values) )
|
|
some_budget_left[categories] = values
|
|
|
|
final_tuple = (format_dicts(some_budget_left), format_dicts(negative_budget), format_dicts(no_budget_left))
|
|
print(f"""**This is an automated message!** Here's your spending status so far for this month.\nFirst, here are the categories where you've overspent:\n{final_tuple[1]}.\n\nHere is where you still have some budget left:\n{final_tuple[0]}.\n\nAnd finally, here is where you're exactly where you need to be. $0 left.\n {final_tuple[2]}""")
|
|
|
|
|
|
def format_dicts(budgets_sorted):
|
|
x = str(budgets_sorted)[10:]
|
|
# y = re.search(r"(\'\w*\':)|(\'\w{1,9}\s\w{1,9})|(\d*.\d{2})", x).group()
|
|
y = (x
|
|
.replace("{'", "")
|
|
.replace("}", "")
|
|
.replace("Money('", "")
|
|
.replace("'", "")
|
|
.replace(", USD", "")
|
|
.replace(")","")
|
|
.replace("(","")
|
|
.replace('"', '')
|
|
)
|
|
z = re.sub(r"((([A-Z][a-z]*( | & )){1,2}[A-Z][a-z]*:)|([A-Z][a-z]*)|529 Contrib)", '\n\\1', y)
|
|
t = re.sub(r", \n", '\n', z)
|
|
h = re.sub(r'([a-z]*_[a-z]*)',lambda x: x[1].replace('_', ' ').capitalize(), t)
|
|
p = re.sub(r'(\d{1,5}.\d{2})', '$\\1', h)
|
|
o = re.sub(r'(\d{4,5})', lambda x: x[1][:-3]+','+x[1][-3:], p)
|
|
return o
|
|
|
|
|
|
if __name__ == "__main__":
|
|
with Actual(
|
|
base_url=os.getenv('BASEURL'),
|
|
password=os.getenv('PASSWORD'),
|
|
encryption_password=None, # Optional: Password for the file encryption. Will not use it if set to None.
|
|
file=os.getenv('FILE')
|
|
) as actual:
|
|
# main(actual)
|
|
simple_track(actual)
|