Printing Marmoset Assignments
There are three steps to printing out assignments
- Download the students code and grades csv files from marmoset;
- Unpack the assignments
- Reorganize the assignments to look like an RST hierarchy
- Extract the grades from the csv file
- Run RST
- Print out assignments with printOut
TODO: Harmonize the information dealing with downloading assignments/marks with the information at LocalMarmosetSubmissionMirror
The
RST hierarchy is simple. All of the assignments are put in
~/handin/<assignment #>/<student id>
. Create a directory for the assignment and download all of the zip files to this location. Copy the
marmoset2rst
scripts into
~/handin/<assignment #>
and run
make
. This will unpack all of the zips (e.g.
unzip A1P1.zip
,
unzip A1P2.zip
, ...), properly rename all of the directories (the
rename
script), will remove submissions by course staff (e.g.
rm -rf djshaw*
,
rm -rf f2qiu
), and will automatically generate grade files (the
parseGrades
script).
Now you need to run
RST. Create a directory for marking in
~/marking/<assignment #>/test.<assignment #>/
and copy the
marking
scripts into this folder. Run
rst
with
/u/isg/bin/rst <assignment #> p <assignment #> '*' print
I recommend that you spot check
ps files that are generated by
RST, just to make sure that your marking scheme is correct that the grades are properly calculated.
Print out the assignments with
/u/isg/bin/printOut <assignment #> <assignment #> <timestamp> <# of piles> -g
Organizing Late Submissions
If your course accepts late submissions on Marmoset, when it comes to printing the assignments, it is most likely that you will only print their latest submission. Printing both the late and on-time submissions will make for nearly doubling the work for the TAs as well as wasting a considerable amount of paper. Since the style marks are usually only a small portion of the total grade, marking the latest submission for the written portion is typically okay, but you should double check with the instructors for your course.
The rename script has been modified to handle this procedure, so use the follow rename script if you wish to handle this case.
Rename (for late submissions)
#!/usr/bin/env bash
# first arg is suffix to remove
function renameDirs
{
if [ `ls -d -- *-$1 2> /dev/null | wc -w` -ne 0 ]; then
for i in *$1; do
suffixRemoved=${i%$1}
if [ -d $suffixRemoved ]; then
echo " C Copying files from $i to $suffixRemoved ..."
cp -- $i/* $suffixRemoved/
echo " X Removing directory $i ..."
rm -rf -- $i
else
echo " R Renaming $i to $suffixRemoved ..."
mv -- "$i" "$suffixRemoved" 2> /dev/null
fi
done
fi
}
# important to do it in this order to ensure on-times get overwritten by lates
echo "Renaming On-time Directories ..."
renameDirs "-On-time"
echo "Renaming Late Directories ..."
renameDirs "-Late"
echo "Done renaming."
marmoset2rst scripts
Rename
When you unpack the zips from marmoset, each student has a directory named
<userid>-On-time
. To make these directory look like an
RST hierarchy, we need to remove the
-On-time
. The following script
rename
accomplishes this.
#!/software/.admin/bins/bin/bash
for i in `ls`; do
if [[ ${i:(-8)} == "-On-time" ]]; then
if [ -d $i ]; then
mv $i `basename $i -On-time`
fi
fi
done
Parse grades
Note that this script contains hardcoded references to file names, and so will need to be edited before being used.
#!/software/.admin/bins/bin/python
import csv
import os
def sumRow (row, start, length, student):
ret = 0
for i in range(start, start + length, 1):
try:
ret += int(row[i])
except:
print "sumRow expcetion for student " + student + " at index %i" % i
ret += 0
return ret
def reader (file, numPublic, numRelease, numSecret):
ret = {}
for row in file:
if "cvsAccount" == row[0] or row[0].endswith("-canonical"):
continue
name = row[0]
public = sumRow (row, 3, numPublic, row[0])
release = sumRow (row, 3 + numPublic, numRelease, row[0])
secret = sumRow (row, 3 + numPublic + numRelease, numSecret, row[0])
ret[name] = [public, release, secret]
return ret
def writer (out, key, part, filename, marks, maxpublic, maxrelease, maxsecret, maxbonus=0, human=False, humanSum=0):
if marks.has_key(key):
if human:
out.writelines ("____/%i Part %i: %s\n\n" % ((maxpublic + maxrelease + maxsecret + humanSum), part, filename))
else:
out.writelines ("%i/%i Part %i: %s\n\n" % ((marks[f][0] + marks[f][1] + marks[f][2]), (maxpublic + maxrelease + maxsecret), part, filename))
out.writelines ("\t %i/%i Public tests\n\n" % (marks[f][0], maxpublic))
if maxrelease != 0:
out.writelines ("\t %i/%i Release tests\n\n" % (marks[f][1], maxrelease))
if maxsecret != 0:
out.writelines ("\t %i/%i Secret tests\n\n" % (marks[f][2], maxsecret))
else:
out.writelines ("0/%i Part %i: %s\n\n" % ((maxpublic + maxrelease + maxsecret + humanSum), part, filename))
out.writelines ("\t 0/%i Public tests\n\n" % maxpublic)
if maxrelease != 0:
out.writelines ("\t 0/%i Release tests\n\n" % maxrelease)
if maxsecret != 0:
out.writelines ("\t 0/%i Secret tests\n\n" % maxsecret)
p1 = reader (csv.reader(open('project-A5P1-grades.csv'), dialect='excel'), 1, 2, 0)
p2 = reader (csv.reader(open('project-A5P2-grades.csv'), dialect='excel'), 1, 2, 0)
dirs = os.listdir (".")
for f in dirs:
if not os.path.isdir(f):
continue
if f == ".svn":
continue
print f + "/grades..."
grades = open(f + "/grades", "w")
grades.writelines ("\t\t\t\t=============================================\n")
grades.writelines ("\t\t\t\t Winter 2009 CS 139 Assignment 4\n")
grades.writelines ("\n")
grades.writelines ("\t\t\t\t Marked By:\n")
grades.writelines ("\n")
grades.writelines ("\t\t\t\t TOTAL: /TODO\n")
grades.writelines ("\t\t\t\t=============================================\n")
grades.writelines ("\n")
grades.writelines ("\n")
grades.writelines ("\n")
grades.writelines ("\n")
writer (grades, f, 1, "a5p1.cc", p1, 4, 15, 0, 0, True, 8)
grades.writelines ("\t____/1 pre/post conditions for isEmpty\n\n")
grades.writelines ("\t____/1 pre/post conditions for push\n\n")
grades.writelines ("\t____/1 pre/post conditions for pop\n\n")
grades.writelines ("\t____/2 properly deleting node in pop\n\n")
grades.writelines ("\t____/1 pre/post conditions for top\n\n")
grades.writelines ("\t____/1 pre/post conditions for swap\n\n")
grades.writelines ("\t____/1 pre/post conditions for size\n\n")
writer (grades, f, 2, "a5p2.cc", p2, 3, 14, 0, 0, True, 10)
grades.writelines ("\t____/1 pre/post conditions for isEmptyPQ\n\n")
grades.writelines ("\t____/1 pre/post conditions for enterPQ\n\n")
grades.writelines ("\t____/1 pre/post conditions for firstPQ\n\n")
grades.writelines ("\t____/1 pre/post conditions for leavePQ\n\n")
grades.writelines ("\t____/2 properly deleting node in leavePQ\n\n")
grades.writelines ("\t____/2 properly deleting priority in leavePQ\n\n")
grades.writelines ("\t____/1 pre/post conditions for sizePQ\n\n")
grades.writelines ("\t____/1 pre/post conditions for sizeByPriority\n\n")
grades.writelines ("\t____/1 pre/post conditions for numPriorities\n\n")
grades.close ()
Makefile
ZIP_FILES=$(wildcard p*.zip)
REMOVE_USERS=pbeshai glu ctkierst jmatlee omnafees
.PHONY: all unzip renameDirs parseGrades
all:
@$(MAKE) unzip
@$(MAKE) renameDirs
@$(MAKE) parseGrades
parseGrades:
@./parseGrades
renameDirs:
@./rename
unzip:
@for zipFile in $(ZIP_FILES); do \\
unzip -o $$zipFile; \\
/bin/bash -c "rm -rf *-canonical*/ *-student*/ $(foreach user,$(REMOVE_USERS),$(user)*/)"; \\
done
clean:
-/bin/bash -c "rm -rf */"
remove:
@rm *.zip rename Makefile parseGrades
The REMOVE_USERS variable specifies the tutor's and instructor's userids to ensure their files do not get printed. (Note that commands must begin with a tab character, not several spaces)
computeMarks
computeMarks is often a very simple, short script.
For a scheme assignment:
#!/bin/bash
FULLPROG=`type $0 | awk '{print $3}'` # find the program's full path
PROGDIR=`dirname ${FULLPROG}` # extract the directory
PATH=`/bin/showpath gnu standard current`
retainFile () {
if [ -s "$submitdir/$1" ]; then # only keep the file if it exists
"$PROGDIR/plt370mangle" "$submitdir/$1" > "$submitdir/$1.mod" # remove the compiler directive from the student's code and put it in $1.mod
keepFile "$submitdir/$1.mod" 512 -f -h "$1" # tell rst to keep the first 512 lines of $1.mod
fi
}
cat "$submitdir/grades" > "$marksheet"
retainFile a1-princess.ss
retainFile a1-grades.ss
retainFile a1-temperature.ss
The plt370mangle script referenced above is borrowed from
BitterSuite, and can be found in the appropriate subdirectory of /u/isg if it cannot be found on the course account.
For a c++ assignment (same as the scheme computeMarks, without the call to plt370mangle):
#!/bin/bash
FULLPROG=`type $0 | awk '{print $3}'` # find the program's full path
PROGDIR=`dirname ${FULLPROG}` # extract the directory
PATH=`/bin/showpath gnu standard current`
retainFile () {
if [ -s "$submitdir/$1" ]; then
keepFile "$submitdir/$1" 512 -f -h "$1" # tell rst to keep the first 512 lines
fi
}
cat "$submitdir/grades" > "$marksheet"
retainFile a5p1.cc
retainFile a5p2.cc
CS 246 C++ computeMarks Script (supports any number of submitted files)
In Spring 2009, the computeMarks script had to become a bit more complicated than the one specified above, and is shown below. The
REQUIRED_FILES
do not count towards the file limit specified by
FILE_LIMIT
, and binary files are (hopefully) ignored.
#!/bin/bash
PATH=`/bin/showpath gnu standard current`
FILE_LIMIT=10
FILE_LINE_LIMIT=1000
REQUIRED_FILES="Card.cc Card.h Common.cc Common.h Parser.h Deck.h Deck.cc Makefile Name.h Name.cc Player.h Player.cc Hand.h Hand.cc Driver.cc"
# use the grades file if you ran parseGrades to include Marmoset testing results
cat "$submitdir/grades" > "$marksheet"
#cat "$testdir/mark-scheme" > "$marksheet"
ls $submitdir
fileCount=0
for file in $submitdir/*; do
base=`basename $file`
# ensure that binary files are not printed
filetype=`file $file`
if [[ "$filetype" == *executable* ]]; then
echo >> "$marksheet"
echo "### Warning: file $base is an executable and was not printed ###" >> "$marksheet"
continue
fi
if [ "${file##*.}" == "o" -o "${file##*.}" == "zip" ]; then
echo >> "$marksheet"
echo "### Warning: file $base is an executable and was not printed ###" >> "$marksheet"
continue
fi
if [[ "$base" == *~ ]]; then
echo >> "$marksheet"
echo "### Warning: file $base is assumed to be a backup and was not printed ###" >> "$marksheet"
fi
# if this isn't a required file, increase file count
if [[ "$REQUIRED_FILES" != *$base* ]]; then
let fileCount=fileCount+1
fi
# if we have reached the file limit, stop printing
if [ "$fileCount" -gt "$FILE_LIMIT" ]; then
echo >> "$marksheet"
echo "### Warning: student submitted more than $FILE_LIMIT non-required files. Only $FILE_LIMIT files shown. ###" >> "$marksheet"
break
fi
# print the file if it the marksheet
if [ "$base" != "grades" ]; then
printFile $base $FILE_LINE_LIMIT -f
fi
done