Files
OpenBB/openbb_terminal/reports/widget_helpers.py
Chavithra 72b0a9f1ee Next release : reports on steroids (#2349)
* fix gov tests

* refactor insider

* new virtual path extraction

* removed some symbol default params as they're considered critical

* little adjustments

* portfolio refactor

* merge API factory

* add helpers, stocks, crypto, forex

* minor forex changes

* include forex api paths

* add 2 missing forex funcs

* portfolio brokers refactor

* display help on api func call

* add econometrics virtual paths to api

* add api unit test

* fixed report for the new api

* minor portfolio refactorings

* added gdapps

* anchor_yield path

* some more crypto path fixes

* small change

* fixed wrong param

* minor fixes

* wip - inital commit for forex report

* add bw as a model, we'll get better solution afterwards

* added ema with dummy model as it adds great functionality to the report

* minor fixes

* wip - added functions to forex report

* add feedparser news path

* add new virtual paths to api

* adding commands to equity report

* revert to old paths, new ones were breaking

* Add in very basic ETF report

* Add candle chart to ETF report

* add etf load

* allow use of candle without data

* add raw to candle

* added forex report

* ongoing equity report

* equity report change

* fix some portfolio bugs and add docstrings

* include portfolio paths and coin class

* add crypto paths

* change event dates to str

* starting economy report

* window for limit

* equity report and refactor newsapi

* add helper to api

* update on economy report

* equity report

* update economy report

* refactor some docstrings

* change maturities helper

* refactor newsapi

* refactor futures command

* add some sauce to ycrv plot

* black

* update report

* refactor alphavantage

* refactor wsj

* update economy report

* ycrv tenor

* map avaiable_indices

* map economy helpers

* fix econdb docstring

* add plots on economy report

* minor fixes

* wip - crypto report

* update economy report

* added same default args as view

* added view to explicity use chart=True when suing the api

* adjustments - removed rich tables to use only df

* final version economy report

* change report name

* equity report for review

* linting

* add etf symbols endpoint

* incorporate feedback economy report

* fix reports launch by adding tag to economy report

* fix equity bug

* remove analyst name

* fix

* fix news

* make links hyperlinks for equity

* click links

* fixed arg name

* improved news

* small improves

* Fix light terminal stylesheet that would prevent using it in notebooks (#2473)

* improved report

* run reports in installer

* fix #2209

* minor ycrv refactoring

* refactor portfolio/holdv virtual path

* refactor benchmark trades

* fix events args

* adapt economy report to changes

* fix portfolio controller bug

* holdv refactor

* refactor perf command

* start portfolio report

* remove perf view

* refactor holp

* add textwrap3 to poetry (doesn't solve the error)

* fix equity after merge

* add some rolling commands

* fix equity after save button

* improved crypto report, plus minor fixes

* minor fixes on the reports

* add maxdd and distr

* refactor qa

* var command

* refactor qa expected shortfall

* add es command

* add es command

* fix qa percentile bug

* fix economy rendering

* refactor qa omega

* add om command

* add summary command

* add dret command

* add mret command

* add yret command

* add metrics

* add allocs to report

* remove bro and po commands, add later

* fixed some tests

* adjustments to crypto report

* Fix docstring for VSCode

Added a note about installing Jupyter PowerToys extension for optimal API usage in Jupyter VSCode, in the API_README.md.

* minor adjustment

* remove nft calendar model virtual paths

* Add in Portfolio report

* fix external axes portfolio view

* Update portfolio report with rolling plots

* Details for ETF and Portfolio

* fix economy report

* change analyst to openbb

* floppy

* fixed unmatched axis in reports

* Speed up tests

* fix file and load on po

* get_news output

* add some po paths

* Add integration tests for Reports menu

* refactor maxsharpe

* open maxsharpe

* open minrisk

* open maxutil

* open maxret

* Added fixes

* black

* remove useless views

* Fixed small issue

* refactor ef

* open ef api

* portfolio optimization report

* Added fixes

* unblock api loading

* add more endpoints

* update po report

* unblock api loading

* update po report

* expose herc

* expose property endpoint

* Added fixes

* More api fixes

* flake8

* Fixed some mypy

* news api model

* flake8

* mypy fix

* mypy

* black

* pylint

* fix tests

* markdown

* markdown

* Added fixes

* fix economy report

* merge

* fix economy report

* remove empty notebook

* expose nco

* remove jupyter notebook

* expose plot endpoint

* remove po report, just used for tests

* api v paths plot

* remove api_old

* change loading msg

Co-authored-by: montezdesousa <montezdesousa@gmail.com>
Co-authored-by: hjoaquim <h.joaquim@campus.fct.unl.pt>
Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com>
Co-authored-by: Om Gupta <guptagom@gmail.com>
Co-authored-by: minhhoang1023 <40023817+minhhoang1023@users.noreply.github.com>
Co-authored-by: JerBouma <jer.bouma@gmail.com>
Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com>
Co-authored-by: Om Gupta <85685255+soggyomelette@users.noreply.github.com>
Co-authored-by: Diogo Sousa <diogosousa@Diogos-MBP.lan>
Co-authored-by: Colin Delahunty <72827203+colin99d@users.noreply.github.com>
Co-authored-by: northern-64bit <75195383+northern-64bit@users.noreply.github.com>
Co-authored-by: colin99d <colin99delahunty@gmail.com>
Co-authored-by: Minh Hoang <nminh.hoang1023@gmail.com>
2022-09-20 15:07:58 +01:00

378 lines
11 KiB
Python

"""Widgets Helper Library.
A library of `ipywidgets` wrappers for notebook based reports and voila dashboards.
The library includes both python code and html/css/js elements that can be found in the
`./widgets` folder.
"""
import os
from typing import List
import base64
from jinja2 import Template
def price_card_stylesheet():
"""Load a default CSS stylesheet from file."""
with open(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "widgets", "style.css")
) as f:
style = f.read()
return style
def price_card(ticker: str, price: str, price_color: str = "neutral_color") -> str:
"""Prepare a styled HTML element of a 128 by 128 price card.
Parameters
----------
ticker : str
Instrument ticker for the price card
price : str
Instrument price as a string
price_color : str, optional
The color of the price. Accepts "up_color", "down_color" and default "neutral_color"
Returns
-------
str
HTML code as string
"""
with open(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "widgets", "card.j2")
) as f:
template = Template(f.read())
card = template.render(ticker=ticker, price=price, price_color=price_color)
return card
def html_report_stylesheet():
"""Load a default CSS stylesheet from file."""
with open(
os.path.join(
os.path.dirname(os.path.abspath(__file__)), "widgets", "report.css"
)
) as f:
style = f.read()
return style
def html_report(title: str = "", stylesheet: str = "", body: str = "") -> str:
"""Prepare a styled HTML page element.
Parameters
----------
title : str, optional
Contents of the title tag, by default ""
stylesheet : str, optional
Contents of the stylesheet tag, by default ""
body : str, optional
Contents of the body tag, by default ""
Returns
-------
str
HTML code as string
"""
with open(
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 + "</html>")
def h(level: int, text: str) -> str:
"""Wrap text into an HTML `h` tag.
Parameters
----------
level : int
HTML `h` level tag
text : str
Contents for `h` level tag
Returns
-------
str
HTML code as string
"""
return f"<h{str(level)}>{text}</h{level}>"
def p(text: str, style: str = "") -> str:
"""Wrap text into an HTML `p` tag.
Parameters
----------
text : str
Contents for HTML `p` tag
style: str
Div style
Returns
-------
str
HTML code as string
"""
return f'<p style="{style}">{text}</p>'
def row(elements: List) -> str:
"""HTML code elements to add in a single row
Parameters
----------
elements : List
List of HTML code elements to add in a row
Returns
-------
str
HTML code as string
"""
with open(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "widgets", "row.j2")
) as f:
template = Template(f.read())
return template.render(elements=elements)
def kpi(thresholds: List[float], sentences: List[str], value: float) -> str:
"""Add new key performance indicator to main page of report
Parameters
----------
thresholds : List[float]
List of thresholds to take into account
sentences : List[str]
List of sentences to take into account. len(sentences) = len(thresholds)+1
value : float
Current value for the KPI in question
Returns
-------
str
HTML code as string
"""
if len(thresholds) == 1 and len(sentences) == 2:
if value < thresholds[0]:
return f'<p style="color:red">&#10060; {sentences[0]}. {value} < {thresholds[0]} </p>'
return f'<p style="color:green">&#x2705; {sentences[1]}. {value} > {thresholds[0]} </p>'
if len(thresholds) == 2 and len(sentences) == 3:
if value < thresholds[0]:
return f'<p style="color:red">&#10060; {sentences[0]}. {value} < {thresholds[0]} </p>'
if value > thresholds[1]:
return f'<p style="color:green">&#x2705; {sentences[2]}. {value} > {thresholds[1]} </p>'
return f'<p style="color:orange">&#128993; {sentences[1]}. {thresholds[0]} < {value} < {thresholds[1]} </p>'
print("Error. KPI condition is not correctly set")
return ""
def add_tab(
title: str, htmlcode: str, comment_cell: bool = True, commment_text: str = ""
) -> str:
"""Add new tab section for the report. By default adds an opinion editable box at the start.
Parameters
----------
title : str
Title associated with this tab / section
htmlcode : str
All HTML code contain within this section
comment_cell : bool
Comment cell
Returns
-------
str
HTML code as string
"""
html_text = f'<div id="{title}" class="tabcontent"></br>'
if comment_cell:
comment = commment_text if commment_text else "No comment."
html_text += f"""<p style="border:3px; border-style:solid;
border-color:#000000; padding: 1em; width: 1050px;" contentEditable="true">
{comment}
</p>"""
html_text += f"{htmlcode}</div>"
return html_text
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
javascript code in HTML to process interactive tabs
"""
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";
}
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>"""
def tablinks(tabs: List[str]) -> str:
"""Adds list of tabs/sections for the reports that are able to be clicked. For every
6 tabs we push them onto a new line.
Parameters
----------
tabs : List[str]
List of tabs/sections for the reports.
Returns
-------
str
HTML code for interactive tabs
"""
htmlcode = '<div class="tab">'
for idx, tab in enumerate(tabs):
htmlcode += f"""<button class="tablinks" onclick="menu(event, '{tab}')">{tab}</button>"""
if ((idx + 1) % 5) == 0:
htmlcode += "</br>"
htmlcode += "</div>"
return htmlcode
def header(
openbb_img, floppy_disk_img, author, report_date, report_time, report_tz, title
) -> str:
"""Creates reports header
Parameters
----------
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
Date when report is run
report_time : str
Time when report is run
report_tz : str
Timezone associated with datetime of report being run
title : str
Title of the report
Returns
-------
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"""
<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>
"""
def add_external_fig(figloc: str, style: str = "") -> str:
"""Add external figure to HTML
Parameters
----------
figloc : str
Relative figure location
style: str
Div style
Returns
-------
str
HTML code for figure
"""
try:
with open(figloc, "rb") as image_file:
img = base64.b64encode(image_file.read()).decode()
return f'<img src="data:image/{figloc.split(".")[1]};base64,{img}"style="{style}">'
except Exception:
return ""