Source code for basic_report.utils

import datetime
from jinja2 import Environment, FileSystemLoader
from loguru import logger
from pathlib import Path
from typing import Any, Union, Optional

from . import TEMPLATE_DIR

#----------------------------------------------------------------------------------------------------------------------#
# region COLOR MAP
#----------------------------------------------------------------------------------------------------------------------#
[docs] class ColorMap: """ Color map object for handling the color space of the report """ def __init__(self, config: dict[str, Any], color_mode: str, text_color: Optional[str]=None): """ Initialize a new color map given a config dict Args: config (dict[str, Any]): The configuration for this report. Mainly responsible for defining new color profiles that can later be used color_mode (str): Select the current color mode of the report, i.e., `dark` or `light` text_color (Optional[str]): Override for the text color of the report. For a more granular approach use the config file """ self.config = config self.color_mode = color_mode self.text_color = text_color # Prepare the jinja templater self.file_loader = FileSystemLoader(TEMPLATE_DIR) self.jinja_env = Environment(loader=self.file_loader) # Default color map - Maps the default bootstrap colors to their respective color names self.cm = { 'blue': 'primary', 'gray': 'secondary', 'green': 'success', 'red': 'danger', 'yellow': 'warning', 'teal': 'info', 'black': 'dark', 'primary': 'primary', 'secondary': 'secondary', 'success': 'success', 'danger': 'danger', 'warning': 'warning', 'info': 'info', 'dark': 'dark', 'light': 'light', } # Create the custom color CSS that will be injected and make the profiles available in the color map for cc in self.config['custom_colors']: self.cm[cc] = cc #---------------# # Magic Methods # #---------------# def __getitem__(self, key: str) -> str: """ Verify the color name and return the proper color mapping """ return self.verify_and_get_color(key) #---------# # Private # #---------# def _make_color_css_files(self, output_dir: Path): """ Create the custom color css file """ # Create CSS string color_items = [v | {'name': k} for k,v in self.config['custom_colors'].items()] # Color CSS template = self.jinja_env.get_template('colors.css') css = template.render(color_items=color_items) # Dump to scripts dir custom_color_file = output_dir / 'custom_colors.css' custom_color_file.write_text(css) # Color CSS template = self.jinja_env.get_template('navbar.css') css = template.render(color_items=color_items) # Dump to scripts dir navbar_file = output_dir / 'navbar.css' navbar_file.write_text(css) def _make_css_file(self, element_type: str, output_dir: Path): """ Create the custom css files for the element type """ # Create CSS string colors = self.config['css'][element_type][self.color_mode] template = self.jinja_env.get_template(f'{element_type}.css') css = template.render(**colors) # Dump to scripts dir css_file = output_dir / f'{element_type}.css' css_file.write_text(css) #-----# # API # #-----#
[docs] def verify_and_get_color(self, color: str) -> str: """ Check that the requested color is actually available Args: color (str): Color to check Returns: str - color name to use """ if color not in self.cm: msg = f'No color `{color}` found. Available: {self.cm.keys()}' logger.error(msg) raise RuntimeError(msg) return self.cm[color]
[docs] def get_default_color(self, field: str) -> str: """ Determine the correct default color for a given color mode and field Args: field (str): The type/field the color should be used for color_mode (str): The color mode of the report, i.e., either `light` or `dark` Returns: str - The color to be used """ if self.text_color is None or field not in ['text', 'table', 'report_ball']: color = self.config['default_color_profiles'][self.color_mode].get(field) else: color = self.text_color if color is None: msg = f'No default color defined for `{field}` with color mode `{self.color_mode}`' logger.error(msg) raise RuntimeError(msg) return color
[docs] def make_css_files(self, output_dir: Path): """ Create the custom css files for the site Args: output_dir (Path): The reports css and script dir """ self._make_color_css_files(output_dir) for element_type in ['site', 'tabs', 'accordion', 'table']: self._make_css_file(element_type, output_dir)
#----------------------------------------------------------------------------------------------------------------------# # region MISC HELPER #----------------------------------------------------------------------------------------------------------------------#
[docs] def coerce_to_date(date: Union[str, int, datetime.date, datetime.datetime]) -> datetime.date: """ Coerce random data formats to a datetime.date object Args: date (Union[str, int, datetime.date, datetime.datetime]): The date to coerce Returns: datetime.date - The coerce date object """ if isinstance(date, datetime.date): return date elif isinstance(date, datetime.datetime): return date.date elif isinstance(date, int): date = str(int) if not isinstance(date, str): msg = f'Cannot coerce date {date}' logger.error(msg) raise RuntimeError(msg) if len(date) == 6: # noqa: PLR2004 year = int(date[:2]) if year < 50: # noqa: PLR2004 year += 2000 else: year += 1900 month = int(date[2:4]) day = int(date[4:]) elif len(date) == 8: # noqa: PLR2004 if date.isdigit(): year = int(date[:4]) month = int(date[4:6]) day = int(date[6:]) else: year = int(date[:2]) if year < 50: # noqa: PLR2004 year += 2000 else: year += 1900 month = int(date[3:5]) day = int(date[6:]) elif len(date) == 10: # noqa: PLR2004 year = int(date[:4]) month = int(date[5:7]) day = int(date[8:]) return datetime.date(year, month, day)
[docs] def verify_alignment(align: str) -> str: """ Check that the requested alignment is actually available Args: align (str): The alignment tag that needs to be verified Returns: str - The alignment if it is acceptable """ alignments = ['left', 'center', 'right'] if align not in alignments: msg = f'Unknown alignment {align}. Available: {alignments}' logger.error(msg) raise RuntimeError(msg) return align
[docs] def verify_color_mode(color_mode: str) -> str: """ Check that the requested alignment is actually available Args: color_mode (str): The alignment tag that needs to be verified Returns: str - The alignment if it is acceptable """ color_modes = ['light', 'dark'] if color_mode not in color_modes: msg = f'Unknown color_mode {color_mode}. Available: {color_modes}' logger.error(msg) raise RuntimeError(msg) return color_mode
[docs] def default(var: Any, default: Any) -> Any: """ Helper function to check kwargs and set them to a default You could simply use var or default. But for future additions we will use a separat function here Args: var (Any): The input variable default (Any): Default value for the variable Returns Any - Either the variable itself, or the default value if var is `None` """ if var is None: return default else: return var