Skip to content

Commit

Permalink
Merge pull request #166 from akib1689/main
Browse files Browse the repository at this point in the history
feat: add the `rendercv_settings` block
  • Loading branch information
sinaatalay authored Sep 7, 2024
2 parents cc2ce60 + 9a1c74a commit 3a5d09b
Show file tree
Hide file tree
Showing 15 changed files with 478 additions and 52 deletions.
Binary file modified docs/assets/images/classic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/images/sb2nov.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/reference/data/models/rendercv_settings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `rendercv.data.models.rendercv_settings`

::: rendercv.data.models.rendercv_settings
27 changes: 25 additions & 2 deletions docs/user_guide/structure_of_the_yaml_input_file.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Structure of the YAML Input File

RenderCV's input file consists of three parts: `cv`, `design`, and `locale_catalog`.
RenderCV's input file consists of four parts: `cv`, `design`, `locale_catalog` and `rendercv_settings`.

```yaml title="Your_Name_CV.yaml"
cv:
Expand All @@ -15,11 +15,16 @@ locale_catalog:
...
TRANSLATIONS TO YOUR LANGUAGE
...
rendercv_settings:
...
RENDERCV SETTINGS
...
```
- The `cv` field is mandatory. It contains the **content of the CV**.
- The `design` field is optional. It contains the **design options of the CV**. If you don't provide a `design` field, RenderCV will use the default design options with the `classic` theme.
- The `locale_catalog` field is optional. You can provide translations for some of the strings used in the CV, for example, month abbreviations. RenderCV will use English strings if you don't provide a `locale_catalog` field.
- The `rendercv_settings` field is optional. It contains the **settings of RenderCV**. If you don't provide a `rendercv_settings` field, RenderCV will use the default settings.

