|
|
|
|
|
from __future__ import annotations |
|
|
|
|
|
import re |
|
|
from dataclasses import dataclass |
|
|
from typing import ClassVar |
|
|
|
|
|
|
|
|
import colorama as c |
|
|
import numpy as np |
|
|
|
|
|
|
|
|
|
|
|
FALSY = ["false", "no", "0"] |
|
|
TRUTHY = ["true", "yes", "1"] |
|
|
DEBUG_ICON = "\U0001F41B" |
|
|
INFO_ICON = "\U0001F6C8" |
|
|
WARN_ICON = "\U000026A0" |
|
|
ERR_ICON = "\U000026D4" |
|
|
CRIT_ICON = "\U000026A1" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True) |
|
|
class LogLevel: |
|
|
name: str |
|
|
severity: int |
|
|
prefix: str |
|
|
color: str |
|
|
|
|
|
|
|
|
class Logger: |
|
|
DEBUG: ClassVar = LogLevel("debug", 10, "[D]", c.Fore.GREEN) |
|
|
INFO: ClassVar = LogLevel("info", 20, "[I]", c.Fore.BLUE) |
|
|
WARN: ClassVar = LogLevel("warning", 30, "[W]", c.Fore.MAGENTA) |
|
|
ERROR: ClassVar = LogLevel("error", 40, "[E]", c.Fore.RED) |
|
|
CRITICAL: ClassVar = LogLevel("critical", 50, "[C]", c.Fore.YELLOW + c.Style.BRIGHT) |
|
|
|
|
|
def __init__(self, level: LogLevel = INFO, *, color: bool = False): |
|
|
self.__min_sev = level.severity |
|
|
c.init(autoreset=False, strip=not color) |
|
|
|
|
|
def __print(self, level: LogLevel, msg: str, dataset): |
|
|
if level.severity >= self.__min_sev: |
|
|
print(f"\n{level.color}{level.prefix} {msg}") |
|
|
for sub_msg in dataset: |
|
|
print(f" * {sub_msg}") |
|
|
print(c.Style.RESET_ALL) |
|
|
|
|
|
def debug(self, msg: str, dataset: list | None = None): |
|
|
self.__print(Logger.DEBUG, msg, dataset or []) |
|
|
|
|
|
def info(self, msg, dataset: list | None = None): |
|
|
self.__print(Logger.INFO, msg, dataset or []) |
|
|
|
|
|
def warn(self, msg, dataset: list | None = None): |
|
|
self.__print(Logger.WARN, msg, dataset or []) |
|
|
|
|
|
def error(self, msg, dataset: list | None = None): |
|
|
self.__print(Logger.ERROR, msg, dataset or []) |
|
|
|
|
|
|
|
|
|
|
|
logger = Logger(Logger.DEBUG, color=True) |
|
|
|
|
|
|
|
|
def clean_name(name: str): |
|
|
return str(name).lower().strip().replace(" ", "_").replace("[", "").replace("]", "") |
|
|
|
|
|
|
|
|
def get_closest_from_list(name: str, choices: list[str]) -> str: |
|
|
if name in choices: |
|
|
return name |
|
|
|
|
|
found = sorted([item for item in choices if name in item], key=len) |
|
|
if found: |
|
|
return found[0] |
|
|
return "" |
|
|
|
|
|
|
|
|
re_range = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\(([+-]\d+)\s*\))?\s*") |
|
|
re_range_float = re.compile( |
|
|
r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\(([+-]\d+(?:.\d*)?)\s*\))?\s*" |
|
|
) |
|
|
|
|
|
re_range_count = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\[(\d+)\s*\])?\s*") |
|
|
re_range_count_float = re.compile( |
|
|
r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\[(\d+(?:.\d*)?)\s*\])?\s*" |
|
|
) |
|
|
|
|
|
|
|
|
def parse_range_int(value_list: list[str]) -> list[int]: |
|
|
parsed_list = [] |
|
|
|
|
|
for val in value_list: |
|
|
match = re_range.fullmatch(val) |
|
|
count = re_range_count.fullmatch(val) |
|
|
if match is not None: |
|
|
start = int(match.group(1)) |
|
|
end = int(match.group(2)) + 1 |
|
|
step = int(match.group(3)) if match.group(3) is not None else 1 |
|
|
|
|
|
parsed_list += list(range(start, end, step)) |
|
|
elif count is not None: |
|
|
start = int(count.group(1)) |
|
|
end = int(count.group(2)) |
|
|
num = int(count.group(3)) if count.group(3) is not None else 1 |
|
|
parsed_list += [int(x) for x in np.linspace(start=start, stop=end, num=num).tolist()] |
|
|
else: |
|
|
parsed_list.append(int(val)) |
|
|
|
|
|
return parsed_list |
|
|
|
|
|
|
|
|
def parse_range_float(value_list: list[str]) -> list[float]: |
|
|
parsed_list = [] |
|
|
|
|
|
for val in value_list: |
|
|
match = re_range_float.fullmatch(val) |
|
|
count = re_range_count_float.fullmatch(val) |
|
|
if match is not None: |
|
|
start = float(match.group(1)) |
|
|
end = float(match.group(2)) |
|
|
step = float(match.group(3)) if match.group(3) is not None else 1 |
|
|
parsed_list += np.arange(start, end + step, step).tolist() |
|
|
elif count is not None: |
|
|
start = float(count.group(1)) |
|
|
end = float(count.group(2)) |
|
|
num = int(count.group(3)) if count.group(3) is not None else 1 |
|
|
parsed_list += np.linspace(start=start, stop=end, num=num).tolist() |
|
|
else: |
|
|
parsed_list += [round(float(val), 2)] |
|
|
|
|
|
return parsed_list |
|
|
|