diff --git a/.gitignore b/.gitignore index c4df824b65212877ccbb9386d297b7c8dc6b76a5..9e357c60be30e78d3e9ea0456ea862cedf6db4ca 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ # generated /Selection +/Selection.zip /students.csv +/select-by-*.txt diff --git a/html2csv.pl b/html2csv.pl new file mode 100644 index 0000000000000000000000000000000000000000..c83fdd68f143f82b4dc9749995ebc77688a3162b --- /dev/null +++ b/html2csv.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl -w + +# Copyright © 2019 Forschungszentrum Juelich GmbH (Andreas Beckmann <a.beckmann@fz-juelich.de>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +while (<>) { + last if /REGISTRANTS EVALUATIONS/; +} + +while (<>) { + if (/<tr/) { + @cell = (); + while (<>) { + last if /<\/tr>/; + push @cell, $1 if /<td.*?>(.*?)(<\/td>|$)/; + } + if (@cell) { + $_ = $cell[1]; + $id = $1 if /\(ID: (\d+)\)/; + $name = $1 if /<a.*?>(.*?)<\/a>/; + @prj = /project_(\d+)/g; + $score = $cell[-1]; + #printf "%s: %s\n", $_, $cell[$_] foreach (0..$#cell-1); + printf "%3d, %.2f, %4d, %4d, %4d, %s\n", $id || 0, $score || 0.0, $prj[0] || 0, $prj[1] || 0, $prj[2] || 0, $name || "???"; + } + } +} + diff --git a/prepare-selection.sh b/prepare-selection.sh index d4de3e84f41d1edae2327201172b8adff0ae9cea..1a423245a3f0f3683fbc630bf8c4cb43bd935a4b 100755 --- a/prepare-selection.sh +++ b/prepare-selection.sh @@ -5,10 +5,11 @@ cd Selection python3 ../create-evaluation-files.py ../registrations.csv ../submissions-638.csv cd .. perl html2csv.pl Selection/evaluation.html > students.csv -perl sohpc-assign.pl students.csv +perl sohpc-assign.pl weights-pref.csv students.csv project-preferences.csv > select-by-pref.txt +perl sohpc-assign.pl weights-score.csv students.csv project-preferences.csv > select-by-score.txt mkdir Selection/scripts cp sohpc-assign.pl html2csv.pl submissions-638.csv registrations.csv create-evaluation-files.py Selection/scripts -cp prepare-selection.sh students.csv Selection/scripts +cp prepare-selection.sh weights-*.csv students.csv project-preferences.csv select-by-*.txt Selection/scripts echo zip -r Selection Selection # submissions-638.csv: David Henty's #24 renamed to #34, Duplicate #15 on line 151 removed # registrations.csv: Keys 1,2,3 changed to Choice 1, Choice 2, Choice 3 diff --git a/project-preferences.csv b/project-preferences.csv new file mode 100644 index 0000000000000000000000000000000000000000..536f30b20c2c0fdae871ef74d3777280b6e2e265 --- /dev/null +++ b/project-preferences.csv @@ -0,0 +1,5 @@ +# ProjectID, ApplicantID, Weight[, Comment] +# Weight: > 1.0: preference (use same value for equal student, different values for ranking) +# = 1.0: indifference +# < 1.0: weighted rejection +# = 0.0: invalid preference/unsuitable diff --git a/sohpc-assign.pl b/sohpc-assign.pl new file mode 100755 index 0000000000000000000000000000000000000000..833e7978a0945c9194e91d80a1116617a5bf1b3b --- /dev/null +++ b/sohpc-assign.pl @@ -0,0 +1,134 @@ +#!/usr/bin/perl -w + +# Copyright © 2019-2020 Forschungszentrum Juelich GmbH (Andreas Beckmann <a.beckmann@fz-juelich.de>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Usage: ./sohpc-assign.pl [weights.csv] students.csv [projectprefs.csv] + +$verbose = 1; + +$weight1 = 1.00; +$weight2 = 0.90; +$weight3 = 0.75; + +%project = (); +%student = (); +%project2student = (); +%student2project = (); + +$cmt = ""; + +while (<>) { + chomp; + s/^\s*//; + $first = (split(/, */, $_, 2))[0]; + if (!$first || $first =~ /^#/) { + # empty or comment + } elsif ($first eq "weights") { + ($first, $weight1, $weight2, $weight3) = split(/, */, $_, 4); + next; + } elsif ($first < 1000) { + ($id, $score, $pref1, $pref2, $pref3, $name) = split(/[, ]+/, $_, 6); + #print "id=$id score=$score p1=$pref1 p2=$pref2 p3=$pref3\n"; + $pref2 = 0 if $pref1 == $pref2; + $pref3 = 0 if $pref1 == $pref3 || $pref2 == $pref3; + $student{$id}{name} = $name; + $student{$id}{score} = $score; + $student{$id}{pref1} = $pref1; + $student{$id}{pref2} = $pref2; + $student{$id}{pref3} = $pref3; + + push @{$project{$pref1}{cand}}, {value => $score * $weight1, student => $id, score => $score, spref => 1, pweight => 1.0, ppref => 0} if $pref1; + push @{$project{$pref2}{cand}}, {value => $score * $weight2, student => $id, score => $score, spref => 2, pweight => 1.0, ppref => 0} if $pref2; + push @{$project{$pref3}{cand}}, {value => $score * $weight3, student => $id, score => $score, spref => 3, pweight => 1.0, ppref => 0} if $pref3; + } else { + ($proj, $stud, $pweight, $cmt) = split(/[, ]+/, $_, 4); + if (defined $project{$proj}) { + foreach (@{$project{$proj}{cand}}) { + if ($_->{student} == $stud) { + $_->{pweight} = $pweight; + $_->{value} *= $pweight; + } + } + } + } +} + +foreach $p (sort keys %project) { + $pw = -1; + $pp = 0; + foreach (sort {$b->{pweight} <=> $a->{pweight}} @{$project{$p}{cand}}) { + if ($pw != $_->{pweight}) { + $pw = $_->{pweight}; + ++$pp; + } + $_->{ppref} = $pp; + } + @{$project{$p}{cand}} = sort {$b->{value} <=> $a->{value}} @{$project{$p}{cand}}; + if ($verbose) { + print "$p: $_->{student}: $_->{spref}/$_->{ppref} $_->{score} ($_->{value})\n" foreach (@{$project{$p}{cand}}); + } +} + +sub max_score($$) +{ + my $p2s = shift; + my $s2p = shift; + my $s = 0.; + foreach my $p (keys %project) { + next if defined $p2s->{$p}; + foreach my $c (@{$project{$p}{cand}}) { + if (!defined $s2p->{$c->{student}}) { + $s += $c->{score}; + last; + } + } + } + return $s; +} + +$total = 0.; +@todo = keys %project; + +print "max_score=", max_score({}, {}), "\n" if $verbose; +while (@todo) { + my @cand = (); + my ($p, $c, $s); + foreach $p (@todo) { + foreach $c (@{$project{$p}{cand}}) { + next if defined $student2project{$c->{student}}; + push @cand, {project => $p, student => $c, value => $c->{value} + max_score({%project2student, $p => $c->{student}}, {%student2project, $c->{student} => $p})}; + } + } + last unless @cand; + $_ = (sort {$b->{value} <=> $a->{value}} @cand)[0]; + $p = $_->{project}; + $s = $_->{student}; + $project2student{$p} = $s; + $student2project{$s->{student}} = $p; + $total += $s->{score}; + printf "assigned %4d: %3d (%d/%d) %.2f\n", $p, $s->{student}, $s->{spref}, $s->{ppref}, $s->{score} if $verbose; + + @todo = grep {$_ != $p} @todo; +} + +foreach $p (sort keys %project) { + my $s = $project2student{$p}->{student} || "???"; + my $spref = $project2student{$p}->{spref}; + my $ppref = $project2student{$p}->{ppref}; + printf "%4d: %3d (%d/%d) %.2f %s\n", $p, $s, $spref, $ppref, $student{$s}{score} || 0, $student{$s}{name}; +} +print "total score: $total\n"; +print "weights: $weight1, $weight2, $weight3\n"; diff --git a/weights-pref.csv b/weights-pref.csv new file mode 100644 index 0000000000000000000000000000000000000000..5359eb7cd5821cf0030dcc1f627b065917fd8232 --- /dev/null +++ b/weights-pref.csv @@ -0,0 +1 @@ +weights, 1.0, 0.8, 0.5 diff --git a/weights-score.csv b/weights-score.csv new file mode 100644 index 0000000000000000000000000000000000000000..80c8ae6bac7bc5727b342ea9c271022ab9f4f636 --- /dev/null +++ b/weights-score.csv @@ -0,0 +1 @@ +weights, 1.0, 0.95, 0.90