update JSON schema generator

This commit is contained in:
Sina Atalay 2024-02-06 19:18:46 +01:00
parent 7f291d30d8
commit 2a7db08e1b
1 changed files with 42 additions and 29 deletions

View File

@ -137,15 +137,18 @@ class EntryBase(RenderCVBaseModel):
start_date: Optional[int | RenderCVDate] = pydantic.Field( start_date: Optional[int | RenderCVDate] = pydantic.Field(
default=None, default=None,
title="Start Date", title="Start Date",
description="The start date of the event in YYYY-MM-DD format.", description=(
"The start date of the event in YYYY-MM-DD, YYYY-MM, or YYYY format."
),
examples=["2020-09-24"], examples=["2020-09-24"],
) )
end_date: Optional[Literal["present"] | int | RenderCVDate] = pydantic.Field( end_date: Optional[Literal["present"] | int | RenderCVDate] = pydantic.Field(
default=None, default=None,
title="End Date", title="End Date",
description=( description=(
"The end date of the event in YYYY-MM-DD format. If the event is still" "The end date of the event in YYYY-MM-DD, YYYY-MM, or YYYY format. If the"
' ongoing, then the value should be "present".' ' event is still ongoing, then type "present" or provide only the start'
" date."
), ),
examples=["2020-09-24", "present"], examples=["2020-09-24", "present"],
) )
@ -452,7 +455,9 @@ class PublicationEntry(RenderCVBaseModel):
) )
date: int | RenderCVDate = pydantic.Field( date: int | RenderCVDate = pydantic.Field(
title="Publication Date", title="Publication Date",
description="The date of the publication.", description=(
"The date of the publication in YYYY-MM-DD, YYYY-MM, or YYYY format."
),
examples=["2021-10-31", "2010"], examples=["2021-10-31", "2010"],
) )
journal: Optional[str] = pydantic.Field( journal: Optional[str] = pydantic.Field(
@ -725,7 +730,18 @@ class CurriculumVitae(RenderCVBaseModel):
"The social networks of the person. They will be rendered in the heading." "The social networks of the person. They will be rendered in the heading."
), ),
) )
sections_input: dict[str, Section | list[Any]] = pydantic.Field( sections_input: dict[
str,
Section
| list[
EducationEntry
| ExperienceEntry
| PublicationEntry
| NormalEntry
| OneLineEntry
| str
],
] = pydantic.Field(
default=None, default=None,
title="Sections", title="Sections",
description="The sections of the CV.", description="The sections of the CV.",
@ -839,7 +855,7 @@ class RenderCVDataModel(RenderCVBaseModel):
) )
design: Design = pydantic.Field( design: Design = pydantic.Field(
title="Design", title="Design",
description="The design information.", description="The design information of the CV.",
discriminator="theme", discriminator="theme",
) )
@ -984,9 +1000,8 @@ def read_input_file(file_path: pathlib.Path) -> RenderCVDataModel:
f" {accepted_extensions}. The input file is {file_path}." f" {accepted_extensions}. The input file is {file_path}."
) )
with open(file_path) as file: file_content = file_path.read_text()
file_content = file.read() parsed_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content)
parsed_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content)
def loop_through_dictionary(dictionary: dict[str, Any]) -> dict[str, Any]: def loop_through_dictionary(dictionary: dict[str, Any]) -> dict[str, Any]:
"""Recursively loop through a dictionary and apply `markdown_to_latex` and """Recursively loop through a dictionary and apply `markdown_to_latex` and
@ -1173,32 +1188,28 @@ def get_a_sample_data_model(name: str) -> RenderCVDataModel:
return RenderCVDataModel(cv=cv, design=desgin) return RenderCVDataModel(cv=cv, design=desgin)
def generate_json_schema(json_schema_path: pathlib.Path): def generate_json_schema() -> dict:
"""Generate the JSON schema of the data model and save it to a file. """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
file. The JSON Schema of RenderCV is saved in the `docs` directory of the repository file. The JSON Schema of RenderCV is saved in the `docs` directory of the repository
and distributed to the users with the and distributed to the users with the
[JSON Schema Store](https://www.schemastore.org/). [JSON Schema Store](https://www.schemastore.org/).
Args: Returns:
json_schema_path (str): The path to save the JSON schema. dict: The JSON schema of RenderCV.
""" """
class RenderCVSchemaGenerator(pydantic.json_schema.GenerateJsonSchema): class RenderCVSchemaGenerator(pydantic.json_schema.GenerateJsonSchema):
def generate(self, schema, mode="validation"): def generate(self, schema, mode="validation"):
json_schema = super().generate(schema, mode=mode) json_schema = super().generate(schema, mode=mode)
json_schema["title"] = "RenderCV Input"
# remove the description of the class (RenderCVDataModel) # Basic information about the schema:
del json_schema["description"] json_schema["title"] = "RenderCV"
json_schema["description"] = "RenderCV data model."
# add $id
json_schema["$id"] = ( json_schema["$id"] = (
"https://raw.githubusercontent.com/sinaatalay/rendercv/main/schema.json" "https://raw.githubusercontent.com/sinaatalay/rendercv/main/schema.json"
) )
# add $schema
json_schema["$schema"] = "http://json-schema.org/draft-07/schema#" json_schema["$schema"] = "http://json-schema.org/draft-07/schema#"
# Loop through $defs and remove docstring descriptions and fix optional # Loop through $defs and remove docstring descriptions and fix optional
@ -1207,10 +1218,6 @@ def generate_json_schema(json_schema_path: pathlib.Path):
# Don't allow additional properties # Don't allow additional properties
value["additionalProperties"] = False value["additionalProperties"] = False
# I don't want the docstrings in the schema, so remove them:
if "This class" in value["description"]:
del value["description"]
# If a type is optional, then Pydantic sets the type to a list of two # If a type is optional, then Pydantic sets the type to a list of two
# types, one of which is null. The null type can be removed since we # types, one of which is null. The null type can be removed since we
# already have the required field. Moreover, we would like to warn # already have the required field. Moreover, we would like to warn
@ -1226,6 +1233,9 @@ def generate_json_schema(json_schema_path: pathlib.Path):
): ):
field["allOf"] = [field["anyOf"][0]] field["allOf"] = [field["anyOf"][0]]
del field["anyOf"] del field["anyOf"]
else:
field["oneOf"] = field["anyOf"]
del field["anyOf"]
# In date field, we both accept normal strings and Date objects. They # In date field, we both accept normal strings and Date objects. They
# are both strings, therefore, if user provides a Date object, then # are both strings, therefore, if user provides a Date object, then
@ -1244,10 +1254,13 @@ def generate_json_schema(json_schema_path: pathlib.Path):
schema = RenderCVDataModel.model_json_schema( schema = RenderCVDataModel.model_json_schema(
schema_generator=RenderCVSchemaGenerator schema_generator=RenderCVSchemaGenerator
) )
schema = json.dumps(schema, indent=2)
# Change all anyOf to oneOf return schema
schema = schema.replace('"anyOf"', '"oneOf"')
with open(json_schema_path, "w") as f:
f.write(schema) 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`
"""
schema = generate_json_schema()
schema_json = json.dumps(schema, indent=2)
json_schema_path.write_text(schema_json)