2008-01-01 18:07:13 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
class BoltExerciseSet extends BoltUnit {
|
|
|
|
public $units;
|
|
|
|
public $repeats;
|
|
|
|
public $state_rec;
|
|
|
|
function __construct($name, $units, $number, $repeats) {
|
|
|
|
$this->name = $name;
|
|
|
|
$this->units = $units;
|
|
|
|
$this->number = $number;
|
|
|
|
$this->repeats = $repeats;
|
|
|
|
$this->is_item = false;
|
|
|
|
$this->shuffled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the scheduler calls this when an exercise in this set
|
|
|
|
// has just been graded.
|
2008-01-03 17:37:38 +00:00
|
|
|
// - record the score in our state structure
|
2008-01-01 18:07:13 +00:00
|
|
|
// - return a structure saying what navigation info to show:
|
|
|
|
// - review
|
|
|
|
// - repeat now
|
|
|
|
// - next
|
|
|
|
//
|
|
|
|
function xset_callback($iter, $score, &$is_last, &$nav_info) {
|
|
|
|
$state_rec = $iter->state[$this->name];
|
|
|
|
$number_shown = $state_rec['number_shown'];
|
|
|
|
$state_rec['scores'][$number_shown] = $score;
|
2008-01-03 17:37:38 +00:00
|
|
|
$is_last = ($number_shown == $this->number);
|
|
|
|
if ($is_last) {
|
|
|
|
} else {
|
2008-01-01 18:07:13 +00:00
|
|
|
}
|
2008-01-03 17:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The user clicked "Next" on last answer page,
|
|
|
|
// so this exercise set is now "completed".
|
|
|
|
// - create exercise_set_result record
|
|
|
|
// - optionally create or update bolt_refresh record
|
|
|
|
//
|
|
|
|
function completed($score, $view_id) {
|
|
|
|
$now = time();
|
|
|
|
$id = BoincXsetResult::insert("(create_time, user_id, course_id, name, score, view_id) values ($now, $user->id, $course->id, '$this->name', $score, $view_id)");
|
|
|
|
$due_time = $now + 100000;
|
|
|
|
BoincRefresh::replace("create_time=$now, user_id=$user->id, course_id=$course->id, name='$this->name', set_result_id=$id, due_time=$due_time");
|
2008-01-01 18:07:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function walk(&$iter, $incr, &$frac_done) {
|
|
|
|
$iter->xset = $this;
|
|
|
|
$n = count($this->units);
|
|
|
|
if (array_key_exists($this->name, $iter->state)) {
|
|
|
|
$state_rec = $iter->state[$this->name];
|
|
|
|
$this->state_rec = $state_rec;
|
|
|
|
$child_name = $state_rec['child_name'];
|
|
|
|
$number_shown = $state_rec['number_shown'];
|
|
|
|
if (!$this->shuffled) {
|
|
|
|
srand($state_rec['seed']);
|
|
|
|
shuffle($this->units);
|
|
|
|
$this->shuffled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// look up unit by name
|
|
|
|
//
|
|
|
|
$child = null;
|
|
|
|
for ($i=0; $i<$n; $i++) {
|
|
|
|
$c = $this->units[$i];
|
|
|
|
if ($c->name == $child_name) {
|
|
|
|
$child = $c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if not there, look up by index
|
|
|
|
//
|
|
|
|
if (!$child) {
|
|
|
|
$i = $state_rec['i'];
|
|
|
|
if ($i >= $n) {
|
|
|
|
// and if index is too big, use last unit
|
|
|
|
//
|
|
|
|
$i = $n-1;
|
|
|
|
}
|
|
|
|
$child = $this->units[$i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// at this point, $child is the current unit, and $i is its index
|
|
|
|
//
|
|
|
|
if ($incr) {
|
|
|
|
$my_inc = false;
|
|
|
|
if ($child->is_item) {
|
|
|
|
$my_inc = true;
|
|
|
|
} else {
|
|
|
|
$my_inc = $child->walk($iter, $incr, $frac_done);
|
|
|
|
}
|
|
|
|
if ($my_inc) {
|
|
|
|
$i = ($i+1)%$n;
|
|
|
|
$number_shown++;
|
|
|
|
if ($number_shown >= $this->number) {
|
|
|
|
$frac_done = 1;
|
|
|
|
$state_rec['i'] = $i;
|
|
|
|
$state_rec['number_shown'] = 0;
|
|
|
|
$state_rec['child_name'] = null;
|
|
|
|
$iter->state[$this->name] = $state_rec;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$i = 0;
|
|
|
|
$number_shown = 0;
|
|
|
|
$state_rec = null;
|
|
|
|
$seed = ((double)microtime()*1000000);
|
|
|
|
srand($seed);
|
|
|
|
shuffle($this->units);
|
|
|
|
$state_rec['seed'] = $seed;
|
|
|
|
}
|
|
|
|
$child = $this->units[$i];
|
|
|
|
$frac_done = $number_shown/$this->number;
|
|
|
|
$state_rec['i'] = $i;
|
|
|
|
$state_rec['number_shown'] = $number_shown;
|
|
|
|
$state_rec['child_name'] = $child->name;
|
|
|
|
$iter->state[$this->name] = $state_rec;
|
|
|
|
if ($child->is_item) {
|
|
|
|
$iter->item = $child;
|
|
|
|
} else {
|
|
|
|
$child->walk($iter, false, $f);
|
|
|
|
$frac_done += $f*(1/$number);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class BoltRefresh{
|
|
|
|
public $intervals;
|
|
|
|
function __construct($i) {
|
|
|
|
$this->intervals = $i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class BoltRepeat {
|
|
|
|
public $score;
|
|
|
|
public $unit;
|
|
|
|
public $flags;
|
|
|
|
function __construct($s, $u, $f) {
|
|
|
|
$this->score = $s;
|
|
|
|
$this->unit = $u;
|
|
|
|
$this->flags = $f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
define('REVIEW', 1);
|
|
|
|
define('REPEAT', 2);
|
|
|
|
define('NEXT', 4);
|
|
|
|
|
|
|
|
function repeat($s, $u, $f) {
|
|
|
|
return new BoltRepeat($s, $u, $f);
|
|
|
|
}
|
|
|
|
|
|
|
|
function refresh($a) {
|
|
|
|
return new BoltRefresh($a);
|
|
|
|
}
|
|
|
|
|
|
|
|
function exercise_set() {
|
|
|
|
$args = func_get_args();
|
|
|
|
$units = array();
|
|
|
|
$repeats = array();
|
|
|
|
$refresh = null;
|
|
|
|
$name = "";
|
|
|
|
$number = 1;
|
|
|
|
foreach ($args as $arg) {
|
|
|
|
if (is_array($arg)) {
|
|
|
|
switch ($arg[0]) {
|
|
|
|
case 'name': $name = $arg[1]; break;
|
|
|
|
case 'title': $title = $arg[1]; break;
|
|
|
|
case 'number': $number = $arg[1]; break;
|
|
|
|
default: echo "Unrecognized array arg: ", $arg[0], "\n"; break;
|
|
|
|
}
|
|
|
|
} else if (is_object($arg)) {
|
|
|
|
if (get_class($arg) == "BoltExercise") {
|
|
|
|
$units[] = $arg;
|
|
|
|
} else if (get_class($arg) == "BoltRepeat") {
|
|
|
|
$repeats[] = $arg;
|
|
|
|
} else if (get_class($arg) == "BoltRefresh") {
|
|
|
|
$refresh= $arg;
|
|
|
|
} else {
|
2008-01-28 15:48:09 +00:00
|
|
|
echo "Can't include object of type ".get_class($arg)." within an exercise set.";
|
2008-01-01 18:07:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new BoltExerciseSet($name, $units, $number, $repeats, $refresh);
|
|
|
|
}
|
|
|
|
|
|
|
|
?>
|