amychangster commited on
Commit
60345ae
·
verified ·
1 Parent(s): 1ae6917

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +220 -189
app.py CHANGED
@@ -1,15 +1,22 @@
1
- # -*- coding: utf-8 -*-
2
- """DASS心理模型(Q12).ipynb
3
 
4
- Automatically generated by Colab.
5
 
6
- Original file is located at
7
- https://colab.research.google.com/drive/19ATyW5Lb692QV2Gk2I0rlsbeSQNwEDnQ
 
 
 
 
 
 
 
 
8
 
9
- 建立環境
10
- """
11
 
12
- # !pip install gradio
 
13
 
14
  import pandas as pd
15
  import matplotlib.pyplot as plt
@@ -36,7 +43,11 @@ from AutoPreprocess import AutoPreprocess
36
  from google.oauth2.service_account import Credentials
37
  from datetime import datetime, timezone, timedelta
38
 
39
- """Gradio 使用者介面"""
 
 
 
 
40
 
41
  # 載入模型
42
  import pickle
@@ -46,14 +57,78 @@ with open(model_path, "rb") as f:
46
  model = pickle.load(f)
47
  model
48
 
49
- """定義歷史紀錄功能"""
50
 
51
- def update_history(current_result_1, current_result_2, out_error, history_list):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  """
53
  current_result_1 & 2: 來自 predict_risk 的兩個回傳值 (HTML 字串)
54
  history_list: 來自 gr.State 的現有紀錄列表
55
  """
56
- tw_timezone = timezone(timedelta(hours=8))
57
  # 獲取當前時間,格式為:2023-10-27 14:30:05
58
  now = datetime.now(tw_timezone).strftime("%Y-%m-%d %H:%M:%S")
59
 
@@ -95,183 +170,120 @@ def update_history(current_result_1, current_result_2, out_error, history_list):
95
 
96
  return combined_html, history_list
97
 
98
- """定義儲存測試資料的功能
99
-
100
- Google Sheet 連線
101
- """
102
-
103
- import os
104
- import json
105
- from datetime import datetime, timezone, timedelta
106
- import gspread
107
- from google.oauth2.service_account import Credentials
108
-
109
- # 1. 設定 Google Sheets 存取權限
110
- scope = ['https://www.googleapis.com/auth/spreadsheets',
111
- 'https://www.googleapis.com/auth/drive']
112
 
113
- # ... 之前的 import ...
114
 
115
- sheet = None # 預設
116
 
117
- # 寫一個連線函式
118
- def init_gspread():
119
- global sheet
120
- google_json = os.environ.get("DASS_JSON")
121
- if not google_json:
122
- print("⚠️ 找不到 DASS_JSON")
123
- return
124
- try:
125
- info = json.loads(google_json)
126
- creds = Credentials.from_service_account_info(info, scopes=scope)
127
- client = gspread.authorize(creds)
128
- # 再次確認試算表名稱是否完全正確
129
- sheet = client.open_by_key("1SPMKe5uOK7EMukB4udyEFKCibpJRNGCQdOQEUVJAgN4").sheet1
130
- print("✅ Google Sheets 初始化成功")
131
- except Exception as e:
132
- print(f"❌ 初始化失敗: {e}")
133
-
134
- def save_to_google_sheets(inputs, a_score, d_score, s_score, t_score, score):
135
-
136
- global sheet
137
-
138
- if sheet is None:
139
- print("⚠️ 試算表未連線,跳過儲存步驟")
140
- return
141
-
142
- try:
143
-
144
- # 設定台灣時區
145
- tw_timezone = timezone(timedelta(hours=8))
146
- # 1. 拆分資料:前 3 個是基本資料,後面剩下的 (*rest) 是 12 題答案
147
- user_info = inputs[:3] # 取得前三個:姓名, 年齡, 性別
148
- q_answers = inputs[3:] # 取得剩下的 12 題
149
- now = datetime.now(tw_timezone).strftime("%Y-%m-%d %H:%M:%S")
150
 
151
- # 2. 準備要儲存資料字典
152
- row_to_add = [
153
- now, # 欄位 A: 測試
154
- user_info[0], # 欄位 B: 性別
155
- user_info[1], # 欄位 C: 年齡
156
- user_info[2], # 欄位 D: 家庭人數
157
- a_score, # 欄位 E: 焦慮分數
158
- d_score, # 欄位 F: 憂鬱分數
159
- s_score, # 欄位 G: 壓力分數
160
- t_score, # 欄位 H: 總體分數
161
- score # 欄位 I: 整體程度 (標籤)
162
- ]
163
 
164
- row_to_add.extend(q_answers) # 加入 Q1-Q12 (J欄以後)
165
 
166
- # 4. 追加到算表最後一行
167
- sheet.append_row(row_to_add)
168
- print(f"✨ 資料已成功存入試算表: {now}")
169
 
170
- except Exception as e:
171
- print(f"❌ 寫入資料時發生錯誤: {e}")
172
 
173
- """定義重新測驗功能"""
174
 
175
- # 清空函數:回傳與輸入組件數量相同的 None (15個:gen, age, family + 12個問題)
176
- def clear_all():
177
- # 15個輸入(gen, age, family, q1~q12) + 2個即時結果
178
- return [None] * 15 + ["", ""]
179
-
180
- """定義主要測試功能"""
181
 
182
  def predict_risk(gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12):
183
  inputs = [gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12]
184
- result_score = ""
185
- label_html = ""
186
- error_message = ""
187
 
188
- # 檢查是否有任何一個選項是 None (未)
189
  if any(v is None or v == "" for v in inputs):
190
- error_message = '<div style="color: red; font-weight: bold;">⚠️測驗載入有誤:請確保每一題都已填答或查看填答格式是否正確。</div>'
191
- return result_score, label_html, error_message
192
-
193
- try:
194
- # 1. 跑進度條 (需確保函式參數有 progress=gr.Progress())
195
- progress = gr.Progress()
196
- progress(0, desc="模型計算中...")
197
-
198
- # 2. 將 12 個輸入整理成模型認得的 DataFrame
199
- # 欄位名稱必須與訓練時完全相同
200
- cols = ["gender", "age", "familysize", "Q2A", "Q4A", "Q19A", "Q20A", "Q28A", "Q21A", "Q26A", "Q37A", "Q42A", "Q11A", "Q12A", "Q27A"]
201
- input_df = pd.DataFrame([inputs], columns=cols)
202
-
203
- progress(0.5, desc="正在分析數據...")
204
- time.sleep(0.5) # 模擬運算時間
205
-
206
- # 3. 使用模型 model 進行預測
207
- score = model.predict(input_df)[0]
208
-
209
- # 4. 定義風險標籤
210
- risk_map = {
211
- 0: ("低度風險", "#91cd92"),
212
- 1: ("中度風險", "#f59e0b"),
213
- 2: ("高度風險", "#ef4444")
214
- }
215
- label, color = risk_map.get(score, ("計算結果有誤,請重新測試。", "#ef4444"))
216
-
217
-
218
- # 定義類別分數條
219
- a_score = sum([q1, q2, q3, q4, q5])
220
- d_score = sum([q6, q7, q8, q9])
221
- s_score = sum([q10, q11, q12])
222
- t_score = a_score + d_score + s_score
223
- max_val = 36
224
-
225
- def make_bar(label, score, max_val, color):
226
- percent = (score / max_val) * 100
227
- return f"""
228
- <div style="margin-bottom: 10px;">
229
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
230
- <span style="font-weight: bold;">{label}</span>
231
- </div>
232
- <div style="background-color: #e0e0e0; border-radius: 10px; height: 12px; width: 100%;">
233
- <div style="background-color: {color}; width: {percent}%; height: 100%; border-radius: 10px;"></div>
234
- </div>
 
 
 
 
 
235
  </div>
236
- """
237
-
238
- # 5. 準備回傳內容
239
- # 總分與風險標籤
240
- result_score = f"""
241
- <div style="text-align: center; font-family: sans-serif;">
242
- <h2 style="color: #313230;">您的預測結果為</h2>
243
- <h1 style="font-size: 60px; color: {color}; margin: 0;">
244
- {label}
245
- </h1>
246
- <h1 style="font-size: 20px; color: #bbbbc2; margin: 0;">
247
- {t_score}/36
248
- </h1>
249
  </div>
250
  """
251
 
252
- # 類別分數條
253
- label_html = f"""
254
- <div style="padding: 20px; background: white; border-radius: 10px; border: 1px solid #ddd;">
255
- <h2 style="color: #313230;margin-top: 0; margin-bottom: 15px;">各面向之比重</h2>
256
- {make_bar("焦慮 (Anxiety)", a_score, max_val, "#fccb42")}
257
- {make_bar("憂鬱 (Depression)", d_score, max_val, "#6dc8fe")}
258
- {make_bar("壓力 (Stress)", s_score, max_val, "#fb6d6d")}
259
- </div>
260
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
- # 儲存測試資料
263
- try:
264
- save_to_google_sheets(inputs, a_score, d_score, s_score, t_score, score)
265
- except Exception as sheet_err:
266
- print(f"Sheet Error: {sheet_err}")
267
 
 
268
 
269
- progress(1.0, desc="計算完成!")
270
- return result_score, label_html, error_message
271
 
272
- except Exception as e:
273
- error_message = f"<div style='color: red;'>系統錯誤: {str(e)}</div>"
274
- return "", "", error_message
275
 
276
  # 設定主題色
277
 
@@ -282,11 +294,16 @@ theme = gr.themes.Default(
282
  body_background_fill="#fffbeb"
283
  )
284
 
 
 
 
 
285
  # 線上主題調色器
286
  # gr.themes.builder()
287
 
288
- # 執行連線
289
- init_gspread()
 
290
 
291
  # 介面編排
292
 
@@ -388,18 +405,17 @@ with gr.Blocks(theme=theme, css=custom_css) as demo:
388
  q12 = gr.Radio([("從不", 0), ("偶爾", 1), ("經常", 2), ("總是", 3)],
389
  label="Q12.我發現自己非常易怒(容易焦躁)。")
390
 
391
- # 錯誤訊息顯示區 (放在按鈕上方)
392
- out_error = gr.HTML()
393
 
394
- # 確認送出、重新測驗按鈕
395
  sub_button = gr.Button("確認送出", elem_id="my_green_btn")
396
- btn_reset = gr.Button("重新測驗", elem_id="my_white_btn")
 
 
397
 
398
  # 輸出測試結果
399
  with gr.Row():
400
- out_html = gr.HTML()
401
- out_label = gr.HTML()
402
-
403
 
404
  # --- 新增:歷史紀錄呈現區域 ---
405
  with gr.Accordion("查看歷史紀錄", open=False, elem_id="history_panel"):
@@ -408,28 +424,43 @@ with gr.Blocks(theme=theme, css=custom_css) as demo:
408
 
409
  # 按鈕設定
410
  # 1. 確認送出
411
- sub_button.click(
412
- fn=predict_risk,
413
- inputs= [gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12],
414
- outputs= [out_html, out_label, out_error]
415
- ).then(
416
- fn=update_history,
417
- inputs=[out_html, out_label, out_error, history_state],
418
- outputs=[history_display, history_state]
419
- )
420
 
421
  # 2. 重新測驗 (清空所有輸入與輸出)
422
  # 注意:outputs 必須包含所有輸入的組件
423
  btn_reset.click(
424
- fn=lambda: [None]*15 + ["", "", ""],
425
  inputs=None,
426
- outputs=[gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12, out_html , out_label, out_error]
427
  )
428
 
 
 
 
 
429
  gr.Markdown("## 免責聲明")
430
  gr.Markdown("""本測驗結果僅供參考,非屬正規醫療檢驗範疇。
431
  若對於自身狀況有任何疑慮,敬請尋求正規專業醫療協助!♡第四組關心您♡""")
432
 
433
- demo.launch(share=True)
434
 
435
- # 如需免費永久托管,需在終端機模式執行「gradio deploy」部署到 Hugging Face Spaces。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # coding: utf-8
3
 
4
+ # 建立環境
5
 
6
+ # In[50]:
7
+
8
+
9
+ # get_ipython().system('pip install gradio')
10
+
11
+
12
+ # In[51]:
13
+
14
+
15
+ # get_ipython().system('pip install gspread google-auth')
16
 
 
 
17
 
18
+ # In[52]:
19
+
20
 
21
  import pandas as pd
22
  import matplotlib.pyplot as plt
 
43
  from google.oauth2.service_account import Credentials
44
  from datetime import datetime, timezone, timedelta
45
 
46
+
47
+ # Gradio 使用者介面
48
+
49
+ # In[53]:
50
+
51
 
52
  # 載入模型
53
  import pickle
 
57
  model = pickle.load(f)
58
  model
59
 
 
60
 
61
+ # 定義儲存測試資料的功能
62
+
63
+ # In[54]:
64
+
65
+
66
+ import os
67
+ import json
68
+ from datetime import datetime, timezone, timedelta
69
+ import gspread
70
+ from google.oauth2.service_account import Credentials
71
+
72
+ # 設定台灣時區
73
+ tw_timezone = timezone(timedelta(hours=8))
74
+
75
+ def save_to_google_sheets(inputs, a_score, d_score, s_score, t_score, score):
76
+
77
+ # 1. 設定 Google Sheets 存取權限
78
+ scope = ['https://www.googleapis.com/auth/spreadsheets',
79
+ 'https://www.googleapis.com/auth/drive']
80
+
81
+ # 設定Secret Variables(藏金鑰)
82
+ google_json = os.environ.get("DASS_JSON")
83
+ info = json.loads(google_json)
84
+ creds = Credentials.from_service_account_info(info, scopes=scope)
85
+ client = gspread.authorize(creds)
86
+
87
+ # 2. 開啟指定名稱的試算表 (確保已分享權限給 service account)
88
+ sheet = client.open("DASS使用者測試資料").sheet1 # 存於檔案的第一張工作表
89
+
90
+
91
+ # 1. 拆分資料:前 3 個是基本資料,後面剩下的 (*rest) 是 12 題答案
92
+ user_info = inputs[:3] # 取得前三個:姓名, 年齡, 性別
93
+ q_answers = inputs[3:] # 取得剩下的 12 題
94
+ now = datetime.now(tw_timezone).strftime("%Y-%m-%d %H:%M:%S")
95
+
96
+ # 2. 準備要儲存的資料字典
97
+ row_to_add = [
98
+ now, # 欄位 A: 測試時間
99
+ user_info[0], # 欄位 B: 性別
100
+ user_info[1], # 欄位 C: 年齡
101
+ user_info[2], # 欄位 D: 家庭人數
102
+ a_score, # 欄位 E: 焦慮分數
103
+ d_score, # 欄位 F: 憂鬱分數
104
+ s_score, # 欄位 G: 壓力分數
105
+ t_score, # 欄位 H: 總體分數
106
+ score # 欄位 I: 整體程度 (標籤)
107
+ ]
108
+
109
+ row_to_add.extend(q_answers) # 加入 Q1-Q12 (J欄以後)
110
+
111
+ # 4. 追加到試算表最後一行
112
+
113
+ def to_py(v):
114
+ return v.item() if hasattr(v, "item") else v
115
+
116
+ row_to_add = [to_py(x) for x in row_to_add]
117
+
118
+ sheet.append_row(row_to_add)
119
+
120
+
121
+ # 定義歷史紀錄功能
122
+
123
+ # In[56]:
124
+
125
+
126
+ def update_history(current_result_1, current_result_2, history_list):
127
  """