!!! tip
To maximize your productivity while editing the input YAML file, set up RenderCV's JSON Schema in your IDE. It will validate your inputs on the fly and give auto-complete suggestions.
Expand Down Expand Up @@ -355,4 +360,22 @@ locale_catalog:
```

1. The available phone number formats are: `national`, `international`, and `E164`.
2. The `MONTH_ABBREVIATION` and `YEAR` are placeholders. The available placeholders are: `FULL_MONTH_NAME`, `MONTH_ABBREVIATION`, `MONTH`, `MONTH_IN_TWO_DIGITS`, `YEAR`, and `YEAR_IN_TWO_DIGITS`.
2. The `MONTH_ABBREVIATION` and `YEAR` are placeholders. The available placeholders are: `FULL_MONTH_NAME`, `MONTH_ABBREVIATION`, `MONTH`, `MONTH_IN_TWO_DIGITS`, `YEAR`, and `YEAR_IN_TWO_DIGITS`.

## "`rendercv_settings`" field

The `rendercv_settings` field contains the settings of RenderCV. This feature is what makes RenderCV a flexible tool. You can change the output folder name, the paths of the output files, and disable the generation of some output files directly from within the code (You can also use the [cli arguments](cli.md) which ever is convinient for you). Below is an example of the `rendercv_settings` field:

```yaml
rendercv_settings:
output_folder_name: output # default value is 'rendercv_output'
pdf_path: cv.pdf # default value is 'None'
latex_path: cv.tex # default value is 'None'
html_path: cv.html # default value is 'None'
markdown_path: cv.md # default value is 'None'
dont_generate_html: false # default value is 'false'
dont_generate_markdown: false # default value is 'false'
dont_generate_png: false # default value is 'false'
```

All this fields are optional. If you don't provide a `rendercv_settings` field, RenderCV will use the default settings.
Binary file modified examples/John_Doe_ClassicTheme_CV.pdf
Binary file not shown.
Binary file modified examples/John_Doe_EngineeringresumesTheme_CV.pdf
Binary file not shown.
Binary file modified examples/John_Doe_ModerncvTheme_CV.pdf
Binary file not shown.
Binary file modified examples/John_Doe_Sb2novTheme_CV.pdf
Binary file not shown.
1 change: 1 addition & 0 deletions mkdocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ nav:
- curriculum_vitae.py: reference/data/models/curriculum_vitae.md
- design.py: reference/data/models/design.md
- locale_catalog.py: reference/data/models/locale_catalog.md
- rendercv_settings.py: reference/data/models/rendercv_settings.md
- rendercv_data_model.py: reference/data/models/rendercv_data_model.md
- generator.py: reference/data/generator.md
- reader.py: reference/data/reader.md
Expand Down
131 changes: 84 additions & 47 deletions rendercv/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import os
import pathlib
from typing import Annotated, Literal, Optional
from typing import Annotated, Optional

import typer
from rich import print
Expand Down Expand Up @@ -139,59 +139,73 @@ def cli_command_render(

# Get paths:
input_file_path: pathlib.Path = utilities.string_to_file_path(
input_file_name
string=input_file_name
) # type: ignore
output_directory = pathlib.Path.cwd() / output_folder_name

paths: dict[
Literal["latex", "pdf", "markdown", "html", "png"], Optional[pathlib.Path]
] = {
"latex": utilities.string_to_file_path(latex_path),
"pdf": utilities.string_to_file_path(pdf_path),
"markdown": utilities.string_to_file_path(markdown_path),
"html": utilities.string_to_file_path(html_path),
"png": utilities.string_to_file_path(png_path),

# dictionary for command line arguments:
cli_args = {
"use_local_latex_command": use_local_latex_command,
"output_folder_name": output_folder_name,
"latex_path": latex_path,
"pdf_path": pdf_path,
"markdown_path": markdown_path,
"html_path": html_path,
"png_path": png_path,
"dont_generate_png": dont_generate_png,
"dont_generate_markdown": dont_generate_markdown,
"dont_generate_html": dont_generate_html,
}

# keep the current working directory:
working_directory = pathlib.Path.cwd()

# 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)

# compute the number of steps
# 1. read and validate the input file
# 0. read the input file
# 1. validate the input file
# 2. generate the LaTeX file
# 3. render the LaTeX file to a PDF
# 4. render PNG files from the PDF
# 5. generate the Markdown file
# 6. render the Markdown file to a HTML (for Grammarly)

data_as_a_dict = data.read_a_yaml_file(input_file_path)

# update the data if there are extra override arguments:
if extra_data_model_override_argumets:
key_and_values = dict()
key_and_values = utilities.parse_render_command_override_arguments(
extra_data_model_override_argumets
)
data_as_a_dict = utilities.set_or_update_values(data_as_a_dict, key_and_values)

data_as_a_dict = utilities.parse_render_settings(data_as_a_dict, cli_args)

rendercv_settings = data_as_a_dict.get("rendercv_settings", dict())

# Calculate the number of steps:
number_of_steps = 6
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
if rendercv_settings.get("dont_generate_png", False):
number_of_steps -= 1
if rendercv_settings.get("dont_generate_markdown", False):
number_of_steps -= 2
else:
if dont_generate_html:
number_of_steps = number_of_steps - 1

with printer.LiveProgressReporter(number_of_steps) as progress:
progress.start_a_step("Reading and validating the input file")
data_as_a_dict = data.read_a_yaml_file(input_file_path)

# update the data if there are extra override arguments:
if extra_data_model_override_argumets:
key_and_values = dict()
key_and_values = utilities.parse_render_command_override_arguments(
extra_data_model_override_argumets
)
data_as_a_dict = utilities.set_or_update_values(
data_as_a_dict, key_and_values
)
if rendercv_settings.get("dont_generate_html", False):
number_of_steps -= 1

with printer.LiveProgressReporter(number_of_steps=number_of_steps) as progress:
progress.start_a_step("Validating the input file")

data_model = data.validate_input_dictionary_and_return_the_data_model(
data_as_a_dict
)

rendercv_settings = data_model.rendercv_settings
output_directory = working_directory / rendercv_settings.output_folder_name

progress.finish_the_current_step()

progress.start_a_step("Generating the LaTeX file")
Expand All @@ -200,47 +214,70 @@ def cli_command_render(
data_model, output_directory
)
)
if paths["latex"]:
utilities.copy_files(latex_file_path_in_output_folder, paths["latex"])
if rendercv_settings.latex_path:
utilities.copy_files(
latex_file_path_in_output_folder,
utilities.string_to_file_path(
parent=working_directory, string=rendercv_settings.latex_path
),
)
progress.finish_the_current_step()

progress.start_a_step("Rendering the LaTeX file to a PDF")
pdf_file_path_in_output_folder = renderer.render_a_pdf_from_latex(
latex_file_path_in_output_folder, use_local_latex_command
)
if paths["pdf"]:
utilities.copy_files(pdf_file_path_in_output_folder, paths["pdf"])
if rendercv_settings.pdf_path:
utilities.copy_files(
pdf_file_path_in_output_folder,
utilities.string_to_file_path(
parent=working_directory, string=rendercv_settings.pdf_path
),
)
progress.finish_the_current_step()

if not dont_generate_png:
if not rendercv_settings.dont_generate_png:
progress.start_a_step("Rendering PNG files from the PDF")
png_file_paths_in_output_folder = renderer.render_pngs_from_pdf(
pdf_file_path_in_output_folder
)
if paths["png"]:
utilities.copy_files(png_file_paths_in_output_folder, paths["png"])
if rendercv_settings.png_path:
utilities.copy_files(
png_file_paths_in_output_folder,
utilities.string_to_file_path(
parent=working_directory, string=rendercv_settings.png_path
),
)
progress.finish_the_current_step()

if not dont_generate_markdown:
if not rendercv_settings.dont_generate_markdown:
progress.start_a_step("Generating the Markdown file")
markdown_file_path_in_output_folder = renderer.create_a_markdown_file(
data_model, output_directory
)
if paths["markdown"]:
if rendercv_settings.markdown_path:
utilities.copy_files(
markdown_file_path_in_output_folder, paths["markdown"]
markdown_file_path_in_output_folder,
utilities.string_to_file_path(
parent=working_directory, string=rendercv_settings.markdown_path
),
)
progress.finish_the_current_step()

if not dont_generate_html:
if not rendercv_settings.dont_generate_html:
progress.start_a_step(
"Rendering the Markdown file to a HTML (for Grammarly)"
)
html_file_path_in_output_folder = renderer.render_an_html_from_markdown(
markdown_file_path_in_output_folder
)
if paths["html"]:
utilities.copy_files(html_file_path_in_output_folder, paths["html"])
if rendercv_settings.html_path:
utilities.copy_files(
html_file_path_in_output_folder,
utilities.string_to_file_path(
parent=working_directory, string=rendercv_settings.html_path
),
)
progress.finish_the_current_step()


Expand Down
95 changes: 93 additions & 2 deletions rendercv/cli/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
The `rendercv.cli.utilities` module contains utility functions that are required by CLI.
"""

import inspect
import json
import pathlib
import re
Expand All @@ -12,18 +13,26 @@
import typer


def string_to_file_path(string: Optional[str]) -> Optional[pathlib.Path]:
def string_to_file_path(
string: Optional[str], parent: Optional[str] = None
) -> Optional[pathlib.Path]:
"""Convert a string to a pathlib.Path object. If the string is None, then return
None.
Args:
parent (Optional[str]): The parent directory of the file path.
string (str): The string to be converted to a pathlib.Path object.
Returns:
pathlib.Path: The pathlib.Path object.
"""
# check if the string is not None:
if string is not None:
return pathlib.Path(string).absolute()
# check if the parent is not None:
if parent is not None:
return pathlib.Path(parent).absolute() / string
else:
return pathlib.Path(string).absolute()
else:
return None

Expand Down Expand Up @@ -260,3 +269,85 @@ def parse_render_command_override_arguments(
key_and_values[key] = value

return key_and_values


def update_render_settings(
dictionary: dict,
arguments: dict[str, str],
arguments_default_values: dict[str, str],
) -> dict[str, str]:
"""Build the RenderCV settings dictionary by combining the dictionary and the
command line arguments.
Args:
dictionary (dict): The dictionary to be combined with the command line
arguments.
arguments (dict[str, str]): The command line arguments.
arguments_default_values (dict[str, str]): The default values of
the command line arguments.
Returns:
dict[str, str]: The combined dictionary.
"""

# if the dictionary is empty, initialize it from the default values:
if not dictionary:
dictionary = arguments_default_values

# Combine the dictionary and the command line arguments if the values are not None:
for key, value in arguments.items():
# check if the key is present in the both
# command line arguments and the default values:
if key in arguments_default_values:
default_value = arguments_default_values[key]
if value != default_value:
dictionary = set_or_update_a_value(dictionary, key, str(value))
else:
# The key is not present in the default values, set the value:
# throw an error reporting this
raise ValueError(
f"The key ({key}) is not present in the default values of the command"
" line arguments!"
)
return dictionary


def parse_render_settings(
dictionary: dict,
cli_arguments: dict[str, str],
) -> dict[str, str]:
"""Build the RenderCV settings dictionary by combining the dictionary and the
command line arguments.
Args:
dictionary (dict): The dictionary to be combined with the command line
arguments.
cli_arguments (dict[str, str]): The command line arguments.
Returns:
dict[str, str]: The combined dictionary.
"""

# Use inspect to get the default values of the arguments:
from .commands import cli_command_render

sig = inspect.signature(cli_command_render)
cli_arguments_default = {
k: v.default
for k, v in sig.parameters.items()
if v.default is not inspect.Parameter.empty
}

# update the data of the rendercv settings:
rendercv_settings = dictionary.get("rendercv_settings", dict())
if rendercv_settings is None:
rendercv_settings = dict()

rendercv_settings = update_render_settings(
rendercv_settings, cli_arguments, cli_arguments_default
)

# update the data model with the rendercv settings:
dictionary["rendercv_settings"] = rendercv_settings

return dictionary
Loading

0 comments on commit 3a5d09b

Please sign in to comment.