mai / app.py
Sooteemon's picture
Update app.py
d5e84de verified
import gradio as gr
import pandas as pd
from scraper import YahooFinanceScraper
from sentiment_analyzer import NewsAnalyzer # Import new class
import time
from collections import Counter # For counting themes/impacts
# --- MODIFIED BLOCK 1 ---
# เราจะโหลด Scraper (เบา) แต่จะยังไม่โหลด Analyzer (หนัก)
print("Initializing Yahoo Finance News Analyzer...")
scraper = YahooFinanceScraper()
analyzer = None # <-- ตั้งเป็น None ก่อน จะโหลดเมื่อถูกเรียกใช้ครั้งแรก
# --- END MODIFIED BLOCK ---
def get_sentiment_color(sentiment):
"""กำหนดสีตาม sentiment"""
colors = { "Positive": "🟢", "Negative": "🔴", "Neutral": "🟡" }
return colors.get(sentiment, "⚪")
def get_impact_color(impact):
"""กำหนดสีตาม impact"""
colors = { "Opportunity": "🟢", "Risk": "🔴", "Neutral": "🟡" }
return colors.get(impact, "⚪")
def analyze_news(search_type, search_input, num_articles):
"""
ฟังก์ชันหลักในการวิเคราะห์ข่าว
"""
# --- MODIFIED BLOCK 2: LAZY LOADING ---
# ตรวจสอบว่า analyzer ถูกโหลดหรือยัง
global analyzer
if analyzer is None:
# ถ้ายัง ให้แสดงสถานะว่ากำลังโหลด
yield "⌛ กำลังโหลดโมเดล AI (ครั้งแรก)...", None, "⌛ กำลังโหลดโมเดล AI (ครั้งแรก)..."
print("Analyzer not loaded. Initializing NewsAnalyzer (lazy)...")
# นี่คือจุดที่จะโหลดโมเดล (ใช้เวลา 1-2 นาทีในครั้งแรก)
analyzer = NewsAnalyzer()
print("Analyzer loaded successfully.")
# --- END MODIFIED BLOCK ---
try:
yield "กำลังดึงข่าว...", None, "กำลังดึงข่าว..." # <-- แก้ไข Output ที่ 3 ด้วย
if search_type == "Latest News":
news_list = scraper.get_latest_news(max_articles=int(num_articles))
elif search_type == "Stock Symbol":
if not search_input:
yield "⚠️ กรุณาใส่ ticker symbol (เช่น AAPL, TSLA)", None, None
return
news_list = scraper.get_latest_news(symbol=search_input.upper(), max_articles=int(num_articles))
else: # Keyword search
if not search_input:
yield "⚠️ กรุณาใส่คำค้นหา", None, None
return
news_list = scraper.search_news(keyword=search_input, max_articles=int(num_articles))
if not news_list:
yield "❌ ไม่พบข่าว กรุณาลองใหม่อีกครั้ง", None, None
return
yield f"พบข่าว {len(news_list)} รายการ | กำลังวิเคราะห์...", None, f"พบข่าว {len(news_list)} รายการ | กำลังวิเคราะห์..."
results = analyzer.analyze_batch(news_list)
# (ส่วนที่เหลือของฟังก์ชันเหมือนเดิม)
total = len(results)
positive = sum(1 for r in results if r['sentiment'] == 'Positive')
negative = sum(1 for r in results if r['sentiment'] == 'Negative')
neutral = sum(1 for r in results if r['sentiment'] == 'Neutral')
avg_score = sum(r['score'] for r in results) / total if total > 0 else 0
theme_counts = Counter(r.get('theme', 'N/A') for r in results)
impact_counts = Counter(r.get('impact', 'N/A') for r in results)
if positive > negative and positive > neutral:
overall = "📈 Positive (Bullish)"
elif negative > positive and negative > neutral:
overall = "📉 Negative (Bearish)"
else:
overall = "📊 Neutral"
summary = f"""## 📊 สรุปผลการวิเคราะห์
**ภาพรวม:** {overall} | **คะแนนเฉลี่ย:** {avg_score:.2f}/1.0
### การกระจาย Sentiment
- 🟢 Positive: {positive} ({positive/total*100:.1f}%)
- 🔴 Negative: {negative} ({negative/total*100:.1f}%)
- 🟡 Neutral: {neutral} ({neutral/total*100:.1f}%)
---
### 📌 สรุปหัวข้อ (Themes)
"""
for theme, count in theme_counts.most_common():
summary += f"- **{theme}:** {count} รายการ ({count/total*100:.1f}%)\n"
summary += "\n---\n" # เพิ่มเส้นคั่น
summary += "\n### ⚡ สรุปผลกระทบ (Impact)\n"
for impact, count in impact_counts.most_common():
emoji = get_impact_color(impact)
summary += f"- {emoji} **{impact}:** {count} รายการ ({count/total*100:.1f}%)\n"
summary += "\n\n---\n\n## 📰 รายละเอียดแต่ละข่าว\n\n"
detailed_results = ""
for i, result in enumerate(results, 1):
s_emoji = get_sentiment_color(result['sentiment'])
i_emoji = get_impact_color(result.get('impact', 'N/A'))
detailed_results += f"""### {i}. {s_emoji} {result['title']}
**Theme:** {result.get('theme', 'N/A')} | **Impact:** {i_emoji} {result.get('impact', 'N/A')}
**Sentiment:** {result['sentiment']} | **Score:** {result['score']:.2f}
**เผยแพร่:** {result['published']}
**คำอธิบาย:** {result['explanation']}
{result['summary'][:200]}{'...' if len(result['summary']) > 200 else ''}
[🔗 อ่านต่อ]({result['link']})
---"""
df_data = []
for result in results:
df_data.append({
'Title': result['title'][:60] + '...' if len(result['title']) > 60 else result['title'],
'Theme': result.get('theme', 'N/A'),
'Impact': f"{get_impact_color(result.get('impact', 'N/A'))} {result.get('impact', 'N/A')}",
'Sentiment': f"{get_sentiment_color(result['sentiment'])} {result['sentiment']}",
'Score': f"{result['score']:.2f}",
'Published': result['published'],
'Link': result['link']
})
df = pd.DataFrame(df_data)
final_output = summary + detailed_results
yield final_output, df, "✅ วิเคราะห์เสร็จสมบูรณ์!"
except Exception as e:
error_msg = f"❌ เกิดข้อผิดพลาด: {str(e)}"
yield error_msg, None, error_msg
# --- (Interface Block: ไม่มีการเปลี่ยนแปลง) ---
with gr.Blocks(title="Yahoo Finance News Analyzer", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 📈 Yahoo Finance News Analyzer
วิเคราะห์ข่าวการเงินจาก Yahoo Finance ด้วย AI แบบครบวงจร
🔍 **ฟีเจอร์:**
- วิเคราะห์ Sentiment (ความรู้สึก)
- วิเคราะห์ Theme (หัวข้อข่าว)
- วิเคราะห์ Impact (ผลกระทบ: โอกาส หรือ ความเสี่ยง)
- ให้คะแนนความมั่นใจและคำอธิบาย
- รองรับค้นหาตาม Stock Symbol และ Keyword
""")
with gr.Row():
with gr.Column(scale=1):
search_type = gr.Radio(
choices=["Latest News", "Stock Symbol", "Keyword"],
value="Latest News",
label="ประเภทการค้นหา"
)
search_input = gr.Textbox(
label="Ticker Symbol / Keyword",
placeholder="เช่น AAPL, TSLA, AI, cryptocurrency",
info="ใส่เฉพาะเมื่อเลือก Stock Symbol หรือ Keyword"
)
num_articles = gr.Slider(
minimum=5,
maximum=20,
value=10,
step=1,
label="จำนวนข่าว"
)
analyze_btn = gr.Button("🔍 วิเคราะห์ข่าว", variant="primary", size="lg")
status = gr.Textbox(label="สถานะ", interactive=False)
with gr.Column(scale=2):
results_md = gr.Markdown("### กดปุ่ม 'วิเคราะห์ข่าว' เพื่อเริ่มต้น")
with gr.Row():
results_table = gr.Dataframe(
label="📊 ตารางสรุปผล",
wrap=True
)
gr.Markdown("""
---
### 💡 วิธีใช้งาน:
1. เลือกประเภทการค้นหา (ข่าวล่าสุด / หุ้นเฉพาะ / คำค้นหา)
2. ใส่ ticker symbol หรือ keyword (ถ้าต้องการ)
3. เลือกจำนวนข่าวที่ต้องการวิเคราะห์
4. กดปุ่ม "วิเคราะห์ข่าว"
### 🎯 ตัวอย่าง Stock Symbols:
- **AAPL** - Apple Inc.
- **TSLA** - Tesla
- **NVDA** - Nvidia
- **MSFT** - Microsoft
- **GOOGL** - Google
### 📌 หมายเหตุ:
- **Sentiment:** Positive (ดี), Negative (แย่), Neutral (กลาง)
- **Theme:** หัวข้อหลักของข่าว (เช่น ผลประกอบการ, สินค้าใหม่)
- **Impact:** ผลกระทบต่อบริษัท (🟢 Opportunity - โอกาส, 🔴 Risk - ความเสี่ยง)
""")
analyze_btn.click(
fn=analyze_news,
inputs=[search_type, search_input, num_articles],
outputs=[results_md, results_table, status]
)
# Launch app
if __name__ == "__main__":
demo.queue()
demo.launch(share=False)