mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-29 13:32:08 +00:00
* Update portfolio_view.py * mypy 1.0 test * Update helpers.py * black * ruff * Update thought_of_the_day.py * isort * Update portfolio_view.py * Update business_insider_view.py * funds,bt,ca,fa view updates * economy,etf,futures view updates * econometrics regression * mypy * reports notebooks, PlotlyTA class adjustments, crypto finbrain view * crypto views updates * crypto views done?, ba, econometrics, funds, reports * update docstrings * Update sec_view.py * portfolio reports * Update equity.ipynb * ruff * mypy + equity reports * Update yahoo_finance_view.py * Update economy_controller.py * clean up returns * mypy * finished dashboards/reports templates * update some tests * Update common.py * Update account_controller.py * Update banner.txt * update tests * tests * Update helpers_denomination.py * tests * tests * finished tests, refactor add_histplot/add_corr_plot * ruff * Update backend.py * mypy * Update backend.py * pywry linux libs for tests * pywry version bump * OpenBBFIgure import changes * Update business_insider_view.py * Update backend.py * tests * bump pywry version * forecast plot_explainability converted to plotly * Update helpers.py * tests * Update helpers.py * tests * Update helpers.py * Update helpers.py * default plotly annotation `showarrow` to false * Update backend.py * fixes * Update poetry.lock * docker * Update openbb.dockerfile * up * Update style.css * bump pywry version * Update unit-test.yml * Update unit-test.yml * figure to image/pdf export * requirements txt files, pywry version bump * mypy, pylint * cleanup * Update covid_view.py * Update helper_funcs.py * Update backend.py * updates * Update backend.py * Update backend.py * bump pywry version * Update poetry.lock * Update trailmap.py * update reports * Update custom_indicators_plugin.py * Update momentum_plugin.py * equity report * reqs files * Update fred_view.py * ruff * Update po_view.py * bump pywry version * reqs txt files * fix installer icons for mac * test updated torch/darts/pyinstaller on mac * Update dashboards_controller.py * Update terminal.py * Update terminal.spec * Update terminal.spec * test * Update poetry.lock * Report adjustments, improved dashboard subprocesses termination * pylint * Update plotly_helper.py * Update plotly_helper.py * pylint * revert darts * test * test * fix * improve shap scatter plot * Update helpers.py * Update helpers.py * cleanup * Update runa_view.py * more cleanupp * fixes * update tests * Update intrinio_view.py * Update intrinio_view.py * Update intrinio_view.py * reqs files, poetry lock * Update backend.py * test for new icon * Update fmp_view.py * updates * Update plotly_helper.py * Update businessinsider_view.py * ruff * pylint * fix previous merge * Update plotly_helper.py * update reqs files * Update directories.py * fix alt covid * fix display_candle data is None load being unreachable, get covid views ready for sdk server * Update covid_view.py * refactor cleanup * plotly_ta adjustments * terminal wide ignore print_table if export * Update plotly_helper.py * forgot to save a few * added edge case fallback if figures are in queue before backend fail * Update ta_class.py * Rename env variable name required to return plotly json * Resolve pydocstyle warnings * Resolve linting errors * revert forecasting/pyinstaller versions * Update terminal.spec * finished incoming fixedincome menu, improve get_ecb_yield_curve speed with multiprocessing * update deps * Update backend.py * fix fred model/view from switch to DataFrame, improve ecb_model * Update config_terminal.py * Update backend.py * black * clean up * Update fred_view.py * Update ta_class.py * Update ta_class.py * Update plotly_helper.py * sync fixedincome * Update ecb_model.py * convert excel to csv for faster processing * cleanup * remove excel files * Update terminal_controller.py * update tests * adjustments/missed figs in export_data * Update cryptocurrency_helpers.py * fixes * account for volume column with no volume when plotting indicators , images autogen POC * Update plotly_helper.py * Update etf_controller.py * Update etf_controller.py * margin arg for export_data func figures, some plot adjustments * logic for numpy array for plotly helper `get_dateindex` function * cleanup * update * req file updates * views * Update backend.py * update tests * Update test_load_none[btc-usd].yaml * convert merged cpi chart * linting * updates * clean * tests * Update openbb.dockerfile * remove test docker in root * Update CONTRIBUTING.md --------- Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com> Co-authored-by: Chavithra <chavithra@gmail.com>
296 lines
9.8 KiB
Python
296 lines
9.8 KiB
Python
import inspect
|
|
import json
|
|
import os
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import Any, Dict, Literal, Optional, Union
|
|
|
|
from docstring_parser import parse
|
|
|
|
from openbb_terminal.core.sdk.trailmap import Trailmap, get_trailmaps
|
|
from website.controller_doc_classes import sub_names_full as subnames
|
|
|
|
website_path = Path(__file__).parent.absolute()
|
|
|
|
|
|
def get_function_meta(trailmap: Trailmap, trail_type: Literal["model", "view"]):
|
|
"""Gets the function meta data."""
|
|
func_attr = trailmap.func_attrs[trail_type]
|
|
if not func_attr.func_unwrapped:
|
|
return None
|
|
doc_parsed = parse(func_attr.long_doc)
|
|
line = func_attr.lineon
|
|
path = func_attr.full_path
|
|
func_def = func_attr.func_def
|
|
source_code_url = (
|
|
f"https://github.com/OpenBB-finance/OpenBBTerminal/tree/main/{path}#L{line}"
|
|
)
|
|
function_name = trailmap.view if trail_type == "view" else trailmap.model
|
|
params = []
|
|
for param in doc_parsed.params:
|
|
arg_default = (
|
|
func_attr.params[param.arg_name].default
|
|
if param.arg_name in func_attr.params
|
|
else None
|
|
)
|
|
params.append(
|
|
{
|
|
"name": param.arg_name,
|
|
"doc": param.description,
|
|
"type": param.type_name,
|
|
"default": arg_default
|
|
if arg_default is not inspect.Parameter.empty
|
|
else None,
|
|
"optional": bool(arg_default is not inspect.Parameter.empty)
|
|
or param.is_optional,
|
|
}
|
|
)
|
|
if doc_parsed.returns:
|
|
returns = {
|
|
"doc": doc_parsed.returns.description,
|
|
"type": doc_parsed.returns.type_name,
|
|
}
|
|
else:
|
|
returns = None
|
|
|
|
examples = []
|
|
|
|
for example in doc_parsed.examples:
|
|
examples.append(
|
|
{"snippet": example.snippet, "description": example.description.strip()}
|
|
)
|
|
|
|
return {
|
|
"name": trailmap.class_attr,
|
|
"path": path,
|
|
"function_name": function_name,
|
|
"func_def": func_def,
|
|
"source_code_url": source_code_url,
|
|
"description": doc_parsed.short_description,
|
|
"params": params,
|
|
"returns": returns,
|
|
"examples": examples,
|
|
}
|
|
|
|
|
|
def generate_markdown(meta_model: dict, meta_view: dict):
|
|
main_model = meta_model
|
|
if not meta_model:
|
|
if not meta_view:
|
|
raise ValueError("No model or view")
|
|
main_model = meta_view
|
|
markdown = f"""---
|
|
title: {main_model["name"]}
|
|
description: OpenBB SDK Function
|
|
---\n\n"""
|
|
if meta_view and meta_model:
|
|
markdown += """import Tabs from '@theme/Tabs';
|
|
import TabItem from '@theme/TabItem';\n\n"""
|
|
|
|
markdown += f"# {main_model['name']}\n\n"
|
|
|
|
if meta_view and meta_model:
|
|
markdown += f"""<Tabs>
|
|
<TabItem value="model" label="Model" default>\n
|
|
{generate_markdown_section(meta_model)}\n
|
|
</TabItem>
|
|
<TabItem value="view" label="Chart">\n
|
|
{generate_markdown_section(meta_view)}\n
|
|
</TabItem>
|
|
</Tabs>"""
|
|
else:
|
|
markdown += generate_markdown_section(main_model)
|
|
return markdown
|
|
|
|
|
|
def generate_markdown_section(meta: Dict[str, Any]):
|
|
# head meta https://docusaurus.io/docs/markdown-features/head-metadata
|
|
# use real description but need to parse it
|
|
markdown = (
|
|
f"{meta['description']}\n\nSource Code: [[link]({meta['source_code_url']})]\n\n"
|
|
)
|
|
markdown += f"```python wordwrap\n{meta['func_def']}\n```\n\n"
|
|
|
|
markdown += "---\n\n## Parameters\n\n"
|
|
if meta["params"]:
|
|
markdown += "| Name | Type | Description | Default | Optional |\n"
|
|
markdown += "| ---- | ---- | ----------- | ------- | -------- |\n"
|
|
for param in meta["params"]:
|
|
description = param["doc"].replace("\n", "<br/>") if param["doc"] else ""
|
|
markdown += f"| {param['name']} | {param['type']} | {description} | {param['default']} | {param['optional']} |\n" # noqa: E501
|
|
markdown += "\n\n"
|
|
else:
|
|
markdown += "This function does not take any parameters.\n\n"
|
|
|
|
markdown += "---\n\n## Returns\n\n"
|
|
if meta["returns"]:
|
|
markdown += "| Type | Description |\n"
|
|
markdown += "| ---- | ----------- |\n"
|
|
return_desc = (
|
|
meta["returns"]["doc"].replace("\n", "<br/>")
|
|
if meta["returns"]["doc"]
|
|
else ""
|
|
)
|
|
markdown += f"| {meta['returns']['type']} | {return_desc} |\n"
|
|
else:
|
|
markdown += "This function does not return anything\n\n"
|
|
|
|
markdown += "---\n\n## Examples\n\n" if meta["examples"] else ""
|
|
prev_snippet = " "
|
|
for example in meta["examples"]:
|
|
if isinstance(example["snippet"], str) and ">>>" in example["snippet"]:
|
|
snippet = example["snippet"].replace(">>> ", "")
|
|
markdown += f"```python\n{snippet}\n```\n\n"
|
|
if example["description"] and prev_snippet != "":
|
|
markdown += f"```\n{example['description']}\n```\n"
|
|
prev_snippet = snippet.strip()
|
|
else:
|
|
if example["description"]:
|
|
markdown += f"\n{example['description']}\n\n"
|
|
else:
|
|
if example["description"]:
|
|
markdown += f"\n{example['description']}\n\n"
|
|
prev_snippet = ""
|
|
|
|
markdown += "---\n\n"
|
|
|
|
return markdown
|
|
|
|
|
|
def add_todict(d: dict, location_path: list, tmap: Trailmap) -> dict:
|
|
"""Adds the trailmap to the dictionary. A trailmap is a path to a function
|
|
in the sdk. This function creates the dictionary paths to the function."""
|
|
|
|
if location_path[0] not in d:
|
|
d[location_path[0]] = {}
|
|
|
|
if len(location_path) > 1:
|
|
add_todict(d[location_path[0]], location_path[1:], tmap)
|
|
else:
|
|
d[location_path[0]][tmap.class_attr] = (
|
|
f"/sdk/reference/{'/'.join(tmap.location_path)}/{tmap.class_attr}"
|
|
).replace("//", "/")
|
|
|
|
return d
|
|
|
|
|
|
def get_nested_dict(d: dict, path: Path) -> Union[dict, None]:
|
|
"""Returns the nested dictionary for the given key."""
|
|
root, sub = path.parent.name, path.name
|
|
|
|
if sub in d:
|
|
return d[sub]
|
|
if root in d and sub in d[root]:
|
|
return d[root][sub]
|
|
for v in d.values():
|
|
if isinstance(v, dict):
|
|
item = get_nested_dict(v, path)
|
|
if item is not None:
|
|
return item
|
|
|
|
return None
|
|
|
|
|
|
def get_subname(name: str) -> str:
|
|
"""Returns the subname of the given name."""
|
|
if name != "reference":
|
|
subname = (
|
|
name.title() if name.lower() not in subnames else subnames[name.lower()]
|
|
)
|
|
return subname
|
|
return ""
|
|
|
|
|
|
def main() -> bool:
|
|
print("Loading trailmaps...")
|
|
trailmaps = get_trailmaps()
|
|
kwargs = {"encoding": "utf-8", "newline": "\n"}
|
|
|
|
print("Generating markdown files...")
|
|
content_path = website_path / "content/sdk/reference"
|
|
functions_dict: dict = {}
|
|
|
|
for file in content_path.glob("*"):
|
|
if file.is_file():
|
|
file.unlink()
|
|
else:
|
|
shutil.rmtree(file)
|
|
for trailmap in trailmaps:
|
|
try:
|
|
if trailmap.location_path[0] == "root":
|
|
trailmap.location_path[0] = ""
|
|
|
|
functions_dict = add_todict(
|
|
functions_dict, trailmap.location_path, trailmap
|
|
)
|
|
model_meta = (
|
|
get_function_meta(trailmap, "model") if trailmap.model else None
|
|
)
|
|
view_meta = get_function_meta(trailmap, "view") if trailmap.view else None
|
|
markdown = generate_markdown(model_meta, view_meta)
|
|
|
|
if trailmap.class_attr == "index":
|
|
trailmap.class_attr = "index_cmd"
|
|
|
|
filepath = f"{str(content_path)}/{'/'.join(trailmap.location_path)}/{trailmap.class_attr}.md"
|
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
with open(filepath, "w", **kwargs) as f: # type: ignore
|
|
f.write(markdown)
|
|
except Exception as e:
|
|
print(
|
|
f"Error generating {trailmap.location_path} {trailmap.class_attr} - {e}"
|
|
)
|
|
return False
|
|
|
|
functions_dict = {
|
|
k: dict(sorted(v.items(), key=lambda item: item[0]))
|
|
for k, v in sorted(functions_dict.items(), key=lambda item: item[0])
|
|
}
|
|
index_markdown = (
|
|
f"# OpenBB SDK Reference\n\n{generate_index_markdown('', functions_dict, 2)}"
|
|
)
|
|
with open(content_path / "index.md", "w", **kwargs) as f: # type: ignore
|
|
f.write(index_markdown)
|
|
|
|
with open(content_path / "_category_.json", "w", **kwargs) as f: # type: ignore
|
|
f.write(json.dumps({"label": "SDK Reference", "position": 4}, indent=2))
|
|
|
|
def gen_category_json(fname: str, path: Path):
|
|
"""Generate category json"""
|
|
fdict = {fname: get_nested_dict(functions_dict, path)}
|
|
|
|
with open(path / "index.md", "w", **kwargs) as f: # type: ignore
|
|
f.write(f"# {fname}\n\n{generate_index_markdown('', fdict, 2, path)}")
|
|
|
|
def gen_category_recursive(nested_path: Path):
|
|
"""Generate category json recursively"""
|
|
for folder in nested_path.iterdir():
|
|
if folder.is_dir():
|
|
gen_category_json(folder.name, folder)
|
|
gen_category_recursive(folder) # pylint: disable=cell-var-from-loop
|
|
|
|
gen_category_recursive(content_path)
|
|
print("Markdown files generated, check the functions folder")
|
|
|
|
return True
|
|
|
|
|
|
def generate_index_markdown(
|
|
markdown: str, d: dict, level: int, path: Optional[Path] = None
|
|
) -> str:
|
|
"""Generates the index markdown for the given dictionary."""
|
|
if path is None:
|
|
path = Path()
|
|
for key in d:
|
|
if isinstance(d[key], dict):
|
|
if path and path.name != key and key != "":
|
|
markdown += f"\n{'#' * level} {key}\n"
|
|
markdown = generate_index_markdown(markdown, d[key], level + 1, path)
|
|
else:
|
|
markdown += f"- [{key}]({d[key]})\n"
|
|
return markdown
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|