oceanicdayi commited on
Commit
e47d3cd
·
verified ·
1 Parent(s): a605291

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +57 -21
app.py CHANGED
@@ -105,6 +105,19 @@ def level(text):
105
  return "neutral"
106
 
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  def module_items(payload):
109
  rows = []
110
  sources = ["containers", "container_status", "container", "docker", "modules", "module_status", "services", "checks"]
@@ -112,17 +125,16 @@ def module_items(payload):
112
  value = payload.get(key)
113
  if isinstance(value, dict):
114
  for name, info in value.items():
115
- if isinstance(info, dict):
116
- status = info.get("status") or info.get("state") or info.get("health") or info.get("alive") or info.get("ok") or "unknown"
117
- else:
118
- status = info
119
  rows.append((str(name), str(status)))
120
  elif isinstance(value, list):
121
  for i, info in enumerate(value):
122
  if isinstance(info, dict):
123
  name = info.get("name") or info.get("module") or info.get("container") or f"module_{i+1}"
124
- status = info.get("status") or info.get("state") or info.get("health") or info.get("alive") or "unknown"
125
  rows.append((str(name), str(status)))
 
 
126
  header = payload.get("latest_rep_header") or {}
127
  if isinstance(header, dict) and header.get("file"):
128
  rows.append(("Earthworm EEW header", "available"))
@@ -141,24 +153,48 @@ def render_status(source):
141
  with open(FIXTURES / "normal_event.json", "r", encoding="utf-8") as f:
142
  payload = json.load(f)
143
  label = f"fallback: {e}"
144
- cards = []
 
 
145
  for name, status in module_items(payload):
146
- cls = level(status)
147
- cards.append(f"<div class='card {cls}'><span class='lamp'></span><div><b>{esc(name)}</b><small>{esc(status)}</small></div></div>")
 
 
 
 
 
 
 
 
 
 
 
148
  return f"""
149
  <style>
150
- .wrap{{display:grid;gap:14px}}.hero{{border-radius:20px;padding:18px;color:#fff;background:linear-gradient(135deg,#0f172a,#2563eb);box-shadow:0 10px 24px rgba(15,23,42,.16)}}
151
- .hero h2{{margin:0 0 6px;font-size:24px}}.src{{font-size:13px;opacity:.85;word-break:break-all}}
152
- .grid{{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}}
153
- .card{{display:flex;gap:12px;align-items:center;padding:14px;border-radius:16px;background:#fff;border:1px solid #e5e7eb;box-shadow:0 6px 18px rgba(15,23,42,.06)}}
154
- .lamp{{width:22px;height:22px;border-radius:99px;background:#64748b;box-shadow:0 0 0 6px rgba(100,116,139,.12)}}
155
- .ok .lamp{{background:#22c55e;box-shadow:0 0 0 6px rgba(34,197,94,.16),0 0 18px rgba(34,197,94,.65)}}
156
- .warn .lamp{{background:#f59e0b;box-shadow:0 0 0 6px rgba(245,158,11,.18),0 0 18px rgba(245,158,11,.65)}}
157
- .bad .lamp{{background:#ef4444;box-shadow:0 0 0 6px rgba(239,68,68,.18),0 0 18px rgba(239,68,68,.65)}}
158
- b{{display:block;color:#0f172a}}small{{display:block;color:#64748b;margin-top:3px;word-break:break-all}}
159
- @media(max-width:640px){{.grid{{grid-template-columns:1fr}}.hero h2{{font-size:21px}}}}
 
 
 
 
 
 
 
160
  </style>
161
- <div class='wrap'><div class='hero'><h2>系統狀態亮燈</h2><div class='src'>來源:{esc(STATUS_DATASET_ID)} / {esc(label)}</div></div><div class='grid'>{''.join(cards)}</div></div>
 
 
 
 
162
  """
163
 
164
 
@@ -275,7 +311,7 @@ def trace_stats(label, y):
275
  def plot_waveform(source):
