diff --git a/openbb_terminal/account/account_controller.py b/openbb_terminal/account/account_controller.py index 3e28aec91f14..eb27adf79c5a 100644 --- a/openbb_terminal/account/account_controller.py +++ b/openbb_terminal/account/account_controller.py @@ -53,7 +53,7 @@ def update_runtime_choices(self): """Update runtime choices""" self.ROUTINE_FILES = self.get_routines() if session and obbff.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} + choices: dict = {c: {} for c in self.controller_choices} # type: ignore choices["sync"] = {"--on": {}, "--off": {}} choices["upload"]["--file"] = {c: {} for c in self.ROUTINE_FILES} choices["upload"]["-f"] = choices["upload"]["--file"] diff --git a/openbb_terminal/core/log/collection/log_sender.py b/openbb_terminal/core/log/collection/log_sender.py index 11fef8556be7..4634ace84da8 100644 --- a/openbb_terminal/core/log/collection/log_sender.py +++ b/openbb_terminal/core/log/collection/log_sender.py @@ -68,7 +68,7 @@ def run(self): identifier = app_settings.identifier while True: - item: QueueItem = queue.get() + item: QueueItem = queue.get() # type: ignore file = item.path last = item.last diff --git a/openbb_terminal/economy/economy_controller.py b/openbb_terminal/economy/economy_controller.py index d1bf1e4f77ae..e36f6640c58d 100644 --- a/openbb_terminal/economy/economy_controller.py +++ b/openbb_terminal/economy/economy_controller.py @@ -1666,7 +1666,7 @@ def call_qa(self, _): QaController, ) - data: Dict = {} + data: Dict = {} # type: ignore for source, _ in self.DATASETS.items(): if not self.DATASETS[source].empty: if len(self.DATASETS[source].columns) == 1: diff --git a/openbb_terminal/forecast/forecast_controller.py b/openbb_terminal/forecast/forecast_controller.py index 09225edaf85e..9ebc1071bd46 100644 --- a/openbb_terminal/forecast/forecast_controller.py +++ b/openbb_terminal/forecast/forecast_controller.py @@ -232,7 +232,7 @@ def update_runtime_choices(self): # Load in any newly exported files self.DATA_FILES = forecast_model.get_default_files() if session and obbff.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default + choices: dict = self.choices_default # type: ignore self.choices = choices self.completer = NestedCompleter.from_nested_dict(choices) diff --git a/openbb_terminal/reports/reports_controller.py b/openbb_terminal/reports/reports_controller.py index fd0f5355a02e..dbb6d83dc435 100644 --- a/openbb_terminal/reports/reports_controller.py +++ b/openbb_terminal/reports/reports_controller.py @@ -59,7 +59,7 @@ def update_choices(self): ) if session and obbff.USE_PROMPT_TOOLKIT: - self.choices: dict = {c: {} for c in self.controller_choices} + self.choices: dict = {c: {} for c in self.controller_choices} # type: ignore self.choices["run"] = { "--file": {c: None for c in reports_model.USER_REPORTS}, "-f": "--file", diff --git a/openbb_terminal/sdk_core/sdk_helpers.py b/openbb_terminal/sdk_core/sdk_helpers.py index 7fef5ee4b984..0d86bbc2fd3e 100644 --- a/openbb_terminal/sdk_core/sdk_helpers.py +++ b/openbb_terminal/sdk_core/sdk_helpers.py @@ -1,5 +1,6 @@ """OpenBB Terminal SDK Helpers.""" import json +from inspect import signature from logging import Logger, getLogger from typing import Any, Callable, Dict, Optional @@ -15,6 +16,7 @@ OPTIMIZATION_TOOLKIT_ENABLED, OPTIMIZATION_TOOLKIT_WARNING, ) +from openbb_terminal.session.sdk_session import login if not FORECASTING_TOOLKIT_ENABLED and not load_env_vars( "OPENBB_DISABLE_FORECASTING_WARNING", strtobool, False @@ -51,23 +53,49 @@ def class_repr(cls_dict: Dict[str, Any]) -> list: ] -def helptext(func: Callable) -> Callable: - """Wrapper to preserve the help text of the function.""" +class Operation: + def __init__(self, name: str, trail: str, func: Callable) -> None: + self._trail = trail + self._method = func + self._name = name - def decorator(f: Callable) -> Callable: for attr in [ "__doc__", "__name__", "__annotations__", - "__module__", "__defaults__", "__kwdefaults__", - "__dict__", + "__module__", ]: - setattr(f, attr, getattr(func, attr)) - return f + setattr(self.__class__, attr, getattr(func, attr)) + setattr(self, attr, getattr(func, attr)) + + self.__signature__ = signature(func) + self.__class__.__signature__ = signature(func) + + def __call__(self, *args: Any, **kwargs: Any) -> Any: + method = self._method + + # We make a copy of the kwargs to avoid modifying the original + log_kwargs = kwargs.copy() + log_kwargs["chart"] = "chart" in self._name + + operation_logger = OperationLogger( + trail=self._trail, method_chosen=method, args=args, kwargs=log_kwargs + ) + operation_logger.log_before_call() + method_result = method(*args, **kwargs) + operation_logger.log_after_call(method_result=method_result) + + return method_result + + def about(self): + # pylint: disable=C0415 + import webbrowser - return decorator + trail = "/".join(self._trail.split(".")) + url = f"https://docs.openbb.co/sdk/reference/{trail}" + webbrowser.open(url) class Category: @@ -93,33 +121,27 @@ def __getattribute__(self, name: str): """We override the __getattribute__ method and wrap all callable attributes with a wrapper that logs the call and the result. """ - if name.startswith("_"): - return super().__getattribute__(name) - attr = super().__getattribute__(name) - trail = f"{self.__class__._location_path}.{name}" - - if callable(attr): + if isinstance(attr, Operation) or name.startswith("__"): + return attr - @helptext(attr) - def wrapper(*args, **kwargs): - method = attr + trail = f"{self.__class__._location_path}.{name}" - # We make a copy of the kwargs to avoid modifying the original - log_kwargs = kwargs.copy() - log_kwargs["chart"] = "chart" in name + if callable(attr) and not isinstance(attr, Operation): + # We set the attribute to the wrapped function so that we don't + # have to wrap it when called again. + setattr(self, name, Operation(name=name, trail=trail, func=attr)) + return getattr(self, name) - operation_logger = OperationLogger( - trail=trail, method_chosen=method, args=args, kwargs=log_kwargs - ) - operation_logger.log_before_call() - method_result = method(*args, **kwargs) - operation_logger.log_after_call(method_result=method_result) + return attr - return method_result + def about(self): + # pylint: disable=C0415 + import webbrowser - return wrapper - return attr + trail = "/".join(self._location_path.split(".")) + url = f"https://docs.openbb.co/sdk/reference/{trail}" + webbrowser.open(url) class OperationLogger: @@ -257,6 +279,9 @@ def log_after_call( method_result=method_result, method_chosen=self.__method_chosen, ) + self.__log_if_login( + method_chosen=self.__method_chosen, + ) self.__log_end( logger=logger, method_chosen=self.__method_chosen, @@ -280,6 +305,17 @@ def __log_exception_if_any( extra={"func_name_override": method_chosen.__name__}, ) + @staticmethod + def __log_if_login( + method_chosen: Callable, + ): + if method_chosen.__name__ == login.__name__: + from openbb_terminal.core.log.generation.user_logger import ( # pylint: disable=import-outside-toplevel + log_user, + ) + + log_user(with_rollover=False) + @staticmethod def __log_end(logger: Logger, method_chosen: Callable): logger.info( diff --git a/openbb_terminal/stocks/options/hedge/hedge_controller.py b/openbb_terminal/stocks/options/hedge/hedge_controller.py index bd2faf4b0d9f..0ccd93d9b1e0 100644 --- a/openbb_terminal/stocks/options/hedge/hedge_controller.py +++ b/openbb_terminal/stocks/options/hedge/hedge_controller.py @@ -502,7 +502,7 @@ def call_sop(self, other_args): for _, value in self.options.items(): if value: - option_side: str = "Long" if value["sign"] == 1 else "Short" + option_side: str = "Long" if value["sign"] == 1 else "Short" # type: ignore positions = positions.append( [ [ diff --git a/openbb_terminal/terminal_controller.py b/openbb_terminal/terminal_controller.py index 0b11ee2dbdd5..af8e86cd10ef 100644 --- a/openbb_terminal/terminal_controller.py +++ b/openbb_terminal/terminal_controller.py @@ -151,7 +151,7 @@ def update_runtime_choices(self): self.ROUTINE_CHOICES["--h"] = None if session and obbff.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} + 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 diff --git a/website/content/sdk/quickstart/pypi.md b/website/content/sdk/quickstart/pypi.md index d07592c2d8ff..575b6b471da5 100644 --- a/website/content/sdk/quickstart/pypi.md +++ b/website/content/sdk/quickstart/pypi.md @@ -3,7 +3,7 @@ | OpenBB is committed to build the future of investment research by focusing on an open source infrastructure accessible to everyone, everywhere. | |:--:| -| Screenshot 2023-02-14 at 5 12 08 PM | +| ![OpenBBLogo](https://user-images.githubusercontent.com/25267873/218899768-1f0964b8-326c-4f35-af6f-ea0946ac970b.png) | | Check our website at [openbb.co](www.openbb.co) | @@ -41,10 +41,10 @@ ___ Access raw financial data from any data source that you are interested. The open source nature of this SDK makes it so that this is an ever-growing project and that everyone can contribute to. -![image-20221025-092439 2](https://user-images.githubusercontent.com/25267873/218906336-cebd1fc8-7e7a-45bc-a5fc-641eb19c3e8c.png) +![Stocks Load](https://user-images.githubusercontent.com/25267873/218906336-cebd1fc8-7e7a-45bc-a5fc-641eb19c3e8c.png) #### GENERATE INSIGHTS 10X FASTER For most of the functionalities, adding `_chart` to the function will allow you to visualize the output directly from a Jupyter Notebook. Not only speeding up the time it takes to create a plot for the data you are interested in, but making building custom reports much faster. -Group 550 +![Economy Treasury Chart](https://user-images.githubusercontent.com/25267873/218906112-b2272d43-11fc-4ec1-9a8f-b2d8e2ed7dc1.png)