ginipick commited on
Commit
a88e0cc
Β·
verified Β·
1 Parent(s): bca6d69

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -95
app.py CHANGED
@@ -34,7 +34,6 @@ KOREAN_COMPANIES = [
34
  "investing"
35
  ]
36
 
37
-
38
  #########################################################
39
  # 곡톡 ν•¨μˆ˜
40
  #########################################################
@@ -129,6 +128,7 @@ def load_from_db(keyword, country):
129
  return json.loads(result[0]), convert_to_seoul_time(result[1])
130
  return None, None
131
 
 
132
  #########################################################
133
  # "id"둜 직접 λ‘œλ”©ν•˜κΈ° (νžˆμŠ€ν† λ¦¬μ—μ„œ μ‚¬μš©)
134
  #########################################################
@@ -168,6 +168,10 @@ def display_results(articles):
168
  # (1) 검색 μ‹œ => 기사 + 뢄석 λ™μ‹œ 좜λ ₯, DB μ €μž₯
169
  #########################################################
170
  def search_company(company):
 
 
 
 
171
  error_message, articles = serphouse_search(company, "United States")
172
  if not error_message and articles:
173
  analysis = analyze_sentiment_batch(articles, client)
@@ -185,6 +189,9 @@ def search_company(company):
185
  # (2) 좜λ ₯ μ‹œ => DB에 μ €μž₯된 기사 + 뢄석 ν•¨κ»˜ 좜λ ₯
186
  #########################################################
187
  def load_company(company):
 
 
 
188
  data, timestamp = load_from_db(company, "United States")
189
  if data:
190
  articles = data.get("articles", [])
@@ -252,7 +259,6 @@ def show_stats():
252
 
253
  return output
254
 
255
-
256
  #########################################################
257
  # 전체 검색 (병렬) / 전체 좜λ ₯ / 전체 리포트
258
  #########################################################
@@ -262,6 +268,7 @@ def search_all_companies():
262
  def do_search(comp):
263
  return comp, search_company(comp)
264
 
 
265
  with ThreadPoolExecutor(max_workers=5) as executor:
266
  futures = [executor.submit(do_search, c) for c in KOREAN_COMPANIES]
267
  for future in as_completed(futures):
@@ -345,7 +352,6 @@ def get_custom_search_history():
345
  (id, timestamp, keyword, country) ν˜•νƒœλ‘œ λ°˜ν™˜
346
  μ΅œμ‹ μˆœ μ •λ ¬
