ssboost commited on
Commit
9e318a1
ยท
verified ยท
1 Parent(s): 06d652f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +277 -216
app.py CHANGED
@@ -17,6 +17,18 @@ from dotenv import load_dotenv
17
  load_dotenv()
18
 
19
  # ๋กœ๊น… ์„ค์ • (API ์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋Š” ์ œ์™ธ)
 
 
 
 
 
 
 
 
 
 
 
 
20
  logging.basicConfig(
21
  level=logging.INFO,
22
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
@@ -27,6 +39,11 @@ logging.basicConfig(
27
  )
28
 
29
  logger = logging.getLogger(__name__)
 
 
 
 
 
30
 
31
  # API ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
32
  def get_api_client():
@@ -123,8 +140,14 @@ def get_app_temp_dir():
123
  return os.environ.get('CONTROL_TOWER_TEMP', tempfile.gettempdir())
124
 
125
  def get_session_id():
126
- """์„ธ์…˜ ID ์ƒ์„ฑ"""
127
- return str(uuid.uuid4())
 
 
 
 
 
 
128
 
129
  def cleanup_session_files(session_id, delay=300):
130
  """์„ธ์…˜๋ณ„ ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ ํ•จ์ˆ˜"""
@@ -199,99 +222,92 @@ def create_session_temp_file(session_id, suffix='.xlsx'):
199
  register_session_file(session_id, temp_file_path)
200
  return temp_file_path
201
 
202
- def wrapper_modified(keyword, korean_only, apply_main_keyword_option, exclude_zero_volume, session_id):
203
- """ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰ ๋ฐ ์ฒ˜๋ฆฌ ๋ž˜ํผ ํ•จ์ˆ˜ (API ํด๋ผ์ด์–ธํŠธ ์‚ฌ์šฉ)"""
204
- update_session_activity(session_id)
205
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  try:
207
  client = get_api_client()
208
-
209
- # API ํ˜ธ์ถœ
210
  result = client.predict(
211
  keyword=keyword,
212
  korean_only=korean_only,
213
- apply_main_keyword=apply_main_keyword_option,
214
  exclude_zero_volume=exclude_zero_volume,
215
  api_name="/process_search_results"
216
  )
217
 
218
- # ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ
219
- table_html, cat_choices, vol_choices, selected_cat, download_file = result
 
 
 
 
 
 
 
 
 
 
 
220
 
221
  # ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋กœ์ปฌ๋กœ ๋ณต์‚ฌ
222
- excel_path = None
223
  if download_file:
224
- excel_path = create_session_temp_file(session_id, '.xlsx')
225
- # ์›๊ฒฉ ํŒŒ์ผ์„ ๋กœ์ปฌ๋กœ ๋ณต์‚ฌ
226
  try:
227
- import shutil
228
- shutil.copy2(download_file, excel_path)
229
  except Exception as e:
230
  logger.error(f"ํŒŒ์ผ ๋ณต์‚ฌ ์˜ค๋ฅ˜: {e}")
231
- excel_path = None
232
 
233
- # ๊ฐ€์ƒ์˜ DataFrame ์ƒํƒœ (์‹ค์ œ๋กœ๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ)
234
- df_state = pd.DataFrame()
235
 
236
- if table_html and "๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค" not in table_html:
237
- return (gr.update(value=table_html), gr.update(choices=cat_choices),
238
- gr.update(choices=vol_choices), df_state,
239
- gr.update(choices=cat_choices, value=selected_cat), excel_path,
240
- gr.update(visible=True), gr.update(visible=True), keyword)
241
- else:
242
- return (gr.update(value="<p>๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ‚ค์›Œ๋“œ๋กœ ์‹œ๋„ํ•ด๋ณด์„ธ์š”.</p>"),
243
- gr.update(choices=["์ „์ฒด ๋ณด๊ธฐ"]), gr.update(choices=["์ „์ฒด"]),
244
- df_state, gr.update(choices=["์ „์ฒด ๋ณด๊ธฐ"], value="์ „์ฒด ๋ณด๊ธฐ"), None,
245
- gr.update(visible=False), gr.update(visible=False), keyword)
246
-
247
  except Exception as e:
248
  logger.error(f"API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
249
- return (gr.update(value="<p>์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.</p>"),
250
- gr.update(choices=["์ „์ฒด ๋ณด๊ธฐ"]), gr.update(choices=["์ „์ฒด"]),
251
- pd.DataFrame(), gr.update(choices=["์ „์ฒด ๋ณด๊ธฐ"], value="์ „์ฒด ๋ณด๊ธฐ"), None,
252
- gr.update(visible=False), gr.update(visible=False), "")
253
-
254
- def analyze_with_auto_download(analysis_keywords, selected_category, state_df, session_id):
255
- """์นดํ…Œ๊ณ ๋ฆฌ ์ผ์น˜ ๋ถ„์„ ์‹คํ–‰ ๋ฐ ์ž๋™ ๋‹ค์šด๋กœ๋“œ (API ํด๋ผ์ด์–ธํŠธ ์‚ฌ์šฉ)"""
256
- update_session_activity(session_id)
257
-
258
  try:
259
  client = get_api_client()
260
-
261
- # API ํ˜ธ์ถœ
262
  result = client.predict(
263
- analysis_keywords=analysis_keywords,
264
- selected_category=selected_category,
265
- api_name="/process_analyze_results"
 
 
 
 
266
  )
267
-
268
- analysis_result, download_file = result
269
-
270
- # ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋กœ์ปฌ๋กœ ๋ณต์‚ฌ
271
- excel_path = None
272
- if download_file:
273
- excel_path = create_session_temp_file(session_id, '.xlsx')
274
- try:
275
- import shutil
276
- shutil.copy2(download_file, excel_path)
277
- except Exception as e:
278
- logger.error(f"๋ถ„์„ ๊ฒฐ๊ณผ ํŒŒ์ผ ๋ณต์‚ฌ ์˜ค๋ฅ˜: {e}")
279
- excel_path = None
280
-
281
- return analysis_result, excel_path, gr.update(visible=True)
282
-
283
  except Exception as e:
284
- logger.error(f"๋ถ„์„ API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
285
- return "๋ถ„์„ ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.", None, gr.update(visible=True)
286
 
287
- def filter_and_sort_table(df, selected_cat, keyword_sort, total_volume_sort, usage_count_sort, selected_volume_range, exclude_zero_volume, session_id):
288
- """ํ…Œ์ด๋ธ” ํ•„ํ„ฐ๋ง ๋ฐ ์ •๋ ฌ ํ•จ์ˆ˜ (API ํด๋ผ์ด์–ธํŠธ ์‚ฌ์šฉ)"""
289
- update_session_activity(session_id)
290
-
291
  try:
292
  client = get_api_client()
293
-
294
- # API ํ˜ธ์ถœ
295
  result = client.predict(
296
  selected_cat=selected_cat,
297
  keyword_sort=keyword_sort,
@@ -299,122 +315,222 @@ def filter_and_sort_table(df, selected_cat, keyword_sort, total_volume_sort, usa
299
  usage_count_sort=usage_count_sort,
300
  selected_volume_range=selected_volume_range,
301
  exclude_zero_volume=exclude_zero_volume,
302
- api_name="/filter_and_sort_table"
303
  )
304
-
305
  return result
306
-
307
  except Exception as e:
308
- logger.error(f"ํ•„ํ„ฐ๋ง API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
309
  return "<p>ํ•„ํ„ฐ๋ง ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</p>"
310
 
311
- def update_category_selection(selected_cat, session_id):
312
- """์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ ์„ ํƒ ์‹œ ๋ถ„์„ํ•  ์นดํ…Œ๊ณ ๋ฆฌ๋„ ๊ฐ™์€ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ (API ํด๋ผ์ด์–ธํŠธ ์‚ฌ์šฉ)"""
313
- update_session_activity(session_id)
314
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  try:
316
  client = get_api_client()
317
-
318
  result = client.predict(
319
  selected_cat=selected_cat,
320
  api_name="/update_category_selection"
321
  )
322
-
323
  return gr.update(value=result)
324
-
325
  except Exception as e:
326
- logger.error(f"์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
327
  return gr.update(value=selected_cat)
328
 
329
- def reset_interface(session_id):
330
- """์ธํ„ฐํŽ˜์ด์Šค ๋ฆฌ์…‹ ํ•จ์ˆ˜ - ์„ธ์…˜๋ณ„ ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™”"""
331
- update_session_activity(session_id)
332
-
333
- # ์„ธ์…˜๋ณ„ ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
334
- if session_id in session_temp_files:
335
- for file_path in session_temp_files[session_id]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  try:
337
- if os.path.exists(file_path):
338
- os.remove(file_path)
339
- logger.info(f"์„ธ์…˜ {session_id[:8]}... ๋ฆฌ์…‹ ์‹œ ํŒŒ์ผ ์‚ญ์ œ: {file_path}")
340
  except Exception as e:
341
- logger.error(f"์„ธ์…˜ {session_id[:8]}... ๋ฆฌ์…‹ ์‹œ ํŒŒ์ผ ์‚ญ์ œ ์˜ค๋ฅ˜: {e}")
342
- session_temp_files[session_id] = []
343
-
 
 
 
 
 
 
 
 
344
  try:
345
  client = get_api_client()
346
-
347
  result = client.predict(api_name="/reset_interface")
348
-
349
  return result
350
-
351
  except Exception as e:
352
- logger.error(f"๋ฆฌ์…‹ API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
353
- # ๊ธฐ๋ณธ ๋ฆฌ์…‹ ๊ฐ’ ๋ฐ˜ํ™˜
354
  return (
355
  "", # ๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ
356
  True, # ํ•œ๊ธ€๋งŒ ์ถ”์ถœ
357
  False, # ๊ฒ€์ƒ‰๋Ÿ‰ 0 ํ‚ค์›Œ๋“œ ์ œ์™ธ
358
  "๋ฉ”์ธํ‚ค์›Œ๋“œ ์ ์šฉ", # ์กฐํ•ฉ ๋ฐฉ์‹
359
  "", # HTML ํ…Œ์ด๋ธ”
360
- ["์ „์ฒด ๋ณด๊ธฐ"], # ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ
361
- "์ „์ฒด ๋ณด๊ธฐ", # ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ ์„ ํƒ
362
- ["์ „์ฒด"], # ๊ฒ€์ƒ‰๋Ÿ‰ ๊ตฌ๊ฐ„ ํ•„ํ„ฐ
363
- "์ „์ฒด", # ๊ฒ€์ƒ‰๋Ÿ‰ ๊ตฌ๊ฐ„ ์„ ํƒ
364
  "์ •๋ ฌ ์—†์Œ", # ์ด๊ฒ€์ƒ‰๋Ÿ‰ ์ •๋ ฌ
365
  "์ •๋ ฌ ์—†์Œ", # ํ‚ค์›Œ๋“œ ์‚ฌ์šฉํšŸ์ˆ˜ ์ •๋ ฌ
366
- ["์ „์ฒด ๋ณด๊ธฐ"], # ๋ถ„์„ํ•  ์นดํ…Œ๊ณ ๋ฆฌ
367
- "์ „์ฒด ๋ณด๊ธฐ", # ๋ถ„์„ํ•  ์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ
368
  "", # ํ‚ค์›Œ๋“œ ์ž…๋ ฅ
369
  "", # ๋ถ„์„ ๊ฒฐ๊ณผ
370
- None, # ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ
371
- gr.update(visible=False), # ํ‚ค์›Œ๋“œ ๋ถ„์„ ์„น์…˜
372
- gr.update(visible=False), # ๋ถ„์„ ๊ฒฐ๊ณผ ์ถœ๋ ฅ ์„น์…˜
373
- "" # ํ‚ค์›Œ๋“œ ์ƒํƒœ
374
  )
375
 
376
- # ๋ž˜ํผ ํ•จ์ˆ˜๋“ค
377
- def search_with_loading(keyword, korean_only, apply_main_keyword, exclude_zero_volume, session_id):
378
- update_session_activity(session_id)
 
 
379
  return (
380
- gr.update(visible=True),
381
- gr.update(visible=False)
382
  )
383
 
384
- def process_search_results(keyword, korean_only, apply_main_keyword, exclude_zero_volume, session_id):
385
- update_session_activity(session_id)
386
-
387
- result = wrapper_modified(keyword, korean_only, apply_main_keyword, exclude_zero_volume, session_id)
388
 
389
- table_html, cat_choices, vol_choices, df, selected_cat, excel, keyword_section_vis, cat_section_vis, new_keyword_state = result
 
 
 
 
 
 
 
 
 
 
 
390
 
391
- if table_html and "๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค" not in table_html:
392
- empty_placeholder_vis = False
393
  keyword_section_visibility = True
 
 
394
  execution_section_visibility = True
395
  else:
396
- empty_placeholder_vis = True
397
  keyword_section_visibility = False
 
 
398
  execution_section_visibility = False
399
 
 
 
 
400
  return (
401
- table_html, cat_choices, vol_choices, df, selected_cat, excel,
402
- gr.update(visible=keyword_section_visibility),
403
- gr.update(visible=cat_section_vis),
404
- gr.update(visible=False),
405
- gr.update(visible=empty_placeholder_vis),
406
- gr.update(visible=execution_section_visibility),
407
- new_keyword_state
 
 
 
 
 
408
  )
409
 
410
- def analyze_with_loading(analysis_keywords, selected_category, state_df, session_id):
411
- update_session_activity(session_id)
412
- return gr.update(visible=True)
 
413
 
414
- def process_analyze_results(analysis_keywords, selected_category, state_df, session_id):
415
- update_session_activity(session_id)
416
- results = analyze_with_auto_download(analysis_keywords, selected_category, state_df, session_id)
417
- return results + (gr.update(visible=False),)
 
 
 
 
 
418
 
419
  # ์„ธ์…˜ ์ •๋ฆฌ ์Šค์ผ€์ค„๋Ÿฌ
420
  def start_session_cleanup_scheduler():
@@ -423,7 +539,6 @@ def start_session_cleanup_scheduler():
423
  while True:
424
  time.sleep(600) # 10๋ถ„๋งˆ๋‹ค ์‹คํ–‰
425
  cleanup_old_sessions()
426
- # ์ถ”๊ฐ€๋กœ ํ—ˆ๊น…ํŽ˜์ด์Šค ์ž„์‹œ ํด๋”๋„ ์ฃผ๊ธฐ์  ์ •๋ฆฌ
427
  cleanup_huggingface_temp_folders()
428
 
429
  threading.Thread(target=cleanup_scheduler, daemon=True).start()
@@ -487,9 +602,6 @@ def create_app():
487
  )) as demo:
488
  gr.HTML(fontawesome_html)
489
 
490
- # ์„ธ์…˜ ID ์ƒํƒœ (๊ฐ ์‚ฌ์šฉ์ž๋ณ„๋กœ ๊ณ ์œ )
491
- session_id = gr.State(get_session_id)
492
-
493
  # ํ‚ค์›Œ๋“œ ์ƒํƒœ ๊ด€๋ฆฌ
494
  keyword_state = gr.State("")
495
 
@@ -662,14 +774,14 @@ def create_app():
662
  # ์ƒํƒœ ์ €์žฅ์šฉ ๋ณ€์ˆ˜
663
  state_df = gr.State()
664
 
665
- # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - ๋ชจ๋“  ํ•จ์ˆ˜์— session_id ์ถ”๊ฐ€
666
  search_btn.click(
667
- fn=search_with_loading,
668
- inputs=[keyword, korean_only, apply_main_keyword, exclude_zero_volume, session_id],
669
  outputs=[progress_section, empty_table_html]
670
  ).then(
671
- fn=process_search_results,
672
- inputs=[keyword, korean_only, apply_main_keyword, exclude_zero_volume, session_id],
673
  outputs=[
674
  table_output, category_filter, search_volume_filter,
675
  state_df, selected_category, download_output,
@@ -679,29 +791,29 @@ def create_app():
679
  ]
680
  )
681
 
682
- # ํ•„ํ„ฐ ๋ฐ ์ •๋ ฌ ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - session_id ์ถ”๊ฐ€
683
  category_filter.change(
684
  fn=filter_and_sort_table,
685
  inputs=[
686
- state_df, category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
687
  total_volume_sort, usage_count_sort,
688
- search_volume_filter, exclude_zero_volume, session_id
689
  ],
690
  outputs=[table_output]
691
  )
692
 
693
  category_filter.change(
694
  fn=update_category_selection,
695
- inputs=[category_filter, session_id],
696
  outputs=[selected_category]
697
  )
698
 
699
  total_volume_sort.change(
700
  fn=filter_and_sort_table,
701
  inputs=[
702
- state_df, category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
703
  total_volume_sort, usage_count_sort,
704
- search_volume_filter, exclude_zero_volume, session_id
705
  ],
706
  outputs=[table_output]
707
  )
@@ -709,9 +821,9 @@ def create_app():
709
  usage_count_sort.change(
710
  fn=filter_and_sort_table,
711
  inputs=[
712
- state_df, category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
713
  total_volume_sort, usage_count_sort,
714
- search_volume_filter, exclude_zero_volume, session_id
715
  ],
716
  outputs=[table_output]
717
  )
@@ -719,9 +831,9 @@ def create_app():
719
  search_volume_filter.change(
720
  fn=filter_and_sort_table,
721
  inputs=[
722
- state_df, category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
723
  total_volume_sort, usage_count_sort,
724
- search_volume_filter, exclude_zero_volume, session_id
725
  ],
726
  outputs=[table_output]
727
  )
@@ -729,87 +841,36 @@ def create_app():
729
  exclude_zero_volume.change(
730
  fn=filter_and_sort_table,
731
  inputs=[
732
- state_df, category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
733
  total_volume_sort, usage_count_sort,
734
- search_volume_filter, exclude_zero_volume, session_id
735
  ],
736
  outputs=[table_output]
737
  )
738
 
739
- # ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„์„ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ - session_id ์ถ”๊ฐ€
740
  analyze_btn.click(
741
- fn=analyze_with_loading,
742
- inputs=[analysis_keywords, selected_category, state_df, session_id],
743
  outputs=[progress_section]
744
  ).then(
745
- fn=process_analyze_results,
746
- inputs=[analysis_keywords, selected_category, state_df, session_id],
747
  outputs=[analysis_result, download_output, analysis_output_section, progress_section]
748
  )
749
 
750
- # ๋ฆฌ์…‹ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - session_id ์ถ”๊ฐ€
751
  reset_btn.click(
752
  fn=reset_interface,
753
- inputs=[session_id],
754
  outputs=[
755
  keyword, korean_only, exclude_zero_volume, apply_main_keyword,
756
  table_output, category_filter, category_filter,
757
  search_volume_filter, search_volume_filter,
758
  total_volume_sort, usage_count_sort,
759
  selected_category, selected_category,
760
- analysis_keywords, analysis_result, download_output,
761
- keyword_analysis_section, analysis_output_section,
762
- keyword_state
763
  ]
764
  )
765
 
766
- return demo
767
-
768
- if __name__ == "__main__":
769
- # ========== ์‹œ์ž‘ ์‹œ ์ „์ฒด ์ดˆ๊ธฐํ™” ==========
770
- logger.info("๐Ÿš€ ์ปจํŠธ๋กค ํƒ€์›Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘...")
771
-
772
- # 1. ์ฒซ ๋ฒˆ์งธ: ํ—ˆ๊น…ํŽ˜์ด์Šค ์ž„์‹œ ํด๋” ์ •๋ฆฌ ๋ฐ ํ™˜๊ฒฝ ์„ค์ •
773
- app_temp_dir = cleanup_on_startup()
774
-
775
- # 2. ์„ธ์…˜ ์ •๋ฆฌ ์Šค์ผ€์ค„๋Ÿฌ ์‹œ์ž‘
776
- start_session_cleanup_scheduler()
777
-
778
- # 3. API ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ
779
- try:
780
- test_client = get_api_client()
781
- logger.info("โœ… API ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ ์„ฑ๊ณต")
782
- except Exception as e:
783
- logger.error(f"โŒ API ์—ฐ๊ฒฐ ์‹คํŒจ: {e}")
784
- raise
785
-
786
- logger.info("===== ์ปจํŠธ๋กค ํƒ€์›Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์™„๋ฃŒ at %s =====", time.strftime("%Y-%m-%d %H:%M:%S"))
787
- logger.info(f"๐Ÿ“ ์ž„์‹œ ํŒŒ์ผ ์ €์žฅ ์œ„์น˜: {app_temp_dir}")
788
-
789
- # ========== ์•ฑ ์‹คํ–‰ ==========
790
- try:
791
- app = create_app()
792
- app.launch(
793
- share=False, # ๋ณด์•ˆ์„ ์œ„ํ•ด share ๋น„ํ™œ์„ฑํ™”
794
- server_name="0.0.0.0", # ๋ชจ๋“  IP์—์„œ ์ ‘๊ทผ ํ—ˆ์šฉ
795
- server_port=7860, # ํฌํŠธ ์ง€์ •
796
- max_threads=40, # ๋ฉ€ํ‹ฐ์œ ์ €๋ฅผ ์œ„ํ•œ ์Šค๋ ˆ๋“œ ์ˆ˜ ์ฆ๊ฐ€
797
- auth=None, # ํ•„์š”์‹œ ์ธ์ฆ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
798
- show_error=True, # ์—๋Ÿฌ ํ‘œ์‹œ
799
- quiet=False, # ๋กœ๊ทธ ํ‘œ์‹œ
800
- favicon_path=None, # ํŒŒ๋น„์ฝ˜ ์„ค์ •
801
- ssl_verify=False # SSL ๊ฒ€์ฆ ๋น„ํ™œ์„ฑํ™” (๊ฐœ๋ฐœ์šฉ)
802
- )
803
- except Exception as e:
804
- logger.error(f"์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹คํŒจ: {e}")
805
- raise
806
- finally:
807
- # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ ์‹œ ์ •๋ฆฌ
808
- logger.info("๐Ÿงน ์ปจํŠธ๋กค ํƒ€์›Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ - ์ตœ์ข… ์ •๋ฆฌ ์ž‘์—…...")
809
- try:
810
- cleanup_huggingface_temp_folders()
811
- if os.path.exists(app_temp_dir):
812
- shutil.rmtree(app_temp_dir, ignore_errors=True)
813
- logger.info("โœ… ์ตœ์ข… ์ •๋ฆฌ ์™„๋ฃŒ")
814
- except Exception as e:
815
- logger.error(f"์ตœ์ข… ์ •๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {e}")
 
17
  load_dotenv()
18
 
19
  # ๋กœ๊น… ์„ค์ • (API ์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋Š” ์ œ์™ธ)
20
+ class SecureFilter(logging.Filter):
21
+ """API ์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋ฅผ ๋กœ๊ทธ์—์„œ ์ œ๊ฑฐํ•˜๋Š” ํ•„ํ„ฐ"""
22
+ def filter(self, record):
23
+ # API ์—”๋“œํฌ์ธํŠธ ๊ด€๋ จ ๋กœ๊ทธ ํ•„ํ„ฐ๋ง
24
+ message = record.getMessage()
25
+ if any(keyword in message.lower() for keyword in ['hf.space', 'happydoggg', 'gradio_api', 'http request']):
26
+ return False
27
+ return True
28
+
29
+ # ๋ณด์•ˆ ํ•„ํ„ฐ ์ ์šฉ
30
+ secure_filter = SecureFilter()
31
+
32
  logging.basicConfig(
33
  level=logging.INFO,
34
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
 
39
  )
40
 
41
  logger = logging.getLogger(__name__)
42
+ logger.addFilter(secure_filter)
43
+
44
+ # HTTP ์š”์ฒญ ๋กœ๊ทธ๋„ ์ˆจ๊น€
45
+ logging.getLogger("httpx").setLevel(logging.WARNING)
46
+ logging.getLogger("gradio_client").setLevel(logging.WARNING)
47
 
48
  # API ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
49
  def get_api_client():
 
140
  return os.environ.get('CONTROL_TOWER_TEMP', tempfile.gettempdir())
141
 
142
  def get_session_id():
143
+ """์„ธ์…˜ ID ์ƒ์„ฑ - ์›๋ณธ API์™€ ๋™์ผ"""
144
+ try:
145
+ client = get_api_client()
146
+ result = client.predict(api_name="/get_session_id")
147
+ return result
148
+ except Exception as e:
149
+ logger.error(f"์„ธ์…˜ ID ์ƒ์„ฑ API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
150
+ return str(uuid.uuid4())
151
 
152
  def cleanup_session_files(session_id, delay=300):
153
  """์„ธ์…˜๋ณ„ ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ ํ•จ์ˆ˜"""
 
222
  register_session_file(session_id, temp_file_path)
223
  return temp_file_path
224
 
225
+ # ========== ์›๋ณธ API์™€ ์ •ํ™•ํžˆ ๋™์ผํ•œ ํ•จ์ˆ˜๋“ค ==========
226
+
227
+ def search_with_loading(keyword, korean_only, apply_main_keyword, exclude_zero_volume):
228
+ """์›๋ณธ API: /search_with_loading (4๊ฐœ ๋งค๊ฐœ๋ณ€์ˆ˜, 1๊ฐœ ๋ฐ˜ํ™˜๊ฐ’)"""
229
+ try:
230
+ client = get_api_client()
231
+ result = client.predict(
232
+ keyword=keyword,
233
+ korean_only=korean_only,
234
+ apply_main_keyword=apply_main_keyword,
235
+ exclude_zero_volume=exclude_zero_volume,
236
+ api_name="/search_with_loading"
237
+ )
238
+ return result
239
+ except Exception as e:
240
+ logger.error(f"search_with_loading API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
241
+ return gr.update(visible=True)
242
+
243
+ def process_search_results(keyword, korean_only, apply_main_keyword, exclude_zero_volume):
244
+ """์›๋ณธ API: /process_search_results (4๊ฐœ ๋งค๊ฐœ๋ณ€์ˆ˜, 6๊ฐœ ๋ฐ˜ํ™˜๊ฐ’)"""
245
  try:
246
  client = get_api_client()
 
 
247
  result = client.predict(
248
  keyword=keyword,
249
  korean_only=korean_only,
250
+ apply_main_keyword=apply_main_keyword,
251
  exclude_zero_volume=exclude_zero_volume,
252
  api_name="/process_search_results"
253
  )
254
 
255
+ # ์›๋ณธ API ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด 6๊ฐœ ๋ฐ˜ํ™˜๊ฐ’์ด์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” 5๊ฐœ์ผ ์ˆ˜ ์žˆ์Œ
256
+ # ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
257
+ if len(result) == 6:
258
+ table_html, cat_choices, vol_choices, selected_cat, download_file, progress_html = result
259
+ elif len(result) == 5:
260
+ table_html, cat_choices, vol_choices, selected_cat, download_file = result
261
+ progress_html = ""
262
+ else:
263
+ logger.error(f"์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅธ ๋ฐ˜ํ™˜๊ฐ’ ๊ฐœ์ˆ˜: {len(result)}")
264
+ return (
265
+ "<p>์„œ๋น„์Šค ์‘๋‹ต ํ˜•์‹์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</p>",
266
+ ["์ „์ฒด ๋ณด๊ธฐ"], ["์ „์ฒด"], "์ „์ฒด ๋ณด๊ธฐ", None, ""
267
+ )
268
 
269
  # ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋กœ์ปฌ๋กœ ๋ณต์‚ฌ
270
+ local_download_file = None
271
  if download_file:
272
+ session_id = get_session_id()
273
+ local_download_file = create_session_temp_file(session_id, '.xlsx')
274
  try:
275
+ shutil.copy2(download_file, local_download_file)
 
276
  except Exception as e:
277
  logger.error(f"ํŒŒ์ผ ๋ณต์‚ฌ ์˜ค๋ฅ˜: {e}")
278
+ local_download_file = None
279
 
280
+ return table_html, cat_choices, vol_choices, selected_cat, local_download_file, progress_html
 
281
 
 
 
 
 
 
 
 
 
 
 
 
282
  except Exception as e:
283
  logger.error(f"API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
284
+ return (
285
+ "<p>์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.</p>",
286
+ ["์ „์ฒด ๋ณด๊ธฐ"], ["์ „์ฒด"], "์ „์ฒด ๋ณด๊ธฐ", None, ""
287
+ )
288
+
289
+ def filter_and_sort_table(selected_cat, keyword_sort, total_volume_sort, usage_count_sort, selected_volume_range, exclude_zero_volume):
290
+ """์›๋ณธ API: /filter_and_sort_table (6๊ฐœ ๋งค๊ฐœ๋ณ€์ˆ˜, 1๊ฐœ ๋ฐ˜ํ™˜๊ฐ’)"""
 
 
291
  try:
292
  client = get_api_client()
 
 
293
  result = client.predict(
294
+ selected_cat=selected_cat,
295
+ keyword_sort=keyword_sort,
296
+ total_volume_sort=total_volume_sort,
297
+ usage_count_sort=usage_count_sort,
298
+ selected_volume_range=selected_volume_range,
299
+ exclude_zero_volume=exclude_zero_volume,
300
+ api_name="/filter_and_sort_table"
301
  )
302
+ return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  except Exception as e:
304
+ logger.error(f"filter_and_sort_table API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
305
+ return "<p>ํ•„ํ„ฐ๋ง ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</p>"
306
 
307
+ def filter_and_sort_table_1(selected_cat, keyword_sort, total_volume_sort, usage_count_sort, selected_volume_range, exclude_zero_volume):
308
+ """์›๋ณธ API: /filter_and_sort_table_1"""
 
 
309
  try:
310
  client = get_api_client()
 
 
311
  result = client.predict(
312
  selected_cat=selected_cat,
313
  keyword_sort=keyword_sort,
 
315
  usage_count_sort=usage_count_sort,
316
  selected_volume_range=selected_volume_range,
317
  exclude_zero_volume=exclude_zero_volume,
318
+ api_name="/filter_and_sort_table_1"
319
  )
 
320
  return result
 
321
  except Exception as e:
322
+ logger.error(f"filter_and_sort_table_1 API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
323
  return "<p>ํ•„ํ„ฐ๋ง ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</p>"
324
 
325
+ def filter_and_sort_table_2(selected_cat, keyword_sort, total_volume_sort, usage_count_sort, selected_volume_range, exclude_zero_volume):
326
+ """์›๋ณธ API: /filter_and_sort_table_2"""
327
+ try:
328
+ client = get_api_client()
329
+ result = client.predict(
330
+ selected_cat=selected_cat,
331
+ keyword_sort=keyword_sort,
332
+ total_volume_sort=total_volume_sort,
333
+ usage_count_sort=usage_count_sort,
334
+ selected_volume_range=selected_volume_range,
335
+ exclude_zero_volume=exclude_zero_volume,
336
+ api_name="/filter_and_sort_table_2"
337
+ )
338
+ return result
339
+ except Exception as e:
340
+ logger.error(f"filter_and_sort_table_2 API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
341
+ return "<p>ํ•„ํ„ฐ๋ง ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</p>"
342
+
343
+ def filter_and_sort_table_3(selected_cat, keyword_sort, total_volume_sort, usage_count_sort, selected_volume_range, exclude_zero_volume):
344
+ """์›๋ณธ API: /filter_and_sort_table_3"""
345
+ try:
346
+ client = get_api_client()
347
+ result = client.predict(
348
+ selected_cat=selected_cat,
349
+ keyword_sort=keyword_sort,
350
+ total_volume_sort=total_volume_sort,
351
+ usage_count_sort=usage_count_sort,
352
+ selected_volume_range=selected_volume_range,
353
+ exclude_zero_volume=exclude_zero_volume,
354
+ api_name="/filter_and_sort_table_3"
355
+ )
356
+ return result
357
+ except Exception as e:
358
+ logger.error(f"filter_and_sort_table_3 API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
359
+ return "<p>ํ•„ํ„ฐ๋ง ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</p>"
360
+
361
+ def filter_and_sort_table_4(selected_cat, keyword_sort, total_volume_sort, usage_count_sort, selected_volume_range, exclude_zero_volume):
362
+ """์›๋ณธ API: /filter_and_sort_table_4"""
363
+ try:
364
+ client = get_api_client()
365
+ result = client.predict(
366
+ selected_cat=selected_cat,
367
+ keyword_sort=keyword_sort,
368
+ total_volume_sort=total_volume_sort,
369
+ usage_count_sort=usage_count_sort,
370
+ selected_volume_range=selected_volume_range,
371
+ exclude_zero_volume=exclude_zero_volume,
372
+ api_name="/filter_and_sort_table_4"
373
+ )
374
+ return result
375
+ except Exception as e:
376
+ logger.error(f"filter_and_sort_table_4 API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
377
+ return "<p>ํ•„ํ„ฐ๋ง ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</p>"
378
+
379
+ def update_category_selection(selected_cat):
380
+ """์›๋ณธ API: /update_category_selection (1๊ฐœ ๋งค๊ฐœ๋ณ€์ˆ˜, 1๊ฐœ ๋ฐ˜ํ™˜๊ฐ’)"""
381
  try:
382
  client = get_api_client()
 
383
  result = client.predict(
384
  selected_cat=selected_cat,
385
  api_name="/update_category_selection"
386
  )
 
387
  return gr.update(value=result)
 
388
  except Exception as e:
389
+ logger.error(f"update_category_selection API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
390
  return gr.update(value=selected_cat)
391
 
392
+ def analyze_with_loading(analysis_keywords, selected_category):
393
+ """์›๋ณธ API: /analyze_with_loading (2๊ฐœ ๋งค๊ฐœ๋ณ€์ˆ˜, 1๊ฐœ ๋ฐ˜ํ™˜๊ฐ’)"""
394
+ try:
395
+ client = get_api_client()
396
+ result = client.predict(
397
+ analysis_keywords=analysis_keywords,
398
+ selected_category=selected_category,
399
+ api_name="/analyze_with_loading"
400
+ )
401
+ return result
402
+ except Exception as e:
403
+ logger.error(f"analyze_with_loading API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
404
+ return gr.update(visible=True)
405
+
406
+ def process_analyze_results(analysis_keywords, selected_category):
407
+ """์›๋ณธ API: /process_analyze_results (2๊ฐœ ๋งค๊ฐœ๋ณ€์ˆ˜, 2๊ฐœ ๋ฐ˜ํ™˜๊ฐ’)"""
408
+ try:
409
+ client = get_api_client()
410
+ result = client.predict(
411
+ analysis_keywords=analysis_keywords,
412
+ selected_category=selected_category,
413
+ api_name="/process_analyze_results"
414
+ )
415
+
416
+ analysis_result, download_file = result
417
+
418
+ # ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋กœ์ปฌ๋กœ ๋ณต์‚ฌ
419
+ local_download_file = None
420
+ if download_file:
421
+ session_id = get_session_id()
422
+ local_download_file = create_session_temp_file(session_id, '.xlsx')
423
  try:
424
+ shutil.copy2(download_file, local_download_file)
 
 
425
  except Exception as e:
426
+ logger.error(f"๋ถ„์„ ๊ฒฐ๊ณผ ํŒŒ์ผ ๋ณต์‚ฌ ์˜ค๋ฅ˜: {e}")
427
+ local_download_file = None
428
+
429
+ return analysis_result, local_download_file
430
+
431
+ except Exception as e:
432
+ logger.error(f"process_analyze_results API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
433
+ return "๋ถ„์„ ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.", None
434
+
435
+ def reset_interface():
436
+ """์›๋ณธ API: /reset_interface (0๊ฐœ ๋งค๊ฐœ๋ณ€์ˆ˜, 16๊ฐœ ๋ฐ˜ํ™˜๊ฐ’)"""
437
  try:
438
  client = get_api_client()
 
439
  result = client.predict(api_name="/reset_interface")
 
440
  return result
 
441
  except Exception as e:
442
+ logger.error(f"reset_interface API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {e}")
443
+ # ๊ธฐ๋ณธ ๋ฆฌ์…‹ ๊ฐ’ ๋ฐ˜ํ™˜ (16๊ฐœ)
444
  return (
445
  "", # ๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ
446
  True, # ํ•œ๊ธ€๋งŒ ์ถ”์ถœ
447
  False, # ๊ฒ€์ƒ‰๋Ÿ‰ 0 ํ‚ค์›Œ๋“œ ์ œ์™ธ
448
  "๋ฉ”์ธํ‚ค์›Œ๋“œ ์ ์šฉ", # ์กฐํ•ฉ ๋ฐฉ์‹
449
  "", # HTML ํ…Œ์ด๋ธ”
450
+ ["์ „์ฒด ๋ณด๊ธฐ"], # ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ choices
451
+ "์ „์ฒด ๋ณด๊ธฐ", # ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ value
452
+ ["์ „์ฒด"], # ๊ฒ€์ƒ‰๋Ÿ‰ ๊ตฌ๊ฐ„ ํ•„ํ„ฐ choices
453
+ "์ „์ฒด", # ๊ฒ€์ƒ‰๋Ÿ‰ ๊ตฌ๊ฐ„ ํ•„ํ„ฐ value
454
  "์ •๋ ฌ ์—†์Œ", # ์ด๊ฒ€์ƒ‰๋Ÿ‰ ์ •๋ ฌ
455
  "์ •๋ ฌ ์—†์Œ", # ํ‚ค์›Œ๋“œ ์‚ฌ์šฉํšŸ์ˆ˜ ์ •๋ ฌ
456
+ ["์ „์ฒด ๋ณด๊ธฐ"], # ๋ถ„์„ํ•  ์นดํ…Œ๊ณ ๋ฆฌ choices
457
+ "์ „์ฒด ๋ณด๊ธฐ", # ๋ถ„์„ํ•  ์นดํ…Œ๊ณ ๋ฆฌ value
458
  "", # ํ‚ค์›Œ๋“œ ์ž…๋ ฅ
459
  "", # ๋ถ„์„ ๊ฒฐ๊ณผ
460
+ None # ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ
 
 
 
461
  )
462
 
463
+ # ========== UI ์ฒ˜๋ฆฌ ๋ž˜ํผ ํ•จ์ˆ˜๋“ค ==========
464
+
465
+ def wrapper_search_with_loading(keyword, korean_only, apply_main_keyword, exclude_zero_volume):
466
+ """๊ฒ€์ƒ‰ ๋กœ๋”ฉ UI ์ฒ˜๋ฆฌ"""
467
+ result = search_with_loading(keyword, korean_only, apply_main_keyword, exclude_zero_volume)
468
  return (
469
+ gr.update(visible=True), # progress_section
470
+ gr.update(visible=False) # empty_table_html
471
  )
472
 
473
+ def wrapper_process_search_results(keyword, korean_only, apply_main_keyword, exclude_zero_volume):
474
+ """๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ UI"""
475
+ result = process_search_results(keyword, korean_only, apply_main_keyword, exclude_zero_volume)
 
476
 
477
+ # ์•ˆ์ „ํ•˜๊ฒŒ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ
478
+ if len(result) >= 5:
479
+ table_html, cat_choices, vol_choices, selected_cat, download_file = result[:5]
480
+ progress_html = result[5] if len(result) > 5 else ""
481
+ else:
482
+ logger.error(f"๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: ์˜ˆ์ƒ๋ณด๋‹ค ์ ์€ ๋ฐ˜ํ™˜๊ฐ’ {len(result)}")
483
+ table_html = "<p>๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.</p>"
484
+ cat_choices = ["์ „์ฒด ๋ณด๊ธฐ"]
485
+ vol_choices = ["์ „์ฒด"]
486
+ selected_cat = "์ „์ฒด ๋ณด๊ธฐ"
487
+ download_file = None
488
+ progress_html = ""
489
 
490
+ # UI ํ‘œ์‹œ ์—ฌ๋ถ€ ๊ฒฐ์ •
491
+ if table_html and "๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค" not in table_html and "๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค" not in table_html:
492
  keyword_section_visibility = True
493
+ category_section_visibility = True
494
+ empty_placeholder_vis = False
495
  execution_section_visibility = True
496
  else:
 
497
  keyword_section_visibility = False
498
+ category_section_visibility = False
499
+ empty_placeholder_vis = True
500
  execution_section_visibility = False
501
 
502
+ # ๊ฐ€์ƒ์˜ state_df (์›๋ณธ๊ณผ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด)
503
+ state_df = pd.DataFrame()
504
+
505
  return (
506
+ table_html, # table_output
507
+ cat_choices, # category_filter choices
508
+ vol_choices, # search_volume_filter choices
509
+ state_df, # state_df
510
+ selected_cat, # selected_category value
511
+ download_file, # download_output
512
+ gr.update(visible=keyword_section_visibility), # keyword_analysis_section
513
+ gr.update(visible=category_section_visibility), # category_analysis_section
514
+ gr.update(visible=False), # progress_section
515
+ gr.update(visible=empty_placeholder_vis), # empty_table_html
516
+ gr.update(visible=execution_section_visibility), # execution_section
517
+ keyword # keyword_state
518
  )
519
 
520
+ def wrapper_analyze_with_loading(analysis_keywords, selected_category, state_df):
521
+ """๋ถ„์„ ๋กœ๋”ฉ UI ์ฒ˜๋ฆฌ"""
522
+ result = analyze_with_loading(analysis_keywords, selected_category)
523
+ return gr.update(visible=True) # progress_section
524
 
525
+ def wrapper_process_analyze_results(analysis_keywords, selected_category, state_df):
526
+ """๋ถ„์„ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ UI"""
527
+ analysis_result, download_file = process_analyze_results(analysis_keywords, selected_category)
528
+ return (
529
+ analysis_result, # analysis_result
530
+ download_file, # download_output
531
+ gr.update(visible=True), # analysis_output_section
532
+ gr.update(visible=False) # progress_section
533
+ )
534
 
535
  # ์„ธ์…˜ ์ •๋ฆฌ ์Šค์ผ€์ค„๋Ÿฌ
536
  def start_session_cleanup_scheduler():
 
539
  while True:
540
  time.sleep(600) # 10๋ถ„๋งˆ๋‹ค ์‹คํ–‰
541
  cleanup_old_sessions()
 
542
  cleanup_huggingface_temp_folders()
543
 
544
  threading.Thread(target=cleanup_scheduler, daemon=True).start()
 
602
  )) as demo:
603
  gr.HTML(fontawesome_html)
604
 
 
 
 
605
  # ํ‚ค์›Œ๋“œ ์ƒํƒœ ๊ด€๋ฆฌ
606
  keyword_state = gr.State("")
607
 
 
774
  # ์ƒํƒœ ์ €์žฅ์šฉ ๋ณ€์ˆ˜
775
  state_df = gr.State()
776
 
777
+ # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
778
  search_btn.click(
779
+ fn=wrapper_search_with_loading,
780
+ inputs=[keyword, korean_only, apply_main_keyword, exclude_zero_volume],
781
  outputs=[progress_section, empty_table_html]
782
  ).then(
783
+ fn=wrapper_process_search_results,
784
+ inputs=[keyword, korean_only, apply_main_keyword, exclude_zero_volume],
785
  outputs=[
786
  table_output, category_filter, search_volume_filter,
787
  state_df, selected_category, download_output,
 
791
  ]
792
  )
793
 
794
+ # ํ•„ํ„ฐ ๋ฐ ์ •๋ ฌ ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
795
  category_filter.change(
796
  fn=filter_and_sort_table,
797
  inputs=[
798
+ category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
799
  total_volume_sort, usage_count_sort,
800
+ search_volume_filter, exclude_zero_volume
801
  ],
802
  outputs=[table_output]
803
  )
804
 
805
  category_filter.change(
806
  fn=update_category_selection,
807
+ inputs=[category_filter],
808
  outputs=[selected_category]
809
  )
810
 
811
  total_volume_sort.change(
812
  fn=filter_and_sort_table,
813
  inputs=[
814
+ category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
815
  total_volume_sort, usage_count_sort,
816
+ search_volume_filter, exclude_zero_volume
817
  ],
818
  outputs=[table_output]
819
  )
 
821
  usage_count_sort.change(
822
  fn=filter_and_sort_table,
823
  inputs=[
824
+ category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
825
  total_volume_sort, usage_count_sort,
826
+ search_volume_filter, exclude_zero_volume
827
  ],
828
  outputs=[table_output]
829
  )
 
831
  search_volume_filter.change(
832
  fn=filter_and_sort_table,
833
  inputs=[
834
+ category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
835
  total_volume_sort, usage_count_sort,
836
+ search_volume_filter, exclude_zero_volume
837
  ],
838
  outputs=[table_output]
839
  )
 
841
  exclude_zero_volume.change(
842
  fn=filter_and_sort_table,
843
  inputs=[
844
+ category_filter, gr.Textbox(value="์ •๋ ฌ ์—†์Œ", visible=False),
845
  total_volume_sort, usage_count_sort,
846
+ search_volume_filter, exclude_zero_volume
847
  ],
848
  outputs=[table_output]
849
  )
850
 
851
+ # ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„์„ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ
852
  analyze_btn.click(
853
+ fn=wrapper_analyze_with_loading,
854
+ inputs=[analysis_keywords, selected_category, state_df],
855
  outputs=[progress_section]
856
  ).then(
857
+ fn=wrapper_process_analyze_results,
858
+ inputs=[analysis_keywords, selected_category, state_df],
859
  outputs=[analysis_result, download_output, analysis_output_section, progress_section]
860
  )
861
 
862
+ # ๋ฆฌ์…‹ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
863
  reset_btn.click(
864
  fn=reset_interface,
865
+ inputs=[],
866
  outputs=[
867
  keyword, korean_only, exclude_zero_volume, apply_main_keyword,
868
  table_output, category_filter, category_filter,
869
  search_volume_filter, search_volume_filter,
870
  total_volume_sort, usage_count_sort,
871
  selected_category, selected_category,
872
+ analysis_keywords, analysis_result, download_output
 
 
873
  ]
874
  )
875
 
876
+ return demo