Expose submissions: list_submissions, and get_task shows your own
Adds what the API actually permits, which is less than the request asked
for and worth being precise about.
GET /api/v3/submissions/status/task/{taskId} is the only submission
route — no list, no fetch-by-id — so a task id is the only way in. The
probe in the report missed it by trying /api/v3/submissions (404). Its
payload is {id, submitters, isSubmitted, isGraded, grade,
submittingCourseGroupName} and nothing more: no submitted text, no grade
comment, no graded-at. Those lived on /api/v1, which this instance does
not serve at all (404 across the board, confirmed — not the proxy). So
"what feedback did I get" is answerable only when the feedback is a file.
Submitted files are reachable, which covers the main workflow:
get_task now shows the submission id, graded state, grade, group, and
the handed-in files with ids ready for download_file. list_submissions
surveys tasks for "what have I handed in" and "what is still ungraded".
Both state the text/feedback gap rather than implying none was given.
Two things found while building it:
files-storage ignores the parentType path segment when listing —
.../gradings/{id} returns the same records, saying parentType
"submissions". Filtering on each record's own parentType, or a student's
own upload gets reported back as teacher feedback.
get_task could not find this task at all: the task lists only cover the
dashboard, and group-project tasks are absent from both, so it claimed
the id was wrong for a task the account can plainly see. It now falls
back to scanning course pages.
Also bounds live search by measured cost: resolving attachments needs a
request per board element, which is 2s for one course but 325s for all
of them — beyond any client timeout. An unscoped fresh search now reads
text only and says so.
38/38 smoke checks; verified end to end through Claude Code against a
real graded group submission.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -154,10 +154,22 @@ if (fileId) {
|
||||
|
||||
console.log('\n== search ==');
|
||||
const searchTerm = process.env.SMOKE_SEARCH ?? 'Datenschutz';
|
||||
const search = await call('search', { query: searchTerm, fresh: true });
|
||||
check(`search "${searchTerm}" (fresh, bypassing any index)`, !search.isError, search.text.split('\n')[0]);
|
||||
const search = await call('search', { query: searchTerm, fresh: true, courseId: courseIds[0] });
|
||||
check(`search "${searchTerm}" (fresh + scoped, bypassing any index)`, !search.isError, search.text.split('\n')[0]);
|
||||
const freshAll = await call('search', { query: searchTerm, fresh: true });
|
||||
check('unscoped fresh search returns without timing out', !freshAll.isError, freshAll.text.split('\n')[0]);
|
||||
check('search scoped to one course', !(await call('search', { query: 'a b', courseId: courseIds[0], fresh: true })).isError);
|
||||
|
||||
console.log('\n== submissions ==');
|
||||
if (taskId) {
|
||||
const task = await call('get_task', { taskId });
|
||||
check('get_task still works with submission lookup', !task.isError);
|
||||
const subs = await call('list_submissions', { courseId: courseIds[0], scope: 'all' });
|
||||
check('list_submissions scoped to a course', !subs.isError, subs.text.split('\n')[0]);
|
||||
const all = await call('list_submissions', { limit: 5 });
|
||||
check('list_submissions unscoped', !all.isError, all.text.split('\n')[0]);
|
||||
}
|
||||
|
||||
console.log('\n== index tools ==');
|
||||
// These degrade gracefully without DATABASE_URL, so assert on either outcome
|
||||
// rather than requiring a database for the smoke run to be meaningful.
|
||||
|
||||
Reference in New Issue
Block a user