diff options
-rwxr-xr-x | analyze_grades.r | 71 | ||||
-rwxr-xr-x | concat_pdfs.sh | 65 |
2 files changed, 136 insertions, 0 deletions
diff --git a/analyze_grades.r b/analyze_grades.r new file mode 100755 index 0000000..6b64099 --- /dev/null +++ b/analyze_grades.r @@ -0,0 +1,71 @@ +#!/usr/bin/env Rscript +# Copyright (C) 2018 Ryan Kavanagh <rkavanagh@cs.cmu.edu> +# +# Processes a CSV file into a format that Canvas will accept. +# Also spits out statistics on the grades and pretty plots. + +library(matrixStats) +library(tidyr) +library(ggplot2) + +args = commandArgs(trailingOnly=TRUE) + +if(length(args)==0) { + stop("supply csv filename as cli arg") +} + +HW <- args[1] + +metadata <- c("Student", "ID","Section", "SIS Login ID") + +hwdata <- read.csv(HW, check.names=FALSE) +grades <- hwdata[, setdiff(names(hwdata), metadata)] +totals <- rowSums(grades) +hwdata["total"] <- totals +grades <- hwdata[, setdiff(names(hwdata), metadata)] + +gradesMatrix = as.matrix(grades) + +stats <- data.frame( "mean" = colMeans(gradesMatrix, na.rm=TRUE) + , "sd" = colSds(gradesMatrix, na.rm=TRUE) + , "median" = colMedians(gradesMatrix, na.rm=TRUE) + , "max" = colMaxs(gradesMatrix, na.rm=TRUE) + , "min" = colMins(gradesMatrix, na.rm=TRUE)) + +print("Statistics") +print(stats) + +print("Raw anonymized grades") +print(gradesMatrix[sample(nrow(gradesMatrix)),]) + +myrange <- function(x) max(x) - min(x) + + # Binwidth +scottbw <- function(x) 3.49 * sd(x) * length(x)^(1/3) +fdrbw <- function(x) 2 * IQR(x) / length(x)^(1/3) + +mybw <- function(x) { + if (myrange(x) <= 5) { + 1 + } else if (myrange(x) <= 10) { + 2 + } else { + myrange(x) / 6 + } +} + +pdf(paste(HW, "_plot.pdf", sep="")) +ggplot(gather(grades), aes(value)) + + geom_histogram(binwidth = function(x) mybw(x)) + + facet_wrap(~key, scales = 'free_x') + + xlim(c(-1,NA)) +dev.off() + +if(length(args) == 2) { + output <- hwdata[metadata] + output["SIS Login ID"] <- NULL + output[args[2]] <- totals + write.csv(output, file = paste(HW, "_totals.csv", sep=""), na="", row.names=FALSE) +} + +warnings() diff --git a/concat_pdfs.sh b/concat_pdfs.sh new file mode 100755 index 0000000..8d84d66 --- /dev/null +++ b/concat_pdfs.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Copyright (C) 2017 Ryan Kavanagh <rkavanagh@cs.cmu.edu> +# Takes all pdf files (at most 4 directories deep) +# and concatenates them in a PDF, such that each +# PDF starts on an odd numbered page (useful for when +# you want to print all student submissions double-sidedly) +# +# Puts the filename in the top right corner of the page. + +TMPDIR=`mktemp -d` +TEXFILE="${TMPDIR}/all.tex" + +cat<<EOF>${TEXFILE} +\documentclass[twoside]{minimal} +\usepackage[letterpaper,top=0.25in,left=0.5in,right=0.5in,bottom=0.5in]{geometry} +\usepackage{pdfpages} +\usepackage{cprotect} +\makeatletter +\def\ps@headings{ +\def\@oddfoot{}\def\@evenfoot{} +\def\@oddhead{}\def\@evenhead{} +} +\makeatother +\includepdfset{pages=-,pagecommand={\pagestyle{headings}}} +\pagenumbering{gobble} +\begin{document} +\cleardoublepage +EOF + +c=0; +shopt -s nullglob # kill the globs that don't match +for pdf in */*.pdf */*/*.pdf */*/*/*.pdf */*/*/*/*.pdf; +do + # We should also check that the file ${pdf} is non-empty + # Here, sometimes students submit PDF files that gs accepts, but which + # pdfpages doesn't like. Having gs rewrite the PDF fixes this. + # We can also convert all of the submitted PDFs to grayscale to save + # colour ink. + mutool clean `pwd`"/${pdf}" `pwd`"/${pdf}" + gs -o "${TMPDIR}/${c}.pdf" \ + -sDEVICE=pdfwrite \ + -dPDFSETTINGS=/prepress \ + -sColorConversionStrategy=Gray \ + -dProcessColorModel=/DeviceGray \ + -dCompatibilityLevel=1.4 \ + `pwd`"/${pdf}" + ESCAPED=`echo "${pdf}" | sed -e 's:&:\\\\&:g;s:\\$:\\\\$:g;s:%:\\\\%:g;s:_:\\\\_:g'` + cat<<EOF>>${TEXFILE} +\makeatletter +\renewcommand{\@oddhead}{\hfill ${ESCAPED}} +\renewcommand{\@evenhead}{\hfill ${ESCAPED}} +\makeatother +\includepdf{${TMPDIR}/${c}.pdf} +\cleardoublepage +EOF + c=`expr "${c}" + 1` +done + +cat<<EOF>>${TEXFILE} +\end{document} +EOF +pdflatex ${TEXFILE} +pdflatex ${TEXFILE} + +rm -fr ${TMPDIR} |