diff --git a/README.md b/README.md index 221332fb10d..8c9170ac86c 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,9 @@ Any of our social media platforms: [openbb.co/links](https://openbb.co/links) ## 6. Star History -This is a proxy of our growth and that we are just getting started. But for more metrics important to us check [openbb.co/open](https://openbb.co/open). +This is a proxy of our growth and that we are just getting started. + +But for more metrics important to us check [openbb.co/open](https://openbb.co/open). [![Star History Chart](https://api.star-history.com/svg?repos=openbb-finance/OpenBBTerminal&type=Date)](https://star-history.com/#openbb-finance/OpenBBTerminal&Date) diff --git a/openbb_terminal/account/account_controller.py b/openbb_terminal/account/account_controller.py index 12b26bb8869..8db4d421c25 100644 --- a/openbb_terminal/account/account_controller.py +++ b/openbb_terminal/account/account_controller.py @@ -255,6 +255,7 @@ class AccountController(BaseController): auth_header=get_current_user().profile.get_auth_header(), page=ns_parser.page, size=ns_parser.size, + base_url=Hub.BackendEnvironment.BASE_URL, ) df, page, pages = get_personal_routines_info(response) if not df.empty: @@ -356,6 +357,7 @@ class AccountController(BaseController): "routine": routine, "tags": tags, "public": ns_parser.public, + "base_url": Hub.BackendEnvironment.BASE_URL, } response = Hub.upload_routine(**kwargs) # type: ignore @@ -412,6 +414,7 @@ class AccountController(BaseController): response = Hub.download_routine( auth_header=get_current_user().profile.get_auth_header(), uuid=self.REMOTE_CHOICES[name], + base_url=Hub.BackendEnvironment.BASE_URL, ) if response and response.status_code == 200: data = [response.json(), "personal"] @@ -499,6 +502,7 @@ class AccountController(BaseController): response = Hub.delete_routine( auth_header=get_current_user().profile.get_auth_header(), uuid=self.REMOTE_CHOICES[name], + base_url=Hub.BackendEnvironment.BASE_URL, ) if ( response @@ -551,6 +555,7 @@ class AccountController(BaseController): response = Hub.generate_personal_access_token( auth_header=get_current_user().profile.get_auth_header(), + base_url=Hub.BackendEnvironment.BASE_URL, days=ns_parser.days, ) if response and response.status_code == 200: diff --git a/openbb_terminal/common/ultima_newsmonitor_model.py b/openbb_terminal/common/ultima_newsmonitor_model.py index b4e3782ac0d..656ba75c53f 100644 --- a/openbb_terminal/common/ultima_newsmonitor_model.py +++ b/openbb_terminal/common/ultima_newsmonitor_model.py @@ -18,7 +18,7 @@ from openbb_terminal.rich_config import console logger = logging.getLogger(__name__) -BASE_URL = "https://api.ultimainsights.ai/v1" +ULTIMA_BASE_URL = "https://api.ultimainsights.ai/v1" NO_API_KEY = "REPLACE_ME" @@ -69,10 +69,10 @@ def get_news(term: str = "", sort: str = "articlePublishedDate") -> pd.DataFrame if term in supported_terms(): if auth_header: data = request( - f"{BASE_URL}/getNewsArticles/{term}", headers=auth_header + f"{ULTIMA_BASE_URL}/getNewsArticles/{term}", headers=auth_header ) else: - data = request(f"{BASE_URL}/getNewsArticles/{term}") + data = request(f"{ULTIMA_BASE_URL}/getNewsArticles/{term}") else: console.print( "[red]Ticker not supported. Unable to retrieve data\n[/red]" @@ -150,7 +150,7 @@ def supported_terms() -> list: # https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/73270162#73270162 os.environ["REQUESTS_CA_BUNDLE"] = certifi.where() os.environ["SSL_CERT_FILE"] = certifi.where() - data = request(f"{BASE_URL}/supportedTickers") + data = request(f"{ULTIMA_BASE_URL}/supportedTickers") return list(data.json()) @@ -176,7 +176,7 @@ def get_company_info(ticker: str) -> dict: os.environ["SSL_CERT_FILE"] = certifi.where() if ticker in supported_terms(): - data = request(f"{BASE_URL}/getCompanyInfo/{ticker}") + data = request(f"{ULTIMA_BASE_URL}/getCompanyInfo/{ticker}") return data.json() console.print("[red]Ticker not supported. Unable to retrieve data\n[/red]") return {} @@ -213,9 +213,11 @@ def get_top_headlines(ticker: str) -> dict: if ticker in supported_terms(): if auth_header: - data = request(f"{BASE_URL}/getTopHeadlines/{ticker}", headers=auth_header) + data = request( + f"{ULTIMA_BASE_URL}/getTopHeadlines/{ticker}", headers=auth_header + ) else: - data = request(f"{BASE_URL}/getTopHeadlines/{ticker}") + data = request(f"{ULTIMA_BASE_URL}/getTopHeadlines/{ticker}") if ( hasattr(data, "status") and data.status_code == 429 ): # If data request failed diff --git a/openbb_terminal/core/completer/choices.py b/openbb_terminal/core/completer/choices.py index eff17a6f1fd..fabfb46c384 100644 --- a/openbb_terminal/core/completer/choices.py +++ b/openbb_terminal/core/completer/choices.py @@ -1,11 +1,11 @@ from argparse import ArgumentParser from contextlib import contextmanager from inspect import isfunction, unwrap -from os import environ from types import MethodType from typing import Callable, List from unittest.mock import patch +from openbb_terminal.core.session.current_system import get_current_system from openbb_terminal.helper_funcs import check_file_type_saved, check_positive from openbb_terminal.rich_config import get_ordered_list_sources @@ -211,7 +211,7 @@ def __patch_controller_functions(controller): ), ] - if str(environ.get("DEBUG_MODE", "false")).lower() != "true": + if not get_current_system().DEBUG_MODE: rich.start() patched_function_list = [] for patcher in patcher_list: @@ -219,7 +219,7 @@ def __patch_controller_functions(controller): yield patched_function_list - if str(environ.get("DEBUG_MODE", "false")).lower() != "true": + if not get_current_system().DEBUG_MODE: rich.stop() for patcher in patcher_list: patcher.stop() @@ -317,7 +317,7 @@ def build_controller_choice_map(controller) -> dict: argument_parser=argument_parser ) except Exception as exception: - if str(environ.get("DEBUG_MODE", "false")).lower() == "true": + if get_current_system().DEBUG_MODE: raise Exception( f"On command : `{command}`.\n{str(exception)}" ) from exception diff --git a/openbb_terminal/core/models/system_model.py b/openbb_terminal/core/models/system_model.py index e8a1b6c181d..7f478d30436 100644 --- a/openbb_terminal/core/models/system_model.py +++ b/openbb_terminal/core/models/system_model.py @@ -54,6 +54,7 @@ class SystemModel(BaseModel): # Others TEST_MODE: bool = False DEBUG_MODE: bool = False + DEV_BACKEND: bool = False ENABLE_AUTHENTICATION: bool = True HEADLESS: bool = False diff --git a/openbb_terminal/core/plots/backend.py b/openbb_terminal/core/plots/backend.py index ba82753ab6d..af6a29e2947 100644 --- a/openbb_terminal/core/plots/backend.py +++ b/openbb_terminal/core/plots/backend.py @@ -17,7 +17,9 @@ import plotly.graph_objects as go from packaging import version from reportlab.graphics import renderPDF -# pylint: disable=C0415 +from openbb_terminal.core.session.constants import BackendEnvironment + +# pylint: disable=C0411,C0412,C0415 try: from pywry import PyWry except ImportError as e: @@ -441,8 +443,10 @@ class Backend(PyWry): self.check_backend() endpoint = {True: "login", False: "logout"}[login] + json_url = f"{BackendEnvironment.HUB_URL}{endpoint}?pywry=true" + outgoing = dict( - json_data=dict(url=f"https://my.openbb.co/{endpoint}?pywry=true"), + json_data=dict(url=json_url), **self.get_kwargs(endpoint.title()), width=900, height=800, diff --git a/openbb_terminal/core/session/constants.py b/openbb_terminal/core/session/constants.py index 252b362d165..83b9b854aac 100644 --- a/openbb_terminal/core/session/constants.py +++ b/openbb_terminal/core/session/constants.py @@ -1,13 +1,16 @@ -BASE_URL = "https://payments.openbb.co/" -# BASE_URL = "http://127.0.0.1:8000/" +class HubEnvironment: + # BASE_URL = "http://127.0.0.1:8000/" + BASE_URL = "https://payments.openbb.co/" + HUB_URL = "https://my.openbb.co/" -HUB_URL = "https://my.openbb.co/" -REGISTER_URL = HUB_URL + "register" -SUPPORT_URL = HUB_URL + "app/terminal/support" -SOURCES_URL = HUB_URL + "app/terminal/data-sources" -KEYS_URL = HUB_URL + "app/terminal/api-keys" -COLORS_URL = HUB_URL + "app/terminal/theme" -CHARTS_TABLES_URL = HUB_URL + "app/terminal/theme/charts-tables" + def __new__(cls, *args, **kwargs): # pylint: disable=W0613 + """Create a singleton instance of the backend.""" + if not hasattr(cls, "instance"): + cls.instance = super().__new__(cls) # pylint: disable=E1120 + return cls.instance + + +BackendEnvironment = HubEnvironment() DEFAULT_ROUTINES_URL = "https://openbb-cms.directus.app/items/Routines" diff --git a/openbb_terminal/core/session/hub_model.py b/openbb_terminal/core/session/hub_model.py index f47412a46b0..8c711b10d07 100644 --- a/openbb_terminal/core/session/hub_model.py +++ b/openbb_terminal/core/session/hub_model.py @@ -7,18 +7,22 @@ import requests from jose import jwt from openbb_terminal.core.session.constants import ( - BASE_URL, CONNECTION_ERROR_MSG, CONNECTION_TIMEOUT_MSG, DEFAULT_ROUTINES_URL, TIMEOUT, + BackendEnvironment, ) from openbb_terminal.core.session.current_system import get_current_system +from openbb_terminal.core.session.current_user import get_current_user from openbb_terminal.rich_config import console def create_session( - email: str, password: str, base_url: str = BASE_URL, timeout: int = TIMEOUT + email: str, + password: str, + base_url: str = BackendEnvironment.BASE_URL, + timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """Create a session. @@ -71,7 +75,7 @@ def check_token_expiration(token: str): def create_session_from_token( - token: str, base_url: str = BASE_URL, timeout: int = TIMEOUT + token: str, base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT ): """Create a session from token. @@ -108,7 +112,10 @@ def create_session_from_token( def delete_session( - auth_header: str, token: str, base_url: str = BASE_URL, timeout: int = TIMEOUT + auth_header: str, + token: str, + base_url: str = BackendEnvironment.BASE_URL, + timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """Delete the session. @@ -190,7 +197,7 @@ def get_session(email: str, password: str) -> dict: dict The session info. """ - response = create_session(email, password) + response = create_session(email, password, base_url=BackendEnvironment.BASE_URL) if response is None: return {} return process_session_response(response) @@ -209,14 +216,14 @@ def get_session_from_token(token: str) -> dict: dict The session info. """ - response = create_session_from_token(token) + response = create_session_from_token(token, base_url=BackendEnvironment.BASE_URL) if response is None: return {} return process_session_response(response) def fetch_user_configs( - session: dict, base_url: str = BASE_URL, timeout: int = TIMEOUT + session: dict, base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT ) -> Optional[requests.Response]: """Fetch user configurations. @@ -258,7 +265,10 @@ def fetch_user_configs( def clear_user_configs( - config: str, auth_header: str, base_url: str = BASE_URL, timeout: int = TIMEOUT + config: str, + auth_header: str, + base_url: str = BackendEnvironment.BASE_URL, + timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """Clear user configurations to the server. @@ -307,7 +317,7 @@ def upload_user_field( key: str, value: Any, auth_header: str, - base_url: str = BASE_URL, + base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT, silent: bool = False, ) -> Optional[requests.Response]: @@ -365,7 +375,7 @@ def upload_config( value: str, type_: Literal["settings", "terminal_style"], auth_header: str, - base_url: str = BASE_URL, + base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """Patch user configurations to the server. @@ -429,7 +439,7 @@ def upload_routine( override: bool = False, tags: str = "", public: bool = False, - base_url: str = BASE_URL, + base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """Send a routine to the server. @@ -467,7 +477,6 @@ def upload_routine( "version": get_current_system().VERSION, "public": public, } - try: response = requests.post( headers={"Authorization": auth_header}, @@ -476,7 +485,21 @@ def upload_routine( timeout=timeout, ) if response.status_code == 200: - console.print("[green]Successfully uploaded your routine.[/green]") + username = get_current_user().profile.username + console.print("[green]Successfully uploaded your routine.[/]") + + run_env = BackendEnvironment.HUB_URL.rstrip("/") + + if public: + console.print( + f"\n[yellow]Share or edit it at {run_env}/u/{username}/routine/{name.replace(' ', '-')}[/]" + ) + else: + console.print(f"\n[yellow]Go to {run_env} to edit this script,[/]") + console.print( + f"[yellow]or even make it public so you can access it at " + f"{run_env}/u/{username}/routine/{name.replace(' ', '-')}[/]" + ) elif response.status_code != 409: # 409: routine already exists console.print( "[red]" + response.json().get("detail", "Unknown error.") + "[/red]" @@ -496,7 +519,7 @@ def upload_routine( def download_routine( auth_header: str, uuid: UUID, - base_url=BASE_URL, + base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """Download a routine from the server. @@ -520,7 +543,7 @@ def download_routine( try: response = requests.get( headers={"Authorization": auth_header}, - url=base_url + "terminal/script/" + uuid, + url=base_url + "terminal/script/" + uuid, # type: ignore timeout=timeout, ) if response.status_code == 404: @@ -542,7 +565,7 @@ def download_routine( def delete_routine( auth_header: str, uuid: UUID, - base_url=BASE_URL, + base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """Delete a routine from the server. @@ -566,7 +589,7 @@ def delete_routine( try: response = requests.delete( headers={"Authorization": auth_header}, - url=base_url + "terminal/script/" + uuid, + url=base_url + "terminal/script/" + uuid, # type: ignore timeout=timeout, ) if response.status_code == 200: @@ -592,7 +615,7 @@ def list_routines( fields: Optional[List[str]] = None, page: int = 1, size: int = 10, - base_url=BASE_URL, + base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT, silent: bool = False, ) -> Optional[requests.Response]: @@ -683,7 +706,10 @@ def get_default_routines( def generate_personal_access_token( - auth_header: str, base_url: str = BASE_URL, timeout: int = TIMEOUT, days: int = 30 + auth_header: str, + base_url: str = BackendEnvironment.BASE_URL, + timeout: int = TIMEOUT, + days: int = 30, ) -> Optional[requests.Response]: """ Generate an OpenBB Personal Access Token. @@ -732,7 +758,9 @@ def generate_personal_access_token( def get_personal_access_token( - auth_header: str, base_url: str = BASE_URL, timeout: int = TIMEOUT + auth_header: str, + base_url: str = BackendEnvironment.BASE_URL, + timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """ Show the user's OpenBB Personal Access Token. @@ -777,7 +805,9 @@ def get_personal_access_token( def revoke_personal_access_token( - auth_header: str, base_url: str = BASE_URL, timeout: int = TIMEOUT + auth_header: str, + base_url: str = BackendEnvironment.BASE_URL, + timeout: int = TIMEOUT, ) -> Optional[requests.Response]: """ Delete the user's OpenBB Personal Access Token. diff --git a/openbb_terminal/core/session/routines_handler.py b/openbb_terminal/core/session/routines_handler.py index bef93b1e747..56f185aaf2d 100644 --- a/openbb_terminal/core/session/routines_handler.py +++ b/openbb_terminal/core/session/routines_handler.py @@ -51,6 +51,7 @@ def download_routines(auth_header: str, silent: bool = False) -> list: fields=["name", "script"], page=1, size=100, + base_url=Hub.BackendEnvironment.BASE_URL, silent=silent, ) if response and response.status_code == 200: diff --git a/openbb_terminal/core/session/session_controller.py b/openbb_terminal/core/session/session_controller.py index 4069106a971..6d26fb29784 100644 --- a/openbb_terminal/core/session/session_controller.py +++ b/openbb_terminal/core/session/session_controller.py @@ -5,11 +5,11 @@ from prompt_toolkit import PromptSession import openbb_terminal.core.session.local_model as Local from openbb_terminal.core.config.paths import PACKAGE_DIRECTORY from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.session.constants import ( - REGISTER_URL, - SUPPORT_URL, +from openbb_terminal.core.session.constants import BackendEnvironment +from openbb_terminal.core.session.current_system import ( + get_current_system, + set_system_variable, ) -from openbb_terminal.core.session.current_system import get_current_system from openbb_terminal.core.session.session_model import ( LoginStatus, create_session, @@ -24,8 +24,12 @@ def display_welcome_message(links: bool = True) -> None: with open(PACKAGE_DIRECTORY / "core" / "session" / "banner.txt") as f: console.print(f"[menu]{f.read()}[/menu]\n") if links: - console.print(f"Register : [cmds]{REGISTER_URL}[/cmds]") - console.print(f"Support : [cmds]{SUPPORT_URL}[/cmds]") + console.print( + f"Register : [cmds]{BackendEnvironment.HUB_URL + 'register'}[/cmds]" + ) + console.print( + f"Support : [cmds]{BackendEnvironment.HUB_URL + 'app/terminal/support'}[/cmds]" + ) def get_user_input() -> Tuple[str, str, bool]: @@ -135,13 +139,15 @@ def prompt_cli(welcome: bool = True): # pylint: disable=inconsistent-return-statements -def launch_terminal(debug: bool = False, queue: Optional[List[str]] = None): +def launch_terminal( + debug: bool = False, dev: bool = False, queue: Optional[List[str]] = None +): """Launch terminal""" # pylint: disable=import-outside-toplevel from openbb_terminal import terminal_controller if queue: - return terminal_controller.main(debug, queue, module="") + return terminal_controller.main(debug, dev, queue, module="") terminal_controller.parse_args_and_run() @@ -170,8 +176,13 @@ def main( welcome: bool = True, prompt: bool = True, queue: Optional[List[str]] = None, + dev: bool = False, ): """Main function""" + if dev: + set_system_variable("DEV_BACKEND", True) + BackendEnvironment.BASE_URL = "https://payments.openbb.dev/" + BackendEnvironment.HUB_URL = "https://my.openbb.dev/" local_session = Local.get_session() if session is None else session diff --git a/openbb_terminal/core/session/session_model.py b/openbb_terminal/core/session/session_model.py index a0e9732e499..5140932fe8e 100644 --- a/openbb_terminal/core/session/session_model.py +++ b/openbb_terminal/core/session/session_model.py @@ -93,7 +93,7 @@ def login(session: Dict, remember: bool = False) -> LoginStatus: preferences=get_current_user().preferences, sources=SourcesModel(), ) - response = Hub.fetch_user_configs(session) + response = Hub.fetch_user_configs(session, Hub.BackendEnvironment.BASE_URL) if response is not None: if response.status_code == 200: configs = json.loads(response.content) @@ -159,6 +159,7 @@ def update_backend_sources(auth_header, configs, silent: bool = True): key="features_sources", value=updated_sources, auth_header=auth_header, + base_url=Hub.BackendEnvironment.BASE_URL, silent=silent, ) except Exception: @@ -190,7 +191,7 @@ def logout( success = True - r = Hub.delete_session(auth_header, token) + r = Hub.delete_session(auth_header, token, base_url=Hub.BackendEnvironment.BASE_URL) if not r or r.status_code != 200: success = False diff --git a/openbb_terminal/econometrics/econometrics_controller.py b/openbb_terminal/econometrics/econometrics_controller.py index 9d1bf49a9f1..a429a7230ff 100644 --- a/openbb_terminal/econometrics/econometrics_controller.py +++ b/openbb_terminal/econometrics/econometrics_controller.py @@ -164,7 +164,7 @@ class EconometricsController(BaseController): } if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} + choices: dict = self.choices_default choices["load"] = { "--file": {c: {} for c in self.DATA_FILES}, "-f": "--file", diff --git a/openbb_terminal/economy/quantitative_analysis/qa_controller.py b/openbb_terminal/economy/quantitative_analysis/qa_controller.py index a188b9b8e22..f35048f26e1 100644 --- a/openbb_terminal/economy/quantitative_analysis/qa_controller.py +++ b/openbb_terminal/economy/quantitative_analysis/qa_controller.py @@ -83,7 +83,7 @@ class QaController(StockBaseController): self.resolution = "" # For the views if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} + choices: dict = self.choices_default choices["pick"] = {c: {} for c in self.options} choices["unitroot"] = { "--fuller_reg": {c: {} for c in self.FULLER_REG}, diff --git a/openbb_terminal/featflags_controller.py b/openbb_terminal/featflags_controller.py index 43ef32893e6..b3d3374819d 100644 --- a/openbb_terminal/featflags_controller.py +++ b/openbb_terminal/featflags_controller.py @@ -66,7 +66,7 @@ class FeatureFlagsController(BaseController): super().__init__(queue) if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} + choices: dict = self.choices_default self.completer = NestedCompleter.from_nested_dict(choices) def print_help(self): diff --git a/openbb_terminal/keys_controller.py b/openbb_terminal/keys_controller.py index 3a522f68b64..69c94595a53 100644 --- a/openbb_terminal/keys_controller.py +++ b/openbb_terminal/keys_controller.py @@ -8,7 +8,7 @@ import logging from typing import Callable, Dict, List, Optional from openbb_terminal import keys_model, keys_view -from openbb_terminal.core.session.constants import KEYS_URL +from openbb_terminal.core.session.constants import BackendEnvironment from openbb_terminal.core.session.current_user import get_current_user, is_local from openbb_terminal.core.session.env_handler import get_reading_order from openbb_terminal.custom_prompt_toolkit import NestedCompleter @@ -75,7 +75,11 @@ class KeysController(BaseController): # pylint: disable=too-many-public-methods """ reading_order = get_reading_order() hierarchy = "\n ".join(list(map(str, reversed(reading_order)))) - return hierarchy if is_local() else f"{KEYS_URL}\n {hierarchy}" + return ( + hierarchy + if is_local() + else f"{BackendEnvironment.HUB_URL + 'app/terminal/api-keys'}\n {hierarchy}" + ) def print_help(self, update_status: bool = True, reevaluate: bool = True): """Print help""" diff --git a/openbb_terminal/parent_classes.py b/openbb_terminal/parent_classes.py index f3600597df5..e5ec6945bc9 100644 --- a/openbb_terminal/parent_classes.py +++ b/openbb_terminal/parent_classes.py @@ -29,8 +29,8 @@ from openbb_terminal.account.show_prompt import get_show_prompt from openbb_terminal.core.completer.choices import build_controller_choice_map from openbb_terminal.core.config.paths import HIST_FILE_PATH from openbb_terminal.core.session import hub_model as Hub +from openbb_terminal.core.session.constants import SCRIPT_TAGS from openbb_terminal.core.session.current_user import get_current_user, is_local -from openbb_terminal.core.session.routines_handler import read_routine from openbb_terminal.cryptocurrency import cryptocurrency_helpers from openbb_terminal.custom_prompt_toolkit import NestedCompleter from openbb_terminal.decorators import log_start_end @@ -85,6 +85,7 @@ SUPPORT_TYPE = ["bug", "suggestion", "question", "generic"] # TODO: We should try to avoid these global variables RECORD_SESSION = False +RECORD_SESSION_LOCAL_ONLY = False SESSION_RECORDED = list() SESSION_RECORDED_NAME = "" SESSION_RECORDED_DESCRIPTION = "" @@ -111,7 +112,6 @@ class BaseController(metaclass=ABCMeta): "reset", "support", "wiki", - "record", "stop", "screenshot", "askobb", @@ -892,8 +892,9 @@ class BaseController(metaclass=ABCMeta): action="store", dest="name", type=str, - default=datetime.now().strftime("%Y%m%d_%H%M%S_routine.openbb"), - help="Routine file name to be saved.", + default="", + help="Routine title name to be saved - only use characters, digits and whitespaces.", + nargs="+", ) parser.add_argument( "-d", @@ -901,15 +902,30 @@ class BaseController(metaclass=ABCMeta): type=str, dest="description", help="The description of the routine", + default=f"Routine recorded at {datetime.now().strftime('%H:%M')} from the OpenBB Terminal", + nargs="+", + ) + parser.add_argument( + "--tag1", + type=str, + dest="tag1", + help=f"The tag associated with the routine. Select from: {', '.join(SCRIPT_TAGS)}", default="", nargs="+", ) parser.add_argument( - "-t", - "--tags", + "--tag2", type=str, - dest="tags", - help="The tags of the routine", + dest="tag2", + help=f"The tag associated with the routine. Select from: {', '.join(SCRIPT_TAGS)}", + default="", + nargs="+", + ) + parser.add_argument( + "--tag3", + type=str, + dest="tag3", + help=f"The tag associated with the routine. Select from: {', '.join(SCRIPT_TAGS)}", default="", nargs="+", ) @@ -921,32 +937,108 @@ class BaseController(metaclass=ABCMeta): help="Whether the routine should be public or not", default=False, ) + parser.add_argument( + "-l", + "--local", + dest="local", + action="store_true", + help="Only save the routine locally - this is necessary if you are running terminal in guest mode.", + default=False, + ) if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-n") + ns_parser = self.parse_simple_args(parser, other_args) if ns_parser: + if not ns_parser.name: + console.print( + "[red]Set a routine title by using the '-n' flag. E.g. 'record -n Morning routine'[/red]" + ) + return + + tag1 = ( + " ".join(ns_parser.tag1) + if isinstance(ns_parser.tag1, list) + else ns_parser.tag1 + ) + if tag1 and tag1 not in SCRIPT_TAGS: + console.print( + f"[red]The parameter 'tag1' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]" + ) + return + + tag2 = ( + " ".join(ns_parser.tag2) + if isinstance(ns_parser.tag2, list) + else ns_parser.tag2 + ) + if tag2 and tag2 not in SCRIPT_TAGS: + console.print( + f"[red]The parameter 'tag2' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]" + ) + return + + tag3 = ( + " ".join(ns_parser.tag3) + if isinstance(ns_parser.tag3, list) + else ns_parser.tag3 + ) + if tag3 and tag3 not in SCRIPT_TAGS: + console.print( + f"[red]The parameter 'tag3' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]" + ) + return + + if is_local() and not ns_parser.local: + console.print( + "[red]Recording session to the OpenBB Hub is not supported in guest mode.[/red]" + ) + console.print( + "\n[yellow]Sign to OpenBB Hub to register: http://openbb.co[/yellow]" + ) + console.print( + "\n[yellow]Otherwise set the flag '-l' to save the file locally.[/yellow]" + ) + return + + # Check if title has a valid format + title = " ".join(ns_parser.name) if ns_parser.name else "" + # To this day, the reason why /^[a-zA-Z0-9\s]+$/ does not work is unknown + pattern = re.compile(r"^[a-zA-Z0-9\s]+(?:[a-zA-Z0-9\s]+)*$") + if not pattern.match(title): + console.print( + f"[red]Title '{title}' has invalid format. Please use only digits, characters and whitespaces.[/]" + ) + return + global RECORD_SESSION + global RECORD_SESSION_LOCAL_ONLY global SESSION_RECORDED_NAME global SESSION_RECORDED_DESCRIPTION global SESSION_RECORDED_TAGS global SESSION_RECORDED_PUBLIC - SESSION_RECORDED_NAME = ( - ns_parser.name - if ".openbb" in ns_parser.name - else ns_parser.name + ".openbb" + RECORD_SESSION = True + RECORD_SESSION_LOCAL_ONLY = ns_parser.local + SESSION_RECORDED_NAME = title + SESSION_RECORDED_DESCRIPTION = ( + " ".join(ns_parser.description) + if isinstance(ns_parser.description, list) + else ns_parser.description ) + SESSION_RECORDED_TAGS = tag1 if tag1 else "" + SESSION_RECORDED_TAGS += "," + tag2 if tag2 else "" + SESSION_RECORDED_TAGS += "," + tag3 if tag3 else "" - SESSION_RECORDED_DESCRIPTION = " ".join(ns_parser.description) - SESSION_RECORDED_TAGS = " ".join(ns_parser.tags) if ns_parser.tags else "" SESSION_RECORDED_PUBLIC = ns_parser.public console.print( - "[green]The session is successfully being recorded." - + " Remember to 'stop' before exiting terminal!\n[/green]" + f"[green]The routine '{title}' is successfully being recorded.[/green]" + ) + console.print( + "\n[yellow]Remember to run 'stop' command when you are done!\n[/yellow]" ) - RECORD_SESSION = True @log_start_end(log=logger) def call_stop(self, _) -> None: @@ -956,57 +1048,102 @@ class BaseController(metaclass=ABCMeta): if not RECORD_SESSION: console.print( - "[red]There is no session being recorded. Start one using 'record'[/red]\n" + "[red]There is no session being recorded. Start one using the command 'record'[/red]\n" ) - elif not SESSION_RECORDED: + elif len(SESSION_RECORDED) < 5: console.print( - "[red]There is no session to be saved. Run at least 1 command after starting 'record'[/red]\n" + "[red]Run at least 4 commands before stopping recording a session.[/red]\n" ) else: current_user = get_current_user() - routine_file = os.path.join( - current_user.preferences.USER_ROUTINES_DIRECTORY, SESSION_RECORDED_NAME - ) - if os.path.isfile(routine_file): - routine_file = os.path.join( - current_user.preferences.USER_ROUTINES_DIRECTORY, - datetime.now().strftime("%Y%m%d_%H%M%S_") + SESSION_RECORDED_NAME, + # Check if the user just wants to store routine locally + # This works regardless of whether they are logged in or not + if RECORD_SESSION_LOCAL_ONLY: + # Whitespaces are replaced by underscores and an .openbb extension is added + title_for_local_storage = ( + SESSION_RECORDED_NAME.replace(" ", "_") + ".openbb" ) - # Writing to file - with open(routine_file, "w") as file1: - # Writing data to a file - file1.writelines([c + "\n\n" for c in SESSION_RECORDED[:-1]]) + routine_file = os.path.join( + current_user.preferences.USER_ROUTINES_DIRECTORY, + title_for_local_storage, + ) - console.print( - f"[green]Your routine has been recorded and saved here: {routine_file}[/green]\n" - ) - - if not is_local(): - routine = read_routine(file_name=routine_file) - if routine is not None: - name = SESSION_RECORDED_NAME.split(sep=".openbb", maxsplit=-1)[0] - kwargs = { - "auth_header": current_user.profile.get_auth_header(), - "name": name, - "description": SESSION_RECORDED_DESCRIPTION, - "routine": routine, - "tags": SESSION_RECORDED_TAGS, - "public": SESSION_RECORDED_PUBLIC, - } - response = Hub.upload_routine(**kwargs) # type: ignore - if response is not None and response.status_code == 409: - i = console.input( - "A routine with the same name already exists, " - "do you want to replace it? (y/n): " - ) + # If file already exists, add a timestamp to the name + if os.path.isfile(routine_file): + i = console.input( + "A local routine with the same name already exists, " + "do you want to override it? (y/n): " + ) + console.print("") + while i.lower() not in ["y", "yes", "n", "no"]: + i = console.input("Select 'y' or 'n' to proceed: ") console.print("") - if i.lower() in ["y", "yes"]: - kwargs["override"] = True # type: ignore - response = Hub.upload_routine(**kwargs) # type: ignore - else: - console.print("[info]Aborted.[/info]") + + if i.lower() in ["n", "no"]: + new_name = ( + datetime.now().strftime("%Y%m%d_%H%M%S_") + + title_for_local_storage + ) + routine_file = os.path.join( + current_user.preferences.USER_ROUTINES_DIRECTORY, + new_name, + ) + console.print( + f"[yellow]The routine name has been updated to '{new_name}'[/yellow]\n" + ) + + # Writing to file + with open(routine_file, "w") as file1: + lines = ["# OpenBB Terminal - Routine", "\n"] + username = get_current_user().profile.username + lines += [f"# Author: {username}", "\n\n"] if username else ["\n"] + lines += [ + f"# Title: {SESSION_RECORDED_NAME}", + "\n", + f"# Tags: {SESSION_RECORDED_TAGS}", + "\n\n", + f"# Description: {SESSION_RECORDED_DESCRIPTION}", + "\n\n", + ] + lines += [c + "\n" for c in SESSION_RECORDED[:-1]] + # Writing data to a file + file1.writelines(lines) + + console.print( + f"[green]Your routine has been recorded and saved here: {routine_file}[/green]\n" + ) + + # If user doesn't specify they want to store routine locally + else: + # Confirm that the user is logged in + if not is_local(): + # routine = read_routine(file_name=routine_file) + routine = "\n".join(SESSION_RECORDED[:-1]) + + if routine is not None: + kwargs = { + "auth_header": current_user.profile.get_auth_header(), + "name": SESSION_RECORDED_NAME, + "description": SESSION_RECORDED_DESCRIPTION, + "routine": routine, + "tags": SESSION_RECORDED_TAGS, + "public": SESSION_RECORDED_PUBLIC, + "base_url": Hub.BackendEnvironment.BASE_URL, + } + response = Hub.upload_routine(**kwargs) # type: ignore + if response is not None and response.status_code == 409: + i = console.input( + "A routine with the same name already exists, " + "do you want to replace it? (y/n): " + ) + console.print("") + if i.lower() in ["y", "yes"]: + kwargs["override"] = True # type: ignore + response = Hub.upload_routine(**kwargs) # type: ignore + else: + console.print("[info]Aborted.[/info]") # Clear session to be recorded again RECORD_SESSION = False diff --git a/openbb_terminal/portfolio/brokers/degiro/degiro_controller.py b/openbb_terminal/portfolio/brokers/degiro/degiro_controller.py index fdae00fab59..bae880cd568 100644 --- a/openbb_terminal/portfolio/brokers/degiro/degiro_controller.py +++ b/openbb_terminal/portfolio/brokers/degiro/degiro_controller.py @@ -43,7 +43,7 @@ class DegiroController(BaseController): self.__degiro_view = DegiroView() if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} + choices: dict = self.choices_default zero_to_hundred: dict = {str(c): {} for c in range(0, 100)} diff --git a/openbb_terminal/portfolio/portfolio_engine.py b/openbb_terminal/portfolio/portfolio_engine.py index 22adcd191e9..2c945b661d5 100644 --- a/openbb_terminal/portfolio/portfolio_engine.py +++ b/openbb_terminal/portfolio/portfolio_engine.py @@ -4,7 +4,6 @@ __docformat__ = "numpy" import datetime import logging import warnings -from os import environ from typing import Any, Dict import numpy as np @@ -12,6 +11,7 @@ import pandas as pd import yfinance as yf from tqdm import tqdm +from openbb_terminal.core.session.current_system import get_current_system from openbb_terminal.decorators import log_start_end from openbb_terminal.portfolio.allocation_model import get_allocation from openbb_terminal.portfolio.portfolio_helper import ( @@ -196,7 +196,7 @@ class PortfolioEngine: # Load transactions from file if path.endswith(".xlsx"): - if str(environ.get("DEBUG_MODE", "false")).lower() != "true": + if not get_current_system().DEBUG_MODE: warnings.filterwarnings( "ignore", category=UserWarning, module="openpyxl" ) diff --git a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_controller.py b/openbb_terminal/portfolio/portfolio_optimization/parameters/params_controller.py index 472f0d24bd4..8d92c0188ab 100644 --- a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_controller.py +++ b/openbb_terminal/portfolio/portfolio_optimization/parameters/params_controller.py @@ -78,7 +78,7 @@ class ParametersController(BaseController): self.DATA_FILES = params_helpers.load_data_files() if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} + choices: dict = self.choices_default choices["set"] = {c: None for c in self.models} choices["set"]["--model"] = {c: None for c in self.models} choices["set"]["-m"] = "--model" diff --git a/openbb_terminal/settings_controller.py b/openbb_terminal/settings_controller.py index 4d0b31e6007..4ba909c0021 100644 --- a/openbb_terminal/settings_controller.py +++ b/openbb_terminal/settings_controller.py @@ -21,7 +21,7 @@ from openbb_terminal.core.config.paths import ( SETTINGS_ENV_FILE, STYLES_DIRECTORY_REPO, ) -from openbb_terminal.core.session.constants import CHARTS_TABLES_URL, COLORS_URL +from openbb_terminal.core.session.constants import BackendEnvironment from openbb_terminal.core.session.current_user import ( get_current_user, is_local, @@ -231,7 +231,7 @@ class SettingsController(BaseController): " Then, place the downloaded file 'openbb_config.richstyle.json'" f" inside {get_current_user().preferences.USER_STYLES_DIRECTORY} or " f"{STYLES_DIRECTORY_REPO}. If you have a hub account you can change colors " - f"here {COLORS_URL}.", + f"here {BackendEnvironment.HUB_URL + 'app/terminal/theme'}.", ) if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-s") @@ -246,6 +246,7 @@ class SettingsController(BaseController): value=ns_parser.style, type_="settings", auth_header=get_current_user().profile.get_auth_header(), + base_url=BackendEnvironment.HUB_URL, ) console.print("") console.print("Colors updated.") @@ -266,7 +267,7 @@ class SettingsController(BaseController): dest="style", choices=["dark", "light"], help="Choose chart style. If you have a hub account you can change theme " - f"here {CHARTS_TABLES_URL}.", + f"here {BackendEnvironment.HUB_URL + 'app/terminal/theme/charts-tables'}.", required="-h" not in other_args and "--help" not in other_args, ) if other_args and "-" not in other_args[0][0]: @@ -282,6 +283,7 @@ class SettingsController(BaseController): value=ns_parser.style, type_="terminal_style", auth_header=get_current_user().profile.get_auth_header(), + base_url=BackendEnvironment.HUB_URL, ) console.print("") theme.apply_style(ns_parser.style) @@ -303,7 +305,7 @@ class SettingsController(BaseController): dest="style", choices=["dark", "light"], help="Choose table style. If you have a hub account you can change theme " - f"here {CHARTS_TABLES_URL}.", + f"here {BackendEnvironment.HUB_URL + 'app/terminal/theme/charts-tables'}.", required="-h" not in other_args and "--help" not in other_args, ) if other_args and "-" not in other_args[0][0]: @@ -319,6 +321,7 @@ class SettingsController(BaseController): value=ns_parser.style, type_="terminal_style", auth_header=get_current_user().profile.get_auth_header(), + base_url=BackendEnvironment.HUB_URL, ) console.print("") console.print("Table theme updated.\n") diff --git a/openbb_terminal/sources_controller.py b/openbb_terminal/sources_controller.py index eebedbe7904..d6483057bbf 100644 --- a/openbb_terminal/sources_controller.py +++ b/openbb_terminal/sources_controller.py @@ -8,7 +8,7 @@ import os from pathlib import Path from typing import List, Optional -from openbb_terminal.core.session.constants import SOURCES_URL +from openbb_terminal.core.session.constants import BackendEnvironment # IMPORTATION THIRDPARTY # IMPORTATION INTERNAL @@ -54,7 +54,7 @@ class SourcesController(BaseController): if Path(sources_file).exists() and os.stat(sources_file).st_size > 0: self.source = sources_file else: - self.source = SOURCES_URL + self.source = BackendEnvironment.HUB_URL + "app/terminal/data-sources" def parse_input(self, an_input: str) -> List: """Parse controller input @@ -176,6 +176,7 @@ class SourcesController(BaseController): key="features_sources", value=choices, auth_header=get_current_user().profile.get_auth_header(), + base_url=BackendEnvironment.BASE_URL, ) console.print("") console.print( diff --git a/openbb_terminal/terminal_controller.py b/openbb_terminal/terminal_controller.py index 649efdb55df..84453250a20 100644 --- a/openbb_terminal/terminal_controller.py +++ b/openbb_terminal/terminal_controller.py @@ -20,7 +20,6 @@ import certifi import pandas as pd import requests from prompt_toolkit import PromptSession -from prompt_toolkit.completion import NestedCompleter from prompt_toolkit.formatted_text import HTML from prompt_toolkit.styles import Style @@ -34,9 +33,10 @@ from openbb_terminal.core.config.paths import ( SETTINGS_ENV_FILE, ) from openbb_terminal.core.log.generation.custom_logger import log_terminal -from openbb_terminal.core.session import session_controller +from openbb_terminal.core.session import constants, session_controller from openbb_terminal.core.session.current_system import set_system_variable from openbb_terminal.core.session.current_user import get_current_user, set_preference +from openbb_terminal.custom_prompt_toolkit import NestedCompleter from openbb_terminal.helper_funcs import ( EXPORT_ONLY_RAW_DATA_ALLOWED, check_positive, @@ -90,6 +90,7 @@ class TerminalController(BaseController): "news", "intro", "askobb", + "record", ] CHOICES_MENUS = [ "stocks", @@ -142,45 +143,10 @@ class TerminalController(BaseController): def update_runtime_choices(self): """Update runtime choices.""" - self.ROUTINE_FILES = { - filepath.name: filepath - for filepath in get_current_user().preferences.USER_ROUTINES_DIRECTORY.rglob( - "*.openbb" - ) - } - if get_current_user().profile.get_token(): - self.ROUTINE_DEFAULT_FILES = { - filepath.name: filepath - for filepath in Path( - get_current_user().preferences.USER_ROUTINES_DIRECTORY - / "hub" - / "default" - ).rglob("*.openbb") - } - self.ROUTINE_PERSONAL_FILES = { - filepath.name: filepath - for filepath in Path( - get_current_user().preferences.USER_ROUTINES_DIRECTORY - / "hub" - / "personal" - ).rglob("*.openbb") - } - - self.ROUTINE_CHOICES["--file"] = { - filename: None for filename in self.ROUTINE_FILES - } - self.ROUTINE_CHOICES["--example"] = None - self.ROUTINE_CHOICES["-e"] = None - self.ROUTINE_CHOICES["--input"] = None - self.ROUTINE_CHOICES["-i"] = None - self.ROUTINE_CHOICES["--help"] = None - self.ROUTINE_CHOICES["--h"] = None - self.ROUTINE_CHOICES["--url"] = None - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: + # choices: dict = self.choices_default choices: dict = {c: {} for c in self.controller_choices} # type: ignore choices["support"] = self.SUPPORT_CHOICES - choices["exe"] = self.ROUTINE_CHOICES choices["news"] = self.NEWS_CHOICES choices["news"]["--source"] = {c: {} for c in ["Biztoc", "Feedparser"]} choices["hold"] = {c: None for c in ["on", "off", "-s", "--sameaxis"]} @@ -188,6 +154,56 @@ class TerminalController(BaseController): if biztoc_model.BIZTOC_TAGS: choices["news"]["--tag"] = {c: {} for c in biztoc_model.BIZTOC_TAGS} + self.ROUTINE_FILES = { + filepath.name: filepath + for filepath in get_current_user().preferences.USER_ROUTINES_DIRECTORY.rglob( + "*.openbb" + ) + } + if get_current_user().profile.get_token(): + self.ROUTINE_DEFAULT_FILES = { + filepath.name: filepath + for filepath in Path( + get_current_user().preferences.USER_ROUTINES_DIRECTORY + / "hub" + / "default" + ).rglob("*.openbb") + } + self.ROUTINE_PERSONAL_FILES = { + filepath.name: filepath + for filepath in Path( + get_current_user().preferences.USER_ROUTINES_DIRECTORY + / "hub" + / "personal" + ).rglob("*.openbb") + } + + choices["exe"] = { + "--file": { + filename: {} for filename in list(self.ROUTINE_FILES.keys()) + }, + "-f": "--file", + "--example": None, + "-e": "--example", + "--input": None, + "-i": "--input", + "--url": None, + } + + choices["record"] = { + "--name": None, + "-n": "--name", + "--description": None, + "-d": "--description", + "--public": None, + "-p": "--public", + "--local": None, + "-l": "--local", + "--tag1": {c: None for c in constants.SCRIPT_TAGS}, + "--tag2": {c: None for c in constants.SCRIPT_TAGS}, + "--tag3": {c: None for c in constants.SCRIPT_TAGS}, + } + self.completer = NestedCompleter.from_nested_dict(choices) def print_help(self): @@ -1085,6 +1101,7 @@ def run_routine(file: str, routines_args=Optional[str]): def main( debug: bool, + dev: bool, path_list: List[str], routines_args: Optional[List[str]] = None, **kwargs, @@ -1095,6 +1112,8 @@ def main( ---------- debug : bool Whether to run the terminal in debug mode + dev: + Points backend towards development environment instead of production test : bool Whether to run the terminal in integrated test mode filtert : str @@ -1114,6 +1133,11 @@ def main( if debug: set_system_variable("DEBUG_MODE", True) + if dev: + set_system_variable("DEV_BACKEND", True) + constants.BackendEnvironment.BASE_URL = "https://payments.openbb.dev/" + constants.BackendEnvironment.HUB_URL = "https://my.openbb.dev/" + cfg.start_plot_backend() if isinstance(path_list, list) and path_list[0].endswith(".openbb"): @@ -1141,6 +1165,13 @@ def parse_args_and_run(): default=False, help="Runs the terminal in debug mode.", ) + parser.add_argument( + "--dev", + dest="dev", + action="store_true", + default=False, + help="Points backend towards development environment instead of production", + ) parser.add_argument( "--file", help="The path or .openbb file to run.", @@ -1212,6 +1243,7 @@ def parse_args_and_run(): main( ns_parser.debug, + ns_parser.dev, ns_parser.path, ns_parser.routine_args, module=ns_parser.module, diff --git a/openbb_terminal/terminal_helper.py b/openbb_terminal/terminal_helper.py index 81f3ff7e33e..63458b34186 100644 --- a/openbb_terminal/terminal_helper.py +++ b/openbb_terminal/terminal_helper.py @@ -23,7 +23,7 @@ from openbb_terminal import thought_of_the_day as thought from openbb_terminal.base_helpers import load_env_files from openbb_terminal.core.config.paths import HIST_FILE_PATH, SETTINGS_ENV_FILE from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.session.constants import REGISTER_URL +from openbb_terminal.core.session.constants import BackendEnvironment from openbb_terminal.core.session.current_system import get_current_system from openbb_terminal.core.session.current_user import ( get_current_user, @@ -257,7 +257,8 @@ def print_guest_block_msg(): console.print( "[info]You are currently logged as a guest.[/info]\n" "[info]Login to use this feature.[/info]\n\n" - f"[info]If you don't have an account, you can create one here: [/info][cmds]{REGISTER_URL}\n[/cmds]" + "[info]If you don't have an account, you can create one here: [/info]" + f"[cmds]{BackendEnvironment.HUB_URL + 'register'}\n[/cmds]" ) @@ -369,6 +370,7 @@ def reset(queue: Optional[List[str]] = None): plots_backend().close(reset=True) load_env_files() debug = get_current_system().DEBUG_MODE + dev = get_current_system().DEV_BACKEND try: # save the current user @@ -399,7 +401,7 @@ def reset(queue: Optional[List[str]] = None): if is_local(): from openbb_terminal.terminal_controller import main - main(debug, queue_list, module="") # type: ignore + main(debug, dev, queue_list, module="") # type: ignore else: from openbb_terminal.core.session import session_controller diff --git a/terminal.py b/terminal.py index dd0977a5f62..f9d8d8e5248 100644 --- a/terminal.py +++ b/terminal.py @@ -34,8 +34,9 @@ def main(): and ("--login" in sys.argv[1:] or is_installer()) and sys.stdin.isatty() ) + dev = "--dev" in sys.argv[1:] - session_controller.main(prompt=prompt_login) + session_controller.main(prompt=prompt_login, dev=dev) if __name__ == "__main__": diff --git a/tests/openbb_terminal/session/test_hub_model.py b/tests/openbb_terminal/session/test_hub_model.py index 25f620deef9..489ccae660f 100644 --- a/tests/openbb_terminal/session/test_hub_model.py +++ b/tests/openbb_terminal/session/test_hub_model.py @@ -46,7 +46,7 @@ def test_create_session_success(email, password): assert response.json() == TEST_RESPONSE mock_post.assert_called_once_with( - url=hub_model.BASE_URL + "login", + url=hub_model.BackendEnvironment.BASE_URL + "login", json={"email": email, "password": password, "remember": True}, timeout=hub_model.TIMEOUT, ) @@ -113,7 +113,7 @@ def test_create_session_from_token_success(mocker, token): assert response.json() == TEST_RESPONSE mock_post.assert_called_once_with( - url=hub_model.BASE_URL + "sdk/login", + url=hub_model.BackendEnvironment.BASE_URL + "sdk/login", json={"token": token}, timeout=hub_model.TIMEOUT, ) @@ -151,7 +151,7 @@ def test_delete_session_success(auth_header, token): assert response.status_code == 200 mock_post.assert_called_once_with( - url=hub_model.BASE_URL + "logout", + url=hub_model.BackendEnvironment.BASE_URL + "logout", headers={"Authorization": "Bearer test_token"}, json={"token": "test_token"}, timeout=hub_model.TIMEOUT, @@ -336,7 +336,7 @@ def test_fetch_user_configs_success(token_type, access_token): assert result == mock_response requests_get_mock.assert_called_once() _, kwargs = requests_get_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "terminal/user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user" assert kwargs["headers"] == { "Authorization": f"{token_type.title()} {access_token}" } @@ -352,7 +352,7 @@ def test_fetch_user_configs_failure(): result = hub_model.fetch_user_configs(session) assert isinstance(result, requests.Response) get_mock.assert_called_once_with( - url=hub_model.BASE_URL + "terminal/user", + url=hub_model.BackendEnvironment.BASE_URL + "terminal/user", headers={"Authorization": "Bearer abc123"}, timeout=hub_model.TIMEOUT, ) @@ -366,7 +366,7 @@ def test_fetch_user_configs_connection_error(): result = hub_model.fetch_user_configs(session) assert result is None get_mock.assert_called_once_with( - url=hub_model.BASE_URL + "terminal/user", + url=hub_model.BackendEnvironment.BASE_URL + "terminal/user", headers={"Authorization": "Bearer abc123"}, timeout=hub_model.TIMEOUT, ) @@ -378,7 +378,7 @@ def test_fetch_user_configs_timeout(): result = hub_model.fetch_user_configs(session) assert result is None get_mock.assert_called_once_with( - url=hub_model.BASE_URL + "terminal/user", + url=hub_model.BackendEnvironment.BASE_URL + "terminal/user", headers={"Authorization": "Bearer abc123"}, timeout=hub_model.TIMEOUT, ) @@ -390,7 +390,7 @@ def test_fetch_user_configs_exception(): result = hub_model.fetch_user_configs(session) assert result is None get_mock.assert_called_once_with( - url=hub_model.BASE_URL + "terminal/user", + url=hub_model.BackendEnvironment.BASE_URL + "terminal/user", headers={"Authorization": "Bearer abc123"}, timeout=hub_model.TIMEOUT, ) @@ -415,7 +415,7 @@ def test_upload_config_success(key, value, type_, auth_header): assert result.status_code == mock_response.status_code requests_patch_mock.assert_called_once() _, kwargs = requests_patch_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "terminal/user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user" assert kwargs["headers"] == {"Authorization": f"{auth_header}"} assert kwargs["json"] == {"key": f"features_{type_}.{key}", "value": value} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -434,7 +434,7 @@ def test_upload_config_failure(): assert result.status_code == mock_response.status_code requests_patch_mock.assert_called_once() _, kwargs = requests_patch_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "terminal/user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"key": "features_settings.key", "value": "value"} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -451,7 +451,7 @@ def test_upload_config_connection_error(): assert result is None requests_patch_mock.assert_called_once() _, kwargs = requests_patch_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "terminal/user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"key": "features_settings.key", "value": "value"} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -468,7 +468,7 @@ def test_upload_config_timeout(): assert result is None requests_patch_mock.assert_called_once() _, kwargs = requests_patch_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "terminal/user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"key": "features_settings.key", "value": "value"} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -485,7 +485,7 @@ def test_upload_config_exception(): assert result is None requests_patch_mock.assert_called_once() _, kwargs = requests_patch_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "terminal/user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"key": "features_settings.key", "value": "value"} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -504,7 +504,7 @@ def test_clear_user_configs_success(): assert result.status_code == mock_response.status_code requests_put_mock.assert_called_once() _, kwargs = requests_put_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"config": {}} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -523,7 +523,7 @@ def test_clear_user_configs_failure(): assert result.status_code == mock_response.status_code requests_put_mock.assert_called_once() _, kwargs = requests_put_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"config": {}} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -539,7 +539,7 @@ def test_clear_user_configs_timeout(): assert result is None requests_put_mock.assert_called_once() _, kwargs = requests_put_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"config": {}} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -555,7 +555,7 @@ def test_clear_user_configs_connection_error(): assert result is None requests_put_mock.assert_called_once() _, kwargs = requests_put_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"config": {}} assert kwargs["timeout"] == hub_model.TIMEOUT @@ -570,7 +570,7 @@ def test_clear_user_configs_exception(): assert result is None requests_put_mock.assert_called_once() _, kwargs = requests_put_mock.call_args - assert kwargs["url"] == hub_model.BASE_URL + "user" + assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user" assert kwargs["headers"] == {"Authorization": "auth_header"} assert kwargs["json"] == {"config": {}} assert kwargs["timeout"] == hub_model.TIMEOUT diff --git a/tests/openbb_terminal/test_terminal_controller.py b/tests/openbb_terminal/test_terminal_controller.py index 7abdbadde20..5ff5f757236 100644 --- a/tests/openbb_terminal/test_terminal_controller.py +++ b/tests/openbb_terminal/test_terminal_controller.py @@ -68,10 +68,10 @@ def no_suppress(): @pytest.mark.vcr(record_mode="none") @pytest.mark.record_stdout @pytest.mark.parametrize( - "debug, path", - [(True, None), (False, ["scripts/test_alt_covid.openbb"])], + "debug, dev, path", + [(True, False, None), (False, False, ["scripts/test_alt_covid.openbb"])], ) -def test_menu(mocker, debug, path): +def test_menu(mocker, debug, dev, path): mocker.patch(target="terminal_controller.terminal") mocker.patch(target="terminal_controller.suppress_stdout", side_effect=no_suppress) - terminal_controller.main(debug, path) + terminal_controller.main(debug, dev, path)