import copy import json import os from datetime import date from storage import load_json, save_json DATA_DIR = os.environ.get("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: 3–6 months expenses"}, ] def load_funds(): funds = load_json(SINKING_FILE, None) if funds is None: # First run — seed with a copy of the defaults (never hand out the # module-level list, which callers mutate in place). funds = copy.deepcopy(DEFAULT_FUNDS) save_funds(funds) return funds def save_funds(funds): save_json(SINKING_FILE, funds) 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)