From df16aca58416405e6888aaa7a461f03816b30143 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 13 Dec 2022 18:24:51 -0500 Subject: [PATCH] Bug fixes and new features - Only pushes valid data to grade history (messes up a lot if this happens) - Fixed grade average calculation - Added support for "Calculate based only on graded assignments" checkbox. It now shows the average if unused weight groups were 0% - Updated naming to be more consistent --- src/content.ts | 18 ++++-- src/displayGradeData.ts | 91 +++++++++++++++++++++++++++--- src/getClassAverage.ts | 19 ++++--- src/getGradesByWeightGroup.ts | 4 +- src/gradeHistory.ts | 10 +++- src/onlyGradedAssignmentsToggle.ts | 35 ++++++++++++ 6 files changed, 151 insertions(+), 26 deletions(-) create mode 100644 src/onlyGradedAssignmentsToggle.ts diff --git a/src/content.ts b/src/content.ts index a641e41..f34464e 100644 --- a/src/content.ts +++ b/src/content.ts @@ -14,6 +14,7 @@ import { configureSettings, displaySettings, getSettings } from "./settings"; import { getWeightGroups } from "./getWeightGroups"; import { Assignment, WeightGroups } from "./types"; import { displayInaccuracies } from "./getInaccuracies"; +import { setOnlyGradedAssignmentsHandler } from "./onlyGradedAssignmentsToggle"; function gradesPage() { @@ -35,16 +36,21 @@ function gradesPage() { // Get user score const userScore: number = getUserScore(); - // Set the grade history - setGradeHistory(courseId, { - date: new Date(), - average: classAverage, - total: userScore - }); + // Set the grade history only if the class average and user score are defined + if (classAverage && userScore) { + setGradeHistory(courseId, { + date: new Date(), + average: classAverage, + total: userScore + }); + } // Get the grade history const gradeHistory = getGradeHistory(courseId); + // Set only graded assignments handler + setOnlyGradedAssignmentsHandler(gradeHistory, assignments, weightGroups); + // Display the class average displayAverage(classAverage, userScore, gradeHistory); diff --git a/src/displayGradeData.ts b/src/displayGradeData.ts index 2b3b637..e12de60 100644 --- a/src/displayGradeData.ts +++ b/src/displayGradeData.ts @@ -1,6 +1,7 @@ import { devLog } from "./devLog"; import { getGradesByWeightGroup } from "./getGradesByWeightGroup"; import { getUserScore } from "./getUserScore"; +import { onlyGradedAssignments } from "./onlyGradedAssignmentsToggle"; import { getSettings } from "./settings"; import { Assignment, GradeHistory, WeightGroups } from "./types"; @@ -24,6 +25,7 @@ function gradeChangeSpan(previousScore: number, currentScore: number): string { export function displayAverage(average: number, userScore: number, gradeHistory: GradeHistory[]) { const finalGradeElement = document.getElementById('student-grades-right-content')?.querySelector('.final_grade'); const classAverageElement = document.createElement('div'); + classAverageElement.id = 'cca-class-average'; classAverageElement.innerText = `Class Average: `; // If there is no final grade element, return @@ -32,8 +34,11 @@ export function displayAverage(average: number, userScore: number, gradeHistory: return; } + // Sort grade history by date + const sortedGradeHistory = gradeHistory.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + // Get the previous average, or set it to the current average if there is no previous average - const previousAverage: number = gradeHistory.length <= 1 ? average : gradeHistory[gradeHistory.length - 2].average; + const previousAverage: number = sortedGradeHistory.length <= 1 ? average : sortedGradeHistory[1].average; // Create the class average percent element let averagePercentSpan: string = gradeChangeSpan(previousAverage, average); @@ -61,6 +66,43 @@ export function displayAverage(average: number, userScore: number, gradeHistory: } +// Update the average under the user's total score +export function updateAverageDisplay(average: number, userScore: number, gradeHistory: GradeHistory[]) { + + const classAverageElement = document.getElementById('cca-class-average'); + + // If there is no final grade element, return + if (!classAverageElement) { + devLog('No class average element found', 'err'); + return; + } + + // Sort grade history by date + const sortedGradeHistory = gradeHistory.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + + // Get the previous average, or set it to the current average if there is no previous average + const previousAverage: number = sortedGradeHistory.length <= 1 ? average : sortedGradeHistory[0].average; + + // Create the class average percent element + let averagePercentSpan: string = gradeChangeSpan(previousAverage, average); + + // If enabled, display how the user's score compares to the class average + if (getSettings('averageComparison').value) { + + if (average > userScore) { + const averageDifference = ((Math.abs(average - userScore) * 100).toFixed(2)); + averagePercentSpan += ` (You are ${averageDifference}% behind the class average)`; + } else if (average < userScore) { + const averageDifference = ((Math.abs(userScore - average) * 100).toFixed(2)); + averagePercentSpan += ` (You are ${averageDifference}% ahead of the class average)`; + } + } + + // Add the average percent element to the class average element + classAverageElement.innerHTML = `Class Average: ${averagePercentSpan}`; + +} + // Display user's grade change export function displayGradeChange(gradeHistory: GradeHistory[]) { const finalGradeElement = document.getElementById('student-grades-right-content')?.querySelector('.final_grade'); @@ -71,8 +113,11 @@ export function displayGradeChange(gradeHistory: GradeHistory[]) { return; } + // Sort grade history by date + const sortedGradeHistory = gradeHistory.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + // Get the previous user score - const previousScore: number = gradeHistory[gradeHistory.length - 2].total; + const previousScore: number = sortedGradeHistory[1].total; // Create the new total percent element const totalPercentSpan: string = gradeChangeSpan(previousScore, getUserScore()); @@ -184,23 +229,23 @@ export function displayGradeHistory(gradeHistory: GradeHistory[]) { // Add table body const gradeHistoryTableBody = document.createElement('tbody'); - // Reverse the average history so the most recent is at the top - gradeHistory.reverse(); + // Sort grade history by date + let sortedGradeHistory = gradeHistory.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + // Remove the first element - gradeHistory.shift(); + sortedGradeHistory.shift(); // Trim list to 10 - gradeHistory = gradeHistory.slice(0, 10); + sortedGradeHistory = sortedGradeHistory.slice(0, 10); // Add table rows - gradeHistory.forEach((gradeHistoryItem, index) => { - const previousGradeHistory = gradeHistory[index + 1]; + sortedGradeHistory.forEach((gradeHistoryItem, index) => { + const previousGradeHistory = sortedGradeHistory[index + 1]; gradeHistoryTableBody.appendChild(gradeHistoryRow(gradeHistoryItem, previousGradeHistory)); }); // Append table body gradeHistoryTable.appendChild(gradeHistoryTableBody); - const hideShowHistoryButton = hideShowGradeHistoryButton(gradeHistoryTable); // Add the button to the DOM @@ -261,9 +306,11 @@ export function displayAverageByWeightGroup(assignments: Assignment[], weightGro // If the weight group average doesn't exist, return if (!weightGroupAverage && weightGroupName !== 'total') { averageCell.innerText = '-'; + averageCell.classList.add('cca-no-score'); } else if (weightGroupName === 'total') { // Populate the average cell with average averageCell.innerText = `${(average * 100).toFixed(1)}%`; + averageCell.id = 'cca-weight-table-total-average'; } else { // Populate the average cell with average averageCell.innerText = `${((weightGroupAverage.average / weightGroupAverage.possible) * 100).toFixed(1)}%`; @@ -275,6 +322,30 @@ export function displayAverageByWeightGroup(assignments: Assignment[], weightGro }); } +// Update average by weight group +export function updateAverageAndScoreByWeightGroup(average: number, userScore: number) { + const averageCell = document.getElementById('cca-weight-table-total-average'); + const scoreCell = document.getElementById('cca-weight-table-total-score'); + const noScoreCells = Array.from(document.getElementsByClassName('cca-no-score')); + + // Update the average cell + if (averageCell) { + averageCell.innerText = `${(average * 100).toFixed(1)}%`; + } + + // Update the users score cell + if (scoreCell) { + scoreCell.innerText = `${(userScore * 100).toFixed(1)}%`; + } + + // Update the no score cells + if (noScoreCells.length > 0) { + noScoreCells.forEach((noScoreCell) => { + noScoreCell.textContent = onlyGradedAssignments() ? '-' : '0%'; + }); + } +} + // Display user's score by weight group export function displayScoreByWeightGroup(assignments: Assignment[], weightGroups: WeightGroups, userScore: number) { @@ -320,9 +391,11 @@ export function displayScoreByWeightGroup(assignments: Assignment[], weightGroup // If the weight group score doesn't exist, return if (!weightGroupScore && weightGroupName !== 'total') { scoreCell.innerText = '-'; + scoreCell.classList.add('cca-no-score'); } else if (weightGroupName === 'total') { // Populate the score cell with score scoreCell.innerText = `${(userScore * 100).toFixed(1)}%`; + scoreCell.id = 'cca-weight-table-total-score'; } else { // Populate the score cell with score scoreCell.innerText = `${((weightGroupScore.score / weightGroupScore.possible) * 100).toFixed(1)}%`; diff --git a/src/getClassAverage.ts b/src/getClassAverage.ts index ca6bd63..a2d5d43 100644 --- a/src/getClassAverage.ts +++ b/src/getClassAverage.ts @@ -1,10 +1,11 @@ import { getGradesByWeightGroup } from "./getGradesByWeightGroup"; +import { onlyGradedAssignments } from "./onlyGradedAssignmentsToggle"; import { Assignment, GroupScores, WeightGroups } from "./types"; -export function getClassAverage(assignmentList: Assignment[], weightGroups: WeightGroups): number { +export function getClassAverage(assignments: Assignment[], weightGroups: WeightGroups): number { // Get group scores - const groupScores: GroupScores = getGradesByWeightGroup(assignmentList, weightGroups); + const groupScores: GroupScores = getGradesByWeightGroup(assignments, weightGroups); let classAverage = 0; @@ -14,14 +15,16 @@ export function getClassAverage(assignmentList: Assignment[], weightGroups: Weig classAverage += (groupScore.average / groupScore.possible) * weightGroups[group]; } - // Adjust total for missing weight group averages - for (const group in weightGroups) { - if (!groupScores[group] && group !== 'total' && weightGroups[group] > 0) { - weightGroups.total -= weightGroups[group]; + // Add percentage for missing weights if only graded assignments is checked + let totalMissingWeight = 1 - weightGroups.total; + if (onlyGradedAssignments()) { + for (const group in weightGroups) { + if (!groupScores[group] && group !== 'total' && weightGroups[group] > 0) { + totalMissingWeight += weightGroups[group]; + } } } - // Return the average of all assignments - return classAverage / weightGroups.total; + return classAverage + totalMissingWeight; } \ No newline at end of file diff --git a/src/getGradesByWeightGroup.ts b/src/getGradesByWeightGroup.ts index 75ed548..c2f5efa 100644 --- a/src/getGradesByWeightGroup.ts +++ b/src/getGradesByWeightGroup.ts @@ -1,12 +1,12 @@ import { devLog } from "./devLog"; import { Assignment, GroupScores, WeightGroups } from "./types"; -export function getGradesByWeightGroup(assignmentList: Assignment[], weightGroups: WeightGroups): GroupScores { +export function getGradesByWeightGroup(assignments: Assignment[], weightGroups: WeightGroups): GroupScores { let groupScores: GroupScores = {}; // For each assignment, calculate the weighted score - assignmentList.forEach((assignment: Assignment) => { + assignments.forEach((assignment: Assignment) => { if (assignment.countsTowardsFinal && !assignment.dropped && assignment.hasAverage) { if (Object.keys(weightGroups).length <= 1 || !assignment.group || !weightGroups[assignment.group]) { diff --git a/src/gradeHistory.ts b/src/gradeHistory.ts index 8289c56..bc1df07 100644 --- a/src/gradeHistory.ts +++ b/src/gradeHistory.ts @@ -10,8 +10,16 @@ export function getGradeHistory(courseId: number): GradeHistory[] { return []; } + const parsedGradeHistory = JSON.parse(gradeHistory); + + // Sort grade history by date + const sortedGradeHistory = parsedGradeHistory.sort((a: GradeHistory, b: GradeHistory) => { + return new Date(a.date).getTime() - new Date(b.date).getTime(); + }); + + // Return the parsed grade history - return JSON.parse(gradeHistory); + return sortedGradeHistory; } export function setGradeHistory(courseId: number, gradeHistory: GradeHistory) { diff --git a/src/onlyGradedAssignmentsToggle.ts b/src/onlyGradedAssignmentsToggle.ts new file mode 100644 index 0000000..3a5f397 --- /dev/null +++ b/src/onlyGradedAssignmentsToggle.ts @@ -0,0 +1,35 @@ +import { displayGradeChange, updateAverageAndScoreByWeightGroup, updateAverageDisplay } from "./displayGradeData"; +import { getClassAverage } from "./getClassAverage"; +import { getUserScore } from "./getUserScore"; +import { Assignment, GradeHistory, WeightGroups } from "./types"; + +export function onlyGradedAssignments(): boolean { + // Get the checkbox + const checkbox = document.getElementById("only_consider_graded_assignments") as HTMLInputElement; + const onlyGradedAssignments = checkbox.checked; + + return onlyGradedAssignments; +} + +function updateGradeData(gradeHistory: GradeHistory[], assignments: Assignment[], weightGroups: WeightGroups) { + + const average = getClassAverage(assignments, weightGroups); + const userScore = getUserScore(); + + // Update the average + updateAverageDisplay(average, userScore, gradeHistory); + + // Update user's score + displayGradeChange(gradeHistory); + + // Update totals in weight table + updateAverageAndScoreByWeightGroup(average, userScore); +} + +export function setOnlyGradedAssignmentsHandler(gradeHistory: GradeHistory[], assignments: Assignment[], weightGroups: WeightGroups) { + const checkbox = document.getElementById("only_consider_graded_assignments") as HTMLInputElement; + checkbox.addEventListener("change", () => { + // Update the average + updateGradeData(gradeHistory, assignments, weightGroups); + }); +} \ No newline at end of file