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 re
import ssl import ssl
import pathlib import pathlib
import copy
import pydantic import pydantic
import pydantic_extra_types.phone_numbers as pydantic_phone_numbers 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. 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: elif "institution" in entry and "area" in entry and "degree" in entry:
entry_type = "EducationEntry" entry_type = "EducationEntry"
section_type = SectionWithEducationEntries 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" entry_type = "PublicationEntry"
section_type = SectionWithPublicationEntries section_type = SectionWithPublicationEntries
elif "name" in entry: elif "name" in entry:
@ -777,7 +783,19 @@ def validate_section_input(
"entries": sections_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 return sections_input
@ -974,16 +992,17 @@ class RenderCVDataModel(RenderCVBaseModel):
# it is a built-in theme # it is a built-in theme
return design return design
else: else:
theme_name: str = design["theme"] # type: ignore
# check if the theme name is valid: # check if the theme name is valid:
if not design["theme"].isalpha(): # type: ignore if not theme_name.isalpha():
raise ValueError( raise ValueError(
"The custom theme name should contain only letters.", "The custom theme name should contain only letters.",
"theme", # this is the location of the error "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 # 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: # check if the custom theme folder exists:
if not 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." f"The custom theme folder `{custom_theme_folder}` does not exist."
" It should be in the working directory as the input file.", " It should be in the working directory as the input file.",
"", # this is the location of the error "", # 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: # check if all the necessary files are provided in the custom theme folder:
required_files = [ required_files = [
"__init__.py", # design's data model
"EducationEntry.j2.tex", # education entry template "EducationEntry.j2.tex", # education entry template
"ExperienceEntry.j2.tex", # experience entry template "ExperienceEntry.j2.tex", # experience entry template
"NormalEntry.j2.tex", # normal 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"You provided a custom theme, but the file `{file}` is not"
f" found in the folder `{custom_theme_folder}`.", f" found in the folder `{custom_theme_folder}`.",
"", # this is the location of the error "", # 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:
spec = importlib.util.spec_from_file_location( path_to_init_file = pathlib.Path(f"{theme_name}/__init__.py")
"", # this is somehow not required
pathlib.Path(f"{design["theme"]}/__init__.py"), # type: ignore if path_to_init_file.exists():
) spec = importlib.util.spec_from_file_location(
if spec is None: "", # this is somehow not required
raise RuntimeError( path_to_init_file,
"This error shouldn't have been raised. Please open an issue on GitHub."
) )
else: if spec is None:
raise RuntimeError(
"This error shouldn't have been raised. Please open an issue on"
" GitHub."
)
theme_module = importlib.util.module_from_spec(spec) theme_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(theme_module) # type: ignore spec.loader.exec_module(theme_module) # type: ignore
ThemeDataModel = getattr( 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: # initialize and validate the custom theme data model:
theme_data_model = ThemeDataModel(**design) 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 return theme_data_model
@ -1226,17 +1255,19 @@ def convert_a_markdown_dictionary_to_a_latex_dictionary(
return dictionary return dictionary
def read_input_file(file_path: pathlib.Path) -> RenderCVDataModel: def read_input_file(
"""Read the input file and return an instance of RenderCVDataModel. file_path: pathlib.Path,
) -> tuple[RenderCVDataModel, RenderCVDataModel]:
This function reads the input file, converts the markdown strings to $\\LaTeX$, and """Read the input file and return two instances of RenderCVDataModel. The first
validates the input file with the data models. instance is the data model with LaTeX strings and the second instance is the data
model with markdown strings.
Args: Args:
file_path (str): The path to the input file. file_path (str): The path to the input file.
Returns: Returns:
str: The input file as a string. tuple[RenderCVDataModel, RenderCVDataModel]: The data models with LaTeX and
markdown strings.
""" """
# check if the file exists: # check if the file exists:
if not file_path.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") 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 = convert_a_markdown_dictionary_to_a_latex_dictionary(
parsed_dictionary copy.deepcopy(original_dictionary)
) )
# validate the parsed dictionary by creating an instance of RenderCVDataModel: # 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: def get_a_sample_data_model(name: str) -> RenderCVDataModel: