PhilipL commited on
Commit
b3b99e5
·
verified ·
1 Parent(s): 9490421

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +193 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import datetime
3
+ import pandas as pd
4
+
5
+ # --- 模擬資料庫 ---
6
+ # 格式: "帳號": ["密碼", "角色名稱", "顯示姓名"]
7
+ USER_DB = {
8
+ "student": ["1234", "學生", "林宸平"],
9
+ "teacher": ["1234", "老師", "王教授"],
10
+ "admin": ["1234", "行政", "行政人員"],
11
+ "ta": ["1234", "TA", "系統助教"],
12
+ "sysadmin": ["1234", "系統管理員", "管理員"]
13
+ }
14
+
15
+ # 全域變數來儲存 Log
16
+ log_history = []
17
+
18
+ def record_log(ip, cookie, account, role, action, status):
19
+ """將行為記錄到模擬的資料庫中"""
20
+ new_log = {
21
+ "timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
22
+ "ip_address": ip,
23
+ "cookie_id": cookie,
24
+ "account": account,
25
+ "role": role,
26
+ "action": action,
27
+ "status": status
28
+ }
29
+ log_history.append(new_log)
30
+ # 返回最新的 DataFrame 更新畫面
31
+ return pd.DataFrame(log_history)
32
+
33
+ def process_login(ip, cookie, account, password, captcha):
34
+ """處理登入邏輯並記錄 Log"""
35
+ # 預設驗證碼
36
+ valid_captcha = "FEIK"
37
+
38
+ # 檢查帳號是否存在
39
+ if account not in USER_DB:
40
+ df = record_log(ip, cookie, account, "Unknown", "login_attempt", "failed_no_account")
41
+ return gr.update(visible=True), gr.update(visible=False), "帳號不存在!", gr.update(), df
42
+
43
+ role = USER_DB[account][1]
44
+ name = USER_DB[account][2]
45
+
46
+ # 檢查驗證碼
47
+ if captcha.upper() != valid_captcha:
48
+ df = record_log(ip, cookie, account, role, "login_attempt", "failed_captcha")
49
+ return gr.update(visible=True), gr.update(visible=False), "驗證碼錯誤!", gr.update(), df
50
+
51
+ # 檢查密碼
52
+ if USER_DB[account][0] == password:
53
+ welcome_msg = f"**{name}** 歡迎您 | 線上人數: 938"
54
+ df = record_log(ip, cookie, account, role, "login_attempt", "success")
55
+ # 登入成功:隱藏登入區塊,顯示系統區塊
56
+ return gr.update(visible=False), gr.update(visible=True), "", welcome_msg, df
57
+ else:
58
+ df = record_log(ip, cookie, account, role, "login_attempt", "failed_password")
59
+ return gr.update(visible=True), gr.update(visible=False), "密碼錯誤!", gr.update(), df
60
+
61
+ def simulate_click(ip, cookie, account, button_name):
62
+ """模擬登入後點擊系統選單的行為"""
63
+ role = USER_DB.get(account, ["", "Unknown"])[1]
64
+ action_name = f"click_{button_name}"
65
+ df = record_log(ip, cookie, account, role, action_name, "success")
66
+ return df
67
+
68
+ def logout(ip, cookie, account):
69
+ """處理登出並記錄"""
70
+ role = USER_DB.get(account, ["", "Unknown"])[1]
71
+ df = record_log(ip, cookie, account, role, "logout", "success")
72
+ # 登出後清空輸入框
73
+ return gr.update(visible=True), gr.update(visible=False), "", "", "", "", df
74
+
75
+ # --- UI 介面設計 ---
76
+ custom_css = """
77
+ .mock-panel { background-color: #f0f4f8; padding: 15px; border-radius: 8px; border: 1px dashed #7b9bb3; }
78
+ .login-container { max-width: 500px; margin: 0 auto; padding: 20px; }
79
+ .placeholder-box { background-color: #e9ecef; border: 2px dashed #ced4da; text-align: center; color: #6c757d; padding: 40px 0; margin-bottom: 15px; border-radius: 5px; }
80
+ .ntut-title { color: #d9534f; font-size: 22px; font-weight: bold; text-align: center; margin-bottom: 20px;}
81
+ """
82
+
83
+ with gr.Blocks(title="模擬校園入口與Log收集系統", css=custom_css) as demo:
84
+
85
+ with gr.Row():
86
+ # ==========================================
87
+ # 左側:後端 Log 與環境模擬區
88
+ # ==========================================
89
+ with gr.Column(scale=1, elem_classes="mock-panel"):
90
+ gr.Markdown("### ⚙️ 環境變數模擬器\n*(負責控制右側使用者的連線狀態)*")
91
+
92
+ mock_ip = gr.Dropdown(
93
+ label="模擬來源 IP",
94
+ choices=["140.124.71.55 (校內)", "140.124.18.22 (宿舍)", "211.75.2.11 (校外ISP)", "45.33.22.11 (國外異常IP)"],
95
+ value="140.124.71.55 (校內)",
96
+ allow_custom_value=True
97
+ )
98
+ mock_cookie = gr.Textbox(
99
+ label="模擬 Cookie Session ID",
100
+ value="sess_a1b2c3d4e5",
101
+ placeholder="手動輸入或使用預設值"
102
+ )
103
+
104
+ gr.Markdown("---")
105
+ gr.Markdown("### 📊 即時 Log 收集區")
106
+ log_display = gr.Dataframe(
107
+ headers=["timestamp", "ip_address", "cookie_id", "account", "role", "action", "status"],
108
+ datatype=["str", "str", "str", "str", "str", "str", "str"],
109
+ interactive=False,
110
+ wrap=True
111
+ )
112
+
113
+ # ==========================================
114
+ # 右側:正常使用者介面
115
+ # ==========================================
116
+ with gr.Column(scale=2):
117
+
118
+ # --- 狀態 1: 登入畫面 ---
119
+ with gr.Column(visible=True, elem_classes="login-container") as login_view:
120
+ gr.HTML("<div class='placeholder-box'>預留位置:台北科大 Logo 與 e化校園圖片</div>")
121
+
122
+ gr.Markdown("<div class='ntut-title'>校園入口網站 Taipei Tech Portal</div>")
123
+ login_msg = gr.Markdown(value="", visible=True)
124
+
125
+ with gr.Row():
126
+ acc_input = gr.Textbox(label="使用者帳號 (Account)", placeholder="例如: student, admin...")
127
+ with gr.Row():
128
+ pwd_input = gr.Textbox(label="使用者密碼 (Password)", type="password", placeholder="密碼皆為 1234")
129
+
130
+ with gr.Row():
131
+ gr.HTML("<div style='background:#fdfd96; padding:10px; font-weight:bold; letter-spacing: 3px; border:1px solid #ccc; text-align:center;'>F E I K</div>")
132
+ with gr.Row():
133
+ captcha_input = gr.Textbox(label="請輸入驗證碼 (Keyin Code)")
134
+
135
+ login_btn = gr.Button("登入 Login", variant="primary")
136
+ gr.Markdown("*測試帳號:student (學生), teacher (老師), admin (行政), ta (助教), sysadmin (管理員)*")
137
+
138
+ # --- 狀態 2: 登入後儀表板 ---
139
+ with gr.Column(visible=False) as dashboard_view:
140
+ gr.HTML("<div class='placeholder-box'>預留位置:學生學習歷程入口網站橫幅 (Banner)</div>")
141
+
142
+ with gr.Row():
143
+ gr.Markdown("### 資訊系統")
144
+ welcome_text = gr.Markdown(value="", elem_classes="text-right")
145
+ logout_btn = gr.Button("登出", size="sm")
146
+
147
+ gr.Markdown("---")
148
+
149
+ with gr.Accordion("▼ 教務系統", open=True):
150
+ with gr.Row():
151
+ btn_course_en = gr.Button("▶ 課程系統英文版", size="sm")
152
+ btn_course = gr.Button("▶ 課程系統", size="sm")
153
+ btn_select = gr.Button("▶ 新生網路選課系統", size="sm")
154
+
155
+ with gr.Accordion("▼ 學務系統", open=True):
156
+ with gr.Row():
157
+ btn_student_query = gr.Button("▶ 學生查詢專區", size="sm")
158
+ btn_leave = gr.Button("▶ 學生請假系統", size="sm")
159
+
160
+ with gr.Accordion("▼ 總務系統", open=False):
161
+ btn_payment = gr.Button("▶ 線上繳費系統", size="sm")
162
+
163
+ # --- 綁定事件 ---
164
+
165
+ # 登入動作
166
+ login_btn.click(
167
+ fn=process_login,
168
+ inputs=[mock_ip, mock_cookie, acc_input, pwd_input, captcha_input],
169
+ outputs=[login_view, dashboard_view, login_msg, welcome_text, log_display]
170
+ )
171
+
172
+ # 登出動作
173
+ logout_btn.click(
174
+ fn=logout,
175
+ inputs=[mock_ip, mock_cookie, acc_input],
176
+ outputs=[login_view, dashboard_view, acc_input, pwd_input, captcha_input, login_msg, log_display]
177
+ )
178
+
179
+ # 綁定選單點擊動作到 Log 系統
180
+ # 使用迴圈與 default argument (n=name) 確保 lambda 綁定正確的值
181
+ for btn, name in [
182
+ (btn_course_en, "course_en"), (btn_course, "course_system"),
183
+ (btn_select, "course_selection"), (btn_student_query, "student_query"),
184
+ (btn_leave, "leave_system"), (btn_payment, "payment_system")
185
+ ]:
186
+ btn.click(
187
+ fn=lambda ip, c, acc, n=name: simulate_click(ip, c, acc, n),
188
+ inputs=[mock_ip, mock_cookie, acc_input],
189
+ outputs=[log_display]
190
+ )
191
+
192
+ if __name__ == "__main__":
193
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio>=4.0.0
2
+ pandas
3
+ scikit-learn>=1.3.0