forked from science-ation/science-ation
- A working tour annealer.. Now I just have to glue it all together.
This commit is contained in:
parent
dba389618d
commit
c5887eb432
237
admin/tours_sa.php
Normal file
237
admin/tours_sa.php
Normal file
@ -0,0 +1,237 @@
|
||||
<?
|
||||
/*
|
||||
This file is part of the 'Science Fair In A Box' project
|
||||
SFIAB Website: http://www.sfiab.ca
|
||||
|
||||
Copyright (C) 2005 Sci-Tech Ontario Inc <info@scitechontario.org>
|
||||
Copyright (C) 2005 James Grant <james@lightbox.org>
|
||||
Copyright (C) 2008 Daivd Grant <dave@lightbox.org>
|
||||
|
||||
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, version 2.
|
||||
|
||||
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; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
require_once("../common.inc.php");
|
||||
require_once("../user.inc.php");
|
||||
require_once("anneal.inc.php");
|
||||
|
||||
if($_SERVER['SERVER_ADDR']) {
|
||||
echo "This script must be run from the command line";
|
||||
exit;
|
||||
}
|
||||
|
||||
//function TRACE() { }
|
||||
//function TRACE_R() { }
|
||||
function TRACE($str) { print($str); }
|
||||
function TRACE_R($array) { print_r($array); }
|
||||
|
||||
|
||||
TRACE("<pre>");
|
||||
|
||||
|
||||
function set_status($txt)
|
||||
{
|
||||
TRACE("Status: $txt\n");
|
||||
return;
|
||||
mysql_query("UPDATE config SET val='$txt' WHERE
|
||||
var='tours_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");
|
||||
$set_percent_last_percent = $p;
|
||||
return;
|
||||
mysql_query("UPDATE config SET val='$p' WHERE
|
||||
var='tours_scheduler_percent' AND year=0");
|
||||
}
|
||||
|
||||
set_status("Initializing...");
|
||||
set_percent(0);
|
||||
|
||||
/* The cost function is:
|
||||
- Foreach student in a tour
|
||||
+15 - Above the grade level
|
||||
+25 - Below the grade level
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/* Compute the cost of adding a judge to a team */
|
||||
|
||||
function tour_cost_function($annealer, $bucket_id, $ids)
|
||||
{
|
||||
global $config;
|
||||
global $tid;
|
||||
global $tours;
|
||||
global $students;
|
||||
/* Bucket ID is the tour number */
|
||||
/* ids are the judge ids currently in the bucket */
|
||||
|
||||
// TRACE("Bucket id=$bucket_id, ids=");
|
||||
// TRACE_R($ids);
|
||||
|
||||
$cost = 0;
|
||||
|
||||
$t =& $tours[$bucket_id];
|
||||
$tid = $t['id'];
|
||||
|
||||
/* Compute the over max / under min costs */
|
||||
$c = count($ids);
|
||||
$over = ($c > $t['capacity']) ? $c - $t['capacity'] : 0;
|
||||
$under = ($c < ($t['capacity']/4)) ? ($t['capacity']/4) - $c : 0;
|
||||
$cost += $over * 100;
|
||||
$cost += $under * 200;
|
||||
|
||||
// TRACE("Under min=$min, over max=$max\n");
|
||||
|
||||
$schools = array();
|
||||
/* For each student on the tour */
|
||||
foreach($ids as $x=>$sid) {
|
||||
$s =& $students[$sid];
|
||||
|
||||
/* Score the rank */
|
||||
$rank_cost = -1;
|
||||
foreach($s['rank'] as $rank=>$rank_tid) {
|
||||
if($rank_tid != $tid) continue;
|
||||
$rank_cost = ($rank * $rank * 5) - 5;
|
||||
}
|
||||
if($rank_cost == -1) {
|
||||
$rank_cost = $config['tours_choices_max'] * $config['tours_choices_max'] * 5;
|
||||
}
|
||||
$cost += $rank_cost;
|
||||
|
||||
/* Check for student below/above grade range */
|
||||
if($s['grade'] < $t['grade_min']) $cost += 15;
|
||||
if($s['grade'] > $t['grade_min']) $cost += 25;
|
||||
|
||||
/* Record the school */
|
||||
$schools[$s['schools_id']]++;
|
||||
}
|
||||
|
||||
/* Search the schools array for insteances of '1' */
|
||||
foreach($schools as $sid=>$cnt) {
|
||||
if($cnt == 1) $cost += 5;
|
||||
}
|
||||
|
||||
// TRACE("Team $bucket_id, cost is $cost\n");
|
||||
|
||||
return $cost;
|
||||
}
|
||||
|
||||
set_status("Cleaning existing tour assignments...");
|
||||
TRACE("\n\n");
|
||||
$q=mysql_query("DELETE FROM tours_choice
|
||||
WHERE year='{$config['FAIRYEAR']}'
|
||||
AND rank='0'");
|
||||
|
||||
set_status("Loading Data From Database...");
|
||||
TRACE("\n\n");
|
||||
TRACE("Tours...\n");
|
||||
$tours = array();
|
||||
$q=mysql_query("SELECT * FROM tours WHERE year='{$config['FAIRYEAR']}'");
|
||||
$x=0;
|
||||
while($r=mysql_fetch_object($q)) {
|
||||
$tours[$x]['capacity'] = $r->capacity;
|
||||
$tours[$x]['grade_min'] = $r->grade_min;
|
||||
$tours[$x]['grade_max'] = $r->grade_max;
|
||||
$tours[$x]['id'] = $r->id;
|
||||
$tours[$x]['name'] = $r->name;
|
||||
TRACE(" $x: #{$r->id} {$r->name} (c:{$r->capacity} g:{$r->grade_min}-{$r->grade_max})\n");
|
||||
$x++;
|
||||
}
|
||||
|
||||
$students = array();
|
||||
TRACE("Loading Students...\n");
|
||||
$q=mysql_query("SELECT students.id,students.grade,
|
||||
students.registrations_id,
|
||||
students.schools_id,
|
||||
students.firstname, students.lastname,
|
||||
tours_choice.rank,tours_choice.tour_id
|
||||
FROM students
|
||||
LEFT JOIN tours_choice ON (tours_choice.students_id=students.id)
|
||||
LEFT JOIN registrations ON (registrations.id=students.registrations_id)
|
||||
WHERE
|
||||
students.year='{$config['FAIRYEAR']}'
|
||||
AND tours_choice.year='{$config['FAIRYEAR']}'
|
||||
AND registrations.status='complete'
|
||||
ORDER BY
|
||||
students.id, tours_choice.rank
|
||||
");
|
||||
$last_sid = -1;
|
||||
TRACE(mysql_error());
|
||||
while($r=mysql_fetch_object($q)) {
|
||||
$sid = $r->id;
|
||||
if($last_sid != $sid) {
|
||||
$students[$sid]['name'] = $r->firstname.' '.$r->lastname;
|
||||
$students[$sid]['grade'] = $r->grade;
|
||||
$students[$sid]['registrations_id'] = $r->registrations_id;
|
||||
$students[$sid]['rank'] = array();
|
||||
$students[$sid]['schools_id'] = $r->schools_id;
|
||||
$last_sid = $sid;
|
||||
}
|
||||
$students[$sid]['rank'][$r->rank] = $r->tour_id;
|
||||
}
|
||||
$student_ids = array_keys($students);
|
||||
TRACE(" ".(count($student_ids))." students loaded\n");
|
||||
|
||||
function tours_assignment_update($progress, $total)
|
||||
{
|
||||
set_percent(($progress * 50) / $total);
|
||||
}
|
||||
|
||||
TRACE("Effort: {$config['effort']}\n");
|
||||
set_status("Assigning students to tours");
|
||||
$e = 100 + 10 * ($config['effort'] / 100);
|
||||
$a = new annealer(count($tours), 50, $e, 0.98,
|
||||
tour_cost_function, $student_ids);
|
||||
$a->set_update_callback(tours_assignment_update);
|
||||
$a->anneal();
|
||||
|
||||
/* Record the assignments */
|
||||
foreach($tours as $x=>$t) {
|
||||
TRACE("#{$t['id']} {$t['name']} (c:{$t['capacity']} g:{$t['grade_min']}-{$t['grade_max']})\n");
|
||||
|
||||
$sids = $a->bucket[$x];
|
||||
|
||||
TRACE(" - Cost:{$a->bucket_cost[$x]} Students: ".(count($sids))."\n");
|
||||
foreach($sids as $sid) {
|
||||
$s = $students[$sid];
|
||||
$tids = implode(' ', $s['rank']);
|
||||
TRACE(" - {$s['name']} ($tids) (g:{$s['grade']} s:{$s['schools_id']})\n");
|
||||
mysql_query("INSERT INTO tours_choice
|
||||
(`students_id`,`registrations_id`,
|
||||
`tour_id`,`year`,`rank`)
|
||||
VALUES (
|
||||
'$sid', '{$s['registrations_id']}',
|
||||
'{$t['id']}', '{$config['FAIRYEAR']}',
|
||||
'0')");
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("All Done.\n");
|
||||
echo "</pre>";
|
||||
|
||||
set_percent(-1);
|
||||
set_status("Done");
|
||||
|
||||
//echo happy("Scheduler completed successfully");
|
||||
|
||||
//send_footer();
|
||||
?>
|
Loading…
Reference in New Issue
Block a user