mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
Various changes/fixes/polish
- Adds weight label below each assessment
- Correlates assessment name with its ID, so each dom element can be identified: {trimmed_title: assessmentID}
- Slightly changed regex to be a little more permissive
- Changed pdfjs src/type due to CORS issues on firefox
- Permitted weightings to be zero, but not less than zero
- Modified how assessment items are iterated through, as the previous approach assumed they're in the same order as they are in react
- Changed parseAssessments() to immediately dispatch parsing for all pdfs asynchronously, as doing it serially is painstakingly slow
- Discard useless decimals when displaying weight (.0)
This commit is contained in:
@@ -16,6 +16,7 @@ pdfjs.GlobalWorkerOptions.workerSrc =
|
|||||||
// Storage
|
// Storage
|
||||||
interface weightingsStorage {
|
interface weightingsStorage {
|
||||||
weightings: Record<string, string>;
|
weightings: Record<string, string>;
|
||||||
|
assessments: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = defineSettings({
|
const settings = defineSettings({
|
||||||
@@ -47,6 +48,9 @@ const assessmentsAveragePlugin: Plugin<typeof settings, weightingsStorage> = {
|
|||||||
if (!api.storage.weightings) {
|
if (!api.storage.weightings) {
|
||||||
api.storage.weightings = {};
|
api.storage.weightings = {};
|
||||||
}
|
}
|
||||||
|
if (!api.storage.assessments) {
|
||||||
|
api.storage.assessments = {};
|
||||||
|
}
|
||||||
|
|
||||||
// Clear any stuck "processing" states so they can retry
|
// Clear any stuck "processing" states so they can retry
|
||||||
let hasStuckProcessing = false;
|
let hasStuckProcessing = false;
|
||||||
@@ -190,9 +194,8 @@ const assessmentsAveragePlugin: Plugin<typeof settings, weightingsStorage> = {
|
|||||||
let hasInaccurateWeighting = false;
|
let hasInaccurateWeighting = false;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for (let i = 0; i < marks.length && i < assessmentItems.length; i++) {
|
// Iterate through assessments for processing
|
||||||
const mark = marks[i];
|
for (const assessmentItem of assessmentItems) {
|
||||||
const assessmentItem = assessmentItems[i];
|
|
||||||
const gradeElement = assessmentItem.querySelector(
|
const gradeElement = assessmentItem.querySelector(
|
||||||
`[class*='Thermoscore__text___']`,
|
`[class*='Thermoscore__text___']`,
|
||||||
);
|
);
|
||||||
@@ -202,18 +205,86 @@ const assessmentsAveragePlugin: Plugin<typeof settings, weightingsStorage> = {
|
|||||||
const grade = parseGrade(gradeElement.textContent || "");
|
const grade = parseGrade(gradeElement.textContent || "");
|
||||||
if (grade <= 0) continue;
|
if (grade <= 0) continue;
|
||||||
|
|
||||||
const assessmentID = String(mark.id);
|
const titleEl = assessmentItem.querySelector(
|
||||||
const weighting = api.storage.weightings[assessmentID];
|
`[class*='AssessmentItem__title___']`,
|
||||||
|
);
|
||||||
|
if (!titleEl) continue;
|
||||||
|
|
||||||
|
const title = titleEl.textContent?.trim();
|
||||||
|
if (!title) continue;
|
||||||
|
|
||||||
|
// Get correlated assessment ID in order to fetch weightings
|
||||||
|
const assessmentID = api.storage.assessments?.[title];
|
||||||
|
const weighting = assessmentID
|
||||||
|
? api.storage.weightings?.[assessmentID]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const statsContainer = assessmentItem.querySelector(
|
||||||
|
`[class*='AssessmentItem__stats___']`,
|
||||||
|
) as HTMLElement;
|
||||||
|
|
||||||
|
// Creates a weighting label next to the average score
|
||||||
|
if (statsContainer) {
|
||||||
|
// Only add label if it hasn't been added before
|
||||||
|
if (!statsContainer.querySelector(".betterseqta-weight-label")) {
|
||||||
|
const label = statsContainer.querySelector(
|
||||||
|
`[class*='Label__Label___']`,
|
||||||
|
) as HTMLElement;
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
// Clone average score node
|
||||||
|
const weightLabel = label.cloneNode(true) as HTMLElement;
|
||||||
|
|
||||||
|
// Mark as added to prevent duplicates
|
||||||
|
weightLabel.classList.add("betterseqta-weight-label");
|
||||||
|
|
||||||
|
const innerTextDiv = weightLabel.querySelector(
|
||||||
|
`[class*='Label__innerText___']`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Repurpose for showing weight
|
||||||
|
if (innerTextDiv) innerTextDiv.textContent = "Weight";
|
||||||
|
|
||||||
|
const textNodes = Array.from(weightLabel.childNodes).filter(
|
||||||
|
(node) => node.nodeType === Node.TEXT_NODE,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set weight value, discarding useless decimals (.0)
|
||||||
|
if (textNodes.length) {
|
||||||
|
textNodes[0].textContent =
|
||||||
|
weighting && weighting !== "processing"
|
||||||
|
? `${Number(weighting) % 1 === 0 ? Number(weighting) : weighting}%`
|
||||||
|
: "N/A";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set position opposite to the average score node
|
||||||
|
statsContainer.style.position = "relative";
|
||||||
|
weightLabel.style.position = "absolute";
|
||||||
|
weightLabel.style.right = "0";
|
||||||
|
weightLabel.style.top = "50%";
|
||||||
|
weightLabel.style.transform = "translateY(-50%)";
|
||||||
|
|
||||||
|
statsContainer.appendChild(weightLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if weighting is unavailable or still processing
|
// Check if weighting is unavailable or still processing
|
||||||
if (!weighting || weighting === "N/A" || weighting === "processing") {
|
if (
|
||||||
|
weighting === null ||
|
||||||
|
weighting === undefined ||
|
||||||
|
weighting === "N/A" ||
|
||||||
|
weighting === "processing"
|
||||||
|
) {
|
||||||
hasInaccurateWeighting = true;
|
hasInaccurateWeighting = true;
|
||||||
// Fall back to equal weighting if unavailable
|
// Fall back to equal weighting if unavailable
|
||||||
weightedTotal += grade;
|
weightedTotal += grade;
|
||||||
totalWeight += 1;
|
totalWeight += 1;
|
||||||
} else {
|
} else {
|
||||||
const weight = parseFloat(weighting);
|
const weight = parseFloat(weighting);
|
||||||
if (!isNaN(weight) && weight > 0) {
|
|
||||||
|
// If weight is a positive number, add to total
|
||||||
|
if (!isNaN(weight) && weight >= 0) {
|
||||||
weightedTotal += grade * weight;
|
weightedTotal += grade * weight;
|
||||||
totalWeight += weight;
|
totalWeight += weight;
|
||||||
} else {
|
} else {
|
||||||
@@ -410,8 +481,8 @@ async function extractPDFText(url: string): Promise<string> {
|
|||||||
} else {
|
} else {
|
||||||
// Load pdfjs in page context
|
// Load pdfjs in page context
|
||||||
const pdfjsScript = document.createElement('script');
|
const pdfjsScript = document.createElement('script');
|
||||||
pdfjsScript.src = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.min.mjs';
|
pdfjsScript.src = 'https://cdn.jsdelivr.net/npm/pdfjs-dist/build/pdf.min.js';
|
||||||
pdfjsScript.type = 'module';
|
pdfjsScript.type = 'text/javascript';
|
||||||
|
|
||||||
pdfjsScript.onload = function() {
|
pdfjsScript.onload = function() {
|
||||||
extractPDF();
|
extractPDF();
|
||||||
@@ -592,6 +663,7 @@ async function handleWeightings(mark: any, api: any) {
|
|||||||
const metaclassID = mark.metaclassID;
|
const metaclassID = mark.metaclassID;
|
||||||
const userInfo = await getUserInfo();
|
const userInfo = await getUserInfo();
|
||||||
const userID = userInfo.id;
|
const userID = userInfo.id;
|
||||||
|
const title = mark.title;
|
||||||
|
|
||||||
// Skip if already processed (not "processing")
|
// Skip if already processed (not "processing")
|
||||||
if (api.storage.weightings[assessmentID] != undefined && api.storage.weightings[assessmentID] !== "processing") {
|
if (api.storage.weightings[assessmentID] != undefined && api.storage.weightings[assessmentID] !== "processing") {
|
||||||
@@ -604,6 +676,12 @@ async function handleWeightings(mark: any, api: any) {
|
|||||||
[assessmentID]: "processing",
|
[assessmentID]: "processing",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Correlate assessment title with its ID
|
||||||
|
api.storage.assessments = {
|
||||||
|
...api.storage.assessments,
|
||||||
|
[title.trim()]: assessmentID
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const filename =
|
const filename =
|
||||||
"BetterSEQTA-" + String(Math.floor(Math.random() * 1e15)).padStart(15, "0");
|
"BetterSEQTA-" + String(Math.floor(Math.random() * 1e15)).padStart(15, "0");
|
||||||
@@ -652,9 +730,11 @@ async function handleWeightings(mark: any, api: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const match = text.match(/Assessment weight:\s*(\d+\.?\d*)/i);
|
// Match weighting from extracted text
|
||||||
|
const match = text.match(/weight:\s*(\d+\.?\d*)/i);
|
||||||
const weight = match ? match[1] : "N/A";
|
const weight = match ? match[1] : "N/A";
|
||||||
|
|
||||||
|
// Store and correlate weight with assessment ID
|
||||||
api.storage.weightings = {
|
api.storage.weightings = {
|
||||||
...api.storage.weightings,
|
...api.storage.weightings,
|
||||||
[assessmentID]: weight,
|
[assessmentID]: weight,
|
||||||
@@ -675,9 +755,8 @@ async function parseAssessments(api: any) {
|
|||||||
const marks = state["marks"];
|
const marks = state["marks"];
|
||||||
if (!marks) return;
|
if (!marks) return;
|
||||||
|
|
||||||
for (const mark of marks) {
|
// Dispatch for all assessments asynchronously
|
||||||
await handleWeightings(mark, api);
|
await Promise.all(marks.map((mark: any) => handleWeightings(mark, api)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default assessmentsAveragePlugin;
|
export default assessmentsAveragePlugin;
|
||||||
|
|||||||
Reference in New Issue
Block a user