Skip to content

Commit 5463f12

Browse files
authored
Merge pull request #46 from joshmoore/fix-45-labels
Link shapes (and their annotations)
2 parents bd20cfa + cc2b897 commit 5463f12

File tree

2 files changed

+74
-35
lines changed

2 files changed

+74
-35
lines changed

src/omero_rdf/__init__.py

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,21 @@ def __call__(self, o: BlitzObjectWrapper) -> URIRef:
387387
data = encoder.encode(o)
388388
return self.handle(data)
389389

390+
def annotations(self, obj, objid):
391+
"""
392+
Loop through all annotations and handle them individually.
393+
"""
394+
if isinstance(obj, IObject):
395+
# Not a wrapper object
396+
for annotation in obj.linkedAnnotationList():
397+
annid = self(annotation)
398+
self.contains(objid, annid)
399+
else:
400+
for annotation in obj.listAnnotations(None):
401+
obj._loadAnnotationLinks()
402+
annid = self(annotation)
403+
self.contains(objid, annid)
404+
390405
def handle(self, data: Data) -> URIRef:
391406
"""
392407
Parses the data object into RDF triples.
@@ -412,6 +427,15 @@ def handle(self, data: Data) -> URIRef:
412427

413428
return _id
414429

430+
def contains(self, parent, child):
431+
"""
432+
Use emit to generate isPartOf and hasPart triples
433+
434+
TODO: add an option to only choose one of the two directions.
435+
"""
436+
self.emit((child, DCTERMS.isPartOf, parent))
437+
self.emit((parent, DCTERMS.hasPart, child))
438+
415439
def emit(self, triple: Triple):
416440
if self.formatter.streaming:
417441
print(self.formatter.serialize_triple(triple), file=self.filehandle)
@@ -623,68 +647,56 @@ def descend(
623647
scrid = handler(scr)
624648
for plate in scr.listChildren():
625649
pltid = self.descend(gateway, plate._obj, handler)
626-
handler.emit((pltid, DCTERMS.isPartOf, scrid))
627-
handler.emit((scrid, DCTERMS.hasPart, pltid))
628-
for annotation in scr.listAnnotations(None):
629-
annid = handler(annotation)
630-
handler.emit((annid, DCTERMS.isPartOf, scrid))
650+
handler.contains(scrid, pltid)
651+
handler.annotations(scr, scrid)
631652
return scrid
632653

633654
elif isinstance(target, Plate):
634655
plt = self._lookup(gateway, "Plate", target.id)
635656
pltid = handler(plt)
636-
for annotation in plt.listAnnotations(None):
637-
annid = handler(annotation)
638-
handler.emit((annid, DCTERMS.isPartOf, pltid))
657+
handler.annotations(plt, pltid)
639658
for well in plt.listChildren():
640659
wid = handler(well) # No descend
641-
handler.emit((wid, DCTERMS.isPartOf, pltid))
660+
handler.contains(pltid, wid)
642661
for idx in range(0, well.countWellSample()):
643662
img = well.getImage(idx)
644663
imgid = self.descend(gateway, img._obj, handler)
645-
handler.emit((imgid, DCTERMS.isPartOf, wid))
646-
handler.emit((wid, DCTERMS.hasPart, imgid))
664+
handler.contains(wid, imgid)
647665
return pltid
648666

649667
elif isinstance(target, Project):
650668
prj = self._lookup(gateway, "Project", target.id)
651669
prjid = handler(prj)
652-
for annotation in prj.listAnnotations(None):
653-
annid = handler(annotation)
654-
handler.emit((annid, DCTERMS.isPartOf, prjid))
670+
handler.annotations(prj, prjid)
655671
for ds in prj.listChildren():
656672
dsid = self.descend(gateway, ds._obj, handler)
657-
handler.emit((dsid, DCTERMS.isPartOf, prjid))
658-
handler.emit((prjid, DCTERMS.hasPart, dsid))
673+
handler.contains(prjid, dsid)
659674
return prjid
660675

661676
elif isinstance(target, Dataset):
662677
ds = self._lookup(gateway, "Dataset", target.id)
663678
dsid = handler(ds)
664-
for annotation in ds.listAnnotations(None):
665-
annid = handler(annotation)
666-
handler.emit((annid, DCTERMS.isPartOf, dsid))
679+
handler.annotations(ds, dsid)
667680
for img in ds.listChildren():
668681
imgid = self.descend(gateway, img._obj, handler)
669-
handler.emit((imgid, DCTERMS.isPartOf, dsid))
670-
handler.emit((dsid, DCTERMS.hasPart, imgid))
682+
handler.contains(dsid, imgid)
671683
return dsid
672684

673685
elif isinstance(target, Image):
674686
img = self._lookup(gateway, "Image", target.id)
675687
imgid = handler(img)
676688
if img.getPrimaryPixels() is not None:
677689
pixid = handler(img.getPrimaryPixels())
678-
handler.emit((pixid, DCTERMS.isPartOf, imgid))
679-
handler.emit((imgid, DCTERMS.hasPart, pixid))
680-
for annotation in img.listAnnotations(None):
681-
img._loadAnnotationLinks()
682-
annid = handler(annotation)
683-
handler.emit((annid, DCTERMS.isPartOf, imgid))
690+
handler.contains(imgid, pixid)
691+
handler.annotations(img, imgid)
684692
for roi in self._get_rois(gateway, img):
685693
roiid = handler(roi)
686-
handler.emit((roiid, DCTERMS.isPartOf, pixid))
687-
handler.emit((pixid, DCTERMS.hasPart, roiid))
694+
handler.annotations(roi, roiid)
695+
handler.contains(pixid, roiid)
696+
for shape in roi.iterateShapes():
697+
shapeid = handler(shape)
698+
handler.annotations(shape, shapeid)
699+
handler.contains(roiid, shapeid)
688700
return imgid
689701

690702
else:

test/integration/clitest/test_rdf.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121

2222
from omero.testlib.cli import CLITest
2323
from omero_rdf import RdfControl
24-
from omero.model import RoiI
24+
from omero.model import LabelI, RoiI, CommentAnnotationI
25+
from omero.rtypes import rstring
2526

26-
from rdflib import Graph, Namespace, RDF
27+
from rdflib import Graph, Literal, Namespace, RDF
2728
from rdflib.namespace import DCTERMS
2829

2930

@@ -53,7 +54,17 @@ def test_rois(self, capfd):
5354
# Setup a test image with a roi
5455
pix = self.create_pixels()
5556
img = pix.image
57+
roi_ann = CommentAnnotationI()
58+
roi_ann.setTextValue(rstring("my roi annotation"))
5659
roi = RoiI()
60+
roi.setDescription(rstring("please check me"))
61+
roi.linkAnnotation(roi_ann)
62+
label_ann = CommentAnnotationI()
63+
label_ann.setTextValue(rstring("my label annotation"))
64+
label = LabelI()
65+
label.setTextValue(rstring("this is the label"))
66+
label.linkAnnotation(label_ann)
67+
roi.addShape(label)
5768
img.addRoi(roi)
5869
img = update.saveAndReturnObject(img)
5970

@@ -68,11 +79,27 @@ def test_rois(self, capfd):
6879
g.parse(data=out, format="ttl")
6980

7081
xml = Namespace("http://www.openmicroscopy.org/Schemas/OME/2016-06#")
71-
82+
self.assert_contained_type(g, xml.ROI, xml.Pixels)
83+
for x in (
84+
"my roi annotation",
85+
"please check me",
86+
"my label annotation",
87+
"this is the label",
88+
):
89+
self.assert_string_found(g, x)
90+
91+
def assert_contained_type(self, g, child_type, parent_type):
7292
found = False
7393
for s, p, o in g.triples((None, DCTERMS.isPartOf, None)):
74-
print(s, p, o)
75-
if (s, RDF.type, xml.ROI) in g and (o, RDF.type, xml.Pixels) in g:
94+
if (s, RDF.type, child_type) in g and (o, RDF.type, parent_type) in g:
7695
found = True
96+
break
97+
assert found, f"no link between {parent_type} and {child_type}:" + g.serialize()
7798

78-
assert found, "no link between pixels and ROI:" + g.serialize()
99+
def assert_string_found(self, g, search_string):
100+
found = False
101+
search_literal = Literal(search_string)
102+
for s, p, o in g.triples((None, None, search_literal)):
103+
found = True
104+
break
105+
assert found, f"string not found '{search_string}':\n" + g.serialize()

0 commit comments

Comments
 (0)