change data model design for a better JSON schema

This commit is contained in:
Sina Atalay 2023-11-03 20:52:14 +01:00
parent e176f1bf36
commit 3070092a2b
2 changed files with 309 additions and 61 deletions

View File

@ -323,7 +323,7 @@ def generate_json_schema(output_directory: str) -> str:
# ====================================================================================== # ======================================================================================
# CUSTOM DATA TYPES ==================================================================== # DESIGN MODELS ========================================================================
# ====================================================================================== # ======================================================================================
# To understand how to create custom data types, see: # To understand how to create custom data types, see:
@ -337,14 +337,6 @@ LaTeXDimension = Annotated[
LaTeXString = Annotated[str, AfterValidator(escape_latex_characters)] LaTeXString = Annotated[str, AfterValidator(escape_latex_characters)]
SpellCheckedString = Annotated[LaTeXString, AfterValidator(check_spelling)] SpellCheckedString = Annotated[LaTeXString, AfterValidator(check_spelling)]
# ======================================================================================
# ======================================================================================
# ======================================================================================
# ======================================================================================
# DESIGN MODELS ========================================================================
# ======================================================================================
class ClassicThemePageMargins(BaseModel): class ClassicThemePageMargins(BaseModel):
"""This class stores the margins of pages for the classic theme.""" """This class stores the margins of pages for the classic theme."""
@ -1034,24 +1026,20 @@ class Connection(BaseModel):
return url return url
class Section(BaseModel): class SectionBase(BaseModel):
"""This class stores a section information.""" """This class stores a section information.
It is the parent class of all the section classes like
`#!python SectionWithEducationEntries`, `#!python SectionWithExperienceEntries`,
`#!python SectionWithNormalEntries`, `#!python SectionWithOneLineEntries`, and
`#!python SectionWithPublicationEntries`.
"""
title: LaTeXString = Field( title: LaTeXString = Field(
title="Section Title", title="Section Title",
description="The title of the section.", description="The title of the section.",
examples=["My Custom Section"], examples=["My Custom Section"],
) )
entry_type: Literal[
"OneLineEntry",
"NormalEntry",
"ExperienceEntry",
"EducationEntry",
"PublicationEntry",
] = Field(
title="Entry Type",
description="The type of the entries in the section.",
)
link_text: Optional[LaTeXString] = Field( link_text: Optional[LaTeXString] = Field(
default=None, default=None,
title="Link Text", title="Link Text",
@ -1062,12 +1050,6 @@ class Section(BaseModel):
), ),
examples=["view on GitHub", "view on LinkedIn"], examples=["view on GitHub", "view on LinkedIn"],
) )
entries: list[
OneLineEntry | NormalEntry | ExperienceEntry | EducationEntry | PublicationEntry
] = Field(
title="Entries",
description="The entries of the section. The format depends on the entry type.",
)
@field_validator("title") @field_validator("title")
@classmethod @classmethod
@ -1076,6 +1058,73 @@ class Section(BaseModel):
return title.title() return title.title()
entry_type_field = Field(
title="Entry Type",
description="The type of the entries in the section.",
)
entries_field = Field(
title="Entries",
description="The entries of the section. The format depends on the entry type.",
)
class SectionWithEducationEntries(SectionBase):
"""This class stores a section with
[EducationEntry](../user_guide.md#educationentry)s.
"""
entry_type: Literal["EducationEntry"] = entry_type_field
entries: list[EducationEntry] = entries_field
class SectionWithExperienceEntries(SectionBase):
"""This class stores a section with
[ExperienceEntry](../user_guide.md#experienceentry)s.
"""
entry_type: Literal["ExperienceEntry"] = entry_type_field
entries: list[ExperienceEntry] = entries_field
class SectionWithNormalEntries(SectionBase):
"""This class stores a section with
[NormalEntry](../user_guide.md#normalentry)s.
"""
entry_type: Literal["NormalEntry"] = entry_type_field
entries: list[NormalEntry] = entries_field
class SectionWithOneLineEntries(SectionBase):
"""This class stores a section with
[OneLineEntry](../user_guide.md#onelineentry)s.
"""
entry_type: Literal["OneLineEntry"] = entry_type_field
entries: list[OneLineEntry] = entries_field
class SectionWithPublicationEntries(SectionBase):
"""This class stores a section with
[PublicationEntry](../user_guide.md#publicationentry)s.
"""
entry_type: Literal["PublicationEntry"] = entry_type_field
entries: list[PublicationEntry] = entries_field
Section = Annotated[
SectionWithEducationEntries
| SectionWithExperienceEntries
| SectionWithNormalEntries
| SectionWithOneLineEntries
| SectionWithPublicationEntries,
Field(
discriminator="entry_type",
),
]
class CurriculumVitae(BaseModel): class CurriculumVitae(BaseModel):
"""This class bindes all the information of a CV together.""" """This class bindes all the information of a CV together."""
@ -1284,7 +1333,7 @@ class CurriculumVitae(BaseModel):
@computed_field @computed_field
@cached_property @cached_property
def sections(self) -> list[Section]: def sections(self) -> list[SectionBase]:
sections = [] sections = []
# Pre-defined sections (i.e. sections that are not custom)): # Pre-defined sections (i.e. sections that are not custom)):
@ -1368,7 +1417,15 @@ class CurriculumVitae(BaseModel):
" order 😷" " order 😷"
) )
section = Section( object_map = {
"EducationEntry": SectionWithEducationEntries,
"ExperienceEntry": SectionWithExperienceEntries,
"NormalEntry": SectionWithNormalEntries,
"OneLineEntry": SectionWithOneLineEntries,
"PublicationEntry": SectionWithPublicationEntries,
}
section = object_map[entry_type](
title=section_name, title=section_name,
entry_type=entry_type, # type: ignore entry_type=entry_type, # type: ignore
entries=entries, entries=entries,

View File

@ -534,7 +534,33 @@
"allOf": [ "allOf": [
{ {
"items": { "items": {
"$ref": "#/$defs/Section" "discriminator": {
"mapping": {
"EducationEntry": "#/$defs/SectionWithEducationEntries",
"ExperienceEntry": "#/$defs/SectionWithExperienceEntries",
"NormalEntry": "#/$defs/SectionWithNormalEntries",
"OneLineEntry": "#/$defs/SectionWithOneLineEntries",
"PublicationEntry": "#/$defs/SectionWithPublicationEntries"
},
"propertyName": "entry_type"
},
"oneOf": [
{
"$ref": "#/$defs/SectionWithEducationEntries"
},
{
"$ref": "#/$defs/SectionWithExperienceEntries"
},
{
"$ref": "#/$defs/SectionWithNormalEntries"
},
{
"$ref": "#/$defs/SectionWithOneLineEntries"
},
{
"$ref": "#/$defs/SectionWithPublicationEntries"
}
]
}, },
"type": "array" "type": "array"
} }
@ -1263,7 +1289,7 @@
"type": "object", "type": "object",
"additionalProperties": false "additionalProperties": false
}, },
"Section": { "SectionWithEducationEntries": {
"properties": { "properties": {
"title": { "title": {
"description": "The title of the section.", "description": "The title of the section.",
@ -1273,18 +1299,6 @@
"title": "Section Title", "title": "Section Title",
"type": "string" "type": "string"
}, },
"entry_type": {
"description": "The type of the entries in the section.",
"enum": [
"OneLineEntry",
"NormalEntry",
"ExperienceEntry",
"EducationEntry",
"PublicationEntry"
],
"title": "Entry Type",
"type": "string"
},
"link_text": { "link_text": {
"default": null, "default": null,
"description": "If the section has a link, then what should be the text of the link? If this field is not provided, then the link text will be generated automatically based on the URL.", "description": "If the section has a link, then what should be the text of the link? If this field is not provided, then the link text will be generated automatically based on the URL.",
@ -1299,27 +1313,16 @@
} }
] ]
}, },
"entry_type": {
"const": "EducationEntry",
"description": "The type of the entries in the section.",
"title": "Entry Type"
},
"entries": { "entries": {
"description": "The entries of the section. The format depends on the entry type.", "description": "The entries of the section. The format depends on the entry type.",
"items": { "items": {
"oneOf": [
{
"$ref": "#/$defs/OneLineEntry"
},
{
"$ref": "#/$defs/NormalEntry"
},
{
"$ref": "#/$defs/ExperienceEntry"
},
{
"$ref": "#/$defs/EducationEntry" "$ref": "#/$defs/EducationEntry"
}, },
{
"$ref": "#/$defs/PublicationEntry"
}
]
},
"title": "Entries", "title": "Entries",
"type": "array" "type": "array"
} }
@ -1329,7 +1332,195 @@
"entry_type", "entry_type",
"entries" "entries"
], ],
"title": "Section", "title": "SectionWithEducationEntries",
"type": "object",
"additionalProperties": false
},
"SectionWithExperienceEntries": {
"properties": {
"title": {
"description": "The title of the section.",
"examples": [
"My Custom Section"
],
"title": "Section Title",
"type": "string"
},
"link_text": {
"default": null,
"description": "If the section has a link, then what should be the text of the link? If this field is not provided, then the link text will be generated automatically based on the URL.",
"examples": [
"view on GitHub",
"view on LinkedIn"
],
"title": "Link Text",
"allOf": [
{
"type": "string"
}
]
},
"entry_type": {
"const": "ExperienceEntry",
"description": "The type of the entries in the section.",
"title": "Entry Type"
},
"entries": {
"description": "The entries of the section. The format depends on the entry type.",
"items": {
"$ref": "#/$defs/ExperienceEntry"
},
"title": "Entries",
"type": "array"
}
},
"required": [
"title",
"entry_type",
"entries"
],
"title": "SectionWithExperienceEntries",
"type": "object",
"additionalProperties": false
},
"SectionWithNormalEntries": {
"properties": {
"title": {
"description": "The title of the section.",
"examples": [
"My Custom Section"
],
"title": "Section Title",
"type": "string"
},
"link_text": {
"default": null,
"description": "If the section has a link, then what should be the text of the link? If this field is not provided, then the link text will be generated automatically based on the URL.",
"examples": [
"view on GitHub",
"view on LinkedIn"
],
"title": "Link Text",
"allOf": [
{
"type": "string"
}
]
},
"entry_type": {
"const": "NormalEntry",
"description": "The type of the entries in the section.",
"title": "Entry Type"
},
"entries": {
"description": "The entries of the section. The format depends on the entry type.",
"items": {
"$ref": "#/$defs/NormalEntry"
},
"title": "Entries",
"type": "array"
}
},
"required": [
"title",
"entry_type",
"entries"
],
"title": "SectionWithNormalEntries",
"type": "object",
"additionalProperties": false
},
"SectionWithOneLineEntries": {
"properties": {
"title": {
"description": "The title of the section.",
"examples": [
"My Custom Section"
],
"title": "Section Title",
"type": "string"
},
"link_text": {
"default": null,
"description": "If the section has a link, then what should be the text of the link? If this field is not provided, then the link text will be generated automatically based on the URL.",
"examples": [
"view on GitHub",
"view on LinkedIn"
],
"title": "Link Text",
"allOf": [
{
"type": "string"
}
]
},
"entry_type": {
"const": "OneLineEntry",
"description": "The type of the entries in the section.",
"title": "Entry Type"
},
"entries": {
"description": "The entries of the section. The format depends on the entry type.",
"items": {
"$ref": "#/$defs/OneLineEntry"
},
"title": "Entries",
"type": "array"
}
},
"required": [
"title",
"entry_type",
"entries"
],
"title": "SectionWithOneLineEntries",
"type": "object",
"additionalProperties": false
},
"SectionWithPublicationEntries": {
"properties": {
"title": {
"description": "The title of the section.",
"examples": [
"My Custom Section"
],
"title": "Section Title",
"type": "string"
},
"link_text": {
"default": null,
"description": "If the section has a link, then what should be the text of the link? If this field is not provided, then the link text will be generated automatically based on the URL.",
"examples": [
"view on GitHub",
"view on LinkedIn"
],
"title": "Link Text",
"allOf": [
{
"type": "string"
}
]
},
"entry_type": {
"const": "PublicationEntry",
"description": "The type of the entries in the section.",
"title": "Entry Type"
},
"entries": {
"description": "The entries of the section. The format depends on the entry type.",
"items": {
"$ref": "#/$defs/PublicationEntry"
},
"title": "Entries",
"type": "array"
}
},
"required": [
"title",
"entry_type",
"entries"
],
"title": "SectionWithPublicationEntries",
"type": "object", "type": "object",
"additionalProperties": false "additionalProperties": false
}, },