Example #1
0
    def test_text_turtle_another_session(self):
        """Test to a non-default session."""
        another_session = CoreSession()
        with CoreSession() as session:
            test_data_path = str(
                Path(__file__).parent / "test_importexport_data.ttl")
            with open(test_data_path, "r") as test_data_file:
                test_data = test_data_file.read()
            test_data = io.StringIO(test_data)
            loaded_objects = import_cuds(test_data,
                                         format="text/turtle",
                                         session=another_session)
            # The expected objects will not be found in session, they will
            # be none.
            expected_objects = tuple(
                session.load_from_iri(
                    rdflib.URIRef(
                        f"http://example.org/test-ontology#x_{i}")).first()
                for i in range(1, 5))
            self.assertTrue(all(x is None for x in expected_objects))

            # Test correctness in the other session.
            data_integrity(self,
                           another_session,
                           loaded_objects,
                           label="import")
Example #2
0
 def test_all(self):
     """Test the all() method."""
     r = QueryResult(CoreSession(), iter(range(10)))
     self.assertEqual(r.all(), list(range(10)))
     self.assertEqual(r.all(), list(range(10)))
     r = QueryResult(CoreSession(), iter(range(10)))
     next(r)
     self.assertEqual(r.all(), list(range(10)))
     self.assertEqual(r.all(), list(range(10)))
Example #3
0
 def test_first(self):
     """Test the first() method."""
     r = QueryResult(CoreSession(), iter(range(10)))
     self.assertEqual(r.first(), 0)
     self.assertEqual(r.first(), 0)
     self.assertEqual(r.all(), list(range(10)))
     r = QueryResult(CoreSession(), iter(range(0)))
     self.assertEqual(r.first(), None)
     self.assertEqual(r.first(), None)
     self.assertEqual(r.all(), list(range(0)))
Example #4
0
 def test_text_turtle(self):
     """Test importing and exporting the `text/turtle` mime type."""
     with CoreSession() as session:
         test_data_path = str(
             Path(__file__).parent / "test_importexport_data.ttl")
         loaded_objects = import_cuds(test_data_path, format="text/turtle")
         data_integrity(self, session, loaded_objects, label="import")
         exported_file = io.StringIO()
         export_cuds(file=exported_file, format="text/turtle")
         exported_file.seek(0)
     with CoreSession() as session:
         exported_objects = import_cuds(exported_file, format="text/turtle")
         data_integrity(self, session, exported_objects, label="export")
Example #5
0
 def test_application_json(self):
     """Test importing and exporting the `application/ld+json` mime type."""
     with CoreSession() as session:
         test_data_path = str(
             Path(__file__).parent / "test_importexport_data.json")
         loaded_objects = import_cuds(test_data_path,
                                      format="application/ld+json")
         data_integrity(self, session, loaded_objects, label="import")
         exported_file = io.StringIO()
         export_cuds(file=exported_file, format="application/ld+json")
         exported_file.seek(0)
     with CoreSession() as session:
         exported_objects = import_cuds(exported_file,
                                        format="application/ld+json")
         data_integrity(self, session, exported_objects, label="export")
Example #6
0
 def test_one(self):
     """Test the one() method."""
     r = QueryResult(CoreSession(), iter(range(10)))
     self.assertRaises(MultipleResultsError, r.one)
     self.assertRaises(MultipleResultsError, r.one)
     self.assertEqual(r.all(), list(range(10)))
     r = QueryResult(CoreSession(), iter(range(2)))
     next(r)
     self.assertRaises(MultipleResultsError, r.one)
     self.assertRaises(MultipleResultsError, r.one)
     self.assertEqual(r.all(), list(range(2)))
     r = QueryResult(CoreSession(), iter(range(0)))
     self.assertRaises(ResultEmptyError, r.one)
     self.assertRaises(ResultEmptyError, r.one)
     self.assertEqual(r.all(), list(range(0)))
Example #7
0
    def test_recursive_store(self):
        """Test if _recursive_store correctly stores cuds_objects correctly.

        It should correct dangling and one-way connections.
        """
        c = city.City(name="Freiburg")
        p1 = city.Citizen()
        p2 = city.Citizen()
        p3 = city.Citizen()
        p4 = city.Citizen()
        with CoreSession() as session:
            w = city.CityWrapper(session=session)
            cw = w.add(c)

            c.add(p1, p2, p3, rel=city.hasInhabitant)
            p3.add(p1, p2, rel=city.isChildOf)
            p3.add(p4, rel=city.hasChild)

            cw = w._recursive_store(c, cw)

            p1w, p2w, p3w = cw.get(p1.uid, p2.uid, p3.uid)
            p4w = p3w.get(p4.uid)

            self.assertEqual(w.get(rel=city.hasPart), [cw])
            self.assertEqual(set(cw.get(rel=city.hasInhabitant)),
                             {p1w, p2w, p3w})
            self.assertEqual(set(cw.get(rel=city.isPartOf)), {w})

            self.assertEqual(p3w.get(rel=city.INVERSE_OF_hasInhabitant), [cw])
            self.assertEqual(set(p3w.get(rel=city.isChildOf)), {p1w, p2w})
            self.assertEqual(p3w.get(rel=city.hasChild), [p4w])
Example #8
0
    def test_fix_old_neighbors(self):
        """Check if _fix_old_neighbors.

        - Deletes old children.
        - Adds connection to old parents.
        """
        c = city.City(name="Freiburg")

        with CoreSession() as session:
            wrapper = city.CityWrapper(session=session)
            cw = wrapper.add(c)
            n = city.Neighborhood(name="Zähringen")
            nw = cw.add(n)

            c = clone_cuds_object(c)
            c._session = session
            old_neighbor_diff = get_neighbor_diff(cw, c)
            old_neighbors = session.load(*[x[0] for x in old_neighbor_diff])
            Cuds._fix_old_neighbors(
                new_cuds_object=c,
                old_cuds_object=cw,
                old_neighbors=old_neighbors,
                old_neighbor_diff=old_neighbor_diff,
            )
        self.assertEqual(c.get(rel=city.isPartOf), [wrapper])
        self.assertEqual(c.get(rel=city.hasPart), [])
        self.assertEqual(nw.get(rel=city.isPartOf), [])
        self.assertEqual(wrapper.get(rel=city.hasPart), [c])
Example #9
0
 def test_application_rdf_xml_guess_format(self):
     """Test guessing and importing the `application/rdf+xml` mime type."""
     with CoreSession() as session:
         test_data_path = str(
             Path(__file__).parent / "test_importexport_data.owl")
         loaded_objects = import_cuds(test_data_path)
         data_integrity(self, session, loaded_objects, label="import")
Example #10
0
 def test_text_turtle_guess_format(self):
     """Test guessing and importing the `text/turtle` mime type."""
     with CoreSession() as session:
         test_data_path = str(
             Path(__file__).parent / "test_importexport_data.ttl")
         loaded_objects = import_cuds(test_data_path)
         data_integrity(self, session, loaded_objects, label="import")
