Question format
tech-exam reads questions from a file. This page describes how that file has to be built — so that an AI of your choice can write questions you then take into your collection. Every example here is the content a new account starts with, and would import unchanged. The subject in it is an example, not a model: what counts is the structure, not the topic.
Getting questions out of your own AI
- In the question collection, choose «With your own AI» and set topic, number and question type.
- Open the finished request in an AI service, or copy it. It points at this page so the AI knows how the file is built.
- Copy the result out of the chat and paste it into the same dialog — or save it as a file and read it in via «From file».
- Read the questions in the collection before running an exam with them. Invented questions sometimes contain mistakes that look convincing.
How it is built
On the outside there is an identifier, a version number and the list of questions. Everything else belongs to the questions themselves. A field that carries its default value is left out — that is how the examples on this page look, and a file written by hand may look the same.
{
"format": "tech-exam/questions",
"formatVersion": 1,
"questions": [
{
"topic": "Beispielfragen",
"subtopic": "Fragetypen",
"subsubtopic": "Multiple Choice",
"title": "Aussagen über Listen",
"contentType": "mc",
"maxPoints": 4,
"description": "Welche Aussagen über Listen in Python sind richtig?",
"answerSpec": {
"kind": "mc",
"options": [
{
"id": "o1",
"text": "Eine Liste ist veränderbar."
},
{
"id": "o2",
"text": "Eine Liste kann nur Zahlen enthalten."
},
{
"id": "o3",
"text": "Eine Liste behält die Reihenfolge der Einträge."
},
{
"id": "o4",
"text": "`len(zahlen)` gibt den grössten Wert der Liste zurück."
}
],
"multiple": true,
"scoring": {
"mode": "allOrNothing"
}
},
"answerKey": {
"kind": "mc",
"correct": [
"o1",
"o3"
]
}
},
{
"topic": "Beispielfragen",
"subtopic": "Fragetypen",
"subsubtopic": "Langtext",
"title": "Wann Liste, wann Dictionary?",
"contentType": "langtext",
"maxPoints": 6,
"description": "Begründe in einem Abschnitt, wann sich eine Liste besser eignet als ein Dictionary – und wann umgekehrt. Nenne je ein Beispiel.",
"solution": "Eine Liste passt, wenn die Reihenfolge zählt und alle Einträge der Reihe nach durchlaufen werden, etwa eine Rangliste.\n\nEin Dictionary passt, wenn ein Wert über einen Schlüssel gefunden werden soll, etwa die Note zu einem Namen: Der Zugriff bleibt gleich schnell, egal wie viele Einträge dazukommen.\n",
"criteria": [
{
"text": "Die Begründung trifft das Entscheidende: Reihenfolge und Durchlaufen bei der Liste, Zugriff über den Schlüssel beim Dictionary.",
"points": 3
},
{
"text": "Zu beiden Fällen steht ein konkretes Beispiel, das das genannte Merkmal wirklich braucht.",
"points": 3
}
]
}
]
}The fields
A question must carry three fields: title, question type and points. All the others may be left out — then whatever stands in the third column applies. A file with three correct fields is worth more than one full of guessed ones.
Around the questions
| Field | Values | If absent | Meaning |
|---|---|---|---|
| format | "tech-exam/questions" | Required | The identifier the file is recognised by. It is always the same. |
| formatVersion | 1 | Required | The number of the layout. |
| questions | [ … ] | Required | The questions. Each one is either a question with an answer area or a question with sub-questions. |
On every question
| Field | Values | If absent | Meaning |
|---|---|---|---|
| title | Text | Required | The title shown above the question. |
| contentType | "svg" · "tigerpython" · "tigerpython_robotik" · "python" · "mc" · "lueckentext" · "langtext" | Required | How the answer is given: «mc» a choice, «lueckentext» gaps in a text, «langtext» prose, «python» and «tigerpython» program code, «tigerpython_robotik» a program for the micro:bit or a robot attached to it, «svg» a drawing. |
| maxPoints | ≥ 0.5 | Required | The most points the question can give. |
| topic | Text | "" | The folder in the collection. |
| subtopic | Text | "" | The subfolder. |
| subsubtopic | Text | "" | The sub-subfolder. |
| description | Text | null | The question text. For a gap text it stays empty, because that text lives in the segments. |
| descriptionFormat | "markdown" · "html" | "markdown" | How the question text is set. Markdown knows tables, code blocks and maths between dollar signs. |
| solution | Text | null | The model solution. Visible to teachers only. |
| starterCode | Text | null | Code already present in the editor when the question is opened. |
| answerSpec | { … } | null | The available answers. Needed for a choice and for a gap text, otherwise left out. |
| answerKey | { … } | null | The correct answer. It uses the same identifiers as answerSpec. |
| criteria | [ … ] | [] | Marking notes: a text for the students and the points for it. Points with a plus are added to the assessment, points with a minus are taken away. |
| graderCode | Text | null | Python code that awards the points of this question itself, instead of them being given by hand. It is executed as soon as it is used for marking. |
| graderRules | { … } | {} | What applies when the program of a submission does not run or does not finish. Only meaningful for «python» and «tigerpython». |
Programming and drawing
«tigerpython», «tigerpython_robotik», «python» and «svg» are answered in an embedded environment. With «tigerpython_robotik» the device is switched on there: the environment shows the simulator of the micro:bit, and as soon as the program imports a robot library, that of the robot as well. «starterCode» puts there what is already present when the question is opened. «graderCode» awards the points itself: a Python function «grade(answer, task, run)» that returns points and feedback — «answer» is the submission, «task» the question with its points, «run» the run of the program with its output. «graderRules» takes over the two cases in which a program does not get through at all. Marking code is executed as soon as it is used for marking; the import points that out expressly, and it should be read before it is taken over.
{
"format": "tech-exam/questions",
"formatVersion": 1,
"questions": [
{
"topic": "Beispielfragen",
"subtopic": "Programmieren",
"subsubtopic": "TigerPython",
"title": "Quadrat mit repeat",
"contentType": "tigerpython",
"maxPoints": 6,
"description": "Zeichne mit der Turtle ein Quadrat mit Seitenlänge 100. Nutze dafür die `repeat`-Schleife von TigerPython.",
"solution": "from gturtle import *\n\nmakeTurtle()\nrepeat 4:\n forward(100)\n right(90)\n",
"criteria": [
{
"text": "`repeat 4:` wiederholt den eingerückten Block viermal – für ein Quadrat braucht es genau vier Durchgänge.",
"points": 3
},
{
"text": "Im Schleifenkörper gehören `forward(100)` und `right(90)` zusammen; ohne die Drehung entsteht eine Linie statt eines Quadrats.",
"points": 3
},
{
"text": "Ohne `makeTurtle()` gibt es nichts, was zeichnen könnte – das Programm bleibt gleich zu Beginn stehen.",
"points": -1
}
]
},
{
"topic": "Beispielfragen",
"subtopic": "Programmieren",
"subsubtopic": "Roboter",
"title": "Zwei Sekunden geradeaus",
"contentType": "tigerpython_robotik",
"maxPoints": 3,
"description": "Lass den Roboter zwei Sekunden geradeaus fahren und danach anhalten.",
"solution": "from mbrobot import *\n\nforward()\ndelay(2000)\nstop()\n",
"criteria": [
{
"text": "`forward()` setzt den Roboter in Bewegung, `stop()` hält ihn wieder an.",
"points": 2
},
{
"text": "`delay(2000)` lässt dazwischen zwei Sekunden verstreichen – ohne die Wartezeit steht er, bevor er losgefahren ist.",
"points": 1
}
]
},
{
"topic": "Beispielfragen",
"subtopic": "Programmieren",
"subsubtopic": "Python",
"title": "Summe zweier Zahlen",
"contentType": "python",
"maxPoints": 10,
"description": "Schreibe eine Funktion `add(a, b)`, die die Summe der beiden Argumente zurückgibt.",
"solution": "def add(a, b):\n return a + b\n",
"starterCode": "def add(a, b):\n # Gib hier die Summe von a und b zurück.\n pass\n",
"graderCode": "def grade(answer, task, run):\n # Die Abgabe ausführen und die Funktion aus der Musterlösung holen. Ob sie\n # sich überhaupt ausführen lässt, entscheiden die Regeln über diesem Feld.\n umgebung = {}\n exec(answer[\"value\"], umgebung)\n\n funktion = umgebung.get(\"add\") # Name aus der Musterlösung\n if not callable(funktion):\n return {\"points\": 0, \"feedback\": \"Die Funktion «add» fehlt.\"}\n\n faelle = [((1, 2), 3), ((-1, 1), 0), ((0, 0), 0)] # (Argumente, erwartet)\n treffer = 0\n for argumente, erwartet in faelle:\n try:\n if funktion(*argumente) == erwartet:\n treffer += 1\n except Exception:\n pass\n\n return {\n \"points\": round(task[\"maxPoints\"] * treffer / len(faelle), 2),\n \"feedback\": f\"{treffer} von {len(faelle)} Testfällen bestanden.\",\n }\n",
"graderRules": {
"error": {
"points": 0,
"feedback": "Das Programm läuft nicht."
},
"timeout": {
"points": 0,
"feedback": "Das Programm wird nicht fertig."
}
}
},
{
"topic": "Beispielfragen",
"subtopic": "Programmieren",
"subsubtopic": "SVG",
"title": "Roter Kreis",
"contentType": "svg",
"maxPoints": 10,
"description": "Zeichne mit SVG einen roten Kreis mit Radius 50 in der Mitte einer Zeichenfläche von $200 * 200$.",
"solution": "<svg viewBox=\"0 0 200 200\">\n <circle cx=\"100\" cy=\"100\" r=\"50\" fill=\"red\" />\n</svg>\n",
"starterCode": "<svg viewBox=\"0 0 200 200\">\n <!-- Zeichne den Kreis hier hinein. -->\n</svg>\n",
"criteria": [
{
"text": "`cx` und `cy` liegen bei `100`, also in der Mitte der Zeichenfläche, und `r` bei `50`.",
"points": 6
},
{
"text": "`fill=\"red\"` füllt den Kreis rot – ohne `fill` zeichnet der Browser schwarz.",
"points": 4
}
]
}
]
}Gap text
The gap text is the only type whose question text is not in «description»: its text lives entirely in the segments. A gap without choices is typed, one with choices is clicked — and every gap needs the same identifier on both sides.
{
"format": "tech-exam/questions",
"formatVersion": 1,
"questions": [
{
"topic": "Beispielfragen",
"subtopic": "Fragetypen",
"subsubtopic": "Lückentext",
"title": "Klammern in Python",
"contentType": "lueckentext",
"maxPoints": 2,
"answerSpec": {
"kind": "cloze",
"segments": [
{
"type": "text",
"text": "Eine Liste wird mit "
},
{
"type": "gap",
"id": "g1",
"choices": null
},
{
"type": "text",
"text": " erzeugt, ein Dictionary mit "
},
{
"type": "gap",
"id": "g2",
"choices": [
{
"id": "g2c1",
"text": "geschweiften Klammern"
},
{
"id": "g2c2",
"text": "runden Klammern"
},
{
"id": "g2c3",
"text": "spitzen Klammern"
}
]
},
{
"type": "text",
"text": "."
}
],
"compare": {
"caseSensitive": false,
"normalizeWhitespace": true,
"numericTolerance": null
}
},
"answerKey": {
"kind": "cloze",
"gaps": {
"g1": {
"type": "text",
"accepted": [
"eckigen Klammern"
]
},
"g2": {
"type": "choice",
"correct": [
"g2c1"
]
}
}
}
}
]
}Questions with sub-questions
Where several parts share one question text, the sub-questions take the place of question type and points. The shared text then stands at the top and everything else with them. In the question collection each part becomes a single question that carries the shared text along. — which is why those same three also stand separately further up this page. Here they carry only title, question type and points, because a sub-question needs no more.
| Field | Values | If absent | Meaning |
|---|---|---|---|
| title | Text | Required | The title shown above the question. |
| subQuestions | [ … ] | Required | The sub-questions. Each one carries its own question type and points. |
| description | Text | null | The question text. For a gap text it stays empty, because that text lives in the segments. |
{
"format": "tech-exam/questions",
"formatVersion": 1,
"questions": [
{
"title": "Robotik mit dem micro:bit",
"description": "Die beiden Teilfragen programmieren dasselbe Gerät: zuerst sein Display, dann den Roboter, auf dem es sitzt.\n\nZum Ausprobieren braucht es keine Hardware – die Programmierumgebung zeigt beides im Simulator.",
"subQuestions": [
{
"title": "Ein blinkendes Herz",
"contentType": "tigerpython_robotik",
"maxPoints": 4
},
{
"title": "Zwei Sekunden geradeaus",
"contentType": "tigerpython_robotik",
"maxPoints": 3
}
]
}
]
}What else applies
- Points are at least 0.5 and have at most 2 decimal places.
- Every identifier in «answerKey» must exist in «answerSpec», otherwise the question cannot be opened.
- A choice question needs at least two answers and at least one correct one.
- The file does not carry images. They are added afterwards in the editor.
- The file contains nothing but the questions — no sentence before it, no explanation after it.