Harden JSON storage (atomic writes + crash-safe loads) + suggestions lightbulb
- storage.py: atomic save_json (temp+fsync+os.replace) and load_json that survives missing/corrupt files (moves corrupt aside). Closes #11/#12. - route all data modules + app.py helpers through storage (closes #31 core); fixes mutable DEFAULT_FUNDS/DEFAULT_CARS return (#23). - gunicorn --workers 1 --threads 4 to remove the write race (#13); strip Dockerfile template scaffolding (#40). - base.html: 💡 suggestions link in the shared nav (shows on every page). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
29
app.py
29
app.py
@@ -5,6 +5,7 @@ import pdfplumber
|
||||
import re
|
||||
from datetime import datetime, date
|
||||
import calendar as cal_module
|
||||
from storage import load_json, save_json
|
||||
from categories import CATEGORIES, CATEGORY_COLORS
|
||||
from bills import load_bills, save_bills, bills_for_month, calendar_grid, get_week_num
|
||||
from sinking import (load_funds, save_funds, FUND_COLORS, monthly_needed,
|
||||
@@ -57,14 +58,10 @@ def money_filter(value):
|
||||
TAX_FILE = os.path.join(os.environ.get("DATA_DIR", os.path.join(os.path.dirname(__file__), "data")), "taxes.json")
|
||||
|
||||
def load_taxes():
|
||||
if os.path.exists(TAX_FILE):
|
||||
with open(TAX_FILE) as f:
|
||||
return json.load(f)
|
||||
return {"rates": {"se_tax": 15.3, "federal": 22.0, "wi_state": 5.3}, "set_aside": []}
|
||||
return load_json(TAX_FILE, {"rates": {"se_tax": 15.3, "federal": 22.0, "wi_state": 5.3}, "set_aside": []})
|
||||
|
||||
def save_taxes(data):
|
||||
with open(TAX_FILE, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
save_json(TAX_FILE, data)
|
||||
|
||||
STANDARD_DEDUCTION_MFJ = 30000 # 2025 married filing jointly
|
||||
|
||||
@@ -177,27 +174,11 @@ NET_WORTH_FILE = os.path.join(DATA_DIR, "net_worth_accounts.json")
|
||||
|
||||
|
||||
def load_nw_accounts():
|
||||
if os.path.exists(NET_WORTH_FILE):
|
||||
with open(NET_WORTH_FILE) as f:
|
||||
return json.load(f)
|
||||
return []
|
||||
return load_json(NET_WORTH_FILE, [])
|
||||
|
||||
|
||||
def save_nw_accounts(accounts):
|
||||
with open(NET_WORTH_FILE, "w") as f:
|
||||
json.dump(accounts, f, indent=2)
|
||||
|
||||
|
||||
def load_json(path, default):
|
||||
if os.path.exists(path):
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
return default
|
||||
|
||||
|
||||
def save_json(path, data):
|
||||
with open(path, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
save_json(NET_WORTH_FILE, accounts)
|
||||
|
||||
|
||||
def get_transactions():
|
||||
|
||||
Reference in New Issue
Block a user