111 lines
3.3 KiB
Python
111 lines
3.3 KiB
Python
import locale
|
|
import numbers
|
|
import os
|
|
|
|
from starlette.templating import Jinja2Templates
|
|
|
|
TRANSACTION_TYPE_DE = {
|
|
"deposit": "Einzahlung",
|
|
"withdrawal": "Auszahlung",
|
|
"expense": "Auslage",
|
|
"order": "Einkauf",
|
|
}
|
|
|
|
UNITS_OF_MEASURE = {"piece": "Stück"}
|
|
|
|
|
|
def format_number(value: float):
|
|
try:
|
|
return f"{value:.2f}".replace(".", ",")
|
|
except TypeError:
|
|
return value
|
|
|
|
|
|
def get_jinja_renderer() -> Jinja2Templates:
|
|
renderer = Jinja2Templates(directory="src/allmende_payment_system/templates")
|
|
renderer.env.filters["transaction_type_de"] = lambda x: TRANSACTION_TYPE_DE[x]
|
|
renderer.env.filters["units_of_measure_de"] = lambda x: UNITS_OF_MEASURE.get(x, x)
|
|
renderer.env.filters["format_number"] = format_number
|
|
renderer.env.filters["timestamp_de"] = lambda x: x.strftime("%d.%m.%Y %H:%M")
|
|
|
|
renderer.env.globals["production_mode"] = "APS_PRODUCTION_MODE" in os.environ
|
|
renderer.env.globals["read_only_mode"] = "APS_READ_ONLY" in os.environ
|
|
return renderer
|
|
|
|
|
|
def timer_context(name: str):
|
|
import time
|
|
|
|
class Timer:
|
|
def __enter__(self):
|
|
self.start = time.perf_counter()
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
end = time.perf_counter()
|
|
print(f"{name} took {end - self.start:.4f} seconds")
|
|
|
|
return Timer()
|
|
|
|
|
|
import cProfile
|
|
import io
|
|
import pstats
|
|
from contextlib import contextmanager
|
|
|
|
|
|
@contextmanager
|
|
def profile(top_n: int = 20, sort_by: str = "cumulative", min_time: float = 0.001):
|
|
pr = cProfile.Profile()
|
|
pr.enable()
|
|
try:
|
|
yield
|
|
finally:
|
|
pr.disable()
|
|
s = io.StringIO()
|
|
ps = pstats.Stats(pr, stream=s).strip_dirs()
|
|
ps.sort_stats(sort_by)
|
|
ps.print_stats()
|
|
|
|
rows = []
|
|
for line in s.getvalue().splitlines():
|
|
parts = line.split(None, 5)
|
|
if len(parts) < 6:
|
|
continue
|
|
try:
|
|
cum = float(parts[3])
|
|
if cum >= min_time:
|
|
rows.append(
|
|
(
|
|
int(parts[0].split("/")[0]),
|
|
float(parts[1]),
|
|
cum,
|
|
float(parts[4]),
|
|
parts[5].strip(),
|
|
)
|
|
)
|
|
except (ValueError, IndexError):
|
|
continue
|
|
|
|
rows = rows[:top_n]
|
|
if not rows:
|
|
print("[profiler] nothing exceeded min_time threshold")
|
|
return
|
|
|
|
w = 55
|
|
print(f"\n── profile ({sort_by}, ≥{min_time}s) ──────────────────────────────")
|
|
print(f"{'calls':>7} {'tot':>8} {'cum':>8} {'percall':>8} {'function':<{w}}")
|
|
print("─" * (7 + 8 + 8 + 8 + w + 6))
|
|
for calls, tot, cum, percall, loc in rows:
|
|
c = (
|
|
f"\033[91m{cum:8.4f}\033[0m"
|
|
if cum > 1
|
|
else f"\033[93m{cum:8.4f}\033[0m" if cum > 0.1 else f"{cum:8.4f}"
|
|
)
|
|
print(
|
|
f"{calls:>7} {tot:8.4f} {c} {percall:8.4f} {loc[-w:] if len(loc) > w else loc:<{w}}"
|
|
)
|
|
print(f"─" * (7 + 8 + 8 + 8 + w + 6))
|
|
print(
|
|
f" {len(rows)} functions | tot accounted: {sum(r[1] for r in rows):.4f}s\n"
|
|
)
|