Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions schema/BaseElement.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pydantic import BaseModel, ConfigDict
from typing import Literal, Optional
from typing import Literal


class BaseElement(BaseModel):
Expand All @@ -12,5 +12,14 @@ class BaseElement(BaseModel):
# not only when an instance of BaseElement is created
model_config = ConfigDict(validate_assignment=True)

# Unique element name
name: Optional[str] = None
# element name
name: str

def model_dump(self, *args, **kwargs):
"""This makes sure the element name property is moved out and up to a one-key dictionary"""
elem_dict = super().model_dump(*args, **kwargs)
name = elem_dict.pop("name", None)
if name is None:
raise ValueError("Element missing 'name' attribute")
data = [{name: elem_dict}]
return data
53 changes: 52 additions & 1 deletion schema/Line.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, Field, field_validator
from typing import Annotated, List, Literal, Union

from schema.BaseElement import BaseElement
Expand Down Expand Up @@ -29,6 +29,57 @@ class Line(BaseModel):
]
]

@field_validator("line", mode="before")
@classmethod
def parse_list_of_dicts(cls, value):
"""This method inserts the key of the one-key dictionary into
the name attribute of the elements"""
if not isinstance(value, list):
raise TypeError("line must be a list")

if value and isinstance(value[0], BaseModel):
# Already a list of models; nothing to do
return value

# we expect a list of dicts or strings
elements = []
for item_dict in value:
# an element is either a reference string to another element or a dict
if isinstance(item_dict, str):
raise RuntimeError("Reference/alias elements not yet implemented")

elif isinstance(item_dict, dict):
if not (isinstance(item_dict, dict) and len(item_dict) == 1):
raise ValueError(
f"Each line element must be a dict with exactly one key, the name of the element, but we got: {item_dict!r}"
)
[(name, fields)] = item_dict.items()

if not isinstance(fields, dict):
raise ValueError(
f"Value for element key '{name}' must be a dict (got {fields!r})"
)

# Insert the name into the fields dict
fields["name"] = name
elements.append(fields)
return elements

def model_dump(self, *args, **kwargs):
"""This makes sure the element name property is moved out and up to a one-key dictionary"""
# Use default dump for non-line fields
data = super().model_dump(*args, **kwargs)

# Reformat 'line' field as list of single-key dicts
new_line = []
for elem in self.line:
# Use custom dump for each line element
elem_dict = elem.model_dump(**kwargs)[0]
new_line.append(elem_dict)

data["line"] = new_line
return data


# Avoid circular import issues
Line.model_rebuild()