diff --git a/rendercv/user_communicator.py b/rendercv/user_communicator.py index af1b791..241613f 100644 --- a/rendercv/user_communicator.py +++ b/rendercv/user_communicator.py @@ -1,5 +1,5 @@ -import time from typing import Callable +import re import rich import rich.console @@ -8,7 +8,10 @@ import rich.live import rich.table import rich.text import rich.progress - +import pydantic_core +import pydantic +import ruamel.yaml +import ruamel.yaml.parser console = rich.console.Console() @@ -17,7 +20,7 @@ def welcome(): """Print a welcome message to the terminal.""" table = rich.table.Table( title="\nWelcome to [bold]Render[dodger_blue3]CV[/dodger_blue3][/bold]!", - title_justify="center", + title_justify="left", ) table.add_column("Title", style="magenta") @@ -28,17 +31,17 @@ def welcome(): table.add_row("Bug reports", "https://github.com/sinaatalay/rendercv/issues/") table.add_row("Feature requests", "https://github.com/sinaatalay/rendercv/issues/") - console.print(table, justify="center") + console.print(table) def warning(text): """Print a warning message to the terminal.""" - console.print(f"[bold yellow]{text}[/bold yellow]") + console.print(f"[bold yellow]{text}") -def error(text): +def error(text, exception=None): """Print an error message to the terminal.""" - console.print(f"[bold red]{text}[/bold red]") + console.print(f"\n[bold red]{text}[/bold red]\n\n[orange4]{exception}") def information(text): @@ -46,11 +49,63 @@ def information(text): console.print(f"[bold green]{text}") +def handle_validation_error(exception: pydantic.ValidationError): + error_dictionary: dict[str, str] = { + r"Value error, Invalid isoformat string: '(.*)'": ( + "This is not a valid date! Please use either YYYY-MM-DD, YYYY-MM, or" + " YYYY format." + ), + r"URL scheme should be 'http' or 'https'": "This is not a valid URL!", + r"Field( )required": "This field is required!", + r"value is not a valid phone number": "This is not a valid phone number!", + r"String should match pattern '\\d\+\\\.\?\\d\* \*\(cm\|in\|pt\|mm\|ex\|em\)'": ( + 'This is not a valid dimension! For example, use "2 cm" or "3.5 in"' + ), + } + new_errors: list[pydantic_core.ErrorDetails] = [] + for error_object in exception.errors(): + for key, value in error_dictionary.items(): + match = re.match(key, error_object["msg"]) + if match: + error_object["msg"] = value + try: + error_object["input"] = match.group(1) + except IndexError: + pass + + new_errors.append(error_object) + + table = rich.table.Table( + title="[bold red]\nThere are some errors in the input file!\n", + title_justify="left", + show_lines=True, + ) + table.add_column("Location", style="cyan", no_wrap=True) + table.add_column("Input Value", style="magenta") + table.add_column("Error Message", style="orange4") + + for error_object in new_errors: + table.add_row( + ".".join([str(i) for i in error_object["loc"]]), + error_object["input"], + error_object["msg"], + ) + + console.print(table) + + def handle_exceptions(function: Callable) -> Callable: """ """ def wrapper(*args, **kwargs): - return function(*args, **kwargs) + try: + function(*args, **kwargs) + except pydantic.ValidationError as e: + handle_validation_error(e) + except ruamel.yaml.YAMLError as e: + error("There is a YAML error in the input file!", e) + except RuntimeError as e: + error("An error occurred:", e) return wrapper @@ -63,7 +118,7 @@ class LiveProgressReporter(rich.live.Live): number_of_steps (int): The number of steps to be finished. """ - def __init__(self, number_of_steps: int): + def __init__(self, number_of_steps: int, end_message: str = "Your CV is rendered!"): class TimeElapsedColumn(rich.progress.ProgressColumn): def render(self, task: "rich.progress.Task") -> rich.text.Text: elapsed = task.finished_time if task.finished else task.elapsed @@ -89,6 +144,7 @@ class LiveProgressReporter(rich.live.Live): self.overall_task_id = self.overall_progress.add_task("", total=number_of_steps) self.number_of_steps = number_of_steps + self.end_message = end_message self.current_step = 0 self.overall_progress.update( self.overall_task_id, @@ -133,5 +189,5 @@ class LiveProgressReporter(rich.live.Live): """End the live progress reporting.""" self.overall_progress.update( self.overall_task_id, - description="[bold green]Your CV is rendered!", + description=f"[bold green]{self.end_message}", )