Grade an exam
Request
Send a multipart/form-data request to POST /api/v1/grade with both parts required:
| Part | Java type | Content |
|---|---|---|
image | MultipartFile | The answer-sheet image. |
answers | MultipartFile | CSV answer key. |
The answer-key CSV is parsed into AnswerKey objects. Its header and fields are:
questionNumber,correctAnswer
1,B
2,B
The controller does not define optional parts, authentication headers, content validation, upload limits, or a stable list of accepted image formats. The OpenCV reader is used for the uploaded file after it is copied to a temporary .jpg file.
Example
curl -s -X POST http://localhost:8080/api/v1/grade \
-F "image=@data/test_1.jpeg" \
-F "answers=@/tmp/answers.csv"
Response
On success, the body is a GradeResult with exactly these Java fields:
| Field | Type | Meaning |
|---|---|---|
studentName | String | OCR result from the name crop. |
className | String | OCR result from the class crop. |
score | double | Correct answers divided by total answer-key rows, multiplied by 10, rounded to two decimals. |
correctCount | int | Number of answer-key rows matching the extracted answer, case-insensitively. |
totalQuestions | int | Number of parsed answer-key rows. |
incorrectQuestions | List<Integer> | Question numbers whose extracted answer did not match. |
studentAnswers | Map<Integer, String> | Extracted answer string for questions 1 through 40. |
For example, the JSON representation contains keys equivalent to:
{
"studentName": "...",
"className": "...",
"score": 10.0,
"correctCount": 40,
"totalQuestions": 40,
"incorrectQuestions": [],
"studentAnswers": {"1": "B", "2": "B"}
}
The number of graded questions is driven by the answer-key row count; the image extractor itself always creates entries for questions 1 through 40.