Update app.py
Browse files
app.py
CHANGED
|
@@ -9,6 +9,8 @@ import concurrent.futures
|
|
| 9 |
import time
|
| 10 |
from typing import Dict, List, Optional, Any, Tuple
|
| 11 |
import logging
|
|
|
|
|
|
|
| 12 |
|
| 13 |
# Set up logging
|
| 14 |
logging.basicConfig(
|
|
@@ -335,7 +337,7 @@ async def analyze_tickers(
|
|
| 335 |
logger.info(f"Analysis completed in {time.time() - start_time:.2f} seconds")
|
| 336 |
return scores_table, metrics_table, bar_chart, radar_chart
|
| 337 |
|
| 338 |
-
# Helper: Convert DataFrame to Markdown
|
| 339 |
def dataframe_to_markdown(df: pd.DataFrame) -> str:
|
| 340 |
if df.empty:
|
| 341 |
return ""
|
|
@@ -345,6 +347,21 @@ def dataframe_to_markdown(df: pd.DataFrame) -> str:
|
|
| 345 |
rows = ["| " + " | ".join(str(val) for val in row) + " |" for _, row in df.iterrows()]
|
| 346 |
return "\n".join([header, separator] + rows)
|
| 347 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
# Custom CSS for better appearance
|
| 349 |
custom_css = """
|
| 350 |
.gradio-container {
|
|
@@ -376,7 +393,9 @@ def create_gradio_interface():
|
|
| 376 |
placeholder="AAPL, MSFT, GOOG, AMZN, TSLA",
|
| 377 |
lines=1
|
| 378 |
)
|
| 379 |
-
|
|
|
|
|
|
|
| 380 |
|
| 381 |
with gr.Row():
|
| 382 |
with gr.Column():
|
|
@@ -398,10 +417,6 @@ def create_gradio_interface():
|
|
| 398 |
bar_chart_output = gr.Plot(label="Component Scores Chart")
|
| 399 |
with gr.Column():
|
| 400 |
radar_chart_output = gr.Plot(label="Top Stocks Comparison")
|
| 401 |
-
|
| 402 |
-
# --- NEW: Compact Markdown copy boxes (only appear after analysis) ---
|
| 403 |
-
scores_md = gr.Textbox(label="📋 Copy Scores as Markdown", value="", lines=3, max_lines=10, interactive=False)
|
| 404 |
-
metrics_md = gr.Textbox(label="📋 Copy Metrics as Markdown", value="", lines=3, max_lines=10, interactive=False)
|
| 405 |
|
| 406 |
with gr.TabItem("Help & Information"):
|
| 407 |
gr.Markdown("""
|
|
@@ -427,16 +442,29 @@ def create_gradio_interface():
|
|
| 427 |
Financial data is provided by Yahoo Finance via the yfinance package.
|
| 428 |
""")
|
| 429 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
def analyze_wrapper(*args):
|
| 431 |
scores_df, metrics_df, bar_fig, radar_fig = asyncio.run(analyze_tickers(*args))
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
|
|
|
|
|
|
|
|
|
| 435 |
|
| 436 |
analyze_btn.click(
|
| 437 |
analyze_wrapper,
|
| 438 |
inputs=[tickers_input, growth_weight, value_weight, risk_weight],
|
| 439 |
-
outputs=[scores_output, metrics_output, bar_chart_output, radar_chart_output,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
)
|
| 441 |
|
| 442 |
return iface
|
|
|
|
| 9 |
import time
|
| 10 |
from typing import Dict, List, Optional, Any, Tuple
|
| 11 |
import logging
|
| 12 |
+
import tempfile
|
| 13 |
+
import os
|
| 14 |
|
| 15 |
# Set up logging
|
| 16 |
logging.basicConfig(
|
|
|
|
| 337 |
logger.info(f"Analysis completed in {time.time() - start_time:.2f} seconds")
|
| 338 |
return scores_table, metrics_table, bar_chart, radar_chart
|
| 339 |
|
| 340 |
+
# Helper: Convert DataFrame to Markdown
|
| 341 |
def dataframe_to_markdown(df: pd.DataFrame) -> str:
|
| 342 |
if df.empty:
|
| 343 |
return ""
|
|
|
|
| 347 |
rows = ["| " + " | ".join(str(val) for val in row) + " |" for _, row in df.iterrows()]
|
| 348 |
return "\n".join([header, separator] + rows)
|
| 349 |
|
| 350 |
+
# NEW: Generate downloadable .txt file with both tables
|
| 351 |
+
def download_tables(scores_df: pd.DataFrame, metrics_df: pd.DataFrame) -> str:
|
| 352 |
+
content = "# Stock Analysis Results\n\n"
|
| 353 |
+
content += "## Scores Table\n"
|
| 354 |
+
content += dataframe_to_markdown(scores_df) + "\n\n"
|
| 355 |
+
content += "## Financial Metrics Table\n"
|
| 356 |
+
content += dataframe_to_markdown(metrics_df) + "\n"
|
| 357 |
+
|
| 358 |
+
# Save to temporary file
|
| 359 |
+
temp_dir = tempfile.gettempdir()
|
| 360 |
+
path = os.path.join(temp_dir, "stock_analysis_tables.txt")
|
| 361 |
+
with open(path, "w", encoding="utf-8") as f:
|
| 362 |
+
f.write(content)
|
| 363 |
+
return path
|
| 364 |
+
|
| 365 |
# Custom CSS for better appearance
|
| 366 |
custom_css = """
|
| 367 |
.gradio-container {
|
|
|
|
| 393 |
placeholder="AAPL, MSFT, GOOG, AMZN, TSLA",
|
| 394 |
lines=1
|
| 395 |
)
|
| 396 |
+
with gr.Column():
|
| 397 |
+
analyze_btn = gr.Button("Analyze Stocks", variant="primary")
|
| 398 |
+
download_btn = gr.Button("📥 Download Tables (.txt)", variant="secondary")
|
| 399 |
|
| 400 |
with gr.Row():
|
| 401 |
with gr.Column():
|
|
|
|
| 417 |
bar_chart_output = gr.Plot(label="Component Scores Chart")
|
| 418 |
with gr.Column():
|
| 419 |
radar_chart_output = gr.Plot(label="Top Stocks Comparison")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
|
| 421 |
with gr.TabItem("Help & Information"):
|
| 422 |
gr.Markdown("""
|
|
|
|
| 442 |
Financial data is provided by Yahoo Finance via the yfinance package.
|
| 443 |
""")
|
| 444 |
|
| 445 |
+
# State to store last results
|
| 446 |
+
scores_state = gr.State(pd.DataFrame())
|
| 447 |
+
metrics_state = gr.State(pd.DataFrame())
|
| 448 |
+
|
| 449 |
def analyze_wrapper(*args):
|
| 450 |
scores_df, metrics_df, bar_fig, radar_fig = asyncio.run(analyze_tickers(*args))
|
| 451 |
+
return scores_df, metrics_df, bar_fig, radar_fig, scores_df, metrics_df
|
| 452 |
+
|
| 453 |
+
def download_wrapper(scores_df, metrics_df):
|
| 454 |
+
if scores_df.empty:
|
| 455 |
+
return None
|
| 456 |
+
return download_tables(scores_df, metrics_df)
|
| 457 |
|
| 458 |
analyze_btn.click(
|
| 459 |
analyze_wrapper,
|
| 460 |
inputs=[tickers_input, growth_weight, value_weight, risk_weight],
|
| 461 |
+
outputs=[scores_output, metrics_output, bar_chart_output, radar_chart_output, scores_state, metrics_state]
|
| 462 |
+
)
|
| 463 |
+
|
| 464 |
+
download_btn.click(
|
| 465 |
+
download_wrapper,
|
| 466 |
+
inputs=[scores_state, metrics_state],
|
| 467 |
+
outputs=gr.File()
|
| 468 |
)
|
| 469 |
|
| 470 |
return iface
|