# Path: slo_generator/utils.py
#     GCS_ENABLED = True
#     GCS_ENABLED = False
# LOGGER = logging.getLogger(__name__)
# def load_configs(path, ctx=os.environ, kind=None):
# def load_config(path, ctx=os.environ, kind=None):
# def parse_config(path=None, content=None, ctx=os.environ):
#     def replace_env_vars(content, ctx):
# def setup_logging():
# def get_human_time(timestamp, timezone=None):
# def get_exporters(config, spec):
# def get_backend(config, spec):
# def get_error_budget_policy(config, spec):
# def get_backend_cls(backend):
# def get_exporter_cls(exporter):
# def import_cls(cls_name, expected_type):
# def import_dynamic(package, name, prefix="class"):
# def capitalize(word):
# def snake_to_caml(word):
# def caml_to_snake(word):
# def dict_snake_to_caml(data):
# def apply_func_dict(data, func):
# def str2bool(string):
# def download_gcs_file(url):
# def decode_gcs_url(url):
# def get_files(source, extensions=['yaml', 'yml', 'json']):
# def get_target_path(source_dir, target_dir, relative_path, mkdir=True):
# def fmt_traceback(exc):
# 
# Path: slo_generator/constants.py
# COLORED_OUTPUT = int(os.environ.get("COLORED_OUTPUT", "0"))
# 
# MIN_VALID_EVENTS = int(os.environ.get("MIN_VALID_EVENTS", "1"))
# 
# NO_DATA = -1
# 
# class Colors:
#     """Colors for console output."""
#     HEADER = '\033[95m'
#     OKBLUE = '\033[94m'
#     OKGREEN = '\033[92m'
#     WARNING = '\033[93m'
#     FAIL = '\033[91m'
#     ENDC = '\033[0m'
#     BOLD = '\033[1m'
#     UNDERLINE = '\033[4m'


import logging
from dataclasses import asdict, dataclass, fields, field
from slo_generator import utils
from slo_generator.constants import (COLORED_OUTPUT, MIN_VALID_EVENTS, NO_DATA,
                                     Colors)
            if name in lambdas:
                value = lambdas[name](value)
            setattr(self, name, value)

    @property
    def info(self):
        """Step information."""
        return f"{self.name :<32} | {self.error_budget_policy_step_name :<8}"

    def __str__(self):
        report = self.to_json()
        if not self.valid:
            errors_str = ' | '.join(self.errors)
            return f'{self.info} | {errors_str}'
        goal_per = self.goal * 100
        sli_per = round(self.sli_measurement * 100, 6)
        gap = round(self.gap * 100, 2)
        gap_str = str(gap)
        if gap >= 0:
            gap_str = f'+{gap}'

        sli_str = (f'SLI: {sli_per:<7} % | SLO: {goal_per} % | '
                   f'Gap: {gap_str:<6}%')
        result_str = ('BR: {error_budget_burn_rate:<2} / '
                      '{error_budget_burn_rate_threshold} | '
                      'Alert: {alert:<1} | Good: {good_events_count:<8} | '
                      'Bad: {bad_events_count:<8}').format_map(report)
        full_str = f'{self.info} | {sli_str} | {result_str}'
        if COLORED_OUTPUT == 1:
            if self.alert:

# Path: slo_generator/utils.py
#     GCS_ENABLED = True
#     GCS_ENABLED = False
# LOGGER = logging.getLogger(__name__)
# def load_configs(path, ctx=os.environ, kind=None):
# def load_config(path, ctx=os.environ, kind=None):
# def parse_config(path=None, content=None, ctx=os.environ):
#     def replace_env_vars(content, ctx):
# def setup_logging():
# def get_human_time(timestamp, timezone=None):
# def get_exporters(config, spec):
# def get_backend(config, spec):
# def get_error_budget_policy(config, spec):
# def get_backend_cls(backend):
# def get_exporter_cls(exporter):
# def import_cls(cls_name, expected_type):
# def import_dynamic(package, name, prefix="class"):
# def capitalize(word):
# def snake_to_caml(word):
# def caml_to_snake(word):
# def dict_snake_to_caml(data):
# def apply_func_dict(data, func):
# def str2bool(string):
# def download_gcs_file(url):
# def decode_gcs_url(url):
# def get_files(source, extensions=['yaml', 'yml', 'json']):
# def get_target_path(source_dir, target_dir, relative_path, mkdir=True):
# def fmt_traceback(exc):
# 
# Path: slo_generator/constants.py
# COLORED_OUTPUT = int(os.environ.get("COLORED_OUTPUT", "0"))
# 
# MIN_VALID_EVENTS = int(os.environ.get("MIN_VALID_EVENTS", "1"))
# 
# NO_DATA = -1
# 
# class Colors:
#     """Colors for console output."""
#     HEADER = '\033[95m'
#     OKBLUE = '\033[94m'
#     OKGREEN = '\033[92m'
#     WARNING = '\033[93m'
#     FAIL = '\033[91m'
#     ENDC = '\033[0m'
#     BOLD = '\033[1m'
#     UNDERLINE = '\033[4m'


# Path: slo_generator/report.py
import logging
from dataclasses import asdict, dataclass, fields, field
from slo_generator import utils
from slo_generator.constants import (COLORED_OUTPUT, MIN_VALID_EVENTS, NO_DATA,
                                     Colors)

            if name in lambdas:
                value = lambdas[name](value)
            setattr(self, name, value)

    @property
    def info(self):
        """Step information."""
        return f"{self.name :<32} | {self.error_budget_policy_step_name :<8}"

    def __str__(self):
        report = self.to_json()
        if not self.valid:
            errors_str = ' | '.join(self.errors)
            return f'{self.info} | {errors_str}'
        goal_per = self.goal * 100
        sli_per = round(self.sli_measurement * 100, 6)
        gap = round(self.gap * 100, 2)
        gap_str = str(gap)
        if gap >= 0:
            gap_str = f'+{gap}'

        sli_str = (f'SLI: {sli_per:<7} % | SLO: {goal_per} % | '
                   f'Gap: {gap_str:<6}%')
        result_str = ('BR: {error_budget_burn_rate:<2} / '
                      '{error_budget_burn_rate_threshold} | '
                      'Alert: {alert:<1} | Good: {good_events_count:<8} | '
                      'Bad: {bad_events_count:<8}').format_map(report)
        full_str = f'{self.info} | {sli_str} | {result_str}'
        if COLORED_OUTPUT == 1:
            if self.alert:

                full_str = Colors.FAIL + full_str + Colors.ENDC