From 84b8511f896f2ccf711ad4364d17f04da9ac989c Mon Sep 17 00:00:00 2001 From: Sina Atalay Date: Fri, 5 Jul 2024 01:07:15 +0300 Subject: [PATCH] cli: simplify commands.py --- rendercv/cli/commands.py | 64 ++++++++++++++++++--------------------- rendercv/cli/printer.py | 8 ++++- rendercv/cli/utilities.py | 54 +++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 36 deletions(-) diff --git a/rendercv/cli/commands.py b/rendercv/cli/commands.py index 88cf3d4f..6b2f1d0c 100644 --- a/rendercv/cli/commands.py +++ b/rendercv/cli/commands.py @@ -6,7 +6,7 @@ commands of RenderCV. import os import pathlib import shutil -from typing import Annotated, Optional +from typing import Annotated, Optional, Literal import pydantic import typer @@ -140,9 +140,23 @@ def cli_command_render( """Render a CV from a YAML input file.""" printer.welcome() - input_file_path = pathlib.Path(input_file_name).absolute() + # Get paths: + input_file_path = utilities.string_to_file_path(input_file_name) output_directory = pathlib.Path.cwd() / output_folder_name + paths: dict[ + Literal["latex", "pdf", "markdown", "html", "png"], Optional[pathlib.Path] + ] = { + "latex": latex_path, + "pdf": pdf_path, + "markdown": markdown_path, + "html": html_path, + "png": png_path, + } + for file_type, path in paths.items(): + if path: + paths[file_type] = utilities.string_to_file_path(path) + # change the current working directory to the input file's directory (because # the template overrides are looked up in the current working directory): os.chdir(input_file_path.parent) @@ -158,6 +172,7 @@ def cli_command_render( if dont_generate_png: number_of_steps = number_of_steps - 1 if dont_generate_markdown: + # if the Markdown file is not generated, then the HTML file is not generated number_of_steps = number_of_steps - 2 else: if dont_generate_html: @@ -168,23 +183,12 @@ def cli_command_render( data_model = data.read_input_file(input_file_path) # update the data model if there are extra arguments: - key_and_values = dict() - if extra_data_model_override_argumets: - key_and_values = printer.parse_render_command_override_arguments( + key_and_values = dict() + key_and_values = utilities.parse_render_command_override_arguments( extra_data_model_override_argumets ) - for key, value in key_and_values.items(): - try: - # set the key (for example, cv.sections.education.0.institution) to - # the value - data_model = utilities.set_or_update_a_value(data_model, key, value) - except pydantic.ValidationError as e: - raise e - except (ValueError, KeyError, IndexError, AttributeError): - raise ValueError( - f'The key "{key}" does not exist in the data model!' - ) + data_model = utilities.set_or_update_values(data_model, key_and_values) progress.finish_the_current_step() @@ -194,35 +198,25 @@ def cli_command_render( data_model, output_directory ) ) - if latex_path: - shutil.copy2(latex_file_path_in_output_folder, latex_path) + if paths["latex"]: + utilities.copy_files(latex_file_path_in_output_folder, paths["latex"]) progress.finish_the_current_step() progress.start_a_step("Rendering the LaTeX file to a PDF") pdf_file_path_in_output_folder = renderer.render_pdf_from_latex( latex_file_path_in_output_folder, use_local_latex_command ) - if pdf_path: - shutil.copy2(pdf_file_path_in_output_folder, pdf_path) + if paths["pdf"]: + utilities.copy_files(pdf_file_path_in_output_folder, paths["pdf"]) progress.finish_the_current_step() if not dont_generate_png: progress.start_a_step("Rendering PNG files from the PDF") - png_file_paths_in_output_folder = renderer.render_a_markdown_file( + png_file_paths_in_output_folder = renderer.render_pngs_from_pdf( pdf_file_path_in_output_folder ) - if png_path: - if len(png_file_paths_in_output_folder) == 1: - shutil.copy2(png_file_paths_in_output_folder[0], png_path) - else: - for i, png_file_path in enumerate(png_file_paths_in_output_folder): - # append the page number to the file name - page_number = i + 1 - png_path_with_page_number = ( - pathlib.Path(png_path).parent - / f"{pathlib.Path(png_path).stem}_{page_number}.png" - ) - shutil.copy2(png_file_path, png_path_with_page_number) + if paths["png"]: + utilities.copy_files(png_file_paths_in_output_folder, paths["png"]) progress.finish_the_current_step() if not dont_generate_markdown: @@ -231,7 +225,7 @@ def cli_command_render( data_model, output_directory ) if markdown_path: - shutil.copy2(markdown_file_path_in_output_folder, markdown_path) + utilities.copy_files(markdown_file_path_in_output_folder, markdown_path) progress.finish_the_current_step() if not dont_generate_html: @@ -242,7 +236,7 @@ def cli_command_render( markdown_file_path_in_output_folder ) if html_path: - shutil.copy2(html_file_path_in_output_folder, html_path) + utilities.copy_files(html_file_path_in_output_folder, html_path) progress.finish_the_current_step() diff --git a/rendercv/cli/printer.py b/rendercv/cli/printer.py index 13ba3b6b..434aacaa 100644 --- a/rendercv/cli/printer.py +++ b/rendercv/cli/printer.py @@ -25,6 +25,8 @@ class LiveProgressReporter(rich.live.Live): Args: number_of_steps (int): The number of steps to be finished. + end_message (str, optional): The message to be printed when the progress is + finished. Defaults to "Your CV is rendered!". """ def __init__(self, number_of_steps: int, end_message: str = "Your CV is rendered!"): @@ -68,7 +70,11 @@ class LiveProgressReporter(rich.live.Live): return self def start_a_step(self, step_name: str): - """Start a step and update the progress bars.""" + """Start a step and update the progress bars. + + Args: + step_name (str): The name of the step. + """ self.current_step_name = step_name self.current_step_id = self.step_progress.add_task( f"{self.current_step_name} has started." diff --git a/rendercv/cli/utilities.py b/rendercv/cli/utilities.py index a670ad6a..f78893aa 100644 --- a/rendercv/cli/utilities.py +++ b/rendercv/cli/utilities.py @@ -13,6 +13,17 @@ import typer import pydantic +def string_to_file_path(string: str) -> pathlib.Path: + """Convert a string to a pathlib.Path object. + + Args: + string (str): The string to be converted to a pathlib.Path object. + Returns: + pathlib.Path: The pathlib.Path object. + """ + return pathlib.Path(string).absolute() + + def set_or_update_a_value( data_model: pydantic.BaseModel | dict | list, key: str, @@ -86,6 +97,49 @@ def set_or_update_a_value( set_or_update_a_value(data_model, key, value, sub_model) +def set_or_update_values( + data_model: pydantic.BaseModel, + key_and_values: dict[str, str], +) -> pydantic.BaseModel: + """Set or update values in a data model for specific keys. It uses the + `set_or_update_a_value` function to set or update the values. + + Args: + data_model (pydantic.BaseModel): The data model to set or update the values. + key_and_values (dict[str, str]): The key and value pairs to set or update. + """ + for key, value in key_and_values.items(): + try: + data_model = set_or_update_a_value(data_model, key_and_values) + except pydantic.ValidationError as e: + raise e + except (ValueError, KeyError, IndexError, AttributeError): + raise ValueError(f'The key "{key}" does not exist in the data model!') + + return data_model + + +def copy_files(paths: list[pathlib.Path], new_path: pathlib.Path): + """Copy files to the given path. If there are multiple files, then rename the new + path by adding a number to the end of the path. + + Args: + paths (list[pathlib.Path]): The paths of the files to be copied. + new_path (pathlib.Path): The path to copy the files to. + """ + if len(paths) == 1: + shutil.copy2(paths[0], new_path) + else: + for i, file_path in enumerate(paths): + # append a number to the end of the path: + number = i + 1 + png_path_with_page_number = ( + pathlib.Path(new_path).parent + / f"{pathlib.Path(new_path).stem}_{number}.png" + ) + shutil.copy2(file_path, png_path_with_page_number) + + def get_latest_version_number_from_pypi() -> Optional[str]: """Get the latest version number of RenderCV from PyPI.