Release v1.3.1 reliability improvements

This commit is contained in:
2026-06-23 00:11:11 +02:00
parent 70121adaf6
commit b9f687f118
8 changed files with 224 additions and 17 deletions
+74 -3
View File
@@ -4,7 +4,7 @@ from urllib.parse import quote
from zoneinfo import ZoneInfo
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from scanner import (
@@ -13,6 +13,7 @@ from scanner import (
MARKET_CONFIGS,
MARKET_CRYPTO,
MARKET_TRADFI,
count_current_signals,
db_connect,
init_db,
)
@@ -21,8 +22,8 @@ from scanner import (
app = FastAPI(title="Crypto ATR Signal")
templates = Jinja2Templates(directory="templates")
MADRID_TZ = ZoneInfo("Europe/Madrid")
APP_VERSION = "v1.3.0"
APP_RELEASE_DATE = "2026-06-22"
APP_VERSION = "v1.3.1"
APP_RELEASE_DATE = "2026-06-23"
APP_AUTHOR = "Z"
@@ -36,6 +37,7 @@ PAGE_SHOW_CRYPTO = env_bool("PAGE_SHOW_CRYPTO", True)
PAGE_SHOW_TRADFI = env_bool("PAGE_SHOW_TRADFI", True)
PAGE_GROUP_BY_MARKET = env_bool("PAGE_GROUP_BY_MARKET", True)
PAGE_SHOW_VERSION = env_bool("PAGE_SHOW_VERSION", True)
HEALTH_MAX_SCAN_AGE_HOURS = float(os.getenv("HEALTH_MAX_SCAN_AGE_HOURS", "8"))
def fmt_decimal(value: str | None, places: int = 6) -> str:
@@ -104,6 +106,75 @@ def startup() -> None:
conn.close()
@app.get("/health", response_class=JSONResponse)
def health() -> JSONResponse:
checked_at = datetime.now(timezone.utc)
payload = {
"status": "ok",
"version": APP_VERSION,
"checked_at": checked_at.isoformat(),
"database": "ok",
"markets": {},
}
healthy = True
conn = None
try:
conn = db_connect(DB_PATH)
integrity = conn.execute("PRAGMA quick_check;").fetchone()[0]
if integrity != "ok":
payload["database"] = integrity
healthy = False
for market_type, slug in ((MARKET_CRYPTO, "crypto"), (MARKET_TRADFI, "tradfi")):
latest_run = conn.execute(
"""
SELECT status, finished_at, symbols_failed, error_summary
FROM scan_runs
WHERE market_type = ?
ORDER BY finished_at DESC
LIMIT 1
""",
(market_type,),
).fetchone()
if latest_run:
finished_at = datetime.fromisoformat(latest_run["finished_at"])
age_hours = max(0.0, (checked_at - finished_at).total_seconds() / 3600)
market_healthy = (
latest_run["status"] == "success"
and age_hours <= HEALTH_MAX_SCAN_AGE_HOURS
)
payload["markets"][slug] = {
"status": latest_run["status"],
"last_scan_at": finished_at.isoformat(),
"age_hours": round(age_hours, 2),
"failed_symbols": latest_run["symbols_failed"],
"current_signals": count_current_signals(conn, market_type),
"error": latest_run["error_summary"],
}
else:
market_healthy = False
payload["markets"][slug] = {
"status": "unavailable",
"last_scan_at": None,
"age_hours": None,
"failed_symbols": None,
"current_signals": count_current_signals(conn, market_type),
"error": "no scan history",
}
healthy = healthy and market_healthy
except Exception as exc: # noqa: BLE001 - health endpoint must report failures as JSON.
healthy = False
payload["database"] = "error"
payload["error"] = str(exc)
finally:
if conn is not None:
conn.close()
if not healthy:
payload["status"] = "degraded"
return JSONResponse(payload, status_code=200 if healthy else 503)
def load_market_view(
conn, market_type: str, sort_sql: str
) -> dict: