Printing Marmoset Assignments

There are three steps to printing out assignments

  1. Download the students code and grades csv files from marmoset;
  2. Unpack the assignments
  3. Reorganize the assignments to look like an RST hierarchy
  4. Extract the grades from the csv file
  5. Run RST
  6. 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, unzip, ...), 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

            if [ -d $suffixRemoved ]; then
                echo " C   Copying files from $i to $suffixRemoved ..."
                cp -- $i/* $suffixRemoved/

                echo " X   Removing directory $i ..."
                rm -rf -- $i 
                echo " R   Renaming $i to $suffixRemoved ..."
                mv -- "$i" "$suffixRemoved" 2> /dev/null

# 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


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.


for i in `ls`; do
    if [[ ${i:(-8)} == "-On-time" ]]; then
        if [ -d $i ]; then
            mv $i `basename $i -On-time`

Parse grades

Note that this script contains hardcoded references to file names, and so will need to be edited before being used.


import csv
import os

def sumRow (row, start, length, student):
        ret = 0

        for i in range(start, start + length, 1):
                        ret += int(row[i])
                        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"):

                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))
                        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))
                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):
        if f == ".svn":
        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, "", 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, "", 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 ()


ZIP_FILES=$(wildcard p*.zip)
REMOVE_USERS=pbeshai glu ctkierst jmatlee omnafees

.PHONY: all unzip renameDirs parseGrades
        @$(MAKE) unzip
        @$(MAKE) renameDirs
        @$(MAKE) parseGrades



        @for zipFile in $(ZIP_FILES); do \\

        unzip -o $$zipFile; \\

        /bin/bash -c "rm -rf *-canonical*/ *-student*/ $(foreach user,$(REMOVE_USERS),$(user)*/)"; \\


        -/bin/bash -c "rm -rf */"
        @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 is often a very simple, short script.

For a scheme assignment:


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

cat "$submitdir/grades" > "$marksheet"


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):


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

cat "$submitdir/grades" > "$marksheet"


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.

PATH=`/bin/showpath gnu standard current`
REQUIRED_FILES=" Card.h Common.h Parser.h Deck.h Makefile Name.h Player.h Hand.h"

# use the grades file if you ran parseGrades to include Marmoset testing results
cat "$submitdir/grades" > "$marksheet"
#cat "$testdir/mark-scheme" > "$marksheet"

ls $submitdir
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"
    if [ "${file##*.}" == "o" -o "${file##*.}" == "zip" ]; then
        echo >> "$marksheet" 
        echo "### Warning: file $base is an executable and was not printed ###" >> "$marksheet"
    if [[ "$base" == *~ ]]; then
        echo >> "$marksheet"
        echo "### Warning: file $base is assumed to be a backup and was not printed ###" >> "$marksheet"

    # if this isn't a required file, increase file count
    if [[ "$REQUIRED_FILES" != *$base* ]]; then
         let fileCount=fileCount+1

    # 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"

    # print the file if it the marksheet 
    if [ "$base" != "grades" ]; then
        printFile $base $FILE_LINE_LIMIT  -f
Topic revision: r5 - 2010-03-08 - TerryVaskor
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback