DeepLearning101 commited on
Commit
d1f6865
·
verified ·
1 Parent(s): b23981f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -18
app.py CHANGED
@@ -4,10 +4,13 @@ import os
4
  import pandas as pd
5
  from dotenv import load_dotenv
6
  from services import GeminiService
 
7
 
8
  # Load Env
9
  load_dotenv()
10
  SAVE_FILE = os.getenv("SAVE_FILE_NAME", "saved_professors.json")
 
 
11
 
12
  # Init Service
13
  try:
@@ -22,24 +25,55 @@ def get_key(p):
22
  return f"{p['name']}-{p['university']}"
23
 
24
  def load_data():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  if os.path.exists(SAVE_FILE):
26
  try:
27
  with open(SAVE_FILE, 'r', encoding='utf-8') as f:
28
- return json.load(f)
29
  except:
30
- return []
31
- return []
32
 
33
  def save_data(data):
 
34
  try:
35
  with open(SAVE_FILE, 'w', encoding='utf-8') as f:
36
  json.dump(data, f, ensure_ascii=False, indent=2)
37
  except Exception as e:
38
  print(f"Save Error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
  def format_df(source_list, saved_list):
41
  if not source_list:
42
- # 如果沒有來源資料,回傳空表格
43
  return pd.DataFrame(columns=["狀態", "姓名", "大學", "系所", "標籤"])
44
 
45
  if saved_list is None:
@@ -82,7 +116,6 @@ def search_professors(query, current_saved):
82
 
83
  try:
84
  results = gemini_service.search_professors(query)
85
- # 搜尋時,顯示搜尋結果
86
  return format_df(results, current_saved), results, gr.update(visible=True)
87
  except Exception as e:
88
  raise gr.Error(f"搜尋失敗: {e}")
@@ -119,14 +152,12 @@ def select_professor_from_df(evt: gr.SelectData, search_results, saved_data, vie
119
 
120
  details_md = ""
121
 
122
- # Check if details are cached
123
  if current_prof.get('details') and len(current_prof.get('details')) > 10:
124
  details_md = current_prof['details']
125
  if not saved_prof:
126
  saved_data.insert(0, current_prof)
127
  save_data(saved_data)
128
  else:
129
- # Call API
130
  gr.Info(f"正在調查 {current_prof['name']}...")
131
  try:
132
  res = gemini_service.get_professor_details(current_prof)
@@ -266,25 +297,19 @@ def remove_prof(selected_prof, saved_data, view_mode, search_results):
266
 
267
  def toggle_view(mode, search_res, saved_data):
268
  if mode == "搜尋結果":
269
- # 如果是搜尋結果,傳入搜尋結果清單
270
  return format_df(search_res, saved_data), gr.update(visible=True)
271
  else:
272
- # 如果是追蹤清單,傳入追蹤清單
273
  return format_df(saved_data, saved_data), gr.update(visible=False)
274
 
275
- # 新增這個函式,用來在網頁載入時初始化畫面
276
  def init_on_load(saved_data):
277
- # 預設顯示追蹤清單,這樣使用者一重新整理就能看到資料
278
  return format_df(saved_data, saved_data)
279
 
280
  # --- UI Layout ---
281
 
282
  with gr.Blocks(title="Prof.404 開箱教授去哪兒?", theme=gr.themes.Soft()) as demo:
283
 
284
- # 🌟 修正點1: load_data() 的括號拿掉,傳入函式本身
285
- # 這樣每次重新整理網頁時,都會重新執行 load_data,讀取最新的檔案
286
- saved_state = gr.State(load_data)
287
-
288
  search_res_state = gr.State([])
289
  selected_prof_state = gr.State(None)
290
 
@@ -294,7 +319,6 @@ with gr.Blocks(title="Prof.404 開箱教授去哪兒?", theme=gr.themes.Soft()
294
  search_input = gr.Textbox(label="搜尋研究領域", placeholder="例如: 大型語言模型, 後量子密碼遷移...", scale=4)
295
  search_btn = gr.Button("🔍 搜尋", variant="primary", scale=1)
296
 
297
- # 🌟 修正點2: 預設選取 "追蹤清單",讓使用者一進來就看到存檔
298
  with gr.Row():
299
  view_radio = gr.Radio(["搜尋結果", "追蹤清單"], label="顯示模式", value="追蹤清單")
300
 
@@ -342,7 +366,7 @@ with gr.Blocks(title="Prof.404 開箱教授去哪兒?", theme=gr.themes.Soft()
342
 
343
  # --- Wiring ---
344
 
345
- # ���� 修正點3: 網頁載入時(Load),自動把 saved_state 的內容顯示到 prof_df 上
346
  demo.load(init_on_load, inputs=[saved_state], outputs=[prof_df])
347
 
348
  search_btn.click(
@@ -350,7 +374,6 @@ with gr.Blocks(title="Prof.404 開箱教授去哪兒?", theme=gr.themes.Soft()
350
  inputs=[search_input, saved_state],
351
  outputs=[prof_df, search_res_state, load_more_btn]
352
  ).then(
353
- # 搜尋後自動切換到 "搜尋結果" tab
354
  lambda: gr.update(value="搜尋結果"), outputs=[view_radio]
355
  )
356
 
 
4
  import pandas as pd
5
  from dotenv import load_dotenv
6
  from services import GeminiService
7
+ from huggingface_hub import HfApi, hf_hub_download
8
 
9
  # Load Env
10
  load_dotenv()
11
  SAVE_FILE = os.getenv("SAVE_FILE_NAME", "saved_professors.json")
12
+ HF_TOKEN = os.getenv("HF_TOKEN")
13
+ DATASET_REPO_ID = os.getenv("DATASET_REPO_ID")
14
 
15
  # Init Service
16
  try:
 
25
  return f"{p['name']}-{p['university']}"
26
 
27
  def load_data():
28
+ data = []
29
+ # 1. 嘗試從雲端下載
30
+ if HF_TOKEN and DATASET_REPO_ID:
31
+ try:
32
+ print(f"正在同步雲端資料: {DATASET_REPO_ID}...")
33
+ hf_hub_download(
34
+ repo_id=DATASET_REPO_ID,
35
+ filename=SAVE_FILE,
36
+ repo_type="dataset",
37
+ token=HF_TOKEN,
38
+ local_dir="." # 覆蓋本地檔案
39
+ )
40
+ except Exception as e:
41
+ print(f"雲端同步略過 (初次啟動或無權限): {e}")
42
+
43
+ # 2. 讀取檔案
44
  if os.path.exists(SAVE_FILE):
45
  try:
46
  with open(SAVE_FILE, 'r', encoding='utf-8') as f:
47
+ data = json.load(f)
48
  except:
49
+ data = []
50
+ return data
51
 
52
  def save_data(data):
53
+ # 1. 存本地
54
  try:
55
  with open(SAVE_FILE, 'w', encoding='utf-8') as f:
56
  json.dump(data, f, ensure_ascii=False, indent=2)
57
  except Exception as e:
58
  print(f"Save Error: {e}")
59
+ return
60
+
61
+ # 2. 上傳雲端
62
+ if HF_TOKEN and DATASET_REPO_ID:
63
+ try:
64
+ api = HfApi(token=HF_TOKEN)
65
+ api.upload_file(
66
+ path_or_fileobj=SAVE_FILE,
67
+ path_in_repo=SAVE_FILE,
68
+ repo_id=DATASET_REPO_ID,
69
+ repo_type="dataset",
70
+ commit_message="Sync data from Space"
71
+ )
72
+ except Exception as e:
73
+ print(f"Upload Error: {e}")
74
 
75
  def format_df(source_list, saved_list):
76
  if not source_list:
 
77
  return pd.DataFrame(columns=["狀態", "姓名", "大學", "系所", "標籤"])
78
 
79
  if saved_list is None:
 
116
 
117
  try:
118
  results = gemini_service.search_professors(query)
 
119
  return format_df(results, current_saved), results, gr.update(visible=True)
120
  except Exception as e:
121
  raise gr.Error(f"搜尋失敗: {e}")
 
152
 
153
  details_md = ""
154
 
 
155
  if current_prof.get('details') and len(current_prof.get('details')) > 10:
156
  details_md = current_prof['details']
157
  if not saved_prof:
158
  saved_data.insert(0, current_prof)
159
  save_data(saved_data)
160
  else:
 
161
  gr.Info(f"正在調查 {current_prof['name']}...")
162
  try:
163
  res = gemini_service.get_professor_details(current_prof)
 
297
 
298
  def toggle_view(mode, search_res, saved_data):
299
  if mode == "搜尋結果":
 
300
  return format_df(search_res, saved_data), gr.update(visible=True)
301
  else:
 
302
  return format_df(saved_data, saved_data), gr.update(visible=False)
303
 
 
304
  def init_on_load(saved_data):
 
305
  return format_df(saved_data, saved_data)
306
 
307
  # --- UI Layout ---
308
 
309
  with gr.Blocks(title="Prof.404 開箱教授去哪兒?", theme=gr.themes.Soft()) as demo:
310
 
311
+ # 🌟 使用 load_data 函式本身作為 State 初始值 (不加括號),確保每次開啟都會重新讀取
312
+ saved_state = gr.State(load_data)
 
 
313
  search_res_state = gr.State([])
314
  selected_prof_state = gr.State(None)
315
 
 
319
  search_input = gr.Textbox(label="搜尋研究領域", placeholder="例如: 大型語言模型, 後量子密碼遷移...", scale=4)
320
  search_btn = gr.Button("🔍 搜尋", variant="primary", scale=1)
321
 
 
322
  with gr.Row():
323
  view_radio = gr.Radio(["搜尋結果", "追蹤清單"], label="顯示模式", value="追蹤清單")
324
 
 
366
 
367
  # --- Wiring ---
368
 
369
+ # 🌟 啟動時自動載入資料到表格
370
  demo.load(init_on_load, inputs=[saved_state], outputs=[prof_df])
371
 
372
  search_btn.click(
 
374
  inputs=[search_input, saved_state],
375
  outputs=[prof_df, search_res_state, load_more_btn]
376
  ).then(
 
377
  lambda: gr.update(value="搜尋結果"), outputs=[view_radio]
378
  )
379