mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-07-02 03:44:22 +08:00
* stash some changes * add more robust testing * mypy * point PR at V5 * introduce spec file * codespell * test fix * fix workflow environment setup * fix workflow environment setup * fix workflow environment setup * add pyyaml to dependencies * split lint jobs * fix workflow environment setup * fix workflow environment setup * workflow env setup * workflow env setup * clean up code comments * add auth hook entrypoints * codespell * add codegen feature * codespell * move _unpack into dispatchers for consistency with codegen packages * surface nested models in the response * fix missing coverage in CI * socrata updates * test fix * detect plotly output * add --include and --exclude flags from generate-extension command * cap test matrix at python 3.14 * no useless comments * platform controller command description split * merge URL overloads from path params * exclude none and unset from model dump --------- Co-authored-by: deeleeramone <> Co-authored-by: Copilot <copilot@github.com>
139 lines
3.9 KiB
Python
139 lines
3.9 KiB
Python
"""HTML output adapter."""
|
|
|
|
import tempfile
|
|
import webbrowser
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import pandas as pd
|
|
|
|
from openbb_cli.session import Session
|
|
|
|
session = Session()
|
|
|
|
|
|
class HtmlOutput:
|
|
"""HTML output adapter - generates HTML and opens in default browser."""
|
|
|
|
def display(
|
|
self,
|
|
data: Any,
|
|
title: str = "",
|
|
export: bool = False,
|
|
chart: bool = False,
|
|
) -> None:
|
|
"""Display as HTML in default browser."""
|
|
if export:
|
|
return
|
|
|
|
if hasattr(data, "model_dump"):
|
|
if session.settings.USE_INTERACTIVE_DF and session.backend is not None:
|
|
try:
|
|
data.charting.table()
|
|
return
|
|
except Exception as e:
|
|
session.console.print(
|
|
f"[yellow]Interactive table not available: {e}[/yellow]"
|
|
)
|
|
|
|
if chart:
|
|
try:
|
|
data.show()
|
|
return
|
|
except Exception as e:
|
|
session.console.print(
|
|
f"[yellow]Chart not available, showing table instead: {e}[/yellow]"
|
|
)
|
|
|
|
if hasattr(data, "model_dump"):
|
|
results = data.model_dump(exclude_unset=True, exclude_none=True).get(
|
|
"results"
|
|
)
|
|
if results is None:
|
|
session.console.print("[yellow]No results to display[/yellow]")
|
|
return
|
|
|
|
if isinstance(results, pd.DataFrame):
|
|
df = results
|
|
elif isinstance(results, list):
|
|
df = pd.DataFrame(results)
|
|
elif isinstance(results, dict):
|
|
df = pd.DataFrame([results])
|
|
else:
|
|
df = pd.DataFrame({"value": [results]})
|
|
elif isinstance(data, pd.DataFrame):
|
|
df = data
|
|
if session.settings.USE_INTERACTIVE_DF and session.backend is not None:
|
|
try:
|
|
session.backend.send_table(
|
|
df_table=df,
|
|
title=title,
|
|
theme=session.user.preferences.table_style,
|
|
)
|
|
return
|
|
except Exception: # noqa: S110
|
|
pass
|
|
elif isinstance(data, pd.Series):
|
|
df = data.to_frame()
|
|
elif isinstance(data, dict):
|
|
df = pd.DataFrame.from_dict(data, orient="columns")
|
|
elif isinstance(data, (list, tuple)):
|
|
df = pd.DataFrame(data)
|
|
else:
|
|
df = pd.DataFrame({"value": [data]})
|
|
|
|
html_content = f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>{title}</title>
|
|
<style>
|
|
body {{
|
|
font-family: Arial, sans-serif;
|
|
margin: 20px;
|
|
background-color: #f5f5f5;
|
|
}}
|
|
h1 {{
|
|
color: #333;
|
|
}}
|
|
table {{
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
background-color: white;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}}
|
|
th, td {{
|
|
border: 1px solid #ddd;
|
|
padding: 8px;
|
|
text-align: left;
|
|
}}
|
|
th {{
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
font-weight: bold;
|
|
}}
|
|
tr:nth-child(even) {{
|
|
background-color: #f9f9f9;
|
|
}}
|
|
tr:hover {{
|
|
background-color: #f5f5f5;
|
|
}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>{title}</h1>
|
|
{df.to_html(index=True, border=0)}
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
with tempfile.NamedTemporaryFile(
|
|
mode="w", delete=False, suffix=".html", encoding="utf-8"
|
|
) as f:
|
|
f.write(html_content)
|
|
temp_path = f.name
|
|
|
|
webbrowser.open(f"file://{Path(temp_path).as_posix()}")
|
|
session.console.print(f"[green]Opened table in browser: {temp_path}[/green]")
|