Skip to content

Instantly share code, notes, and snippets.

@atilberk
Last active February 8, 2024 21:59
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save atilberk/d5eb06bb6b80ac9516ba to your computer and use it in GitHub Desktop.
Save atilberk/d5eb06bb6b80ac9516ba to your computer and use it in GitHub Desktop.
Simple JS GPA Calculator for Koç University Students.
<html>
<head>
<meta charset="UTF-8">
<title>KUSIS GPA Calculator</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript">
var cues = {
"Course Description Term Grade Units Status Incl GPA":'Chrome',
"Course Description Term Grade Units Status Incl GPA":'Firefox',
"CourseDescriptionTermGradeUnitsStatusInclGPA":'Nospace'
};
var lookup = {
'A+':4.3,
'A':4.,
'A-':3.7,
'B+':3.3,
'B':3.,
'B-':2.7,
'C+':2.3,
'C':2.,
'C-':1.7,
'D+':1.3,
'D':1.,
'F':0.,
'S':0.,
'U':0.,
'W':0.,
'I':0.,
'AU':0.,
'T':0.,
'AP':0.,
'NA':0.,
'':0.
}
var statuses = {
"Taken":"",
"InProgress":"",
"Transferred":""
};
var result = {'gpa':0., 'inConsideration': {}, 'outConsideration': {}};
function isValidInput(candidateInput) {
var candidates = candidateInput.split('\n');
var lines = [];
for (var i=0;i<candidates.length;i++) {
//if (candidates[i].replace(/\t/g,'').length > 0) {
if (candidates[i].replace(/\t/g,'').length > 0 && isLetterOrDigitOrSpace(candidates[i][0])) {
lines.push(candidates[i]);
}
}
for (var i=0;i<lines.length;i++) {
if (lines[i].replace(/ |\t/g,'') in cues) {
return (i+7 < lines.length && lines[i+4] in lookup && /^[0-9]+.00$/.test(lines[i+5]));
}
}
return false;
}
function preprocessInput(rawInput) {
var candidates = rawInput.split('\n');
var lines = [];
for (var i=0;i<candidates.length;i++) {
if (!(candidates[i].replace(/ |\t/g,'') in statuses) && candidates[i].replace(/\t/g,'').length > 0 && isLetterOrDigitOrSpace(candidates[i][0])) {
lines.push(candidates[i]);
}
}
var i;
for (i=0;i<lines.length;i++) {
if (lines[i].replace(/ |\t/g,'') in cues) {
i++;
break;
}
}
var input = {'inConsideration':{},'outConsideration':{}};
var key = 0
while (i < lines.length && lines[i+3] !== undefined && lines[i+3].trim() in lookup && /^[0-9]+.00$/.test(lines[i+4])) {
var semester = lines[i+2].split(' ')[0].substring(0,2).toUpperCase() + "'" + lines[i+2].split(' ')[1].substring(2);
if ((lines[i].split(' ')[0] != 'ELC') && // If the course code does not start with ELC
( !(lines[i] in courseNameSet(input.inConsideration)) || (lookup[lines[i+3].trim()] > getObjFromCourse(input.inConsideration,lines[i])['grade']) ) // couse is not taken yet, if taken before, check if the grade is better
) {
if (lines[i] in courseNameSet(input.inConsideration)) {
ckey = getKey(input.inConsideration, lines[i]);
input.outConsideration[ckey] = Object.create(input.inConsideration[ckey]);
delete input.inConsideration[ckey];
}
input.inConsideration[key+""] = {'course': lines[i], 'letter':lines[i+3],'grade':lookup[lines[i+3].trim()],'credits':parseFloat(lines[i+4]), 'semester':semester};
key++;
} else {
input.outConsideration[key+""] = {'course': lines[i], 'letter':lines[i+3],'grade':lookup[lines[i+3].trim()],'credits':parseFloat(lines[i+4]), 'semester':semester};
key++;
}
i+=6;
}
return input;
}
function calculate(input) {
var sumGrades = 0.;
var sumCredits = 0.;
var onCalc = {};
var offCalc = input.outConsideration;
for (var key in input.inConsideration) {
if (input.inConsideration[key]['letter'] == 'F' || input.inConsideration[key]['grade'] != 0.) {
sumGrades += input.inConsideration[key]['grade'] * input.inConsideration[key]['credits'];
sumCredits += input.inConsideration[key]['credits'];
onCalc[key] = input.inConsideration[key];
} else {
offCalc[key] = input.inConsideration[key];
}
}
return {'gpa':sumGrades / sumCredits, 'inConsideration':onCalc, 'outConsideration': offCalc};
}
function courseNameSet(courseObjSet) {
var set = {};
for (var key in courseObjSet) {
set[(courseObjSet[key].course)] = 0;
}
return set;
}
function getKey(set,courseName) {
for (var key in set) {
if (set[key].course == courseName) {
return key;
}
}
return "nokey";
}
// returns the course with courseName, the one with the highest grade if multiple
function getObjFromCourse(set,courseName) {
grade = -1.;
element = null;
for (var key in set) {
e = set[key];
if (e.course == courseName) {
g = e.grade;
if (grade < g) {
grade = g;
element = e;
}
}
}
return e;
}
function isLetterOrDigitOrSpace(str) {
return str.length === 1 && str.match(/[\sa-z0-9]/i) != null;
}
$(document).ready(function() {
$("#gpa-info").bind("paste", function() {
setTimeout(function() {
var inputText = $("#gpa-info").val();
if(isValidInput(inputText)) {
var input = preprocessInput(inputText);
result = calculate(input);
$("#result").html(
"<span class='success'>Your GPA is "
+ "<span class='gpa'>"+result.gpa.toFixed(2)+"</span></span>"
);
var receipt = "<table id='both'><tr><td><h2>Courses included:</h2></td><td><h2>Courses excluded:</h2></td></tr>"
+"<tr><td><table class='receipt-table' id='included'><tr><th></th><th>Course</th><th>Semester</th><th>Letter</th></tr>";
for(var key in result.inConsideration) {
receipt += "<tr data-key='" + key + "'><td><span class='addrmbtn rmbtn'>-</span></td><td>"+result.inConsideration[key]['course']+"</td><td>"+result.inConsideration[key]['semester']+"</td><td>"+result.inConsideration[key]['letter']+"</td></tr>";
}
receipt += "</table></td>"
+"<td><table class='receipt-table' id='excluded'><tr><th></th><th>Course</th><th>Semester</th><th>Letter</th></tr>"
for(var key in result.outConsideration) {
receipt += "<tr data-key='" + key + "'><td>"
+ ((result.outConsideration[key]['grade'] > 0. || result.outConsideration[key]['letter'] == 'F' ) ? "<span class='addrmbtn addbtn'>+</span>" : "")
+ "</td><td>"+result.outConsideration[key]['course']+"</td><td>"+result.outConsideration[key]['semester']+"</td><td>"+result.outConsideration[key]['letter']+"</td></tr>";
}
receipt += "</table></td></tr></table>"
var adder = "<div id='manual-add-div'>"
+ "<p><span class='new'>NEW</span>Add a fictional course:</p>"
+ "<input id='add-coursecode' type='text' maxlength='8' width='7' placeholder='An alias...'/>"
+ "<select id='add-credits'>";
for (var i of [1,3,4]) {
adder += "<option"+(i==3 ? " selected": "")+">"+i+"</option>";
}
adder += "</select><select id='add-grade'>";
for (var key in lookup) {
adder += "<option>"+key+"</option>";
if (key == "F") break;
}
adder += "</select><button class='add-new-button'>ADD</button></div>";
receipt += adder;
$('#receipt').html(receipt);
$('#receipt-container').show();
$(document).on("click",".rmbtn",function() {
var e = $(this).parents("tr").first();
var key = e.data("key");
result.outConsideration[key] = Object.create(result.inConsideration[key]);
delete result.inConsideration[key];
result = calculate(result);
e.find(".addrmbtn").removeClass("rmbtn").addClass("addbtn").html("+");
e.remove();
$("#excluded").append(e.prop("outerHTML"));
$(".gpa").html(result.gpa.toFixed(2));
});
$(document).on("click",".addbtn", function() {
var e = $(this).parents("tr").first();
var key = e.data("key");
result.inConsideration[key] = Object.create(result.outConsideration[key]);
delete result.outConsideration[key];
result = calculate(result);
e.find(".addrmbtn").removeClass("addbtn").addClass("rmbtn").html("-");
e.remove();
$("#included").append(e.prop("outerHTML"));
$(".gpa").html(result.gpa.toFixed(2));
});
$(document).on("click",".add-new-button", function() {
//var e = $(this).parents("tr").first();
var key = Object.keys(result.inConsideration).length + Object.keys(result.outConsideration).length;
result.inConsideration[key] = Object.create({'course': $('#add-coursecode').val(), 'letter':$('#add-grade option:selected').val(),'grade':lookup[$('#add-grade option:selected').val()],'credits':parseFloat($('#add-credits option:selected').val()), 'semester':'----'});
//delete result.outConsideration[key];
result = calculate(result);
//e.find(".addrmbtn").removeClass("addbtn").addClass("rmbtn").html("-");
//e.remove();
newrow = "<tr data-key='" + key + "'><td><span class='addrmbtn rmbtn'>-</span></td><td>"+result.inConsideration[key]['course']+"</td><td>"+result.inConsideration[key]['semester']+"</td><td>"+result.inConsideration[key]['letter']+"</td></tr>";
$("#included").append(newrow);
$(".gpa").html(result.gpa.toFixed(2));
});
} else {
$("#result").html(
"<span class='fail'>Oops. Something is not right."
+ "<br> Either you pasted rubbish or it's me."
+ "<br> If you think it's me, then please contact my coder.</span>"
);
}
$("#gpa-info").val("PASTED!");
setTimeout(function() {
$("#gpa-info").trigger("blur");
}, 1000);
}, 0);
});
$("#gpa-info").bind("blur", function() {
$("#gpa-info").val("");
});
$("#receipt-toggler").click(function() {
$("#receipt-toggler").html(($("#receipt").css('display') == 'none' ? "Hide" : "Show") + " receipt");
$("#receipt").animate({'height':'toggle'},800);
});
});
</script>
<style>
body
{
font-family: Helvetica;
font-size: 14pt;
}
a
{
color: inherit;
text-decoration: none;
}
#calc-wrapper
{
width: 44%;
margin: auto;
color: #2c3e50;
}
#calc-container
{
background-color: #cdd3d7;
border-radius: 10px;
padding: 10px 20px 20px 20px;
}
h1
{
text-align: center;
}
ol
{
width: 80%;
padding:0;
margin: auto;
}
ol>li
{
width: 100%;
margin: 10px 0;
}
.howto
{
font-size: 14pt;
text-decoration: underline;
}
.kusis
{
background-color: #afd0f1;
color: #416291;
font-size: 10pt;
padding: 0 3px 0 3px;
border-radius: 2px;
}
.key
{
background-color: #eee;
color: #333;
border:1px solid gray;
font-size:8pt;
box-shadow:1px 0 1px 0 #eee, 0 2px 0 2px #ccc, 0 2px 0 3px #444;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px;
margin:2px 3px;
padding:1px 5px;
}
#gpa-info
{
width: 100%;
min-width: 100%;
max-width: 100%;
height: 40px;
min-height: 40px;
max-height: 40px;
border: 3px solid #2c3e50;
font-size: 20pt;
margin: 30px 0 10px 0;
}
#gpa-info[placeholder] {
color: #2c3e50;
}
#result
{
width: 80%;
text-align: center;
vertical-align: center;
font-weight: bold;
background-color: #fff;
border-radius: 15px;
margin: 10px auto;
}
#result .success
{
font-size: 24pt;
}
#result .gpa
{
font-size: 36pt;
}
#result .fail
{
font-size: 14pt;
}
#receipt-container
{
display: none;
margin: auto;
width: 80%;
}
#receipt-toggler
{
width: 100%;
text-align: center;
border-radius: 10px;
cursor: pointer;
font-size: 14pt;
margin: auto;
}
#receipt-toggler:hover
{
background-color: #eee;
}
#receipt
{
display: none;
color: inherit;
}
#both
{
width: 100%;
}
#both td, th
{
font-size: 8pt;
width: 33%;
vertical-align: top;
text-align: center;
}
.receipt-table
{
text-align: center;
width: 100%;
border-radius: 10px;
background-color: #eee;
color: inherit;
}
#receipt h2
{
margin: 0;
}
.receipt-table th
{
font-size: 12pt;
}
.receipt-table .addrmbtn
{
display: inline-block;
cursor: pointer;
background-color: rgba(200,200,200,0.7);
font-weight: bold;
min-width: 10px;
border-radius: 10px;
}
#calc-footer
{
opacity: 0.6;
font-size: 10pt;
text-align: center;
margin-top: 10px;
font-style: italic;
}
#calc-footer p
{
margin: 5px 0;
}
#calc-footer a
{
color: #2c3e50;
text-decoration: none;
}
#calc-footer a:hover
{
text-decoration: underline;
}
.new
{
font-size: 7pt;
color: #eee;
display: inline-block;
padding: 2px;
margin-right: 5px;
position: relative;
bottom: 5px;
font-weight: bold;
color: white;
background-color: rgba(46, 204, 113,1.0);
border-radius: 5px;
}
#manual-add-div
{
text-align: center;
}
#manual-add-div p
{
margin-bottom: 5px;
}
#add-coursecode
{
width: 8em;
}
@media all and (max-width: 768px) {
#calc-wrapper
{
width: 96%;
}
}
</style>
</head>
<body>
<div id="calc-wrapper">
<h1>KUSIS GPA Calculator</h1>
<div id="calc-container">
<ol>
<span class="howto">How to use:</span>
<li>Login to <a href="https://kusis.ku.edu.tr" target="blank">KUSIS</a> in <u>English</u> and navigate to <br><span class="kusis">Self Service > Academic Records > My Course History</span></li>
<li>Copy the page content by <span class="key">CTRL</span>+<span class="key">A</span> then <span class="key">CTRL</span>+<span class="key">C</span></span></li>
<li>
Paste it with <span class="key">CTRL</span>+<span class="key">V</span> into the field below
<textarea name="gpa-info" id="gpa-info" cols="30" rows="1" placeholder="PASTE HERE!"></textarea>
</li>
<li>
See the receipt and exclude courses!
</li>
<li>
<span class='new'>NEW</span>Add fictional courses to your transcript!
</li>
</ol>
<div id="result"></div>
<div id="receipt-container">
<div id="receipt-toggler">Show receipt</div>
<div id="receipt"></div>
</div>
</div>
<div id="calc-footer">
<p>Primarily tested on <a href="https://www.mozilla.org/en-US/firefox/new/">Mozilla Firefox</a></p>
<p>Source is also available at <a href="https://gist.github.com/atilberk/d5eb06bb6b80ac9516ba">GitHub</a></p>
</div>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment