Path: blob/master/final-grade-calculator/core.js
574 views
/* core.js1* Functions related directly to grade calculation and conversion2* (C) 2019 Ryan Zhang.3This code is free software: you can redistribute it and/or modify4it under the terms of the GNU General Public License as published by5the Free Software Foundation, either version 3 of the License, or6(at your option) any later version.78This code is distributed in the hope that it will be useful,9but WITHOUT ANY WARRANTY; without even the implied warranty of10MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11GNU General Public License for more details.1213You should have received a copy of the GNU General Public License14along with this code. If not, see <https://www.gnu.org/licenses/>.1516Note that this license does NOT apply to the entire site. However, if you're interested in anything on here, contact me at https://ryan778.github.io/about-me/ and we can discuss.17*/181920function convToGrade(letter){21//Returns number: approximate of letter grade, -1 if unknown22letter = letter.toLowerCase();23letter = letter.replace(/ /g, '');24if(letter.length > 3){return -1}25let val = letter.charCodeAt(0);26let grade = -1;27if(val >= 97 && val <= 100){ //A through D28grade = 105 - 10*(val-96); //Returns middle of grade (ex. A -> 95)29let mod = letter.slice(1, 3);30switch(mod){31case '+':32grade += 3;33break;34case '-':35grade -= 3;36break;37case '++':38grade += 5;39break;40case '--':41grade -= 5;42break;43case '':44break;45default:46grade = -1; //Unknown grade47}48}49else if(letter === 'f'){50grade = 50}51else if(letter === 'f-' || letter === 'f--'){52grade = 0}53else if(letter === 's'){grade = 100} //"Satisfactory"54else if(letter === 'u'){grade = 0} //"Unsatisfactory"55return grade;56}5758function convToLetter(grade){59// Returns string: letter grade, input number60if(typeof grade !== 'number'){61return '??'62}63grade = Math.round(grade*100)/100 //Fix floating point rounding errors64if(grade > 100){return 'A++'}65else if(grade === 100){return 'A+'}66else if(grade < 60){67if(grade < 50){68return 'F-'}69return 'F'70}71let letter = String.fromCharCode(Math.floor((199.9999-grade)/10)+87).toUpperCase();72if(grade%10 >= 8){letter += '+'}73else if(grade%10 < 4){letter += '-'}74return letter;75}7677function handleQueryAction(t, n) {78//Returns undefined, sends event to ga79gtag('event', 'fgc_query', {80type: t,81value: n82});83}8485/* The "Math" section with lots and lots of somewhat complicated equations that even I barely / kinda understand */86function calcTestDrop(){87//Requires: testWorth, testAvg, totalTests, lowestTest (from globalData)88//Returns [before (weighted), weighted, overall] rounded to four decimal places89let w = globalData.testWorth, a = globalData.testAvg, t = globalData.totalTests, l = globalData.lowestTest, s = globalData.totalTests*globalData.testAvg;90return [Math.round(10000*a*(w/100))/10000, Math.round((1000000*(s-l)/((t-1)*100))*(w/100))/10000, Math.round(1000000*(s-l)/((t-1)*100))/10000]91}9293function calcTargetGradeS(t){ // "simple" target grade - standard settings w/o test adjustments94let c = globalData.currentGrade, f = globalData.finalWorth, w = globalData.testWorth, a = globalData.testAvg, o = globalData.totalTests, l = globalData.lowestTest;95return (t-c*(1-(f/100)))/(f/100);96}9798function calcTargetTestGrade(t){99/* t = targetGrade100@requires currentGrade, targetGrade, testsWorth, either (tests taken + test average) or (points in cat + points test is worth)101// w(i/j) + d = w((i+s)/(j+k))102*/103let d = (t - globalData.currentGrade)/100;104let w = globalData.testWorth/100, a, i, j, k;105// a = test average (out of 1.00), i = test category points, j = test category total points, k = points this test is worth106if(globalData.unequalTests){107// a = globalData.testCatPts / globalData.testCatTotalPts108i = globalData.testCatPts;109j = globalData.testCatTotalPts;110k = globalData.testWorthPts;111}112else{113// a = globalData.testAvg / 100114i = globalData.totalTests * (globalData.testAvg / 100);115j = globalData.totalTests;116k = 1;117}118if(j === 0){119// d = a*s120return 100*d/a}121let s = (k*d + j*d)/w + i*k/j; // output122return 100*(s/k);123}124125function calcTargetGrade(t){126/* t = targetGrade127@requires currentGrade, finalWorth, testPolicy, testWorth, testAvg, totalTests, lowestTest (from globalData)128@returns Array [Weighted, Full] - Dynamic is only returned for testPolicy one and two, and is a multiplier of finalScore129*/130//targetGrade = (finalWorth/100)*result + currentGrade*(1-(finalWorth/100))131132if(globalData.gradeType === 1){133// (cur + req) / (total + worth) = goal134let w = globalData.finalWorthPts, c = globalData.currentGradePts, tp = globalData.currentGradeTotalPts;135let r = ((t/100)*(tp+w) - c);136$('#res-pts').show();137$('#res-val-pts').text(`${r.toFixed(1)}/${w.toFixed(1)} pts`);138return 100*r/w;139}140else if(globalData.examType === 1){141return calcTargetTestGrade(t); // test grades are done a bit differently so that's done separately142}143144let c = globalData.currentGrade, f = globalData.finalWorth, w = globalData.testWorth, a = globalData.testAvg, o = globalData.totalTests, l = globalData.lowestTest, s = globalData.totalTests*globalData.testAvg, res; //s = total test score145if(globalData.testPolicy !== 0){146$('#resi').find('b')[0].innerText = (a).toFixed(2);147$('#resi').find('b')[1].innerText = ((a*(w/100)).toFixed(2));148$('#resi').find('b')[2].innerText = (w).toFixed(2);149}150switch(globalData.testPolicy){151case 0:152//result = (targetGrade - currentGrade*(1-(finalWorth/100))) / (finalWorth/100)153return (t-c*(1-(f/100)))/(f/100);154break;155case 1: //Lowest test dropped156let td = calcTestDrop();157let diff = td[1] - td[0];158$('#resi_a').find('b')[0].innerText = (td[2]).toFixed(2);159$('#resi_a').find('b')[1].innerText = (td[1]).toFixed(2);160$('#resi_c').find('span')[0].innerText = detPlu(diff);161$('#resi_c').find('b')[0].innerText = (diff).toFixed(2);162return ((t - diff)-c*(1-(f/100)))/(f/100);163break;164case 2:165//targetGrade = (finalWorth/100)*result + currentGrade*(1-(finalWorth/100)) + testReplacementDifference166//targetGrade = (finalWorth/100)*result + currentGrade*(1-(finalWorth/100)) + (testWorth/100)*[ [(avgTestGrade * o_totalTests) + (result - lowestTest)]/(o_totalTests) - (avgTestGrade) ]167//targetGrade = (currentGrade * totalTests * (finalWorth - 100) + ((lowestTest * testWorth) + 100*totalTests*targetGrade) / (finalGrade*totalTests + testWorth)168//targetGrade = (F/100) * R + C(1 - (F/100)) + (W/100)*(R-L)/O169//https://www.wolframalpha.com/input/?i=T+%3D+(F%2F100)*R+%2B+C*(1-(F%2F100))+%2B+(W%2F100)*%5B(R-L)%5D%2FO,+solve+for+R170res = (c*(f - 100)*o + l*w + 100*o*t)/(f*o + w);171if(res > calcTargetGradeS(t)){172$('#res_moreInfo').hide();173$('#res-warn-grdAdj').show();174return calcTargetGradeS(t);175}176$('#res_moreInfo').show();177$('#res-warn-grdAdj').hide();178$('#resi_b').find('b')[0].innerText = (res - l).toFixed(2);179$('#resi_b').find('b')[1].innerText = ((res - l) / o).toFixed(2);180$('#resi_c').find('span')[0].innerText = detPlu((w / 100) * (res - l) / o);181$('#resi_c').find('b')[0].innerText = ((w / 100) * (res - l) / o).toFixed(2);182return res;183break;184case 3:185//Same as case 2 but with (0.5 * (result - lowestTest)) instead of (result - lowestTest)186//https://www.wolframalpha.com/input/?i=T+%3D+(F%2F100)*R+%2B+C*(1-(F%2F100))+%2B+(W%2F100)*%5B0.5(R-L)%5D%2FO,+solve+for+R187res = (2*c*(f - 100)*o + l*w + 200*o*t)/(2*f*o + w);188console.log(res); console.log(calcTargetGradeS(t));189if(res > calcTargetGradeS(t)){190$('#res_moreInfo').hide();191$('#res-warn-grdAdj').show();192return calcTargetGradeS(t);193}194$('#res_moreInfo').show();195$('#res-warn-grdAdj').hide();196$('#resi_b').find('b')[0].innerText = (0.5*(res - l)).toFixed(2);197$('#resi_b').find('b')[1].innerText = (0.5 * (res - l) / o).toFixed(2);198$('#resi_c').find('span')[0].innerText = detPlu((w / 100) * 0.5 * (res - l) / o);199$('#resi_c').find('b')[0].innerText = ((w / 100) * 0.5 * (res - l) / o).toFixed(2);200return res;201break;202}203}204205function calcResultingGrade(score) {206// simple resulting grade calculation207// supports weighted and unweighted, but does NOT account for test adjustments208let f = score;209let w = globalData.finalWorth, wp = globalData.finalWorthPts, c = globalData.currentGrade, cp = globalData.currentGradePts, ct = globalData.currentGradeTotalPts;210return globalData.gradeType?(100 * (cp + (f/100*wp)) / (ct + wp)):(f*(w/100)) + (c*(1-(w/100)))211}212213214