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 2 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 @@ -1094,6 +1094,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
@@ -1,6 +1,7 @@
"""Reports Controller Module."""
__docformat__ = "numpy"

import requests
import argparse
import logging
import os
Expand All @@ -16,6 +17,8 @@
from openbb_terminal.reports import reports_model
from openbb_terminal.rich_config import MenuText, console

from openbb_terminal.helper_funcs import parse_and_split_input

# from openbb_terminal.terminal_helper import is_packaged_application

logger = logging.getLogger(__name__)
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)

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)}")
Loading