Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to execute reports url from reports menu #5385

Merged
merged 6 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openbb_terminal/miscellaneous/i18n/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,7 @@ en:
reports/_OpenBB_reports_: OpenBB reports
reports/_Custom_reports_: Custom reports
reports/run: Run a Jupyter notebook from OpenBBUserData/reports/custom reports
reports/exe: Execute a Jupyter notebook through a hyperlink using papermill
forecast/_disclaimer_: DISCLAIMER
forecast/_data_loc: Looking for data in
forecast/load: load a dataset from csv
Expand Down
115 changes: 113 additions & 2 deletions openbb_terminal/reports/reports_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
# pylint: disable=R1732, R0912
from typing import Any, Dict, List, Optional

import requests

from openbb_terminal.core.session.current_user import get_current_user
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import parse_and_split_input
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.reports import reports_model
Expand All @@ -34,6 +37,7 @@ class ReportController(BaseController):
"portfolio",
"run",
"load",
"exe",
]
PATH = "/reports/"

Expand Down Expand Up @@ -78,6 +82,16 @@ def update_choices(self):

self.completer = NestedCompleter.from_nested_dict(self.choices)

def parse_input(self, an_input: str) -> List:
"""Overwrite the BaseController parse_input for `askobb` and 'exe'

This will allow us to search for something like "P/E" ratio
"""
raw_url = r"(exe (--url |-u )?(https?://)?raw\.githubusercontent\.(com)/.*)"
github_url = r"(exe (--url |-u )?(https?://)?github\.(com)/.*)"
custom_filters = [raw_url, github_url]
return parse_and_split_input(an_input=an_input, custom_filters=custom_filters)

def print_help(self):
"""Print help."""

Expand All @@ -88,7 +102,6 @@ def print_help(self):
self.update_choices()

mt = MenuText("reports/")
mt.add_info("_reports_")
mt.add_raw("\n")
mt.add_info("_OpenBB_reports_")
MAX_LEN_NAME = max(len(name) for name in self.REPORTS) + 2
Expand All @@ -110,7 +123,8 @@ def print_help(self):
mt.add_raw("\n")
mt.add_info("_Custom_reports_")
mt.add_cmd("run")
console.print(text=mt.menu_text, menu="Reports - WORK IN PROGRESS")
mt.add_cmd("exe")
console.print(text=mt.menu_text, menu="Reports")

@log_start_end(log=logger)
def call_etf(self, other_args: List[str]):
Expand Down Expand Up @@ -264,3 +278,100 @@ def call_run(self, other_args: List[str]):
console.print(
f"[red]Notebook '{ns_parser.file}' not found![/red]\n"
)

def call_exe(self, other_args: List[str]):
"""Process exe command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="exe",
description="Run a notebook from a url that contains the ipynb contents",
)
parser.add_argument(
"-u",
"--url",
dest="url",
required="-h" not in other_args,
help="The url of the file to be loaded",
)
parser.add_argument(
"-p",
"--parameters",
nargs="+",
dest="parameters",
help="Report parameters with format 'name:value'.",
)

# If first argument is a url, insert the -u flag
if other_args[0].startswith("http"):
other_args.insert(0, "--url")

ns_parser = self.parse_known_args_and_warn(parser, other_args)

if ns_parser:
# Validate parameter inputs
parameters_dict = {}
if ns_parser.parameters:
for p in ns_parser.parameters:
if ":" in p:
item = p.split(":")
if item[1]:
parameters_dict[item[0]] = item[1]
else:
console.print(
f"[red]Bad format '{p}': empty value.[/red]\nExecuting with defaults.\n"
)
else:
console.print(
f"[red]Bad format '{p}': use format 'name:value'.[/red]\nExecuting with defaults.\n"
)

if "raw.githubusercontent" in ns_parser.url:
url = ns_parser.url
else:
url = ns_parser.url.replace(
"github.com", "raw.githubusercontent.com"
).replace("/blob", "")

if url:
try:
# Send an HTTP GET request to fetch the raw notebook
response = requests.get(url, timeout=20)

if response.status_code == 200:
temporary_folder = os.path.join(
get_current_user().preferences.USER_REPORTS_DIRECTORY,
"temporary",
)

# Does the temp folder exist? if not create it
if not os.path.exists(temporary_folder):
os.makedirs(temporary_folder)

# Local file path where you want to save the notebook
local_file_path = os.path.join(
temporary_folder,
url.split(".com/")[1].replace(
"/", "_"
), # .replace(".ipynb", "")
)

# Save the template notebook locally
with open(local_file_path, "wb") as notebook_file:
notebook_file.write(response.content)

# To confirm that the notebook is saved locally
if os.path.exists(local_file_path):
reports_model.render_report(
input_path=local_file_path, args_dict=parameters_dict
)
else:
console.print(f"[red]Notebook '{url}' not found![/red]\n")

else:
console.print(
f"Failed to fetch notebook from {url}. Status code: {response.status_code}"
)

except Exception as e:
console.print(f"An error occurred: {str(e)}")
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
Build and run your custom reports or try one of our templates:

OpenBB reports:
crypto <symbol>
Expand All @@ -11,4 +10,5 @@ OpenBB reports:

Custom reports:
run Run a Jupyter notebook from OpenBBUserData/reports/custom reports
exe Execute a Jupyter notebook through a hyperlink using papermill

Loading