Spaces:
openfree
/
Running on CPU Upgrade

MoneyRadar / app.py
ginipick's picture
Update app.py
4649353 verified
raw
history blame
32.7 kB
import gradio as gr
import requests
import json
import os
from datetime import datetime, timedelta
from concurrent.futures import ThreadPoolExecutor, as_completed
from functools import lru_cache
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from openai import OpenAI
from bs4 import BeautifulSoup
import re
import pathlib
import sqlite3
import pytz
# List of target companies/keywords
KOREAN_COMPANIES = [
"NVIDIA",
"ALPHABET",
"APPLE",
"TESLA",
"AMAZON",
"MICROSOFT",
"META",
"INTEL",
"SAMSUNG",
"HYNIX",
"BITCOIN",
"crypto",
"stock",
"Economics",
"Finance",
"investing"
]
def convert_to_seoul_time(timestamp_str):
"""
Convert a given timestamp string (UTC) to Seoul time (KST).
"""
try:
dt = datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S')
seoul_tz = pytz.timezone('Asia/Seoul')
seoul_time = seoul_tz.localize(dt)
return seoul_time.strftime('%Y-%m-%d %H:%M:%S KST')
except Exception as e:
print(f"Time conversion error: {str(e)}")
return timestamp_str
def analyze_sentiment_batch(articles, client):
"""
Perform a comprehensive sentiment analysis of the news articles using the OpenAI API.
"""
try:
# Combine all articles into a single text
combined_text = "\n\n".join([
f"Title: {article.get('title', '')}\nContent: {article.get('snippet', '')}"
for article in articles
])
prompt = f"""Please perform an overall sentiment analysis of the following collection of news articles:
News content:
{combined_text}
Please follow this format:
1. Overall Sentiment: [Positive/Negative/Neutral]
2. Key Positive Factors:
- [Item1]
- [Item2]
3. Key Negative Factors:
- [Item1]
- [Item2]
4. Summary: [Detailed explanation]
"""
response = client.chat.completions.create(
model="CohereForAI/c4ai-command-r-plus-08-2024",
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=1000
)
return response.choices[0].message.content
except Exception as e:
return f"Sentiment analysis failed: {str(e)}"
# Initialize the database
def init_db():
"""
Initialize the SQLite database (search_results.db) if it doesn't already exist.
"""
db_path = pathlib.Path("search_results.db")
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS searches
(id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT,
country TEXT,
results TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)''')
conn.commit()
conn.close()
def save_to_db(keyword, country, results):
"""
Save the search results for a specific (keyword, country) combination into the database.
"""
conn = sqlite3.connect("search_results.db")
c = conn.cursor()
seoul_tz = pytz.timezone('Asia/Seoul')
now = datetime.now(seoul_tz)
timestamp = now.strftime('%Y-%m-%d %H:%M:%S')
c.execute("""INSERT INTO searches
(keyword, country, results, timestamp)
VALUES (?, ?, ?, ?)""",
(keyword, country, json.dumps(results), timestamp))
conn.commit()
conn.close()
def load_from_db(keyword, country):
"""
Load the most recent search results for a specific (keyword, country) combination from the database.
Returns the data and the timestamp.
"""
conn = sqlite3.connect("search_results.db")
c = conn.cursor()
c.execute(
"SELECT results, timestamp FROM searches WHERE keyword=? AND country=? ORDER BY timestamp DESC LIMIT 1",
(keyword, country)
)
result = c.fetchone()
conn.close()
if result:
return json.loads(result[0]), convert_to_seoul_time(result[1])
return None, None
def display_results(articles):
"""
Convert a list of news articles into a Markdown string for display.
"""
output = ""
for idx, article in enumerate(articles, 1):
output += f"### {idx}. {article['title']}\n"
output += f"Source: {article['channel']}\n"
output += f"Time: {article['time']}\n"
output += f"Link: {article['link']}\n"
output += f"Summary: {article['snippet']}\n\n"
return output
########################################
# 1) Search => Articles + Analysis, then save to DB
########################################
def search_company(company):
"""
For a single company (or keyword), search US news.
1) Retrieve a list of articles
2) Perform sentiment analysis
3) Save results to DB
4) Return (articles + analysis) in a single output.
"""
error_message, articles = serphouse_search(company, "United States")
if not error_message and articles:
# Perform sentiment analysis
analysis = analyze_sentiment_batch(articles, client)
# Prepare data to save in DB
store_dict = {
"articles": articles,
"analysis": analysis
}
save_to_db(company, "United States", store_dict)
# Prepare output for display
output = display_results(articles)
output += f"\n\n### Analysis Report\n{analysis}\n"
return output
return f"No search results found for {company}."
########################################
# 2) Load => Return articles + analysis from DB
########################################
def load_company(company):
"""
Load the most recent US news search results for the given company (or keyword) from the database,
and return the articles + analysis in a single output.
"""
data, timestamp = load_from_db(company, "United States")
if data:
articles = data.get("articles", [])
analysis = data.get("analysis", "")
output = f"### {company} Search Results\nLast Updated: {timestamp}\n\n"
output += display_results(articles)
output += f"\n\n### Analysis Report\n{analysis}\n"
return output
return f"No saved results for {company}."
########################################
# 3) Updated show_stats() with new title
########################################
def show_stats():
"""
For each company in KOREAN_COMPANIES:
- Retrieve the most recent timestamp in DB
- Number of articles
- Sentiment analysis result
Return these in a report format.
Title changed to: "EarnBOT Analysis Report"
"""
conn = sqlite3.connect("search_results.db")
c = conn.cursor()
output = "## EarnBOT Analysis Report\n\n"
data_list = []
for company in KOREAN_COMPANIES:
c.execute("""
SELECT results, timestamp
FROM searches
WHERE keyword = ?
ORDER BY timestamp DESC
LIMIT 1
""", (company,))
row = c.fetchone()
if row:
results_json, timestamp = row
data_list.append((company, timestamp, results_json))
conn.close()
def analyze_data(item):
comp, tstamp, results_json = item
data = json.loads(results_json)
articles = data.get("articles", [])
analysis = data.get("analysis", "")
count_articles = len(articles)
return (comp, tstamp, count_articles, analysis)
results_list = []
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(analyze_data, dl) for dl in data_list]
for future in as_completed(futures):
results_list.append(future.result())
for comp, tstamp, count, analysis in results_list:
seoul_time = convert_to_seoul_time(tstamp)
output += f"### {comp}\n"
output += f"- Last updated: {seoul_time}\n"
output += f"- Number of articles stored: {count}\n\n"
if analysis:
output += "#### News Sentiment Analysis\n"
output += f"{analysis}\n\n"
output += "---\n\n"
return output
def search_all_companies():
"""
Search all companies in KOREAN_COMPANIES (in parallel),
perform sentiment analysis + save to DB => return Markdown of all results.
"""
overall_result = "# [Search Results for All Companies]\n\n"
def do_search(comp):
return comp, search_company(comp)
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(do_search, c) for c in KOREAN_COMPANIES]
for future in as_completed(futures):
comp, res_text = future.result()
overall_result += f"## {comp}\n"
overall_result += res_text + "\n\n"
return overall_result
def load_all_companies():
"""
Load articles + analysis for all companies in KOREAN_COMPANIES from the DB => return Markdown.
"""
overall_result = "# [All Companies Data Output]\n\n"
for comp in KOREAN_COMPANIES:
overall_result += f"## {comp}\n"
overall_result += load_company(comp)
overall_result += "\n"
return overall_result
def full_summary_report():
"""
1) Search all companies (in parallel) -> 2) Load results -> 3) Show sentiment analysis stats
Return a combined report with all three steps.
"""
# 1) Search all companies => store to DB
search_result_text = search_all_companies()
# 2) Load all results => from DB
load_result_text = load_all_companies()
# 3) Show stats => EarnBOT Analysis Report
stats_text = show_stats()
combined_report = (
"# Full Analysis Summary Report\n\n"
"Executed in the following order:\n"
"1. Search all companies (parallel) + sentiment analysis => 2. Load results from DB => 3. Show overall sentiment analysis stats\n\n"
f"{search_result_text}\n\n"
f"{load_result_text}\n\n"
"## [Overall Sentiment Analysis Stats]\n\n"
f"{stats_text}"
)
return combined_report
########################################
# Additional feature: User custom search
########################################
def search_custom(query, country):
"""
For a user-provided (query, country):
1) Search + sentiment analysis => save to DB
2) Load from DB => display articles + analysis
"""
error_message, articles = serphouse_search(query, country)
if error_message:
return f"An error occurred: {error_message}"
if not articles:
return "No results were found for your query."
# 1) Perform analysis
analysis = analyze_sentiment_batch(articles, client)
# 2) Save to DB
save_data = {
"articles": articles,
"analysis": analysis
}
save_to_db(query, country, save_data)
# 3) Reload from DB
loaded_data, timestamp = load_from_db(query, country)
if not loaded_data:
return "Failed to load data from DB."
# 4) Prepare final output
out = f"## [Custom Search Results]\n\n"
out += f"**Keyword**: {query}\n\n"
out += f"**Country**: {country}\n\n"
out += f"**Timestamp**: {timestamp}\n\n"
arts = loaded_data.get("articles", [])
analy = loaded_data.get("analysis", "")
out += display_results(arts)
out += f"### News Sentiment Analysis\n{analy}\n"
return out
########################################
# API Authentication
########################################
ACCESS_TOKEN = os.getenv("HF_TOKEN")
if not ACCESS_TOKEN:
raise ValueError("HF_TOKEN environment variable is not set")
client = OpenAI(
base_url="https://api-inference.huggingface.co/v1/",
api_key=ACCESS_TOKEN,
)
API_KEY = os.getenv("SERPHOUSE_API_KEY")
########################################
# Country-specific settings
########################################
COUNTRY_LANGUAGES = {
"United States": "en",
"KOREA": "ko",
"United Kingdom": "en",
"Taiwan": "zh-TW",
"Canada": "en",
"Australia": "en",
"Germany": "de",
"France": "fr",
"Japan": "ja",
"China": "zh",
"India": "hi",
"Brazil": "pt",
"Mexico": "es",
"Russia": "ru",
"Italy": "it",
"Spain": "es",
"Netherlands": "nl",
"Singapore": "en",
"Hong Kong": "zh-HK",
"Indonesia": "id",
"Malaysia": "ms",
"Philippines": "tl",
"Thailand": "th",
"Vietnam": "vi",
"Belgium": "nl",
"Denmark": "da",
"Finland": "fi",
"Ireland": "en",
"Norway": "no",
"Poland": "pl",
"Sweden": "sv",
"Switzerland": "de",
"Austria": "de",
"Czech Republic": "cs",
"Greece": "el",
"Hungary": "hu",
"Portugal": "pt",
"Romania": "ro",
"Turkey": "tr",
"Israel": "he",
"Saudi Arabia": "ar",
"United Arab Emirates": "ar",
"South Africa": "en",
"Argentina": "es",
"Chile": "es",
"Colombia": "es",
"Peru": "es",
"Venezuela": "es",
"New Zealand": "en",
"Bangladesh": "bn",
"Pakistan": "ur",
"Egypt": "ar",
"Morocco": "ar",
"Nigeria": "en",
"Kenya": "sw",
"Ukraine": "uk",
"Croatia": "hr",
"Slovakia": "sk",
"Bulgaria": "bg",
"Serbia": "sr",
"Estonia": "et",
"Latvia": "lv",
"Lithuania": "lt",
"Slovenia": "sl",
"Luxembourg": "Luxembourg",
"Malta": "Malta",
"Cyprus": "Cyprus",
"Iceland": "Iceland"
}
COUNTRY_LOCATIONS = {
"United States": "United States",
"KOREA": "kr",
"United Kingdom": "United Kingdom",
"Taiwan": "Taiwan",
"Canada": "Canada",
"Australia": "Australia",
"Germany": "Germany",
"France": "France",
"Japan": "Japan",
"China": "China",
"India": "India",
"Brazil": "Brazil",
"Mexico": "Mexico",
"Russia": "Russia",
"Italy": "Italy",
"Spain": "Spain",
"Netherlands": "Netherlands",
"Singapore": "Singapore",
"Hong Kong": "Hong Kong",
"Indonesia": "Indonesia",
"Malaysia": "Malaysia",
"Philippines": "Philippines",
"Thailand": "Thailand",
"Vietnam": "Vietnam",
"Belgium": "Belgium",
"Denmark": "Denmark",
"Finland": "Finland",
"Ireland": "Ireland",
"Norway": "Norway",
"Poland": "Poland",
"Sweden": "Sweden",
"Switzerland": "Switzerland",
"Austria": "Austria",
"Czech Republic": "Czech Republic",
"Greece": "Greece",
"Hungary": "Hungary",
"Portugal": "Portugal",
"Romania": "Romania",
"Turkey": "Turkey",
"Israel": "Israel",
"Saudi Arabia": "Saudi Arabia",
"United Arab Emirates": "United Arab Emirates",
"South Africa": "South Africa",
"Argentina": "Argentina",
"Chile": "Chile",
"Colombia": "Colombia",
"Peru": "Peru",
"Venezuela": "Venezuela",
"New Zealand": "New Zealand",
"Bangladesh": "Bangladesh",
"Pakistan": "Pakistan",
"Egypt": "Egypt",
"Morocco": "Morocco",
"Nigeria": "Nigeria",
"Kenya": "Kenya",
"Ukraine": "Ukraine",
"Croatia": "Croatia",
"Slovakia": "Slovakia",
"Bulgaria": "Bulgaria",
"Serbia": "Serbia",
"Estonia": "et",
"Latvia": "lv",
"Lithuania": "lt",
"Slovenia": "sl",
"Luxembourg": "Luxembourg",
"Malta": "Malta",
"Cyprus": "Cyprus",
"Iceland": "Iceland"
}
@lru_cache(maxsize=100)
def translate_query(query, country):
"""
Use the unofficial Google Translation API to translate the query into the target country's language.
If the query is already in English, or if translation fails, return the original query.
"""
try:
if is_english(query):
return query
if country in COUNTRY_LANGUAGES:
if country == "South Korea":
return query
target_lang = COUNTRY_LANGUAGES[country]
url = "https://translate.googleapis.com/translate_a/single"
params = {
"client": "gtx",
"sl": "auto",
"tl": target_lang,
"dt": "t",
"q": query
}
session = requests.Session()
retries = Retry(total=3, backoff_factor=0.5)
session.mount('https://', HTTPAdapter(max_retries=retries))
response = session.get(url, params=params, timeout=(5, 10))
translated_text = response.json()[0][0][0]
return translated_text
return query
except Exception as e:
print(f"Translation error: {str(e)}")
return query
def is_english(text):
"""
Check if a string is (mostly) English by verifying character code ranges.
"""
return all(ord(char) < 128 for char in text.replace(' ', '').replace('-', '').replace('_', ''))
def search_serphouse(query, country, page=1, num_result=10):
"""
Send a real-time search request to the SerpHouse API,
specifying the 'news' tab (sort_by=date) for the given query.
Returns a dict with 'results' or 'error'.
"""
url = "https://api.serphouse.com/serp/live"
now = datetime.utcnow()
yesterday = now - timedelta(days=1)
date_range = f"{yesterday.strftime('%Y-%m-%d')},{now.strftime('%Y-%m-%d')}"
translated_query = translate_query(query, country)
payload = {
"data": {
"q": translated_query,
"domain": "google.com",
"loc": COUNTRY_LOCATIONS.get(country, "United States"),
"lang": COUNTRY_LANGUAGES.get(country, "en"),
"device": "desktop",
"serp_type": "news",
"page": str(page),
"num": "100",
"date_range": date_range,
"sort_by": "date"
}
}
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": f"Bearer {API_KEY}"
}
try:
session = requests.Session()
retries = Retry(
total=5,
backoff_factor=1,
status_forcelist=[500, 502, 503, 504, 429],
allowed_methods=["POST"]
)
adapter = HTTPAdapter(max_retries=retries)
session.mount('http://', adapter)
session.mount('https://', adapter)
response = session.post(
url,
json=payload,
headers=headers,
timeout=(30, 30)
)
response.raise_for_status()
return {"results": response.json(), "translated_query": translated_query}
except requests.exceptions.Timeout:
return {
"error": "Search timed out. Please try again later.",
"translated_query": query
}
except requests.exceptions.RequestException as e:
return {
"error": f"Error during search: {str(e)}",
"translated_query": query
}
except Exception as e:
return {
"error": f"Unexpected error occurred: {str(e)}",
"translated_query": query
}
def format_results_from_raw(response_data):
"""
Process the SerpHouse API response data and return (error_message, article_list).
"""
if "error" in response_data:
return "Error: " + response_data["error"], []
try:
results = response_data["results"]
translated_query = response_data["translated_query"]
news_results = results.get('results', {}).get('results', {}).get('news', [])
if not news_results:
return "No search results found.", []
# Filter out Korean domains and Korean keywords (example filtering)
korean_domains = [
'.kr', 'korea', 'korean', 'yonhap', 'hankyung', 'chosun',
'donga', 'joins', 'hani', 'koreatimes', 'koreaherald'
]
korean_keywords = [
'korea', 'korean', 'seoul', 'busan', 'incheon', 'daegu',
'gwangju', 'daejeon', 'ulsan', 'sejong'
]
filtered_articles = []
for idx, result in enumerate(news_results, 1):
url = result.get("url", result.get("link", "")).lower()
title = result.get("title", "").lower()
channel = result.get("channel", result.get("source", "")).lower()
is_korean_content = (
any(domain in url or domain in channel for domain in korean_domains) or
any(keyword in title for keyword in korean_keywords)
)
# Exclude Korean content
if not is_korean_content:
filtered_articles.append({
"index": idx,
"title": result.get("title", "No Title"),
"link": url,
"snippet": result.get("snippet", "No Content"),
"channel": result.get("channel", result.get("source", "Unknown")),
"time": result.get("time", result.get("date", "Unknown Time")),
"image_url": result.get("img", result.get("thumbnail", "")),
"translated_query": translated_query
})
return "", filtered_articles
except Exception as e:
return f"Error processing results: {str(e)}", []
def serphouse_search(query, country):
"""
Helper function to search and then format results.
Returns (error_message, article_list).
"""
response_data = search_serphouse(query, country)
return format_results_from_raw(response_data)
# Refined, modern, and sleek custom CSS
css = """
body {
background: linear-gradient(to bottom right, #f9fafb, #ffffff);
font-family: 'Arial', sans-serif;
}
/* Hide default Gradio footer */
footer {
visibility: hidden;
}
/* Header/Status area */
#status_area {
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-bottom: 1px solid #ddd;
margin-bottom: 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
/* Results area */
#results_area {
padding: 10px;
margin-top: 10px;
}
/* Tabs style */
.tabs {
border-bottom: 2px solid #ddd !important;
margin-bottom: 20px !important;
}
.tab-nav {
border-bottom: none !important;
margin-bottom: 0 !important;
}
.tab-nav button {
font-weight: bold !important;
padding: 10px 20px !important;
background-color: #f0f0f0 !important;
border: 1px solid #ccc !important;
border-radius: 5px !important;
margin-right: 5px !important;
}
.tab-nav button.selected {
border-bottom: 2px solid #1f77b4 !important;
background-color: #e6f2fa !important;
color: #1f77b4 !important;
}
/* Status message styling */
#status_area .markdown-text {
font-size: 1.1em;
color: #2c3e50;
padding: 10px 0;
}
/* Main container grouping */
.group {
border: 1px solid #eee;
padding: 15px;
margin-bottom: 15px;
border-radius: 5px;
background: white;
transition: all 0.3s ease;
opacity: 0;
transform: translateY(20px);
}
.group.visible {
opacity: 1;
transform: translateY(0);
}
/* Buttons */
.primary-btn {
background: #1f77b4 !important;
border: none !important;
color: #fff !important;
border-radius: 5px !important;
padding: 10px 20px !important;
cursor: pointer !important;
}
.primary-btn:hover {
background: #155a8c !important;
}
.secondary-btn {
background: #f0f0f0 !important;
border: 1px solid #ccc !important;
color: #333 !important;
border-radius: 5px !important;
padding: 10px 20px !important;
cursor: pointer !important;
}
.secondary-btn:hover {
background: #e0e0e0 !important;
}
/* Input fields */
.textbox {
border: 1px solid #ddd !important;
border-radius: 4px !important;
}
/* Progress bar container */
.progress-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 6px;
background: #e0e0e0;
z-index: 1000;
}
/* Progress bar */
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #2196F3, #00BCD4);
box-shadow: 0 0 10px rgba(33, 150, 243, 0.5);
transition: width 0.3s ease;
animation: progress-glow 1.5s ease-in-out infinite;
}
/* Progress text */
.progress-text {
position: fixed;
top: 8px;
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 4px 12px;
border-radius: 15px;
font-size: 14px;
z-index: 1001;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
/* Progress bar animation */
@keyframes progress-glow {
0% {
box-shadow: 0 0 5px rgba(33, 150, 243, 0.5);
}
50% {
box-shadow: 0 0 20px rgba(33, 150, 243, 0.8);
}
100% {
box-shadow: 0 0 5px rgba(33, 150, 243, 0.5);
}
}
/* Loading state */
.loading {
opacity: 0.7;
pointer-events: none;
transition: opacity 0.3s ease;
}
/* Responsive design for smaller screens */
@media (max-width: 768px) {
.group {
padding: 10px;
margin-bottom: 15px;
}
.progress-text {
font-size: 12px;
padding: 3px 10px;
}
}
/* Example section styling */
.examples-table {
margin-top: 10px !important;
margin-bottom: 20px !important;
}
.examples-table button {
background-color: #f0f0f0 !important;
border: 1px solid #ddd !important;
border-radius: 4px !important;
padding: 5px 10px !important;
margin: 2px !important;
transition: all 0.3s ease !important;
}
.examples-table button:hover {
background-color: #e0e0e0 !important;
transform: translateY(-1px) !important;
box-shadow: 0 2px 5px rgba(0,0,0,0.1) !important;
}
.examples-table .label {
font-weight: bold !important;
color: #444 !important;
margin-bottom: 5px !important;
}
"""
# --- Gradio Interface (UI portion only) ---
with gr.Blocks(css=css, title="NewsAI Service") as iface:
# Initialize the database first (keeping the call to init_db(), unchanged)
init_db()
with gr.Tabs():
with gr.Tab("MoneyRadar"):
# Added usage instructions and feature explanations here:
gr.Markdown(
"""
## MoneyRadar: Implies scanning the market to spot money-making opportunities.
**How to Use This Service**:
1. **Custom Search**: Enter any keyword and choose a target country to fetch the latest news. The system automatically performs sentiment analysis and stores results in the database.
2. **Generate Full Analysis Summary Report**: This will automatically:
- Search all predefined companies (in parallel),
- Store the articles and sentiment analysis,
- Display a combined overall report.
3. **Individual Companies**:
- **Search**: Fetch and analyze the latest news from Google (for the chosen company).
- **Load from DB**: Retrieve the most recent saved news and sentiment analysis from the local database.
**Features**:
- **Real-time News Scraping**: Retrieves fresh articles from multiple regions.
- **Advanced Sentiment Analysis**: Uses state-of-the-art NLP models via the OpenAI API.
- **Data Persistence**: Automatically saves and retrieves search results in a local SQLite database for quick reference.
- **Flexible**: Ability to search any keyword/country or select from predefined Big Tech & finance-related terms.
---
"""
)
# User custom search section
with gr.Group():
gr.Markdown("### Custom Search")
with gr.Row():
with gr.Column():
user_input = gr.Textbox(
label="Enter your keyword",
placeholder="e.g., Apple, Samsung, etc.",
elem_classes="textbox"
)
with gr.Column():
country_selection = gr.Dropdown(
choices=list(COUNTRY_LOCATIONS.keys()),
value="United States",
label="Select Country"
)
with gr.Column():
custom_search_btn = gr.Button(
"Search",
variant="primary",
elem_classes="primary-btn"
)
custom_search_output = gr.Markdown()
custom_search_btn.click(
fn=search_custom,
inputs=[user_input, country_selection],
outputs=custom_search_output
)
# Button to generate a full report
with gr.Row():
full_report_btn = gr.Button(
"Generate Full Analysis Summary Report",
variant="primary",
elem_classes="primary-btn"
)
full_report_display = gr.Markdown()
full_report_btn.click(
fn=full_summary_report,
outputs=full_report_display
)
# Individual search/load for companies in KOREAN_COMPANIES
with gr.Column():
for i in range(0, len(KOREAN_COMPANIES), 2):
with gr.Row():
# Left column
with gr.Column():
company = KOREAN_COMPANIES[i]
with gr.Group():
gr.Markdown(f"### {company}")
with gr.Row():
search_btn = gr.Button(
"Search",
variant="primary",
elem_classes="primary-btn"
)
load_btn = gr.Button(
"Load from DB",
variant="secondary",
elem_classes="secondary-btn"
)
result_display = gr.Markdown()
search_btn.click(
fn=lambda c=company: search_company(c),
outputs=result_display
)
load_btn.click(
fn=lambda c=company: load_company(c),
outputs=result_display
)
# Right column (if exists)
if i + 1 < len(KOREAN_COMPANIES):
with gr.Column():
company = KOREAN_COMPANIES[i + 1]
with gr.Group():
gr.Markdown(f"### {company}")
with gr.Row():
search_btn = gr.Button(
"Search",
variant="primary",
elem_classes="primary-btn"
)
load_btn = gr.Button(
"Load from DB",
variant="secondary",
elem_classes="secondary-btn"
)
result_display = gr.Markdown()
search_btn.click(
fn=lambda c=company: search_company(c),
outputs=result_display
)
load_btn.click(
fn=lambda c=company: load_company(c),
outputs=result_display
)
# Launch the Gradio interface
iface.launch(
server_name="0.0.0.0",
server_port=7860,
share=True,
ssl_verify=False,
show_error=True
)