Skip to content

Commit

Permalink
Make default routines available & download personal routines on login (
Browse files Browse the repository at this point in the history
…#4737)

* make default routines available

* remove unecessary change

* user to personal

* rename some funcs

* add last update to tables

* add routines download on login

* move some routines funcs to controller

* fix tests

* add space

* wrap some funcs inside try, except

* fix account test

* suppress outputs

* rewrite docstring

* remove circular import

* reverse order

* update docstring

* allow exe with spaces in file name

* fix exe with input

* Update terminal_controller.py

* fix regex for file extensions

* pylint

* put default routines url inside constants

---------

Co-authored-by: Jeroen Bouma <jer.bouma@gmail.com>
Co-authored-by: James Maslek <jmaslek11@gmail.com>
  • Loading branch information
3 people authored Apr 13, 2023
1 parent f0b5cc0 commit 7d3172a
Show file tree
Hide file tree
Showing 21 changed files with 467 additions and 214 deletions.
Empty file.
180 changes: 126 additions & 54 deletions openbb_terminal/account/account_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
from pathlib import Path
from typing import Dict, List, Optional

from openbb_terminal.account.account_model import (
get_routines_info,
read_routine,
save_routine,
set_login_called,
from openbb_terminal.account.account_view import (
display_default_routines,
display_personal_routines,
)
from openbb_terminal.account.account_view import display_routines_list
from openbb_terminal.core.session import hub_model as Hub
from openbb_terminal.core.session.current_user import (
get_current_user,
is_local,
)
from openbb_terminal.core.session.routines_handler import (
get_default_routines_info,
get_personal_routines_info,
read_routine,
save_routine,
)
from openbb_terminal.core.session.session_model import logout
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal.decorators import log_start_end
Expand All @@ -27,6 +30,32 @@
logger = logging.getLogger(__name__)


__login_called = False


def get_login_called():
"""Get the login/logout called flag.
Returns
-------
bool
The login/logout called flag.
"""
return __login_called


def set_login_called(value: bool):
"""Set the login/logout called flag.
Parameters
----------
value : bool
The login/logout called flag.
"""
global __login_called # pylint: disable=global-statement
__login_called = value


class AccountController(BaseController):
"""Account Controller Class"""

Expand All @@ -49,27 +78,41 @@ class AccountController(BaseController):
def __init__(self, queue: Optional[List[str]] = None):
"""Constructor"""
super().__init__(queue)
self.ROUTINE_FILES: Dict[str, Path] = {}
self.LOCAL_ROUTINES: Dict[str, Path] = {}
self.REMOTE_CHOICES: List[str] = []

self.DEFAULT_ROUTINES: List[Dict[str, str]] = self.fetch_default_routines()
self.DEFAULT_CHOICES: List[str] = [
r["name"] for r in self.DEFAULT_ROUTINES if "name" in r
]

if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
self.choices: dict = self.choices_default
self.completer = NestedCompleter.from_nested_dict(self.choices)

def update_runtime_choices(self):
"""Update runtime choices"""
self.ROUTINE_FILES = self.get_routines()
self.LOCAL_ROUTINES = self.get_local_routines()
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
self.choices["upload"]["--file"].update({c: {} for c in self.ROUTINE_FILES})
self.choices["upload"]["--file"].update(
{c: {} for c in self.LOCAL_ROUTINES}
)
self.choices["download"]["--name"].update(
{c: {} for c in self.REMOTE_CHOICES}
{c: {} for c in self.DEFAULT_CHOICES + self.REMOTE_CHOICES}
)
self.choices["delete"]["--name"].update(
{c: {} for c in self.REMOTE_CHOICES}
)
self.completer = NestedCompleter.from_nested_dict(self.choices)

def get_routines(self):
"""Get routines"""
def get_local_routines(self) -> Dict[str, Path]:
"""Get local routines
Returns
-------
Dict[str, Path]
The local routines
"""
current_user = get_current_user()
return {
filepath.name: filepath
Expand All @@ -78,6 +121,20 @@ def get_routines(self):
)
}

def fetch_default_routines(self) -> List[Dict[str, str]]:
"""Fetch default routines
Returns
-------
List[Dict[str, str]]
The default routines
"""
response = Hub.get_default_routines()
if response and response.status_code == 200:
d = response.json()
return d.get("data", [])
return []

def print_help(self):
"""Print help"""
mt = MenuText("account/", 100)
Expand Down Expand Up @@ -201,13 +258,16 @@ def call_list(self, other_args: List[str]):
page=ns_parser.page,
size=ns_parser.size,
)
df, page, pages = get_routines_info(response)
df, page, pages = get_personal_routines_info(response)
if not df.empty:
self.REMOTE_CHOICES += list(df["name"])
self.update_runtime_choices()
display_routines_list(df, page, pages)
display_personal_routines(df, page, pages)
else:
console.print("[red]No routines found.[/red]")
console.print("")
df = get_default_routines_info(self.DEFAULT_ROUTINES)
display_default_routines(df)

