184 lines
6.3 KiB
Markdown
184 lines
6.3 KiB
Markdown
# Exam Format Specification (Minimal)
|
|
|
|
## Purpose
|
|
A simple, portable exam format. The system reads an exam JSON, renders it online, collects answers, and returns a JSON that bundles the original exam plus the answers (and optionally a basic result summary).
|
|
|
|
## Top-level Exam Structure
|
|
- **examId**: string (unique)
|
|
- **subject**: string
|
|
- **title**: string
|
|
- **difficulty**: one of `beginner | intermediate | advanced`
|
|
- **durationMinutes**: integer ≥ 1
|
|
- **sections**: array of sections, each containing questions
|
|
- **metadata**: optional object (version, createdAt, etc.)
|
|
|
|
## Question Types (required support)
|
|
- Single Choice (one correct answer)
|
|
- Multiple Choices (multiple correct answers)
|
|
- True/False
|
|
- Essay
|
|
- Simple Coding
|
|
- Coding Exercise
|
|
|
|
## "I Don't Know" Option
|
|
For `single_choice`, `multiple_choices`, and `true_false` questions, an "I don't know" option is automatically available. This allows honest assessment without guessing.
|
|
|
|
## Section Structure
|
|
- **id**: string
|
|
- **title**: string
|
|
- **questions**: array of questions (of any supported type)
|
|
|
|
## Common Question Fields
|
|
- **id**: string
|
|
- **type**: `single_choice | multiple_choices | true_false | essay | code_simple | code_exercise`
|
|
- **prompt**: string (supports simple Markdown)
|
|
- **points**: integer ≥ 0
|
|
- **allowIDK**: boolean (optional, default true for single_choice, multiple_choices, true_false)
|
|
|
|
## Type-specific Fields
|
|
- **single_choice** (one correct answer)
|
|
- `choices`: array of `{ key: string, text: string }`
|
|
- `answer`: string (the correct `key`)
|
|
- `allowIDK`: boolean (default true) - adds "I don't know" option
|
|
|
|
- **multiple_choices** (multiple correct answers)
|
|
- `choices`: array of `{ key: string, text: string }`
|
|
- `answer`: array of strings (all correct `key`s, e.g., ["A", "C"])
|
|
- `allowIDK`: boolean (default true) - adds "I don't know" option
|
|
- `partialCredit`: boolean (default true) - award partial points for some correct
|
|
|
|
- **true_false**
|
|
- `answer`: boolean
|
|
- `allowIDK`: boolean (default true) - adds "I don't know" option (scores as wrong)
|
|
|
|
- **essay**
|
|
- `rubric`: `{ criteria: [{ name: string, weight: number }], maxPoints: integer }`
|
|
- Notes: `answer` omitted; scored manually or later
|
|
|
|
- **code_simple**
|
|
- `language`: `python | typescript | javascript`
|
|
- `tests`: array of `{ input: string, expected: string, visibility?: public|hidden }`
|
|
|
|
- **code_exercise**
|
|
- `language`: `python | typescript | javascript`
|
|
- `tests`: array of `{ input: string, expected: string, visibility?: public|hidden, weight?: number }`
|
|
- `rubric`: `{ criteria: [{ name: string, weight: number }], maxPoints: integer }`
|
|
- `constraints?`: string (optional)
|
|
- `starterCode?`: string (optional)
|
|
|
|
## Minimal Valid Exam JSON (example)
|
|
```json
|
|
{
|
|
"examId": "sample-exam-v1",
|
|
"subject": "python",
|
|
"title": "Sample Exam",
|
|
"difficulty": "beginner",
|
|
"durationMinutes": 60,
|
|
"sections": [
|
|
{
|
|
"id": "sec-1",
|
|
"title": "Single Choice",
|
|
"questions": [
|
|
{
|
|
"id": "q1",
|
|
"type": "single_choice",
|
|
"prompt": "Which is a valid list literal?",
|
|
"choices": [
|
|
{ "key": "A", "text": "(1, 2, 3)" },
|
|
{ "key": "B", "text": "{1, 2, 3}" },
|
|
{ "key": "C", "text": "[1, 2, 3]" }
|
|
],
|
|
"answer": "C",
|
|
"points": 2
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "sec-2",
|
|
"title": "True / False",
|
|
"questions": [
|
|
{ "id": "q2", "type": "true_false", "prompt": "Tuples are immutable.", "answer": true, "points": 2 }
|
|
]
|
|
},
|
|
{
|
|
"id": "sec-3",
|
|
"title": "Essay",
|
|
"questions": [
|
|
{
|
|
"id": "q3",
|
|
"type": "essay",
|
|
"prompt": "Explain decorators and a common use case.",
|
|
"rubric": { "criteria": [{ "name": "Correctness", "weight": 0.6 }, { "name": "Clarity", "weight": 0.4 }], "maxPoints": 8 },
|
|
"points": 8
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "sec-4",
|
|
"title": "Simple Coding",
|
|
"questions": [
|
|
{
|
|
"id": "q4",
|
|
"type": "code_simple",
|
|
"language": "python",
|
|
"prompt": "Implement squares(n) returning list of squares 0..n.",
|
|
"tests": [ { "input": "squares(3)", "expected": "[0, 1, 4, 9]", "visibility": "hidden" } ],
|
|
"points": 10
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "sec-5",
|
|
"title": "Coding Exercise",
|
|
"questions": [
|
|
{
|
|
"id": "q5",
|
|
"type": "code_exercise",
|
|
"language": "python",
|
|
"prompt": "Implement paginate(items, page, per_page). Return items, page, per_page, total, total_pages.",
|
|
"constraints": "O(n) acceptable; validate inputs (page>=1, per_page>=1).",
|
|
"tests": [
|
|
{ "input": "paginate([1,2,3,4,5], 2, 2)", "expected": "{items:[3,4],page:2,per_page:2,total:5,total_pages:3}", "visibility": "hidden", "weight": 2 },
|
|
{ "input": "paginate([], 1, 10)", "expected": "{items:[],page:1,per_page:10,total:0,total_pages:0}", "visibility": "hidden" }
|
|
],
|
|
"rubric": { "criteria": [{ "name": "Correctness", "weight": 0.6 }, { "name": "Structure", "weight": 0.2 }, { "name": "EdgeCases", "weight": 0.2 }], "maxPoints": 20 },
|
|
"points": 20
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## Output Shape (what the system returns)
|
|
- Echoes the input exam JSON as `exam`
|
|
- Captured answers as `attempt`
|
|
|
|
```json
|
|
{
|
|
"exam": { "...": "(same as input)" },
|
|
"attempt": {
|
|
"attemptId": "attempt-001",
|
|
"startedAt": "2025-10-20T10:00:00Z",
|
|
"submittedAt": "2025-10-20T10:45:00Z",
|
|
"answers": [
|
|
{ "questionId": "q1", "response": "C", "timeSec": 25 },
|
|
{ "questionId": "q2", "response": true, "timeSec": 10 },
|
|
{ "questionId": "q3", "response": "Decorators wrap functions to add behavior...", "timeSec": 180 },
|
|
{ "questionId": "q4", "response": { "code": "def squares(n): ..." }, "timeSec": 420 },
|
|
{ "questionId": "q5", "response": { "code": "def paginate(items, page, per_page): ..." }, "timeSec": 900 }
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
## Validation Checklist
|
|
- Required top-level fields present
|
|
- Each section and question has a unique `id`
|
|
- `points` ≥ 0
|
|
- Type-specific fields present according to question type
|
|
|
|
## Versioning
|
|
- Use semantic versioning in `metadata.version` for exam files
|
|
- New optional fields are allowed without breaking existing behavior
|