Files
OpenBB/website/generate_terminal_markdown.py
jose-donato 2e7eb4c9b6 New documentation website (#3526)
* fix(docs): added margin between sections

* feat: removed unused files; updated ga

* fix: juan stuff doesnt work

* fix: random fix

* moon at noon

* fix: update docusaurus config

* Update controller_doc_classes.py

* fix: temp fix

* fix: temp fix

* Update controller_doc_classes.py

* fix: remove old content

* fix: taggfix

* init

* fix: styling and moved file

* Update generate_terminal_markdown.py

* fix: removed unused images

* fix: port new files

* Update generate_sdk_markdown.py

* Update controller_doc_classes.py

Co-authored-by: teh_coderer <me@tehcoderer.com>
2022-11-22 16:47:03 +00:00

263 lines
8.7 KiB
Python

from datetime import datetime
import os
import json
from pathlib import Path
import traceback
from typing import Dict, List, Optional, Union
from openbb_terminal.rich_config import console
from website.controller_doc_classes import (
LoadControllersDoc,
ControllerDoc,
sub_names_full,
)
website_path = Path(__file__).parent.absolute()
def existing_markdown_file_examples(
ctrl: ControllerDoc, cat: Dict[str, str]
) -> Dict[str, Optional[Union[str, List[str]]]]:
"""Get existing markdown file examples"""
trail = ctrl.trailmap.split(".")
for sub in trail:
if sub in ["ba", "ta", "qa"]:
trail.remove(ctrl.trailmap.split(".")[0])
examples_path = (
f"old_content/terminal/{'/'.join(trail)}/{cat['cmd_name']}/_index.md"
)
examples_dict: Dict[str, Optional[Union[str, List[str]]]] = {}
if os.path.exists(website_path / examples_path):
with open(website_path / examples_path, encoding="utf-8") as f:
content = f.read()
examples: Optional[str] = None
if "Example:" in content:
example_split = content.split("Example:")[1].split("```")
if example_split and len(example_split) > 1:
examples = f"{example_split[1].strip()}"
examples_dict["example"] = examples
images = [
x for x in content.split("\n") if x.startswith("!") and "TODO" not in x
]
examples_dict["images"] = images
return examples_dict
# pylint: disable=isinstance-second-argument-not-valid-type
def get_parser(ctrl: ControllerDoc) -> Dict[str, List[Dict[str, str]]]:
"""Get commands and parsers from ControllerDoc"""
commands = []
for cmd, parser in ctrl.cmd_parsers.items():
actions = []
for action in parser._actions: # pylint: disable=protected-access
if action.dest == "help":
continue
default = action.default
if default is not None:
if isinstance(default, list):
default = ", ".join([str(x) for x in default])
elif isinstance(default, datetime):
if "start" in action.dest:
default = "datetime.now() - timedelta(days=365)"
elif "end" in action.dest or "date" in action.dest:
default = "datetime.now()"
choices = action.choices
if choices is not None:
if isinstance(choices, list):
listdict = []
for choice in choices:
if isinstance(choice, (dict, type({}.keys()))):
listdict.append([f"{k}" for k in choice])
if listdict:
choices = listdict
else:
choices = [f"{x}" for x in choices]
choices = ", ".join(choices) if len(choices) > 0 else None
elif isinstance(choices, (dict, type({}.keys()))):
choices = [f"{k}" for k in choices]
choices = ", ".join(choices) if len(choices) > 0 else None
doc = action.help
if doc is not None:
# We do this to fix multiline docstrings for the markdown
doc = " ".join(doc.split())
actions.append(
{
"opt_name": action.dest if action.dest else "",
"doc": doc if doc else "",
"default": default,
"optional": not action.required,
"choices": choices,
}
)
desc = parser.description
if desc is not None:
# We do this to fix multiline docstrings for the markdown
desc = " ".join(desc.split())
param = {
"cmd_name": cmd.replace("call_", ""),
"actions": actions,
"usage": parser.format_usage(),
"description": desc if desc else "",
}
commands.append(param)
return {
"category_name": ctrl.name,
"cmds": commands,
}
def generate_markdown(cmd_meta: Dict[str, str], examples: Dict[str, str]):
"""Generate markdown string"""
if not cmd_meta:
raise ValueError("No command metadata found")
markdown = f"""---
title: {cmd_meta["cmd_name"]}
description: OpenBB Terminal Function
---\n\n"""
markdown += generate_markdown_section(cmd_meta, examples)
return markdown
def generate_markdown_section(meta: Dict[str, str], examples: Dict[str, str]) -> str:
"""Generate markdown section"""
# head meta https://docusaurus.io/docs/markdown-features/head-metadata
markdown = f"# {meta['cmd_name']}\n\n{meta['description']}\n\n"
markdown += f"### Usage\n\n```python\n{meta['usage']}```\n\n"
markdown += "---\n\n## Parameters\n\n"
if meta["actions"]:
markdown += "| Name | Description | Default | Optional | Choices |\n"
markdown += "| ---- | ----------- | ------- | -------- | ------- |\n"
for param in meta["actions"]:
if isinstance(param, dict):
markdown += (
f"| {param['opt_name']} | {param['doc']} | {param['default']} "
f"| {param['optional']} | {param['choices']} |\n"
)
else:
markdown += "This command has no parameters\n\n"
if examples.get("example", None):
markdown += "---\n\n## Examples\n\n"
markdown += f"```python\n{examples['example']}\n```\n\n"
if examples.get("images", []):
for image in examples["images"]:
markdown += f"{image}\n\n"
markdown += "---\n\n"
return markdown.replace("<", "").replace(">", "")
def add_todict(d: dict, location_path: list, cmd_name: str, full_path: str) -> dict:
"""Adds the trailmap to the dictionary. 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:], cmd_name, full_path)
else:
d[location_path[0]][cmd_name] = (
"/terminal/features/"
+ "/".join(full_path).replace(" ", "%20")
+ "/"
+ cmd_name
)
return d
def main():
"""Main function to generate markdown files"""
console.print(
"[bright_yellow]Loading Controllers... Please wait and ignore any errors, this is normal.[/bright_yellow]"
)
load_ctrls = LoadControllersDoc()
ctrls = load_ctrls.available_controllers()
console.print(
"[bright_yellow]Generating markdown files... Don't ignore any errors now[/bright_yellow]"
)
content_path = website_path / "content/terminal/reference"
terminal_ref = {}
for ctrlstr in ctrls:
try:
ctrl = load_ctrls.get_controller_doc(ctrlstr)
cmd_meta = get_parser(ctrl)
for cat in cmd_meta["cmds"]:
examples = existing_markdown_file_examples(ctrl, cat)
markdown = generate_markdown(cat, examples)
if cat["cmd_name"] == "index":
cat["cmd_name"] = "index_cmd"
trail = []
for sub in ctrl.trailmap.split("."):
if sub_names_full.get(sub, None):
sub = sub_names_full[sub].lower()
trail.append(sub)
terminal_ref = add_todict(terminal_ref, trail, cat["cmd_name"], trail)
filepath = f"{str(content_path)}/{'/'.join(trail)}/{cat['cmd_name']}.md"
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, "w", encoding="utf-8") as f:
f.write(markdown)
except Exception as e:
traceback.print_exc()
console.print(f"[red]Failed to generate markdown for {ctrlstr}: {e}[/red]")
with open(content_path / "_category_.json", "w", encoding="utf-8") as f:
f.write(json.dumps({"label": "Terminal Reference", "position": 4}, indent=2))
with open(content_path / "index.md", "w", encoding="utf-8") as f:
f.write(
f"# OpenBB Terminal Features\n\n{generate_index_markdown('', terminal_ref, 2)}"
)
console.print(
"[green]Markdown files generated, check the website/content/terminal/reference/ folder[/green]"
)
def generate_index_markdown(markdown, d, level):
for key in d:
if isinstance(d[key], dict):
markdown += f"\n{'#' * level} {key}\n\n"
markdown = generate_index_markdown(markdown, d[key], level + 1)
else:
markdown += f"- [{key}]({d[key]})\n"
return markdown
if __name__ == "__main__":
main()