prepare for markdown generation

This commit is contained in:
Sina Atalay 2024-02-13 18:51:39 +01:00
parent baf1027265
commit 56e1c76c2e
1 changed files with 63 additions and 31 deletions

View File

@ -23,6 +23,7 @@ import json
import re
import ssl
import pathlib
import copy
import pydantic
import pydantic_extra_types.phone_numbers as pydantic_phone_numbers
@ -126,7 +127,7 @@ class RenderCVBaseModel(pydantic.BaseModel):
unknown key is provided in the input file.
"""
model_config = pydantic.ConfigDict(extra="forbid")
model_config = pydantic.ConfigDict(extra="forbid", validation_error_cause=True)
# ======================================================================================
@ -703,7 +704,12 @@ def get_entry_and_section_type(
elif "institution" in entry and "area" in entry and "degree" in entry:
entry_type = "EducationEntry"
section_type = SectionWithEducationEntries
elif "title" in entry and "authors" in entry and "doi" in entry and "date" in entry:
elif (
"title" in entry
and "authors" in entry
and "doi" in entry
and "date" in entry
):
entry_type = "PublicationEntry"
section_type = SectionWithPublicationEntries
elif "name" in entry:
@ -777,7 +783,19 @@ def validate_section_input(
"entries": sections_input,
}
section_type.model_validate(test_section)
try:
section_type.model_validate(
test_section,
context={"section": "test"},
)
except pydantic.ValidationError as e:
new_error = ValueError(
"There are problems with the entries. RenderCV detected the entry type"
f" of this section to be {entry_type}! The problems are shown below.",
"", # this is the location of the error
"", # this is value of the error
)
raise new_error from e
return sections_input
@ -974,16 +992,17 @@ class RenderCVDataModel(RenderCVBaseModel):
# it is a built-in theme
return design
else:
theme_name: str = design["theme"] # type: ignore
# check if the theme name is valid:
if not design["theme"].isalpha(): # type: ignore
if not theme_name.isalpha():
raise ValueError(
"The custom theme name should contain only letters.",
"theme", # this is the location of the error
design["theme"], # this is value of the error # type: ignore
theme_name, # this is value of the error
)
# then it is a custom theme
custom_theme_folder = pathlib.Path(design["theme"]) # type: ignore
custom_theme_folder = pathlib.Path(theme_name)
# check if the custom theme folder exists:
if not custom_theme_folder.exists():
@ -991,12 +1010,11 @@ class RenderCVDataModel(RenderCVBaseModel):
f"The custom theme folder `{custom_theme_folder}` does not exist."
" It should be in the working directory as the input file.",
"", # this is the location of the error
design["theme"], # this is value of the error # type: ignore
theme_name, # this is value of the error
)
# check if all the necessary files are provided in the custom theme folder:
required_files = [
"__init__.py", # design's data model
"EducationEntry.j2.tex", # education entry template
"ExperienceEntry.j2.tex", # experience entry template
"NormalEntry.j2.tex", # normal entry template
@ -1016,28 +1034,39 @@ class RenderCVDataModel(RenderCVBaseModel):
f"You provided a custom theme, but the file `{file}` is not"
f" found in the folder `{custom_theme_folder}`.",
"", # this is the location of the error
design["theme"], # this is value of the error # type: ignore
theme_name, # this is value of the error
)
# import __init__.py file from the custom theme folder:
# import __init__.py file from the custom theme folder if it exists:
path_to_init_file = pathlib.Path(f"{theme_name}/__init__.py")
if path_to_init_file.exists():
spec = importlib.util.spec_from_file_location(
"", # this is somehow not required
pathlib.Path(f"{design["theme"]}/__init__.py"), # type: ignore
path_to_init_file,
)
if spec is None:
raise RuntimeError(
"This error shouldn't have been raised. Please open an issue on GitHub."
"This error shouldn't have been raised. Please open an issue on"
" GitHub."
)
else:
theme_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(theme_module) # type: ignore
ThemeDataModel = getattr(
theme_module, f"{design['theme'].title()}ThemeOptions" # type: ignore
theme_module, f"{theme_name.title()}ThemeOptions" # type: ignore
)
# initialize and validate the custom theme data model:
theme_data_model = ThemeDataModel(**design)
else:
# Then it means there is no __init__.py file in the custom theme folder.
# So, create a dummy data model and use that instead.
class ThemeOptionsAreNotProvided(RenderCVBaseModel):
theme: str = theme_name
theme_data_model = ThemeOptionsAreNotProvided(theme=theme_name)
return theme_data_model
@ -1226,17 +1255,19 @@ def convert_a_markdown_dictionary_to_a_latex_dictionary(
return dictionary
def read_input_file(file_path: pathlib.Path) -> RenderCVDataModel:
"""Read the input file and return an instance of RenderCVDataModel.
This function reads the input file, converts the markdown strings to $\\LaTeX$, and
validates the input file with the data models.
def read_input_file(
file_path: pathlib.Path,
) -> tuple[RenderCVDataModel, RenderCVDataModel]:
"""Read the input file and return two instances of RenderCVDataModel. The first
instance is the data model with LaTeX strings and the second instance is the data
model with markdown strings.
Args:
file_path (str): The path to the input file.
Returns:
str: The input file as a string.
tuple[RenderCVDataModel, RenderCVDataModel]: The data models with LaTeX and
markdown strings.
"""
# check if the file exists:
if not file_path.exists():
@ -1251,15 +1282,16 @@ def read_input_file(file_path: pathlib.Path) -> RenderCVDataModel:
)
file_content = file_path.read_text(encoding="utf-8")
parsed_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content)
original_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content)
parsed_dictionary = convert_a_markdown_dictionary_to_a_latex_dictionary(
parsed_dictionary
copy.deepcopy(original_dictionary)
)
# validate the parsed dictionary by creating an instance of RenderCVDataModel:
data = RenderCVDataModel(**parsed_dictionary) ## type: ignore
data_model_markdown = RenderCVDataModel(**original_dictionary)
data_model_latex = RenderCVDataModel(**parsed_dictionary)
return data
return data_model_latex, data_model_markdown
def get_a_sample_data_model(name: str) -> RenderCVDataModel: