Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MetaData rework #281

Merged
merged 13 commits into from
Nov 14, 2024
119 changes: 118 additions & 1 deletion docker/irods_client/tests/test_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pytest import mark

from ibridges.data_operations import Operations
from ibridges.meta import MetaData
from ibridges.meta import MetaData, MetaDataItem
from ibridges.path import IrodsPath


Expand Down Expand Up @@ -66,6 +66,7 @@ def test_meta(item_name, request):
assert "x" in meta
assert ("y", "z") not in meta
assert ("y", "x") in meta
meta.clear()

@mark.parametrize("item_name", ["collection", "dataobject"])
def test_metadata_todict(item_name, request):
Expand Down Expand Up @@ -108,3 +109,119 @@ def test_metadata_export(item_name, request, session, tmpdir):
with open(tmp_file, "r", encoding="utf-8"):
new_meta_dict = json.load(tmp_file)
assert isinstance(new_meta_dict, dict)

@mark.parametrize("item_name", ["collection", "dataobject"])
def test_metadata_getitem(item_name, request):
item = request.getfixturevalue(item_name)
meta = MetaData(item)
meta.clear()

assert len(meta) == 0
meta.add("some_key", "some_value", "some_units")
assert isinstance(meta["some_key"], MetaDataItem)
meta.add("some_key", "some_value", None)
meta.add("some_key", "other_value", "some_units")
meta.add("other_key", "third_value", "other_units")
with pytest.raises(ValueError):
meta["some_key"]
with pytest.raises(ValueError):
meta["some_key", "some_value"]
assert isinstance(meta["some_key", "some_value", "some_units"], MetaDataItem)
assert tuple(meta["other_key"]) == ("other_key", "third_value", "other_units")
with pytest.raises(KeyError):
meta["unknown"]
with pytest.raises(KeyError):
meta["some_key", "unknown"]
with pytest.raises(KeyError):
meta["some_key", "some_value", "unknown"]
meta.clear()


@mark.parametrize("item_name", ["collection", "dataobject"])
def test_metadata_rename(item_name, request, session):
item = request.getfixturevalue(item_name)
meta = MetaData(item)
meta.clear()


meta.add("some_key", "some_value", "some_units")
meta["some_key"].key = "new_key"
assert ("new_key", "some_value", "some_units") in meta
assert len(meta) == 1

meta["new_key"].value = "new_value"
assert ("new_key", "new_value", "some_units") in meta
assert len(meta) == 1

meta["new_key"].units = "new_units"
assert ("new_key", "new_value", "new_units") in meta
assert len(meta) == 1

meta.add("new_key", "new_value", "other_units")
with pytest.raises(ValueError):
meta["new_key", "new_value", "other_units"].units = "new_units"
assert len(meta) == 2
meta["new_key", "new_value", "other_units"].remove()

meta.add("new_key", "other_value", "new_units")
with pytest.raises(ValueError):
meta["new_key", "other_value", "new_units"].value = "new_value"
assert len(meta) == 2
meta["new_key", "other_value", "new_units"].remove()

meta.add("other_key", "new_value", "new_units")
with pytest.raises(ValueError):
meta["other_key", "new_value", "new_units"].key = "new_key"
assert len(meta) == 2

with pytest.raises(ValueError):
meta["other_key"].key = "org_something"
assert len(meta) == 2
assert "other_key" in meta

meta.clear()


@mark.parametrize("item_name", ["collection", "dataobject"])
def test_metadata_findall(item_name, request, session):
item = request.getfixturevalue(item_name)
meta = MetaData(item)
meta.clear()


meta.add("some_key", "some_value", "some_units")
meta.add("some_key", "some_value", None)
meta.add("some_key", "other_value", "some_units")
meta.add("other_key", "third_value", "other_units")

assert len(meta.find_all()) == 4
assert len(meta.find_all(key="some_key")) == 3
assert isinstance(meta.find_all(key="some_key")[0], MetaDataItem)
assert len(meta.find_all(key="?")) == 0
assert len(meta.find_all(value="some_value")) == 2
assert len(meta.find_all(units="some_units")) == 2


@mark.parametrize("item_name", ["collection", "dataobject"])
def test_metadata_errors(item_name, request, session):
item = request.getfixturevalue(item_name)
meta = MetaData(item)
meta.clear()

with pytest.raises(ValueError):
meta.add("", "some_value")
with pytest.raises(TypeError):
meta.add(None, "some_value")
with pytest.raises(TypeError):
meta.add(10, "some_value")

with pytest.raises(ValueError):
meta.add("key", "")
with pytest.raises(TypeError):
meta.add("key", None)
with pytest.raises(TypeError):
meta.add("key", 10)

with pytest.raises(TypeError):
meta.add("key", "value", 10)

66 changes: 62 additions & 4 deletions docs/source/metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ Metadata
iRODS offers metadata as key, value, units triplets. The type of the keys, values and units is always a string.
Below we show how to create a :doc:`Metadata <api/generated/ibridges.meta.MetaData>` object from a data object or collection.

The Metadata object
--------------------
The MetaData class
------------------

.. code-block:: python

Expand All @@ -17,17 +17,30 @@ The Metadata object
session = interactive_auth()
meta = IrodsPath(session, "~", "collection_or_dataobject").meta

# Show all metadata entries with print.
print(meta)

With the object :code:`meta` we can now access and manipulate the metadata of the data object.

The MetaDataItem class
----------------------

As explained above, the metadata of a collection or dataobject can have multiple entries. You can iterate over
these entries as follows:

.. code-block:: python

for item in meta:
print(item.key, item.value, item.units)


Add metadata
------------
To add metadata, you always need to provide a key and a value, the units are optional and can be left out.

.. code-block:: python

meta.add('NewKey', 'NewValue', 'NewUnit')
print(meta)


.. note::
You can have several metadata entries with the same key but different values and units,
Expand All @@ -46,6 +59,51 @@ same key first. This mirrors the implementation of the `iCommands <https://rdm-d
meta.set('ExistingKey', 'Value', 'Unit')


Find metadata items
-------------------

If you want to find all items with a certain key/value/units, you can use the ``find_all`` method
which returns a list of items:

.. code-block:: python


# Find all metadata items with key "some_key".
items = meta.find_all(key="some_key")

# Find all metadata items with value "some_value".
items = meta.find_all(value="some_value")

# Find all metadata items with some units "some_units".
items = meta.find_all(units="some_units")

# Find all metadata items with key == "some_key" and value == "some_value"
items = meta.find_all(key="some_key", value="some_value")

If you are searching for one specific metadata item, then you can also use the following notation,
which will either give back one metadata item, raise a ``KeyError`` if no item matches the criteria, or
a ``ValueError`` if more than one value matches the criteria:

.. code-block:: python

item = meta["some_key"]
item = meta["some_key", "some_value", "some_units"]

Modify metadata items
---------------------

You can also rename the ``key``, ``value`` and ``units`` of a metadata item, by setting it to a new value:

.. code-block:: python

item = metadata["some_key"]
item.key = "new_key"
item.value = "new_value"
item.units = "new_units"

If you are trying to rename the metadata item so that it would overwrite an existing metadata item,
ibridges will throw an error.

Delete metadata
---------------
Below are examples on how to delete metadata entries:
Expand Down
Loading
Loading