2025-11-05 14:43:11 -05:00
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 )
2025-11-11 09:47:47 -05:00
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 ) }
2025-11-05 14:43:11 -05:00
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
2025-11-11 09:47:47 -05:00
main_list [ ' Income ' ] [ ' Left Against Budget ' ] = amount_left
2025-11-05 14:43:11 -05:00
main_list [ ' Income ' ] [ ' Total Spent ' ] = curr_sum
2025-11-11 09:47:47 -05:00
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
2025-11-05 14:43:11 -05:00
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. \n First, here are the categories where you ' ve overspent: \n { final_tuple [ 1 ] } . \n \n Here is where you still have some budget left: \n { final_tuple [ 0 ] } . \n \n And 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 )