Coverage for src/metador_core/schema/common/__init__.py: 80%
90 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-02 09:33 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-02 09:33 +0000
1"""Common metadata models based on RO-Crate and Schema.org."""
3from __future__ import annotations
5from typing import Any, Dict, List, Optional, Tuple
7from pydantic import parse_obj_as
9from ...plugins import schemas
10from .. import MetadataSchema
11from ..decorators import make_mandatory
12from ..parser import BaseParser
13from ..types import PintQuantity, PintUnit
14from .rocrate import Person
15from .schemaorg import Number, QuantitativeValue, Text
17# ----
20class SIValueParser(BaseParser):
21 @classmethod
22 def parse(cls, tcls, v) -> SIValue:
23 if isinstance(v, str):
24 q = parse_obj_as(PintQuantity, v)
25 return tcls(value=q.m, unitText=str(q.u))
27 # special case: its a quantitative value already,
28 # just check unit and repack as SIValue
29 if isinstance(v, tcls.__base__):
30 v.unitText = str(parse_obj_as(PintUnit, v.unitText or ""))
31 return tcls.construct(v.dict())
33 # important: parse back serialized data!
34 if isinstance(v, dict):
35 return tcls.validate(v)
37 msg = f"Cannot parse {v} ({type(v).__name__}) into a {tcls.__name__}!"
38 raise TypeError(msg)
41class SIValue(QuantitativeValue):
42 """QuantitativeValue that holds a numerical value given in SI units.
44 Uses the `pint` library to parse and normalize the unit.
45 """
47 value: Number
48 Parser = SIValueParser
51class NumValue(QuantitativeValue):
52 """Quantitative value that can have a unit out of a fixed list."""
54 value: Number
56 class Parser(BaseParser):
57 schema_info: Dict[str, Any] = {}
59 allowed_units: List[str] = []
60 infer_unit: Optional[str] = None
61 require_unit: bool = False
63 @classmethod
64 def parse(cls, tcls, v):
65 if isinstance(v, (int, float)):
66 if cls.require_unit:
67 raise ValueError(f"Value '{v}' must have a unit!")
68 return tcls.construct(value=v, unitText=cls.infer_unit)
70 if isinstance(v, str):
71 arr = v.strip().split(maxsplit=1)
73 # important to parse back serialized data!
74 if isinstance(v, dict):
75 v = tcls.__base__.validate(v) # -> QuantitativeValue
77 if isinstance(v, tcls.__base__): # unpack QuantitativeValue
78 def_unit = cls.infer_unit or ""
79 arr = (v.value, v.unitText or v.unitCode or def_unit)
81 # check that value and unit are valid:
83 if len(arr) == 1: # no unit given?
84 if cls.require_unit:
85 raise ValueError(f"Value '{v}' must have a unit!")
86 val = parse_obj_as(Number, arr[0])
87 # return with inferred unit
88 return tcls.construct(value=val, unitText=cls.infer_unit)
90 val = parse_obj_as(Tuple[Number, str], arr)
91 if cls.allowed_units and val[1] not in cls.allowed_units:
92 msg = (
93 f"Invalid unit '{val[1]}', unit must be one of {cls.allowed_units}"
94 )
95 raise ValueError(msg)
96 return tcls.construct(value=val[0], unitText=val[1])
99class Pixels(NumValue):
100 """Numeric value representing pixels."""
102 class Parser(NumValue.Parser):
103 allowed_units = ["px"]
104 infer_unit = "px"
107# ----
109FileMeta: Any = schemas.get("core.file", (0, 1, 0))
110DirMeta: Any = schemas.get("core.dir", (0, 1, 0))
113@make_mandatory("name", "abstract", "dateCreated")
114class BibMeta(DirMeta):
115 """Minimal bibliographic metadata required for a container."""
117 class Plugin:
118 name = "core.bib"
119 version = (0, 1, 0)
121 author: List[Person]
122 """List of authors (creators of the actual data)."""
125class ImageFileMeta(FileMeta):
126 """A rasterized image file with known dimensions.
128 Also serves as marker schema for the imagefile widget.
129 """
131 class Plugin:
132 name = "core.imagefile"
133 version = (0, 1, 0)
135 width: Pixels
136 """Width of the image in pixels."""
138 height: Pixels
139 """Height of the image in pixels."""
142# TODO: see https://github.com/Materials-Data-Science-and-Informatics/metador-core/issues/8
145class ColumnHeader(MetadataSchema):
146 """Table column metadata."""
148 name: Text
149 """Column title."""
151 unit: PintUnit
152 """Physical unit for this column."""
155class TableMeta(MetadataSchema):
156 """Table metadata."""
158 class Plugin:
159 name = "core.table"
160 version = (0, 1, 0)
162 name: Text
163 """Table title."""
165 columns: List[ColumnHeader]
166 """List of column descriptions."""