|
|
"""Chart and style helpers for Plotly.""" |
|
|
|
|
|
|
|
|
import json |
|
|
from pathlib import Path |
|
|
from typing import Any, Dict, List, Optional |
|
|
|
|
|
from rich.console import Console |
|
|
|
|
|
from openbb_cli.config.constants import STYLES_DIRECTORY |
|
|
|
|
|
console = Console() |
|
|
|
|
|
|
|
|
class Style: |
|
|
"""The class that helps with handling of style configurations. |
|
|
|
|
|
It serves styles for 2 libraries. For `Plotly` this class serves absolute paths |
|
|
to the .pltstyle files. For `Plotly` and `Rich` this class serves custom |
|
|
styles as python dictionaries. |
|
|
""" |
|
|
|
|
|
STYLES_REPO = STYLES_DIRECTORY |
|
|
|
|
|
console_styles_available: Dict[str, Path] = {} |
|
|
console_style: Dict[str, Any] = {} |
|
|
|
|
|
line_color: str = "" |
|
|
up_color: str = "" |
|
|
down_color: str = "" |
|
|
up_colorway: List[str] = [] |
|
|
down_colorway: List[str] = [] |
|
|
up_color_transparent: str = "" |
|
|
down_color_transparent: str = "" |
|
|
|
|
|
line_width: float = 1.5 |
|
|
|
|
|
def __init__( |
|
|
self, |
|
|
style: Optional[str] = "", |
|
|
directory: Optional[Path] = None, |
|
|
): |
|
|
"""Initialize the class.""" |
|
|
self._load(directory) |
|
|
self.apply(style, directory) |
|
|
|
|
|
def apply( |
|
|
self, style: Optional[str] = None, directory: Optional[Path] = None |
|
|
) -> None: |
|
|
"""Apply the style to the console.""" |
|
|
if style: |
|
|
if style in self.console_styles_available: |
|
|
json_path: Optional[Path] = self.console_styles_available[style] |
|
|
else: |
|
|
self._load(directory) |
|
|
if style in self.console_styles_available: |
|
|
json_path = self.console_styles_available[style] |
|
|
else: |
|
|
console.print(f"\nInvalid console style '{style}', using default.") |
|
|
json_path = self.console_styles_available.get("dark", None) |
|
|
|
|
|
if json_path: |
|
|
self.console_style = self._from_json(json_path) |
|
|
else: |
|
|
console.print("Error loading default.") |
|
|
|
|
|
def _from_directory(self, folder: Optional[Path]) -> None: |
|
|
"""Load custom styles from folder. |
|
|
|
|
|
Parses the styles/default and styles/user folders and loads style files. |
|
|
To be recognized files need to follow a naming convention: |
|
|
*.pltstyle - plotly stylesheets |
|
|
*.richstyle.json - rich stylesheets |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
folder : str |
|
|
Path to the folder containing the stylesheets |
|
|
""" |
|
|
if not folder or not folder.exists(): |
|
|
return |
|
|
|
|
|
for attr, ext in zip( |
|
|
["console_styles_available"], |
|
|
[".richstyle.json"], |
|
|
): |
|
|
for file in folder.rglob(f"*{ext}"): |
|
|
getattr(self, attr)[file.name.replace(ext, "")] = file |
|
|
|
|
|
def _load(self, directory: Optional[Path] = None) -> None: |
|
|
"""Load custom styles from default and user folders.""" |
|
|
self._from_directory(self.STYLES_REPO) |
|
|
self._from_directory(directory) |
|
|
|
|
|
def _from_json(self, file: Path) -> Dict[str, Any]: |
|
|
"""Load style from json file.""" |
|
|
with open(file) as f: |
|
|
json_style: dict = json.load(f) |
|
|
for key, value in json_style.items(): |
|
|
json_style[key] = value.replace( |
|
|
" ", "" |
|
|
) |
|
|
return json_style |
|
|
|
|
|
@property |
|
|
def available_styles(self) -> List[str]: |
|
|
"""Return available styles.""" |
|
|
return list(self.console_styles_available.keys()) |
|
|
|