TotalGrade

Adds a total grade on the D2L course grade report

// ==UserScript==
// @name         TotalGrade
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Adds a total grade on the D2L course grade report
// @author       Todd Roberts
// @match        https://ccis.ucourses.com/d2l/lms/grades/my_grades/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // constants
    var RE = /\d+\s\/\s\d+/;
    var GRADEBOX_INDEX = 1;

    function totalGrade(){
        var gradeBox = assembleGradeBox();
        attachGradeBoxToPage(gradeBox);
    }

    function assembleGradeBox(){
    	var gradeBox = createGradeBox();
    	var grade = getGrade();
        var text = getText(grade);
        addGradeToGradeBox(gradeBox, text);
    	return gradeBox;
    }

    function createGradeBox(){
    	return document.createElement("span");
    }

    function getGrade(){
    	var grade = { pointsEarned : 0, pointsPossible : 0 };
    	var rows = getRows();
    	rows.forEach(function(row){
            addRowsPointsToGrade(row, grade);
        });
        return grade;
    }

    function getRows(){
        var allRows = [].slice.call(document.getElementById("z_a").rows);
        var assignmentRows = [];
        allRows.forEach(function (row){
            if (isAssignmentRow(row))
                assignmentRows.push(row);
        });
    	return assignmentRows;
    }

    function isAssignmentRow(row){
        var assignment = false;
        var tds = [].slice.call(row.getElementsByTagName('td'));
        tds.forEach(function(td){
            var label = td.getElementsByTagName('label')[0];
            var text = label === undefined ? "" : label.innerText;
            if (RE.test(text)){
                assignment = true;
            }
        });
        return assignment;
    }

    function addRowsPointsToGrade(row, grade){
        var points = getPoints(row);
        grade.pointsEarned += Number(points.earned);
        if (points.possible !== "ExtraCredit"){
            grade.pointsPossible += Number(points.possible);
    		fixIndividualGrade(row, points);
        }

    }

    function getPoints(row){
        var pointsTd = getPointsTd(row);
        var pointsText = getPointsText(pointsTd);
        return parsePoints(pointsText);
    }

    function getPointsTd(row){
        var pointsTd;
        var tds = row.getElementsByTagName('td');
        if (tds[0].getElementsByTagName('img')[0] === undefined)
            pointsTd = tds[0];
        else{
            GRADEBOX_INDEX = 2;
            pointsTd = tds[1];
        }

        return pointsTd;
    }

    function getPointsText(pointsTd){
        var pointsLabel = pointsTd.getElementsByTagName('label')[0];
    	return pointsLabel.innerText;
    }

    function parsePoints(pointsText){
        var re2 = /^\d+$/;
        if (RE.test(pointsText)){
    		var numerator = getNumerator(pointsText);
    		var denominator = getDenominator(pointsText);
    		return {earned : numerator, possible: denominator};
    	}
    	else if (re2.test(pointsText)){
    		return {earned : pointsText, possible: "ExtraCredit"};
    	}
    }

    function getNumerator(pointsText){
    	var re = /[0-9.]+\s/;
    	var match = pointsText.match(re)[0];
    	return match.trim();
    }

    function getDenominator(pointsText){
    	var re = /\s[0-9.]+/;
    	var match = pointsText.match(re)[0];
    	return match.trim();
    }

    function fixIndividualGrade(row, points){
    	var percentage = getPercentage(points.earned, points.possible);
    	var indivGradeBox = row.getElementsByTagName('td')[GRADEBOX_INDEX];
    	indivGradeBox.innerText = `${percentage}%`;
    }

    function getText(grade){
        var pointsEarned = grade.pointsEarned;
    	var pointsPossible = grade.pointsPossible;
    	var letterGrade = getLetterGrade(pointsEarned, pointsPossible);
    	var percentage = getPercentage(pointsEarned, pointsPossible);
    	return document.createTextNode(`Total: ${pointsEarned} / ${pointsPossible}, ${percentage}% (${letterGrade})`);
    }

     function getLetterGrade(earned, possible){
    	var total = earned / possible;
    	var letter;
    	if (total >= 0.9)
    		letter = 'A';
    	else if (total >= 0.8)
    		letter = 'B';
    	else if (total >= 0.7)
    		letter = 'C';
    	else if (total >= 0.6)
    		letter = 'D';
    	else
    		letter = 'F';
    	return letter;

    }

    function getPercentage(earned, possible){
    	return (earned / possible * 100).toFixed(2);
    }

    function addGradeToGradeBox(gradeBox, text){
        gradeBox.appendChild(text);
    }

    function attachGradeBoxToPage(gradeBox){
    	var host = document.getElementById("d_page_header");
    	host.appendChild(gradeBox);
    }

    totalGrade();

})();