Exam generation rework, batch user tables, fastapi endpoint switch

This commit is contained in:
Carlos-Mesquita
2024-11-04 23:29:14 +00:00
parent a2bc997e8f
commit 15c9c4d4bd
148 changed files with 11348 additions and 3901 deletions

View File

@@ -51,7 +51,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
return currentUser;
}));
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/batch_users`, { makerID: maker.id, users: usersWithPasswordHashes }, {
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/user/import`, { makerID: maker.id, users: usersWithPasswordHashes }, {
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},

View File

@@ -84,7 +84,7 @@ async function getCorrespondingStat(id: string, index: number): Promise<Stat> {
}
async function evaluate(body: {answers: object[]}, variant?: "initial" | "final"): Promise<AxiosResponse> {
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/speaking_task_${variant === "initial" ? "1" : "3"}`, body, {
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/grade/speaking/${variant === "initial" ? "1" : "3"}`, body, {
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},

View File

@@ -79,7 +79,7 @@ async function getCorrespondingStat(id: string, index: number): Promise<Stat> {
}
async function evaluate(body: {answer: string; question: string}, task: number): Promise<AxiosResponse> {
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/speaking_task_2`, body, {
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/grade/speaking/2`, body, {
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},

View File

@@ -69,7 +69,7 @@ async function getCorrespondingStat(id: string, index: number): Promise<Stat> {
async function evaluate(body: Body): Promise<AxiosResponse> {
const taskNumber = body.task.toString() !== "1" && body.task.toString() !== "2" ? "1" : body.task.toString();
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/writing_task${taskNumber}`, body as Body, {
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/grade/writing/${taskNumber}`, body as Body, {
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},

View File

@@ -1,57 +0,0 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type {NextApiRequest, NextApiResponse} from "next";
import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import {Difficulty, Exam} from "@/interfaces/exam";
import {Module} from "@/interfaces";
import axios from "axios";
export default withIronSessionApiRoute(handler, sessionOptions);
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "GET") return get(req, res);
if (req.method === "POST") return post(req, res);
return res.status(404).json({ok: false});
}
async function get(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) return res.status(401).json({ok: false});
const {endpoint, topic, exercises, difficulty} = req.query as {
module: Module;
endpoint: string;
topic?: string;
exercises?: string[] | string;
difficulty?: Difficulty;
};
const url = `${process.env.BACKEND_URL}/${endpoint}`;
const params = new URLSearchParams();
if (topic) params.append("topic", topic);
if (exercises) (typeof exercises === "string" ? [exercises] : exercises).forEach((exercise) => params.append("exercises", exercise));
if (difficulty) params.append("difficulty", difficulty);
const result = await axios.get(`${url}${params.toString().length > 0 ? `?${params.toString()}` : ""}`, {
headers: {Authorization: `Bearer ${process.env.BACKEND_JWT}`},
});
res.status(200).json(result.data);
}
async function post(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) return res.status(401).json({ok: false});
const {endpoint, topic, exercises} = req.query as {module: Module; endpoint: string[]; topic?: string; exercises?: string[]};
const url = `${process.env.BACKEND_URL}/${endpoint.join("/")}`;
const result = await axios.post(
`${url}${topic && exercises ? `?topic=${topic.toLowerCase()}&exercises=${exercises.join("&exercises=")}` : ""}`,
req.body,
{
headers: {Authorization: `Bearer ${process.env.BACKEND_JWT}`},
},
);
res.status(200).json(result.data);
}

View File

@@ -0,0 +1,79 @@
export const config = {
api: {
bodyParser: false
}
};
import type { NextApiRequest, NextApiResponse } from "next";
import { withIronSessionApiRoute } from "iron-session/next";
import { sessionOptions } from "@/lib/session";
import axios from "axios";
import formidable from 'formidable';
import FormData from 'form-data';
import { readFileSync } from 'fs';
export default withIronSessionApiRoute(handler, sessionOptions);
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") return post(req, res);
return res.status(404).json({ ok: false });
}
async function post(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) return res.status(401).json({ ok: false });
try {
const form = formidable({
multiples: true,
});
const [_, files] = await form.parse(req);
const formData = new FormData();
if (files.exercises?.[0]) {
const file = files.exercises[0];
const buffer = readFileSync(file.filepath);
formData.append('exercises', buffer, {
filename: file.originalFilename!,
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
knownLength: buffer.length
});
}
if (files.solutions?.[0]) {
const file = files.solutions[0];
const buffer = readFileSync(file.filepath);
formData.append('solutions', buffer, {
filename: file.originalFilename!,
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
knownLength: buffer.length
});
}
const result = await axios.post(
`${process.env.BACKEND_URL}/${req.query.module}/import`,
formData,
{
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
...formData.getHeaders()
}
}
);
return res.status(200).json(result.data);
} catch (error) {
console.error(error);
return res.status(500).json({
error: 'Upload failed',
details: error instanceof Error ? error.message : 'Unknown error'
});
}
}

