From a862da207e6a97d1ab316c419347dcbc39f1f9b4 Mon Sep 17 00:00:00 2001 From: MatthiasProbst Date: Wed, 1 May 2024 09:15:45 +0200 Subject: [PATCH] move attrsdef to rdf[].definition --- h5rdmtoolbox/_repr.py | 4 ++-- h5rdmtoolbox/wrapper/core.py | 27 ++++++++------------------- h5rdmtoolbox/wrapper/h5attr.py | 11 +++++------ h5rdmtoolbox/wrapper/jsonld.py | 27 ++++++++++++++------------- h5rdmtoolbox/wrapper/rdf.py | 17 +++++++++++++++-- tests/test_repr.py | 2 +- tests/wrapper/test_rdf.py | 12 ++++-------- 7 files changed, 49 insertions(+), 51 deletions(-) diff --git a/h5rdmtoolbox/_repr.py b/h5rdmtoolbox/_repr.py index 0316ce02..e30c6808 100644 --- a/h5rdmtoolbox/_repr.py +++ b/h5rdmtoolbox/_repr.py @@ -298,7 +298,7 @@ def __attrs__(self, name, h5obj) -> str: use_attr_name = name else: pred = h5obj.rdf[name].get(RDF_PREDICATE_ATTR_NAME, None) - attrsdef = h5obj.attrsdef.get(name, None) + attrsdef = h5obj.rdf[name].definition if pred and attrsdef: use_attr_name = f'{name} (p={pred}, def={attrsdef})' elif pred: @@ -576,7 +576,7 @@ def __attrs__(self, name, h5obj): if rdf_predicate is not None: disp_name += get_iri_icon_href(rdf_predicate, icon_url=IRI_ICON) - attrs_def = h5obj.attrsdef.get(name, None) + attrs_def = h5obj.rdf[name].definition if attrs_def is not None: disp_name += get_def_icon_href(attrs_def) diff --git a/h5rdmtoolbox/wrapper/core.py b/h5rdmtoolbox/wrapper/core.py index 938b9aeb..9e3b89c1 100644 --- a/h5rdmtoolbox/wrapper/core.py +++ b/h5rdmtoolbox/wrapper/core.py @@ -28,7 +28,6 @@ from .. import get_ureg from .. import protocols from .._repr import H5Repr, H5PY_SPECIAL_ATTRIBUTES -from ..convention import definition from ..convention.consts import DefaultValue logger = logging.getLogger('h5rdmtoolbox') @@ -406,10 +405,10 @@ def iri(self): warnings.warn('Property "iri" is deprecated. Use "rdf" instead.', DeprecationWarning) return rdf.RDFManager(self.attrs) - @property - def attrsdef(self) -> definition.DefinitionManager: - """Return DefinitionManager""" - return definition.DefinitionManager(self.attrs) + # @property + # def attrsdef(self) -> definition.DefinitionManager: + # """Return DefinitionManager""" + # return definition.DefinitionManager(self.attrs) def get_tree_structure(self, recursive=True, ignore_attrs: List[str] = None): """Return the tree (attributes, names, shapes) of the group and subgroups""" @@ -1411,11 +1410,6 @@ def iri(self): warnings.warn('Property "iri" is deprecated. Use "rdf" instead.', DeprecationWarning) return rdf.RDFManager(self.attrs) - @property - def attrsdef(self) -> definition.DefinitionManager: - """Return DefinitionManager""" - return definition.DefinitionManager(self.attrs) - @property def hdf_filename(self) -> pathlib.Path: """The filename of the file, even if the HDF5 file is closed.""" @@ -1515,11 +1509,6 @@ def attach_ancillary_dataset(self, ancillary_dataset: Union[str, h5py.Dataset]): self.attrs[consts.ANCILLARY_DATASET] = ancillary_datasets return self - # @property - # def has_flag_data(self): - # """Check if the dataset has a flag dataset attached.""" - # return ANCILLARY_DATASET in self.attrs and self.attrs[FLAG_DATASET_CONST] in self.parent - def detach_data_scale(self): """Remove the attached data scale dataset from this dataset.""" warnings.warn('Note, that detaching data scale may influence the correctness and traceability of your data', @@ -2171,10 +2160,10 @@ def iri(self): warnings.warn('Property "iri" is deprecated. Use "rdf" instead.', DeprecationWarning) return rdf.RDFManager(self.attrs) - @property - def attrsdef(self) -> definition.DefinitionManager: - """Return DefinitionManager""" - return definition.DefinitionManager(self.attrs) + # @property + # def attrsdef(self) -> definition.DefinitionManager: + # """Return DefinitionManager""" + # return definition.DefinitionManager(self.attrs) def moveto(self, destination: Path, overwrite: bool = False) -> Path: """Move the opened file to a new destination. diff --git a/h5rdmtoolbox/wrapper/h5attr.py b/h5rdmtoolbox/wrapper/h5attr.py index 55327467..8f2c0d49 100644 --- a/h5rdmtoolbox/wrapper/h5attr.py +++ b/h5rdmtoolbox/wrapper/h5attr.py @@ -1,20 +1,19 @@ """Attribute module""" import ast +import h5py import json import logging -import warnings -from datetime import datetime -from typing import Dict, Union, Tuple, Optional, Any - -import h5py import numpy as np import pint import pydantic import rdflib +import warnings +from datetime import datetime from h5py._hl.attrs import AttributeManager from h5py._hl.base import with_phil from h5py._objects import ObjectID, phil from pydantic import HttpUrl +from typing import Dict, Union, Tuple, Optional, Any from .h5utils import get_rootparent from .. import errors @@ -265,7 +264,7 @@ def create(self, if rdf_object is not None: self._parent.rdf.object[name] = rdf_object if definition is not None: - self._parent.attrsdef[name] = definition + self._parent.rdf[name].definition = definition return r @with_phil diff --git a/h5rdmtoolbox/wrapper/jsonld.py b/h5rdmtoolbox/wrapper/jsonld.py index a44a646f..995ddbfc 100644 --- a/h5rdmtoolbox/wrapper/jsonld.py +++ b/h5rdmtoolbox/wrapper/jsonld.py @@ -1,18 +1,17 @@ +import h5py import json import logging -import pathlib -import warnings -from itertools import count -from typing import Dict, List -from typing import Optional, Union, Iterable, Tuple, Any - -import h5py import numpy as np import ontolutils +import pathlib import rdflib +import warnings +from itertools import count from ontolutils.classes.utils import split_URIRef -from rdflib import Graph, URIRef, Literal, BNode, XSD, RDF, SKOS +from rdflib import Graph, URIRef, BNode, XSD, RDF, SKOS from rdflib.plugins.shared.jsonld.context import Context +from typing import Dict, List +from typing import Optional, Union, Iterable, Tuple, Any from h5rdmtoolbox.convention import hdf_ontology from .core import Dataset, File @@ -94,7 +93,8 @@ def build_node_list(g: Graph, data: List, use_simple_bnode_value: bool = True) - if i == n - 1: flag_list_rest = RDF.nil else: - flag_list_rest = rdflib.BNode(value=f'N{next(_bnode_counter)}') if use_simple_bnode_value else rdflib.BNode() + flag_list_rest = rdflib.BNode( + value=f'N{next(_bnode_counter)}') if use_simple_bnode_value else rdflib.BNode() g.add((flag_list, RDF.rest, flag_list_rest)) flag_list = flag_list_rest @@ -149,7 +149,7 @@ def resolve_iri(key_or_iri: str, context: Context) -> str: # return Literal(_av) -def _get_id(_node, use_simple_bnode_value:bool=True) -> Union[URIRef, BNode]: +def _get_id(_node, use_simple_bnode_value: bool = True) -> Union[URIRef, BNode]: """if an attribute in the node is called "@id", use that, otherwise use the node name""" _id = _node.rdf.subject # _node.attrs.get('@id', None) # if local is not None: @@ -638,7 +638,8 @@ def _add_hdf_node(name, obj, ctx) -> Dict: if structural: # add hdf type and name nodes _add_node(g, (attr_node, RDF.type, HDF5.Attribute)) - attr_def: str = obj.attrsdef.get(ak, None) + # attr_def: str = obj.attrsdef.get(ak, None) + attr_def: str = obj.rdf[ak].definition# .get(ak, None) if attr_def: _add_node(g, (attr_node, HDF5.name, rdflib.Literal(ak))) _add_node(g, (attr_node, SKOS.definition, rdflib.Literal(attr_def))) @@ -741,8 +742,8 @@ def _parse_val(_k, _v): attr_object = None - - attr_def = obj.attrsdef.get(ak, None) + # attr_def = obj.attrsdef.get(ak, None) + attr_def = obj.rdf[ak].definition#.get(ak, None) if attr_def: _add_node(g, (attr_node, SKOS.definition, rdflib.Literal(attr_def))) diff --git a/h5rdmtoolbox/wrapper/rdf.py b/h5rdmtoolbox/wrapper/rdf.py index ea5d217a..b6ec5de2 100644 --- a/h5rdmtoolbox/wrapper/rdf.py +++ b/h5rdmtoolbox/wrapper/rdf.py @@ -1,16 +1,17 @@ """RDF (Resource Description Framework) module for use with HDF5 files""" import abc -from typing import Dict, Union, Optional, List - import h5py import pydantic from pydantic import HttpUrl +from typing import Dict, Union, Optional, List RDF_OBJECT_ATTR_NAME = 'RDF_OBJECT' RDF_PREDICATE_ATTR_NAME = 'RDF_PREDICATE' RDF_SUBJECT_ATTR_NAME = '@ID' # equivalent to @ID in JSON-LD, thus can only be one value!!! RDF_TYPE_ATTR_NAME = '@TYPE' # equivalent to @type in JSON-LD, thus can be multiple values. +DEFINITION_ATTR_NAME = 'ATTR_DEFINITION' + class RDFError(Exception): """Generic RDF error""" @@ -154,6 +155,18 @@ def object(self, value): else: set_object(self._attr, self._attr_name, value) + @property + def definition(self): + """Return the definition of the attribute""" + return self._attr.get(DEFINITION_ATTR_NAME, {}).get(self._attr_name, None) + + @definition.setter + def definition(self, definition: str): + """Define the attribute. JSON-LD export will interpret this as SKOS.definition.""" + attr_def = self._attr.get(DEFINITION_ATTR_NAME, {}) + attr_def.update({self._attr_name: definition}) + self._attr[DEFINITION_ATTR_NAME] = attr_def + class _RDFPO(abc.ABC): """Abstract class for predicate (P) and object (O)""" diff --git a/tests/test_repr.py b/tests/test_repr.py index 7a479c7b..28c357e8 100644 --- a/tests/test_repr.py +++ b/tests/test_repr.py @@ -68,7 +68,7 @@ def test_dump_orcid(self): def test_repr_def(self): with File() as h5: h5.attrs['orcid'] = h5tbx.__author_orcid__ - h5.attrsdef['orcid'] = 'https://example.com/hasOrcid' + h5.rdf['orcid'].definition = 'https://example.com/hasOrcid' ssr = _repr.HDF5StructureStrRepr() ssr(h5) diff --git a/tests/wrapper/test_rdf.py b/tests/wrapper/test_rdf.py index 2d8710fd..f0178aad 100644 --- a/tests/wrapper/test_rdf.py +++ b/tests/wrapper/test_rdf.py @@ -365,18 +365,14 @@ def test_find_object(self): self.assertEqual(len(res), 1) - - def test_definition(self): with h5tbx.File() as h5: h5.attrs['title'] = 'test' - h5.attrsdef['title'] = 'This is the title of the dataset' - self.assertEqual(h5.attrsdef['title'], 'This is the title of the dataset') + h5.rdf['title'].definition = 'This is the title of the dataset' + # h5.attrsdef['title'] = 'This is the title of the dataset' + self.assertEqual(h5.rdf['title'].definition, 'This is the title of the dataset') h5.attrs['name'] = h5tbx.Attribute('Matthias', definition='This is the name of the person to contact') - self.assertEqual(h5.attrsdef['name'], 'This is the name of the person to contact') + self.assertEqual(h5.rdf['name'].definition, 'This is the name of the person to contact') h5.dumps() - - with self.assertRaises(KeyError): - h5.attrsdef['test'] = 'This should not work!'