128
  current_result_1 & 2: 來自 predict_risk 的兩個回傳值 (HTML 字串)
129
  history_list: 來自 gr.State 的現有紀錄列表
130
  """
131
+
132
  # 獲取當前時間,格式為:2023-10-27 14:30:05
133
  now = datetime.now(tw_timezone).strftime("%Y-%m-%d %H:%M:%S")
134
 
 
170
 
171
  return combined_html, history_list
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
+ # 定義重新測驗功能
175
 
176
+ # In[57]:
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
+ # 清空函數:回傳與輸入組件數量相同 None (15個:gen, age, family + 12個問題)
180
+ def clear_all():
181
+ # 15個輸入(gen, age, family, q1~q12) + 2個即結果 + 1個歷史面板
182
+ return [None] * 15 + ["", ""]
 
 
 
 
 
 
 
 
183
 
 
184
 
185
+ # 定義主要測功能
 
 
186
 
187
+ # In[58]:
 
188
 
 
189
 
190
+ # 定義預測功能
 
 
 
 
 
191
 
192
  def predict_risk(gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12):
193
  inputs = [gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12]
 
 
 
194
 
195
+ # 檢查是否有任何一個選項是 None (未)
196
  if any(v is None or v == "" for v in inputs):
197
+ # 觸發彈出視窗
198
+ raise gr.Error("⚠️測驗載入有誤:請確保每一題都已填答或查看填答格式是否正確。")
199
+
200
+ # 1. 跑進度條 (需確保函式參數有 progress=gr.Progress())
201
+ progress = gr.Progress()
202
+ progress(0, desc="模型計算中...")
203
+
204
+ # 2. 將 12 個輸入整理成模型認得的 DataFrame
205
+ # 欄位名稱必須與訓練時完全相同
206
+ cols = ["gender", "age", "familysize", "Q2A", "Q4A", "Q19A", "Q20A", "Q28A", "Q21A", "Q26A", "Q37A", "Q42A", "Q11A", "Q12A", "Q27A"
207
+ ]
208
+ input_df = pd.DataFrame([[gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12]], columns=cols)
209
+
210
+ progress(0.5, desc="正在分析數據...")
211
+ time.sleep(0.5) # 模擬運算時間
212
+
213
+ # 3. 使用模型 model 進行預測
214
+ score = model.predict(input_df)[0]
215
+ progress(1.0, desc="計算完成!")
216
+
217
+
218
+ # 4. 定義風險標籤
219
+ if score == 0:
220
+ label = "低���風險"
221
+ color = "#91cd92" # 綠色
222
+ elif score == 1:
223
+ label = "中度風險"
224
+ color = "#f59e0b" # 橘色
225
+ elif score == 2:
226
+ label = "高度風險"
227
+ color = "#ef4444" # 紅色
228
+ else:
229
+ label = "計算結果有誤,請重新測試。"
230
+
231
+ # 定義類別分數條
232
+ a_score = (q1 + q2 + q3 + q4 + q5)
233
+ d_score = (q6 + q7 + q8 + q9)
234
+ s_score = (q10 + q11 + q12)
235
+ t_score = a_score + d_score + s_score
236
+ max_val = 36
237
+
238
+ def make_bar(label, score, max_val, color):
239
+ percent = (score / max_val) * 100
240
+ return f"""
241
+ <div style="margin-bottom: 10px;">
242
+ <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
243
+ <span style="font-weight: bold;">{label}</span>
244
+ </div>
245
+ <div style="background-color: #e0e0e0; border-radius: 10px; height: 12px; width: 100%;">
246
+ <div style="background-color: {color}; width: {percent}%; height: 100%; border-radius: 10px;"></div>
247
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  </div>
249
  """
250
 
251
+ # 5. 準備回傳內容
252
+ # 總分與風險標籤
253
+ result_score = f"""
254
+ <div style="text-align: center; font-family: sans-serif;">
255
+ <h2 style="color: #313230;">您的預測結果為</h2>
256
+ <h1 style="font-size: 60px; color: {color}; margin: 0;">
257
+ {label}
258
+ </h1>
259
+ <h1 style="font-size: 20px; color: #bbbbc2; margin: 0;">
260
+ {t_score}/36
261
+ </h1>
262
+ </div>
263
+ """
264
+
265
+ # 類別分數條
266
+ label_html = f"""
267
+ <div style="padding: 20px; background: white; border-radius: 10px; border: 1px solid #ddd;">
268
+ <h2 style="color: #313230;margin-top: 0; margin-bottom: 15px;">各面向之比重</h2>
269
+ {make_bar("焦慮 (Anxiety)", a_score, max_val, "#fccb42")}
270
+ {make_bar("憂鬱 (Depression)", d_score, max_val, "#6dc8fe")}
271
+ {make_bar("壓力 (Stress)", s_score, max_val, "#fb6d6d")}
272
+ </div>
273
+ """
274
+
275
+ # 儲存測試資料
276
+ save_to_google_sheets(inputs, a_score, d_score, s_score, t_score, score)
277
+
278
 
279
+ progress(1.0, desc="完成")
 
 
 
 
280
 
281
+ return result_score, label_html
282
 
 
 
283
 
284
+
285
+ # In[59]:
286
+
287
 
288
  # 設定主題色
289
 
 
294
  body_background_fill="#fffbeb"
295
  )
296
 
297
+
298
+ # In[60]:
299
+
300
+
301
  # 線上主題調色器
302
  # gr.themes.builder()
303
 
304
+
305
+ # In[61]:
306
+
307
 
308
  # 介面編排
309
 
 
405
  q12 = gr.Radio([("從不", 0), ("偶爾", 1), ("經常", 2), ("總是", 3)],
406
  label="Q12.我發現自己非常易怒(容易焦躁)。")
407
 
 
 
408
 
409
+ # 確認送出按鈕
410
  sub_button = gr.Button("確認送出", elem_id="my_green_btn")
411
+ # 重新測驗按鈕
412
+ with gr.Row():
413
+ btn_reset = gr.Button("重新測驗", elem_id="my_white_btn")
414
 
415
  # 輸出測試結果
416
  with gr.Row():
417
+ out_html = gr.HTML()
418
+ out_label = gr.HTML()
 
419
 
420
  # --- 新增:歷史紀錄呈現區域 ---
421
  with gr.Accordion("查看歷史紀錄", open=False, elem_id="history_panel"):
 
424
 
425
  # 按鈕設定
426
  # 1. 確認送出
427
+ sub_button.click(fn=predict_risk,
428
+ inputs= [gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12],
429
+ outputs= [out_html, out_label]
430
+ ).then(
431
+ fn=update_history,
432
+ inputs=[out_html, out_label, history_state],
433
+ outputs=[history_display, history_state])
 
 
434
 
435
  # 2. 重新測驗 (清空所有輸入與輸出)
436
  # 注意:outputs 必須包含所有輸入的組件
437
  btn_reset.click(
438
+ fn=lambda: [None]*15 + ["", ""], # 只清空輸入與目前的顯示結果
439
  inputs=None,
440
+ outputs=[gen, age, family, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12, out_html , out_label]
441
  )
442
 
443
+
444
+
445
+
446
+
447
  gr.Markdown("## 免責聲明")
448
  gr.Markdown("""本測驗結果僅供參考,非屬正規醫療檢驗範疇。
449
  若對於自身狀況有任何疑慮,敬請尋求正規專業醫療協助!♡第四組關心您♡""")
450
 
 
451
 
452
+ # In[ ]:
453
+
454
+
455
+ demo.launch()
456
+
457
+
458
+ # In[63]:
459
+
460
+
461
+ # 如需免費永久托管,需在終端機模式執行「gradio deploy」部署到 Hugging Face Spaces。
462
+
463
+
464
+
465
+
466
+