API

CakeCMS offers a very basic API to automate access to its contents. Most API endpoints are similar to the pages the user sees, they accept the same parameters and return the data presented on the page, but encoded as plain JSON. Access to API endpoints is guarded by API Tokens, which in turn are bound to user accounts. To ease access, a Python API is given that wraps all available endpoints in a simple class.

Authentication Basics - API Token

You get API tokens from your global user's page. You can either select your name in the user list, or use this direct link: /system/users/view. Click on "Edit API tokens" at the end of the first section. On this page you can create and manage API tokens. Normal tokens give you the same permissions than your account has, "read-only" tokens are limited to GET requests.

Python API Intro

The simplest way to access a CMS is the Python API. A basic script looks like this:

import cakecms

HOST = 'https://cms.cispa.saarland'
TOKEN = '...'

# create an (unauthenticated) instance
cms = cakecms.CakeCMS(HOST)
# create an authenticated instance
cms = cakecms.CakeCMS(HOST, token=TOKEN)
# create an authenticated instance bound to a course
cms = cakecms.CakeCMS(HOST, token=TOKEN, course='shortname1')
# switch course
cms.course = 'shortname2'

After creating a CMS instance, you can use the instance's methods to access the CMS. The method name is usually similar to the URL you would use as a normal user: For example:

print(cms.courses_index())  # returns list of all courses like "/system/courses/index"
print(cms.students_index())  # return data from the big student list ("/course/students/index")
# go to the website, select your filters as desired, and copy them from the url in your browser.
# in this case the URL was ".../students/index/cols:Si~Sm~Uu~Uf~Ul~T1g/filter_testg1:notzero"
print(cms.students_index(columns=['Si','Sm','Uu','Uf','Ul','T1g'], named_params={'filter_testg1': 'notzero'}))

To see all available methods, take a look at the source code: cakecms.py. To get some inspiration, take a look at the existing examples: examples

HTTP Basics

If you don't want to use the Python API, you can do HTTP requests manually. Requests are either GET or POST, go to https://cms.cispa.saarland/<coursename>/api/..., and usually return JSON. To authenticate, send a custom HTTP header X-CMS-API-TOKEN: <your token>. Example:

$> curl 'https://cms.cispa.saarland/system/api/courses/index' -H 'X-CMS-API-TOKEN: ...'
{
    "courses": [
        {
            "Course": {
                "id": 1,
                "shortname": "testcourse",
                "name": "Testkurs",
                "systemversion": 1,
                "term": "SS",
                "term_year": 2020,
                "course_type": "",
                "credit_points": null,
                "display": "Testkurs (SS 2020)"
            },
            "Instructors": [
                "Test User"
            ]
        }
    ]
}

HTTP Endpoints

Each endpoint corresponds to the user page with the same url (without /api/). For example, <course>/api/students/index corresponds to the student list that users visit at <course>/students/index. To get the parameters you need, you can use the UI: Browse that page, select what you need (filtering, order, details etc) and look at the new browser URL. It will very likely work with the API.

Example: From the student list we want to include the final grade, but only from those who passed. We browse to the student list, select the grade column, and add a filter to it. After applying, the url in browser is /<course>/students/index/cols:Si~Sm~Uf~Ul~T1g/filter_testg1:notzero. That means our API url will be: /<course>/api/students/index/cols:Si~Sm~Uf~Ul~T1g/filter_testg1:notzero. Accessing it with API token gives us the same data as the big table in browser, but in machine-readable JSON. The data format is usually self-explanatory (shortened in this example).

$> curl 'https://cms.cispa.saarland/system/api/students/index/cols:Si~Sm~Uf~Ul~T1g/filter_testg1:notzero' -H 'X-CMS-API-TOKEN: ...'
{
    "compressionTable": {
        "Si": "Student.id",
        "Sm": "Student.matriculation",
        "T1g": "Testingresult1.grade",
        ...
    },
    "columnNames": {
        "Student.id": "ID",
        "Student.matriculation": "Matriculation",
        "Testingresult1.grade": "Grade",
        ...
    },
    "students": [
        {
            "Student": {
                "id": 3,
                "user_id": 4,
                "sex": "m",
                "matriculation": "4187517",
                "fsemester": 12,
                "hsemester": 12,
                "subject": "Bioinformatik",
                "tutorial_id": null,
                "email_news": true,
                "joined_at": "2020-05-12 13:17:33",
                "display": "Heinz Becker (4187517)"
            },
            "User": {
                "id": 4,
                "email": "fiana@mk-bauer.de",
                "username": "Heinz",
                "first": "Heinz",
                "last": "Becker",
                "created": "2020-05-12 13:17:33",
                "modified": "2021-04-15 14:07:10",
                "password_rehashed": "2021-04-15 14:07:10.172111",
                "banned": false,
                "twofa": false,
                "twofamethod": null,
                "group_id": 3,
                "display": "Heinz Becker"
            },
            ...
        },
        ...
    ],
    ...
}

POST requests require their body as JSON. The JSON data usually has the same format than the objects returned by similar GET methods. Read-only API tokens can't send POST requests.

List of possible endpoints

  • GET system/api/courses/index
  • GET /<course>/api/admissions/index - List of admitted students (including seating)
  • POST /<course>/api/admissions/edit/<admission id> - edit admission/seating. Body: a single complete Admission object. Example:
    {"Admission": {"id": 1, "student_id": 1, "testing_id": 1, "room_id": 1, ...}}
  • POST /<course>/api/admissions/edit_many - mass seating. Body:
    {"Admission": [{"student_id": 1, "testing_id": 1, "room_id": 1, "row": 1, "seat": 1, "rownumber": 1, "seatnumber": 1}, ...]}
  • GET /<course>/api/assistants/index - List of assistants.
  • GET /<course>/api/full_calendar/events/index - List of entries in the calendar
  • GET /<course>/api/material_categories/index - List of all accessible material categories, their downloadable files and stored links
  • GET /<course>/api/material_files/download/<file-id> - Download a material file by its ID.
  • GET /<course>/api/notes/index - List of all notes.
  • POST /<course>/api/notesEntries/change - Update the value of a note. Encode booleans as 0/1. Body:
    {"NotesEntry": {"student_id": <student id>, "note_id": <note id>, "value": "..."}}
  • POST /<course>/api/registration_items/import/<registration it> - Import registrations (from Hispos etc). See python code how to build proper bodies.
  • GET /<course>/api/students/index - plenty of options (check UI), can retrieve almost all data with the right parameters. Can export data as csv (/format:csv or /format:excelcsv).
  • GET /<course>/api/submissions/index - List of things students can submit to.
  • GET /<course>/api/submission_items/index - List of actual student submissions
  • GET /<course>/api/submission_items/download/<submission_item_id> - Download submitted file
  • GET /<course>/api/submission_items/downloadAll - Download all submitted files. Use with /bySub:<submission id> and/or /byTutorial:<tutorial id>
  • GET /<course>/api/submission_items/feedback/<submission_item_id> - Get current feedback for a student's submission.
  • POST /<course>/api/submission_items/feedback/<submission_item_id> - Set current feedback for a student's submission (whole team if applicable). The body must be multipart/form-data encoded, because it contains a file upload. points is optional, upload can be empty. Format:
    data[SubmissionFeedback][feedback]: <...>
    data[SubmissionFeedback][upload]: <a file upload>
    data[SubmissionFeedback][points]: <number>
  • POST /<course>/api/testingresults/change - Update the points of a student. Use null to remove points. Body:
    {"Testingresult": {"student_id": <student id>, "testing_id": <testing id>, "points": 12.34}}
  • POST /<course>/api/testingresults/import/<testing_id> - Update the points of many student with a csv file. Body:
    {"Import": {"data": "mtknr;points\n1234567,10.0\n2534123,0.0"}}
  • GET /<course>/api/testingresults/retrieve/<testing_id>/studentId:<student_id> - Get points for a student (including sub-testings). In contrast to testings/index it's accessible by tutors.
  • GET /<course>/api/testingresults/index - Get tree of all testings (including sub-testings)
  • GET /<course>/api/testings/index - List of testings in a nested tree structure.
  • GET /<course>/api/team_groupings/index - List of all team groupings.
  • GET /<course>/api/team_groupings/view/<team_grouping_id> - Details of a team grouping and all teams (+ student data).
  • GET /<course>/api/tutors/manage - List of tutors.

An endpoint you need is missing? If you only want to retrieve data, check if /students/index can be configured to return that data. If not, send an email to Markus Bauer, and state the UI urls you would need API access to. Usually API endpoints for existing pages can be added with little work.

Privacy Policy | Legal Notice
If you encounter technical problems, please contact the administrators.