Skip to content

Commit

Permalink
NXxas pydantic model
Browse files Browse the repository at this point in the history
  • Loading branch information
woutdenolf committed Mar 3, 2024
1 parent 3983ad6 commit e6507ae
Show file tree
Hide file tree
Showing 7 changed files with 425 additions and 2 deletions.
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ package_dir=
packages=find:
python_requires = >=3.8
install_requires =
gitpython
xmlschema
pydantic

[options.packages.find]
where=src
Expand Down
Empty file added src/pynxxas/nxdl/__init__.py
Empty file.
329 changes: 329 additions & 0 deletions src/pynxxas/nxdl/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
"""NXDL models"""

from typing import Optional, Union, List

import pydantic


class Item(pydantic.BaseModel):

class Config:
extra = "forbid"


class EnumerationItem(Item):
value: str
doc: Optional[str] = None


class Enumeration(Item):
item: List[EnumerationItem]


class DimensionItem(Item):
index: int
required: bool
value: Optional[str] = None
ref: Optional[str] = None


class DocDimensionItem(Item):
index: str
value: str


class DocDimensions(Item):
dim: List[DocDimensionItem]


class Dimensions(Item):
rank: Optional[Union[int, str]] = None
dim: Optional[List[DimensionItem]] = None
doc: Optional[Union[DocDimensions, str]] = None


class Attribute(Item):
name: str
type: str
doc: Optional[str] = None
recommended: Optional[bool] = None
optional: Optional[bool] = None
dimensions: Optional[Dimensions] = None
enumeration: Optional[Enumeration] = None
deprecated: Optional[str] = None


class Field(Item):
name: str
type: str
nameType: Optional[str] = None
units: Optional[str] = None
signal: Optional[int] = None
axis: Optional[int] = None
primary: Optional[int] = None
axes: Optional[str] = None
doc: Optional[str] = None
recommended: Optional[bool] = None
optional: Optional[bool] = None
minOccurs: Optional[int] = None
maxOccurs: Optional[Union[int, str]] = None
stride: Optional[bool] = None
data_offset: Optional[bool] = None
dimensions: Optional[Dimensions] = None
enumeration: Optional[Enumeration] = None
attribute: Optional[List[Attribute]] = None
deprecated: Optional[str] = None

class Config:
extra = "forbid"


class Group(Item):
type: str
name: Optional[str] = None # type[2:].upper()
doc: Optional[List[Optional[str]]] = None # NXmirror returns [None]
recommended: Optional[bool] = None
optional: Optional[bool] = None
minOccurs: Optional[int] = None
maxOccurs: Optional[Union[int, str]] = None
attribute: Optional[List[Attribute]] = None
field: Optional[List[Field]] = None
group: Optional[List["Group"]] = None
link: Optional[List["Link"]] = None
deprecated: Optional[str] = None

@pydantic.model_validator(mode="after")
def name_from_type(self) -> "Group":
if self.name is None:
self.name = self.type[2:].upper()
return self


class Link(Item):
name: str
target: str
doc: Optional[str] = None

class Config:
extra = "forbid"


class Choice(Item):
name: str
group: List[Group]

class Config:
extra = "forbid"


class Symbol(Item):
name: str
doc: str


class Symbols(Item):
symbol: Optional[List[Symbol]] = None
doc: Optional[str] = None


class Definition(Item):
xmlns: str
xmlns_xsi: str
name: str
type: str
category: str
xsi_schemaLocation: str
ignoreExtraGroups: bool
ignoreExtraFields: bool
ignoreExtraAttributes: bool
xmlns_xs: Optional[str] = None
xmlns_ns: Optional[str] = None
extends: Optional[str] = None
deprecated: Optional[str] = None
doc: Optional[str] = None
symbols: Optional[Symbols] = None
attribute: Optional[List[Attribute]] = None
field: Optional[List[Field]] = None
group: Optional[List[Group]] = None
link: Optional[List[Link]] = None
choice: Optional[List[Choice]] = None

class Config:
extra = "forbid"


def load_enumeration_item(enum_item: dict) -> dict:
data = dict()
for key, value in enum_item.items():
if key.startswith("@"):
key = key[1:]
data[key] = value
return data


def load_enumeration(enumeration: dict) -> dict:
data = dict()
for key, value in enumeration.items():
data[key] = [load_enumeration_item(item) for item in value]
return data


def load_doc_dimension_item(dim_item: dict) -> dict:
data = dict()
for key, value in dim_item.items():
if key.startswith("@"):
key = key[1:]
data[key] = value
return data


def load_dimension_item(dim_item: dict) -> dict:
data = dict()
for key, value in dim_item.items():
if key.startswith("@"):
key = key[1:]
data[key] = value
return data


def load_doc_dimensions(dimensions: dict) -> dict:
data = dict()
for key, value in dimensions.items():
if key.startswith("@"):
key = key[1:]
elif key == "dim":
value = [load_doc_dimension_item(item) for item in value]
data[key] = value
return data


def load_dimensions(dimensions: dict) -> dict:
data = dict()
for key, value in dimensions.items():
if key.startswith("@"):
key = key[1:]
elif key == "dim":
value = [load_dimension_item(item) for item in value]
elif key == "doc" and isinstance(value, dict):
value = load_doc_dimensions(value)
data[key] = value
return data


def load_link(link: dict) -> dict:
data = dict()
for key, value in link.items():
if key.startswith("@"):
key = key[1:]
data[key] = value
return data


def load_choice(choice: dict) -> dict:
data = dict()
for key, value in choice.items():
if key.startswith("@"):
key = key[1:]
elif key == "group":
value = [load_group(group) for group in value]
data[key] = value
return data


def load_attribute(attr: dict) -> dict:
data = dict()
for key, value in attr.items():
if key.startswith("@"):
key = key[1:]
elif key == "enumeration":
value = load_enumeration(value)
elif key == "dimensions":
value = load_dimensions(value)
data[key] = value
return data


def load_field(field: dict) -> dict:
data = dict()
for key, value in field.items():
if key.startswith("@"):
key = key[1:]
elif key == "attribute":
value = [load_attribute(attr) for attr in value]
elif key == "enumeration":
value = load_enumeration(value)
elif key == "dimensions":
value = load_dimensions(value)
data[key] = value
return data


def load_group(group: dict) -> dict:
data = dict()
for key, value in group.items():
if key.startswith("@"):
key = key[1:]
elif key == "attribute":
value = [load_attribute(attr) for attr in value]
elif key == "field":
value = [load_field(field) for field in value]
elif key == "group":
value = [load_group(group) for group in value]
elif key == "link":
value = [load_link(attr) for attr in value]
data[key] = value
return data


def load_symbol(symbol: dict) -> dict:
data = dict()
for key, value in symbol.items():
if key.startswith("@"):
key = key[1:]
data[key] = value
return data


def load_symbols(symbols: dict) -> dict:
data = dict()
for key, value in symbols.items():
if key.startswith("@"):
key = key[1:]
elif key == "symbol":
value = [load_symbol(attr) for attr in value]
data[key] = value
return data


def load_definition(definition: str) -> Definition:
data = dict()

for key, value in definition.items():
if key.startswith("@"):
key = key[1:].replace(":", "_")
elif key == "attribute":
value = [load_attribute(attr) for attr in value]
elif key == "field":
value = [load_field(attr) for attr in value]
elif key == "group":
value = [load_group(attr) for attr in value]
elif key == "link":
value = [load_link(attr) for attr in value]
elif key == "choice":
value = [load_choice(attr) for attr in value]
elif key == "symbols":
value = load_symbols(value)
data[key] = value

return Definition(**data)


if __name__ == "__main__":
from . import repo

names = repo.get_nxdl_class_names(
url="https://github.com/XraySpectroscopy/nexus_definitions.git"
)

for name in names:
load_definition(repo.get_nxdl_class(name))
Loading

0 comments on commit e6507ae

Please sign in to comment.