Attaching Metadata to Data ========================== The :class:`~imfusion.DataComponent` class allows attaching arbitrary metadata to any instance of :class:`~imfusion.Data`. The example below demonstrates this by attaching an :class:`~imfusion.ImageInfoDataComponent`, which can hold information such as patient ID, patient name, etc., to :class:`~imfusion.Data` , e.g. a :class:`~imfusion.SharedImageSet`. >>> import imfusion >>> sis = imfusion.SharedImageSet() >>> sis.components.add(imfusion.ImageInfoDataComponent()) ImageInfoDataComponent( patient_id="", patient_name="", ... ) >>> sis.components.image_info.patient_id = "my_patient_id" There is a variety of built-in :class:`~imfusion.DataComponent` available in the ImFusion SDK. The full list of :class:`~imfusion.DataComponent` can be obtained with :func:`~imfusion.available_data_components`. .. note:: The :func:`~imfusion.available_data_components` function returns all components that are implemented in the ImFusion C++ SDK, including those that are not yet available in the Python bindings and hence do not have a corresponding specialized class in the :mod:`imfusion` Python module. Regardless of whether Python bindings exist, :func:`~imfusion.create_data_component` enables creating every :class:`~imfusion.DataComponent` in Python: >>> prop = imfusion.Properties({"patientId": "my_patient_id"}) >>> image_info_data_comp = imfusion.create_data_component("ImageInfoDataComponent", prop) >>> image_info_data_comp ImageInfoDataComponent( patient_id="my_patient_id", patient_name="", ... ) As with any :class:`~imfusion.DataComponent`, the actual data fields of :class:`~imfusion.ImageInfoDataComponent` are accessible through Python attributes: >>> image_info_data_comp.patient_id 'my_patient_id' .. note:: There is a difference in naming conventions between the :class:`~imfusion.DataComponent`'s Python attributes (e.g., `patient_id`) and the corresponding :class:`~imfusion.Properties` keys (e.g., `patientId`). The former are listed in the documentation of the specific class, e.g., :class:`~imfusion.ImageInfoDataComponent`. To view the keys used in :class:`~imfusion.Properties`, you can instantiate the respective data component with its default constructor and inspect the parameters of its :class:`~imfusion.Properties`, as in the example below: >>> image_info_data_comp = imfusion.create_data_component("ImageInfoDataComponent") >>> prop: imfusion.Properties = image_info_data_comp.configuration() >>> prop.params() ['patientId', 'patientName', ...] :class:`~imfusion.Properties` may also be used to modify the data component after it has been created: >>> image_info_data_comp = imfusion.create_data_component("ImageInfoDataComponent") >>> prop = imfusion.Properties({"patientId": "my_patient_id"}) >>> image_info_data_comp.configure(prop) Provided the data component is available in our Python bindings, you can of course also instantiate it directly: >>> image_info_data_comp = imfusion.ImageInfoDataComponent() The data component system is also extensible from Python. You can create your own data components by subclassing :class:`~imfusion.DataComponent`: >>> class MyComponent(imfusion.DataComponent, accessor_name="my_component"): ... def __init__(self, custom_info: str=""): ... super().__init__() ... self.custom_info: str = custom_info ... ... def configure(self, properties: imfusion.Properties) -> None: ... self.custom_info = str(properties["custom_info"]) ... ... def configuration(self) -> imfusion.Properties: ... return imfusion.Properties({"custom_info": self.custom_info}) ... ... def __eq__(self, other: "MyComponent") -> bool: ... return self.custom_info == other.custom_info ... ... def __repr__(self) -> str: ... return f"MyComponent(custom_info={self.custom_info!r})" With the above class definition, you can attach your customized information to, e.g., any :class:`~imfusion.SharedImageSet`: >>> sis = imfusion.SharedImageSet() >>> sis.components.add(MyComponent(custom_info="my_custom_info")) MyComponent(custom_info='my_custom_info') >>> assert sis.components.my_component.custom_info == "my_custom_info" The value assigned to `accessor_name` will be used as the name of the Python attribute that is automatically created on the list of data components, i.e., `my_component` is now an attribute of `sis.components`. The methods :meth:`~imfusion.Configurable.configure` and :meth:`~imfusion.Configurable.configuration` are part of the :class:`~imfusion.Configurable` interface and are required to support serialization/deserialization of the component. If they are not implemented, the component will not persist when the respective :class:`~imfusion.Data` instance is, e.g., written to disk.