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("../questions.inc.php");
|
||||
require("../projects.inc.php");
|
||||
require("judges.inc.php");
|
||||
require("anneal.inc.php");
|
||||
auth_required('admin');
|
||||
@ -78,13 +79,16 @@ function set_status($txt)
|
||||
var='judge_scheduler_activity' AND year=0");
|
||||
}
|
||||
|
||||
$set_percent_last_percent = -1;
|
||||
function set_percent($n)
|
||||
{
|
||||
global $set_percent_last_percent;
|
||||
$p = floor($n);
|
||||
if($p == $set_percent_last_percent) return;
|
||||
TRACE("Progress: $p\%\n");
|
||||
mysql_query("UPDATE config SET val='$p' WHERE
|
||||
var='judge_scheduler_percent' AND year=0");
|
||||
|
||||
$set_percent_last_percent = $p;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
/* 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']}");
|
||||
while($r = mysql_fetch_object($q)) {
|
||||
$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']}");
|
||||
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']}");
|
||||
print mysql_error();
|
||||
TRACE(" Done.\n");
|
||||
@ -408,8 +412,7 @@ $q=mysql_query("SELECT judges.* FROM judges,judges_years WHERE ".
|
||||
);
|
||||
|
||||
$judges=array();
|
||||
|
||||
|
||||
$sa_judges = array();
|
||||
|
||||
while($r=mysql_fetch_object($q))
|
||||
{
|
||||
@ -456,7 +459,36 @@ while($r=mysql_fetch_object($q))
|
||||
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",
|
||||
"name"=>"$r->firstname $r->lastname",
|
||||
"years_school"=>$r->years_school,
|
||||
@ -465,8 +497,20 @@ while($r=mysql_fetch_object($q))
|
||||
"willing_chair"=>$willing_chair,
|
||||
"divprefs"=>$divprefs,
|
||||
"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");
|
||||
$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++) {
|
||||
$t =& $jteam[$x];
|
||||
$num = $x + $max_jteam_num;
|
||||
print("Judging Team $num: cost={$a->bucket_cost[$x]} ");
|
||||
$max_jteam_num++;
|
||||
print("Judging Team $max_jteam_num: cost={$a->bucket_cost[$x]} ");
|
||||
print("langs=(");
|
||||
$langstr="";
|
||||
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 */
|
||||
$tn = $catstr." ".$divstr." (".$langstr.") ".($t['sub']+1);
|
||||
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();
|
||||
$t['team_id'] = $team_id;
|
||||
@ -598,8 +642,199 @@ for($x=1;$x<count($jteam); $x++) {
|
||||
|
||||
print("Unused Judges:\n");
|
||||
$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");
|
||||
|
||||
TRACE("Loading Timeslot Data\n");
|
||||
@ -613,7 +848,7 @@ while($r=mysql_fetch_object($q)) {
|
||||
"date"=>$r->date,
|
||||
"starttime"=>substr($r->starttime,0,-3),
|
||||
"endtime"=>substr($r->endtime,0,-3));
|
||||
print(" -".$available_timeslots[$x]['starttime']." -> ".
|
||||
print(" ".$available_timeslots[$x]['starttime']." -> ".
|
||||
$available_timeslots[$x]['endtime']."\n");
|
||||
$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
|
||||
$numtoshow=$numfound;
|
||||
|
||||
|
||||
echo "<form name=\"numstudentsform\" method=\"get\" action=\"register_participants_students.php\">";
|
||||
echo i18n("Number of students that worked on the project: ");
|
||||
echo "<select name=\"numstudents\" onchange=\"document.forms.numstudentsform.submit()\">\n";
|
||||
|
Loading…
Reference in New Issue
Block a user