from __future__ import annotations
__all__ = ["Attribute"]
import dataclasses
from typing import Any, Dict, Optional
from typing_extensions import Self
from medkit.core import dict_conv
from medkit.core.id import generate_id
[docs]@dataclasses.dataclass
class Attribute(dict_conv.SubclassMapping):
"""
Medkit attribute, to be added to an annotation
Attributes
----------
label:
The attribute label
value:
The value of the attribute. Should be either simple built-in types (int,
float, bool, str) or collections of these types (list, dict, tuple). If
you need structured complex data you should create a subclass of
`Attribute`.
metadata:
The metadata of the attribute
uid:
The identifier of the attribute
"""
label: str
value: Optional[Any]
metadata: Dict[str, Any]
uid: str
def __init__(
self,
label: str,
value: Optional[Any] = None,
metadata: Optional[Dict[str, Any]] = None,
uid: Optional[str] = None,
):
if metadata is None:
metadata = {}
if uid is None:
uid = generate_id()
self.uid = uid
self.label = label
self.value = value
self.metadata = metadata
def __init_subclass__(cls):
Attribute.register_subclass(cls)
super().__init_subclass__()
def to_dict(self) -> Dict[str, Any]:
attribute_dict = dict(
uid=self.uid,
label=self.label,
value=self.value,
metadata=self.metadata,
)
dict_conv.add_class_name_to_data_dict(self, attribute_dict)
return attribute_dict
[docs] def to_brat(self) -> Optional[Any]:
"""
Return a value compatible with the brat format
"""
return self.value
[docs] def to_spacy(self) -> Optional[Any]:
"""
Return a value compatible with spaCy
"""
return self.value
[docs] def copy(self) -> Attribute:
"""
Create a new attribute that is a copy of the current instance, but
with a new identifier
This is used when we want to duplicate an existing attribute onto a
different annotation.
"""
return dataclasses.replace(self, uid=generate_id())
[docs] @classmethod
def from_dict(cls, attribute_dict: Dict[str, Any]) -> Self:
"""
Creates an Attribute from a dict
Parameters
----------
attribute_dict: dict
A dictionary from a serialized Attribute as generated by to_dict()
"""
subclass = cls.get_subclass_for_data_dict(attribute_dict)
if subclass is not None:
return subclass.from_dict(attribute_dict)
return cls(
uid=attribute_dict["uid"],
label=attribute_dict["label"],
value=attribute_dict["value"],
metadata=attribute_dict["metadata"],
)