commit 62889999fe147d7f8f9ff6572c1ec1fd96f0b357 Author: James Eagan Date: Wed Jan 4 18:00:00 2023 +0100 Initial import diff --git a/process-gradescope-grades.py b/process-gradescope-grades.py new file mode 100755 index 0000000..5f3ebe6 --- /dev/null +++ b/process-gradescope-grades.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Script to ... +# +# Minimally tested. Seems to work. Use at your own risk. +# +# By James Eagan +# https://james.eagan.fr + +import pandas as pd +import re + +skipCols = ['SID', 'Email', 'Submission ID', 'Submission Time', + 'Lateness (H:M:S)', 'View Count', 'Submission Count', + ] + +def readCSV(fileName): + cols = list(pd.read_csv(fileName, nrows=1)) + keepCols = [col for col in cols if col not in skipCols] + + df = pd.read_csv(fileName, usecols=keepCols) + df = df.sort_values(by='Nom') + return df + +pattern = re.compile(r'(\d+\.?\d*): (.+) \((\d+\.\d*) pts\)') +def massageHeaders(df, args): + multiHeaders = [[], [], []] + + for header in df.columns: + match = pattern.match(header) + if match: + # Question in the form: '11.4: types d'attributs (variables) (1.0 pts)' + multiHeaders[0].append('Q' + match.group(1)) # Q1, Q2, Q3.1, Q3.2, etc + multiHeaders[1].append(match.group(2)) # Question label + multiHeaders[2].append(float(match.group(3))) # Question points (e.g. 1, 2, 1.5) + else: + multiHeaders[0].append('') + multiHeaders[1].append('') + multiHeaders[2].append(header) + + df.columns = pd.MultiIndex.from_arrays(multiHeaders) + return df + +def writeExcel(df, fileName): + with pd.ExcelWriter(fileName) as writer: + df.to_excel(writer) +# with open(basename + "-out.csv", 'w') as writer: +# writer.write(df.to_csv()) + +if __name__ == '__main__': + import argparse + import sys + + def parse_args(): + parser = argparse.ArgumentParser(description="Massage Gradescope data") + parser.add_argument("csv", help="csv file as exported from Gradescope") + parser.add_argument("-o", "--out", help="file to write output") + group = parser.add_mutually_exclusive_group() + group.add_argument("-,", "--commas", help="use commas for decimal separator", action="store_true") + group.add_argument("-.", "--dots", help="use dots for decimal separator", action="store_true") + group.add_argument("-d", "--decimal-separator", help="decimal separator for real numbers", default=",") + parser.add_argument("-s", "--sep", help="csv column separator (default: ',' when decimal separator is '.' and ';' for ',')") + + args = parser.parse_args() + if args.commas: + args.decimal_separator = "," + elif args.dots: + args.decimal_separator = "." + + if not args.sep: + args.sep = ';' if args.decimal_separator == ',' else ',' + + return args + + def writeOutput(df, args): + if args.out: + writeExcel(df, args.out) + else : + df.to_csv(sys.stdout, sep=args.sep, decimal=args.decimal_separator) + + def nowDoIt(): + args = parse_args() + df = readCSV(args.csv) + massageHeaders(df, args) + writeOutput(df, args) + + nowDoIt() + \ No newline at end of file