added access variable to exams soo we can distinguish private, public and confidential exams and also bugfixes and improvements

This commit is contained in:
José Lima
2025-02-09 04:28:34 +00:00
parent f95bce6fa2
commit b175d8797e
32 changed files with 1320 additions and 909 deletions

View File

@@ -64,6 +64,7 @@ export const getExams = async (
.collection(module)
.find<Exam>({
isDiagnostic: false,
access: "public",
})
.toArray();
@@ -72,7 +73,7 @@ export const getExams = async (
...doc,
module,
})) as Exam[],
).filter((x) => !x.private);
)
let exams: Exam[] = await filterByEntities(shuffledPublicExams, userId);
exams = filterByVariant(exams, variant);

View File

@@ -23,7 +23,7 @@ export const sortByModuleName = (a: string, b: string) => {
};
export const countExercises = (exercises: Exercise[]) => {
const lengthMap = exercises.map((e) => {
const lengthMap = (exercises ?? []).map((e) => {
if (e.type === "multipleChoice") return e.questions.length;
if (e.type === "interactiveSpeaking") return e.prompts.length;
if (e.type === "fillBlanks") return e.solutions.length;
@@ -40,37 +40,37 @@ export const countCurrentExercises = (
exercises: Exercise[],
exerciseIndex: number,
questionIndex?: number
) => {
) => {
return exercises.reduce((acc, exercise, index) => {
if (index > exerciseIndex) {
return acc;
}
let count = 0;
if (exercise.type === "multipleChoice") {
if (index === exerciseIndex && questionIndex !== undefined) {
count = questionIndex + 1;
} else {
count = exercise.questions!.length;
if (index > exerciseIndex) {
return acc;
}
} else if (exercise.type === "interactiveSpeaking") {
count = exercise.prompts.length;
} else if (exercise.type === "fillBlanks") {
count = exercise.solutions.length;
} else if (exercise.type === "writeBlanks") {
count = exercise.solutions.length;
} else if (exercise.type === "matchSentences") {
count = exercise.sentences.length;
} else if (exercise.type === "trueFalse") {
count = exercise.questions.length;
} else {
count = 1;
}
return acc + count;
let count = 0;
if (exercise.type === "multipleChoice") {
if (index === exerciseIndex && questionIndex !== undefined) {
count = questionIndex + 1;
} else {
count = exercise.questions!.length;
}
} else if (exercise.type === "interactiveSpeaking") {
count = exercise.prompts.length;
} else if (exercise.type === "fillBlanks") {
count = exercise.solutions.length;
} else if (exercise.type === "writeBlanks") {
count = exercise.solutions.length;
} else if (exercise.type === "matchSentences") {
count = exercise.sentences.length;
} else if (exercise.type === "trueFalse") {
count = exercise.questions.length;
} else {
count = 1;
}
return acc + count;
}, 0);
};
};
export const countFullExams = (stats: Stat[]) => {
const sessionExams = groupBySession(stats);

View File

@@ -2,7 +2,6 @@ import { EntityWithRoles, Role } from "@/interfaces/entity";
import { PermissionType } from "@/interfaces/permissions";
import { User, Type, userTypes } from "@/interfaces/user";
import { RolePermission } from "@/resources/entityPermissions";
import axios from "axios";
import { findBy, mapBy } from ".";
import { isAdmin } from "./users";

View File

@@ -1,8 +1,9 @@
import {WithLabeledEntities} from "@/interfaces/entity";
import {User} from "@/interfaces/user";
import {USER_TYPE_LABELS} from "@/resources/user";
import {capitalize} from "lodash";
import { WithLabeledEntities } from "@/interfaces/entity";
import { User } from "@/interfaces/user";
import { USER_TYPE_LABELS } from "@/resources/user";
import { capitalize } from "lodash";
import moment from "moment";
import ExcelJS from "exceljs";
export interface UserListRow {
name: string;
@@ -17,6 +18,22 @@ export interface UserListRow {
gender: string;
}
const indexToLetter = (index: number): string => {
// Base case: if the index is less than 0, return an empty string
if (index < 0) {
return '';
}
// Calculate the quotient for recursion (number of times the letter sequence repeats)
const quotient = Math.floor(index / 26);
// Calculate the remainder for the current letter
const remainder = index % 26;
// Recursively call indexToLetter for the quotient and append the current letter
return indexToLetter(quotient - 1) + String.fromCharCode(65 + remainder);
};
export const exportListToExcel = (rowUsers: WithLabeledEntities<User>[]) => {
const rows: UserListRow[] = rowUsers.map((user) => ({
name: user.name,
@@ -33,10 +50,31 @@ export const exportListToExcel = (rowUsers: WithLabeledEntities<User>[]) => {
gender: user.demographicInformation?.gender ? capitalize(user.demographicInformation.gender) : "N/A",
verified: user.isVerified?.toString() || "FALSE",
}));
const header = "Name,Email,Type,Entities,Expiry Date,Country,Phone,Employment/Department,Gender,Verification";
const rowsString = rows.map((x) => Object.values(x).join(",")).join("\n");
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet("User Data");
const border: Partial<ExcelJS.Borders> = { top: { style: 'thin' as ExcelJS.BorderStyle }, left: { style: 'thin' as ExcelJS.BorderStyle }, bottom: { style: 'thin' as ExcelJS.BorderStyle }, right: { style: 'thin' as ExcelJS.BorderStyle } }
const header = ['Name', 'Email', 'Type', 'Entities', 'Expiry Date', 'Country', 'Phone', 'Employment/Department', 'Gender', 'Verification'].forEach((item, index) => {
const cell = worksheet.getCell(`${indexToLetter(index)}1`);
const column = worksheet.getColumn(index + 1);
column.width = item.length * 2;
cell.value = item;
cell.font = { bold: true, size: 16 };
cell.border = border;
return `${header}\n${rowsString}`;
});
rows.forEach((x, index) => {
(Object.keys(x) as (keyof UserListRow)[]).forEach((key, i) => {
const cell = worksheet.getCell(`${indexToLetter(i)}${index + 2}`);
cell.value = x[key];
if (index === 0) {
const column = worksheet.getColumn(i + 1);
column.width = Math.max(column.width ?? 0, x[key].toString().length * 2);
}
cell.border = border;
});
})
return workbook.xlsx.writeBuffer();
};
export const getUserName = (user?: User) => {