diff --git a/css/styles.css b/css/styles.css index 2ecf1619..fe83cfa0 100644 --- a/css/styles.css +++ b/css/styles.css @@ -31,6 +31,7 @@ animation: spin 2s linear infinite; } + @keyframes spin { 0% { transform: rotate(0deg); @@ -73,10 +74,16 @@ .dateTimePlace { margin-left: 5px; + margin-bottom: 0px; font-size: smaller; font-weight: bold; } +#chartcontainer { + max-width: 100%; + height: 250px; +} + #chart { min-width: auto; max-width: 100%; diff --git a/js/config.js b/js/config.js new file mode 100644 index 00000000..a592cccd --- /dev/null +++ b/js/config.js @@ -0,0 +1,2 @@ +const fadetime = 150; +const butdelay = 75; \ No newline at end of file diff --git a/js/content.js b/js/content.js index 670f3fd3..849f97af 100644 --- a/js/content.js +++ b/js/content.js @@ -1,4 +1,3 @@ -var grades; var rmpLink; var next; var bottom; @@ -20,15 +19,7 @@ var semesterCode; var isIndividual = false; var done = true; -const days = new Map([ - ["M", "Monday"], - ["T", "Tuesday"], - ["W", "Wednesday"], - ["TH", "Thursday"], - ["F", "Friday"] -]); -const fadetime = 150; -const butdelay = 75; + //This extension may be super lit, but you know what's even more lit? //Matthew Tran's twitter and insta: @MATTHEWTRANN and @matthew.trann @@ -209,7 +200,7 @@ function loadNextPages() { nextpage.find('tbody>tr').each(function () { let hasCourseHead = $(this).find('td').hasClass("course_header"); if (!(hasCourseHead && $(this).has('th').length == 0)) { - $(this).append(``); + $(this).append(``); // if ($(this).find('td[data-th="Status"]').text().includes('waitlisted')) { // $(this).find('td').each(function () { // $(this).css('background-color', '#E0E0E0'); @@ -456,7 +447,6 @@ function getDistribution(sem) { query += "and sem like '%" + sem + "%'"; } query += "order by a1+a2+a3+b1+b2+b3+c1+c2+c3+d1+d2+d3+f desc"; - alert(query) chrome.runtime.sendMessage({ command: "gradesQuery", query: query @@ -494,35 +484,11 @@ function openDialog(dep, cls, sem, professor, res) { var data; $("#semesters").empty(); if (typeof res == 'undefined' || profname == "") { - data = []; $("#semesters").append("") + data = []; } else { var semesters = res.values[0][18].split(","); - semesters.sort(function (a, b) { - var as = a.split(' ')[0]; - var ay = parseInt(a.split(' ')[1]); - var bs = b.split(' ')[0]; - var by = parseInt(b.split(' ')[1]); - if (ay < by) { - return -1; - } - if (ay > by) { - return 1; - } - var seas = { - "Spring": 0, - "Fall": 1, - "Summer": 2, - "Winter": 3 - } - if (seas[as] < seas[bs]) { - return -1; - } - if (seas[as] > seas[bs]) { - return 1; - } - return 0; - }); + semesters.sort(semesterSort); semesters.reverse().unshift('Aggregate'); var sems = []; for (var i = 0; i < semesters.length; i++) { @@ -575,112 +541,7 @@ function close() { function setChart(data) { //set up the chart - chart = Highcharts.chart('chart', { - chart: { - type: 'column', - backgroundColor: ' #fefefe', - spacingLeft: 10 - }, - title: { - text: null - }, - subtitle: { - text: null - }, - legend: { - enabled: false - }, - xAxis: { - title: { - text: 'Grades' - }, - categories: [ - 'A', - 'A-', - 'B+', - 'B', - 'B-', - 'C+', - 'C', - 'C-', - 'D+', - 'D', - 'D-', - 'F' - ], - crosshair: true - }, - yAxis: { - min: 0, - title: { - text: 'Students' - } - }, - credits: { - enabled: false - }, - lang: { - noData: "The professor hasn't taught this class :(" - }, - tooltip: { - headerFormat: '{point.key}', - pointFormat: '', - footerFormat: '
{point.y:.0f} Students
', - shared: true, - useHTML: true - }, - plotOptions: { - bar: { - pointPadding: 0.2, - borderWidth: 0 - }, - series: { - animation: { - duration: 700 - } - } - }, - series: [{ - name: 'Grades', - data: [{ - y: data[6], - color: '#4CAF50' - }, { - y: data[7], - color: '#8BC34A' - }, { - y: data[8], - color: '#CDDC39' - }, { - y: data[9], - color: '#FFEB3B' - }, { - y: data[10], - color: '#FFC107' - }, { - y: data[11], - color: '#FFA000' - }, { - y: data[12], - color: '#F57C00' - }, { - y: data[13], - color: '#FF5722' - }, { - y: data[14], - color: '#FF5252' - }, { - y: data[15], - color: '#E64A19' - }, { - y: data[16], - color: '#F44336' - }, { - y: data[17], - color: '#D32F2F' - }] - }] - }, function (chart) { // on complete + chart = Highcharts.chart('chart', buildChartConfig(data), function (chart) { // on complete if (data.length == 0) { //if no data, then show the message and hide the series chart.renderer.text('Could not find data for this Instructor teaching this Course.', 100, 120) @@ -746,7 +607,7 @@ function getDescription() { description = output; console.log(response); if (!description) { - description = "

You have been logged out. Please refresh the page and log back in using your UT EID and password.

" + description = "

There was an error. Please refresh the page and/or log back in using your UT EID and password.

" } $("#description").animate({ 'opacity': 0 diff --git a/js/util.js b/js/util.js new file mode 100644 index 00000000..0cae328d --- /dev/null +++ b/js/util.js @@ -0,0 +1,152 @@ +const days = new Map([ + ["M", "Monday"], + ["T", "Tuesday"], + ["W", "Wednesday"], + ["TH", "Thursday"], + ["F", "Friday"] +]); + +const semOrder = { + "Spring": 0, + "Fall": 1, + "Summer": 2, + "Winter": 3 +} + + +function buildQuery(course_data, sem) { + let query = !sem ? "select * from agg" : "select * from grades"; + query += " where dept like '%" + course_data["department"] + "%'"; + query += " and prof like '%" + course_data["prof_name"].replace(/'/g, "") + "%'"; + query += " and course_nbr like '%" + course_data["number"] + "%'"; + if (sem) { + query += "and sem like '%" + sem + "%'"; + } + return query + "order by a1+a2+a3+b1+b2+b3+c1+c2+c3+d1+d2+d3+f desc"; +} + + +function semesterSort(semA, semB) { + let aName = semA.split(' ')[0]; + let aYear = parseInt(semA.split(' ')[1]); + let bName = semB.split(' ')[0]; + let bYear = parseInt(semB.split(' ')[1]); + if (aYear < bYear) + return -1; + if (aYear > bYear) + return 1; + if (semOrder[aName] < semOrder[bName]) + return -1; + if (semOrder[aName] > semOrder[bName]) + return 1; + return 0; +} + +function buildChartConfig(data) { + return { + chart: { + type: 'column', + backgroundColor: ' #fefefe', + spacingLeft: 10 + }, + title: { + text: null + }, + subtitle: { + text: null + }, + legend: { + enabled: false + }, + xAxis: { + title: { + text: 'Grades' + }, + categories: [ + 'A', + 'A-', + 'B+', + 'B', + 'B-', + 'C+', + 'C', + 'C-', + 'D+', + 'D', + 'D-', + 'F' + ], + crosshair: true + }, + yAxis: { + min: 0, + title: { + text: 'Students' + } + }, + credits: { + enabled: false + }, + lang: { + noData: "The professor hasn't taught this class :(" + }, + tooltip: { + headerFormat: '{point.key}', + pointFormat: '', + footerFormat: '
{point.y:.0f} Students
', + shared: true, + useHTML: true + }, + plotOptions: { + bar: { + pointPadding: 0.2, + borderWidth: 0 + }, + series: { + animation: { + duration: 700 + } + } + }, + series: [{ + name: 'Grades', + data: [{ + y: data[6], + color: '#4CAF50' + }, { + y: data[7], + color: '#8BC34A' + }, { + y: data[8], + color: '#CDDC39' + }, { + y: data[9], + color: '#FFEB3B' + }, { + y: data[10], + color: '#FFC107' + }, { + y: data[11], + color: '#FFA000' + }, { + y: data[12], + color: '#F57C00' + }, { + y: data[13], + color: '#FF5722' + }, { + y: data[14], + color: '#FF5252' + }, { + y: data[15], + color: '#E64A19' + }, { + y: data[16], + color: '#F44336' + }, { + y: data[17], + color: '#D32F2F' + }] + }] + } +} \ No newline at end of file diff --git a/js/utplanner.js b/js/utplanner.js index 594d479e..65099566 100644 --- a/js/utplanner.js +++ b/js/utplanner.js @@ -1,9 +1,241 @@ if ($('html').hasClass('gr__utexas_collegescheduler_com')) { $.initialize("table.section-detail-grid", function () { - console.log('hello') $(this).find('thead>tr').append(' Plustr').each(function () { $(this).append(``); }) }); +} + +curr_course = {} +var modhtml = ``; +$("body").prepend(modhtml); + +$("body").on('click', '#distButton', function () { + var row = $(this).closest('tr'); + console.log(row.text()) + $('.modal-content').stop().animate({ + scrollTop: 0 + }, 500); + $(this).blur(); + getCourseInfo(row) +}); + + +function getCourseInfo(row) { + let rowdata = $(row).find('td').slice(3).toArray().map(x => $(x).text().trim()); + let [uniquenum, department, coursenum, coursename, profname, notes, rawtime] = rowdata + let profinit = "" + if (profname !== undefined && profname != "Staff") { + profinit = profname.split(',')[1].trim(); + profname = profname.split(',')[0].trim(); + } + let times = rawtime.split('\n').map(x => x.trim()); + var course_data = { + "unique": uniquenum, + "department": department, + "number": coursenum, + "name": coursename, + "prof_name": profname, + "initial": profinit, + "notes": notes, + "times": times, + } + curr_course = course_data; + getDistribution(course_data); + var modal = document.getElementById('myModal'); + window.onclick = function (event) { + if (event.target == modal) { + close(); + } + } +} + +function badData(course_data, res) { + return typeof res == 'undefined' || course_data["prof_name"] == "Staff"; +} + +$("#semesters").on('change', function () { + var sem = $(this).val(); + sem = sem == "Aggregate" ? undefined : sem; + getDistribution(curr_course, sem); +}); + + +function showLoading(loading) { + if (loading) { + $('#loader').css('display', 'inline-block'); + $("#chart").hide(); + } else { + $('#loader').hide(); + $("#chart").show(); + } + +} + + +function openDialog(course_data, res) { + $("#title").text(buildTitle(course_data)) + $("#topbuttons").before(buildTimeTitle(course_data["times"])); + $("#profname").text(buildProfTitle(course_data)); + $("#myModal").fadeIn(fadetime); + buildSemestersDropdown(course_data, res) + var data; + if (badData(course_data, res)) { + data = []; + } else { + data = res.values[0]; + } + setChart(data); +} + +function buildProfTitle(course_data) { + const { + initial, + prof_name + } = course_data; + return `with ${initial?initial+". ":""}${prof_name}`; +} + +function buildSemestersDropdown(course_data, res) { + $("#semesters").empty(); + if (badData(course_data, res)) { + $("#semesters").append("") + } else { + var semesters = res.values[0][18].split(","); + semesters.sort(semesterSort); + semesters.reverse().unshift('Aggregate'); + var sems = []; + for (var i = 0; i < semesters.length; i++) { + sems.push($(``)); + } + $("#semesters").append(sems); + } +} + + +/*Query the grades database*/ +function getDistribution(course_data, sem) { + showLoading(true); + let query = buildQuery(course_data, sem); + chrome.runtime.sendMessage({ + command: "gradesQuery", + query: query + }, function (response) { + var res = response.data; + if (!sem) { + openDialog(course_data, res); + } else { + var data = badData(course_data, res) ? [] : res.values[0]; + setChart(data); + } + }); +} + + +function buildTitle(course_data) { + return `${course_data["name"]} (${course_data["department"]} ${course_data["number"]})` +} + + +function buildTimeTitle(times) { + $("h2.dateTimePlace").remove(); + var lines = [] + for (var i = 0; i < times.length; i++) { + date = times[i].substring(0, times[i].indexOf(' ')).toUpperCase(); + time = times[i].substring(times[i].indexOf(' ') + 1, times[i].lastIndexOf('-')).trim(); + place = times[i].substring(times[i].lastIndexOf('-') + 1).trim(); + lines.push($(`

${makeLine(date, time, place)}`)); + } + return lines +} + +function close() { + $("#myModal").fadeOut(fadetime); +} + +function makeLine(date, time, place) { + var arr = seperateDays(date) + var output = prettifyDaysText(arr) + var building = place.substring(0, place.search(/\d/) - 1); + building = building == "" ? "Undecided Location" : building; + return `${output} at ${time.replace(/\./g, '').replace(/\-/g, ' to ')} in ${building}`; +} + + +function seperateDays(date) { + let arr = []; + for (var i = 0; i < date.length; i++) { + let letter = date.charAt(i); + if (letter == "T" && i < date.length - 1 && date.charAt(i + 1) == "H") { + arr.push(days.get("TH")); + } else { + if (letter != "H") { + arr.push(days.get(letter)); + } + } + } + return arr; +} + +function prettifyDaysText(arr) { + var output = ""; + if (arr.length > 2) { + for (var i = 0; i < arr.length; i++) { + if (i < arr.length - 1) + output += arr[i] + ", " + if (i == arr.length - 2) + output += "and "; + if (i == arr.length - 1) + output += arr[i]; + } + } else if (arr.length == 2) { + output = arr[0] + " and " + arr[1]; + } else { + output = arr[0]; + } + return output +} + +function setChart(data) { + //set up the chart + showLoading(false); + chart = Highcharts.chart('chart', buildChartConfig(data), function (chart) { // on complete + if (data.length == 0) { + //if no data, then show the message and hide the series + chart.renderer.text('Could not find data for this Instructor teaching this Course.', 100, 120) + .css({ + fontSize: '20px', + width: '300px', + align: 'center', + left: '160px' + }) + .add(); + $.each(chart.series, function (i, ser) { + ser.hide(); + }); + } + }); } \ No newline at end of file diff --git a/manifest.json b/manifest.json index 8cdd775d..72a69f16 100644 --- a/manifest.json +++ b/manifest.json @@ -16,11 +16,13 @@ ], "content_scripts": [{ "css": ["css/styles.css"], - "js": ["js/moment.min.js", "js/highcharts.js", "js/jquery-3.3.1.min.js", "js/jquery.initialize.min.js", "js/content.js"], + "js": ["js/moment.min.js", "js/highcharts.js", "js/jquery-3.3.1.min.js", "js/jquery.initialize.min.js", "js/config.js", "js/util.js", + "js/content.js" + ], "matches": ["https://utdirect.utexas.edu/apps/registrar/course_schedule/*"] }, { "css": ["css/styles.css"], - "js": ["js/moment.min.js", "js/highcharts.js", "js/jquery-3.3.1.min.js", "js/jquery.initialize.min.js", "js/utplanner.js"], + "js": ["js/moment.min.js", "js/highcharts.js", "js/jquery-3.3.1.min.js", "js/jquery.initialize.min.js", "js/config.js", "js/util.js", "js/utplanner.js"], "matches": ["https://utexas.collegescheduler.com/*"] }, { "css": ["css/styles.css"],