All files / src/api/teacher TeacherQuestionImportAPI.js

5.88% Statements 1/17
0% Branches 0/7
20% Functions 1/5
6.66% Lines 1/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83                                                                                                                                      5x                              
import axios from "axios";
import { API_ENDPOINTS } from "@/api/endpoints";
 
/**
 * Bulk question import.
 *
 * Calls the Teacher-Student API only. The ingest service that does the parsing is not
 * reachable from a browser, and must not be — the existing bulkUpload.users.* endpoints
 * call a bulk service directly from the client, and that is the pattern this avoids.
 *
 * Developer: Allan Ninal
 */
export class TeacherQuestionImportAPI {
    /**
     * Hand a file to the API and get back a job id. Returns immediately; the caller
     * polls the status endpoint. The parse itself can take minutes.
     *
     * @param {File} file
     * @param {string} questionType One of the API's question type strings, e.g.
     *   "Multiple-choice" or "Drag-and-Drop" — capitalisation matters, the API rejects
     *   anything else. "Auto-detect" means the FILE states a type per item, which only
     *   a PDF does.
     * @param {{gradeLevel?: string|number|null, difficulty?: string|null}} [batch]
     *   Values applied to every question in the file, for the things a document cannot
     *   state: a STAAR released-item PDF names the TEKS, the points and the answer for
     *   each item, but never a grade.
     */
    async upload(file, questionType, { gradeLevel = null, difficulty = null } = {}) {
        const formData = new FormData();
        formData.append("file", file);
        formData.append("question_type", questionType);
 
        // Only when the teacher picked one. An empty field would arrive as the string
        // "" or "null", which the import service rejects as an invalid grade — turning
        // "no grade chosen" into a failed upload rather than a question to answer later.
        if (gradeLevel) formData.append("grade_level", String(gradeLevel));
        if (difficulty) formData.append("difficulty", String(difficulty));
 
        // Content-Type is deliberately NOT set. FormData needs a multipart boundary,
        // which the browser generates; setting the header manually omits it and the
        // server cannot parse the body. Same convention as imageUploadHelper.
        const response = await axios.post(API_ENDPOINTS.teacher.question.import.upload, formData, {
            // A large file over a slow connection outlasts the default timeout, and a
            // timeout here loses the upload after the bytes were already sent.
            timeout: 180000,
        });
        return response;
    }
 
    /**
     * Submit a single question as text. Returns the same job shape as upload(), so the
     * caller polls, reviews and commits exactly as it does for a file.
     */
    async text(text, questionType) {
        const response = await axios.post(API_ENDPOINTS.teacher.question.import.text, {
            text,
            question_type: questionType,
        });
        return response;
    }
 
    async status(jobId) {
        const response = await axios.get(API_ENDPOINTS.teacher.question.import.status(jobId));
        return response;
    }
 
    async questions(jobId) {
        const response = await axios.get(API_ENDPOINTS.teacher.question.import.questions(jobId));
        return response;
    }
 
    /**
     * Approve the reviewed set. Sends back what the teacher actually saw and edited,
     * not what the parser originally proposed.
     */
    async commit(jobId, payload) {
        const response = await axios.post(API_ENDPOINTS.teacher.question.import.commit(jobId), payload, {
            timeout: 180000,
        });
        return response;
    }
}