import json import re import shutil import traceback from datetime import datetime from pathlib import Path from textwrap import shorten from typing import Dict, List, TextIO, Union from openbb_terminal.core.session.current_system import set_system_variable from openbb_terminal.core.session.current_user import get_current_user from website.controller_doc_classes import ( ControllerDoc, LoadControllersDoc, console, sub_names_full as subnames, ) set_system_variable("TEST_MODE", True) set_system_variable("LOG_COLLECT", False) website_path = Path(__file__).parent.absolute() CONTENT_PATH: Path = website_path / "content/terminal/reference" EXAMPLES_META: Dict[str, Dict[str, Union[str, List[str]]]] = json.loads( (website_path / "metadata/terminal_v3_examples.json").read_text() ) SEO_META: Dict[str, Dict[str, Union[str, List[str]]]] = json.loads( (website_path / "metadata/terminal_v3_seo_metadata.json").read_text() ) reference_import = ( 'import ReferenceCard from "@site/src/components/General/NewReferenceCard";\n\n' ) refrence_ul_element = """\n") folder_cmd_cards: List[Dict[str, str]] = reference_cards.get(path, {}) # type: ignore if folder_cmd_cards: f.writelines( [ f"\n\n### Commands\n{refrence_ul_element}\n", create_cmd_cards(folder_cmd_cards), "\n", ] ) def main() -> bool: """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() wopen_kwargs = {"encoding": "utf-8", "newline": "\n"} console.print( "[bright_yellow]Generating markdown files... Don't ignore any errors now[/bright_yellow]" ) reference_cards: Dict[Path, List[Dict[str, str]]] = {} global_cmds = {"askobb": False, "hold": False, "about": False} for file in CONTENT_PATH.glob("*"): if file.is_file(): file.unlink() else: shutil.rmtree(file) for ctrl_trailmap in ctrls: try: ctrl = load_ctrls.get_controller_doc(ctrl_trailmap) ctrl_cmds = process_cmd_parsers(ctrl, ctrl_trailmap) for cmd in ctrl_cmds: trail = ctrl_trailmap.split(".") if (created := global_cmds.get(cmd["cmd_name"], None)) is not None: if created: continue global_cmds[cmd["cmd_name"]] = True trail = [] examples = existing_markdown_file_examples(ctrl_trailmap, cmd) markdown = cmd["header"] + generate_markdown(cmd, examples) if cmd["cmd_name"] == "index": cmd["cmd_name"] = "index_cmd" filepath = CONTENT_PATH / f"{'/'.join(trail + [cmd['cmd_name']])}.md" reference_cards.setdefault(filepath.parent, []).append( dict( title=cmd["cmd_name"], description=cmd["description"], url="/".join(trail), ) ) filepath.parent.mkdir(parents=True, exist_ok=True) with open(filepath, "w", **wopen_kwargs) as f: # type: ignore f.write(markdown) except Exception as e: traceback.print_exc() console.print( f"[red]Failed to generate markdown for {ctrl_trailmap}: {e}[/red]" ) return False # Sort reference_cards reference_cards = dict(sorted(reference_cards.items(), key=lambda item: item[0])) # Generate root "_category_.json" file with open(CONTENT_PATH / "_category_.json", "w", **wopen_kwargs) as f: # type: ignore f.write(json.dumps({"label": "Reference", "position": 4}, indent=2)) # Generate root "index.md" file with open(CONTENT_PATH / "index.mdx", "w", **wopen_kwargs) as f: # type: ignore fname = "OpenBB Terminal Features" rel_path = CONTENT_PATH.relative_to(CONTENT_PATH) write_reference_index(reference_cards, fname, CONTENT_PATH, rel_path, f) def gen_category_json(fname: str, path: Path, position: int = 1): """Generate category json""" fname = subnames[fname.lower()] if fname.lower() in subnames else fname.title() with open(path / "_category_.json", "w", **wopen_kwargs) as f: # type: ignore f.write(json.dumps({"label": fname, "position": position}, indent=2)) with open(path / "index.mdx", "w", **wopen_kwargs) as f: # type: ignore rel_path = path.relative_to(CONTENT_PATH) write_reference_index(reference_cards, fname, path, rel_path, f) def gen_category_recursive(nested_path: Path): """Generate category json recursively""" position = 1 for folder in nested_path.iterdir(): if folder.is_dir(): gen_category_json(folder.name, folder, position) gen_category_recursive(folder) # pylint: disable=cell-var-from-loop position += 1 # Generate modules/sub category json and index files gen_category_recursive(CONTENT_PATH) console.print( "[green]Markdown files generated, check the website/content/terminal/reference/ folder[/green]" ) return True def save_metadata() -> Dict[str, Dict[str, Union[str, List[str]]]]: """Save SEO metadata to json file.""" regex = re.compile( r"---\ntitle: (.*)\ndescription: (.*)\nkeywords:(.*)\n---\n\nimport HeadTitle", re.MULTILINE | re.DOTALL, ) metadata = {} for file in CONTENT_PATH.rglob("*/**/*.md"): context = file.read_text(encoding="utf-8") match = regex.search(context) if match: title, description, keywords = match.groups() key = file.relative_to(CONTENT_PATH).as_posix().removesuffix(".md") metadata[key.replace("/", ".")] = { "title": title, "description": description, "keywords": [ keyword.strip() for keyword in keywords.split("\n- ") if keyword ], } filepath = website_path / "metadata/sdk_v3_seo_metadata.json" with open(filepath, "w", encoding="utf-8", newline="\n") as f: json.dump(metadata, f, indent=2) return metadata if __name__ == "__main__": main()