276
  try:
277
  series, label = load_waveform(source)
278
- msg = f"✅ 已載入 {label},alive 狀態會亮綠燈;波形分開繪製並附統計資訊。"
279
  except Exception as e:
280
  series, label = demo_series(), "demo://synthetic fallback"
281
  msg = f"⚠️ 遠端波形讀取失敗,改顯示示範資料:{e}"
@@ -347,7 +383,7 @@ status_opts = status_choices()
347
  wave_opts = waveform_choices()
348
 
349
  with gr.Blocks(title="EEW Dashboard") as demo:
350
- gr.Markdown("# EEW Dashboard\n系統狀態以亮燈呈現;`alive` 亮綠燈。靜態波形使用 Plotly,10 條波線分開畫、one column,並附統計資訊。")
351
  with gr.Tab("系統狀態"):
352
  with gr.Row():
353
  s = gr.Dropdown(choices=status_opts, value=status_opts[0], label="Status file")
 
105
  return "neutral"
106
 
107
 
108
+ def is_alive_status(text):
109
+ return str(text).strip().lower() == "alive"
110
+
111
+
112
+ def normalized_status_from_info(info):
113
+ if isinstance(info, dict):
114
+ if "alive" in info or "is_alive" in info:
115
+ value = info.get("alive", info.get("is_alive"))
116
+ return "alive" if str(value).strip().lower() in {"true", "1", "yes", "alive"} else "not alive"
117
+ return info.get("status") or info.get("state") or info.get("health") or info.get("ok") or "unknown"
118
+ return info
119
+
120
+
121
  def module_items(payload):
122
  rows = []
123
  sources = ["containers", "container_status", "container", "docker", "modules", "module_status", "services", "checks"]
 
125
  value = payload.get(key)
126
  if isinstance(value, dict):
127
  for name, info in value.items():
128
+ status = normalized_status_from_info(info)
 
 
 
129
  rows.append((str(name), str(status)))
130
  elif isinstance(value, list):
131
  for i, info in enumerate(value):
132
  if isinstance(info, dict):
133
  name = info.get("name") or info.get("module") or info.get("container") or f"module_{i+1}"
134
+ status = normalized_status_from_info(info)
135
  rows.append((str(name), str(status)))
136
+ else:
137
+ rows.append((f"module_{i+1}", str(info)))
138
  header = payload.get("latest_rep_header") or {}
139
  if isinstance(header, dict) and header.get("file"):
140
  rows.append(("Earthworm EEW header", "available"))
 
153
  with open(FIXTURES / "normal_event.json", "r", encoding="utf-8") as f:
154
  payload = json.load(f)
155
  label = f"fallback: {e}"
156
+
157
+ alive_cards = []
158
+ info_cards = []
159
  for name, status in module_items(payload):
