feat: add detailed performance profiling with interactive toggle
All checks were successful
Deploy to K8s / deploy (push) Successful in 11s
Build and Push / build (push) Successful in 9s

This commit is contained in:
Jan Novak
2026-03-02 22:34:06 +01:00
parent 7d05e3812c
commit b0276f68b3
4 changed files with 119 additions and 1 deletions

44
app.py
View File

@@ -2,7 +2,8 @@ import sys
from pathlib import Path
from datetime import datetime
import re
from flask import Flask, render_template
import time
from flask import Flask, render_template, g
# Add scripts directory to path to allow importing from it
scripts_dir = Path(__file__).parent / "scripts"
@@ -13,6 +14,35 @@ from match_payments import reconcile, fetch_sheet_data, fetch_exceptions, normal
app = Flask(__name__)
@app.before_request
def start_timer():
g.start_time = time.perf_counter()
g.steps = []
def record_step(name):
g.steps.append((name, time.perf_counter()))
@app.context_processor
def inject_render_time():
def get_render_time():
total = time.perf_counter() - g.start_time
breakdown = []
last_time = g.start_time
for name, timestamp in g.steps:
duration = timestamp - last_time
breakdown.append(f"{name}:{duration:.3f}s")
last_time = timestamp
# Add remaining time as 'render'
render_duration = time.perf_counter() - last_time
breakdown.append(f"render:{render_duration:.3f}s")
return {
"total": f"{total:.3f}",
"breakdown": " | ".join(breakdown)
}
return dict(get_render_time=get_render_time)
@app.route("/")
def index():
# Redirect root to /fees for convenience while there are no other apps
@@ -24,6 +54,7 @@ def fees():
payments_url = f"https://docs.google.com/spreadsheets/d/{PAYMENTS_SHEET_ID}/edit"
members, sorted_months = get_members_with_fees()
record_step("fetch_members")
if not members:
return "No data."
@@ -40,6 +71,7 @@ def fees():
# Get exceptions for formatting
credentials_path = ".secret/fuj-management-bot-credentials.json"
exceptions = fetch_exceptions(PAYMENTS_SHEET_ID, credentials_path)
record_step("fetch_exceptions")
formatted_results = []
for name, month_fees in results:
@@ -63,6 +95,8 @@ def fees():
row["months"].append({"cell": cell, "overridden": is_overridden})
formatted_results.append(row)
record_step("process_data")
return render_template(
"fees.html",
months=[month_labels[m] for m in sorted_months],
@@ -81,12 +115,16 @@ def reconcile_view():
credentials_path = ".secret/fuj-management-bot-credentials.json"
members, sorted_months = get_members_with_fees()
record_step("fetch_members")
if not members:
return "No data."
transactions = fetch_sheet_data(PAYMENTS_SHEET_ID, credentials_path)
record_step("fetch_payments")
exceptions = fetch_exceptions(PAYMENTS_SHEET_ID, credentials_path)
record_step("fetch_exceptions")
result = reconcile(members, sorted_months, transactions, exceptions)
record_step("reconcile")
# Format month labels
month_labels = {
@@ -128,6 +166,8 @@ def reconcile_view():
unmatched = result["unmatched"]
import json
record_step("process_data")
return render_template(
"reconcile.html",
months=[month_labels[m] for m in sorted_months],
@@ -148,6 +188,7 @@ def payments():
credentials_path = ".secret/fuj-management-bot-credentials.json"
transactions = fetch_sheet_data(PAYMENTS_SHEET_ID, credentials_path)
record_step("fetch_payments")
# Group transactions by person
grouped = {}
@@ -171,6 +212,7 @@ def payments():
# Sort by date descending
grouped[p].sort(key=lambda x: str(x.get("date", "")), reverse=True)
record_step("process_data")
return render_template(
"payments.html",
grouped_payments=grouped,