Initial commit — Moon Household Budget

This commit is contained in:
Bonna
2026-06-06 14:22:57 -05:00
commit fa3c030b36
44 changed files with 10860 additions and 0 deletions

56
sinking.py Normal file
View File

@@ -0,0 +1,56 @@
import json
import os
from datetime import date
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
SINKING_FILE = os.path.join(DATA_DIR, "sinking_funds.json")
FUND_COLORS = {
"Travel": "#C5BBA0",
"Vet & Pets": "#A0B8C5",
"Holidays & Gifts": "#D4B8C5",
"Renovations": "#C5A8A0",
"Studio Build": "#B4A0C5",
"Emergency Fund": "#A8C5A0",
}
DEFAULT_FUNDS = [
{"name": "Travel", "type": "ongoing", "monthly": 0, "balance": 0, "notes": ""},
{"name": "Vet & Pets", "type": "ongoing", "monthly": 0, "balance": 0, "notes": ""},
{"name": "Holidays & Gifts", "type": "ongoing", "monthly": 0, "balance": 0, "notes": ""},
{"name": "Renovations", "type": "ongoing", "monthly": 0, "balance": 0, "notes": "Subcategories coming later"},
{"name": "Studio Build", "type": "ongoing", "monthly": 0, "balance": 0, "notes": "Goal amount TBD"},
{"name": "Emergency Fund", "type": "ongoing", "monthly": 0, "balance": 0, "notes": "Target: 36 months expenses"},
]
def load_funds():
if os.path.exists(SINKING_FILE):
with open(SINKING_FILE) as f:
return json.load(f)
# First run — seed with defaults
save_funds(DEFAULT_FUNDS)
return DEFAULT_FUNDS
def save_funds(funds):
with open(SINKING_FILE, "w") as f:
json.dump(funds, f, indent=2)
def months_until(target_date_str):
try:
target = date.fromisoformat(target_date_str)
today = date.today()
months = (target.year - today.year) * 12 + (target.month - today.month)
return max(months, 1)
except Exception:
return None
def monthly_needed(balance, goal, target_date_str):
m = months_until(target_date_str)
if not m:
return None
remaining = max(goal - balance, 0)
return round(remaining / m, 2)