From f1afa74ee64438900226b05b1735436899d7c404 Mon Sep 17 00:00:00 2001 From: StroepWafel <109832156+StroepWafel@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:36:16 +1030 Subject: [PATCH] Add average grade display and Fix CORS violation Add average grade display and also fix the CORS violation caused by pdfjs trying to load PDFs from URLs that Firefox extensions can't access. fixed by instead: - Fetching the PDF as an ArrayBuffer directly from the URL - Passing the ArrayBuffer to pdfjs using { data: arrayBuffer } instead of passing a URL --- .../built-in/assessmentsAverage/index.ts | 87 +++++++++++++++---- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/src/plugins/built-in/assessmentsAverage/index.ts b/src/plugins/built-in/assessmentsAverage/index.ts index 79890d5d..616667d5 100644 --- a/src/plugins/built-in/assessmentsAverage/index.ts +++ b/src/plugins/built-in/assessmentsAverage/index.ts @@ -125,10 +125,12 @@ const assessmentsAveragePlugin: Plugin = { ); if (!assessmentsList) return; - const gradeElements = document.querySelectorAll( - "[class*='Thermoscore__text___']", - ); - if (!gradeElements.length) return; + // Get marks from React state to match with DOM elements + const state = await ReactFiber.find( + "[class*='AssessmentList__items___']", + ).getState(); + const marks = state["marks"]; + if (!marks || !marks.length) return; // Parse and average grades const letterToNumber: Record = { @@ -162,19 +164,59 @@ const assessmentsAveragePlugin: Plugin = { return letterToNumber[str] ?? 0; } - let total = 0; + // Get all assessment items (excluding the average we might have added) + const assessmentItems = Array.from( + assessmentsList.querySelectorAll(`[class*='AssessmentItem__AssessmentItem___']`), + ).filter( + (item) => + !item.querySelector(`[class*='AssessmentItem__title___']`)?.textContent?.includes("Subject Average"), + ); + + // Match marks to assessment items and calculate weighted average + let weightedTotal = 0; + let totalWeight = 0; + let hasInaccurateWeighting = false; let count = 0; - gradeElements.forEach((el) => { - const grade = parseGrade(el.textContent || ""); - if (grade > 0) { - total += grade; - count++; + + for (let i = 0; i < marks.length && i < assessmentItems.length; i++) { + const mark = marks[i]; + const assessmentItem = assessmentItems[i]; + const gradeElement = assessmentItem.querySelector( + `[class*='Thermoscore__text___']`, + ); + + if (!gradeElement) continue; + + const grade = parseGrade(gradeElement.textContent || ""); + if (grade <= 0) continue; + + const assessmentID = String(mark.id); + const weighting = api.storage.weightings[assessmentID]; + + // Check if weighting is unavailable or still processing + if (!weighting || weighting === "N/A" || weighting === "processing") { + hasInaccurateWeighting = true; + // Fall back to equal weighting if unavailable + weightedTotal += grade; + totalWeight += 1; + } else { + const weight = parseFloat(weighting); + if (!isNaN(weight) && weight > 0) { + weightedTotal += grade * weight; + totalWeight += weight; + } else { + // Invalid weight, use equal weighting + weightedTotal += grade; + totalWeight += 1; + hasInaccurateWeighting = true; + } } - }); + count++; + } - if (!count) return; + if (!count || totalWeight === 0) return; - const avg = total / count; + const avg = weightedTotal / totalWeight; const rounded = Math.ceil(avg / 5) * 5; const numberToLetter = Object.entries(letterToNumber).reduce( (acc, [k, v]) => { @@ -195,6 +237,16 @@ const assessmentsAveragePlugin: Plugin = { ); if (existing?.textContent === "Subject Average") return; + // Build warning message if needed + let warningHTML = ""; + if (hasInaccurateWeighting) { + warningHTML = /* html */ ` +
+ ⚠ Some weightings unavailable +
+ `; + } + // Use the dynamic class names in the HTML template const averageElement = stringToHTML(/* html */ `
@@ -202,12 +254,13 @@ const assessmentsAveragePlugin: Plugin = {
Subject Average
+ ${warningHTML}
-
${display}
+
${display}
@@ -219,7 +272,11 @@ const assessmentsAveragePlugin: Plugin = { }; async function extractPDFText(url: string): Promise { - const loadingTask = pdfjs.getDocument(url); + // Fetch PDF as ArrayBuffer to avoid blob URL CSP issues in Firefox extensions + const response = await fetch(url); + const arrayBuffer = await response.arrayBuffer(); + + const loadingTask = pdfjs.getDocument({ data: arrayBuffer }); const pdf = await loadingTask.promise; let text = "";