now allows downloading, defaults include health care and better leave calc. This is probably good enough now, so I have also saved a snapshot/csv of the data
This commit is contained in:
6
README
6
README
@@ -1,7 +1,5 @@
|
||||
NEED to confirm car future
|
||||
|
||||
ALLOW SAVE / export, so I can compare this when we get a chance :)
|
||||
|
||||
TODO:
|
||||
* need to consider whether we pay GMHBA / HCF at what cadence and include it in calcs better
|
||||
|
||||
to run:
|
||||
|
||||
|
||||
7
calc.py
7
calc.py
@@ -24,10 +24,11 @@ def calculate_savings_depletion(finance):
|
||||
TLS_price = finance['TLS_price']
|
||||
CBA_price = finance['CBA_price']
|
||||
|
||||
# leave in days, 10 business days to a fortnight, salary is per fortnight, and we lose 37% to tax
|
||||
# (assuming we don't earn over $180k)
|
||||
# leave in days, 10 business days to a fortnight,
|
||||
# paid before tax I earn $7830.42 / fortnight. Tax on that will be at 37% or $4933.16 after tax
|
||||
# if we could stretch this to July 2026, then would be more (due to less tax)
|
||||
D_leave_after_tax = (D_leave_owed_in_days/10) * D_Salary;
|
||||
after_tax_extra_leave_per_fortnight = 4933.16
|
||||
D_leave_after_tax = (D_leave_owed_in_days/10) * after_tax_extra_leave_per_fortnight;
|
||||
|
||||
# Constants for interest calculations
|
||||
annual_interest_rate = Interest_Rate / 100.0
|
||||
|
||||
12
db.py
12
db.py
@@ -41,7 +41,7 @@ def init_db():
|
||||
cur.execute('''INSERT INTO finance (D_Salary, D_Num_fortnights_pay, School_Fees, Car_loan_via_pay, Car_loan, Car_balloon, Living_Expenses, Savings, Interest_Rate,
|
||||
Inflation, Mich_present, Overseas_trip, Mark_reno, D_leave_owed_in_days, D_TLS_shares, M_TLS_shares, D_CBA_shares, TLS_price, CBA_price, Overseas_trip_date, Mark_reno_date, Sell_shares)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
|
||||
(4762.29, 6, 22000, 620, 1001.12, 45824.68, 78000, 412000, 5.0, 3.9, 10000, 32000, 10000, 90.6, 1000, 750, 1095, 3.99, 160.61, '2025-06-01', '2025-09-01', 5))
|
||||
(4762.29, 6, 22000, 620, 1001.12, 45824.68, 83000, 412000, 5.0, 3.9, 10000, 32000, 10000, 90.6, 1000, 750, 1095, 3.99, 160.61, '2025-06-01', '2025-09-01', 6))
|
||||
# NOTE: 1001.12 car-pay -- is 1017.99 (actual rate) - 16.87 (car park)
|
||||
# NOTE: o/s trip. ~ $4kpp flights x3, then ~$3k / week in barcelona accom, $100pp/pd for food ($2k), + spending money
|
||||
conn.commit()
|
||||
@@ -55,6 +55,16 @@ def get_finance_data():
|
||||
conn.close()
|
||||
return dict(finance)
|
||||
|
||||
def get_budget_data(finance_data, CBA, TLS):
|
||||
# annual bills - health ins (5k), rates (2.4), electricity (1.5), gas (2), internet (1.6), car insurance (.7), rego (.8), house insurance (2.4), GFC (2.2), phones (.5), melb. pollen (.03), nabu casa (.1), eweka (.1) --- noting phone is elevated presuming I also go onto Aldi plan, but that there is no family discount
|
||||
bills = 19330
|
||||
BUDGET=[]
|
||||
BUDGET.append( ('Bills', f"${bills:,.2f}") )
|
||||
BUDGET.append( ('Buffer', f"${CBA*finance_data['CBA_price']+TLS*finance_data['TLS_price']:,.2f}") )
|
||||
BUDGET.append( ('Monthly budget', f"${((finance_data['Living_Expenses']-12000) / 12):,.2f}" ) )
|
||||
BUDGET.append( ('Weekly budget', f"${((finance_data['Living_Expenses']-12000) / 52):,.2f}" ) )
|
||||
return BUDGET
|
||||
|
||||
def update_finance(data):
|
||||
|
||||
conn = connect_db()
|
||||
|
||||
BIN
finance.db
BIN
finance.db
Binary file not shown.
29
finance_data_snapshot-31-01-2025.csv
Normal file
29
finance_data_snapshot-31-01-2025.csv
Normal file
@@ -0,0 +1,29 @@
|
||||
2025-Date,2025-Value,2026-Date,2026-Value,2027-Date,2027-Value,2028-Date,2028-Value,2029-Date,2029-Value,2030-Date,2030-Value,2031-Date,2031-Value
|
||||
2025-02-05,414887.73,2026-01-07,374779.5,2027-01-06,262498.3,2028-01-05,219760.58,2029-01-03,171153.2,2030-01-02,116169.68,2031-01-01,32136.61,,Bills,"$19,330.00",,Variable,Value
|
||||
2025-02-19,415836.12,2026-01-21,370468.42,2027-01-20,259056.92,2028-01-19,216182.55,2029-01-17,167433.11,2030-01-16,112301.89,2031-01-15,28269.95,,Buffer,$0.00,,D_Salary,4762.29
|
||||
2025-03-05,418367.1,2026-02-04,367735.83,2027-02-03,256717.17,2028-02-02,213523.9,2029-01-31,163713.02,2030-01-30,108434.09,2031-01-29,24248.59,,Monthly
|
||||
budget,"$5,916.67",,D_Num_fortnights_pay,6
|
||||
2025-03-19,419305.1,2026-02-18,363413.99,2027-02-17,253264.6,2028-02-16,209934.25,2029-02-14,160692.72,2030-02-13,105031.25,2031-02-12,20334.57,,Weekly
|
||||
budget,"$1,365.38",,School_Fees,22000
|
||||
2025-04-02,422012.43,2026-03-04,360488.77,2027-03-03,250783.27,2028-03-01,206344.6,2029-02-28,156960.54,2030-02-27,101150.88,2031-02-26,16300.14,,,,,Car_loan_via_pay,620
|
||||
2025-04-16,467253.33,2026-03-18,356156.14,2027-03-17,247319.48,2028-03-15,203577.26,2029-03-14,153831.92,2030-03-13,97659.91,2031-03-12,12329.53,,,,,Car_loan,1001.12
|
||||
2025-04-30,463037.5,2026-04-01,351823.5,2027-03-31,243855.69,2028-03-29,199975.94,2029-03-28,150087.61,2030-03-27,93766.93,2031-03-26,8281.98,,,,,Car_balloon,45824.68
|
||||
2025-05-14,460631.46,2026-04-15,348996.24,2027-04-14,241431.71,2028-04-12,197227.75,2029-04-11,146984.46,2030-04-10,90275.82,2031-04-09,4273.14,,,,,Living_Expenses,83000
|
||||
2025-05-28,456405.19,2026-04-29,344652.78,2027-04-28,237956.66,2028-04-26,193614.73,2029-04-25,143227.98,2030-04-24,86370.19,,,,,,,Savings,412000
|
||||
2025-06-11,422125.83,2026-05-13,341734.41,2027-05-12,235462.37,2028-05-10,190799.91,2029-05-09,140062.51,2030-05-08,82821.76,,,,,,,Interest_Rate,5.0
|
||||
2025-06-25,417889.08,2026-05-27,337380.09,2027-05-26,231976.03,2028-05-24,187175.14,2029-05-23,136293.82,2030-05-22,78903.44,,,,,,,Inflation,3.9
|
||||
2025-07-09,451750.54,2026-06-10,334466.85,2027-06-09,229477.84,2028-06-07,184347.87,2029-06-06,133106.4,2030-06-05,75322.5,,,,,,,Mich_present,10000
|
||||
2025-07-23,447503.27,2026-06-24,330101.63,2027-06-23,225980.16,2028-06-21,180711.33,2029-06-20,129325.47,2030-06-19,71391.44,,,,,,,Overseas_trip,32000
|
||||
2025-08-06,445162.62,2026-07-08,363397.17,2027-07-07,259710.84,2028-07-05,214116.72,2029-07-04,162375.12,2030-07-03,82530.95,,,,,,,Mark_reno,10000
|
||||
2025-08-20,440904.8,2026-07-22,359021.02,2027-07-21,256201.8,2028-07-19,210468.35,2029-07-18,158581.9,2030-07-17,78587.12,,,,,,,D_leave_owed_in_days,90.6
|
||||
2025-09-03,428524.06,2026-08-05,356175.3,2027-08-04,253782.55,2028-08-02,207715.12,2029-08-01,154788.67,2030-07-31,74643.29,,,,,,,D_TLS_shares,1000
|
||||
2025-09-17,424255.66,2026-08-19,351788.17,2027-08-18,250262.11,2028-08-16,204054.9,2029-08-15,151657.7,2030-08-14,71021.28,,,,,,,M_TLS_shares,750
|
||||
2025-10-01,419987.25,2026-09-02,348899.23,2027-09-01,246741.66,2028-08-30,200394.68,2029-08-29,147852.15,2030-08-28,67064.63,,,,,,,D_CBA_shares,1095
|
||||
2025-10-15,417454.7,2026-09-16,344501.1,2027-09-15,244273.6,2028-09-13,197589.68,2029-09-12,144678.6,2030-09-11,63396.8,,,,,,,TLS_price,3.99
|
||||
2025-10-29,413175.68,2026-09-30,340102.97,2027-09-29,240741.72,2028-09-27,193917.56,2029-09-26,140860.68,2030-09-25,59427.29,,,,,,,CBA_price,160.61
|
||||
2025-11-12,410660.56,2026-10-14,337111.91,2027-10-13,238202.3,2028-10-11,191045.16,2029-10-10,137624.31,2030-10-09,55704.53,,,,,,,Overseas_trip_date,2025-06-01
|
||||
2025-11-26,406370.89,2026-10-28,322702.74,2027-10-27,234658.94,2028-10-25,187361.11,2029-10-24,133793.99,2030-10-23,51722.12,,,,,,,Mark_reno_date,2025-09-01
|
||||
2025-12-10,381758.47,2026-11-11,319692.08,2027-11-10,232115.41,2028-11-08,184475.65,2029-11-07,130534.62,2030-11-06,47961.98,,,,,,,Sell_shares,6
|
||||
2025-12-24,377458.1,2026-11-25,270448.27,2027-11-24,228560.53,2028-11-22,180779.63,2029-11-21,126691.85,2030-11-20,43966.63,,,,,,
|
||||
,,2026-12-09,268232.01,2027-12-08,225946.99,2028-12-06,177828.21,2029-12-05,123371.23,2030-12-04,40153.28,,,,,,
|
||||
,,2026-12-23,264801.78,2027-12-22,222380.56,2028-12-20,174120.17,2029-12-19,119515.97,2030-12-18,36144.95,,,,,,
|
||||
|
34
main.py
34
main.py
@@ -1,7 +1,7 @@
|
||||
# main.py
|
||||
from flask import Flask, render_template, request, redirect, url_for, Response
|
||||
from calc import calculate_savings_depletion
|
||||
from db import init_db, get_finance_data, update_finance
|
||||
from db import init_db, get_finance_data, update_finance, get_budget_data
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
import csv
|
||||
@@ -16,16 +16,10 @@ init_db()
|
||||
def index():
|
||||
finance_data = get_finance_data()
|
||||
depletion_date, savings_per_fortnight, final_savings, TLS, CBA = calculate_savings_depletion(finance_data)
|
||||
BUDGET=get_budget_data(finance_data, CBA, TLS)
|
||||
if depletion_date:
|
||||
depletion_date=depletion_date.date(); # just show date
|
||||
|
||||
# annual bills - rates (2.4), electricity (1.5), gas (2), internet (1.6), car insurance (.7), rego (.8), house insurance (2.4), GFC (2.2), phones (.5), melb. pollen (.03), nabu casa (.1), eweka (.1) --- noting phone is elevated presuming I also go onto Aldi plan, but that there is no family discount
|
||||
bills = 14330
|
||||
BUDGET=[]
|
||||
BUDGET.append( ('Bills', f"${bills:,.2f}") )
|
||||
BUDGET.append( ('Buffer', f"${CBA*finance_data['CBA_price']:,.2f}") )
|
||||
BUDGET.append( ('Monthly budget', f"${((finance_data['Living_Expenses']-12000) / 12):,.2f}" ) )
|
||||
BUDGET.append( ('Weekly budget', f"${((finance_data['Living_Expenses']-12000) / 52):,.2f}" ) )
|
||||
return render_template('index.html', finance=finance_data, depletion_date=depletion_date, savings=savings_per_fortnight, TLS=TLS, CBA=CBA, BUDGET=BUDGET)
|
||||
|
||||
@app.route('/update', methods=['POST'])
|
||||
@@ -65,6 +59,7 @@ def download_csv():
|
||||
|
||||
finance_data = get_finance_data()
|
||||
depletion_date, savings_per_fortnight, final_savings, TLS, CBA = calculate_savings_depletion(finance_data)
|
||||
BUDGET=get_budget_data(finance_data, CBA, TLS)
|
||||
|
||||
# Group data by year
|
||||
data_by_year = defaultdict(list)
|
||||
@@ -78,6 +73,10 @@ def download_csv():
|
||||
# Sort years for column ordering
|
||||
years = sorted(data_by_year.keys())
|
||||
|
||||
# get finance data into a list for spitting out during csv last column dump
|
||||
fd_lst = list(finance_data.items())
|
||||
fd_lst[0]=('Variable', 'Value')
|
||||
|
||||
# Create an in-memory output file
|
||||
output = io.StringIO()
|
||||
|
||||
@@ -91,6 +90,7 @@ def download_csv():
|
||||
writer.writerow(header)
|
||||
|
||||
# Write the data rows
|
||||
cnt=0
|
||||
for i in range(max_entries_per_year):
|
||||
row = []
|
||||
for year in years:
|
||||
@@ -100,8 +100,26 @@ def download_csv():
|
||||
row.extend([date, value])
|
||||
else:
|
||||
row.extend(["", ""]) # If no data for this year, leave blank cells
|
||||
|
||||
# spacer
|
||||
row.extend([""])
|
||||
|
||||
# now add budget data
|
||||
if cnt < len(BUDGET):
|
||||
row.extend( BUDGET[cnt] )
|
||||
else:
|
||||
row.extend(["", ""]) # If no data for this year, leave blank cells
|
||||
|
||||
# spacer
|
||||
row.extend([""])
|
||||
|
||||
# now add reference data
|
||||
if cnt < len(fd_lst):
|
||||
row.extend(fd_lst[cnt])
|
||||
writer.writerow(row)
|
||||
|
||||
cnt += 1
|
||||
|
||||
csv_data = output.getvalue()
|
||||
|
||||
# Create a Flask Response object, with CSV mime type and downloadable as a file
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<title>Finance Form</title>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
@@ -167,6 +168,22 @@
|
||||
<input type="number" class="form-control text-end float-end bg-light" id="TLS_shares" value="{{TLS}}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button type="button" class="btn btn-primary" onClick="
|
||||
$.ajax( { type: 'GET', url: '/download_csv', xhrFields: { responseType: 'blob' },
|
||||
success: function(res){
|
||||
// Create a link element
|
||||
const link = document.createElement('a');
|
||||
const url = window.URL.createObjectURL(res);
|
||||
link.href = url;
|
||||
link.download = 'finance_data.csv'; // Set the file name
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url); // Clean up the object URL
|
||||
console.log('done') } })
|
||||
"> Export to CSV </button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user