Skip to content

Instantly share code, notes, and snippets.

@zlhaa23
Last active February 28, 2018 14:07
Show Gist options
  • Save zlhaa23/0b4522f112b46ff638ead84f305b615d to your computer and use it in GitHub Desktop.
Save zlhaa23/0b4522f112b46ff638ead84f305b615d to your computer and use it in GitHub Desktop.
Canvas grading
"""
An interactive CLI script for entering grades on Canvas (an LMS).
Python 3.6+
Usage:
1. Export the CSV file with Canvas
2. Modify the file with this script
3. Import the modified file to Canvas
Tip: use rlwrap.
Author: Linghan Zhang
Date: Feb 2018
"""
import sys
assert sys.version_info >= (3, 0)
import csv
import io
import bisect
class Grader(object):
WELCOME = '##### Canvas Grader #####'
TOP_MENU = '\n'.join([
'(TOP)',
'exit',
'help',
'load <fname>: load data from CSV file',
'save <fname>: save data to CSV file',
'print: print data',
'cols: list columns',
'list [<name>...]: filter and list students and their grades',
'col [<num>]: get or set the current column',
'grade: enter GRADE mode to set grades one by one',
'selmake: enter SEL mode to make a selection of students',
'sel: print the current selection',
'selclear: clear the selection',
'selset <grade>: set grade for all selected students'
])
GRADE_MENU = '\n'.join([
'(GRADE)',
'exit',
'<name>... <grade>: set grade for the given student'
])
SEL_MENU = '\n'.join([
'(SEL)',
'exit',
'<name>...: select/deselect the given student'
])
def __init__(self):
self.data = None # list of rows
self.col = 0 # current col index
self.sel = [] # a selection of students as sorted row indices
print(self.WELCOME)
print(self.TOP_MENU)
self.top()
def top(self):
"""Top menu"""
PROMPT = '>> '
words = input(PROMPT).split()
it = iter(words)
try:
s = next(it)
if s == 'exit':
print('Bye')
return
elif s in ['help', 'h']:
print(self.TOP_MENU)
elif s == 'load':
fname = next(it)
try:
with open(fname, newline='') as f:
self.data = list(csv.reader(f))
print('{} rows loaded.'.format(len(self.data)))
except Exception as e:
print(e)
elif self.data:
if s == 'save':
fname = next(it)
try:
with open(fname, 'w', newline='') as f:
csv.writer(f).writerows(self.data)
print('Data saved to file.')
except Exception as e:
print(e)
elif s == 'print':
with io.StringIO() as f:
csv.writer(f).writerows(self.data)
print(f.getvalue())
elif s == 'cols':
for j in range(len(self.data[0])):
self.print_col(j)
elif s in ['list', 'ls']:
for i in self.find(list(it)):
self.print_row(i)
elif s == 'col':
try:
j = int(next(it)) - 1
# Set col
if 0 <= j < len(self.data[0]):
self.col = j
self.print_col(j)
else:
raise ValueError()
except StopIteration:
# Get col
self.print_col(self.col)
except ValueError:
print('Wrong col number!')
elif s == 'grade':
print(self.GRADE_MENU)
self.grade()
elif s == 'selmake':
print(self.SEL_MENU)
self.selmake()
elif s == 'sel':
print('{} students selected.'.format(len(self.sel)))
for i in self.sel:
self.print_row(i)
elif s == 'selclear':
self.sel.clear()
print('Selection cleared.')
elif s == 'selset':
v = next(it)
for i in self.sel:
self.data[i][self.col] = v
print('{} scores set to {}.'.format(len(self.sel), v))
else:
print('Unrecognized command "{}"'.format(s))
else:
print('No data!')
except StopIteration:
if words:
print('Argument(s) missing!')
self.top()
def print_col(self, j):
print('[{:>2}] "{}"'.format(j + 1, self.data[0][j]))
def print_row(self, i):
print('[{:>3}] {:<30} {}'.format
(i + 1, self.data[i][0], self.data[i][self.col]))
def find(self, names):
"""Find students with words."""
names = [n.lower() for n in names]
def match(i):
n0 = self.data[i][0].lower()
return all(n in n0 for n in names)
return list(filter(match, range(len(self.data))))
def choose_student(self, names):
"""
Let the user choose one from the filtered students.
Return: row index (-1 if fail)
"""
FAIL = -1
i_list = self.find(names)
c = len(i_list)
if not c:
print('Name not found!')
return FAIL
if c == 1:
return i_list[0]
for i in i_list:
self.print_row(i)
try:
i = int(input('Choose one: ')) - 1
if i not in i_list:
raise ValueError()
return i
except ValueError:
print('Wrong number!')
return FAIL
def selmake(self):
"""SEL mode"""
PROMPT = 'SEL>> '
cmd = input(PROMPT)
if cmd == 'exit':
return
names = cmd.split()
if names:
i = self.choose_student(names)
if i >= 0:
if i in self.sel:
self.sel.remove(i)
print('Deselected:')
else:
bisect.insort(self.sel, i)
print('Selected:')
self.print_row(i)
self.selmake()
def grade(self):
"""GRADE mode"""
PROMPT = 'GRADE>> '
cmd = input(PROMPT)
if cmd == 'exit':
return
names = cmd.split()
try:
v = names.pop()
if not names:
raise ValueError()
except IndexError:
pass
except ValueError:
print('Wrong syntax!')
else:
i = self.choose_student(names)
if i >= 0:
self.data[i][self.col] = v
self.print_row(i)
self.grade()
def main():
Grader()
return 0
if __name__ == '__main__':
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment