Resource SchoolClass

The SchoolClass resource is represented in the LDAP tree as group objects.

To list those LDAP objects run in a terminal:

FILTER='(&(objectClass=ucsschoolGroup)(ucsschoolRole=school_class:*))'
univention-ldapsearch -LLL "$FILTER"

UCS@school uses the UDM to access the LDAP directory. UDM properties have different names than their associated LDAP attributes. Their values may also differ. To list the same UDM objects as above, run:

$ FILTER='(&(objectClass=ucsschoolGroup)(ucsschoolRole=school_class:*))'
$ udm groups/group list --filter "$FILTER"

SchoolClass class

The ucsschool.kelvin.client.SchoolClass class has the following public attributes and methods:

class SchoolClass(KelvinObject):
    def __init__(
        self,
        name: str,
        school: str,
        *,
        description: str = None,
        users: List[str] = None,
        create_share: bool = True,
        udm_properties: Dict[str, Any] = None,
        ucsschool_roles: List[str] = None,
        dn: str = None,
        url: str = None,
        session: Session = None,
        language: str = None,
        **kwargs,
    ):
        self.name = name
        self.school = school
        self.description = description
        self.users = users
        self.create_share = create_share
        self.udm_properties = udm_properties or {}
        self.ucsschool_roles = ucsschool_roles
        self.dn = dn
        self.url = url
        self.session = session
        if language:
            self.session.language = language


    async def reload(self) -> SchoolClass:
        ...
    async def save(self) -> SchoolClass:
        ...
    async def delete(self) -> None:
        ...
    def as_dict(self) -> Dict[str, Any]:
        ...

SchoolClassResource class

The ucsschool.kelvin.client.SchoolClassResource class has the following public attributes and methods:

class SchoolClassResource(KelvinResource):
    def __init__(self, session: Session, language: str = None):
        ...
    async def get(self, **kwargs) -> SchoolClass:
        ...
    async def get_from_url(self, url: str) -> SchoolClass:
        ...
    async def search(self, **kwargs) -> AsyncIterator[SchoolClass]:
        ...

Create school class

School classes can be created explicitly or implicitly when creating or modifying users.

School classes will be automatically created when mentioned in a users school_classes attribute. They will however not be deleted automatically if they are removed from all users and are thus empty.

from ucsschool.kelvin.client import Session, SchoolClass

async with Session(**credentials) as session:
    sc = SchoolClass(
        name="testclass",
        school="DEMOSCHOOL",
        description="A test class",
        users=["demo_student", "demo_teacher"],
        create_share=True,
        session=session,
    )
    await sc.save()

sc.as_dict()
{'name': 'testclass',
 'ucsschool_roles': ['school_class:school:DEMOSCHOOL'],
 'school': 'DEMOSCHOOL',
 'description': 'A test class',
 'users': ['demo_student', 'demo_teacher'],
 'create_share': True,
 'udm_properties': {},
 'dn': 'cn=DEMOSCHOOL-testclass,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com',
 'url': 'https://master.ucs.local/ucsschool/kelvin/v1/classes/DEMOSCHOOL/testclass'}

School classes are saved as groups in the UCS LDAP. The result can be verified on the target system using UDM:

$ udm groups/group list --filter cn=DEMOSCHOOL-testclass

DN: cn=DEMOSCHOOL-testclass,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com
  name: DEMOSCHOOL-testclass
  description: A test class
  ucsschoolRole: school_class:school:DEMOSCHOOL
  users: uid=demo_student,cn=schueler,cn=users,ou=DEMOSCHOOL,dc=example,dc=com
  users: uid=demo_teacher,cn=lehrer,cn=users,ou=DEMOSCHOOL,dc=example,dc=com
  ...

Every school class has a share with the same name:

$  udm shares/share list --filter cn=DEMOSCHOOL-testclass

DN: cn=DEMOSCHOOL-testclass,cn=klassen,cn=shares,ou=DEMOSCHOOL,dc=example,dc=com
  name: DEMOSCHOOL-testclass
  host: DEMOSCHOOL.example.com
  path: /home/DEMOSCHOOL/groups/klassen/DEMOSCHOOL-testclass
  directorymode: 0770
  group: 7110
  ...

Example creating two school classes as a byproduct of creating a user:

from ucsschool.kelvin.client import Session, SchoolClassResource, User

async with Session(**credentials) as session:
    user = User(
        school="DEMOSCHOOL", schools=["DEMOSCHOOL"],
        roles=["student"], name="test2",
        firstname="test", lastname="two",
        record_uid="test2", source_uid="TESTID",
        school_classes={"DEMOSCHOOL": ["class1", "class2"]},
        session=session)
    await user.save()

    async for sc in SchoolClassResource(session=session).search(school="DEMOSCHOOL"):
        print(sc)

SchoolClass('name'='class1', 'school'='DEMOSCHOOL', dn='cn=DEMOSCHOOL-class1,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com')
SchoolClass('name'='class2', 'school'='DEMOSCHOOL', dn='cn=DEMOSCHOOL-class2,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com')
SchoolClass('name'='Democlass', 'school'='DEMOSCHOOL', dn='cn=DEMOSCHOOL-Democlass,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com')

Retrieve school class

It is necessary to pass both name and school arguments to the get() method, as the name alone wouldn’t be unique in a domain (there can be classes of the same name in multiple schools).

from ucsschool.kelvin.client import Session, SchoolClassResource

async with Session(**credentials) as session:
    sc = await SchoolClassResource(session=session).get(
        school="DEMOSCHOOL", name="testclass"
    )

sc.as_dict()
{'name': 'testclass',
 'ucsschool_roles': ['school_class:school:DEMOSCHOOL'],
 'school': 'DEMOSCHOOL',
 'description': 'A test class',
 'users': ['demo_student', 'demo_teacher'],
 'create_share': True,
 'dn': 'cn=DEMOSCHOOL-testclass,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com',
 'url': 'https://10.200.3.70/ucsschool/kelvin/v1/classes/DEMOSCHOOL/testclass'}

Check if school class exists

from ucsschool.kelvin.client import Session, SchoolClassResource

async with Session(**credentials) as session:
    if await SchoolClassResource(session=session).exists(name="testclass", school="DEMOSCHOOL"):
        print("The school class exists!")

Search school classes

The search() method allows searching for school classes, filtering by school (mandatory) and name (optional).

The mandatory school argument must be exact while the optional name argument support an inexact search using * as a placeholder.

from ucsschool.kelvin.client import Session, SchoolClassResource

async with Session(**credentials) as session:
    async for sc in SchoolClassResource(session=session).search(school="DEMOSCHOOL"):
        print(sc)

SchoolClass('name'='Democlass', 'school'='DEMOSCHOOL', dn='cn=DEMOSCHOOL-Democlass,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com')
SchoolClass('name'='testclass', 'school'='DEMOSCHOOL', dn='cn=DEMOSCHOOL-testclass,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com')

    async for sc in SchoolClassResource(session=session).search(
        school="DEMOSCHOOL", name="test*"
    ):
        print(sc)

SchoolClass('name'='testclass', 'school'='DEMOSCHOOL', dn='cn=DEMOSCHOOL-testclass,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com')

Change school class properties

Get the current school class object, change some attributes and save the changes back to LDAP:

from ucsschool.kelvin.client import Session, SchoolClassResource

async with Session(**credentials) as session:
    sc = await SchoolClassResource(session=session).get(
        school="DEMOSCHOOL",
        name="testclass"
    )
    sc.description = "new description"
    sc.users.remove("demo_teacher")
    await sc.save()

sc.as_dict()
{'name': 'testclass',
 'ucsschool_roles': ['school_class:school:DEMOSCHOOL'],
 'school': 'DEMOSCHOOL',
 'description': 'new description',
 'users': ['demo_student'],
 'create_share': True,
 'dn': 'cn=DEMOSCHOOL-testclass,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com',
 'url': 'https://10.200.3.70/ucsschool/kelvin/v1/classes/DEMOSCHOOL/testclass'}

Move school class

School class objects do not support changing the school. Changing the name is allowed however.

from ucsschool.kelvin.client import Session, SchoolClassResource

async with Session(**credentials) as session:
    sc = await SchoolClassResource(session=session).get(
            school="DEMOSCHOOL",
            name="testclass"
        )
    sc.name = "testclass-new"
    await sc.save()

sc.dn
'cn=DEMOSCHOOL-testclass-new,cn,cn=klassen,cn=schueler,cn=groups,ou=DEMOSCHOOL,dc=example,dc=com'

Delete school class

Get the current school class object and delete it:

from ucsschool.kelvin.client import Session, SchoolClassResource

async with Session(**credentials) as session:
    sc = await SchoolClassResource(session=session).get(
            school="DEMOSCHOOL",
            name="testclass"
        )
    await sc.delete()