proofread

This commit is contained in:
transatlanticism1976 2024-05-26 14:30:09 -04:00
parent e1403b537e
commit 9b5deee285
1 changed files with 48 additions and 48 deletions

View File

@ -143,7 +143,7 @@ class RenderCVBaseModel(pydantic.BaseModel):
# Entry models: ======================================================================== # Entry models: ========================================================================
# ====================================================================================== # ======================================================================================
# Create an URL validator to validate social network URLs: # Create a URL validator to validate social network URLs:
url_validator = pydantic.TypeAdapter(pydantic.HttpUrl) # type: ignore url_validator = pydantic.TypeAdapter(pydantic.HttpUrl) # type: ignore
@ -189,7 +189,7 @@ class EntryWithDate(RenderCVBaseModel):
title="Date", title="Date",
description=( description=(
"The date field can be filled in YYYY-MM-DD, YYYY-MM, or YYYY formats or as" "The date field can be filled in YYYY-MM-DD, YYYY-MM, or YYYY formats or as"
' an arbitrary string like "Fall 2023".' " an arbitrary string like \"Fall 2023\"."
), ),
examples=["2020-09-24", "Fall 2023"], examples=["2020-09-24", "Fall 2023"],
) )
@ -210,7 +210,7 @@ class EntryWithDate(RenderCVBaseModel):
# Check if it is a valid date: # Check if it is a valid date:
get_date_object(date) get_date_object(date)
# check if it is in YYYY format, and if so, convert it to an # Check if it is in YYYY format, and if so, convert it to an
# integer: # integer:
if re.fullmatch(r"\d{4}", date): if re.fullmatch(r"\d{4}", date):
# This is not required for start_date and end_date because they # This is not required for start_date and end_date because they
@ -281,7 +281,7 @@ class PublicationEntryBase(RenderCVBaseModel):
if err.code == 404: if err.code == 404:
raise ValueError("DOI cannot be found in the DOI System!") raise ValueError("DOI cannot be found in the DOI System!")
except (InvalidURL, URLError): except (InvalidURL, URLError):
raise ValueError("This DOI is not valid!") raise ValueError("This DOI is invalid!")
return doi return doi
@ -322,7 +322,7 @@ class EntryBase(EntryWithDate):
title="End Date", title="End Date",
description=( description=(
"The end date of the event in YYYY-MM-DD, YYYY-MM, or YYYY format. If the" "The end date of the event in YYYY-MM-DD, YYYY-MM, or YYYY format. If the"
' event is still ongoing, then type "present" or provide only the' " event is still ongoing, then type \"present\" or provide only the"
" start_date." " start_date."
), ),
examples=["2020-09-24", "present"], examples=["2020-09-24", "present"],
@ -359,7 +359,7 @@ class EntryBase(EntryWithDate):
) )
def check_and_adjust_dates(self) -> "EntryBase": def check_and_adjust_dates(self) -> "EntryBase":
""" """
Check if the dates are provided correctly and do the necessary adjustments. Check if the dates are provided correctly and make the necessary adjustments.
""" """
date_is_provided = self.date is not None date_is_provided = self.date is not None
start_date_is_provided = self.start_date is not None start_date_is_provided = self.start_date is not None
@ -388,8 +388,8 @@ class EntryBase(EntryWithDate):
if start_date > end_date: if start_date > end_date:
raise ValueError( raise ValueError(
'"start_date" can not be after "end_date"!', '"start_date" can not be after "end_date"!',
"start_date", # this is the location of the error "start_date", # This is the location of the error
str(start_date), # this is value of the error str(start_date), # This is value of the error
) )
return self return self
@ -435,7 +435,7 @@ class EntryBase(EntryWithDate):
date_string = f"{start_date} {locale_catalog['to']} {end_date}" date_string = f"{start_date} {locale_catalog['to']} {end_date}"
else: else:
# Neither date, start_date, nor end_date is provided, so return an empty # Neither date, start_date, nor end_date are provided, so return an empty
# string: # string:
date_string = "" date_string = ""
@ -488,7 +488,7 @@ class EntryBase(EntryWithDate):
date_string = f"{start_date} {locale_catalog['to']} {end_date}" date_string = f"{start_date} {locale_catalog['to']} {end_date}"
else: else:
# Neither date, start_date, nor end_date is provided, so return an empty # Neither date, start_date, nor end_date are provided, so return an empty
# string: # string:
date_string = "" date_string = ""
@ -541,10 +541,10 @@ class EntryBase(EntryWithDate):
end_date = get_date_object(self.end_date) # type: ignore end_date = get_date_object(self.end_date) # type: ignore
start_date = get_date_object(self.start_date) # type: ignore start_date = get_date_object(self.start_date) # type: ignore
# calculate the number of days between start_date and end_date: # Calculate the number of days between start_date and end_date:
timespan_in_days = (end_date - start_date).days # type: ignore timespan_in_days = (end_date - start_date).days # type: ignore
# calculate the number of years between start_date and end_date: # Calculate the number of years between start_date and end_date:
how_many_years = timespan_in_days // 365 how_many_years = timespan_in_days // 365
if how_many_years == 0: if how_many_years == 0:
how_many_years_string = None how_many_years_string = None
@ -553,14 +553,14 @@ class EntryBase(EntryWithDate):
else: else:
how_many_years_string = f"{how_many_years} {locale_catalog['years']}" how_many_years_string = f"{how_many_years} {locale_catalog['years']}"
# calculate the number of months between start_date and end_date: # Calculate the number of months between start_date and end_date:
how_many_months = round((timespan_in_days % 365) / 30) how_many_months = round((timespan_in_days % 365) / 30)
if how_many_months <= 1: if how_many_months <= 1:
how_many_months_string = f"1 {locale_catalog['month']}" how_many_months_string = f"1 {locale_catalog['month']}"
else: else:
how_many_months_string = f"{how_many_months} {locale_catalog['months']}" how_many_months_string = f"{how_many_months} {locale_catalog['months']}"
# combine howManyYearsString and howManyMonthsString: # Combine howManyYearsString and howManyMonthsString:
if how_many_years_string is None: if how_many_years_string is None:
time_span_string = how_many_months_string time_span_string = how_many_months_string
else: else:
@ -576,7 +576,7 @@ class NormalEntryBase(RenderCVBaseModel):
) )
# The following class is to make sure NormalEntryBase keys comes first, # The following class is to make sure NormalEntryBase keys come first,
# then the keys of the EntryBase class. The only way to achieve this in Pydantic is # then the keys of the EntryBase class. The only way to achieve this in Pydantic is
# to do this. # to do this.
class NormalEntry(EntryBase, NormalEntryBase): class NormalEntry(EntryBase, NormalEntryBase):
@ -596,7 +596,7 @@ class ExperienceEntryBase(RenderCVBaseModel):
) )
# The following class is to make sure ExperienceEntryBase keys comes first, # The following class is to make sure ExperienceEntryBase keys come first,
# then the keys of the EntryBase class. The only way to achieve this in Pydantic is # then the keys of the EntryBase class. The only way to achieve this in Pydantic is
# to do this. # to do this.
class ExperienceEntry(EntryBase, ExperienceEntryBase): class ExperienceEntry(EntryBase, ExperienceEntryBase):
@ -622,7 +622,7 @@ class EducationEntryBase(RenderCVBaseModel):
) )
# The following class is to make sure EducationEntryBase keys comes first, # The following class is to make sure EducationEntryBase keys come first,
# then the keys of the EntryBase class. The only way to achieve this in Pydantic is # then the keys of the EntryBase class. The only way to achieve this in Pydantic is
# to do this. # to do this.
class EducationEntry(EntryBase, EducationEntryBase): class EducationEntry(EntryBase, EducationEntryBase):
@ -765,7 +765,7 @@ def validate_section_input(
entries. Since there are multiple entry types, it is not possible to validate it entries. Since there are multiple entry types, it is not possible to validate it
directly. This function looks at the entry list's first element and determines the directly. This function looks at the entry list's first element and determines the
section's entry type based on the first element. Then, it validates the rest of the section's entry type based on the first element. Then, it validates the rest of the
list based on the determined entry type. If it is a `Section` object, then it list based on the determined entry type. If it is a `Section` object, it then
validates it directly. validates it directly.
Args: Args:
@ -774,7 +774,7 @@ def validate_section_input(
SectionBase | list[Any]: The validated sections input. SectionBase | list[Any]: The validated sections input.
""" """
if isinstance(sections_input, list): if isinstance(sections_input, list):
# find the entry type based on the first identifiable entry: # Find the entry type based on the first identifiable entry:
entry_type = None entry_type = None
section_type = None section_type = None
for entry in sections_input: for entry in sections_input:
@ -786,10 +786,10 @@ def validate_section_input(
if entry_type is None or section_type is None: if entry_type is None or section_type is None:
raise ValueError( raise ValueError(
"RenderCV couldn't match this section with any entry type! Please check" "RenderCV couldn't match this section with any entry types! Please check"
" the entries and make sure they are provided correctly.", " the entries and make sure they are provided correctly.",
"", # this is the location of the error "", # This is the location of the error
"", # this is value of the error "", # This is value of the error
) )
test_section = { test_section = {
@ -903,11 +903,12 @@ class SocialNetwork(RenderCVBaseModel):
"GitHub": "https://github.com/", "GitHub": "https://github.com/",
"GitLab": "https://gitlab.com/", "GitLab": "https://gitlab.com/",
"Instagram": "https://instagram.com/", "Instagram": "https://instagram.com/",
"Orcid": "https://orcid.org/", "ORCID": "https://orcid.org/",
"Twitter": "https://twitter.com/", "Twitter": "https://twitter.com/",
"StackOverflow": "https://stackoverflow.com/users/", "StackOverflow": "https://stackoverflow.com/users/",
"ResearchGate": "https://researchgate.net/profile/", "ResearchGate": "https://researchgate.net/profile/",
"YouTube": "https://youtube.com/", "YouTube": "https://youtube.com/",
"Google Scholar": "https://scholar.google.com/citations?user=",
} }
url = url_dictionary[self.network] + self.username url = url_dictionary[self.network] + self.username
@ -1066,37 +1067,37 @@ class LocaleCatalog(RenderCVBaseModel):
default="month", default="month",
title='Translation of "Month"', title='Translation of "Month"',
description='Translation of the word "month" in the locale.', description='Translation of the word "month" in the locale.',
validate_default=True, # to initialize the locale catalog with the default values validate_default=True, # To initialize the locale catalog with the default values
) )
months: Optional[str] = pydantic.Field( months: Optional[str] = pydantic.Field(
default="months", default="months",
title='Translation of "Months"', title='Translation of "Months"',
description='Translation of the word "months" in the locale.', description='Translation of the word "months" in the locale.',
validate_default=True, # to initialize the locale catalog with the default values validate_default=True, # To initialize the locale catalog with the default values
) )
year: Optional[str] = pydantic.Field( year: Optional[str] = pydantic.Field(
default="year", default="year",
title='Translation of "Year"', title='Translation of "Year"',
description='Translation of the word "year" in the locale.', description='Translation of the word "year" in the locale.',
validate_default=True, # to initialize the locale catalog with the default values validate_default=True, # To initialize the locale catalog with the default values
) )
years: Optional[str] = pydantic.Field( years: Optional[str] = pydantic.Field(
default="years", default="years",
title='Translation of "Years"', title='Translation of "Years"',
description='Translation of the word "years" in the locale.', description='Translation of the word "years" in the locale.',
validate_default=True, # to initialize the locale catalog with the default values validate_default=True, # To initialize the locale catalog with the default values
) )
present: Optional[str] = pydantic.Field( present: Optional[str] = pydantic.Field(
default="present", default="present",
title='Translation of "Present"', title='Translation of "Present"',
description='Translation of the word "present" in the locale.', description='Translation of the word "present" in the locale.',
validate_default=True, # to initialize the locale catalog with the default values validate_default=True, # To initialize the locale catalog with the default values
) )
to: Optional[str] = pydantic.Field( to: Optional[str] = pydantic.Field(
default="to", default="to",
title='Translation of "To"', title='Translation of "To"',
description='Translation of the word "to" in the locale.', description='Translation of the word "to" in the locale.',
validate_default=True, # to initialize the locale catalog with the default values validate_default=True, # To initialize the locale catalog with the default values
) )
abbreviations_for_months: Optional[ abbreviations_for_months: Optional[
Annotated[list[str], at.Len(min_length=12, max_length=12)] Annotated[list[str], at.Len(min_length=12, max_length=12)]
@ -1138,9 +1139,8 @@ class LocaleCatalog(RenderCVBaseModel):
# Create a custom type called Design: # Create a custom type called Design:
# It is a union of all the design options and the correct design option is determined by # It is a union of all the design options and the correct design option is determined by
# the theme field, thanks Pydantic's discriminator feature. # the theme field, thanks to Pydantic's discriminator feature.
# See https://docs.pydantic.dev/2.5/concepts/fields/#discriminator for more information # See https://docs.pydantic.dev/2.5/concepts/fields/#discriminator for more information
# about discriminators.
RenderCVDesign = Annotated[ RenderCVDesign = Annotated[
ClassicThemeOptions ClassicThemeOptions
| ModerncvThemeOptions | ModerncvThemeOptions
@ -1188,15 +1188,15 @@ class RenderCVDataModel(RenderCVBaseModel):
theme_data_model_types = get_args(RenderCVDesign)[0] theme_data_model_types = get_args(RenderCVDesign)[0]
if isinstance(design, theme_data_model_types): if isinstance(design, theme_data_model_types):
# then it means RenderCVDataModel is already initialized with a design, so # Then it means RenderCVDataModel is already initialized with a design, so
# return it as is: # return it as is:
return design return design
elif design["theme"] in available_themes: # type: ignore elif design["theme"] in available_themes: # type: ignore
# then it means it's a built-in theme, but it is not initialized (validated) # Then it means it's a built-in theme, but it is not initialized (validated)
# yet. So, validate and return it: # yet. So, validate and return it:
return rendercv_design_validator.validate_python(design) return rendercv_design_validator.validate_python(design)
else: else:
# then it means it is a custom theme, so initialize and validate it: # Then it means it is a custom theme, so initialize and validate it:
theme_name: str = str(design["theme"]) theme_name: str = str(design["theme"])
# check if the theme name is valid: # check if the theme name is valid:
@ -1262,11 +1262,11 @@ class RenderCVDataModel(RenderCVBaseModel):
theme_module, f"{theme_name.capitalize()}ThemeOptions" # type: ignore theme_module, f"{theme_name.capitalize()}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: else:
# Then it means there is no __init__.py file in the custom theme folder. # Then it means there is no __init__.py file in the custom theme folder.
# So, create a dummy data model and use that instead. # Create a dummy data model and use that instead.
class ThemeOptionsAreNotProvided(RenderCVBaseModel): class ThemeOptionsAreNotProvided(RenderCVBaseModel):
theme: str = theme_name theme: str = theme_name
@ -1303,9 +1303,9 @@ def set_or_update_a_value(
update the value. This is used for recursive calls. When the value is set update the value. This is used for recursive calls. When the value is set
to a sub model, the original data model is validated. Defaults to None. to a sub model, the original data model is validated. Defaults to None.
""" """
# recursively call this function until the last key is reached: # Recursively call this function until the last key is reached:
# rename `sections` with `sections_input` since the key is `sections` is an alias: # Rename `sections` with `sections_input` since the key is `sections` is an alias:
key = key.replace("sections.", "sections_input.") key = key.replace("sections.", "sections_input.")
keys = key.split(".") keys = key.split(".")
@ -1315,12 +1315,12 @@ def set_or_update_a_value(
model = data_model model = data_model
if len(keys) == 1: if len(keys) == 1:
# set the value: # Set the value:
if value.startswith("{") and value.endswith("}"): if value.startswith("{") and value.endswith("}"):
# allow users to assign dictionaries: # Allow users to assign dictionaries:
value = eval(value) value = eval(value)
elif value.startswith("[") and value.endswith("]"): elif value.startswith("[") and value.endswith("]"):
# allow users to assign lists: # Allow users to assign lists:
value = eval(value) value = eval(value)
if isinstance(model, pydantic.BaseModel): if isinstance(model, pydantic.BaseModel):
@ -1374,14 +1374,14 @@ def read_input_file(
RenderCVDataModel: The data models with $\\LaTeX$ and markdown strings. RenderCVDataModel: The data models with $\\LaTeX$ and markdown strings.
""" """
if isinstance(file_path_or_contents, pathlib.Path): if isinstance(file_path_or_contents, pathlib.Path):
# check if the file exists: # Check if the file exists:
if not file_path_or_contents.exists(): if not file_path_or_contents.exists():
raise FileNotFoundError( raise FileNotFoundError(
f"The input file [magenta]{file_path_or_contents}[/magenta] doesn't" f"The input file [magenta]{file_path_or_contents}[/magenta] doesn't"
" exist!" " exist!"
) )
# check the file extension: # Check the file extension:
accepted_extensions = [".yaml", ".yml", ".json", ".json5"] accepted_extensions = [".yaml", ".yml", ".json", ".json5"]
if file_path_or_contents.suffix not in accepted_extensions: if file_path_or_contents.suffix not in accepted_extensions:
user_friendly_accepted_extensions = [ user_friendly_accepted_extensions = [
@ -1402,7 +1402,7 @@ def read_input_file(
input_as_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content) # type: ignore 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)
return rendercv_data_model return rendercv_data_model
@ -1418,7 +1418,7 @@ def get_a_sample_data_model(
Returns: Returns:
RenderCVDataModel: A sample data model. RenderCVDataModel: A sample data model.
""" """
# check if the theme is valid: # Check if the theme is valid:
if theme not in available_themes: if theme not in available_themes:
available_themes_string = ", ".join(available_themes) available_themes_string = ", ".join(available_themes)
raise ValueError( raise ValueError(
@ -1583,7 +1583,7 @@ def get_a_sample_data_model(
"projects": [ "projects": [
NormalEntry( NormalEntry(
name="Multi-User Drawing Tool", name="Multi-User Drawing Tool",
date="2004", # would omit start/end dates and replace with a project link as the date is largely irrelevant date="2004", # I would omit start/end dates and replace with a project link as the date is largely irrelevant
#project_link = "github.com/username/repo" #project_link = "github.com/username/repo"
highlights=[ highlights=[
( (
@ -1624,7 +1624,7 @@ def get_a_sample_data_model(
"additional_experience_and_awards": [ "additional_experience_and_awards": [
OneLineEntry( OneLineEntry(
label="Instructor (2003-2005)", label="Instructor (2003-2005)",
details="Taught two full-credit computer science courses", details="Taught 2 full-credit computer science courses",
), ),
OneLineEntry( OneLineEntry(
label="Third Prize, Senior Design Project", label="Third Prize, Senior Design Project",
@ -1772,7 +1772,7 @@ def generate_json_schema() -> dict[str, Any]:
field["oneOf"] = [field["anyOf"][0]] field["oneOf"] = [field["anyOf"][0]]
del field["anyOf"] del field["anyOf"]
# for sections field of CurriculumVitae: # For sections field of CurriculumVitae:
if "additionalProperties" in field["oneOf"][0]: if "additionalProperties" in field["oneOf"][0]:
field["oneOf"][0]["additionalProperties"]["oneOf"] = ( field["oneOf"][0]["additionalProperties"]["oneOf"] = (
field["oneOf"][0]["additionalProperties"]["anyOf"] field["oneOf"][0]["additionalProperties"]["anyOf"]