forked from science-ation/science-ation
- Add a special awards feature (off by default) to the judge scheduler. It:
- Creates a judging team for each special award - Assigns judges to special awards based on the number of students self nominated. - Obeys the judges special award preferences (if enabled), and also judges that specify if they are a judge for a specific special award (if enabled). - Add 2 new config variables. - Enable the special award scheduler - Specify the max. number of projects each special award judge can handle (default: 20) - Delete an extra blank line in register_participants_students.php
This commit is contained in:
parent
cc2e6b5bce
commit
1bdba54ed5
@ -24,6 +24,7 @@
|
|||||||
<?
|
<?
|
||||||
require("../common.inc.php");
|
require("../common.inc.php");
|
||||||
require("../questions.inc.php");
|
require("../questions.inc.php");
|
||||||
|
require("../projects.inc.php");
|
||||||
require("judges.inc.php");
|
require("judges.inc.php");
|
||||||
require("anneal.inc.php");
|
require("anneal.inc.php");
|
||||||
auth_required('admin');
|
auth_required('admin');
|
||||||
@ -78,13 +79,16 @@ function set_status($txt)
|
|||||||
var='judge_scheduler_activity' AND year=0");
|
var='judge_scheduler_activity' AND year=0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$set_percent_last_percent = -1;
|
||||||
function set_percent($n)
|
function set_percent($n)
|
||||||
{
|
{
|
||||||
|
global $set_percent_last_percent;
|
||||||
$p = floor($n);
|
$p = floor($n);
|
||||||
|
if($p == $set_percent_last_percent) return;
|
||||||
TRACE("Progress: $p\%\n");
|
TRACE("Progress: $p\%\n");
|
||||||
mysql_query("UPDATE config SET val='$p' WHERE
|
mysql_query("UPDATE config SET val='$p' WHERE
|
||||||
var='judge_scheduler_percent' AND year=0");
|
var='judge_scheduler_percent' AND year=0");
|
||||||
|
$set_percent_last_percent = $p;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_status("Initializing...");
|
set_status("Initializing...");
|
||||||
@ -376,7 +380,7 @@ $willing_chair_question_id = questions_find_question_id("judgereg", $config['FAI
|
|||||||
printf("Judge Willing Chair = Question ID $willing_chair_question_id\n");
|
printf("Judge Willing Chair = Question ID $willing_chair_question_id\n");
|
||||||
|
|
||||||
/* Clean out the judging teams that were autocreated */
|
/* Clean out the judging teams that were autocreated */
|
||||||
TRACE("Deleting existing divisional judging teams:");
|
TRACE("Deleting autocreated divisional and special award judging teams:");
|
||||||
$q = mysql_query("SELECT * FROM judges_teams WHERE autocreate_type_id=1 AND year={$config['FAIRYEAR']}");
|
$q = mysql_query("SELECT * FROM judges_teams WHERE autocreate_type_id=1 AND year={$config['FAIRYEAR']}");
|
||||||
while($r = mysql_fetch_object($q)) {
|
while($r = mysql_fetch_object($q)) {
|
||||||
$jteam_id = $r->id;
|
$jteam_id = $r->id;
|
||||||
@ -394,7 +398,7 @@ while($r = mysql_fetch_object($q)) {
|
|||||||
mysql_query("DELETE FROM judges_teams_timeslots_projects_link WHERE judges_teams_id='$jteam_id' AND year={$config['FAIRYEAR']}");
|
mysql_query("DELETE FROM judges_teams_timeslots_projects_link WHERE judges_teams_id='$jteam_id' AND year={$config['FAIRYEAR']}");
|
||||||
print mysql_error();
|
print mysql_error();
|
||||||
}
|
}
|
||||||
/* Findally, delete all the autocreated judges teams */
|
/* Finally, delete all the autocreated judges teams */
|
||||||
mysql_query("DELETE FROM judges_teams WHERE autocreate_type_id=1 AND year={$config['FAIRYEAR']}");
|
mysql_query("DELETE FROM judges_teams WHERE autocreate_type_id=1 AND year={$config['FAIRYEAR']}");
|
||||||
print mysql_error();
|
print mysql_error();
|
||||||
TRACE(" Done.\n");
|
TRACE(" Done.\n");
|
||||||
@ -408,8 +412,7 @@ $q=mysql_query("SELECT judges.* FROM judges,judges_years WHERE ".
|
|||||||
);
|
);
|
||||||
|
|
||||||
$judges=array();
|
$judges=array();
|
||||||
|
$sa_judges = array();
|
||||||
|
|
||||||
|
|
||||||
while($r=mysql_fetch_object($q))
|
while($r=mysql_fetch_object($q))
|
||||||
{
|
{
|
||||||
@ -456,7 +459,36 @@ while($r=mysql_fetch_object($q))
|
|||||||
if($r2->answer == 'yes') $willing_chair = 'yes';
|
if($r2->answer == 'yes') $willing_chair = 'yes';
|
||||||
}
|
}
|
||||||
|
|
||||||
$judges[$r->id]=array(
|
$sa_only = 'no';
|
||||||
|
if($r->typepref == 'speconly') $sa_only = 'yes';
|
||||||
|
$sa_sel = array();
|
||||||
|
|
||||||
|
|
||||||
|
if($sa_only == 'yes') {
|
||||||
|
TRACE("Judge [{$r->firstname} {$r->lastname}] is a special awards only.\n");
|
||||||
|
/* Find their special award id */
|
||||||
|
$qq = mysql_query("SELECT award_awards.id,award_awards.name FROM
|
||||||
|
judges_specialaward_sel,award_awards
|
||||||
|
WHERE
|
||||||
|
award_awards.id=judges_specialaward_sel.award_awards_id
|
||||||
|
AND judges_specialaward_sel.judges_id='{$r->id}'
|
||||||
|
AND judges_specialaward_sel.year='{$config['FAIRYEAR']}'
|
||||||
|
AND award_awards.year='{$config['FAIRYEAR']}'");
|
||||||
|
echo mysql_error();
|
||||||
|
if(mysql_num_rows($qq) == 0) {
|
||||||
|
TRACE(" - NO special award selected! (removing special award only request)\n");
|
||||||
|
$sa_only = 'no';
|
||||||
|
} else if(mysql_num_rows($qq) > 1) {
|
||||||
|
TRACE(" - More than ONE special award selected (removing special award only request):\n");
|
||||||
|
$sa_only = 'no';
|
||||||
|
}
|
||||||
|
while($rr = mysql_fetch_object($qq)) {
|
||||||
|
TRACE(" ".$rr->name."\n");
|
||||||
|
$sa_sel[] = $rr->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$j=array(
|
||||||
"judges_id"=>"$r->id",
|
"judges_id"=>"$r->id",
|
||||||
"name"=>"$r->firstname $r->lastname",
|
"name"=>"$r->firstname $r->lastname",
|
||||||
"years_school"=>$r->years_school,
|
"years_school"=>$r->years_school,
|
||||||
@ -465,8 +497,20 @@ while($r=mysql_fetch_object($q))
|
|||||||
"willing_chair"=>$willing_chair,
|
"willing_chair"=>$willing_chair,
|
||||||
"divprefs"=>$divprefs,
|
"divprefs"=>$divprefs,
|
||||||
"catprefs"=>$catprefs,
|
"catprefs"=>$catprefs,
|
||||||
"languages"=>$langprefs
|
"languages"=>$langprefs,
|
||||||
|
"sa_only"=>$sa_only,
|
||||||
|
"sa_sel"=>$sa_sel,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* If it's a special award only judge, keep them
|
||||||
|
* out of the judges list for the divisional annealer */
|
||||||
|
if($sa_only == 'yes') {
|
||||||
|
$sa_judges[$r->id] = $j;
|
||||||
|
} else {
|
||||||
|
$judges[$r->id] = $j;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
TRACE("Loaded ".count($judges)." judges.\n");
|
TRACE("Loaded ".count($judges)." judges.\n");
|
||||||
$jteam[0]['max_judges'] = count($judges);
|
$jteam[0]['max_judges'] = count($judges);
|
||||||
@ -522,8 +566,8 @@ TRACE("Max Judging Team Number is currently $max_jteam_num\n");
|
|||||||
|
|
||||||
for($x=1;$x<count($jteam); $x++) {
|
for($x=1;$x<count($jteam); $x++) {
|
||||||
$t =& $jteam[$x];
|
$t =& $jteam[$x];
|
||||||
$num = $x + $max_jteam_num;
|
$max_jteam_num++;
|
||||||
print("Judging Team $num: cost={$a->bucket_cost[$x]} ");
|
print("Judging Team $max_jteam_num: cost={$a->bucket_cost[$x]} ");
|
||||||
print("langs=(");
|
print("langs=(");
|
||||||
$langstr="";
|
$langstr="";
|
||||||
for($y=0; $y<count($t['langs']); $y++) {
|
for($y=0; $y<count($t['langs']); $y++) {
|
||||||
@ -551,7 +595,7 @@ for($x=1;$x<count($jteam); $x++) {
|
|||||||
/* Add this judging team to the database */
|
/* Add this judging team to the database */
|
||||||
$tn = $catstr." ".$divstr." (".$langstr.") ".($t['sub']+1);
|
$tn = $catstr." ".$divstr." (".$langstr.") ".($t['sub']+1);
|
||||||
mysql_query("INSERT INTO judges_teams (num,name,autocreate_type_id,year) ".
|
mysql_query("INSERT INTO judges_teams (num,name,autocreate_type_id,year) ".
|
||||||
" VALUES ('$num','$tn','1','{$config['FAIRYEAR']}')");
|
" VALUES ('$max_jteam_num','$tn','1','{$config['FAIRYEAR']}')");
|
||||||
|
|
||||||
$team_id=mysql_insert_id();
|
$team_id=mysql_insert_id();
|
||||||
$t['team_id'] = $team_id;
|
$t['team_id'] = $team_id;
|
||||||
@ -598,8 +642,199 @@ for($x=1;$x<count($jteam); $x++) {
|
|||||||
|
|
||||||
print("Unused Judges:\n");
|
print("Unused Judges:\n");
|
||||||
$ids = $a->bucket[0];
|
$ids = $a->bucket[0];
|
||||||
for($y=0; $y<count($ids); $y++) pr_judge($jteam[0], $ids[$y]);
|
for($y=0; $y<count($ids); $y++) {
|
||||||
|
pr_judge($jteam[0], $ids[$y]);
|
||||||
|
$sa_judges[$ids[$y]] = $judges[$ids[$y]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================*/
|
||||||
|
/* Two functions for the Special Award Annealer, if special award
|
||||||
|
* scheduling is disabled, these will never get called */
|
||||||
|
function judges_sa_cost_function($annealer, $bucket_id, $ids)
|
||||||
|
{
|
||||||
|
global $sa_jteam;
|
||||||
|
global $sa_judges;
|
||||||
|
|
||||||
|
/* Bucket ID is the team number */
|
||||||
|
/* ids are the judge ids currently in the bucket */
|
||||||
|
|
||||||
|
$cost = 0;
|
||||||
|
if($bucket_id == 0) {
|
||||||
|
/* This is the placeholder */
|
||||||
|
$cost = count($ids) * 50;
|
||||||
|
return $cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
$t =& $sa_jteam[$bucket_id];
|
||||||
|
|
||||||
|
/* Compute the over max / under min costs */
|
||||||
|
$c = count($ids);
|
||||||
|
$min = ($c < $t['min_judges']) ? $t['min_judges'] - $c : 0;
|
||||||
|
$max = ($c > $t['max_judges']) ? $c - $t['max_judges'] : 0;
|
||||||
|
$cost += $min * 50;
|
||||||
|
$cost += $max * 10;
|
||||||
|
|
||||||
|
/* For each judge on the team, score their preferences */
|
||||||
|
for($x=0; $x<count($ids); $x++) {
|
||||||
|
$j =& $sa_judges[$ids[$x]];
|
||||||
|
$apref = 0;
|
||||||
|
|
||||||
|
/* See if the sa_jteam award id (what the team is judging)
|
||||||
|
* is in the judges selection list */
|
||||||
|
/* Run through all awards this team is judging */
|
||||||
|
foreach($t['award_ids'] as $aid) {
|
||||||
|
if(in_array($aid, $j['sa_sel'])) {
|
||||||
|
/* This judge wants to judge this award */
|
||||||
|
/* No cost */
|
||||||
|
} else {
|
||||||
|
if($j['sa_only'] == 'yes') {
|
||||||
|
/* This judge is for an award, but
|
||||||
|
* NOT assigned to the proper one,
|
||||||
|
* HUGE cost */
|
||||||
|
$cost += 500;
|
||||||
|
}
|
||||||
|
$apref++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cost += 5 * $apref;
|
||||||
|
}
|
||||||
|
// TRACE("Team $bucket_id, cost is $cost\n");
|
||||||
|
|
||||||
|
return $cost;
|
||||||
|
}
|
||||||
|
function judges_sa_pick_move($a)
|
||||||
|
{
|
||||||
|
global $sa_judges;
|
||||||
|
/* Use the existing pick move, but we never want to
|
||||||
|
* select a judge that has sa_only=='yes', they are
|
||||||
|
* already in the correct place and are unmovable */
|
||||||
|
/* FIXME: this will spin forever if there aren't at least
|
||||||
|
* 2 judges to swap */
|
||||||
|
while(1) {
|
||||||
|
list($b1, $i1, $b2, $i2) = $a->pick_move();
|
||||||
|
|
||||||
|
/* See if $b1,$i1 is movable */
|
||||||
|
$id1 = $a->bucket[$b1][$i1];
|
||||||
|
$j1 =& $sa_judges[$id1];
|
||||||
|
// print("J1:");
|
||||||
|
// print_r($j1);
|
||||||
|
if($j1['sa_only'] == 'yes') continue;
|
||||||
|
|
||||||
|
if($i2 != -1) {
|
||||||
|
$id2 = $a->bucket[$b2][$i2];
|
||||||
|
$j2 =& $sa_judges[$id2];
|
||||||
|
// print("J2:");
|
||||||
|
// print_r($j2);
|
||||||
|
if($j2['sa_only'] == 'yes') continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($b1, $i1, $b2, $i2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if($config['scheduler_enable_sa_scheduling'] == 'yes') {
|
||||||
|
set_status("Creating Special Award Judging Teams (one team per award)");
|
||||||
|
$q = "SELECT award_awards.name,award_awards.id FROM award_awards,award_types
|
||||||
|
WHERE
|
||||||
|
award_awards.year='{$config['FAIRYEAR']}'
|
||||||
|
AND award_types.id=award_awards.award_types_id
|
||||||
|
AND award_types.year='{$config['FAIRYEAR']}'
|
||||||
|
AND award_types.type='Special'
|
||||||
|
";
|
||||||
|
$r = mysql_query($q);
|
||||||
|
print(mysql_error());
|
||||||
|
/* sa_jteam for leftover judges, if any */
|
||||||
|
$sa_jteam = array();
|
||||||
|
$sa_jteam[0]['id'] = 0;
|
||||||
|
$sa_jteam[0]['projects'] = array();
|
||||||
|
$sa_jteam[0]['langs'] = array();
|
||||||
|
$sa_jteam[0]['min_judges'] = 0;
|
||||||
|
$sa_jteam[0]['max_judges'] = 0;
|
||||||
|
$sa_jteam[0]['award_ids'] = array();
|
||||||
|
|
||||||
|
$x=1;
|
||||||
|
while($i = mysql_fetch_object($r)) {
|
||||||
|
$projects = getProjectsNominatedForSpecialAward($i->id);
|
||||||
|
|
||||||
|
$max_jteam_num++; /* Pre-increment before using */
|
||||||
|
|
||||||
|
$pids = array_keys($projects);
|
||||||
|
$sa_jteam[$x]['id'] = $jteam_id;
|
||||||
|
$sa_jteam[$x]['projects'] = $pids;
|
||||||
|
$sa_jteam[$x]['sub'] = 0;
|
||||||
|
$sa_jteam[$x]['langs'] = array();
|
||||||
|
$min = floor(count($pids) / $config['projects_per_special_award_judge']) + 1;
|
||||||
|
$sa_jteam[$x]['min_judges'] = $min;
|
||||||
|
$sa_jteam[$x]['max_judges'] = $min;
|
||||||
|
$sa_jteam[$x]['award_ids'] = array($i->id);
|
||||||
|
|
||||||
|
$tn = "{$i->name}";
|
||||||
|
/* Write this team to the DB */
|
||||||
|
mysql_query("INSERT INTO judges_teams (num,name,autocreate_type_id,year)
|
||||||
|
VALUES ('$max_jteam_num','$tn','1','{$config['FAIRYEAR']}')");
|
||||||
|
$sa_jteam[$x]['id'] = mysql_insert_id();
|
||||||
|
|
||||||
|
/* Link the award to this team */
|
||||||
|
mysql_query("INSERT INTO judges_teams_awards_link (award_awards_id,judges_teams_id,year)
|
||||||
|
VALUES ('{$i->id}','{$sa_jteam[$x]['id']}','{$config['FAIRYEAR']}')");
|
||||||
|
|
||||||
|
TRACE("Created Team: $tn\n");
|
||||||
|
$jteam_id++;
|
||||||
|
$x++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ====================================================================*/
|
||||||
|
set_status("Assigning Judges to Special Award Teams\n");
|
||||||
|
|
||||||
|
|
||||||
|
$judge_ids = array_keys($sa_judges);
|
||||||
|
$e = $config['effort'];
|
||||||
|
$a = new annealer(count($sa_jteam), 25, $e, 0.98, judges_sa_cost_function, $judge_ids);
|
||||||
|
//$a->set_update_callback(judges_to_teams_update);
|
||||||
|
//$a->set_pick_move(judges_sa_pick_move);
|
||||||
|
$a->anneal();
|
||||||
|
|
||||||
|
|
||||||
|
$x=0;
|
||||||
|
foreach($sa_jteam as $tid => $t) {
|
||||||
|
if($tid == 0) {
|
||||||
|
$x++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
print("Judging Team {$t['id']}: cost={$a->bucket_cost[$x]} #=({$t['min_judges']},{$t['max_judges']}) ");
|
||||||
|
// print("langs=(");
|
||||||
|
/* $langstr="";
|
||||||
|
for($y=0; $y<count($t['langs']); $y++) {
|
||||||
|
if($y != 0) $langstr .= " ";
|
||||||
|
$langstr .= $t['langs'][$y];
|
||||||
|
}
|
||||||
|
print("$langstr)");*/
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
$ids = $a->bucket[$x];
|
||||||
|
for($y=0; $y<count($ids); $y++) {
|
||||||
|
// pr_judge($t, $ids[$y]);
|
||||||
|
|
||||||
|
$j =& $sa_judges[$ids[$y]];
|
||||||
|
print(" - {$j['name']}\n");
|
||||||
|
|
||||||
|
/* Link Judges to the judging team we just inserted */
|
||||||
|
mysql_query("INSERT INTO judges_teams_link
|
||||||
|
(judges_id,judges_teams_id,captain,year)
|
||||||
|
VALUES ('{$ids[$y]}','{$t['id']}',
|
||||||
|
'{$j['willing_chair']}',
|
||||||
|
'{$config['FAIRYEAR']}')");
|
||||||
|
}
|
||||||
|
$x++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resume normal flow now */
|
||||||
|
|
||||||
|
/* ====================================================================*/
|
||||||
set_status("Assigning Judging Teams and Projects to Timeslots");
|
set_status("Assigning Judging Teams and Projects to Timeslots");
|
||||||
|
|
||||||
TRACE("Loading Timeslot Data\n");
|
TRACE("Loading Timeslot Data\n");
|
||||||
@ -613,7 +848,7 @@ while($r=mysql_fetch_object($q)) {
|
|||||||
"date"=>$r->date,
|
"date"=>$r->date,
|
||||||
"starttime"=>substr($r->starttime,0,-3),
|
"starttime"=>substr($r->starttime,0,-3),
|
||||||
"endtime"=>substr($r->endtime,0,-3));
|
"endtime"=>substr($r->endtime,0,-3));
|
||||||
print(" -".$available_timeslots[$x]['starttime']." -> ".
|
print(" ".$available_timeslots[$x]['starttime']." -> ".
|
||||||
$available_timeslots[$x]['endtime']."\n");
|
$available_timeslots[$x]['endtime']."\n");
|
||||||
$x++;
|
$x++;
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
48
|
49
|
||||||
|
4
db/db.update.49.sql
Normal file
4
db/db.update.49.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
INSERT INTO `config` ( `var` , `val` , `category` , `type` , `type_values` , `ord` , `description` , `year` ) VALUES ( 'scheduler_enable_sa_scheduling', 'no', 'Judge Scheduler', 'yesno', '', '900', 'Allow the scheduler to automatically create a judging team for each special award, and assigned unused divisional judges to special awards.', '-1');
|
||||||
|
|
||||||
|
INSERT INTO `config` ( `var` , `val` , `category` , `type` , `type_values` , `ord` , `description` , `year` ) VALUES ( 'projects_per_special_award_judge', '20', 'Judge Scheduler', 'number', '', '1000', 'The maximum number of projects that each special awards judge can judge.', '-1');
|
||||||
|
|
@ -229,7 +229,6 @@ else if($newstatus=="complete")
|
|||||||
else
|
else
|
||||||
$numtoshow=$numfound;
|
$numtoshow=$numfound;
|
||||||
|
|
||||||
|
|
||||||
echo "<form name=\"numstudentsform\" method=\"get\" action=\"register_participants_students.php\">";
|
echo "<form name=\"numstudentsform\" method=\"get\" action=\"register_participants_students.php\">";
|
||||||
echo i18n("Number of students that worked on the project: ");
|
echo i18n("Number of students that worked on the project: ");
|
||||||
echo "<select name=\"numstudents\" onchange=\"document.forms.numstudentsform.submit()\">\n";
|
echo "<select name=\"numstudents\" onchange=\"document.forms.numstudentsform.submit()\">\n";
|
||||||
|
Loading…
Reference in New Issue
Block a user