improve data models

This commit is contained in:
Sina Atalay 2024-02-07 19:16:55 +01:00
parent da9159d8ec
commit ed93a781f5
1 changed files with 84 additions and 50 deletions

View File

@ -178,9 +178,9 @@ class EntryBase(RenderCVBaseModel):
url: Optional[pydantic.HttpUrl] = None url: Optional[pydantic.HttpUrl] = None
url_text_input: Optional[str] = pydantic.Field(default=None, alias="url_text") url_text_input: Optional[str] = pydantic.Field(default=None, alias="url_text")
@pydantic.model_validator(mode="after") @pydantic.model_validator(mode="after") # type: ignore
@classmethod @classmethod
def check_dates(cls, model): def check_dates(cls, model: "EntryBase") -> "EntryBase":
""" """
Check if the dates are provided correctly and do the necessary adjustments. Check if the dates are provided correctly and do the necessary adjustments.
""" """
@ -236,7 +236,7 @@ class EntryBase(RenderCVBaseModel):
return model return model
@pydantic.computed_field # @pydantic.computed_field
@cached_property @cached_property
def date_string(self) -> str: def date_string(self) -> str:
""" """
@ -285,7 +285,7 @@ class EntryBase(RenderCVBaseModel):
return date_string return date_string
@pydantic.computed_field # @pydantic.computed_field
@cached_property @cached_property
def time_span_string(self) -> str: def time_span_string(self) -> str:
""" """
@ -356,7 +356,7 @@ class EntryBase(RenderCVBaseModel):
return time_span_string return time_span_string
@pydantic.computed_field # @pydantic.computed_field
@cached_property @cached_property
def url_text(self) -> Optional[str]: def url_text(self) -> Optional[str]:
""" """
@ -497,13 +497,13 @@ class PublicationEntry(RenderCVBaseModel):
return doi return doi
@pydantic.computed_field # @pydantic.computed_field
@cached_property @cached_property
def doi_url(self) -> str: def doi_url(self) -> str:
"""Return the URL of the DOI.""" """Return the URL of the DOI."""
return f"https://doi.org/{self.doi}" return f"https://doi.org/{self.doi}"
@pydantic.computed_field # @pydantic.computed_field
@cached_property @cached_property
def date_string(self) -> str: def date_string(self) -> str:
"""Return the date string of the publication.""" """Return the date string of the publication."""
@ -661,9 +661,9 @@ class SocialNetwork(RenderCVBaseModel):
description="The username of the social network. The link will be generated.", description="The username of the social network. The link will be generated.",
) )
@pydantic.model_validator(mode="after") @pydantic.model_validator(mode="after") # type: ignore
@classmethod @classmethod
def check_networks(cls, model): def check_networks(cls, model: "SocialNetwork") -> "SocialNetwork":
"""Check if the `SocialNetwork` is provided correctly.""" """Check if the `SocialNetwork` is provided correctly."""
if model.network == "Mastodon": if model.network == "Mastodon":
if not model.username.startswith("@"): if not model.username.startswith("@"):
@ -679,7 +679,7 @@ class SocialNetwork(RenderCVBaseModel):
return model return model
@pydantic.computed_field # @pydantic.computed_field
@cached_property @cached_property
def url(self) -> pydantic.HttpUrl: def url(self) -> pydantic.HttpUrl:
"""Return the URL of the social network.""" """Return the URL of the social network."""
@ -788,7 +788,7 @@ class CurriculumVitae(RenderCVBaseModel):
return sections_input return sections_input
@pydantic.computed_field # @pydantic.computed_field
@cached_property @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."""
@ -975,7 +975,75 @@ def markdown_to_latex(markdown_string: str) -> str:
return latex_string return latex_string
# @time_the_event_below("Reading and validating the input file") def convert_a_markdown_dictionary_to_a_latex_dictionary(
dictionary: dict[str, Any],
) -> dict[str, Any]:
"""
Recursively loop through a dictionary and convert all the markdown strings (keys and
values) to LaTeX. Also, escape special LaTeX characters in the keys and values.
Example:
```python
convert_a_markdown_dictionary_to_a_latex_dictionary(
{
"key1": "This is a **bold** text with an [*italic link*](https://google.com).",
"key2": "This is a **bold** text with an [*italic link*](https://google.com).",
"**key3**": {
"key4": "This is a **bold** text with an [*italic link*](https://google.com).",
"key5": "This is a **bold** text with an [*italic link*](https://google.com).",
},
}
)
```
will return:
```python
{
"key1": "This is a \\textbf{bold} text with a \\href{https://google.com}{\\textit{link}}.",
"key2": "This is a \\textbf{bold} text with a \\href{https://google.com}{\\textit{link}}.",
"\\textbf{key3}": {
"key4": "This is a \\textbf{bold} text with a \\href{https://google.com}{\\textit{link}}.",
"key5": "This is a \\textbf{bold} text with a \\href{https://google.com}{\\textit{link}}.",
},
}
```
Args:
dictionary (dict): The dictionary to convert.
Returns:
dict: The LaTeX dictionary.
"""
for key, value in dictionary.copy().items():
if isinstance(value, str):
# if the value is a string, then apply markdown_to_latex and
# escape_latex_characters to it:
result = escape_latex_characters(value)
dictionary[key] = markdown_to_latex(result)
elif isinstance(value, list):
# if the value is a list, then loop through the list and apply
# markdown_to_latex and escape_latex_characters to each item:
for index, item in enumerate(value):
if isinstance(item, str):
result = escape_latex_characters(item)
dictionary[key][index] = markdown_to_latex(result)
elif isinstance(item, dict):
# if the item is a dictionary, then call loop_through_dictionary
# again:
dictionary[key][index] = (
convert_a_markdown_dictionary_to_a_latex_dictionary(item)
)
elif isinstance(value, dict):
# if the value is a dictionary, then call loop_through_dictionary again:
dictionary[key] = convert_a_markdown_dictionary_to_a_latex_dictionary(value)
# do the same for the key:
result = escape_latex_characters(key)
dictionary[markdown_to_latex(result)] = dictionary.pop(key)
return dictionary
def read_input_file(file_path: pathlib.Path) -> RenderCVDataModel: def read_input_file(file_path: pathlib.Path) -> RenderCVDataModel:
"""Read the input file and return an instance of RenderCVDataModel. """Read the input file and return an instance of RenderCVDataModel.
@ -1002,42 +1070,9 @@ def read_input_file(file_path: pathlib.Path) -> RenderCVDataModel:
file_content = file_path.read_text() file_content = file_path.read_text()
parsed_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content) parsed_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content)
parsed_dictionary = convert_a_markdown_dictionary_to_a_latex_dictionary(
def loop_through_dictionary(dictionary: dict[str, Any]) -> dict[str, Any]: parsed_dictionary
"""Recursively loop through a dictionary and apply `markdown_to_latex` and )
`escape_latex_characters` to all the fields.
Args:
dictionary (dict[str, Any]): The dictionary to loop through.
Returns:
dict[str, Any]: The dictionary with markdown_to_latex and
escape_latex_characters applied to all the fields.
"""
for key, value in dictionary.items():
if isinstance(value, str):
# if the value is a string, then apply markdown_to_latex and
# escape_latex_characters to it:
result = escape_latex_characters(value)
dictionary[key] = markdown_to_latex(result)
elif isinstance(value, list):
# if the value is a list, then loop through the list and apply
# markdown_to_latex and escape_latex_characters to each item:
for index, item in enumerate(value):
if isinstance(item, str):
result = escape_latex_characters(item)
dictionary[key][index] = markdown_to_latex(result)
elif isinstance(item, dict):
# if the item is a dictionary, then call loop_through_dictionary
# again:
dictionary[key][index] = loop_through_dictionary(item)
elif isinstance(value, dict):
# if the value is a dictionary, then call loop_through_dictionary again:
dictionary[key] = loop_through_dictionary(value)
return dictionary
parsed_dictionary = loop_through_dictionary(parsed_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 = RenderCVDataModel(**parsed_dictionary) ## type: ignore
@ -1259,8 +1294,7 @@ def generate_json_schema() -> dict:
def generate_json_schema_file(json_schema_path: pathlib.Path): def generate_json_schema_file(json_schema_path: pathlib.Path):
"""Generate the JSON schema of RenderCV and save it to a file in the `docs` """Generate the JSON schema of RenderCV and save it to a file in the `docs`"""
"""
schema = generate_json_schema() schema = generate_json_schema()
schema_json = json.dumps(schema, indent=2) schema_json = json.dumps(schema, indent=2)
json_schema_path.write_text(schema_json) json_schema_path.write_text(schema_json)