@log_start_end(log=logger)
def call_upload(self, other_args: List[str]):
Expand Down Expand Up @@ -320,49 +380,61 @@ def call_download(self, other_args: List[str]):
print_guest_block_msg()
else:
if ns_parser:
response = Hub.download_routine(
auth_header=get_current_user().profile.get_auth_header(),
name=" ".join(ns_parser.name),
)
data = None

if response and response.status_code == 200:
data = response.json()
if data:
name = data.get("name", "")
if name:
console.print(f"[info]Name:[/info] {name}")

description = data.get("description", "")
if description:
console.print(f"[info]Description:[/info] {description}")

script = data.get("script", "")
if script:
file_name = f"{name}.openbb"
file_path = save_routine(
file_name=file_name,
routine=script,
# Default routine
name = " ".join(ns_parser.name)
if name in self.DEFAULT_CHOICES:
data = next(
(r for r in self.DEFAULT_ROUTINES if r["name"] == name), None
)
else:
# User routine
response = Hub.download_routine(
auth_header=get_current_user().profile.get_auth_header(),
name=name,
)
data = (
response.json()
if response and response.status_code == 200
else None
)

# Save routine
if data:
name = data.get("name", "")
if name:
console.print(f"[info]Name:[/info] {name}")

description = data.get("description", "")
if description:
console.print(f"[info]Description:[/info] {description}")

script = data.get("script", "")
if script:
file_name = f"{name}.openbb"
file_path = save_routine(
file_name=file_name,
routine=script,
)
if file_path == "File already exists":
i = console.input(
"\nA file with the same name already exists, "
"do you want to replace it? (y/n): "
)
if file_path == "File already exists":
i = console.input(
"\nA file with the same name already exists, "
"do you want to replace it? (y/n): "
console.print("")
if i.lower() in ["y", "yes"]:
file_path = save_routine(
file_name=file_name,
routine=script,
force=True,
)
console.print("")
if i.lower() in ["y", "yes"]:
file_path = save_routine(
file_name=file_name,
routine=script,
force=True,
)
if file_path:
console.print(
f"[info]Location:[/info] {file_path}"
)
else:
console.print("[info]Aborted.[/info]")
elif file_path:
console.print(f"[info]Location:[/info] {file_path}")
if file_path:
console.print(f"[info]Location:[/info] {file_path}")
else:
console.print("[info]Aborted.[/info]")
elif file_path:
console.print(f"[info]Location:[/info] {file_path}")

@log_start_end(log=logger)
def call_delete(self, other_args: List[str]):
Expand Down
56 changes: 43 additions & 13 deletions openbb_terminal/account/account_view.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import pandas as pd

from openbb_terminal.helper_funcs import print_rich_table
from openbb_terminal.rich_config import console


def display_routines_list(df: pd.DataFrame, page: int, pages: int):
"""Display the routines list.
def display_personal_routines(df: pd.DataFrame, page: int, pages: int):
"""Display the routines.
Parameters
----------
Expand All @@ -15,14 +16,43 @@ def display_routines_list(df: pd.DataFrame, page: int, pages: int):
pages : int
The total number of pages.
"""
title = f"Available routines - page {page}"
if pages:
title += f" of {pages}"

print_rich_table(
df=df,
title=title,
headers=["Name", "Description"],
show_index=True,
index_name="#",
)
try:
title = f"Personal routines - page {page}"
if pages:
title += f" of {pages}"

df["updated_date"] = pd.to_datetime(df["updated_date"])
df["updated_date"] = df["updated_date"].dt.strftime("%Y-%m-%d %H:%M:%S")
df.replace(to_replace=[None], value="-", inplace=True)
print_rich_table(
df=df,
title=title,
headers=["Name", "Description", "Version", "Last update"],
show_index=True,
index_name="#",
)
except Exception:
console.print("Failed to display personal routines.")


def display_default_routines(df: pd.DataFrame):
"""Display the default routines.
Parameters
----------
df : pd.DataFrame
The default routines list.
"""
try:
df["date_updated"] = pd.to_datetime(df["date_updated"])
df["date_updated"] = df["date_updated"].dt.strftime("%Y-%m-%d %H:%M:%S")
df.replace(to_replace=[None], value="-", inplace=True)
print_rich_table(
df=df,
title="Default routines",
headers=["Name", "Description", "Version", "Last update"],
show_index=True,
index_name="#",
)
except Exception:
console.print("Failed to display default routines.")
Empty file.
3 changes: 2 additions & 1 deletion openbb_terminal/core/session/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
COLORS_URL = HUB_URL + "app/terminal/theme"
CHARTS_TABLES_URL = HUB_URL + "app/terminal/theme/charts-tables"

TIMEOUT = 15
DEFAULT_ROUTINES_URL = "https://tffo1zc1.directus.app/items/Routines"

TIMEOUT = 15
CONNECTION_ERROR_MSG = "[red]Connection error.[/red]"
CONNECTION_TIMEOUT_MSG = "[red]Connection timeout.[/red]"
Loading

0 comments on commit 7d3172a

Please sign in to comment.