diff options
author | Henrique Joaquim <henriquecjoaquim@gmail.com> | 2024-05-10 14:39:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-10 13:39:28 +0000 |
commit | 13a62486de285d0a6a04827b45c917ccb2e5951b (patch) | |
tree | 6fda36cd2b42ae1384fc45979964e4a940959909 | |
parent | 5bd4ae0856a228df1af47f87cc1d15c799e53e72 (diff) |
[Feature] Misc improvements on the Platform CLI (#6370)
* section description
* remove comment
* show msg only if it's an OBBject
* ommit coverage from menus
* styling: no new lines after settings
* Bugfix/cli max rows (#6374)
* fix: cli max rows
* fix: settings menu
---------
Co-authored-by: Henrique Joaquim <henriquecjoaquim@gmail.com>
* add a new line only to separate menus and commands
* if there's no menu description on reference.json, use the commands of that menu to create a pseudo description
* use the PATH instead in the top of the menu help
* default name len to 23
* keep command on the obbject so it can be shown on the results
* left spacing regardless description
* display cached results on every platform menu's help
* display info instead of sections and display cached results
* prepend OBB to use on the --data
* config to set number of cached results to display
* correct hub link
* Save routines locally if not logged in.
* Change the exit message
* Point to new docs on first launch.
* proper checking of max_obbjects_exceeded
* fix global flag on local routines
* Remove language from settings as it is not supported.
* Remove rcontext flag
* export to account multiple formats
* Revert "Remove rcontext flag"
This reverts commit 8a1f64b71c109217ce48a76a4c8e448157a5675f.
* Remove
* leftover
* properly match provider being used with provider arguments so that kwargs are correctly filtered
---------
Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com>
Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
Co-authored-by: James Maslek <jmaslek11@gmail.com>
-rw-r--r-- | cli/CONTRIBUTING.md | 1 | ||||
-rw-r--r-- | cli/openbb_cli/argparse_translator/argparse_translator.py | 16 | ||||
-rw-r--r-- | cli/openbb_cli/argparse_translator/obbject_registry.py | 1 | ||||
-rw-r--r-- | cli/openbb_cli/assets/i18n/en.yml | 8 | ||||
-rw-r--r-- | cli/openbb_cli/config/constants.py | 4 | ||||
-rw-r--r-- | cli/openbb_cli/config/menu_text.py | 25 | ||||
-rw-r--r-- | cli/openbb_cli/config/setup.py | 3 | ||||
-rw-r--r-- | cli/openbb_cli/controllers/base_controller.py | 21 | ||||
-rw-r--r-- | cli/openbb_cli/controllers/base_platform_controller.py | 63 | ||||
-rw-r--r-- | cli/openbb_cli/controllers/cli_controller.py | 37 | ||||
-rw-r--r-- | cli/openbb_cli/controllers/settings_controller.py | 67 | ||||
-rw-r--r-- | cli/openbb_cli/controllers/utils.py | 25 | ||||
-rw-r--r-- | cli/openbb_cli/models/settings.py | 2 | ||||
-rw-r--r-- | cli/openbb_cli/session.py | 6 |
14 files changed, 165 insertions, 114 deletions
diff --git a/cli/CONTRIBUTING.md b/cli/CONTRIBUTING.md index 49314e4a8e4..5a65b709b68 100644 --- a/cli/CONTRIBUTING.md +++ b/cli/CONTRIBUTING.md @@ -1323,7 +1323,6 @@ This class contains both important variables and methods that are common across - `cls`: clear screen - `home`: go back to the main root -- `about`: allows to open our documentation directly on the menu or command - `h`, `?` and `help`: display the help menu the user is in - `q`, `quit` and `..`: go back to one menu above - `exit`: exit the platform diff --git a/cli/openbb_cli/argparse_translator/argparse_translator.py b/cli/openbb_cli/argparse_translator/argparse_translator.py index 001e7afce04..724c2bea306 100644 --- a/cli/openbb_cli/argparse_translator/argparse_translator.py +++ b/cli/openbb_cli/argparse_translator/argparse_translator.py @@ -203,7 +203,7 @@ class ArgparseTranslator: self.func = func self.signature = inspect.signature(func) self.type_hints = get_type_hints(func) - self.provider_parameters: List[str] = [] + self.provider_parameters: Dict[str, List[str]] = {} self._parser = argparse.ArgumentParser( prog=func.__name__, @@ -218,6 +218,7 @@ class ArgparseTranslator: if custom_argument_groups: for group in custom_argument_groups: + self.provider_parameters[group.name] = [] argparse_group = self._parser.add_argument_group(group.name) for argument in group.arguments: self._handle_argument_in_groups(argument, argparse_group) @@ -278,7 +279,8 @@ class ArgparseTranslator: if f"--{argument.name}" not in self._parser._option_string_actions: kwargs = argument.model_dump(exclude={"name"}, exclude_none=True) group.add_argument(f"--{argument.name}", **kwargs) - self.provider_parameters.append(argument.name) + if group.title in self.provider_parameters: + self.provider_parameters[group.title].append(argument.name) else: kwargs = argument.model_dump(exclude={"name"}, exclude_none=True) @@ -582,11 +584,19 @@ class ArgparseTranslator: kwargs = self._unflatten_args(vars(parsed_args)) kwargs = self._update_with_custom_types(kwargs) + provider = kwargs.get("provider") + provider_args = [] + if provider and provider in self.provider_parameters: + provider_args = self.provider_parameters[provider] + else: + for args in self.provider_parameters.values(): + provider_args.extend(args) + # remove kwargs that doesn't match the signature or provider parameters kwargs = { key: value for key, value in kwargs.items() - if key in self.signature.parameters or key in self.provider_parameters + if key in self.signature.parameters or key in provider_args } return self.func(**kwargs) diff --git a/cli/openbb_cli/argparse_translator/obbject_registry.py b/cli/openbb_cli/argparse_translator/obbject_registry.py index 848140062a7..1c4cbf80dd9 100644 --- a/cli/openbb_cli/argparse_translator/obbject_registry.py +++ b/cli/openbb_cli/argparse_translator/obbject_registry.py @@ -79,6 +79,7 @@ class Registry: "provider": obbject.provider, "standard params": _handle_standard_params(obbject), "data": _handle_data_repr(obbject), + "command": obbject.extra.get("command", ""), } return obbjects diff --git a/cli/openbb_cli/assets/i18n/en.yml b/cli/openbb_cli/assets/i18n/en.yml index 05a772b7320..2e19ee4ce39 100644 --- a/cli/openbb_cli/assets/i18n/en.yml +++ b/cli/openbb_cli/assets/i18n/en.yml @@ -1,6 +1,5 @@ en: intro: introduction on the OpenBB Platform CLI - about: discover the capabilities of the OpenBB Platform CLI (https://openbb.co/docs) support: pre-populate a support ticket for our team to evaluate survey: fill in our 2-minute survey so we better understand how we can improve the CLI settings: enable and disable feature flags, preferences and settings @@ -10,8 +9,8 @@ en: exe: execute .openbb routine scripts (use exe --example for an example) _configure_: Configure your own CLI _main_menu_: Main menu - settings/_info_: Feature flags - settings/_settings_: Settings and preferences + settings/_feature_flags_: Feature flags + settings/_preferences_: Preferences settings/retryload: retry misspelled commands with load first settings/interactive: open dataframes in interactive window settings/cls: clear console after each command @@ -20,7 +19,6 @@ en: settings/thoughts: thoughts of the day settings/reporthtml: open report as HTML otherwise notebook settings/exithelp: automatically print help when quitting menu - settings/rcontext: remember contexts loaded params during session settings/rich: colorful rich CLI settings/richpanel: colorful rich CLI panel settings/watermark: watermark in figures @@ -31,8 +29,8 @@ en: settings/console_style: apply a custom rich style to the CLI settings/flair: choose flair icon settings/timezone: pick timezone - settings/language: translation language settings/n_rows: number of rows to show on non interactive tables settings/n_cols: number of columns to show on non interactive tables settings/obbject_msg: show obbject registry message after a new result is added settings/obbject_res: define the maximum number of obbjects allowed in the registry + settings/obbject_display: define the maximum number of cached results to display on the help menu diff --git a/cli/openbb_cli/config/constants.py b/cli/openbb_cli/config/constants.py index b5b8867a243..08aa50e107a 100644 --- a/cli/openbb_cli/config/constants.py +++ b/cli/openbb_cli/config/constants.py @@ -65,7 +65,7 @@ AVAILABLE_FLAIRS = { ":mercury": "(☿)", ":hidden": "", ":sun": "(☼)", - ":moon": "(☾)", + ":moon": "(🌕)", ":nuke": "(☢)", ":hazard": "(☣)", ":tunder": "(☈)", @@ -76,6 +76,6 @@ AVAILABLE_FLAIRS = { ":scales": "(⚖)", ":ball": "(⚽)", ":golf": "(⛳)", - ":piece": "(☮)", + ":peace": "(☮)", ":yy": "(☯)", } diff --git a/cli/openbb_cli/config/menu_text.py b/cli/openbb_cli/config/menu_text.py index 56e2034998c..e4b4e1bf8c2 100644 --- a/cli/openbb_cli/config/menu_text.py +++ b/cli/openbb_cli/config/menu_text.py @@ -30,7 +30,7 @@ RICH_TAGS = [ class MenuText: """Create menu text with rich colors to be displayed by CLI.""" - CMD_NAME_LENGTH = 18 + CMD_NAME_LENGTH = 23 CMD_DESCRIPTION_LENGTH = 65 CMD_PROVIDERS_LENGTH = 23 SECTION_SPACING = 4 @@ -64,9 +64,7 @@ class MenuText: def _format_cmd_name(self, name: str) -> str: """Truncate command name length if it is too long.""" if len(name) > self.CMD_NAME_LENGTH: - new_name = name[ - : self.CMD_NAME_LENGTH - ] # Default to trimming to 18 characters + new_name = name[: self.CMD_NAME_LENGTH] if "_" in name: name_split = name.split("_") @@ -108,6 +106,22 @@ class MenuText: """Append raw text (without translation).""" self.menu_text += text + def add_section( + self, text: str, description: str = "", leading_new_line: bool = False + ): + """Append raw text (without translation).""" + spacing = (self.CMD_NAME_LENGTH - len(text) + self.SECTION_SPACING) * " " + left_spacing = self.SECTION_SPACING * " " + text = f"{left_spacing}{text}" + if description: + text = f"{text}{spacing}{description}\n" + + if leading_new_line: + self.menu_text += "\n" + text + + else: + self.menu_text += text + def add_custom(self, name: str): """Append custom text (after translation).""" self.menu_text += f"{i18n.t(self.menu_path + name)}" @@ -165,6 +179,9 @@ class MenuText: if description == self.menu_path + name: description = "" + if len(description) > self.CMD_DESCRIPTION_LENGTH: + description = description[: self.CMD_DESCRIPTION_LENGTH - 3] + "..." + menu = f"{name}{spacing}{description}" tag = "unvl" if disable else "menu" self.menu_text += f"[{tag}]> {menu}[/{tag}]\n" diff --git a/cli/openbb_cli/config/setup.py b/cli/openbb_cli/config/setup.py index 09bbe505270..c507e5645a7 100644 --- a/cli/openbb_cli/config/setup.py +++ b/cli/openbb_cli/config/setup.py @@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, List, Optional, TypeVar import i18n from openbb_cli.config.constants import ENV_FILE_SETTINGS, I18N_FILE, SETTINGS_DIRECTORY -from openbb_cli.session import Session if TYPE_CHECKING: from openbb_charting.core.openbb_figure import OpenBBFigure @@ -81,5 +80,3 @@ def bootstrap(): """Setup pre-launch configurations for the CLI.""" SETTINGS_DIRECTORY.mkdir(parents=True, exist_ok=True) Path(ENV_FILE_SETTINGS).touch(exist_ok=True) - - setup_i18n(lang=Session().settings.USE_LANGUAGE) diff --git a/cli/openbb_cli/controllers/base_controller.py b/cli/openbb_cli/controllers/base_controller.py index c5f083079ac..9db39807a09 100644 --- a/cli/openbb_cli/controllers/base_controller.py +++ b/cli/openbb_cli/controllers/base_controller.py @@ -52,7 +52,6 @@ class BaseController(metaclass=ABCMeta): CHOICES_COMMON = [ "cls", "home", - "about", "h", "?", "help", @@ -496,14 +495,7 @@ 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 in guest mode.", - default=False, - ) + if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-n") @@ -549,17 +541,16 @@ class BaseController(metaclass=ABCMeta): ) return - if session.is_local() and not ns_parser.local: + if session.is_local(): session.console.print( "[red]Recording session to the OpenBB Hub is not supported in guest mode.[/red]" ) session.console.print( - "\n[yellow]Sign to OpenBB Hub to register: http://openbb.co[/yellow]" + "\n[yellow]Visit the OpenBB Hub to register: http://my.openbb.co[/yellow]" ) session.console.print( - "\n[yellow]Otherwise set the flag '-l' to save the file locally.[/yellow]" + "\n[yellow]Your routine will be saved locally.[/yellow]\n" ) - return # Check if title has a valid format title = " ".join(ns_parser.name) if ns_parser.name else "" @@ -577,8 +568,8 @@ class BaseController(metaclass=ABCMeta): global SESSION_RECORDED_TAGS # noqa: PLW0603 global SESSION_RECORDED_PUBLIC # noqa: PLW0603 + RECORD_SESSION_LOCAL_ONLY = session.is_local() RECORD_SESSION = True - RECORD_SESSION_LOCAL_ONLY = ns_parser.local SESSION_RECORDED_NAME = title SESSION_RECORDED_DESCRIPTION = ( " ".join(ns_parser.description) @@ -871,6 +862,7 @@ class BaseController(metaclass=ABCMeta): type=check_file_type_saved(choices_export), dest="export", help=help_export, + nargs="+", ) # If excel is an option, add the sheet name @@ -1001,7 +993,6 @@ class BaseController(metaclass=ABCMeta): '<style bg="ansiblack" fg="ansiwhite">[e]</style> exit the program ' '<style bg="ansiblack" fg="ansiwhite">[cmd -h]</style> ' "see usage and available options " - f'<style bg="ansiblack" fg="ansiwhite">[about (cmd/menu)]</style> ' f"{self.path[-1].capitalize()} (cmd/menu) Documentation" ), style=Style.from_dict( diff --git a/cli/openbb_cli/controllers/base_platform_controller.py b/cli/openbb_cli/controllers/base_platform_controller.py index 6f236f27f95..c154baa56cd 100644 --- a/cli/openbb_cli/controllers/base_platform_controller.py +++ b/cli/openbb_cli/controllers/base_platform_controller.py @@ -15,6 +15,7 @@ from openbb_cli.config.menu_text import MenuText from openbb_cli.controllers.base_controller import BaseController from openbb_cli.controllers.utils import export_data, print_rich_table from openbb_cli.session import Session +from openbb_core.app.model.obbject import OBBject session = Session() @@ -74,17 +75,20 @@ class PlatformController(BaseController): for _, trl in self.translators.items(): for action in trl._parser._actions: # pylint: disable=protected-access if action.dest == "data": - action.choices = range(len(session.obbject_registry.obbjects)) - action.type = int + action.choices = [ + "OBB" + str(i) + for i in range(len(session.obbject_registry.obbjects)) + ] + action.type = str action.nargs = None def _intersect_data_processing_commands(self, ns_parser): """Intersect data processing commands and change the obbject id into an actual obbject.""" - if hasattr(ns_parser, "data") and ns_parser.data in range( - len(session.obbject_registry.obbjects) - ): - obbject = session.obbject_registry.get(ns_parser.data) - setattr(ns_parser, "data", obbject.results) + if hasattr(ns_parser, "data"): + ns_parser.data = int(ns_parser.data.replace("OBB", "")) + if ns_parser.data in range(len(session.obbject_registry.obbjects)): + obbject = session.obbject_registry.get(ns_parser.data) + setattr(ns_parser, "data", obbject.results) return ns_parser @@ -154,13 +158,12 @@ class PlatformController(BaseController): title = f"{self.PATH}{translator.func.__name__}" if obbject: - max_obbjects_exceeded = ( - len(session.obbject_registry.obbjects) - >= session.settings.N_TO_KEEP_OBBJECT_REGISTRY - ) - if max_obbjects_exceeded: + if session.max_obbjects_exceeded(): session.obbject_registry.remove() + # use the obbject to store the command so we can display it later on results + obbject.extra["command"] = f"{title} {' '.join(other_args)}" + session.obbject_registry.register(obbject) # we need to force to re-link so that the new obbject # is immediately available for data processing commands @@ -168,7 +171,9 @@ class PlatformController(BaseController): # also update the completer self.update_completer(self.choices_default) - if session.settings.SHOW_MSG_OBBJECT_REGISTRY: + if session.settings.SHOW_MSG_OBBJECT_REGISTRY and isinstance( + obbject, OBBject + ): session.console.print("Added OBBject to registry.") if hasattr(ns_parser, "chart") and ns_parser.chart: @@ -197,7 +202,7 @@ class PlatformController(BaseController): if hasattr(ns_parser, "export") and ns_parser.export: sheet_name = getattr(ns_parser, "sheet_name", None) export_data( - export_type=ns_parser.export, + export_type=",".join(ns_parser.export), dir_path=os.path.dirname(os.path.abspath(__file__)), func_name=translator.func.__name__, df=df, @@ -205,7 +210,7 @@ class PlatformController(BaseController): figure=fig, ) - if max_obbjects_exceeded: + if session.max_obbjects_exceeded(): session.console.print( "[yellow]\nMaximum number of OBBjects reached. The oldest entry was removed.[yellow]" ) @@ -271,13 +276,26 @@ class PlatformController(BaseController): def _get_menu_description(self, menu: str) -> str: """Get menu description.""" + + def _get_sub_menu_commands(): + """Get sub menu commands.""" + sub_path = f"{self.PATH[1:].replace('/','_')}{menu}" + commands = [] + for trl in self.translators: + if sub_path in trl: + commands.append(trl.replace(f"{sub_path}_", "")) + return commands + menu_description = ( obb.reference["routers"] # type: ignore .get(f"{self.PATH}{menu}", {}) .get("description", "") ) or "" + if menu_description: + return menu_description.split(".")[0].lower() - return menu_description.split(".")[0].lower() + # If no description is found, return the sub menu commands + return ", ".join(_get_sub_menu_commands()) def print_help(self): """Print help.""" @@ -288,8 +306,10 @@ class PlatformController(BaseController): description = self._get_menu_description(menu) mt.add_menu(name=menu, description=description) + if self.CHOICES_COMMANDS: + mt.add_raw("\n") + if self.CHOICES_COMMANDS: - mt.add_raw("\n") for command in self.CHOICES_COMMANDS: command_description = self._get_command_description(command) mt.add_cmd( @@ -297,7 +317,14 @@ class PlatformController(BaseController): description=command_description, ) - session.console.print(text=mt.menu_text, menu=self._name) + if session.obbject_registry.obbjects: + mt.add_section("Cached Results:\n", leading_new_line=True) + for key, value in list(session.obbject_registry.all.items())[ + : session.settings.N_TO_DISPLAY_OBBJECT_REGISTRY + ]: + mt.add_raw(f"\tOBB{key}: {value['command']}\n") + + session.console.print(text=mt.menu_text, menu=self.PATH) settings = session.settings dev_mode = settings.DEBUG_MODE or settings.TEST_MODE diff --git a/cli/openbb_cli/controllers/cli_controller.py b/cli/openbb_cli/controllers/cli_controller.py index dfc5eb878dd..e44237ba82d 100644 --- a/cli/openbb_cli/controllers/cli_controller.py +++ b/cli/openbb_cli/controllers/cli_controller.py @@ -228,8 +228,8 @@ class CLIController(BaseController): mt.add_cmd("stop") mt.add_cmd("exe") mt.add_raw("\n") - mt.add_info("Platform CLI") - mt.add_raw(" data\n") + mt.add_info("Retrieve data from different asset classes and providers") + for router, value in PLATFORM_ROUTERS.items(): if router in NON_DATA_ROUTERS or router in DATA_PROCESSING_ROUTERS: continue @@ -247,7 +247,8 @@ class CLIController(BaseController): mt.add_cmd(router) if any(router in PLATFORM_ROUTERS for router in DATA_PROCESSING_ROUTERS): - mt.add_raw("\n data processing\n") + mt.add_info("\nAnalyze and process previously obtained data") + for router, value in PLATFORM_ROUTERS.items(): if router not in DATA_PROCESSING_ROUTERS: continue @@ -264,9 +265,10 @@ class CLIController(BaseController): else: mt.add_cmd(router) - mt.add_raw("\n configuration\n") + mt.add_info("\nConfigure the platform and manage your account") + for router, value in PLATFORM_ROUTERS.items(): - if router not in NON_DATA_ROUTERS or router == "reference": + if router not in NON_DATA_ROUTERS or router in ["reference", "coverage"]: continue if value == "menu": menu_description = ( @@ -281,8 +283,14 @@ class CLIController(BaseController): else: mt.add_cmd(router) - mt.add_raw("\n cached results (OBBjects)\n") + mt.add_info("\nAccess and manage your cached results") mt.add_cmd("results") + if session.obbject_registry.obbjects: + mt.add_section("Cached Results:\n", leading_new_line=True) + for key, value in list(session.obbject_registry.all.items())[ # type: ignore + : session.settings.N_TO_DISPLAY_OBBJECT_REGISTRY + ]: + mt.add_raw(f"\tOBB{key}: {value['command']}\n") # type: ignore session.console.print(text=mt.menu_text, menu="Home") self.update_runtime_choices() @@ -300,7 +308,7 @@ class CLIController(BaseController): return parse_and_split_input(an_input=an_input, custom_filters=custom_filters) def call_settings(self, _): - """Process feature flags command.""" + """Process settings command.""" from openbb_cli.controllers.settings_controller import ( SettingsController, ) @@ -315,8 +323,7 @@ class CLIController(BaseController): if not other_args: session.console.print( "[info]Provide a path to the routine you wish to execute. For an example, please use " - "`exe --example` and for documentation and to learn how create your own script " - "type `about exe`.\n[/info]" + "`exe --example`.\n[/info]" ) return parser = argparse.ArgumentParser( @@ -324,8 +331,7 @@ class CLIController(BaseController): formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="exe", description="Execute automated routine script. For an example, please use " - "`exe --example` and for documentation and to learn how create your own script " - "type `about exe`.", + "`exe --example`.", ) parser.add_argument( "--file", @@ -368,8 +374,8 @@ class CLIController(BaseController): if ns_parser: if ns_parser.example: routine_path = ASSETS_DIRECTORY / "routines" / "routine_example.openbb" - session.console.print( - "[info]Executing an example, please type `about exe` " + session.console.print( # TODO: Point to docs when ready + "[info]Executing an example, please visit our docs " "to learn how to create your own script.[/info]\n" ) time.sleep(3) @@ -525,9 +531,7 @@ def run_cli(jobs_cmds: Optional[List[str]] = None, test_mode=False): if first_time_user(): with contextlib.suppress(EOFError): - webbrowser.open( - "https://docs.openbb.co/terminal/usage/overview/structure-and-navigation" - ) + webbrowser.open("https://docs.openbb.co/cli") t_controller.print_help() @@ -565,7 +569,6 @@ def run_cli(jobs_cmds: Optional[List[str]] = None, test_mode=False): '<style bg="ansiblack" fg="ansiwhite">[e]</style> exit the program ' '<style bg="ansiblack" fg="ansiwhite">[cmd -h]</style> ' "see usage and available options " - '<style bg="ansiblack" fg="ansiwhite">[about (cmd/menu)]</style> ' ), style=Style.from_dict( { diff --git a/cli/openbb_cli/controllers/settings_controller.py b/cli/openbb_cli/controllers/settings_controller.py index d293078a0ef..9f87d4cc677 100644 --- a/cli/openbb_cli/controllers/settings_controller.py +++ b/cli/openbb_cli/controllers/settings_controller.py @@ -1,4 +1,4 @@ -"""Feature Flags Controller Modul |