Remove blocking reconnect before pipeline run
Browse files- src/api.py +6 -90
src/api.py
CHANGED
|
@@ -804,12 +804,6 @@ async def _run_pipeline_async():
|
|
| 804 |
_pipeline_status["current_step"] = None
|
| 805 |
_pipeline_status["current_step_index"] = 0
|
| 806 |
|
| 807 |
-
# Reconnect DB before pipeline run (Neon may have slept)
|
| 808 |
-
try:
|
| 809 |
-
await db.reconnect()
|
| 810 |
-
except Exception as e:
|
| 811 |
-
logger.warning("DB reconnect failed (pipeline will run without DB): %s", e)
|
| 812 |
-
|
| 813 |
def _progress_cb(step_name, step_index):
|
| 814 |
_pipeline_status["current_step"] = step_name
|
| 815 |
_pipeline_status["current_step_index"] = step_index
|
|
@@ -955,57 +949,21 @@ def _pipeline_tracker_html() -> str:
|
|
| 955 |
|
| 956 |
@app.get("/", response_class=HTMLResponse)
|
| 957 |
async def status_page():
|
| 958 |
-
"""
|
| 959 |
-
demo = _get_demo()
|
| 960 |
-
s = demo.get("stats", {})
|
| 961 |
-
zones = demo.get("zones", [])
|
| 962 |
-
triggers = demo.get("triggers", [])
|
| 963 |
-
|
| 964 |
-
tiers = {}
|
| 965 |
-
for z in zones:
|
| 966 |
-
t = z.get("model_tier", "unknown")
|
| 967 |
-
tiers[t] = tiers.get(t, 0) + 1
|
| 968 |
-
tier_str = ", ".join(f"{v} {k}" for k, v in sorted(tiers.items(), key=lambda x: -x[1]))
|
| 969 |
-
|
| 970 |
-
trigger_rows = ""
|
| 971 |
-
for t in triggers[:5]:
|
| 972 |
-
trigger_rows += f"""
|
| 973 |
-
<tr>
|
| 974 |
-
<td>{t.get('zone_name', '')}</td>
|
| 975 |
-
<td>{t.get('city', '')}</td>
|
| 976 |
-
<td><span class="badge {t.get('trigger_level', '')}">{t.get('trigger_level', '').upper()}</span></td>
|
| 977 |
-
<td>{t.get('max_temp_c', 0):.1f}°C</td>
|
| 978 |
-
<td>{t.get('consecutive_days', 0)}</td>
|
| 979 |
-
</tr>"""
|
| 980 |
-
|
| 981 |
return f"""<!DOCTYPE html>
|
| 982 |
<html lang="en">
|
| 983 |
<head>
|
| 984 |
<meta charset="utf-8">
|
| 985 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 986 |
<meta http-equiv="refresh" content="5">
|
| 987 |
-
<title>Heat Risk Engine
|
| 988 |
<style>
|
| 989 |
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
|
| 990 |
-
body {{ font-family: system-ui, -apple-system, sans-serif; background: #faf8f5; color: #1a1a1a; padding: 32px; max-width:
|
| 991 |
h1 {{ font-size: 1.4rem; font-weight: 700; margin-bottom: 4px; }}
|
| 992 |
.subtitle {{ color: #888; font-size: 0.85rem; margin-bottom: 24px; }}
|
| 993 |
-
.
|
| 994 |
-
.card {{ background: #fff; border: 1px solid #e0dcd5; border-radius: 8px; padding: 14px; }}
|
| 995 |
-
.card .label {{ font-size: 0.7rem; color: #888; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; margin-bottom: 4px; }}
|
| 996 |
-
.card .value {{ font-size: 1.3rem; font-weight: 700; }}
|
| 997 |
-
.status {{ display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 0.75rem; font-weight: 600; background: #2a9d8f22; color: #2a9d8f; }}
|
| 998 |
-
.section {{ font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: #d4a019; border-bottom: 2px solid #d4a019; padding-bottom: 6px; margin: 20px 0 12px; }}
|
| 999 |
-
table {{ width: 100%; border-collapse: collapse; font-size: 0.82rem; }}
|
| 1000 |
-
th {{ text-align: left; font-size: 0.7rem; color: #888; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; padding: 6px 8px; border-bottom: 1px solid #e0dcd5; }}
|
| 1001 |
-
td {{ padding: 8px; border-bottom: 1px solid #f0ede8; }}
|
| 1002 |
-
.badge {{ display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; }}
|
| 1003 |
-
.badge.critical {{ background: #e6394622; color: #e63946; }}
|
| 1004 |
-
.badge.warning {{ background: #e67e2222; color: #e67e22; }}
|
| 1005 |
-
.badge.watch {{ background: #d4a01922; color: #d4a019; }}
|
| 1006 |
-
.link {{ color: #d4a019; text-decoration: none; font-weight: 600; }}
|
| 1007 |
.link:hover {{ text-decoration: underline; }}
|
| 1008 |
-
.footer {{ margin-top: 32px; padding-top: 16px; border-top: 1px solid #e0dcd5; font-size: 0.75rem; color: #aaa; }}
|
| 1009 |
.pipeline-tracker {{ background: #fff; border: 1px solid #e0dcd5; border-radius: 8px; padding: 16px; margin-bottom: 24px; }}
|
| 1010 |
.pipeline-tracker h3 {{ font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: #e63946; margin-bottom: 12px; }}
|
| 1011 |
.step-row {{ display: flex; align-items: center; gap: 10px; padding: 6px 0; font-size: 0.82rem; }}
|
|
@@ -1026,51 +984,9 @@ async def status_page():
|
|
| 1026 |
</style>
|
| 1027 |
</head>
|
| 1028 |
<body>
|
| 1029 |
-
<h1>Heat Risk Engine
|
| 1030 |
-
<p class="subtitle">
|
| 1031 |
|
| 1032 |
{_pipeline_tracker_html()}
|
| 1033 |
-
|
| 1034 |
-
<div class="grid">
|
| 1035 |
-
<div class="card">
|
| 1036 |
-
<div class="label">Zones</div>
|
| 1037 |
-
<div class="value">{len(zones)}</div>
|
| 1038 |
-
</div>
|
| 1039 |
-
<div class="card">
|
| 1040 |
-
<div class="label">Active Alerts</div>
|
| 1041 |
-
<div class="value">{s.get('active_triggers', 0)}</div>
|
| 1042 |
-
</div>
|
| 1043 |
-
<div class="card">
|
| 1044 |
-
<div class="label">Workers Enrolled</div>
|
| 1045 |
-
<div class="value">{s.get('total_enrolled', 0):,}</div>
|
| 1046 |
-
</div>
|
| 1047 |
-
<div class="card">
|
| 1048 |
-
<div class="label">Model Tiers</div>
|
| 1049 |
-
<div class="value" style="font-size: 0.85rem;">{tier_str or 'loading'}</div>
|
| 1050 |
-
</div>
|
| 1051 |
-
</div>
|
| 1052 |
-
|
| 1053 |
-
<div class="section">Active Triggers</div>
|
| 1054 |
-
{"<table><thead><tr><th>Zone</th><th>City</th><th>Level</th><th>Max Temp</th><th>Days</th></tr></thead><tbody>" + trigger_rows + "</tbody></table>" if triggers else "<p style='color:#888; font-size:0.85rem;'>No active triggers.</p>"}
|
| 1055 |
-
|
| 1056 |
-
<div class="section">API Endpoints</div>
|
| 1057 |
-
<table>
|
| 1058 |
-
<thead><tr><th>Endpoint</th><th>Description</th></tr></thead>
|
| 1059 |
-
<tbody>
|
| 1060 |
-
<tr><td><code>/health</code></td><td>Service health check</td></tr>
|
| 1061 |
-
<tr><td><code>/api/zones</code></td><td>All 20 zones with current heat data</td></tr>
|
| 1062 |
-
<tr><td><code>/api/indices</code></td><td>Heat indices with 90-day daily history</td></tr>
|
| 1063 |
-
<tr><td><code>/api/triggers</code></td><td>Active heat triggers</td></tr>
|
| 1064 |
-
<tr><td><code>/api/basis-risk</code></td><td>Basis risk assessments</td></tr>
|
| 1065 |
-
<tr><td><code>/api/calibrate</code></td><td>Interactive threshold + budget calibration</td></tr>
|
| 1066 |
-
<tr><td><code>/api/notifications</code></td><td>Sent alert messages</td></tr>
|
| 1067 |
-
<tr><td><code>/api/pipeline/runs</code></td><td>Pipeline run history</td></tr>
|
| 1068 |
-
<tr><td><code>/api/pipeline/stats</code></td><td>Aggregate statistics</td></tr>
|
| 1069 |
-
</tbody>
|
| 1070 |
-
</table>
|
| 1071 |
-
|
| 1072 |
-
<div class="footer">
|
| 1073 |
-
Vercel frontend: <a class="link" href="https://climate-risk-engine.vercel.app">climate-risk-engine.vercel.app</a>
|
| 1074 |
-
</div>
|
| 1075 |
</body>
|
| 1076 |
</html>"""
|
|
|
|
| 804 |
_pipeline_status["current_step"] = None
|
| 805 |
_pipeline_status["current_step_index"] = 0
|
| 806 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 807 |
def _progress_cb(step_name, step_index):
|
| 808 |
_pipeline_status["current_step"] = step_name
|
| 809 |
_pipeline_status["current_step_index"] = step_index
|
|
|
|
| 949 |
|
| 950 |
@app.get("/", response_class=HTMLResponse)
|
| 951 |
async def status_page():
|
| 952 |
+
"""Pipeline tracker for the HF Space."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 953 |
return f"""<!DOCTYPE html>
|
| 954 |
<html lang="en">
|
| 955 |
<head>
|
| 956 |
<meta charset="utf-8">
|
| 957 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 958 |
<meta http-equiv="refresh" content="5">
|
| 959 |
+
<title>Heat Risk Engine</title>
|
| 960 |
<style>
|
| 961 |
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
|
| 962 |
+
body {{ font-family: system-ui, -apple-system, sans-serif; background: #faf8f5; color: #1a1a1a; padding: 32px; max-width: 480px; margin: 0 auto; }}
|
| 963 |
h1 {{ font-size: 1.4rem; font-weight: 700; margin-bottom: 4px; }}
|
| 964 |
.subtitle {{ color: #888; font-size: 0.85rem; margin-bottom: 24px; }}
|
| 965 |
+
.link {{ color: #e63946; text-decoration: none; font-weight: 600; }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 966 |
.link:hover {{ text-decoration: underline; }}
|
|
|
|
| 967 |
.pipeline-tracker {{ background: #fff; border: 1px solid #e0dcd5; border-radius: 8px; padding: 16px; margin-bottom: 24px; }}
|
| 968 |
.pipeline-tracker h3 {{ font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: #e63946; margin-bottom: 12px; }}
|
| 969 |
.step-row {{ display: flex; align-items: center; gap: 10px; padding: 6px 0; font-size: 0.82rem; }}
|
|
|
|
| 984 |
</style>
|
| 985 |
</head>
|
| 986 |
<body>
|
| 987 |
+
<h1>Heat Risk Engine</h1>
|
| 988 |
+
<p class="subtitle"><a class="link" href="https://climate-risk-engine.vercel.app" target="_blank">Open Dashboard</a></p>
|
| 989 |
|
| 990 |
{_pipeline_tracker_html()}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 991 |
</body>
|
| 992 |
</html>"""
|