Skip to content

Instantly share code, notes, and snippets.

@jrr6
Last active April 15, 2024 00:16
Show Gist options
  • Select an option

  • Save jrr6/687a9bd495b6af46d5e5d699d747f5f6 to your computer and use it in GitHub Desktop.

Select an option

Save jrr6/687a9bd495b6af46d5e5d699d747f5f6 to your computer and use it in GitHub Desktop.
Grader statistics for Gradescope
// ==UserScript==
// @name Gradescope Grader Stats
// @version 0.1
// @description Statistics about Gradescope graders.
// @author jrr6
// @match https://www.gradescope.com/courses/*/questions/*/submissions
// @grant none
// @downloadURL https://gist.github.com/jrr6/687a9bd495b6af46d5e5d699d747f5f6/raw/GradescopeGraderStats.user.js
// @updateURL https://gist.github.com/jrr6/687a9bd495b6af46d5e5d699d747f5f6/raw/GradescopeGraderStats.user.js
// ==/UserScript==
(function() {
function hideStatsModal() {
const modal = document.getElementById('grader-stats-modal')
if (modal) modal.remove()
}
let graderInfo
function reSortTable(nm) {
const wasDesc = document.getElementById(nm).classList.contains('sorting_desc')
const sortVal = wasDesc ? -1 : 1
const tbody = document.querySelector('#grader-stats-table tbody')
tbody.innerHTML = ''
graderInfo.sort((g1, g2) => g1[nm] < g2[nm] ? sortVal : -sortVal)
graderInfo.forEach(({grader, numGraded, avgScore}) => {
const row = document.createElement('tr')
; [grader, numGraded, avgScore.toFixed(2)].forEach(field => {
const cell = document.createElement('td')
cell.textContent = field
row.appendChild(cell)
})
tbody.appendChild(row)
})
if (wasDesc) {
document.getElementById(nm).classList.add('sorting_asc')
document.getElementById(nm).classList.remove('sorting_desc')
} else {
document.getElementById(nm).classList.remove('sorting_asc')
document.getElementById(nm).classList.add('sorting_desc')
}
Array.from(document.querySelectorAll('#grader-stats-table th')).forEach(th => {
if (th.getAttribute('id') !== nm) {
th.classList.remove('sorting_asc')
th.classList.remove('sorting_desc')
}
})
}
const button = document.createElement('button')
button.textContent = 'Show Grader Stats'
; ['tiiBtn', 'tiiBtn-primary'].forEach(cls => button.classList.add(cls))
document.querySelector('.table--header').appendChild(button)
button.addEventListener('click', () => {
const rows = Array.from(document.querySelectorAll('#question_submissions tbody tr'))
const graders = [...new Set(rows.map(r => r.children[2].textContent))]
const modal = document.createElement('div')
modal.setAttribute('id', 'grader-stats-modal')
modal.classList.add('ReactModalPortal')
modal.innerHTML = `
<style>
#grader-stats-table > tbody > tr {
height: 30px;
}
</style>
<div class="ReactModal__Overlay ReactModal__Overlay--after-open themodal-overlay">
<div class="ReactModal__Content ReactModal__Content--after-open modal submissionsManagerStudentsModal" tabindex="-1" role="dialog" aria-label="" aria-modal="true" aria-labelledby="modal-1-heading" aria-describedby="modal-1-subheading" style="display: block;">
<button class="tiiBtn" style="position: absolute;right: 2em;" id="stats-close-button">×</button>
<h1 class="modal--heading" id="modal-1-heading">Grader Statistics</h1>
<div class="modal--body">
<table class="table" id="grader-stats-table">
<thead>
<tr>
<th>Grader</th>
<th class="sorting" id="numGraded"># Graded</th>
<th class="sorting" id="avgScore">Avg Score</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>`
graderInfo = graders.filter(g => g !== '').map(grader => {
const gradesGiven = rows
.filter(e =>
e.children[2].textContent === grader
&& !isNaN(parseFloat(e.children[4].textContent)))
.map(e => parseFloat(e.children[4].textContent))
return {
'grader': grader,
'numGraded': gradesGiven.length,
'avgScore': gradesGiven.reduce((e, a) => e + a, 0) / gradesGiven.length
}
})
modal.querySelector('#numGraded').addEventListener('click', () => reSortTable('numGraded'), false)
modal.querySelector('#avgScore').addEventListener('click', () => reSortTable('avgScore'), false)
modal.querySelector('#stats-close-button').addEventListener('click', () => hideStatsModal(), false)
document.body.appendChild(modal)
reSortTable('numGraded')
}, false)
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment