ENCOA-274

This commit is contained in:
Carlos-Mesquita
2024-12-11 15:28:38 +00:00
parent 7538392e44
commit efba1939e5
12 changed files with 344 additions and 130 deletions

View File

@@ -185,12 +185,12 @@ export default function BatchCreateUser({ user, entities = [], permissions, onFi
if (!confirm(`You are about to ${[newUsersSentence, existingUsersSentence].filter((x) => !!x).join(" and ")}, are you sure you want to continue?`))
return;
/*Promise.all(duplicatedUsers.map(async (u) => await axios.post(`/api/invites`, {to: u.id, entity, from: user.id})))
.then(() => toast.success(`Successfully invited ${duplicatedUsers.length} registered student(s)!`))
Promise.all(newUsers.map(async (u) => await axios.post(`/api/invites`, {to: u.id, entity, from: user.id})))
.then(() => toast.success(`Successfully invited ${newUsers.length} registered student(s)!`))
.finally(() => {
if (newUsers.length === 0) setIsLoading(false);
});
*/
if (newUsers.length > 0) {
setIsLoading(true);
@@ -392,6 +392,7 @@ export default function BatchCreateUser({ user, entities = [], permissions, onFi
<UserTable users={duplicatedUsers} />
</div>
)}
{(duplicatedUsers.length !== 0 && newUsers.length === 0) && <span className="text-red-500 font-bold">The imported .csv only contains duplicated users!</span>}
<Button className="my-auto mt-4" onClick={makeUsers} disabled={newUsers.length === 0}>
Create {newUsers.length !== 0 ? `${newUsers.length} New Users` : ''}
</Button>

View File

@@ -11,7 +11,7 @@ import Reading from "@/exams/Reading";
import Selection from "@/exams/Selection";
import Speaking from "@/exams/Speaking";
import Writing from "@/exams/Writing";
import { Exam, LevelExam, UserSolution, Variant } from "@/interfaces/exam";
import { Exam, LevelExam, UserSolution, Variant, WritingExam } from "@/interfaces/exam";
import { User } from "@/interfaces/user";
import { evaluateSpeakingAnswer, evaluateWritingAnswer } from "@/utils/evaluation";
import { getExam } from "@/utils/exams";
@@ -126,7 +126,7 @@ export default function ExamPage({ page, user, destination = "/", hideSidebar =
await Promise.all(
exam.exercises.map(async (exercise, index) => {
if (exercise.type === "writing")
await evaluateWritingAnswer(user.id, sessionId, exercise, index + 1, userSolutions.find((x) => x.exercise === exercise.id)!);
await evaluateWritingAnswer(user.id, sessionId, exercise, index + 1, userSolutions.find((x) => x.exercise === exercise.id)!, exercise.attachment?.url);
if (exercise.type === "interactiveSpeaking" || exercise.type === "speaking"){
await evaluateSpeakingAnswer(

View File

@@ -65,7 +65,8 @@ async function POST(req: NextApiRequest, res: NextApiResponse) {
// Check whether the id of the exam matches another exam with different
// owners, throw exception if there is, else allow editing
const ownersSet = new Set(docSnap?.owners || []);
if (docSnap?.owners?.length === exam.owners.lenght && exam.owners.every((e: string) => ownersSet.has(e))) {
if (docSnap !== null && docSnap?.owners?.length === exam.owners.lenght && exam.owners.every((e: string) => ownersSet.has(e))) {
throw new Error("Name already exists");
}

View File

@@ -31,6 +31,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) return res.status(401).json({ ok: false });
const queryParams = queryToURLSearchParams(req);
let endpoint = queryParams.getAll('module').join("/");
if (endpoint.startsWith("level")) {
@@ -41,12 +42,27 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
endpoint = "reading/"
}
const result = await axios.post(`${process.env.BACKEND_URL}/${endpoint}`,
req.body,
{
headers: { Authorization: `Bearer ${process.env.BACKEND_JWT}` },
},
);
queryParams.delete('module');
const queryString = queryParams.toString();
res.status(200).json(result.data);
}
const hasFiles = req.headers['content-type']?.startsWith('multipart/form-data');
// https://github.com/vercel/next.js/discussions/36153#discussioncomment-3029675
// This just proxies the request
const { data } = await axios.post(
`${process.env.BACKEND_URL}/${endpoint}${hasFiles ? '/attachment' : ''}${queryString.length > 0 ? `?${queryString}` : ''}`, req, {
responseType: "stream",
headers: {
"Content-Type": req.headers["content-type"],
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},
});
data.pipe(res);
}
export const config = {
api: {
bodyParser: false,
},
};

View File

@@ -93,6 +93,11 @@ export default function Generation({ id, user, exam, examModule, permissions }:
useEffect(() => {
return () => {
const state = modules;
if (state.writing.academic_url) {
URL.revokeObjectURL(state.writing.academic_url);
}
state.listening.sections.forEach(section => {
const listeningPart = section.state as ListeningPart;
if (listeningPart.audio?.source) {