347
  """
348
- # set으둜 λ§Œλ“€μ–΄μ„œ λΉ λ₯΄κ²Œ 검색
349
  company_set = set(k.lower() for k in KOREAN_COMPANIES)
350
 
351
  conn = sqlite3.connect("search_results.db")
@@ -360,9 +366,6 @@ def get_custom_search_history():
360
  for (sid, kw, cty, ts) in rows:
361
  # KOREAN_COMPANIES 에 μ—†λŠ” 경우 -> μ‚¬μš©μž μž„μ˜ 검색
362
  if kw.lower() not in company_set:
363
- # "id, μ‹œκ°„, ν‚€μ›Œλ“œ(κ΅­κ°€)" 둜 ν‘œμ‹œν•  ν…μŠ€νŠΈ ꡬ성
364
- # 예: "12 | 2025-01-22 10:23:00 KST | Apple (United States)"
365
- # μ‹€μ œ dropdown value λŠ” sid (id) 만 μ €μž₯
366
  display_time = convert_to_seoul_time(ts)
367
  label = f"{sid} | {display_time} | {kw} ({cty})"
368
  history_list.append((str(sid), label))
@@ -384,7 +387,7 @@ def view_history_record(record_id):
384
  keyword = data["keyword"]
385
  country = data["country"]
386
  timestamp = data["timestamp"]
387
- stored = data["data"] # { articles, analysis }
388
 
389
  articles = stored.get("articles", [])
390
  analysis = stored.get("analysis", "")
@@ -401,46 +404,12 @@ def view_history_record(record_id):
401
 
402
 
403
  #########################################################
404
- # SerpHouse API
405
  #########################################################
406
- def is_english(text):
407
- return all(ord(char) < 128 for char in text.replace(' ', '').replace('-', '').replace('_', ''))
408
-
409
- @lru_cache(maxsize=100)
410
- def translate_query(query, country):
411
- try:
412
- if is_english(query):
413
- return query
414
-
415
- if country in COUNTRY_LANGUAGES:
416
- if country == "South Korea":
417
- return query
418
- target_lang = COUNTRY_LANGUAGES[country]
419
-
420
- url = "https://translate.googleapis.com/translate_a/single"
421
- params = {
422
- "client": "gtx",
423
- "sl": "auto",
424
- "tl": target_lang,
425
- "dt": "t",
426
- "q": query
427
- }
428
-
429
- session = requests.Session()
430
- retries = Retry(total=3, backoff_factor=0.5)
431
- session.mount('https://', HTTPAdapter(max_retries=retries))
432
-
433
- response = session.get(url, params=params, timeout=(5, 10))
434
- translated_text = response.json()[0][0][0]
435
- return translated_text
436
-
437
- return query
438
-
439
- except Exception as e:
440
- print(f"λ²ˆμ—­ 였λ₯˜: {str(e)}")
441
- return query
442
-
443
- def search_serphouse(query, country, page=1, num_result=10):
444
  url = "https://api.serphouse.com/serp/live"
445
 
446
  now = datetime.utcnow()
@@ -492,23 +461,20 @@ def search_serphouse(query, country, page=1, num_result=10):
492
  )
493
 
494
  response.raise_for_status()
495
- return {"results": response.json(), "translated_query": translated_query}
 
 
 
 
 
496
 
497
  except requests.exceptions.Timeout:
498
- return {
499
- "error": "검색 μ‹œκ°„μ΄ μ΄ˆκ³Όλ˜μ—ˆμŠ΅λ‹ˆλ‹€. μž μ‹œ ν›„ λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”.",
500
- "translated_query": query
501
- }
502
  except requests.exceptions.RequestException as e:
503
- return {
504
- "error": f"검색 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}",
505
- "translated_query": query
506
- }
507
  except Exception as e:
508
- return {
509
- "error": f"예기치 μ•Šμ€ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}",
510
- "translated_query": query
511
- }
512
 
513
  def format_results_from_raw(response_data):
514
  if "error" in response_data:
@@ -697,14 +663,14 @@ COUNTRY_LOCATIONS = {
697
  "Bangladesh": "Bangladesh",
698
  "Pakistan": "Pakistan",
699
  "Egypt": "Egypt",
700
- "Morocco": "Morocco",
701
- "Nigeria": "Nigeria",
702
- "Kenya": "Kenya",
703
- "Ukraine": "Ukraine",
704
- "Croatia": "Croatia",
705
- "Slovakia": "Slovakia",
706
- "Bulgaria": "Bulgaria",
707
- "Serbia": "Serbia",
708
  "Estonia": "et",
709
  "Latvia": "lv",
710
  "Lithuania": "lt",
@@ -897,7 +863,7 @@ with gr.Blocks(theme="Yntec/HaleyCH_Theme_Orange", css=css, title="NewsAI μ„œλΉ„
897
 
898
  with gr.Tabs():
899
  # 첫 번째 νƒ­
900
- with gr.Tab("Earnbot"):
901
  gr.Markdown("## EarnBot: κΈ€λ‘œλ²Œ λΉ…ν…Œν¬ κΈ°μ—… 및 투자 전망 AI μžλ™ 뢄석")
902
  gr.Markdown(" - '전체 뢄석 보고 μš”μ•½' 클릭 μ‹œ 전체 μžλ™ 보고 생성.\n - μ•„λž˜ κ°œλ³„ μ’…λͺ©μ˜ '검색(DB μžλ™ μ €μž₯)'κ³Ό '좜λ ₯(DB μžλ™ 호좜)'도 κ°€λŠ₯.\n - ν•˜λ‹¨ 'μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬'μ—μ„œ 이전에 μˆ˜λ™ μž…λ ₯ν•œ 검색어 기둝 확인 κ°€λŠ₯.")
903
 
@@ -958,7 +924,7 @@ with gr.Blocks(theme="Yntec/HaleyCH_Theme_Orange", css=css, title="NewsAI μ„œλΉ„
958
  outputs=result_display
959
  )
960
 
961
- # (μΆ”κ°€) μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬
962
  gr.Markdown("---")
963
  gr.Markdown("### μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬")
964
  with gr.Row():
@@ -966,27 +932,14 @@ with gr.Blocks(theme="Yntec/HaleyCH_Theme_Orange", css=css, title="NewsAI μ„œλΉ„
966
 
967
  history_dropdown = gr.Dropdown(
968
  label="검색 기둝 λͺ©λ‘",
969
- choices=[], # 이 뢀뢄은 click μ΄λ²€νŠΈμ—μ„œ λ™μ μœΌλ‘œ μ—…λ°μ΄νŠΈ
970
  value=None
971
  )
972
  hist_view_btn = gr.Button("보기", variant="primary")
973
  hist_result_display = gr.Markdown()
974
 
975
- # 1) νžˆμŠ€ν† λ¦¬ κ°±μ‹  ν•¨μˆ˜
976
  def update_history_dropdown():
977
- history_list = get_custom_search_history() # [(id, label), ...]
978
- labels = [lbl for (id_val, lbl) in history_list]
979
- values = [id_val for (id_val, lbl) in history_list]
980
-
981
- # gr.Dropdown 에 choices=[..., ...] ν˜•νƒœλ‘œ 전달해야 ν•˜λ―€λ‘œ
982
- # zip ν•˜μ—¬ (value, label) μŒμ„ λ§Œλ“¦
983
- # Gradio 3.xμ—μ„œλŠ” choicesλ₯Ό list둜 μ£Όκ³ , label/value 뢄리 λΆˆκ°€.
984
- # -> workaround: "value|label" 식 or just store label in "choices" and store value separate
985
- # μ—¬κΈ°μ„œλŠ” choices 에 label만 λ„£κ³ , idλŠ” label νŒŒμ‹± λ“± 해야함.
986
- # μ’€ 더 νŽΈν•˜κ²Œ: return a dictionary for "choices"
987
-
988
- # 방법1) label만 전달 -> user picks label -> then parse.
989
- # κ°„λ‹¨νžˆ "value=label"이 λ˜λ„λ‘:
990
  choice_list = []
991
  for (id_val, label) in history_list:
992
  choice_list.append(label)
@@ -998,19 +951,13 @@ with gr.Blocks(theme="Yntec/HaleyCH_Theme_Orange", css=css, title="NewsAI μ„œλΉ„
998
  outputs=history_dropdown
999
  )
1000
 
1001
- # 2) νžˆμŠ€ν† λ¦¬ 보기 ν•¨μˆ˜
1002
  def show_history_record(selected_label):
1003
- # selected_label 이 "id | time | keyword (country)" ν˜•μ‹
1004
  if not selected_label:
1005
  return "νžˆμŠ€ν† λ¦¬κ°€ μ„ νƒλ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."
1006
-
1007
- # idλŠ” κ°€μž₯ μ•žλΆ€λΆ„μ˜ 숫자
1008
- # 예: "12 | 2025-01-22 10:23:00 KST | Apple (United States)"
1009
- # -> id=12
1010
  splitted = selected_label.split("|")
1011
  if len(splitted) < 2:
1012
  return "ν˜•μ‹ 였λ₯˜"
1013
- record_id = splitted[0].strip() # "12"
1014
  return view_history_record(record_id)
1015
 
1016
  hist_view_btn.click(
@@ -1019,11 +966,10 @@ with gr.Blocks(theme="Yntec/HaleyCH_Theme_Orange", css=css, title="NewsAI μ„œλΉ„
1019
  outputs=hist_result_display
1020
  )
1021
 
1022
-
1023
- # 두 번째 νƒ­: "μ§€μ • μžλ™ 검색/뢄석"
1024
- with gr.Tab("μ§€μ • μžλ™ 검색/뢄석"):
1025
  gr.Markdown("## μ‚¬μš©μž μž„μ˜ ν‚€μ›Œλ“œ + κ΅­κ°€ 검색/뢄석")
1026
- gr.Markdown("검색 κ²°κ³Όκ°€ DB에 μ €μž₯되며, 첫 번째 νƒ­μ˜ 'μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬'μ—μ„œ 확인 κ°€λŠ₯ν•©λ‹ˆλ‹€.")
1027
 
1028
  with gr.Row():
1029
  with gr.Column():
@@ -1047,6 +993,33 @@ with gr.Blocks(theme="Yntec/HaleyCH_Theme_Orange", css=css, title="NewsAI μ„œλΉ„
1047
  inputs=[user_input, country_selection],
1048
  outputs=custom_search_output
1049
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1050
 
1051
 
1052
  iface.launch(
 
34
  "investing"
35
  ]
36
 
 
37
  #########################################################
38
  # 곡톡 ν•¨μˆ˜
39
  #########################################################
 
128
  return json.loads(result[0]), convert_to_seoul_time(result[1])
129
  return None, None
130
 
131
+
132
  #########################################################
133
  # "id"둜 직접 λ‘œλ”©ν•˜κΈ° (νžˆμŠ€ν† λ¦¬μ—μ„œ μ‚¬μš©)
134
  #########################################################
 
168
  # (1) 검색 μ‹œ => 기사 + 뢄석 λ™μ‹œ 좜λ ₯, DB μ €μž₯
169
  #########################################################
170
  def search_company(company):
171
+ """
172
+ μ§€μ •λœ KOREAN_COMPANIES κΈ°μ—…(λ˜λŠ” ν‚€μ›Œλ“œ)에 λŒ€ν•΄
173
+ λ―Έκ΅­ λ‰΄μŠ€ 검색 -> 감성 뢄석 -> DB μ €μž₯ -> 기사+뢄석 보고 좜λ ₯
174
+ """
175
  error_message, articles = serphouse_search(company, "United States")
176
  if not error_message and articles:
177
  analysis = analyze_sentiment_batch(articles, client)
 
189
  # (2) 좜λ ₯ μ‹œ => DB에 μ €μž₯된 기사 + 뢄석 ν•¨κ»˜ 좜λ ₯
190
  #########################################################
191
  def load_company(company):
192
+ """
193
+ DB에 이미 μ €μž₯된 (κΈ°μ—…λͺ…) 에 λŒ€ν•œ 기사+뢄석을 ν•¨κ»˜ 좜λ ₯
194
+ """
195
  data, timestamp = load_from_db(company, "United States")
196
  if data:
197
  articles = data.get("articles", [])
 
259
 
260
  return output
261
 
 
262
  #########################################################
263
  # 전체 검색 (병렬) / 전체 좜λ ₯ / 전체 리포트
264
  #########################################################
 
268
  def do_search(comp):
269
  return comp, search_company(comp)
270
 
271
+ # 병렬 처리
272
  with ThreadPoolExecutor(max_workers=5) as executor:
273
  futures = [executor.submit(do_search, c) for c in KOREAN_COMPANIES]
274
  for future in as_completed(futures):
 
352
  (id, timestamp, keyword, country) ν˜•νƒœλ‘œ λ°˜ν™˜
353
  μ΅œμ‹ μˆœ μ •λ ¬
354
  """
 
355
  company_set = set(k.lower() for k in KOREAN_COMPANIES)
356
 
357
  conn = sqlite3.connect("search_results.db")
 
366
  for (sid, kw, cty, ts) in rows:
367
  # KOREAN_COMPANIES 에 μ—†λŠ” 경우 -> μ‚¬μš©μž μž„μ˜ 검색
368
  if kw.lower() not in company_set:
 
 
 
369
  display_time = convert_to_seoul_time(ts)
370
  label = f"{sid} | {display_time} | {kw} ({cty})"
371
  history_list.append((str(sid), label))
 
387
  keyword = data["keyword"]
388
  country = data["country"]
389
  timestamp = data["timestamp"]
390
+ stored = data["data"]
391
 
392
  articles = stored.get("articles", [])
393
  analysis = stored.get("analysis", "")
 
404
 
405
 
406
  #########################################################
407
+ # SerpHouse API (ν•¨μˆ˜λͺ… μˆ˜μ •) => serphouse_search
408
  #########################################################
409
+ def serphouse_search(query, country, page=1, num_result=10):
410
+ """
411
+ SerpHouse API 호좜 + κ²°κ³Ό ν¬λ§€νŒ…
412
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  url = "https://api.serphouse.com/serp/live"
414
 
415
  now = datetime.utcnow()
 
461
  )
462
 
463
  response.raise_for_status()
464
+ response_data = response.json()
465
+
466
+ return format_results_from_raw({
467
+ "results": response_data,
468
+ "translated_query": translated_query
469
+ })
470
 
471
  except requests.exceptions.Timeout:
472
+ return ("검색 μ‹œκ°„μ΄ μ΄ˆκ³Όλ˜μ—ˆμŠ΅λ‹ˆλ‹€. μž μ‹œ ν›„ λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”.", [])
 
 
 
473
  except requests.exceptions.RequestException as e:
474
+ return (f"검색 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}", [])
 
 
 
475
  except Exception as e:
476
+ return (f"예기치 μ•Šμ€ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}", [])
477
+
 
 
478
 
479
  def format_results_from_raw(response_data):
480
  if "error" in response_data:
 
663
  "Bangladesh": "Bangladesh",
664
  "Pakistan": "Pakistan",
665
  "Egypt": "Egypt",
666
+ "Morocco": "ar",
667
+ "Nigeria": "en",
668
+ "Kenya": "sw",
669
+ "Ukraine": "uk",
670
+ "Croatia": "hr",
671
+ "Slovakia": "sk",
672
+ "Bulgaria": "bg",
673
+ "Serbia": "sr",
674
  "Estonia": "et",
675
  "Latvia": "lv",
676
  "Lithuania": "lt",
 
863
 
864
  with gr.Tabs():
865
  # 첫 번째 νƒ­
866
+ with gr.Tab("μ§€μ • μžλ™ 검색/뢄석"):
867
  gr.Markdown("## EarnBot: κΈ€λ‘œλ²Œ λΉ…ν…Œν¬ κΈ°μ—… 및 투자 전망 AI μžλ™ 뢄석")
868
  gr.Markdown(" - '전체 뢄석 보고 μš”μ•½' 클릭 μ‹œ 전체 μžλ™ 보고 생성.\n - μ•„λž˜ κ°œλ³„ μ’…λͺ©μ˜ '검색(DB μžλ™ μ €μž₯)'κ³Ό '좜λ ₯(DB μžλ™ 호좜)'도 κ°€λŠ₯.\n - ν•˜λ‹¨ 'μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬'μ—μ„œ 이전에 μˆ˜λ™ μž…λ ₯ν•œ 검색어 기둝 확인 κ°€λŠ₯.")
869
 
 
924
  outputs=result_display
925
  )
926
 
927
+ # (μΆ”κ°€) μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬ (첫 번째 νƒ­)
928
  gr.Markdown("---")
929
  gr.Markdown("### μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬")
930
  with gr.Row():
 
932
 
933
  history_dropdown = gr.Dropdown(
934
  label="검색 기둝 λͺ©λ‘",
935
+ choices=[],
936
  value=None
937
  )
938
  hist_view_btn = gr.Button("보기", variant="primary")
939
  hist_result_display = gr.Markdown()
940
 
 
941
  def update_history_dropdown():
942
+ history_list = get_custom_search_history()
 
 
 
 
 
 
 
 
 
 
 
 
943
  choice_list = []
944
  for (id_val, label) in history_list:
945
  choice_list.append(label)
 
951
  outputs=history_dropdown
952
  )
953
 
 
954
  def show_history_record(selected_label):
 
955
  if not selected_label:
956
  return "νžˆμŠ€ν† λ¦¬κ°€ μ„ νƒλ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."
 
 
 
 
957
  splitted = selected_label.split("|")
958
  if len(splitted) < 2:
959
  return "ν˜•μ‹ 였λ₯˜"
960
+ record_id = splitted[0].strip()
961
  return view_history_record(record_id)
962
 
963
  hist_view_btn.click(
 
966
  outputs=hist_result_display
967
  )
968
 
969
+ # 두 번째 νƒ­: "μˆ˜λ™ 검색/뢄석" + νžˆμŠ€ν† λ¦¬
970
+ with gr.Tab("μˆ˜λ™ 검색/뢄석"):
 
971
  gr.Markdown("## μ‚¬μš©μž μž„μ˜ ν‚€μ›Œλ“œ + κ΅­κ°€ 검색/뢄석")
972
+ gr.Markdown("검색 κ²°κ³Όκ°€ DB에 μ €μž₯되며, μ•„λž˜ 'μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬'μ—μ„œλ„ 확인 κ°€λŠ₯ν•©λ‹ˆλ‹€.")
973
 
974
  with gr.Row():
975
  with gr.Column():
 
993
  inputs=[user_input, country_selection],
994
  outputs=custom_search_output
995
  )
996
+
997
+ # λ™μΌν•˜κ²Œ, 두 번째 탭에도 νžˆμŠ€ν† λ¦¬ 적용
998
+ gr.Markdown("---")
999
+ gr.Markdown("### μˆ˜λ™ 검색 νžˆμŠ€ν† λ¦¬ (두 번째 νƒ­)")
1000
+ with gr.Row():
1001
+ refresh_hist_btn2 = gr.Button("νžˆμŠ€ν† λ¦¬ κ°±μ‹ ", variant="secondary")
1002
+
1003
+ history_dropdown2 = gr.Dropdown(
1004
+ label="검색 기둝 λͺ©λ‘",
1005
+ choices=[],
1006
+ value=None
1007
+ )
1008
+ hist_view_btn2 = gr.Button("보기", variant="primary")
1009
+ hist_result_display2 = gr.Markdown()
1010
+
1011
+ # 동일 둜직 μž¬μ‚¬μš©
1012
+ refresh_hist_btn2.click(
1013
+ fn=update_history_dropdown,
1014
+ inputs=[],
1015
+ outputs=history_dropdown2
1016
+ )
1017
+
1018
+ hist_view_btn2.click(
1019
+ fn=show_history_record,
1020
+ inputs=[history_dropdown2],
1021
+ outputs=hist_result_display2
1022
+ )
1023
 
1024
 
1025
  iface.launch(