160
+ if is_alive_status(status):
161
+ alive_cards.append(
162
+ f"<div class='alive-card'><span class='lamp'></span><div><b>{esc(name)}</b><small>{esc(status)}</small></div></div>"
163
+ )
164
+ else:
165
+ cls = level(status)
166
+ info_cards.append(
167
+ f"<div class='info-card {cls}'><div><b>{esc(name)}</b><small>{esc(status)}</small></div><span class='tag'>{esc(status)}</span></div>"
168
+ )
169
+
170
+ alive_html = "".join(alive_cards) if alive_cards else "<div class='empty'>目前沒有 alive 型式狀態。</div>"
171
+ info_html = "".join(info_cards) if info_cards else "<div class='empty'>沒有其他狀態資訊。</div>"
172
+
173
  return f"""
174
  <style>
175
+ .status-wrap{{display:grid;gap:16px}}
176
+ .status-hero{{border-radius:20px;padding:18px;color:#fff;background:linear-gradient(135deg,#0f172a,#2563eb);box-shadow:0 10px 24px rgba(15,23,42,.16)}}
177
+ .status-hero h2{{margin:0 0 6px;font-size:24px}}.src{{font-size:13px;opacity:.85;word-break:break-all}}
178
+ .section{{background:#fff;border:1px solid #e5e7eb;border-radius:18px;padding:14px;box-shadow:0 8px 22px rgba(15,23,42,.06)}}
179
+ .section-title{{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;color:#0f172a;font-weight:900;font-size:17px}}
180
+ .section-note{{font-size:12px;color:#64748b;font-weight:600}}
181
+ .alive-grid{{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px}}
182
+ .info-grid{{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}}
183
+ .alive-card{{display:flex;gap:12px;align-items:center;padding:15px;border-radius:16px;background:linear-gradient(180deg,#f0fdf4,#ffffff);border:1px solid rgba(34,197,94,.28)}}
184
+ .lamp{{width:24px;height:24px;border-radius:99px;background:#22c55e;box-shadow:0 0 0 7px rgba(34,197,94,.16),0 0 20px rgba(34,197,94,.70);flex:0 0 auto}}
185
+ .info-card{{display:flex;justify-content:space-between;gap:12px;align-items:center;padding:14px;border-radius:16px;background:#f8fafc;border:1px solid #e2e8f0}}
186
+ .info-card.ok{{background:#f8fafc;border-color:#dbeafe}}.info-card.warn{{background:#fffbeb;border-color:#fde68a}}.info-card.bad{{background:#fef2f2;border-color:#fecaca}}
187
+ b{{display:block;color:#0f172a}}small{{display:block;color:#64748b;margin-top:4px;word-break:break-all}}
188
+ .tag{{font-size:12px;font-weight:800;border-radius:999px;padding:6px 10px;background:#e2e8f0;color:#334155;white-space:nowrap;max-width:140px;overflow:hidden;text-overflow:ellipsis}}
189
+ .info-card.ok .tag{{background:#dbeafe;color:#1d4ed8}}.info-card.warn .tag{{background:#fef3c7;color:#b45309}}.info-card.bad .tag{{background:#fee2e2;color:#b91c1c}}
190
+ .empty{{padding:14px;border-radius:14px;background:#f8fafc;color:#64748b;border:1px dashed #cbd5e1}}
191
+ @media(max-width:640px){{.alive-grid,.info-grid{{grid-template-columns:1fr}}.status-hero h2{{font-size:21px}}}}
192
  </style>
193
+ <div class='status-wrap'>
194
+ <div class='status-hero'><h2>系統狀態</h2><div class='src'>來源:{esc(STATUS_DATASET_ID)} / {esc(label)}</div></div>
195
+ <div class='section'><div class='section-title'>Alive 模組綠燈 <span class='section-note'>僅 alive 使用燈號</span></div><div class='alive-grid'>{alive_html}</div></div>
196
+ <div class='section'><div class='section-title'>其他狀態資訊 <span class='section-note'>非 alive 不使用燈號</span></div><div class='info-grid'>{info_html}</div></div>
197
+ </div>
198
  """
199
 
200
 
 
311
  def plot_waveform(source):
312
  try:
313
  series, label = load_waveform(source)
314
+ msg = f"✅ 已載入 {label},波形分開繪製並附統計資訊。"
315
  except Exception as e:
316
  series, label = demo_series(), "demo://synthetic fallback"
317
  msg = f"⚠️ 遠端波形讀取失敗,改顯示示範資料:{e}"
 
383
  wave_opts = waveform_choices()
384
 
385
  with gr.Blocks(title="EEW Dashboard") as demo:
386
+ gr.Markdown("# EEW Dashboard\n系統狀態分為 Alive 綠燈與其他狀態資訊;非 alive 不使用燈號。靜態波形使用 Plotly,10 條波線分開畫、one column,並附統計資訊。")
387
  with gr.Tab("系統狀態"):
388
  with gr.Row():
389
  s = gr.Dropdown(choices=status_opts, value=status_opts[0], label="Status file")