View File

@@ -0,0 +1,47 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { withIronSessionApiRoute } from "iron-session/next";
import { sessionOptions } from "@/lib/session";
import axios from "axios";
import queryToURLSearchParams from "@/utils/query.to.url.params";
export default withIronSessionApiRoute(handler, sessionOptions);
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "GET") return get(req, res);
if (req.method === "POST") return post(req, res);
return res.status(404).json({ ok: false });
}
async function get(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) return res.status(401).json({ ok: false });
const queryParams = queryToURLSearchParams(req);
const endpoint = queryParams.getAll('module').join("/");
queryParams.delete('module');
const result = await axios.get(`${process.env.BACKEND_URL}/${endpoint}${queryParams.size > 0 ? `?${queryParams.toString()}` : ""}`, {
headers: { Authorization: `Bearer ${process.env.BACKEND_JWT}` },
});
res.status(200).json(result.data);
}
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")) {
endpoint = "level"
}
const result = await axios.post(`${process.env.BACKEND_URL}/${endpoint}`,
req.body,
{
headers: { Authorization: `Bearer ${process.env.BACKEND_JWT}` },
},
);
res.status(200).json(result.data);
}

View File

@@ -1,8 +1,8 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type {NextApiRequest, NextApiResponse} from "next";
import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import axios from "axios";
import queryToURLSearchParams from "@/utils/query.to.url.params";
export default withIronSessionApiRoute(handler, sessionOptions);
@@ -12,16 +12,19 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
return res.status(404).json({ok: false});
}
async function post(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) return res.status(401).json({ok: false});
const body = req.body;
const params = new URLSearchParams();
const queryParams = queryToURLSearchParams(req);
const endpoint = queryParams.getAll('module').join("/");
Object.keys(body).forEach((key) => params.append(key, body[key]));
const result = await axios.get(`${process.env.BACKEND_URL}/custom_level?${params.toString()}`, {
headers: {Authorization: `Bearer ${process.env.BACKEND_JWT}`},
});
const result = await axios.post(`${process.env.BACKEND_URL}/${endpoint}`,
req.body,
{
headers: {Authorization: `Bearer ${process.env.BACKEND_JWT}`},
},
);
res.status(200).json(result.data);
}
}

View File

@@ -94,7 +94,7 @@ interface SkillsFeedbackResponse extends SkillsFeedbackRequest {
const getSkillsFeedback = async (sections: SkillsFeedbackRequest[]) => {
const backendRequest = await axios.post(
`${process.env.BACKEND_URL}/grading_summary`,
`${process.env.BACKEND_URL}/grade/summary`,
{ sections },
{
headers: {

View File

@@ -20,7 +20,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
async function post(req: NextApiRequest, res: NextApiResponse) {
try {
const response = await axios.post(`${process.env.BACKEND_URL}/training_content`, req.body, {
const response = await axios.post(`${process.env.BACKEND_URL}/training/`, req.body, {
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},

View File

@@ -0,0 +1,47 @@
import { sessionOptions } from '@/lib/session';
import { withIronSessionApiRoute } from 'iron-session/next';
import type { NextApiRequest, NextApiResponse } from 'next'
import client from "@/lib/mongodb";
const db = client.db(process.env.MONGODB_DB);
export default withIronSessionApiRoute(handler, sessionOptions);
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { op } = req.query
if (req.method === 'GET') {
switch (op) {
default:
res.status(400).json({ error: 'Invalid operation!' })
}
}
else if (req.method === 'POST') {
switch (op) {
case 'crossRefEmails':
res.status(200).json(await crossRefEmails(req.body.emails))
break;
default:
res.status(400).json({ error: 'Invalid operation!' })
}
} else {
res.status(400).end(`Method ${req.method} Not Allowed`)
}
}
async function crossRefEmails(emails: string[]) {
return await db.collection("users").aggregate([
{
$match: {
email: { $in: emails }
}
},
{
$project: {
_id: 0,
email: 1
}
}
]).toArray();
}