BrianChuan010393 commited on
Commit
8fe5bff
·
unverified ·
1 Parent(s): d41d526

Update API for connect

Browse files
Files changed (2) hide show
  1. app.py +141 -6
  2. baseline_logs.csv +2 -2
app.py CHANGED
@@ -2,7 +2,7 @@ import gradio as gr
2
  import datetime
3
  import pandas as pd
4
  import joblib
5
- from sklearn.cluster import HDBSCAN
6
  import numpy as np
7
  import logger_service
8
  import os
@@ -11,6 +11,8 @@ import matplotlib.pyplot as plt
11
  import seaborn as sns
12
  import matplotlib.font_manager as fm
13
  import shutil
 
 
14
 
15
  # --- 🌟 解決 Matplotlib 中文顯示問題 ---
16
  font_path = 'SimHei.ttf'
@@ -200,6 +202,15 @@ def api_get_chart_data(action_name):
200
  "action_code": action_code,
201
  "data": {"normal": normal_data, "abnormal": abnormal_data}
202
  }
 
 
 
 
 
 
 
 
 
203
  # =====================================================================
204
 
205
 
@@ -244,7 +255,111 @@ def get_dashboard_html():
244
  </div>
245
  """
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  def get_plot_data(selected_key):
 
248
  if not selected_key or not AI_READY: return None
249
  action_code = ACTION_MAP.get(selected_key)
250
  try:
@@ -419,7 +534,7 @@ def check_anomaly(ip, account, action, log_time):
419
  fit_X = np.vstack((other_history, relevant_history, new_data_point))
420
  strictness = max(2, int(2 + (ow / 100.0) * 4))
421
 
422
- model = HDBSCAN(min_cluster_size=strictness, min_samples=2)
423
  clusters = model.fit_predict(fit_X)
424
 
425
  if clusters[-1] == -1: return "⚠️異常_作息或行為不符"
@@ -668,13 +783,12 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
668
 
669
  demo.load(fn=timer_update, inputs=[action_dropdown], outputs=[stat_dashboard_display, action_plot])
670
  dashboard_timer.tick(fn=timer_update, inputs=[action_dropdown], outputs=[stat_dashboard_display, action_plot])
671
- action_dropdown.change(fn=get_plot_data, inputs=[action_dropdown], outputs=[action_plot])
672
  time_mode.change(fn=toggle_time_input, inputs=time_mode, outputs=custom_time_input)
673
  btn_explain_log.click(fn=explain_abnormal_log, inputs=[abnormal_log_input], outputs=[ai_explanation_output])
674
 
675
- weight_inputs = [overall_weight, time_weight, ip_weight, device_weight, geo_weight, freq_weight]
676
- for w_input in weight_inputs:
677
- w_input.change(fn=update_system_weights, inputs=weight_inputs, outputs=[weights_status])
678
 
679
  update_outputs = [main_router, role_tabs, login_msg, welcome_text, store_df_all, store_df_abn, abnormal_log_input]
680
 
@@ -735,6 +849,27 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
735
  api_chart_in = gr.Textbox()
736
  api_chart_out = gr.JSON()
737
  api_chart_btn.click(fn=api_get_chart_data, inputs=[api_chart_in], outputs=[api_chart_out], api_name="get_chart_data")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
738
  # =====================================================================
739
 
740
  if __name__ == "__main__":
 
2
  import datetime
3
  import pandas as pd
4
  import joblib
5
+ import hdbscan
6
  import numpy as np
7
  import logger_service
8
  import os
 
11
  import seaborn as sns
12
  import matplotlib.font_manager as fm
13
  import shutil
14
+ import io
15
+ import base64
16
 
17
  # --- 🌟 解決 Matplotlib 中文顯示問題 ---
18
  font_path = 'SimHei.ttf'
 
202
  "action_code": action_code,
203
  "data": {"normal": normal_data, "abnormal": abnormal_data}
204
  }
205
+
206
+ def api_get_abnormal_logs():
207
+ """取得所有異常 Log"""
208
+ abnormal_logs = [log for log in log_history if "異常" in str(log.get("status", "")) or "錯誤" in str(log.get("status", "")) or "受限" in str(log.get("status", ""))]
209
+ return {
210
+ "status": "success",
211
+ "count": len(abnormal_logs),
212
+ "data": abnormal_logs
213
+ }
214
  # =====================================================================
215
 
216
 
 
255
  </div>
256
  """
257
 
258
+ def get_plot_data_for_api(selected_key):
259
+ """API專用版本,返回Base64圖片字符串"""
260
+ if not selected_key or not AI_READY: return None
261
+ action_code = ACTION_MAP.get(selected_key)
262
+ try:
263
+ plt.close('all')
264
+ df_hist = baseline_df[baseline_df['action'] == action_code].copy()
265
+
266
+ df_live = pd.DataFrame(log_history)
267
+ if not df_live.empty:
268
+ df_live['timestamp'] = pd.to_datetime(df_live['timestamp'], errors='coerce')
269
+ df_live = df_live.dropna(subset=['timestamp'])
270
+ df_live = df_live[df_live['action'] == action_code].copy()
271
+
272
+ if not df_live.empty:
273
+ df_combined = pd.concat([df_hist, df_live], ignore_index=True)
274
+ df_combined = df_combined.drop_duplicates(subset=['timestamp', 'account', 'action'])
275
+ else:
276
+ df_combined = df_hist.copy()
277
+
278
+ if df_combined.empty:
279
+ fig, ax = plt.subplots(figsize=(12, 6))
280
+ if os.path.exists(font_path): plt.rc('font', family='SimHei')
281
+ ax.text(0.5, 0.5, f"【{selected_key}】目前尚無資料", ha='center', va='center', fontsize=15)
282
+ # 將圖片轉換為 Base64
283
+ buf = io.BytesIO()
284
+ fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
285
+ buf.seek(0)
286
+ img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
287
+ buf.close()
288
+ plt.close(fig)
289
+ return f"data:image/png;base64,{img_base64}"
290
+
291
+ df_combined['month'] = df_combined['timestamp'].dt.month
292
+ df_combined['time_of_day'] = df_combined['timestamp'].dt.hour + df_combined['timestamp'].dt.minute / 60.0
293
+
294
+ is_abnormal = df_combined['status'].astype(str).str.contains("異常", na=False)
295
+ df_normal = df_combined[~is_abnormal]
296
+ df_abnormal = df_combined[is_abnormal]
297
+
298
+ fig, ax = plt.subplots(figsize=(12, 6))
299
+ sns.set_theme(style="whitegrid")
300
+ if os.path.exists(font_path): plt.rc('font', family='SimHei')
301
+ plt.rcParams['axes.unicode_minus'] = False
302
+
303
+ has_data = False
304
+ months_order = list(range(1, 13))
305
+
306
+ if not df_normal.empty:
307
+ has_data = True
308
+ sns.stripplot(data=df_normal, x='month', y='time_of_day', jitter=0.3, alpha=0.4, size=5, color='#10b981', order=months_order, ax=ax, label='正常資料')
309
+
310
+ if not df_abnormal.empty:
311
+ has_data = True
312
+ sns.stripplot(data=df_abnormal, x='month', y='time_of_day', jitter=0.3, alpha=0.9, size=9, color='#ef4444', marker='X', order=months_order, ax=ax, label='異常資料')
313
+
314
+ if not has_data:
315
+ ax.text(0.5, 0.5, f"【{selected_key}】目前尚無資料", ha='center', va='center', fontsize=15)
316
+ # 將圖片轉換為 Base64
317
+ buf = io.BytesIO()
318
+ fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
319
+ buf.seek(0)
320
+ img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
321
+ buf.close()
322
+ plt.close(fig)
323
+ return f"data:image/png;base64,{img_base64}"
324
+
325
+ ax.set_xlabel('月份 (1 ~ 12月)', fontsize=12)
326
+ ax.set_ylabel('時間 (24小時制)', fontsize=12)
327
+ ax.set_yticks(np.arange(0, 25, 2))
328
+ ax.set_ylim(24.5, -0.5)
329
+
330
+ handles, labels = ax.get_legend_handles_labels()
331
+ by_label = dict(zip(labels, handles))
332
+ if by_label: ax.legend(by_label.values(), by_label.keys(), loc='upper right', bbox_to_anchor=(1.15, 1))
333
+
334
+ plt.tight_layout()
335
+
336
+ # 將圖片轉換為 Base64
337
+ buf = io.BytesIO()
338
+ fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
339
+ buf.seek(0)
340
+ img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
341
+ buf.close()
342
+ plt.close(fig)
343
+ return f"data:image/png;base64,{img_base64}"
344
+ except Exception as e:
345
+ print(f"繪圖失敗: {e}")
346
+ # 返回一個簡單的錯誤圖片
347
+ fig, ax = plt.subplots(figsize=(8, 4))
348
+ ax.text(0.5, 0.5, f"圖表生成失敗: {str(e)}", ha='center', va='center', fontsize=12, color='red')
349
+ ax.set_xlim(0, 1)
350
+ ax.set_ylim(0, 1)
351
+ ax.axis('off')
352
+
353
+ buf = io.BytesIO()
354
+ fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
355
+ buf.seek(0)
356
+ img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
357
+ buf.close()
358
+ plt.close(fig)
359
+ return f"data:image/png;base64,{img_base64}"
360
+
361
  def get_plot_data(selected_key):
362
+ """給Gradio界面使用,返回Figure對象"""
363
  if not selected_key or not AI_READY: return None
364
  action_code = ACTION_MAP.get(selected_key)
365
  try:
 
534
  fit_X = np.vstack((other_history, relevant_history, new_data_point))
535
  strictness = max(2, int(2 + (ow / 100.0) * 4))
536
 
537
+ model = hdbscan.HDBSCAN(min_cluster_size=strictness, min_samples=2)
538
  clusters = model.fit_predict(fit_X)
539
 
540
  if clusters[-1] == -1: return "⚠️異常_作息或行為不符"
 
783
 
784
  demo.load(fn=timer_update, inputs=[action_dropdown], outputs=[stat_dashboard_display, action_plot])
785
  dashboard_timer.tick(fn=timer_update, inputs=[action_dropdown], outputs=[stat_dashboard_display, action_plot])
786
+ action_dropdown.change(fn=get_plot_data, inputs=[action_dropdown], outputs=[action_plot], api_name="getPlotData")
787
  time_mode.change(fn=toggle_time_input, inputs=time_mode, outputs=custom_time_input)
788
  btn_explain_log.click(fn=explain_abnormal_log, inputs=[abnormal_log_input], outputs=[ai_explanation_output])
789
 
790
+ # weight_inputs = [overall_weight, time_weight, ip_weight, device_weight, geo_weight, freq_weight]
791
+ # 移除所有 UI 欄位的 update_system_weights 綁定,僅保留 API 專用端點
 
792
 
793
  update_outputs = [main_router, role_tabs, login_msg, welcome_text, store_df_all, store_df_abn, abnormal_log_input]
794
 
 
849
  api_chart_in = gr.Textbox()
850
  api_chart_out = gr.JSON()
851
  api_chart_btn.click(fn=api_get_chart_data, inputs=[api_chart_in], outputs=[api_chart_out], api_name="get_chart_data")
852
+
853
+ api_abnormal_logs_btn = gr.Button("API_Abnormal_Logs")
854
+ api_abnormal_logs_out = gr.JSON()
855
+ api_abnormal_logs_btn.click(fn=api_get_abnormal_logs, inputs=[], outputs=[api_abnormal_logs_out], api_name="get_abnormal_logs")
856
+
857
+ api_plot_btn = gr.Button("API_Plot")
858
+ api_plot_in = gr.Textbox()
859
+ api_plot_out = gr.Textbox()
860
+ api_plot_btn.click(fn=get_plot_data_for_api, inputs=[api_plot_in], outputs=[api_plot_out], api_name="get_plot_data_api")
861
+
862
+ # System weights update API endpoint
863
+ api_update_weights_btn = gr.Button("API_Update_Weights")
864
+ api_update_weights_ow = gr.Number(label="Overall Weight")
865
+ api_update_weights_tw = gr.Number(label="Time Weight")
866
+ api_update_weights_iw = gr.Number(label="IP Weight")
867
+ api_update_weights_dw = gr.Number(label="Device Weight")
868
+ api_update_weights_gw = gr.Number(label="Geo Weight")
869
+ api_update_weights_fw = gr.Number(label="Frequency Weight")
870
+ api_update_weights_in = [api_update_weights_ow, api_update_weights_tw, api_update_weights_iw, api_update_weights_dw, api_update_weights_gw, api_update_weights_fw]
871
+ api_update_weights_out = gr.Textbox()
872
+ api_update_weights_btn.click(fn=update_system_weights, inputs=api_update_weights_in, outputs=[api_update_weights_out], api_name="update_system_weights")
873
  # =====================================================================
874
 
875
  if __name__ == "__main__":
baseline_logs.csv CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5ea761decb0a310671beac39fffa37d7e19dde48587d70f29da6f2c68365518f
3
- size 4590684
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:39ee1be5bfccd9325c4ea6fcfde49df1a29ee805a9e1affdf485483f839e6683
3
+ size 4596753