Tutorial 01: CUDS API

This tutorial introduces the CUDS API. The code given here is based on this example.


CUDS stands for Common Universal Data Structure, and it is used to uniformly represent an ontological individual. In the python implementation of OSP-core, it means that every ontological individual is an instance of the Cuds class.

Conceptually, we consider CUDS objects as containers. The content of a CUDS object consists of other CUDS objects. This means that a CUDS is a recursive data structure. There are various types of relationships between a container and its contained objects. We will see this later on.

As a data structure, CUDS exposes an API that provides the functionalities Create, Read, Update and Delete (or simply CRUD). Except Create, all the other functionalities are supported by the methods add, get, remove, update and iter, that are defined in the class Cuds. Here, we cover all of the methods except update since it is used to synchronize between two or more data sources, and in this tutorial we will only use one, that is the data we will create through our python code on the fly. Check out the wrapper tutorial to see the update method in action.

Every CUDS object is related to an ontological concept via the ontological is a relation. In the python implementation of OSP-core, all ontological concepts are instances of the OntologyEntity class. The ontological concept that can be used to Create CUDS objects are instances of OntologyClass, a subclass of OntologyEntity. The ontological concepts are organized in namespaces.

Let’s get hands on

We start by importing the example namespace from osp-core. It consists of concepts that were automatically generated from the dummy city ontology. There is another tutorial on the ontology YAML file. However, it is not important for the purposes of this tutorial.

[ ]:
# If you did not install the CITY ontology (pico install city), you have to execute these commands first:
# from osp.core import Parser
# p = Parser()
# p.parse("city")

from osp.core import CITY

Let’s create a CUDS object that represents the city of Freiburg:

c = CITY.CITY(name="Freiburg", coordinates=[47, 7])

The variable c is assigned with a newly created CUDS object. This object was initialized with a name (Freiburg) and coordinates ([47, 7]). To understand why these two arguments were necessary, we’ll have to take a look at the ontology. This will be explained in another tutorial.

Each CUDS object has a unique identifier (UID) which can be accessed:

[ ]:
print("uid of c: " + str(c.uid))

The type of an object can be queried as well:

print("type of c: " + str(c.oclass))
type of c: CITY.CITY

Let’s add two citizens to our city object:

p1 = CITY.CITIZEN(name="Peter")
p2 = CITY.CITIZEN(name="Anne")
c.add(p1, rel=CITY.HAS_INHABITANT)
c.add(p2, rel=CITY.HAS_INHABITANT)
<CITY.CITIZEN: 192b50d6-9235-4214-91a8-f0669ac7ac18,  CoreSession: @0x7f4cd6f5e610>

Note that the relationship type between the city and its two citizens in this case is ‘HAS_INHABITANT’. In our context, this means that Anne and Peter are Freiburg inhabitants.

Next, we would like to iterate over the objects contained in the city object. We do so by using the iter() function:

for el in c.iter():
    print("uid: " + str(el.uid))
uid: f34cb649-d25a-46fe-8e43-c35e2746f8cf
uid: 192b50d6-9235-4214-91a8-f0669ac7ac18

We can get() an object from a container if we have a UID of one of its immediate contained objects:

CITY.CITIZEN: f34cb649-d25a-46fe-8e43-c35e2746f8cf

We can also filter the contained objects by type:

[<CITY.CITIZEN: f34cb649-d25a-46fe-8e43-c35e2746f8cf,  CoreSession: @0x7f4cd6f5e610>, <CITY.CITIZEN: 192b50d6-9235-4214-91a8-f0669ac7ac18,  CoreSession: @0x7f4cd6f5e610>]

We remove objects using the remove() function:

# c.remove(p1.uid) also works!

Let’s close this tutorial by adding some neighbourhoods in a loop:

for i in range(6):
    c.add(CITY.NEIGHBOURHOOD(name="neighbourhood %s" % i))
[ ]: