Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0276f68b3 |
44
app.py
44
app.py
@@ -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,
|
||||
|
||||
@@ -148,6 +148,23 @@
|
||||
.description a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 50px;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.perf-breakdown {
|
||||
display: none;
|
||||
margin-top: 5px;
|
||||
color: #222;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -199,6 +216,14 @@
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
{% set rt = get_render_time() %}
|
||||
<div class="footer"
|
||||
onclick="document.getElementById('perf-details').style.display = (document.getElementById('perf-details').style.display === 'block' ? 'none' : 'block')">
|
||||
render time: {{ rt.total }}s
|
||||
<div id="perf-details" class="perf-breakdown">
|
||||
{{ rt.breakdown }}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -137,6 +137,23 @@
|
||||
tr:hover {
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 50px;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.perf-breakdown {
|
||||
display: none;
|
||||
margin-top: 5px;
|
||||
color: #222;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -183,6 +200,14 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% set rt = get_render_time() %}
|
||||
<div class="footer"
|
||||
onclick="document.getElementById('perf-details').style.display = (document.getElementById('perf-details').style.display === 'block' ? 'none' : 'block')">
|
||||
render time: {{ rt.total }}s
|
||||
<div id="perf-details" class="perf-breakdown">
|
||||
{{ rt.breakdown }}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -343,6 +343,23 @@
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 50px;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.perf-breakdown {
|
||||
display: none;
|
||||
margin-top: 5px;
|
||||
color: #222;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -485,6 +502,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% set rt = get_render_time() %}
|
||||
<div class="footer"
|
||||
onclick="document.getElementById('perf-details').style.display = (document.getElementById('perf-details').style.display === 'block' ? 'none' : 'block')">
|
||||
render time: {{ rt.total }}s
|
||||
<div id="perf-details" class="perf-breakdown">
|
||||
{{ rt.breakdown }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const memberData = {{ member_data| safe }};
|
||||
const sortedMonths = {{ raw_months| tojson }};
|
||||
|
||||
Reference in New Issue
Block a user