Skip to content

Commit

Permalink
Update SDK Docs Generation (#4262)
Browse files Browse the repository at this point in the history
* update generate_sdk_markdown.py to use sdk trailmap class

* Update trailmap.py

* Update trailmap.py
  • Loading branch information
tehcoderer authored Feb 15, 2023
1 parent 69b4d11 commit 3747424
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 267 deletions.
11 changes: 5 additions & 6 deletions generate_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,13 +528,12 @@ def generate_sdk(sort: bool = False) -> None:


if __name__ == "__main__":
sys_args = sys.argv
sort = False
if len(sys_args) > 1:
if sys_args[1] == "sort":
sort = True
sort_csv = False
if len(sys.argv) > 1:
if sys.argv[1] == "sort":
sort_csv = True
console.print("\n\n[bright_magenta]Sorting CSV...[/]\n")
else:
console.print("[red]Invalid argument.\n Accepted arguments: sort[/]")

generate_sdk(sort)
generate_sdk(sort_csv)
44 changes: 44 additions & 0 deletions openbb_terminal/forecast/forecast_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,23 @@ def show_df(
export: str = "",
sheet_name: str = None,
):
"""Show a dataframe in a table
Parameters
----------
data: pd.DataFrame
The dataframe to show
limit: int
The number of rows to show
limit_col: int
The number of columns to show
name: str
The name of the dataframe
export: str
Format to export data
sheet_name: str
Optionally specify the name of the sheet the data is exported to.
"""
console.print(
f"[green]{name} dataset has shape (row, column): {data.shape}\n[/green]"
)
Expand Down Expand Up @@ -279,6 +296,19 @@ def show_df(
def describe_df(
data: pd.DataFrame, name: str = "", export: str = "", sheet_name: str = None
):
"""Show descriptive statistics for a dataframe
Parameters
----------
data: pd.DataFrame
The dataframe to show
name: str
The name of the dataframe
export: str
Format to export data
sheet_name: str
Optionally specify the name of the sheet the data is exported to.
"""
new_df = forecast_model.describe_df(data)
print_rich_table(
new_df,
Expand All @@ -299,6 +329,20 @@ def describe_df(
def export_df(
data: pd.DataFrame, export: str, name: str = "", sheet_name: str = None
) -> None:
"""Export a dataframe to a file
Parameters
----------
data: pd.DataFrame
The dataframe to export
export: str
The format to export the dataframe to
name: str
The name of the dataframe
sheet_name: str
Optionally specify the name of the sheet the data is exported to.
"""

export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
Expand Down
3 changes: 3 additions & 0 deletions openbb_terminal/sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,11 @@ def forecast(self):
`delete`: Delete a column from a dataframe\n
`delta`: Calculate the %change of a variable based on a specific column\n
`desc`: Returns statistics for a given df\n
`desc_chart`: Show descriptive statistics for a dataframe\n
`ema`: A moving average provides an indication of the trend of the price movement\n
`expo`: Performs Probabilistic Exponential Smoothing forecasting\n
`expo_chart`: Display Probabilistic Exponential Smoothing forecast\n
`export`: Export a dataframe to a file\n
`linregr`: Perform Linear Regression Forecasting\n
`linregr_chart`: Display Linear Regression Forecasting\n
`load`: Load custom file into dataframe.\n
Expand All @@ -244,6 +246,7 @@ def forecast(self):
`season_chart`: Plot seasonality from a dataset\n
`seasonalnaive`: Performs Seasonal Naive forecasting\n
`seasonalnaive_chart`: Display SeasonalNaive Model\n
`show`: Show a dataframe in a table\n
`signal`: A price signal based on short/long term price.\n
`sto`: Stochastic Oscillator %K and %D : A stochastic oscillator is a momentum indicator comparing a particular closing\n
`tcn`: Perform TCN forecasting\n
Expand Down
4 changes: 3 additions & 1 deletion openbb_terminal/sdk_core/models/forecast_sdk_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ class ForecastRoot(Category):
`delete`: Delete a column from a dataframe\n
`delta`: Calculate the %change of a variable based on a specific column\n
`desc`: Returns statistics for a given df\n
`desc_chart`: Show descriptive statistics for a dataframe\n
`ema`: A moving average provides an indication of the trend of the price movement\n
`expo`: Performs Probabilistic Exponential Smoothing forecasting\n
`expo_chart`: Display Probabilistic Exponential Smoothing forecast\n
`export`: Export a dataframe to a file\n
`linregr`: Perform Linear Regression Forecasting\n
`linregr_chart`: Display Linear Regression Forecasting\n
`load`: Load custom file into dataframe.\n
Expand All @@ -55,6 +57,7 @@ class ForecastRoot(Category):
`season_chart`: Plot seasonality from a dataset\n
`seasonalnaive`: Performs Seasonal Naive forecasting\n
`seasonalnaive_chart`: Display SeasonalNaive Model\n
`show`: Show a dataframe in a table\n
`signal`: A price signal based on short/long term price.\n
`sto`: Stochastic Oscillator %K and %D : A stochastic oscillator is a momentum indicator comparing a particular closing\n
`tcn`: Perform TCN forecasting\n
Expand Down Expand Up @@ -136,7 +139,6 @@ def __init__(self):
lib.forecast_seasonalnaive_view.display_seasonalnaive_forecast
)
self.show = lib.forecast_view.show_df
self.show_chart = lib.forecast_view.show_df
self.signal = lib.forecast_model.add_signal
self.sto = lib.forecast_model.add_sto
self.tcn = lib.forecast_tcn_model.get_tcn_data
Expand Down
2 changes: 1 addition & 1 deletion openbb_terminal/sdk_core/trail_map_forecasting.csv
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ forecast.rsi,forecast_model.add_rsi,
forecast.rwd,forecast_rwd_model.get_rwd_data,forecast_rwd_view.display_rwd_forecast
forecast.season,,forecast_view.display_seasonality
forecast.seasonalnaive,forecast_seasonalnaive_model.get_seasonalnaive_data,forecast_seasonalnaive_view.display_seasonalnaive_forecast
forecast.show,forecast_view.show_df,forecast_view.show_df
forecast.show,forecast_view.show_df,
forecast.signal,forecast_model.add_signal,
forecast.sto,forecast_model.add_sto,
forecast.tcn,forecast_tcn_model.get_tcn_data,forecast_tcn_view.display_tcn_forecast
Expand Down
137 changes: 87 additions & 50 deletions openbb_terminal/sdk_core/trailmap.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
import importlib
import inspect
from pathlib import Path
from types import FunctionType
from typing import Dict, List, Optional
from typing import Any, Callable, Dict, ForwardRef, List, Optional

import pandas as pd

import openbb_terminal.sdk_core.sdk_init as lib
from openbb_terminal.core.config.paths import PACKAGE_DIRECTORY
from openbb_terminal.rich_config import console
from openbb_terminal.sdk_core.sdk_helpers import clean_attr_desc
from openbb_terminal.sdk_core.sdk_init import (
FORECASTING_TOOLKIT_ENABLED,
OPTIMIZATION_TOOLKIT_ENABLED,
)


def get_signature_parameters(
function: Callable[..., Any], globalns: Dict[str, Any]
) -> Dict[str, inspect.Parameter]:
signature = inspect.signature(function)
params = {}
cache: dict[str, Any] = {}
for name, parameter in signature.parameters.items():
annotation = parameter.annotation
if annotation is parameter.empty:
params[name] = parameter
continue
if annotation is None:
params[name] = parameter.replace(annotation=type(None))
continue

if isinstance(annotation, ForwardRef):
annotation = annotation.__forward_arg__

if isinstance(annotation, str):
annotation = eval(annotation, globalns, cache) # pylint: disable=W0123

params[name] = parameter.replace(annotation=annotation)

return params


class FuncAttr:
def __init__(self, func: Optional[str] = None):
def __init__(self) -> None:
self.short_doc: Optional[str] = None
self.long_doc: Optional[str] = None
self.func_def: Optional[str] = None
Expand All @@ -25,15 +47,11 @@ def __init__(self, func: Optional[str] = None):
self.full_path: Optional[str] = None
self.func_unwrapped: Optional[FunctionType] = None
self.func_wrapped: Optional[FunctionType] = None
if func:
self.get_func_attrs(func)
self.params: Dict[str, inspect.Parameter] = {}

def get_func_attrs(self, func: str) -> None:
attr = getattr(
importlib.import_module("openbb_terminal.sdk_core.sdk_init"),
func.split(".")[0],
)
func_attr = getattr(attr, func.split(".")[1])
module_path, function_name = func.rsplit(".", 1)
func_attr = getattr(getattr(lib, module_path), function_name)
self.func_wrapped = func_attr

add_lineon = 0
Expand All @@ -45,50 +63,63 @@ def get_func_attrs(self, func: str) -> None:
self.func_unwrapped = func_attr
self.lineon = inspect.getsourcelines(func_attr)[1] + add_lineon

self.func_def = self.get_definition()
self.long_doc = func_attr.__doc__
self.short_doc = clean_attr_desc(func_attr)

for k, p in get_signature_parameters(func_attr, func_attr.__globals__).items():
self.params[k] = p

self.path = inspect.getfile(func_attr)
full_path = (
inspect.getfile(func_attr).replace("\\", "/").split("openbb_terminal/")[1]
)
self.full_path = f"openbb_terminal/{full_path}"

def get_definition(self) -> str:
def get_definition(
self, location_path: list, class_attr: str, view: bool = False
) -> str:
"""Creates the function definition to be used in SDK docs."""
funcspec = inspect.getfullargspec(self.func_unwrapped)

funcspec = self.params
definition = ""
added_comma = False
for arg in funcspec.args:
for arg, param in funcspec.items():
annotation = (
funcspec.annotations[arg] if arg in funcspec.annotations else "Any"
(
str(param.annotation)
.replace("<class '", "")
.replace("'>", "")
.replace("typing.", "")
.replace("pandas.core.frame.", "pd.")
.replace("pandas.core.series.", "pd.")
.replace("openbb_terminal.portfolio.", "")
)
if param.annotation != inspect.Parameter.empty
else "Any"
)
annotation = (
str(annotation)
.replace("<class '", "")
.replace("'>", "")
.replace("typing.", "")
.replace("pandas.core.frame.", "pd.")
.replace("pandas.core.series.", "pd.")
.replace("openbb_terminal.portfolio.", "")
)
definition += f"{arg}: {annotation}, "
added_comma = True

if added_comma:
definition = definition[:-2]

return_def = (
funcspec.annotations["return"].__name__
if "return" in funcspec.annotations
and hasattr(funcspec.annotations["return"], "__name__")
and funcspec.annotations["return"] is not None
else "None"
)
definition = f"def {self.func_unwrapped.__name__}({definition }) -> {return_def}" # type: ignore
return definition

default = ""
if param.default is not param.empty:
arg_default = (
param.default
if param.default is not inspect.Parameter.empty
else "None"
)
default = (
f" = {arg_default}"
if not isinstance(arg_default, str)
else f' = "{arg_default}"'
)
definition += f"{arg}: {annotation}{default}, "

definition = definition.rstrip(", ")

if location_path[0] == "root":
location_path[0] = ""

trail = ".".join([t for t in location_path if t != ""])
sdk_name = class_attr if not view else f"{class_attr}_chart"
sdk_path = f"{f'openbb.{trail}' if trail else 'openbb'}.{sdk_name}"

return f"{sdk_path}({definition })"


# pylint: disable=R0903
Expand All @@ -106,8 +137,14 @@ def __init__(self, trailmap: str, model: str, view: Optional[str] = None):
self.model_func: Optional[str] = f"lib.{model}" if model else None
self.view_func: Optional[str] = f"lib.{view}" if view else None
self.func_attrs: Dict[str, FuncAttr] = {}
self.func_attrs["model"] = FuncAttr(self.model)
self.func_attrs["view"] = FuncAttr(self.view) # type: ignore
self.func_attrs["model"] = FuncAttr()
self.func_attrs["view"] = FuncAttr()
for k, cls in self.func_attrs.items():
if getattr(self, f"{k}_func"):
cls.get_func_attrs(getattr(self, f"{k}"))
cls.func_def = cls.get_definition(
tmap, self.class_attr, view=k == "view"
)


def get_trailmaps(sort: bool = False) -> List[Trailmap]:
Expand All @@ -119,7 +156,7 @@ def get_trailmaps(sort: bool = False) -> List[Trailmap]:
PACKAGE_DIRECTORY / "sdk_core" / "trail_map_optimization.csv"
)

def load_csv(path: Path = None) -> pd.DataFrame:
def load_csv(path: Optional[Path] = None) -> Dict[str, Dict[str, str]]:
path = path or MAP_PATH
df = pd.read_csv(path, keep_default_na=False)
df = df.set_index("trail")
Expand All @@ -139,13 +176,13 @@ def print_error(error: str) -> None:

def load():
map_dict = load_csv(path=MAP_PATH)
if FORECASTING_TOOLKIT_ENABLED:
if lib.FORECASTING_TOOLKIT_ENABLED:
map_forecasting_dict = load_csv(path=MAP_FORECASTING_PATH)
map_dict.update(map_forecasting_dict)
else:
print_error("Forecasting")

if OPTIMIZATION_TOOLKIT_ENABLED:
if lib.OPTIMIZATION_TOOLKIT_ENABLED:
map_optimization_dict = load_csv(path=MAP_OPTIMIZATION_PATH)
map_dict.update(map_optimization_dict)
else:
Expand Down
Loading

0 comments on commit 3747424

Please sign in to comment.