summaryrefslogtreecommitdiffstats
path: root/cli/openbb_cli/config/menu_text.py
blob: 06c7023ef1f5cb0ae1772995c0e9f2dc46e38255 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"""Rich Module."""

__docformat__ = "numpy"

from typing import Dict, List

from openbb import obb

# https://rich.readthedocs.io/en/stable/appendix/colors.html#appendix-colors
# https://rich.readthedocs.io/en/latest/highlighting.html#custom-highlighters


RICH_TAGS = [
    "[menu]",
    "[/menu]",
    "[cmds]",
    "[/cmds]",
    "[info]",
    "[/info]",
    "[param]",
    "[/param]",
    "[src]",
    "[/src]",
    "[help]",
    "[/help]",
]


class MenuText:
    """Create menu text with rich colors to be displayed by CLI."""

    CMD_NAME_LENGTH = 23
    CMD_DESCRIPTION_LENGTH = 65
    CMD_PROVIDERS_LENGTH = 23
    SECTION_SPACING = 4

    def __init__(self, path: str = ""):
        """Initialize menu help."""
        self.menu_text = ""
        self.menu_path = path
        self.warnings: List[Dict[str, str]] = []

    @staticmethod
    def _get_providers(command_path: str) -> List:
        """Return the preferred provider for the given command.

        Parameters
        ----------
        command_path: str
            The command to find the provider for. E.g. "/equity/price/historical

        Returns
        -------
        List
            The list of providers for the given command.
        """
        command_reference = obb.reference.get("paths", {}).get(command_path, {})  # type: ignore
        if command_reference:
            providers = list(command_reference["parameters"].keys())
            return [provider for provider in providers if provider != "standard"]
        return []

    def _format_cmd_name(self, name: str) -> str:
        """Truncate command name length if it is too long."""
        if len(name) > self.CMD_NAME_LENGTH:
            new_name = name[: self.CMD_NAME_LENGTH]

            if "_" in name:
                name_split = name.split("_")

                new_name = (
                    "_".join(name_split[:2]) if len(name_split) > 2 else name_split[0]
                )

                if len(new_name) > self.CMD_NAME_LENGTH:
                    new_name = new_name[: self.CMD_NAME_LENGTH]

            if new_name != name:
                self.warnings.append(
                    {
                        "warning": "Command name too long",
                        "command": name,
                        "trimmed_command": new_name,
                    }
                )
                name = new_name

        return name

    def _format_cmd_description(
        self, name: str, description: str, trim: bool = True
    ) -> str:
        """Truncate command description length if it is too long."""
        if not description or description == f"{self.menu_path}{name}":
            description = ""
        return (
            description[: self.CMD_DESCRIPTION_LENGTH - 3] + "..."
            if len(description) > self.CMD_DESCRIPTION_LENGTH and trim
            else description
        )

    def add_raw(self, text: str):
        """Append raw text (without translation)."""
        self.menu_text += text

    def add_section(
        self, text: str, description: str = "", leading_new_line: bool = False
    ):
        """Append raw text (without translation)."""
        spacing = (self.CMD_NAME_LENGTH - len(text) + self.SECTION_SPACING) * " "
        left_spacing = self.SECTION_SPACING * " "
        text = f"{left_spacing}{text}"
        if description:
            text = f"{text}{spacing}{description}\n"

        if leading_new_line:
            self.menu_text += "\n" + text

        else:
            self.menu_text += text

    def add_info(self, text: str):
        """Append information text (after translation)."""
        self.menu_text += f"[info]{text}:[/info]\n"

    def add_cmd(self, name: str, description: str = "", disable: bool = False):
        """Append command text (after translation)."""
        formatted_name = self._format_cmd_name(name)
        name_padding = (self.CMD_NAME_LENGTH - len(formatted_name)) * " "
        providers = self._get_providers(f"{self.menu_path}{name}")
        formatted_description = self._format_cmd_description(
            formatted_name,
            description,
            bool(providers),
        )
        description_padding = (
            self.CMD_DESCRIPTION_LENGTH - len(formatted_description)
        ) * " "
        spacing = self.SECTION_SPACING * " "
        description_padding = (
            self.CMD_DESCRIPTION_LENGTH - len(formatted_description)
        ) * " "
        cmd = f"{spacing}{formatted_name + name_padding}{spacing}{formatted_description+description_padding}"
        cmd = f"[unvl]{cmd}[/unvl]" if disable else f"[cmds]{cmd}[/cmds]"

        if providers:
            cmd += rf"{spacing}[src]\[{', '.join(providers)}][/src]"

        self.menu_text += cmd + "\n"

    def add_menu(
        self,
        name: str,
        description: str = "",
        disable: bool = False,
    ):
        """Append menu text (after translation)."""
        spacing = (self.CMD_NAME_LENGTH - len(name) + self.SECTION_SPACING) * " "

        if not description or description == f"{self.menu_path}{name}":
            description = ""

        if len(description) > self.CMD_DESCRIPTION_LENGTH:
            description = description[: self.CMD_DESCRIPTION_LENGTH - 3] + "..."

        menu = f"{name}{spacing}{description}"
        tag = "unvl" if disable else "menu"
        self.menu_text += f"[{tag}]>   {menu}[/{tag}]\n"

    def add_setting(self, name: str, status: bool = True, description: str = ""):
        """Append menu text (after translation)."""
        spacing = (self.CMD_NAME_LENGTH - len(name) + self.SECTION_SPACING) * " "
        indentation = self.SECTION_SPACING * " "
        color = "green" if status else "red"

        self.menu_text += (
            f"[{color}]{indentation}{name}{spacing}{description}[/{color}]\n"
        )