compute-grades 4.17 KB
Newer Older
1 2 3 4 5 6 7 8 9
#!/usr/bin/perl -w

use strict;
use List::Util qw(reduce);

my %assignments = (
    homework      => { hw1 => 1, hw2 => 1, hw3 => 1, hw4 => 1, hw5 => 1, hw6 => 1},
    participation => { participation => 1 },
    papers        => { papers1 => 1, papers2 => 1, papers3 => 1, papers4 => 1},
10
    labs          => { lab1 => 1, lab1prep => 1, lab2 => 1, lab3 => 2 }
11 12 13 14 15 16 17 18 19
);

my %weights = (
    participation => 0.125,
    homework      => 0.250,
    papers        => 0.3125,
    labs          => 0.3125
);

20 21 22 23 24 25 26
my @lettergrades = (
                  ['A',  0.95], ['A-', 0.90],
    ['B+', 0.85], ['B',  0.80], ['B-', 0.75],
    ['C+', 0.70], ['C',  0.65], ['C-', 0.60],
    ['D+', 0.55], ['D',  0.50], ['E',  0.00],
);

Robert Ricci's avatar
Robert Ricci committed
27
my $threshold = 0.05;
28

29 30
my $makescorefile = 0;
if (scalar(@ARGV) == 1) {
31
    if ($ARGV[0] eq "-g") {
32 33 34 35
        $makescorefile = 1;
    }
}

36 37 38 39 40
my @students = `list-students`;
chomp @students;

sub get_grade_for($$);
sub sum_pairs(@);
41
sub get_lettergrade($);
42 43

my %scores;
Robert Ricci's avatar
Robert Ricci committed
44
my @allstudents;
45 46
foreach my $student (@students) {
    $scores{$student} = {};
Robert Ricci's avatar
Robert Ricci committed
47
    my @studentscores = ();
48 49 50
    foreach my $type (keys %assignments) {
        $scores{$student}{$type} = {};
        foreach my $hw (keys %{$assignments{$type}}) {
51
            my ($score, $possible, $hash) = get_grade_for($student,$hw);
52 53
            my $weight = $assignments{$type}{$hw};
            $scores{$student}{$type}{$hw} =
54
                [$score * $weight, $possible *$weight, $hash];
55
        }
Robert Ricci's avatar
Robert Ricci committed
56 57 58 59 60
        my ($typetotal, $typepossible) =
            sum_pairs(values $scores{$student}{$type});
        my $pct = $typetotal / $typepossible;
        my $weight = $weights{$type};
        push @studentscores, $pct * $weight;
61
    }
Robert Ricci's avatar
Robert Ricci committed
62 63
    my $studenttotal = reduce { $a + $b } @studentscores;
    push @allstudents, $studenttotal;
64 65
}

Robert Ricci's avatar
Robert Ricci committed
66 67 68 69 70 71 72 73 74 75 76
@allstudents = sort {$b <=> $a} @allstudents;

my $topscore;
if ($allstudents[0] > $allstudents[1] + $threshold) {
    $topscore = $allstudents[1];
} else {
    $topscore = $allstudents[0];
}

print "Top score is $topscore\n";

77 78
foreach my $student (sort keys %scores) {
    print "##### $student\n";
79 80 81 82 83 84 85 86 87

    my $FH;
    if ($makescorefile) {
        system "checkout-homework total $student\n";
        open($FH,">score.txt");
    } else {
        $FH = *STDOUT;
    }

88 89
    my @studentscores = ();
    foreach my $type (sort keys $scores{$student}) {
90 91 92 93
        my ($typetotal, $typepossible) =
            sum_pairs(values $scores{$student}{$type});
        my $pct = $typetotal / $typepossible;
        my $weight = $weights{$type};
94
        printf $FH "\t%13s: %6.2f / %6.2f (%3.2f) (%5.2f%% of grade)\n", $type,
Robert Ricci's avatar
Robert Ricci committed
95
            $typetotal, $typepossible, $pct, $weight*100;
96
        push @studentscores, $pct * $weight;
97 98
        foreach my $hw (sort keys $scores{$student}{$type}) {
            my ($total, $possible, $hash) = @{$scores{$student}{$type}{$hw}};
Robert Ricci's avatar
Robert Ricci committed
99
            printf $FH "\t\t%15s: %6.2f / %6.2f (%s)\n", $hw, $total, $possible, $hash;
100 101
        }
        print $FH "\n";
102 103
    }
    my $studenttotal = reduce { $a + $b } @studentscores;
104
    printf $FH "\t%13s: % 6.2f%%\n", "RAW TOTAL", $studenttotal*100;
Robert Ricci's avatar
Robert Ricci committed
105
    my $adjusted = $studenttotal / $topscore;
106 107 108 109 110 111 112 113
    printf $FH "\t%13s: % 6.2f%%\n", "ADJ. TOTAL", $adjusted*100;
    printf $FH "\t%13s: %s\n", "GRADE", get_lettergrade($adjusted);

    if ($makescorefile) {
        close $FH;
        system "git add score.txt";
        system sprintf("git commit -m 'Total score: %.2f - grade %s'",$adjusted*100,get_lettergrade($adjusted));
    }
114 115 116 117 118 119 120
}

sub get_grade_for($$) {
    my ($student, $hw) = @_;
    my $branch = "$hw-graded-$student";
    if (`git show -s --format=%s $branch` =~ /^\s*Total score: (\d+(\.\d+)?)\s*\/\s*(\d+)/) {
        my ($score, $possible) = ($1,$3);
121 122 123
        my $hash = `git rev-parse --short $branch`;
        chomp $hash;
        return ($score, $possible, $hash);
124 125 126 127 128 129 130 131 132 133 134 135 136 137
    } else {
        die "No score for student $student\n";
    }
}

sub sum_pairs(@) {
    my @pairs = @_;
    my ($total, $possible) = (0,0);
    for my $pair (@pairs) {
        $total += $pair->[0];
        $possible += $pair->[1];
    }
    return ($total, $possible);
}
138 139 140 141 142 143 144 145 146

sub get_lettergrade($) {
    my ($score) = (@_);
    my $idx = 0;
    while ($lettergrades[$idx][1] > $score) {
        $idx++;
    }
    return $lettergrades[$idx][0];
}