cwadayi commited on
Commit
f487de3
·
verified ·
1 Parent(s): 1d3009f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +16 -38
app.py CHANGED
@@ -10,17 +10,16 @@ import matplotlib.pyplot as plt
10
  # === grafanalib: 定義 Dashboard 結構(不負責畫圖) ===
11
  # 注意:grafanalib 只產生 Grafana JSON;我們會把 JSON 顯示出來,並用 Matplotlib 實際畫圖
12
  from grafanalib.core import (
13
- Dashboard, Graph, Row, Target, YAxis, YAxes, # <--- REMOVED OPS_FORMAT
14
  )
15
 
16
  TAIPEI = tz.gettz("Asia/Taipei")
17
 
18
 
19
  def make_demo_dataframe():
20
- """產生示範資料(10 分鐘 1 點),欄位:time, amplitude, count"""
21
  t0 = datetime.now(tz=TAIPEI) - timedelta(minutes=60)
22
  times = [t0 + timedelta(minutes=i) for i in range(61)]
23
- # 假資料:振幅、事件數
24
  amp = [0.5 + 0.4 * __import__("math").sin(i / 6.0) for i in range(61)]
25
  cnt = [max(0, int(5 + 3 * __import__("math").cos(i / 10.0))) for i in range(61)]
26
  df = pd.DataFrame({"time": times, "amplitude": amp, "count": cnt})
@@ -32,15 +31,9 @@ def load_csv(file: gr.File | None) -> pd.DataFrame:
32
  if file is None:
33
  return make_demo_dataframe()
34
 
35
- # 讀 CSV,嘗試自動 parse 日期欄
36
  df = pd.read_csv(file.name)
37
- # 嘗試找到時間欄位(常見命名)
38
  time_col_candidates = ["time", "timestamp", "datetime", "date"]
39
- time_col = None
40
- for c in time_col_candidates:
41
- if c in df.columns:
42
- time_col = c
43
- break
44
  if time_col is None:
45
  raise ValueError("CSV 檔需包含時間欄位,例如:time / timestamp / datetime / date")
46
 
@@ -59,18 +52,15 @@ def build_grafanalib_dashboard(series_columns: list[str]) -> dict:
59
  - Row 2:Event Count(Graph)展示第二個數值欄(如有)
60
  注意:Target 只是示意;實際取數由我們在 Gradio 端處理。
61
  """
62
- # 目標(在 Grafana 會對應到資料來源查詢;這裡只作為 JSON 示意)
63
- targets = [
64
- Target(expr=f"{series_columns[0]}", legendFormat=series_columns[0]),
65
- ]
66
  panels = [
67
  Graph(
68
  title=f"Time Series - {series_columns[0]}",
69
- dataSource="(example)", # 在真實 Grafana 才會用到
70
  targets=targets,
71
  yAxes=YAxes(
72
- left=YAxis(format='none'), # <--- CHANGED HERE
73
- right=YAxis(format='none'), # <--- CHANGED HERE
74
  ),
75
  )
76
  ]
@@ -82,8 +72,8 @@ def build_grafanalib_dashboard(series_columns: list[str]) -> dict:
82
  dataSource="(example)",
83
  targets=[Target(expr=f"{series_columns[1]}", legendFormat=series_columns[1])],
84
  yAxes=YAxes(
85
- left=YAxis(format='none'), # <--- CHANGED HERE
86
- right=YAxis(format='none'), # <--- CHANGED HERE
87
  ),
88
  )
89
  )
@@ -101,11 +91,10 @@ def build_grafanalib_dashboard(series_columns: list[str]) -> dict:
101
  def render_matplotlib(df: pd.DataFrame, value_cols: list[str]):
102
  """
103
  用 Matplotlib 依據 df 實際畫圖,對應 grafanalib 定義的欄位。
104
- 回傳多個 matplotlib Figure 物件(給 gr.Gallery / gr.Plot 使用)
105
  """
106
  figs = []
107
 
108
- # 第一張圖
109
  fig1 = plt.figure()
110
  plt.plot(df["time"], df[value_cols[0]])
111
  plt.title(f"Time Series - {value_cols[0]}")
@@ -115,7 +104,6 @@ def render_matplotlib(df: pd.DataFrame, value_cols: list[str]):
115
  plt.tight_layout()
116
  figs.append(fig1)
117
 
118
- # 第二張圖(選配)
119
  if len(value_cols) > 1:
120
  fig2 = plt.figure()
121
  plt.plot(df["time"], df[value_cols[1]])
@@ -139,12 +127,10 @@ def pipeline(file, series_choice):
139
  """
140
  df = load_csv(file)
141
 
142
- # 自動找出數值欄位(排除 time)
143
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
144
  if not numeric_cols:
145
  raise ValueError("未找到可繪圖的數值欄位。請在 CSV 中提供至少一個數值欄位(除了 time 以外)。")
146
 
147
- # 若使用者有選擇,以選擇優先;否則用前兩個
148
  chosen = series_choice or numeric_cols[:2]
149
  chosen = [c for c in chosen if c in numeric_cols]
150
  if not chosen:
@@ -153,10 +139,8 @@ def pipeline(file, series_choice):
153
  dash_json = build_grafanalib_dashboard(chosen)
154
  figs = render_matplotlib(df, chosen)
155
 
156
- # 輸出 JSON(pretty)
157
  dash_json_str = json.dumps(dash_json, ensure_ascii=False, indent=2, default=str)
158
 
159
- # 讓使用者可以下載 JSON
160
  json_bytes = io.BytesIO(dash_json_str.encode("utf-8"))
161
  json_bytes.name = "dashboard.json"
162
 
@@ -166,7 +150,7 @@ def pipeline(file, series_choice):
166
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
167
  gr.Markdown("# grafanalib 生成 Dashboard + Gradio 呈現\n"
168
  "上傳 CSV(需含 `time` 欄)或用示範資料,選擇要畫的數值欄位。\n\n"
169
- "**說明**:grafanalib 負責產生 Grafana 的 Dashboard JSON 結構;下方 Matplotlib 圖是我們在 Space 端實際繪圖的呈現。")
170
 
171
  with gr.Row():
172
  file_in = gr.File(label="上傳 CSV(可空,會用示範資料)", file_types=[".csv"])
@@ -181,7 +165,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
181
  with gr.Row():
182
  json_file = gr.File(label="下載 dashboard.json")
183
 
184
- # 顯示 DataFrame(互動檢視)
185
  df_view = gr.Dataframe(label="資料預覽(自動偵測 time + 數值欄位)", wrap=True)
186
 
187
  def probe_columns(file):
@@ -191,32 +174,27 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
191
  return gr.CheckboxGroup(choices=numeric_cols), df
192
 
193
  def initial_load():
194
- """在 App 載入時,直接用示範資料跑一次完整流程"""
195
  figs, dash_json, json_file_obj, df = pipeline(file=None, series_choice=[])
196
-
197
- # 更新欄位選項,並設定預設勾選項目
198
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
199
  chosen = numeric_cols[:2]
200
-
201
  return figs, dash_json, json_file_obj, df, gr.CheckboxGroup(choices=numeric_cols, value=chosen)
202
 
203
- # --- 事件綁定 ---
204
-
205
- # 1. 頁面載入時:直接用範例資料顯示圖表
206
  demo.load(
207
  initial_load,
208
  inputs=None,
209
  outputs=[gallery, json_box, json_file, df_view, series_multiselect]
210
  )
211
 
212
- # 2. 當使用者上傳新檔案時:更新欄位選項和資料預覽
213
  file_in.change(
214
  probe_columns,
215
  inputs=[file_in],
216
  outputs=[series_multiselect, df_view],
217
  )
218
 
219
- # 3. 當使用者點擊按鈕時:執行完整流程
220
  run_btn.click(
221
  pipeline,
222
  inputs=[file_in, series_multiselect],
@@ -224,4 +202,4 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
224
  )
225
 
226
  if __name__ == "__main__":
227
- demo.launch()
 
10
  # === grafanalib: 定義 Dashboard 結構(不負責畫圖) ===
11
  # 注意:grafanalib 只產生 Grafana JSON;我們會把 JSON 顯示出來,並用 Matplotlib 實際畫圖
12
  from grafanalib.core import (
13
+ Dashboard, Graph, Row, Target, YAxis, YAxes, # 移除 OPS_FORMAT
14
  )
15
 
16
  TAIPEI = tz.gettz("Asia/Taipei")
17
 
18
 
19
  def make_demo_dataframe():
20
+ """產生示範資料(1 小時,1 分鐘 1 點),欄位:time, amplitude, count"""
21
  t0 = datetime.now(tz=TAIPEI) - timedelta(minutes=60)
22
  times = [t0 + timedelta(minutes=i) for i in range(61)]
 
23
  amp = [0.5 + 0.4 * __import__("math").sin(i / 6.0) for i in range(61)]
24
  cnt = [max(0, int(5 + 3 * __import__("math").cos(i / 10.0))) for i in range(61)]
25
  df = pd.DataFrame({"time": times, "amplitude": amp, "count": cnt})
 
31
  if file is None:
32
  return make_demo_dataframe()
33
 
 
34
  df = pd.read_csv(file.name)
 
35
  time_col_candidates = ["time", "timestamp", "datetime", "date"]
36
+ time_col = next((c for c in time_col_candidates if c in df.columns), None)
 
 
 
 
37
  if time_col is None:
38
  raise ValueError("CSV 檔需包含時間欄位,例如:time / timestamp / datetime / date")
39
 
 
52
  - Row 2:Event Count(Graph)展示第二個數值欄(如有)
53
  注意:Target 只是示意;實際取數由我們在 Gradio 端處理。
54
  """
55
+ targets = [Target(expr=f"{series_columns[0]}", legendFormat=series_columns[0])]
 
 
 
56
  panels = [
57
  Graph(
58
  title=f"Time Series - {series_columns[0]}",
59
+ dataSource="(example)",
60
  targets=targets,
61
  yAxes=YAxes(
62
+ left=YAxis(format="none"),
63
+ right=YAxis(format="none"),
64
  ),
65
  )
66
  ]
 
72
  dataSource="(example)",
73
  targets=[Target(expr=f"{series_columns[1]}", legendFormat=series_columns[1])],
74
  yAxes=YAxes(
75
+ left=YAxis(format="none"),
76
+ right=YAxis(format="none"),
77
  ),
78
  )
79
  )
 
91
  def render_matplotlib(df: pd.DataFrame, value_cols: list[str]):
92
  """
93
  用 Matplotlib 依據 df 實際畫圖,對應 grafanalib 定義的欄位。
94
+ 回傳多個 matplotlib Figure 物件(給 gr.Gallery 使用)
95
  """
96
  figs = []
97
 
 
98
  fig1 = plt.figure()
99
  plt.plot(df["time"], df[value_cols[0]])
100
  plt.title(f"Time Series - {value_cols[0]}")
 
104
  plt.tight_layout()
105
  figs.append(fig1)
106
 
 
107
  if len(value_cols) > 1:
108
  fig2 = plt.figure()
109
  plt.plot(df["time"], df[value_cols[1]])
 
127
  """
128
  df = load_csv(file)
129
 
 
130
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
131
  if not numeric_cols:
132
  raise ValueError("未找到可繪圖的數值欄位。請在 CSV 中提供至少一個數值欄位(除了 time 以外)。")
133
 
 
134
  chosen = series_choice or numeric_cols[:2]
135
  chosen = [c for c in chosen if c in numeric_cols]
136
  if not chosen:
 
139
  dash_json = build_grafanalib_dashboard(chosen)
140
  figs = render_matplotlib(df, chosen)
141
 
 
142
  dash_json_str = json.dumps(dash_json, ensure_ascii=False, indent=2, default=str)
143
 
 
144
  json_bytes = io.BytesIO(dash_json_str.encode("utf-8"))
145
  json_bytes.name = "dashboard.json"
146
 
 
150
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
151
  gr.Markdown("# grafanalib 生成 Dashboard + Gradio 呈現\n"
152
  "上傳 CSV(需含 `time` 欄)或用示範資料,選擇要畫的數值欄位。\n\n"
153
+ "**說明**:grafanalib 產生 Grafana 的 Dashboard JSON;下方 Matplotlib 圖是 Space 端實際呈現。")
154
 
155
  with gr.Row():
156
  file_in = gr.File(label="上傳 CSV(可空,會用示範資料)", file_types=[".csv"])
 
165
  with gr.Row():
166
  json_file = gr.File(label="下載 dashboard.json")
167
 
 
168
  df_view = gr.Dataframe(label="資料預覽(自動偵測 time + 數值欄位)", wrap=True)
169
 
170
  def probe_columns(file):
 
174
  return gr.CheckboxGroup(choices=numeric_cols), df
175
 
176
  def initial_load():
177
+ """在 App 載入時,用示範資料跑一次完整流程"""
178
  figs, dash_json, json_file_obj, df = pipeline(file=None, series_choice=[])
 
 
179
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
180
  chosen = numeric_cols[:2]
 
181
  return figs, dash_json, json_file_obj, df, gr.CheckboxGroup(choices=numeric_cols, value=chosen)
182
 
183
+ # 頁面載入:直接顯示範例圖與 JSON
 
 
184
  demo.load(
185
  initial_load,
186
  inputs=None,
187
  outputs=[gallery, json_box, json_file, df_view, series_multiselect]
188
  )
189
 
190
+ # 上傳新檔:更新欄位清單與表格預覽
191
  file_in.change(
192
  probe_columns,
193
  inputs=[file_in],
194
  outputs=[series_multiselect, df_view],
195
  )
196
 
197
+ # 產生與繪圖
198
  run_btn.click(
199
  pipeline,
200
  inputs=[file_in, series_multiselect],
 
202
  )
203
 
204
  if __name__ == "__main__":
205
+ demo.launch()