Skip to content

Instantly share code, notes, and snippets.

@cjlawson02
Last active September 26, 2022 23:42
Show Gist options
  • Save cjlawson02/fc93524caab046b35d99e1e0be3a3b0a to your computer and use it in GitHub Desktop.
Save cjlawson02/fc93524caab046b35d99e1e0be3a3b0a to your computer and use it in GitHub Desktop.
Tampermonkey script for Polyratings

Polyratings Student Center Grease/Tampermonkey Script

Install

Chrome

Firefox

Install the script

  • Click Raw next to the polyratings_sc.user.js file
  • Or go here

Usage

This script will run only on the Enroll page of Student Center and will not work on Schedule Builder.

Visit the Enroll page to see it in action. To turn off colors, go to the script configuration by visiting the Grease/Tampermonkey extension page.

Demo

Get professor ratings in real time!

// ==UserScript==
// @name Polyratings (Student Center)
// @namespace http://github.com/Chris2fourlaw
// @version 0.1.4
// @description List ratings for instructors in Student Center
// @author Chris Lawson
// @match *://cmsweb.pscs.calpoly.edu/*
// @icon https://polyratings.org/favicon.ico
// @downloadURL https://gist.github.com/Chris2fourlaw/fc93524caab046b35d99e1e0be3a3b0a/raw/polyratings_sc.user.js
// @updateURL https://gist.github.com/Chris2fourlaw/fc93524caab046b35d99e1e0be3a3b0a/raw/polyratings_sc.user.js
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @require https://raw.github.com/odyniec/MonkeyConfig/master/monkeyconfig.js
// ==/UserScript==
// Menu Config
const cfg = new MonkeyConfig({
title: "Polyratings (Student Center) Configuration",
menuCommand: true,
params: {
colors: {
type: "checkbox",
default: true,
},
},
});
// DOM observer
const observeDOM = (() => {
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
return (obj, callback) => {
if (!obj || obj.nodeType !== 1) return;
if (MutationObserver) {
// define a new observer
const mutationObserver = new MutationObserver(callback);
// have the observer observe foo for changes in children
mutationObserver.observe(obj, { childList: true, subtree: true });
return mutationObserver;
}
// browser support fallback
if (window.addEventListener) {
obj.addEventListener('DOMNodeInserted', callback, false);
obj.addEventListener('DOMNodeRemoved', callback, false);
}
};
})();
// Get all professors from API
function getAllProfs() {
return fetch('https://api-prod.polyratings.org/professors')
.then((res) => res.json())
.then((data) => data);
}
// Find intersecting
function intersectingDbEntities(arrays) {
if (arrays.length === 1) {
return {
intersect: arrays[0],
nonIntersect: [],
};
}
const idToEntity = arrays.flat().reduce((acc, curr) => {
acc[curr.id] = curr;
return acc;
}, {});
const idArrays = arrays.map((arr) => arr.map((x) => x.id));
let intersectionSet = new Set(idArrays[0]);
idArrays.slice(1).forEach((array) => {
const compareSet = new Set(array);
intersectionSet = new Set([...intersectionSet].filter((x) => compareSet.has(x)));
});
const nonIntersect = arrays.flat().filter((x) => !intersectionSet.has(x.id));
return {
intersect: Array.from(intersectionSet).map((id) => idToEntity[id]),
nonIntersect,
};
}
// Search API response for professor
function searchForProf(profName) {
const tokens = profName.toLowerCase().split(' ');
let tokenMatches;
// If style is F. Last
if (tokens[0][1] === '.') {
tokenMatches = tokens.map((token) =>
profList.filter((prof) =>
`${prof.firstName[0]}. ${prof.lastName}`.toLowerCase().includes(token),
),
);
} else {
tokenMatches = tokens.map((token) =>
profList.filter((prof) =>
`${prof.lastName}, ${prof.firstName}`.toLowerCase().includes(token),
),
);
}
const { intersect } = intersectingDbEntities(tokenMatches);
return intersect[0];
}
// Get professor rating from API
function getProfRating(profName) {
const rating = searchForProf(profName)?.overallRating;
if (rating) {
return rating.toLocaleString('en', { useGrouping: false, minimumFractionDigits: 1 });
}
return "";
}
// Get row color
function getRowColor(rating) {
if (rating > 3.0) {
return '#D9EAD3';
}
if (rating > 2.5) {
return '#FFF2CC';
}
if (rating > 1.75) {
return '#FDE5CD';
}
return '#F4CCCC';
}
// Update the tables
function modifyTables() {
// Schedule table & Shopping cart
$(
'table[id^="STDNT_ENRL_"], table[id^="SSR_REGFORM_"], table[id^="CLASS_MTG_"], table[id^="SSR_CLSRCH_"]',
).each((i, table) => {
let innerTable;
let instructorCol;
innerTable = $(table).find('table')[0];
innerTable = innerTable ? innerTable : table // If there is no inner table, try the outer one
// Create header
const headers = $(innerTable).find('th');
const polyRatingHeader = $(innerTable).find('th[abbr="PolyRating"]');
headers.each((j, th) => {
if (th.abbr === 'Instructor' && polyRatingHeader.length === 0) {
instructorCol = j;
const alignment = th.align ? th.align : 'left';
$(th).after(
`<th scope="col" abbr="PolyRating" width="80" align="${alignment}" class="PSLEVEL1GRIDCOLUMNHDR">
<a tabindex="291" class="PSLEVEL1GRIDCOLUMNHDR" href="https://polyratings.org" target="_blank" rel="noopener noreferrer">
Polyrating
</a>
</th>`,
);
}
});
// Populate ratings data
$(innerTable)
.find('tr')
.each((j, tr) => {
const cells = $(tr).find('td');
const instructorCell = cells[instructorCol];
const polyRatingCol = $(tr).find('td[abbr="PolyRatingCell"]');
if (instructorCell && polyRatingCol.length === 0) {
const prof = searchForProf(instructorCell.innerText);
const ratingText = prof ? `${prof?.overallRating} / 4.0 (${prof?.numEvals})` : '<a href="https://polyratings.org/new-teacher" target="_blank" rel="noopener noreferrer">Add</a>';
$(instructorCell).after(
`<td class="PSLEVEL1GRIDODDROW" abbr="PolyRatingCell">
<a href="https://polyratings.org/professor/${prof?.id}" target="_blank" rel="noopener noreferrer">
${ratingText}
</a>
</td>`,
);
// Add row colors if enabled
if (prof && cfg.get('colors')) {
$(tr)
.find('td')
.each((k, cell) =>
$(cell).css('background-color', getRowColor(prof?.overallRating)),
);
// These attributes change the bg color
$(tr).removeAttr('onclick');
$(tr).removeAttr('onmouseout');
$(tr).removeAttr('onmouseover');
}
}
});
});
}
// Globalize the full professor list
const profList = await getAllProfs();
// Main function
$(document).ready(() => {
modifyTables();
});
document.body.childNodes.forEach((node) => {
observeDOM(node, () => {
modifyTables();
});
});
@mfish33
Copy link

mfish33 commented Aug 5, 2022

This is clean. Nice job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment