This commit is contained in:
Sina Atalay 2024-02-18 19:13:29 +01:00
parent 9cdd7e5b0f
commit 4178c3e5e9
3 changed files with 66 additions and 57 deletions

View File

@ -54,7 +54,7 @@ def welcome():
print(table) print(table)
def warning(text): def warning(text: str):
"""Print a warning message to the terminal. """Print a warning message to the terminal.
Args: Args:
@ -63,7 +63,7 @@ def warning(text):
print(f"[bold yellow]{text}") print(f"[bold yellow]{text}")
def error(text, exception=None): def error(text: str, exception: Optional[Exception] = None):
"""Print an error message to the terminal. """Print an error message to the terminal.
Args: Args:
@ -80,7 +80,7 @@ def error(text, exception=None):
print(f"\n[bold red]{text}\n") print(f"\n[bold red]{text}\n")
def information(text): def information(text: str):
"""Print an information message to the terminal. """Print an information message to the terminal.
Args: Args:

View File

@ -53,7 +53,7 @@ def get_date_object(date: str | int) -> Date:
data models. data models.
Args: Args:
date_string (str): The date string to parse. date (str): The date string to parse.
Returns: Returns:
datetime.date: The parsed date. datetime.date: The parsed date.
""" """
@ -528,7 +528,7 @@ class PublicationEntry(RenderCVBaseModel):
"""Check if the DOI exists in the DOI System.""" """Check if the DOI exists in the DOI System."""
# see https://stackoverflow.com/a/60671292/18840665 for the explanation of the # see https://stackoverflow.com/a/60671292/18840665 for the explanation of the
# next line: # next line:
ssl._create_default_https_context = ssl._create_unverified_context ssl._create_default_https_context = ssl._create_unverified_context # type: ignore
doi_url = f"http://doi.org/{doi}" doi_url = f"http://doi.org/{doi}"
@ -550,7 +550,8 @@ class PublicationEntry(RenderCVBaseModel):
"""Return the date string of the publication.""" """Return the date string of the publication."""
if isinstance(self.date, int): if isinstance(self.date, int):
date_string = str(self.date) date_string = str(self.date)
elif isinstance(self.date, str): else:
# Then it is a string
date_object = get_date_object(self.date) date_object = get_date_object(self.date)
date_string = format_date(date_object) date_string = format_date(date_object)
@ -667,13 +668,9 @@ def get_entry_and_section_type(
"""Determine the entry and section type based on the entry. """Determine the entry and section type based on the entry.
Args: Args:
entry (dict[str, Any] | EducationEntry | ExperienceEntry | PublicationEntry | entry (dict[str, Any] | EducationEntry | ExperienceEntry | PublicationEntry | NormalEntry | OneLineEntry | str): The entry to determine the type.
NormalEntry | OneLineEntry | str): The entry to determine the type.
Returns: Returns:
tuple[str, Type[SectionWithTextEntries | SectionWithOneLineEntries | tuple[str, Type[SectionWithTextEntries | SectionWithOneLineEntries | SectionWithExperienceEntries | SectionWithEducationEntries | SectionWithPublicationEntries | SectionWithNormalEntries]]: The entry type and the section type.
SectionWithExperienceEntries | SectionWithEducationEntries |
SectionWithPublicationEntries | SectionWithNormalEntries]]: The entry type and the
section type.
""" """
if isinstance(entry, dict): if isinstance(entry, dict):
if "details" in entry: if "details" in entry:
@ -709,7 +706,7 @@ def get_entry_and_section_type(
elif isinstance(entry, PublicationEntry): elif isinstance(entry, PublicationEntry):
entry_type = "PublicationEntry" entry_type = "PublicationEntry"
section_type = SectionWithPublicationEntries section_type = SectionWithPublicationEntries
elif isinstance(entry, NormalEntry): elif isinstance(entry, NormalEntry): # type: ignore
entry_type = "NormalEntry" entry_type = "NormalEntry"
section_type = SectionWithNormalEntries section_type = SectionWithNormalEntries
else: else:
@ -892,7 +889,7 @@ class CurriculumVitae(RenderCVBaseModel):
title="Social Networks", title="Social Networks",
description="The social networks of the person.", description="The social networks of the person.",
) )
sections_input: dict[str, SectionInput] = pydantic.Field( sections_input: Optional[dict[str, SectionInput]] = pydantic.Field(
default=None, default=None,
title="Sections", title="Sections",
description="The sections of the CV.", description="The sections of the CV.",
@ -902,11 +899,11 @@ class CurriculumVitae(RenderCVBaseModel):
@functools.cached_property @functools.cached_property
def sections(self) -> list[Section]: def sections(self) -> list[Section]:
"""Return all the sections of the CV with their titles.""" """Return all the sections of the CV with their titles."""
sections = [] sections: list[Section] = []
if self.sections_input is not None: if self.sections_input is not None:
for title, section_or_entries in self.sections_input.items(): for title, section_or_entries in self.sections_input.items():
title = title.replace("_", " ").title() title = title.replace("_", " ").title()
if isinstance(section_or_entries, list):
entry_type, section_type = get_entry_and_section_type( entry_type, section_type = get_entry_and_section_type(
section_or_entries[0] section_or_entries[0]
) )
@ -918,12 +915,6 @@ class CurriculumVitae(RenderCVBaseModel):
) )
sections.append(section) sections.append(section)
else:
raise RuntimeError(
"This error shouldn't have been raised. Please open an"
" issue on GitHub."
)
return sections return sections
@ -981,6 +972,12 @@ class RenderCVDataModel(RenderCVBaseModel):
return rendercv_design_validator.validate_python(design) return rendercv_design_validator.validate_python(design)
else: else:
theme_name: str = design["theme"] # type: ignore theme_name: str = design["theme"] # type: ignore
if not isinstance(theme_name, str):
raise RuntimeError(
"This error shouldn't have been raised. Please open an issue on"
" GitHub."
)
# check if the theme name is valid: # check if the theme name is valid:
if not theme_name.isalpha(): if not theme_name.isalpha():
raise ValueError( raise ValueError(
@ -1093,7 +1090,7 @@ def read_input_file(
) )
file_content = file_path.read_text(encoding="utf-8") file_content = file_path.read_text(encoding="utf-8")
input_as_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content) input_as_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content) # type: ignore
# validate the parsed dictionary by creating an instance of RenderCVDataModel: # validate the parsed dictionary by creating an instance of RenderCVDataModel:
rendercv_data_model = RenderCVDataModel(**input_as_dictionary) rendercv_data_model = RenderCVDataModel(**input_as_dictionary)
@ -1224,7 +1221,7 @@ def get_a_sample_data_model(name: str = "John Doe") -> RenderCVDataModel:
SocialNetwork(network="LinkedIn", username="yourusername"), SocialNetwork(network="LinkedIn", username="yourusername"),
SocialNetwork(network="GitHub", username="yourusername"), SocialNetwork(network="GitHub", username="yourusername"),
], ],
sections=sections, sections=sections, # type: ignore
) )
design = ClassicThemeOptions(theme="classic", show_timespan_in=["Experience"]) design = ClassicThemeOptions(theme="classic", show_timespan_in=["Experience"])
@ -1232,7 +1229,7 @@ def get_a_sample_data_model(name: str = "John Doe") -> RenderCVDataModel:
return RenderCVDataModel(cv=cv, design=design) return RenderCVDataModel(cv=cv, design=design)
def generate_json_schema() -> dict: def generate_json_schema() -> dict[str, Any]:
"""Generate the JSON schema of RenderCV. """Generate the JSON schema of RenderCV.
JSON schema is generated for the users to make it easier for them to write the input JSON schema is generated for the users to make it easier for them to write the input
@ -1245,7 +1242,7 @@ def generate_json_schema() -> dict:
""" """
class RenderCVSchemaGenerator(pydantic.json_schema.GenerateJsonSchema): class RenderCVSchemaGenerator(pydantic.json_schema.GenerateJsonSchema):
def generate(self, schema, mode="validation"): def generate(self, schema, mode="validation"): # type: ignore
json_schema = super().generate(schema, mode=mode) json_schema = super().generate(schema, mode=mode)
# Basic information about the schema: # Basic information about the schema:
@ -1258,7 +1255,7 @@ def generate_json_schema() -> dict:
# Loop through $defs and remove docstring descriptions and fix optional # Loop through $defs and remove docstring descriptions and fix optional
# fields # fields
for key, value in json_schema["$defs"].items(): for _, value in json_schema["$defs"].items():
# Don't allow additional properties # Don't allow additional properties
value["additionalProperties"] = False value["additionalProperties"] = False

View File

@ -46,7 +46,7 @@ class TemplatedFile:
def template( def template(
self, self,
theme_name, theme_name: str,
template_name: Literal[ template_name: Literal[
"EducationEntry", "EducationEntry",
"ExperienceEntry", "ExperienceEntry",
@ -75,14 +75,7 @@ class TemplatedFile:
Args: Args:
template_name (str): The name of the template file. template_name (str): The name of the template file.
entry (Optional[ entry (Optional[dm.EducationEntry, dm.ExperienceEntry, dm.NormalEntry,dm.PublicationEntry, dm.OneLineEntry, str]): The data model of the entry.
dm.EducationEntry,
dm.ExperienceEntry,
dm.NormalEntry,
dm.PublicationEntry,
dm.OneLineEntry,
str
]): The data model of the entry.
section_title (Optional[str]): The title of the section. section_title (Optional[str]): The title of the section.
is_first_entry (Optional[bool]): Whether the entry is the first one in the is_first_entry (Optional[bool]): Whether the entry is the first one in the
section. section.
@ -144,7 +137,7 @@ class LaTeXFile(TemplatedFile):
) )
super().__init__(data_model, environment) super().__init__(data_model, environment)
def render_templates(self): def render_templates(self) -> tuple[str, str, list[tuple[str, list[str], str]]]:
"""Render and return all the templates for the $\\LaTeX$ file. """Render and return all the templates for the $\\LaTeX$ file.
Returns: Returns:
@ -154,12 +147,12 @@ class LaTeXFile(TemplatedFile):
# Template the preamble, header, and sections: # Template the preamble, header, and sections:
preamble = self.template("Preamble") preamble = self.template("Preamble")
header = self.template("Header") header = self.template("Header")
sections = [] sections: list[tuple[str, list[str], str]] = []
for section in self.cv.sections: for section in self.cv.sections:
section_beginning = self.template( section_beginning = self.template(
"SectionBeginning", section_title=section.title "SectionBeginning", section_title=section.title
) )
entries = [] entries: list[str] = []
for i, entry in enumerate(section.entries): for i, entry in enumerate(section.entries):
if i == 0: if i == 0:
is_first_entry = True is_first_entry = True
@ -203,7 +196,17 @@ class LaTeXFile(TemplatedFile):
section_title: Optional[str] = None, section_title: Optional[str] = None,
is_first_entry: Optional[bool] = None, is_first_entry: Optional[bool] = None,
) -> str: ) -> str:
"""Template one of the files in the `themes` directory.""" """Template one of the files in the `themes` directory.
Args:
template_name (str): The name of the template file.
entry (Optional[dm.EducationEntry, dm.ExperienceEntry, dm.NormalEntry,dm.PublicationEntry, dm.OneLineEntry, str]): The data model of the entry.
section_title (Optional[str]): The title of the section.
is_first_entry (Optional[bool]): Whether the entry is the first one in the section.
Returns:
str: The templated file.
"""
result = super().template( result = super().template(
self.design.theme, self.design.theme,
template_name, template_name,
@ -215,7 +218,11 @@ class LaTeXFile(TemplatedFile):
return result return result
def get_latex_code(self): def get_latex_code(self):
"""Get the $\\LaTeX$ code of the file.""" """Get the $\\LaTeX$ code of the file.
Returns:
str: The $\\LaTeX$ code.
"""
preamble, header, sections = self.render_templates() preamble, header, sections = self.render_templates()
return self.get_full_code( return self.get_full_code(
"main.j2.tex", "main.j2.tex",
@ -234,18 +241,13 @@ class MarkdownFile(TemplatedFile):
data model and Jinja2 templates. It inherits from the TemplatedFile class. Markdown data model and Jinja2 templates. It inherits from the TemplatedFile class. Markdown
files are generated to produce a PDF which can be copy-pasted to files are generated to produce a PDF which can be copy-pasted to
[Grammarly](https://app.grammarly.com/) for proofreading. [Grammarly](https://app.grammarly.com/) for proofreading.
Args:
data_model (dm.RenderCVDataModel): The data model.
environment (jinja2.Environment): The Jinja2 environment.
""" """
def render_templates(self): def render_templates(self):
"""Render and return all the templates for the Markdown file. """Render and return all the templates for the Markdown file.
Returns: Returns:
Tuple[str, List[Tuple[str, List[str]]]: The header and sections of the Tuple[str, List[Tuple[str, List[str]]]]: The header and sections of the Markdown file.
Markdown file.
""" """
# Template the header and sections: # Template the header and sections:
header = self.template("Header") header = self.template("Header")
@ -297,7 +299,17 @@ class MarkdownFile(TemplatedFile):
section_title: Optional[str] = None, section_title: Optional[str] = None,
is_first_entry: Optional[bool] = None, is_first_entry: Optional[bool] = None,
) -> str: ) -> str:
"""Template one of the files in the `themes` directory.""" """Template one of the files in the `themes` directory.
Args:
template_name (str): The name of the template file.
entry (Optional[dm.EducationEntry, dm.ExperienceEntry, dm.NormalEntry,dm.PublicationEntry, dm.OneLineEntry, str]): The data model of the entry.
section_title (Optional[str]): The title of the section.
is_first_entry (Optional[bool]): Whether the entry is the first one in the section.
Returns:
str: The templated file.
"""
result = super().template( result = super().template(
"markdown", "markdown",
template_name, template_name,
@ -729,7 +741,7 @@ def divide_length_by(length: str, divider: float) -> str:
def get_an_item_with_a_specific_attribute_value( def get_an_item_with_a_specific_attribute_value(
items: list[Any], attribute: str, value: Any items: Optional[list[Any]], attribute: str, value: Any
) -> Any: ) -> Any:
"""Get an item from a list of items with a specific attribute value. """Get an item from a list of items with a specific attribute value.