diff options
author | DidierRLopes <dro.lopes@campus.fct.unl.pt> | 2022-09-08 14:25:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-08 12:25:55 +0100 |
commit | 3381a9a907fe6f99ba6a190475a55e1325d9a4a9 (patch) | |
tree | 092e0ad9750e82e8d7f801a574827741aef562ba | |
parent | 1766e2fe3f1caf7c9c6d740b4c24d1dc3496338a (diff) |
Allow reports comments to be saved in a new HTML (#2507)
* allow reports comments to be saved in a new HTML
* clear output of equity report
Co-authored-by: minhhoang1023 <40023817+minhhoang1023@users.noreply.github.com>
-rw-r--r-- | openbb_terminal/reports/equity.ipynb | 144 | ||||
-rw-r--r-- | openbb_terminal/reports/floppy-disc.png | bin | 0 -> 2782 bytes | |||
-rw-r--r-- | openbb_terminal/reports/reports_controller.py | 2 | ||||
-rw-r--r-- | openbb_terminal/reports/widget_helpers.py | 131 |
4 files changed, 174 insertions, 103 deletions
diff --git a/openbb_terminal/reports/equity.ipynb b/openbb_terminal/reports/equity.ipynb index 37da5eff9f3..6c510575db0 100644 --- a/openbb_terminal/reports/equity.ipynb +++ b/openbb_terminal/reports/equity.ipynb @@ -27,8 +27,8 @@ "\n", "from IPython.display import HTML\n", "\n", - "# import sys\n", - "# sys.path.append('../../')\n", + "#import sys\n", + "#sys.path.append('../../')\n", "\n", "from openbb_terminal import api as openbb\n", "from openbb_terminal.helper_classes import TerminalStyle\n", @@ -56,10 +56,7 @@ "try:\n", " theme = TerminalStyle(\"light\", \"light\", \"light\")\n", "except:\n", - " pass\n", - "stylesheet = openbb.widgets.html_report_stylesheet()\n", - "with open(\"./openbb_terminal/reports/OpenBB_reports_logo.png\", \"rb\") as image_file:\n", - " openbb_image_encoded = base64.b64encode(image_file.read())" + " pass" ] }, { @@ -82,7 +79,7 @@ "outputs": [], "source": [ "# Parameters that will be replaced when calling this notebook\n", - "ticker = \"TSLA\"\n", + "symbol = \"GME\"\n", "report_name = \"\"" ] }, @@ -93,7 +90,7 @@ "metadata": {}, "outputs": [], "source": [ - "if \".\" in ticker:\n", + "if \".\" in symbol:\n", " import sys\n", "\n", " sys.exit(0)" @@ -107,16 +104,16 @@ "outputs": [], "source": [ "ticker_data = openbb.stocks.load(\n", - " ticker, start=datetime.datetime.now() - datetime.timedelta(days=4 * 30)\n", + " symbol, start_date=datetime.datetime.now() - datetime.timedelta(days=4 * 30)\n", ")\n", "ticker_data = openbb.stocks.process_candle(ticker_data)\n", "\n", "author = \"Didier Rodrigues Lopes\"\n", - "report_title = f\"INVESTMENT RESEARCH REPORT ON {ticker.upper()}\"\n", + "report_title = f\"INVESTMENT RESEARCH REPORT: {symbol.upper()}\"\n", "report_date = datetime.datetime.now().strftime(\"%d %B, %Y\")\n", "report_time = datetime.datetime.now().strftime(\"%H:%M\")\n", - "report_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo\n", - "report_title, report_date, report_time, report_timezone" + "report_tz = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo\n", + "report_title, report_date, report_time, report_tz" ] }, { @@ -126,7 +123,7 @@ "metadata": {}, "outputs": [], "source": [ - "info = openbb.stocks.fa.models.yahoo_finance.get_info(ticker=ticker).transpose()\n", + "info = openbb.stocks.fa.models.yahoo_finance.get_info(symbol=symbol).transpose()\n", "\n", "if info[\"Long business summary\"][0] != \"NA\":\n", " overview = info[\"Long business summary\"][0]\n", @@ -154,7 +151,7 @@ " df_year_estimates,\n", " df_quarter_earnings,\n", " df_quarter_revenues,\n", - ") = openbb.stocks.dd.models.business_insider.get_estimates(ticker)" + ") = openbb.stocks.dd.models.business_insider.get_estimates(symbol)" ] }, { @@ -196,7 +193,7 @@ "metadata": {}, "outputs": [], "source": [ - "df_sec_filings = openbb.stocks.dd.models.marketwatch.get_sec_filings(ticker)[\n", + "df_sec_filings = openbb.stocks.dd.models.marketwatch.get_sec_filings(symbol)[\n", " [\"Type\", \"Category\", \"Link\"]\n", "].head(5)\n", "df_sec_filings[\"Link\"] = df_sec_filings[\"Link\"].apply(\n", @@ -212,7 +209,7 @@ "metadata": {}, "outputs": [], "source": [ - "df_analyst = openbb.stocks.dd.models.finviz.get_analyst_data(ticker)\n", + "df_analyst = openbb.stocks.dd.models.finviz.get_analyst_data(symbol)\n", "if not df_analyst.empty:\n", " if \"target\" in df_analyst.columns:\n", " df_analyst[\"target_to\"] = df_analyst[\"target_to\"].combine_first(\n", @@ -239,8 +236,8 @@ "fig, ax1 = plt.subplots(figsize=(11, 5), dpi=150)\n", "ax2 = ax1.twinx()\n", "openbb.stocks.dps.spos(\n", - " ticker,\n", - " num=84,\n", + " symbol,\n", + " limit=84,\n", " raw=False,\n", " export=\"\",\n", " external_axes=[ax1, ax2],\n", @@ -261,7 +258,7 @@ "source": [ "fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1, figsize=(11, 5), dpi=150)\n", "openbb.stocks.dps.dpotc(\n", - " ticker,\n", + " symbol=symbol,\n", " external_axes=[ax1, ax2],\n", ")\n", "fig.tight_layout()\n", @@ -281,7 +278,7 @@ "fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1, figsize=(11, 5), dpi=150)\n", "ax3 = ax1.twinx()\n", "openbb.stocks.dps.psi_sg(\n", - " ticker,\n", + " symbol,\n", " external_axes=[ax1, ax2, ax3],\n", ")\n", "fig.tight_layout()\n", @@ -300,8 +297,8 @@ "source": [ "fig, (candles, volume) = plt.subplots(nrows=2, ncols=1, figsize=(11, 5), dpi=150)\n", "openbb.stocks.candle(\n", - " s_ticker=ticker,\n", - " df_stock=ticker_data,\n", + " symbol=symbol,\n", + " data=ticker_data,\n", " use_matplotlib=True,\n", " external_axes=[candles, volume],\n", ")\n", @@ -322,11 +319,10 @@ "source": [ "fig, ax = plt.subplots(figsize=(11, 3), dpi=150)\n", "openbb.stocks.dd.pt(\n", - " ticker=ticker,\n", - " start=\"2022-01-01\",\n", - " interval=\"1440min\",\n", - " stock=ticker_data,\n", - " num=10,\n", + " ticker_data,\n", + " symbol=symbol,\n", + " start_date=\"2022-01-01\",\n", + " limit=10,\n", " raw=False,\n", " external_axes=[ax],\n", ")\n", @@ -343,17 +339,23 @@ "metadata": {}, "outputs": [], "source": [ - "df = openbb.stocks.dd.models.business_insider.get_price_target_from_analysts(ticker)\n", - "avg_ratings_last_30_days = 0\n", + "df = openbb.stocks.dd.models.business_insider.get_price_target_from_analysts(symbol)\n", + "avg_ratings = 0\n", + "days = 30\n", "if not df.empty:\n", - " avg_ratings_last_30_days = round(\n", - " np.mean(\n", - " df[datetime.datetime.now() - datetime.timedelta(days=30) :][\n", - " \"Price Target\"\n", - " ].values\n", - " ),\n", - " 2,\n", - " )\n", + " df_ratings = df[datetime.datetime.now() - datetime.timedelta(days=days) :]\n", + " while df_ratings.empty:\n", + " days += 30\n", + " df_ratings = df[datetime.datetime.now() - datetime.timedelta(days=days) :]\n", + " \n", + " if days > 100:\n", + " break\n", + " \n", + " if not df_ratings.empty:\n", + " avg_ratings = round(np.mean(df_ratings[\"Price Target\"].values), 2)\n", + " else:\n", + " avg_ratings = 0\n", + " \n", "last_price = round(ticker_data[\"Close\"][-1], 2)" ] }, @@ -366,10 +368,9 @@ "source": [ "fig, ax = plt.subplots(figsize=(11, 3), dpi=150)\n", "openbb.stocks.dd.rot(\n", - " ticker=ticker,\n", - " num=10,\n", + " symbol=symbol,\n", + " limit=10,\n", " raw=False,\n", - " export=\"\",\n", " external_axes=[ax],\n", ")\n", "fig.tight_layout()\n", @@ -431,7 +432,7 @@ "import pandas as pd\n", "\n", "df_insider = pd.DataFrame.from_dict(\n", - " openbb.stocks.ins.models.finviz.get_last_insider_activity(ticker)\n", + " openbb.stocks.ins.models.finviz.get_last_insider_activity(symbol)\n", ").head(10)\n", "df_insider[\"Val ($)\"] = df_insider[\"Value ($)\"].replace({\",\": \"\"}, regex=True)\n", "df_insider[\"Trade\"] = df_insider.apply(\n", @@ -453,7 +454,7 @@ "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(11, 3), dpi=150)\n", - "openbb.stocks.ba.headlines(ticker, external_axes=[ax])\n", + "openbb.stocks.ba.headlines(symbol, external_axes=[ax])\n", "fig.tight_layout()\n", "f = io.BytesIO()\n", "fig.savefig(f, format=\"svg\")\n", @@ -467,7 +468,7 @@ "metadata": {}, "outputs": [], "source": [ - "df_sentiment_finbrain = openbb.stocks.ba.models.finbrain.get_sentiment(ticker)\n", + "df_sentiment_finbrain = openbb.stocks.ba.models.finbrain.get_sentiment(symbol)\n", "finbrain_sentiment_val = float(df_sentiment_finbrain.values[-1][0])" ] }, @@ -483,7 +484,7 @@ " n_cases,\n", " n_bull,\n", " n_bear,\n", - ") = openbb.stocks.ba.models.stocktwits.get_bullbear(ticker)\n", + ") = openbb.stocks.ba.models.stocktwits.get_bullbear(symbol)\n", "stocktwits_sentiment = f\"Watchlist count: {watchlist_count}</br>\"\n", "if n_cases > 0:\n", " stocktwits_sentiment += f\"\\nLast {n_cases} sentiment messages:</br>\"\n", @@ -501,7 +502,7 @@ "outputs": [], "source": [ "fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1, figsize=(11, 5), dpi=150)\n", - "openbb.stocks.ba.snews(ticker, external_axes=[ax1, ax2])\n", + "openbb.stocks.ba.snews(symbol, external_axes=[ax1, ax2])\n", "fig.tight_layout()\n", "f = io.BytesIO()\n", "fig.savefig(f, format=\"svg\")\n", @@ -516,7 +517,7 @@ "outputs": [], "source": [ "ticker_data_all = openbb.stocks.load(\n", - " ticker, start=datetime.datetime.now() - datetime.timedelta(days=5 * 12 * 21)\n", + " symbol, start_date=datetime.datetime.now() - datetime.timedelta(days=5 * 12 * 21)\n", ")\n", "ticker_data_all[\"Returns\"] = ticker_data_all[\"Adj Close\"].pct_change()" ] @@ -529,7 +530,7 @@ "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(11, 3), dpi=150)\n", - "openbb.stocks.qa.bw(ticker, ticker_data_all, \"Returns\", False, external_axes=[ax])\n", + "openbb.stocks.qa.bw(ticker_data_all, \"Returns\", symbol, yearly=False, external_axes=[ax])\n", "fig.tight_layout()\n", "f = io.BytesIO()\n", "fig.savefig(f, format=\"svg\")\n", @@ -544,7 +545,7 @@ "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(11, 3), dpi=150)\n", - "openbb.stocks.qa.bw(ticker, ticker_data_all, \"Returns\", True, external_axes=[ax])\n", + "openbb.stocks.qa.bw(ticker_data_all, \"Returns\", symbol, yearly=True, external_axes=[ax])\n", "fig.tight_layout()\n", "f = io.BytesIO()\n", "fig.savefig(f, format=\"svg\")\n", @@ -578,7 +579,7 @@ "if predictions:\n", " fig, ax = plt.subplots(figsize=(11, 3), dpi=150)\n", " openbb.stocks.pred.regression(\n", - " ticker, ticker_data_all[\"Close\"], 1, 80, 20, 1, external_axes=[ax]\n", + " symbol, ticker_data_all[\"Close\"], 1, 80, 20, 1, external_axes=[ax]\n", " )\n", " fig.tight_layout()\n", " f = io.BytesIO()\n", @@ -612,7 +613,7 @@ "if predictions:\n", " fig, ax = plt.subplots(figsize=(11, 3), dpi=150)\n", " openbb.stocks.pred.regression(\n", - " ticker, ticker_data_all[\"Close\"], 1, 80, 20, 1, external_axes=[ax]\n", + " symbol, ticker_data_all[\"Close\"], 1, 80, 20, 1, external_axes=[ax]\n", " )\n", " fig.tight_layout()\n", " f = io.BytesIO()\n", @@ -636,15 +637,14 @@ "outputs": [], "source": [ "body = \"\"\n", - "\n", - "img = f'<img src=\"data:image/png;base64,{openbb_image_encoded.decode()}\" alt=\"OpenBB\" style=\"width:144px;\">'\n", "body += openbb.widgets.header(\n", - " img,\n", - " author,\n", - " report_date,\n", - " report_time,\n", - " report_timezone,\n", - " f\"<b>INVESTMENT RESEARCH REPORT:</b> {ticker}\",\n", + " openbb_img=\"./openbb_terminal/reports/OpenBB_reports_logo.png\",\n", + " floppy_disk_img=\"./openbb_terminal/reports/floppy-disc.png\",\n", + " author=author,\n", + " report_date=report_date,\n", + " report_time=report_time,\n", + " report_tz=report_tz,\n", + " title=f\"<b>{report_title}</b>\",\n", ")\n", "\n", "body += openbb.widgets.tablinks(\n", @@ -663,14 +663,16 @@ ")\n", "\n", "htmlcode = openbb.widgets.h(3, \"KPIs\")\n", - "htmlcode += openbb.widgets.kpi(\n", - " [last_price],\n", - " [\n", - " \"Last closing price is above the average price ratings of last 30 days\",\n", - " \"Average price ratings of last 30 day is above last closing price\",\n", - " ],\n", - " avg_ratings_last_30_days,\n", - ")\n", + "\n", + "if avg_ratings > 0:\n", + " htmlcode += openbb.widgets.kpi(\n", + " [last_price],\n", + " [\n", + " f\"Last closing price is above the average price ratings of last {days} days\",\n", + " f\"Average price ratings of last {days} days is above last closing price\",\n", + " ],\n", + " avg_ratings,\n", + " )\n", "if predictions:\n", " htmlcode += openbb.widgets.kpi(\n", " [0],\n", @@ -771,9 +773,13 @@ " htmlcode = openbb.widgets.row([\"Prediction features not enabled.\"])\n", "body += openbb.widgets.add_tab(\"Prediction Techniques\", htmlcode)\n", "\n", - "body += openbb.widgets.tab_clickable_evt()\n", + "body += openbb.widgets.tab_clickable_and_save_evt()\n", "\n", - "report = openbb.widgets.html_report(title=report_name, stylesheet=stylesheet, body=body)\n", + "report = openbb.widgets.html_report(\n", + " title=report_name, \n", + " stylesheet=openbb.widgets.html_report_stylesheet(), \n", + " body=body\n", + ")\n", "\n", "# to save the results\n", "with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n", @@ -809,7 +815,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.13" + "version": "3.8.6" } }, "nbformat": 4, diff --git a/openbb_terminal/reports/floppy-disc.png b/openbb_terminal/reports/floppy-disc.png Binary files differnew file mode 100644 index 00000000000..a318f414aca --- /dev/null +++ b/openbb_terminal/reports/floppy-disc.png diff --git a/openbb_terminal/reports/reports_controller.py b/openbb_terminal/reports/reports_controller.py index 12ec798fd72..a8c4bae7aae 100644 --- a/openbb_terminal/reports/reports_controller.py +++ b/openbb_terminal/reports/reports_controller.py @@ -79,6 +79,8 @@ class ReportController(BaseController): for param in literal_eval(params.strip('source": ')) if param[0] not in ["#", "\n"] ] + else: + l_params = [] d_params[report_to_run] = l_params # On the menu of choices add the parameters necessary for each template report diff --git a/openbb_terminal/reports/widget_helpers.py b/openbb_terminal/reports/widget_helpers.py index 600e2e8eac8..d0456b41e44 100644 --- a/openbb_terminal/reports/widget_helpers.py +++ b/openbb_terminal/reports/widget_helpers.py @@ -76,7 +76,7 @@ def html_report(title: str = "", stylesheet: str = "", body: str = "") -> str: os.path.join(os.path.dirname(os.path.abspath(__file__)), "widgets", "report.j2") ) as f: template = Template(f.read()) - return template.render(title=title, stylesheet=stylesheet, body=body) + return template.render(title=title, stylesheet=stylesheet, body=body + "</html>") def h(level: int, text: str) -> str: @@ -185,17 +185,21 @@ def add_tab(title: str, htmlcode: str, comment_cell: bool = True) -> str: """ html_text = f'<div id="{title}" class="tabcontent"></br>' if comment_cell: - html_text += """<p style="border:3px; border-style:solid; - border-color:#000000; padding: 1em; width: 1050px;" contentEditable="true"> - No comment. - </p>""" + html_text += """<form><input style="border:3px; border-style:solid; + border-color:#000000; padding: 1em; width: 1050px;" type="text" value="No comment."> + </form>""" html_text += f"{htmlcode}</div>" return html_text -def tab_clickable_evt() -> str: +def tab_clickable_and_save_evt() -> str: """Adds javascript code within HTML at the bottom that allows the interactivity with tabs. + Parameters + ---------- + report_name : str + Report name for the file to be saved + Returns ------- str @@ -204,25 +208,51 @@ def tab_clickable_evt() -> str: return """ <script> function menu(evt, menu_name) { - var i, tabcontent, tablinks; - tabcontent = document.getElementsByClassName("tabcontent"); - for (i = 0; i < tabcontent.length; i++) { - tabcontent[i].style.display = "none"; - } - tablinks = document.getElementsByClassName("tablinks"); - for (i = 0; i < tablinks.length; i++) { - tablinks[i].className = tablinks[i].className.replace(" active", ""); - tablinks[i].style.backgroundColor = "white"; - tablinks[i].style.color = "black"; - } - document.getElementById(menu_name).style.display = "block"; - - evt.currentTarget.className += " active"; - evt.currentTarget.style.backgroundColor = "black"; - evt.currentTarget.style.color = "white"; + var i, tabcontent, tablinks; + tabcontent = document.getElementsByClassName("tabcontent"); + for (i = 0; i < tabcontent.length; i++) { + tabcontent[i].style.display = "none"; + } + tablinks = document.getElementsByClassName("tablinks"); + for (i = 0; i < tablinks.length; i++) { + tablinks[i].className = tablinks[i].className.replace(" active", ""); + tablinks[i].style.backgroundColor = "white"; + tablinks[i].style.color = "black"; + } + document.getElementById(menu_name).style.display = "block"; + + evt.currentTarget.className += " active"; + evt.currentTarget.style.backgroundColor = "black"; + evt.currentTarget.style.color = "white"; + } + + function saveReport() { + const markup = document.documentElement.innerHTML; + var bl = new Blob([markup], { type: "text/html" }); + var a = document.createElement("a"); + a.href = URL.createObjectURL(bl); + a.download = "openbb_report.html"; + a.hidden = true; + document.body.appendChild(a); + a.innerHTML = "Download"; + a.click(); + } + + function readCommentsAndUpdateValues() { + var inputs, index; + + inputs = document.getElementsByTagName('input'); + for (index = 0; index < inputs.length; ++index) { + const elem = inputs[index]; + elem.addEventListener('input', (e) => { + console.log(elem.name, elem.value, e.target.value); + elem.setAttribute("value", e.target.value) + }); + } } window.onload=function(){ + readCommentsAndUpdateValues(); menu(event, 'SUMMARY'); }; </script>""" @@ -251,13 +281,17 @@ def tablinks(tabs: List[str]) -> str: return htmlcode -def header(img, author, report_date, report_time, report_tz, title) -> str: +def header( + openbb_img, floppy_disk_img, author, report_date, report_time, report_tz, title +) -> str: """Creates reports header Parameters ---------- - img : str - Image for customizable report + openbb_img : str + Image of OpenBB logo + floppy_disk_img : str + Image of floppy disk containing the save button author : str Name of author responsible by report report_date : str @@ -274,17 +308,46 @@ def header(img, author, report_date, report_time, report_tz, title) -> str: str HTML code for interactive tabs """ + try: + with open(openbb_img, "rb") as image_file: + openbb_image_encoded = base64.b64encode(image_file.read()) + openbb_img = f""" + <img src="data:image/png;base64,{openbb_image_encoded.decode()}" + alt="OpenBB" style="width:144px;">""" + except Exception: + openbb_img = "" + + try: + with open(floppy_disk_img, "rb") as image_file: + floppy_disk_encoded = base64.b64encode(image_file.read()) + flask_disk_save = f""" + <center><img src="data:image/png;base64,{floppy_disk_encoded.decode()}" + alt="OpenBB" style="width:40px;"></center>""" + except Exception: + flask_disk_save = "" + return f""" - <div style="display:flex; margin-bottom:1cm;"> - {img} - <div style="margin-left:2em"> - <p><b>Analyst:</b> {author}</p> - <p><b>Date :</b> {report_date}</p> - <p><b>Time :</b> {report_time} {report_tz}</p> - <br/> - <p>{title}</p> + <html lang="en" class="" data-lt-installed="true"> + <head> + <meta charset="UTF-8"> + <title>OpenBB Terminal Report</title> + <meta name="robots" content="noindex"> + </head> + <div style="display:flex; margin-bottom:1cm;"> + {openbb_img} + <div style="margin-left:2em"> + <p><b>Analyst:</b> {author}</p> + <p><b>Date :</b> {report_date}</p> + <p><b>Time :</b> {report_time} {report_tz}</p> + <br/> + <p>{title}</p> + </div> + <button style="margin-left:7em; border:0px solid black; + background-color: transparent;" onclick="saveReport()"> + {flask_disk_save}Save changes + </button> </div> - </div>""" + """ def add_external_fig(figloc: str, style: str = "") -> str: |