Example #11
0
 def test_next(self):
     """Test __next__ magic method."""
     r = QueryResult(CoreSession(), iter(range(10)))
     self.assertEqual(0, next(r))
     self.assertEqual(1, next(r))
     self.assertEqual(2, next(r))
     self.assertEqual(r.all(), list(range(10)))
     self.assertRaises(StopIteration, next, r)
Example #12
0
    def test_create_recycle(self):
        """Test creation of cuds_objects for different session."""
        default_session = CoreSession()
        osp.core.cuds.Cuds._session = default_session
        a = city.City(name="Freiburg")
        self.assertIs(a.session, default_session)
        with TestWrapperSession() as session:
            w = city.CityWrapper(session=session)
            with EngineContext(session):
                b = create_recycle(oclass=city.City,
                                   kwargs={"name": "Offenburg"},
                                   uid=a.uid,
                                   session=session,
                                   fix_neighbors=False)
            self.assertEqual(b.name, "Offenburg")
            self.assertEqual(b.uid, a.uid)
            self.assertEqual(set(default_session._registry.keys()), {a.uid})
            self.assertIs(default_session._registry.get(a.uid), a)
            self.assertEqual(set(session._registry.keys()), {b.uid, w.uid})
            self.assertIs(session._registry.get(b.uid), b)
            self.assertEqual(session._buffers, [[{
                w.uid: w
            }, dict(), dict()], [{
                b.uid: b
            }, dict(), dict()]])

            x = city.Citizen()
            x = b.add(x, rel=city.hasInhabitant)

            c = create_recycle(oclass=city.City,
                               kwargs={"name": "Emmendingen"},
                               session=session,
                               uid=a.uid,
                               fix_neighbors=False)
            self.assertIs(b, c)
            self.assertEqual(c.name, "Emmendingen")
            self.assertEqual(c.get(rel=cuba.relationship), [])
            self.assertNotEqual(x.get(rel=cuba.relationship), [])
            self.assertEqual(set(default_session._registry.keys()),
                             {a.uid, x.uid})
            self.assertIs(default_session._registry.get(a.uid), a)
            self.assertEqual(session._buffers, [[{
                w.uid: w,
                x.uid: x
            }, {
                c.uid: c
            }, dict()], [dict(), dict(), dict()]])

            x = city.Citizen()
            x = c.add(x, rel=city.hasInhabitant)

            c = create_recycle(oclass=city.City,
                               kwargs={"name": "Karlsruhe"},
                               session=session,
                               uid=a.uid,
                               fix_neighbors=True)
            self.assertEqual(x.get(rel=cuba.relationship), [])
Example #13
0
 def test_iter(self):
     """Test the __iter__() magic method."""
     r = QueryResult(CoreSession(), iter(range(10)))
     i = iter(r)
     self.assertEqual(list(zip(i, range(5))),
                      list(zip(range(5), range(5))))
     self.assertEqual(list(zip(i, range(5))),
                      list(zip(range(6, 10), range(5))))
     self.assertEqual(r.all(), list(range(10)))
Example #14
0
 def test_text_turtle_file_handle(self):
     """Test importing the `text/turtle` mime type from a file handle."""
     with CoreSession() as session:
         test_data_path = str(
             Path(__file__).parent / "test_importexport_data.ttl")
         with open(test_data_path, "r") as test_data_file:
             loaded_objects = import_cuds(test_data_file,
                                          format="text/turtle")
             data_integrity(self, session, loaded_objects, label="import")
Example #15
0
 def test_add_multi_session(self):
     """Test the add method in a comext of multiple sessions."""
     with CoreSession() as session:
         wrapper = cuba.Wrapper(session=session)
         aw = cuba.Entity(session=session)
         b = cuba.Entity()
         c = cuba.Entity()
         bw = aw.add(b, rel=cuba.activeRelationship)
         c.add(b, rel=cuba.activeRelationship)
         wrapper.add(c, rel=cuba.activeRelationship)
         self.assertIn(bw, aw.get(rel=cuba.activeRelationship))
         self.assertIn(aw, bw.get(rel=cuba.passiveRelationship))
Example #16
0
 def test_clone_cuds_object(self):
     """Test cloning of cuds."""
     a = city.City(name="Freiburg")
     with CoreSession() as session:
         w = city.CityWrapper(session=session)
         aw = w.add(a)
         clone = clone_cuds_object(aw)
         self.assertIsNot(aw, None)
         self.assertIs(clone.session, aw.session)
         self.assertEqual(clone.uid, aw.uid)
         self.assertIs(aw, session._registry.get(aw.uid))
         self.assertEqual(clone.name, "Freiburg")
Example #17
0
 def test_add_twice(self):
     """Test what happens if you add the same object twice."""
     p = city.Citizen(name="Ralf")
     c1 = city.City(name="Freiburg")
     c2 = city.City(name="Offenburg")
     with CoreSession() as session:
         w = city.CityWrapper(session=session)
         c1w, c2w = w.add(c1, c2)
         pw1 = c1w.add(p, rel=city.hasInhabitant)
         pw2 = c2w.add(p, rel=city.hasInhabitant)
         self.assertIs(pw1, pw2)
         self.assertEqual(set(pw1.get(rel=city.INVERSE_OF_hasInhabitant)),
                          {c1w, c2w})
Example #18
0
    def test_application_json_doc_city(self):
        """Test importing the `application/ld+json` mime type from doc dict.

        This test uses a city ontology instead.
        """
        # Importing
        test_data_path = str(
            Path(__file__).parent / "test_importexport_city_import.json")
        with open(test_data_path, "r") as file:
            json_doc = json.loads(file.read())
        with CoreSession():
            cuds = import_cuds(json_doc, format="application/ld+json")
            self.assertTrue(cuds.is_a(city.Citizen))
            self.assertEqual(cuds.name, "Peter")
            self.assertEqual(cuds.age, 23)
            export_file = io.StringIO()
            export_cuds(cuds, file=export_file, format="application/ld+json")
            export_file.seek(0)
            assertJsonLdEqual(self, json_doc, json.loads(export_file.read()))
        # Exporting
        test_data_path = str(
            Path(__file__).parent / "test_importexport_city_export.json")
        with open(test_data_path, "r") as file:
            json_doc = json.loads(file.read())
        with CoreSession():
            c = branch(
                city.City(name="Freiburg", uid=1),
                branch(
                    city.Neighborhood(name="Littenweiler", uid=2),
                    city.Street(name="SchwarzwaldstraĂźe", uid=3),
                ),
            )
            export_file = io.StringIO()
            export_cuds(c, file=export_file, format="application/ld+json")
            export_file.seek(0)
            assertJsonLdEqual(self, json.loads(export_file.read()), json_doc)
Example #19
0
    def test_creation(self):
        """Test the instantiation and type of the objects."""
        self.assertRaises(TypeError,
                          math.Real,
                          hasNumericalData=1.2,
                          uid=0,
                          unwanted="unwanted")
        self.assertRaises(TypeError, math.Real)

        r = math.Real(hasNumericalData=1.2, hasSymbolData="1.2")
        r2 = math.Real(hasNumericalData=1.2)
        p = holistic.Process()
        self.assertEqual(r.oclass, math.Real)
        self.assertEqual(r2.oclass, math.Real)
        self.assertEqual(p.oclass, holistic.Process)
        cuba.Wrapper(session=CoreSession())
Example #20
0
    def test_create_from_cuds_object(self):
        """Test copying cuds_objects to different session."""
        default_session = CoreSession()
        Cuds._session = default_session
        a = city.City(name="Freiburg")
        self.assertIs(a.session, default_session)
        with TestWrapperSession() as session:
            w = city.CityWrapper(session=session)
            with EngineContext(session):
                b = create_from_cuds_object(a, session)
            self.assertEqual(b.name, "Freiburg")
            self.assertEqual(b.uid, a.uid)
            self.assertEqual(set(default_session._registry.keys()), {a.uid})
            self.assertIs(default_session._registry.get(a.uid), a)
            self.assertEqual(set(session._registry.keys()), {b.uid, w.uid})
            self.assertIs(session._registry.get(b.uid), b)
            self.assertEqual(
                session._buffers,
                [[dict(), dict(), dict()], [{
                    b.uid: b
                }, dict(), dict()]],
            )

            b.name = "Emmendingen"
            x = city.Citizen(age=54, name="Franz", session=default_session)
            b.add(x, rel=city.hasInhabitant)
            y = city.Citizen(age=21, name="Rolf", session=default_session)
            a.add(y, rel=city.hasInhabitant)

            c = create_from_cuds_object(a, session)
            self.assertIs(b, c)
            self.assertEqual(c.name, "Freiburg")
            self.assertEqual(len(c.get(rel=cuba.relationship)), 1)
            self.assertEqual(c._neighbors[city.hasInhabitant],
                             {y.uid: [city.Citizen]})
            self.assertEqual(set(default_session._registry.keys()),
                             {a.uid, x.uid, y.uid})
            self.assertIs(default_session._registry.get(a.uid), a)
            self.assertEqual(
                session._buffers,
                [[{
                    x.uid: x
                }, {
                    c.uid: c
                }, dict()], [dict(), dict(), dict()]],
            )
Example #21
0
    def test_fix_new_parents(self):
        """Check _fix_new_parent.

        Make sure the method:
        - Deletes connection to new parents not available in new session
        - Adds connection to new parents available in new session
        """
        n = city.Neighborhood(name="Zähringen")
        # parent in both sessions
        c1 = city.City(name="Berlin")
        # only parent in default session (available in both)
        c2 = city.City(name="Paris")
        n.add(c1, c2, rel=city.isPartOf)

        c3 = city.City(name="London")

        with CoreSession() as session:
            wrapper = city.CityWrapper(session=session)
            c1w, c2w = wrapper.add(c1, c2)
            nw = c2w.get(n.uid)
            nw.remove(c2.uid, rel=cuba.relationship)

            # only parent + available in default session
            n.add(c3, rel=city.isPartOf)

            n = clone_cuds_object(n)
            n._session = session
            new_parent_diff = get_neighbor_diff(n, nw, mode="non-active")
            new_parents = session.load(*[x[0] for x in new_parent_diff])

            missing = dict()
            Cuds._fix_new_parents(
                new_cuds_object=n,
                new_parents=new_parents,
                new_parent_diff=new_parent_diff,
                missing=missing,
            )

        self.assertEqual(
            set(n.get(rel=city.isPartOf)),
            {c1w, c2w, None},  # missing parent, should be in missing dict
        )
        self.assertEqual(missing, {c3.uid: [(n, city.isPartOf)]})
        self.assertEqual(c2w.get(rel=city.hasPart), [n])
Example #22
0
    def test_creation(self):
        """Tests the instantiation and type of the objects."""
        self.assertRaises(
            TypeError,
            city.City,
            name="name",
            coordinates=[1, 2],
            uid=0,
            unwanted="unwanted",
        )
        self.assertRaises(TypeError, city.City)

        c = city.City(name="a city")
        p = city.Person()
        self.assertEqual(c.oclass, city.City)
        self.assertEqual(p.oclass, city.Person)

        self.assertRaises(TypeError, cuba.Nothing)
        self.assertRaises(TypeError, cuba.Wrapper)
        cuba.Wrapper(session=CoreSession())
Example #23
0
    def test_update(self):
        """Test the standard, normal behavior of the update() method."""
        c = holistic.Process()
        n = math.Real(hasNumericalData=1.2)
        new_n = create_from_cuds_object(n, CoreSession())
        new_s = math.Integer(hasNumericalData=42)
        new_n.add(new_s)
        c.add(n)

        old_real = c.get(n.uid)
        old_integers = old_real.get(oclass=math.Integer)
        self.assertEqual(old_integers, [])

        c.update(new_n)

        new_real = c.get(n.uid)
        new_integers = new_real.get(oclass=math.Integer)
        self.assertEqual(new_integers, [new_s])

        self.assertRaises(ValueError, c.update, n)
Example #24
0
    def test_update(self):
        """Tests the standard, normal behavior of the update() method."""
        c = city.City(name="a city")
        n = city.Neighborhood(name="a neighborhood")
        new_n = create_from_cuds_object(n, CoreSession())
        new_s = city.Street(name="a new street")
        new_n.add(new_s)
        c.add(n)

        old_neighborhood = c.get(n.uid)
        old_streets = old_neighborhood.get(oclass=city.Street)
        self.assertEqual(old_streets, [])

        c.update(new_n)

        new_neighborhood = c.get(n.uid)
        self.assertIs(new_neighborhood, n)
        new_streets = new_neighborhood.get(oclass=city.Street)
        self.assertEqual(new_streets, [new_s])

        self.assertRaises(ValueError, c.update, n)
Example #25
0
 def test_default_session_context_manager(self):
     """Test changing the default session with a session context manager."""
     default_session = CoreSession()
     Cuds._session = default_session
     bern = city.City(name="Bern")
     with TestSession() as session1:
         freiburg = city.City(name="Freiburg")
         with TestSession() as session2:
             berlin = city.City(name="Berlin")
             with TestSession() as session3:
                 madrid = city.City(name="Madrid")
                 with TestSession() as session4:
                     beijing = city.City(name="北京")
                     self.assertIs(freiburg.session, session1)
                     self.assertIs(berlin.session, session2)
                     self.assertIs(madrid.session, session3)
                     self.assertIs(beijing.session, session4)
             paris = city.City(name="Paris")
             self.assertIs(berlin.session, paris.session)
             self.assertIsNot(berlin.session, beijing.session)
     tokyo = city.City(name="Tokyo")
     # Test default session restore.
     self.assertIs(bern.session, tokyo.session)
     self.assertIs(bern.session, default_session)
Example #26
0
    def test_recursive_add(self):
        """Tests if add() works correctly.

        In this test case the added cuds_object is from another session.
        """
        c = city.City(name="City")
        p1 = city.Citizen()
        c.add(p1, rel=city.hasInhabitant)

        second_session = CoreSession()
        w = city.CityWrapper(session=second_session)
        cw = w.add(c)
        p1w = cw.get(p1.uid)
        self.assertIs(cw.session, second_session)
        self.assertIs(p1w.session, second_session)
        self.assertIsNot(c.session, second_session)
        self.assertIsNot(p1.session, second_session)
        self.assertTrue(c._stored)
        self.assertTrue(p1._stored)
        self.assertTrue(w._stored)
        self.assertTrue(cw._stored)
        self.assertTrue(p1w._stored)

        p2 = city.Citizen()
        p3 = city.Citizen()
        p4 = city.Citizen()
        c.add(p2, p3, rel=city.hasInhabitant)
        p1.add(p2, rel=city.hasChild)
        p3.add(p2, rel=city.hasChild)
        p2.add(p4, rel=city.hasChild)

        cw.add(p2, rel=city.hasInhabitant)
        p2w = cw.get(p2.uid)
        p4w = p2w.get(p4.uid)

        # check if there are unexpected changes in the first session
        # first check active relationships
        self.assertEqual(
            set(c._neighbors[city.hasInhabitant].keys()),
            set([p1.uid, p2.uid, p3.uid]),
        )
        self.assertEqual(set([p2.uid]),
                         set(p1._neighbors[city.hasChild].keys()))
        self.assertEqual(set([p2.uid]),
                         set(p3._neighbors[city.hasChild].keys()))
        self.assertEqual(set([p4.uid]),
                         set(p2._neighbors[city.hasChild].keys()))

        # check passive relationships
        self.assertEqual(
            set([c.uid]),
            set(p1._neighbors[city.INVERSE_OF_hasInhabitant].keys()),
        )
        self.assertEqual(set([p1.uid, p3.uid]),
                         set(p2._neighbors[city.isChildOf].keys()))
        self.assertEqual(
            set([c.uid]),
            set(p2._neighbors[city.INVERSE_OF_hasInhabitant].keys()),
        )
        self.assertEqual(
            set([c.uid]),
            set(p3._neighbors[city.INVERSE_OF_hasInhabitant].keys()),
        )
        self.assertEqual(set([p2.uid]),
                         set(p4._neighbors[city.isChildOf].keys()))

        # check if items correctly added in second session
        # active relations
        self.assertEqual(
            set(cw._neighbors[city.hasInhabitant].keys()),
            set([p1w.uid, p2w.uid]),
        )
        self.assertEqual(set([p2w.uid]),
                         set(p1w._neighbors[city.hasChild].keys()))
        self.assertEqual(set([p4w.uid]),
                         set(p2w._neighbors[city.hasChild].keys()))

        # passive relations
        self.assertEqual(
            set([cw.uid]),
            set(p1w._neighbors[city.INVERSE_OF_hasInhabitant].keys()),
        )
        self.assertEqual(set([p1w.uid]),
                         set(p2w._neighbors[city.isChildOf].keys()))
        self.assertEqual(
            set([cw.uid]),
            set(p2w._neighbors[city.INVERSE_OF_hasInhabitant].keys()),
        )
        self.assertEqual(set([p2w.uid]),
                         set(p4w._neighbors[city.isChildOf].keys()))
Example #27
0
    def test_fix_neighbors(self):
        """Test fixing the neighbors after replacing CUDS objects."""
        w1 = city.CityWrapper(session=CoreSession())
        w2 = city.CityWrapper(session=CoreSession())

        c1w1 = city.City(name="city1")  # parent in both wrappers
        c2w1 = city.City(name="city2")  # parent in w1, not present in w2
        c3w1 = city.City(name="city3")  # parent only in w1, present in w2
        c4w1 = city.City(name="city4")  # parent only in w2
        p1w1 = city.Citizen(name="citizen")
        p2w1 = city.Citizen(name="child")

        w2.add(c1w1, c3w1, c4w1)
        c1w2, c3w2, c4w2 = w2.get(c1w1.uid, c3w1.uid, c4w1.uid)
        c1w2.add(p1w1, rel=city.hasInhabitant)
        c4w2.add(p1w1, rel=city.hasInhabitant)
        p1w2 = c1w2.get(p1w1.uid)
        p1w2.add(p2w1, rel=city.hasChild)
        p2w2 = p1w2.get(p2w1.uid)

        w1.add(c1w1, c2w1, c3w1, c4w1)
        c1w1.add(p1w1, rel=city.hasInhabitant)
        c2w1.add(p1w1, rel=city.hasInhabitant)
        c3w1.add(p1w1, rel=city.hasInhabitant)

        self.assertEqual(
            set(p1w1._neighbors[city.INVERSE_OF_hasInhabitant].keys()),
            set([c1w1.uid, c2w1.uid, c3w1.uid]),
        )
        self.assertEqual(set(p2w2._neighbors[city.isChildOf].keys()),
                         set([p1w2.uid]))

        missing = dict()
        Cuds._fix_neighbors(
            new_cuds_object=p1w1,
            old_cuds_object=p1w2,
            session=p1w2.session,
            missing=missing,
        )

        # check if connections cuds_objects that are no
        # longer parents are in the missing dict
        self.assertIn(c2w1.uid, missing)
        self.assertEqual(
            set(p1w1._neighbors[city.INVERSE_OF_hasInhabitant].keys()),
            set([c1w1.uid, c2w1.uid, c3w1.uid, c4w1.uid]),
        )
        self.assertNotIn(city.isPartOf, p2w2._neighbors)

        # check if there are no unexpected other changes
        self.assertEqual(set(c1w1._neighbors[city.hasInhabitant].keys()),
                         set([p1w1.uid]))
        self.assertEqual(set(c2w1._neighbors[city.hasInhabitant].keys()),
                         set([p1w1.uid]))
        self.assertEqual(set(c3w1._neighbors[city.hasInhabitant].keys()),
                         set([p1w1.uid]))
        self.assertNotIn(city.hasInhabitant, c4w1._neighbors)

        # check if the parents in w2 have been updated
        self.assertEqual(set(c1w2._neighbors[city.hasInhabitant].keys()),
                         set([p1w1.uid]))
        self.assertEqual(set(c3w2._neighbors[city.hasInhabitant].keys()),
                         set([p1w1.uid]))
        self.assertEqual(set(c4w2._neighbors[city.hasInhabitant].keys()),
                         set([p1w1.uid]))
Example #28
0
class Cuds():
    """A Common Universal Data Structure.

    The CUDS object has attributes and is connected to other
    cuds objects via relationships. It is an ontology individual that
    can be used like a container.
    """

    _session = CoreSession()

    def __init__(
        self,
        attributes: Dict[OntologyAttribute, Any],
        oclass: OntologyEntity,
        session: Session = None,
        uid: uuid.UUID = None
    ):
        """Initialize a CUDS object.

        This method should not be called by the user directly.
        Instead use the __call__ magic method of OntologyClass.
        Construct the CUDS object. This will also register the CUDS objects in
        the corresponding session.

        Args:
            attributes (Dict[OntologyAttribute, Any]): Mapping from ontology
                attribute to specified value.
            oclass (OntologyEntity): The ontology class of the CUDS object.
            session (Session, optional): The session associated with the CUDS,
                if None is given it will be associated with the CoreSession.
                Defaults to None.
            uid (uuid.UUID, optional): A unique identifier. If None given, a
                random uid will be created. Defaults to None.

        Raises:
            ValueError: Uid of zero is not allowed.
        """
        self._session = session or Cuds._session
        self._graph = rdflib.Graph()
        self.__uid = uuid.uuid4() if uid is None else convert_to(uid, "UUID")
        if self.__uid.int == 0:
            raise ValueError("Invalid UUID")

        for k, v in attributes.items():
            self._graph.add((
                self.iri, k.iri, rdflib.Literal(k.convert_to_datatype(v),
                                                datatype=k.datatype)
            ))
        if oclass:
            self._graph.add((
                self.iri, rdflib.RDF.type, oclass.iri
            ))
        self.session._store(self)

    @property
    def uid(self) -> uuid.UUID:
        """Get he uid of the cuds object."""
        return self.__uid

    @property
    def iri(self):
        """Get the IRI of the CUDS object."""
        return iri_from_uid(self.uid)

    @property
    def session(self):
        """Get the session of the cuds object."""
        return self._session

    @property
    def oclasses(self):
        """Get the ontology classes of this CUDS object."""
        result = list()
        for s, p, o in self._graph.triples((self.iri, rdflib.RDF.type, None)):
            r = from_iri(o, raise_error=False)
            if r is not None:
                result.append(r)
        return result

    @property
    def oclass(self):
        """Get the type of the cuds object."""
        oclasses = self.oclasses
        if oclasses:
            return oclasses[0]
        return None

    @property
    def _neighbors(self):
        return NeighborDictRel(self)

    @property
    def _stored(self):
        return self._graph is self.session.graph

    def get_triples(self, include_neighbor_types=False):
        """Get the triples of the cuds object."""
        o_set = set()
        for s, p, o in self._graph.triples((self.iri, None, None)):
            yield s, p, o
            o_set.add(o)
        if include_neighbor_types:
            for o in o_set:
                yield from self._graph.triples((o, rdflib.RDF.type, None))

    def get_attributes(self):
        """Get the attributes as a dictionary."""
        if self.session:
            self.session._notify_read(self)
        result = {}
        for s, p, o in self._graph.triples((self.iri, None, None)):
            obj = from_iri(p, raise_error=False)
            if isinstance(obj, OntologyAttribute):
                result[obj] = o.toPython()
        return result

    def is_a(self, oclass):
        """Check if the CUDS object is an instance of the given oclass.

        Args:
            oclass (OntologyClass): Check if the CUDS object is an instance of
                this oclass.

        Returns:
            bool: Whether the CUDS object is an instance of the given oclass.
        """
        return any(oc in oclass.subclasses for oc in self.oclasses)

    def add(self,
            *args: "Cuds",
            rel: OntologyRelationship = None) -> Union["Cuds", List["Cuds"]]:
        """Add CUDS objects to their respective relationship.

        If the added objects are associated with the same session,
        only a link is created. Otherwise, the a deepcopy is made and added
        to the session of this Cuds object.
        Before adding, check for invalid keys to avoid inconsistencies later.

        Args:
            args (Cuds): The objects to be added
            rel (OntologyRelationship): The relationship between the objects.

        Raises:
            TypeError: Ne relationship given and no default specified.
            ValueError: Added a CUDS object that is already in the container.

        Returns:
            Union[Cuds, List[Cuds]]: The CUDS objects that have been added,
                associated with the session of the current CUDS object.
                Result type is a list, if more than one CUDS object is
                returned.
        """
        check_arguments(Cuds, *args)
        rel = rel or self.oclass.namespace.get_default_rel()
        if rel is None:
            raise TypeError("Missing argument 'rel'! No default "
                            "relationship specified for namespace %s."
                            % self.oclass.namespace)
        result = list()
        # update cuds objects if they are already in the session
        old_objects = self._session.load(
            *[arg.uid for arg in args if arg.session != self.session])
        for arg in args:
            # Recursively add the children to the registry
            if rel in self._neighbors and arg.uid in self._neighbors[rel]:
                message = '{!r} is already in the container'
                raise ValueError(message.format(arg))
            if self.session != arg.session:
                arg = self._recursive_store(arg, next(old_objects))

            self._add_direct(arg, rel)
            arg._add_inverse(self, rel)
            result.append(arg)
        return result[0] if len(args) == 1 else result

    def get(self,
            *uids: uuid.UUID,
            rel: OntologyRelationship = cuba.activeRelationship,
            oclass: OntologyClass = None,
            return_rel: bool = False) -> Union["Cuds", List["Cuds"]]:
        """Return the contained elements.

        Filter elements by given type, uid or relationship.
        Expected calls are get(), get(*uids), get(rel), get(oclass),
        get(*uids, rel), get(rel, oclass).
        If uids are specified:
            The position of each element in the result is determined by to the
            position of the corresponding uid in the given list of uids.
            In this case, the result can contain None values if a given uid
            is not a child of this cuds_object.
            If only a single uid is given, only this one element is returned
            (i.e. no list).
        If no uids are specified:
            The result is a collection, where the elements are ordered
            randomly.

        Args:
            uids (uuid.UUID): UUIDs of the elements.
            rel (OntologyRelationship, optional): Only return cuds_object
                which are connected by subclass of given relationship.
                Defaults to cuba.activeRelationship.
            oclass (OntologyClass, optional): Only return elements which are a
                subclass of the given ontology class. Defaults to None.
            return_rel (bool, optional): Whether to return the connecting
                relationship. Defaults to False.

        Returns:
            Union[Cuds, List[Cuds]]: The queried objects.
        """
        result = list(
            self.iter(*uids, rel=rel, oclass=oclass, return_rel=return_rel)
        )
        if len(uids) == 1:
            return result[0]
        return result

    def update(self, *args: "Cuds") -> List["Cuds"]:
        """Update the Cuds object.

        Updates the object by providing updated versions of CUDS objects
        that are directly in the container of this CUDS object.
        The updated versions must be associated with a different session.

        Args:
            args (Cuds): The updated versions to use to update the current
                object.

        Raises:
            ValueError: Provided a CUDS objects is not in the container of the
                current CUDS
            ValueError: Provided CUDS object is associated with the same
                session as the current CUDS object. Therefore it is not an
                updated version.

        Returns:
            Union[Cuds, List[Cuds]]: The CUDS objects that have been updated,
                associated with the session of the current CUDS object.
                Result type is a list, if more than one CUDS object is
                returned.
        """
        check_arguments(Cuds, *args)
        old_objects = self.get(*[arg.uid for arg in args])
        if len(args) == 1:
            old_objects = [old_objects]
        if any(x is None for x in old_objects):
            message = 'Cannot update because cuds_object not added.'
            raise ValueError(message)

        result = list()
        for arg, old_cuds_object in zip(args, old_objects):
            if arg.session is self.session:
                raise ValueError("Please provide CUDS objects from a "
                                 "different session to update()")
            # Updates all instances
            result.append(self._recursive_store(arg, old_cuds_object))

        if len(args) == 1:
            return result[0]
        return result

    def remove(self,
               *args: Union["Cuds", uuid.UUID],
               rel: OntologyRelationship = cuba.activeRelationship,
               oclass: OntologyClass = None):
        """Remove elements from the CUDS object.

        Expected calls are remove(), remove(*uids/Cuds),
        remove(rel), remove(oclass), remove(*uids/Cuds, rel),
        remove(rel, oclass)

        Args:
            args (Union[Cuds, UUID]): UUIDs of the elements to remove or the
                elements themselves.
            rel (OntologyRelationship, optional): Only remove cuds_object
                which are connected by subclass of given relationship.
                Defaults to cuba.activeRelationship.
            oclass (OntologyClass, optional): Only remove elements which are a
                subclass of the given ontology class. Defaults to None.

        Raises:
            RuntimeError: No CUDS object removed, because specified CUDS
                objects are not in the container of the current CUDS object
                directly.
        """
        uids = [arg.uid if isinstance(arg, Cuds) else arg for arg in args]

        # Get mapping from uids to connecting relationships
        _, relationship_mapping = self._get(*uids, rel=rel, oclass=oclass,
                                            return_mapping=True)
        if not relationship_mapping:
            raise RuntimeError("Did not remove any Cuds object, "
                               "because none matched your filter.")
        uid_relationships = list(relationship_mapping.items())

        # load all the neighbors to delete and remove inverse relationship
        neighbors = self.session.load(*[uid for uid, _ in uid_relationships])
        for uid_relationship, neighbor in zip(uid_relationships, neighbors):
            uid, relationships = uid_relationship
            for relationship in relationships:
                self._remove_direct(relationship, uid)
                neighbor._remove_inverse(relationship, self.uid)

    def iter(self,
             *uids: uuid.UUID,
             rel: OntologyRelationship = cuba.activeRelationship,
             oclass: OntologyClass = None,
             return_rel: bool = False) -> Iterator["Cuds"]:
        """Iterate over the contained elements.

        Only iterate over objects of a given type, uid or oclass.

        Expected calls are iter(), iter(*uids), iter(rel),
        iter(oclass), iter(*uids, rel), iter(rel, oclass).
        If uids are specified:
            The position of each element in the result is determined by to the
            position of the corresponding uid in the given list of uids.
            In this case, the result can contain None values if a given uid
            is not a child of this cuds_object.
        If no uids are specified:
            The result is ordered randomly.

        Args:
            uids (uuid.UUID): UUIDs of the elements.
            rel (OntologyRelationship, optional): Only return cuds_object
                which are connected by subclass of given relationship.
                Defaults to cuba.activeRelationship.
            oclass (OntologyClass, optional): Only return elements which are a
                subclass of the given ontology class. Defaults to None.
            return_rel (bool, optional): Whether to return the connecting
                relationship. Defaults to False.

        Returns:
            Iterator[Cuds]: The queried objects.
        """
        if return_rel:
            collected_uids, mapping = self._get(*uids, rel=rel, oclass=oclass,
                                                return_mapping=True)
        else:
            collected_uids = self._get(*uids, rel=rel, oclass=oclass)

        result = self._load_cuds_objects(collected_uids)
        for r in result:
            if not return_rel:
                yield r
            else:
                yield from ((r, m) for m in mapping[r.uid])

    def _recursive_store(self, new_cuds_object, old_cuds_object=None):
        """Recursively store cuds_object and all its children.

        One-way relationships and dangling references are fixed.

        Args:
            new_cuds_object (Cuds): The Cuds object to store recursively.
            old_cuds_object (Cuds, optional): The old version of the
                CUDS object. Defaults to None.

        Returns:
            Cuds: The added CUDS object.
        """
        # add new_cuds_object to self and replace old_cuds_object
        queue = [(self, new_cuds_object, old_cuds_object)]
        uids_stored = {new_cuds_object.uid, self.uid}
        missing = dict()
        result = None
        while queue:

            # Store copy in registry
            add_to, new_cuds_object, old_cuds_object = queue.pop(0)
            if new_cuds_object.uid in missing:
                del missing[new_cuds_object.uid]
            old_cuds_object = clone_cuds_object(old_cuds_object)
            new_child_getter = new_cuds_object
            new_cuds_object = create_from_cuds_object(new_cuds_object,
                                                      add_to.session)
            # fix the connections to the neighbors
            add_to._fix_neighbors(new_cuds_object, old_cuds_object,
                                  add_to.session, missing)
            result = result or new_cuds_object

            for outgoing_rel in new_cuds_object._neighbors:

                # do not recursively add parents
                if not outgoing_rel.is_subclass_of(cuba.activeRelationship):
                    continue

                # add children not already added
                for child_uid in new_cuds_object._neighbors[outgoing_rel]:
                    if child_uid not in uids_stored:
                        new_child = new_child_getter.get(
                            child_uid, rel=outgoing_rel)
                        old_child = self.session.load(child_uid).first()
                        queue.append((new_cuds_object, new_child, old_child))
                        uids_stored.add(new_child.uid)

        # perform the deletion
        for uid in missing:
            for cuds_object, rel in missing[uid]:
                del cuds_object._neighbors[rel][uid]
                if not cuds_object._neighbors[rel]:
                    del cuds_object._neighbors[rel]
        return result

    @staticmethod
    def _fix_neighbors(new_cuds_object, old_cuds_object, session, missing):
        """Fix all the connections of the neighbors of a Cuds object.

        That CUDS is going to be replaced later.

        Behavior when neighbors change:

        - new_cuds_object has parents, that weren't parents of old_cuds_object.
            - the parents are already stored in the session of old_cuds_object.
            - they are not already stored in the session of old_cuds_object.
            --> Add references between new_cuds_object and the parents that are
                already in the session.
            --> Delete references between new_cuds_object and parents that are
                not available.
        - new_cuds_object has children, that weren't
                children of old_cuds_object.
            --> add/update them recursively.

        - A parent of old_cuds_object is no longer a parent of new_cuds_object.
        --> Add a relationship between that parent and the new cuds_object.
        - A child of old_cuds_object is no longer a child of new_cuds_object.
        --> Remove the relationship between child and new_cuds_object.

        Args:
            new_cuds_object (Cuds): Cuds object that will replace the old one.
            old_cuds_object (Cuds, optional): Cuds object that will be
                replaced by a new one. Can be None if the new Cuds object does
                not replace any object.
            session (Session): The session where the adjustments should take
                place.
            missing (Dict): dictionary that will be populated with connections
              to objects, that are currently not available in the new session.
              The recursive add might add it later.
        """
        old_cuds_object = old_cuds_object or None

        # get the parents that got parents after adding the new Cuds
        new_parent_diff = get_neighbor_diff(
            new_cuds_object, old_cuds_object, mode="non-active")
        # get the neighbors that were neighbors
        # before adding the new cuds_object
        old_neighbor_diff = get_neighbor_diff(old_cuds_object,
                                              new_cuds_object)

        # Load all the cuds_objects of the session
        cuds_objects = iter(session.load(
            *[uid for uid, _ in new_parent_diff + old_neighbor_diff]))

        # Perform the fixes
        Cuds._fix_new_parents(new_cuds_object=new_cuds_object,
                              new_parents=cuds_objects,
                              new_parent_diff=new_parent_diff,
                              missing=missing)
        Cuds._fix_old_neighbors(new_cuds_object=new_cuds_object,
                                old_cuds_object=old_cuds_object,
                                old_neighbors=cuds_objects,
                                old_neighbor_diff=old_neighbor_diff)

    @staticmethod
    def _fix_new_parents(new_cuds_object, new_parents,
                         new_parent_diff, missing):
        """Fix the relationships of the added Cuds objects.

        Fixes relationships to the parents of the added Cuds object.

        Args:
            new_cuds_object (Cuds): The added Cuds object
            new_parents (Iterator[Cuds]): The new parents of the added CUDS
                object
            new_parent_diff (List[Tuple[UID, Relationship]]): The uids of the
                new parents and the relations they are connected with.
            missing (dict): dictionary that will be populated with connections
                to objects, that are currently not available in the new
                session. The recursive_add might add it later.
        """
        # Iterate over the new parents
        for (parent_uid, relationship), parent in zip(new_parent_diff,
                                                      new_parents):
            if relationship.is_subclass_of(cuba.activeRelationship):
                continue
            inverse = relationship.inverse
            # Delete connection to parent if parent is not present
            if parent is None:
                if parent_uid not in missing:
                    missing[parent_uid] = list()
                missing[parent_uid].append((new_cuds_object, relationship))
                continue

            # Add the inverse to the parent
            if inverse not in parent._neighbors:
                parent._neighbors[inverse] = {}

            parent._neighbors[inverse][new_cuds_object.uid] = \
                new_cuds_object.oclasses

    @staticmethod
    def _fix_old_neighbors(new_cuds_object, old_cuds_object, old_neighbors,
                           old_neighbor_diff):
        """Fix the relationships of the added Cuds objects.

        Fixes relationships to Cuds object that were previously neighbors.

        Args:
            new_cuds_object (Cuds): The added Cuds object
            old_cuds_object (Cuds, optional): The Cuds object that is going
                to be replaced
            old_neighbors (Iterator[Cuds]): The Cuds object that were neighbors
                before the replacement.
            old_neighbor_diff (List[Tuple[UID, Relationship]]): The uids of
                the old neighbors and the relations they are connected with.
        """
        # iterate over all old neighbors.
        for (neighbor_uid, relationship), neighbor in zip(old_neighbor_diff,
                                                          old_neighbors):
            inverse = relationship.inverse

            # delete the inverse if neighbors are children
            if relationship.is_subclass_of(cuba.activeRelationship):
                if inverse in neighbor._neighbors:
                    neighbor._remove_direct(inverse, new_cuds_object.uid)

            # if neighbor is parent, add missing relationships
            else:
                if relationship not in new_cuds_object._neighbors:
                    new_cuds_object._neighbors[relationship] = {}
                for (uid, oclasses), parent in \
                        zip(old_cuds_object._neighbors[relationship].items(),
                            neighbor._neighbors):
                    if parent is not None:
                        new_cuds_object._neighbors[relationship][uid] = \
                            oclasses

    def _add_direct(self, cuds_object, rel):
        """Add an cuds_object with a specific relationship.

        Args:
            cuds_object (Cuds): CUDS object to be added
            rel (OntologyRelationship): relationship with the cuds_object to
                add.
        """
        # First element, create set
        if rel not in self._neighbors.keys():
            self._neighbors[rel] = {cuds_object.uid: cuds_object.oclasses}
        # Element not already there
        elif cuds_object.uid not in self._neighbors[rel]:
            self._neighbors[rel][cuds_object.uid] = cuds_object.oclasses

    def _add_inverse(self, cuds_object, rel):
        """Add the inverse relationship from self to cuds_object.

        Args:
            cuds_object (Cuds): CUDS object to connect with.
            rel (OntologyRelationship): direct relationship
        """
        inverse_rel = rel.inverse
        self._add_direct(cuds_object, inverse_rel)

    def _get(self, *uids, rel=None, oclass=None, return_mapping=False):
        """Get the uid of contained elements that satisfy the filter.

        This filter consists of a certain type, uid or relationship.
        Expected calls are _get(), _get(*uids), _get(rel),_ get(oclass),
        _get(*uids, rel), _get(rel, oclass).
        If uids are specified, the result is the input, but
        non-available uids are replaced by None.

        Args:
            uids (UUID): UUIDs of the elements to get.
            rel (OntologyRelationship, optional): Only return CUDS objects
                connected with a subclass of relationship. Defaults to None.
            oclass (OntologyClass, optional): Only return CUDS objects of a
                subclass of this ontology class. Defaults to None.
            return_mapping (bool, optional): Whether to return a mapping from
                uids to relationships, that connect self with the uid.
                Defaults to False.

        Raises:
            TypeError: Specified both uids and oclass.
            ValueError: Wrong type of argument.

        Returns:
            List[UUID] (+ Dict[UUID, Set[Relationship]]): list of uids, or
                None, if not found. (+ Mapping from UUIDs to relationships,
                which connect self to the respective Cuds object.)
        """
        if uids and oclass is not None:
            raise TypeError("Do not specify both uids and oclass")
        if rel is not None and not isinstance(rel, OntologyRelationship):
            raise ValueError("Found object of type %s passed to argument rel. "
                             "Should be an OntologyRelationship." % type(rel))
        if oclass is not None and not isinstance(oclass, OntologyClass):
            raise ValueError("Found object of type %s passed to argument "
                             "oclass. Should be an OntologyClass."
                             % type(oclass))

        if uids:
            check_arguments(uuid.UUID, *uids)

        self.session._notify_read(self)
        # consider either given relationship and subclasses
        # or all relationships.
        consider_relationships = set(self._neighbors.keys())
        if rel:
            consider_relationships &= set(rel.subclasses)
        consider_relationships = list(consider_relationships)

        # return empty list if no element of given relationship is available.
        if not consider_relationships and not return_mapping:
            return [] if not uids else [None] * len(uids)
        elif not consider_relationships:
            return ([], dict()) if not uids else ([None] * len(uids), dict())

        if uids:
            return self._get_by_uids(uids, consider_relationships,
                                     return_mapping=return_mapping)
        return self._get_by_oclass(oclass, consider_relationships,
                                   return_mapping=return_mapping)

    def _get_by_uids(self, uids, relationships, return_mapping):
        """Check for each given uid if it is connected by a given relationship.

        If not, replace it with None.
        Optionally return a mapping from uids to the set of relationships,
        which connect self and the cuds_object with the uid.

        Args:
            uids (List[UUID]): The uids to check.
            relationships (List[Relationship]): Only consider these
                relationships.
            return_mapping (bool): Wether to return a mapping from
                uids to relationships, that connect self with the uid.

        Returns:
            List[UUID] (+ Dict[UUID, Set[Relationship]]): list of found uids,
                None for not found UUIDs (+ Mapping from UUIDs to
                relationships, which connect self to the respective Cuds
                object.)
        """
        not_found_uids = dict(enumerate(uids)) if uids else None
        relationship_mapping = dict()
        for relationship in relationships:

            # Uids are given.
            # Check which occur as object of current relation.
            found_uid_indexes = set()

            # we need to iterate over all uids for every
            # relationship if we compute a mapping
            iterator = enumerate(uids) if relationship_mapping \
                else not_found_uids.items()
            for i, uid in iterator:
                if uid in self._neighbors[relationship]:
                    found_uid_indexes.add(i)
                    if uid not in relationship_mapping:
                        relationship_mapping[uid] = set()
                    relationship_mapping[uid].add(relationship)
            for i in found_uid_indexes:
                if i in not_found_uids:
                    del not_found_uids[i]

        collected_uids = [(uid if i not in not_found_uids else None)
                          for i, uid in enumerate(uids)]
        if return_mapping:
            return collected_uids, relationship_mapping
        return collected_uids

    def _get_by_oclass(self, oclass, relationships, return_mapping):
        """Get the cuds_objects with given oclass.

        Only return objects that are connected to self
        with any of the given relationships. Optionally return a mapping
        from uids to the set of relationships, which connect self and
        the cuds_objects with the uid.

        Args:
            oclass (OntologyClass, optional): Filter by the given
                OntologyClass. None means no filter.
            relationships (List[Relationship]): Filter by list of
                relationships.
            return_mapping (bool): whether to return a mapping from
                uids to relationships, that connect self with the uid.

        Returns:
            List[UUID] (+ Dict[UUID, Set[Relationship]]): The uids of the found
                CUDS objects (+ Mapping from uuid to set of relationsships that
                connect self with the respective cuds_object.)
        """
        relationship_mapping = dict()
        for relationship in relationships:

            # Collect all uids who are object of the current relationship.
            # Possibly filter by OntologyClass.
            for uid, target_classes in self._neighbors[relationship].items():
                if oclass is None or any(t.is_subclass_of(oclass)
                                         for t in target_classes):
                    if uid not in relationship_mapping:
                        relationship_mapping[uid] = set()
                    relationship_mapping[uid].add(relationship)
        if return_mapping:
            return list(relationship_mapping.keys()), relationship_mapping
        return list(relationship_mapping.keys())

    def _load_cuds_objects(self, uids):
        """Load the cuds_objects of the given uids from the session.

        Each in cuds_object is at the same position in the result as
        the corresponding uid in the given uid list.
        If the given uids contain None values, there will be
        None values at the same postion in the result.

        Args:
            uids (List[UUID]): The uids to fetch from the session.

        Yields:
            Cuds: The loaded cuds_objects
        """
        without_none = [uid for uid in uids if uid is not None]
        cuds_objects = self.session.load(*without_none)
        for uid in uids:
            if uid is None:
                yield None
            else:
                try:
                    yield next(cuds_objects)
                except StopIteration:
                    return None

    def _remove_direct(self, relationship, uid):
        """Remove the direct relationship to the object with the given uid.

        Args:
            relationship (OntologyRelationship): The relationship to remove.
            uid (UUID): The uid to remove.
        """
        del self._neighbors[relationship][uid]
        if not self._neighbors[relationship]:
            del self._neighbors[relationship]

    def _remove_inverse(self, relationship, uid):
        """Remove the inverse of the given relationship.

        Args:
            relationship (OntologyRelationship): The relationship to remove.
            uid (UUID): The uid to remove.
        """
        inverse = relationship.inverse
        self._remove_direct(inverse, uid)

    def _check_valid_add(self, to_add, rel):
        return True  # TODO

    def __str__(self) -> str:
        """Get a human readable string.

        Returns:
            str: string with the Ontology class and uid.
        """
        return "%s: %s" % (self.oclass, self.uid)

    def __getattr__(self, name):
        """Set the attributes corresponding to ontology values.

        Args:
            name (str): The name of the attribute

        Raises:
            AttributeError: Unknown attribute name

        Returns:
            The value of the attribute: Any
        """
        try:
            attr = self.oclass.get_attribute_by_argname(name)
            if self.session:
                self.session._notify_read(self)
            return self._graph.value(self.iri, attr.iri).toPython()
        except AttributeError as e:
            if (  # check if user calls session's methods on wrapper
                self.is_a(cuba.Wrapper)
                and self._session is not None
                and hasattr(self._session, name)
            ):
                logger.warning(
                    "Trying to get non-defined attribute '%s' "
                    "of wrapper CUDS object '%s'. Will return attribute of "
                    "its session '%s' instead." % (name, self, self._session)
                )
                return getattr(self._session, name)
            raise AttributeError(name) from e

    def __setattr__(self, name, new_value):
        """Set an attribute.

        Will notify the session of it corresponds to an ontology value.

        Args:
            name (str): The name of the attribute.
            new_value (Any): The new value.

        Raises:
            AttributeError: Unknown attribute name
        """
        if name.startswith("_"):
            super().__setattr__(name, new_value)
            return
        attr = self.oclass.get_attribute_by_argname(name)
        if self.session:
            self.session._notify_read(self)
        self._graph.set((
            self.iri, attr.iri,
            rdflib.Literal(attr.convert_to_datatype(new_value),
                           datatype=attr.datatype)
        ))
        if self.session:
            self.session._notify_update(self)

    def __repr__(self) -> str:
        """Return a machine readable string that represents the cuds object.

        Returns:
            str: Machine readable string representation for Cuds.
        """
        return "<%s: %s,  %s: @%s>" % (self.oclass, self.uid,
                                       type(self.session).__name__,
                                       hex(id(self.session)))

    def __hash__(self) -> int:
        """Make Cuds objects hashable.

        Use the hash of the uid of the object

        Returns:
            int: unique hash
        """
        return hash(self.uid)

    def __eq__(self, other):
        """Define which CUDS objects are treated as equal.

        Same Ontology class and same UUID.

        Args:
            other (Cuds): Instance to check.

        Returns:
            bool: True if they share the uid and class, False otherwise
        """
        return isinstance(other, Cuds) and other.oclass == self.oclass \
            and self.uid == other.uid

    def __getstate__(self):
        """Get the state for pickling or copying.

        Returns:
            Dict[str, Any]: The state of the object. Does not contain session.
                Contains the string of the OntologyClass.
        """
        state = {k: v for k, v in self.__dict__.items()
                 if k not in {"_session", "_graph"}}
        state["_graph"] = list(self.get_triples(include_neighbor_types=True))
        return state

    def __setstate__(self, state):
        """Set the state for pickling or copying.

        Args:
            state (Dict[str, Any]): The state of the object. Does not contain
                session. Contains the string of the OntologyClass.
        """
        state["_session"] = None
        g = rdflib.Graph()
        for triple in state["_graph"]:
            g.add(triple)
        state["_graph"] = g
        self.__dict__ = state
Example #29
0
 def test_contains(self):
     """Test containment."""
     r = QueryResult(CoreSession(), iter(range(10)))
     self.assertIn(1, r)
     self.assertIn(9, r)
     self.assertNotIn(11, r)
Example #30
0
 def setUp(self):
     """Set up the testcases: Reset the session."""
     from osp.core.cuds import Cuds
     from osp.core.session import CoreSession
     Cuds._session = CoreSession()