2007-10-30 22:31:13 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
error_reporting(E_ALL);
|
2007-12-04 22:16:37 +00:00
|
|
|
ini_set('display_errors', true);
|
2007-10-30 22:31:13 +00:00
|
|
|
ini_set('display_startup_errors', true);
|
|
|
|
|
2007-12-07 23:23:25 +00:00
|
|
|
// stuff needed in Bolt course documents
|
|
|
|
|
2007-10-30 22:31:13 +00:00
|
|
|
// rules about course structures:
|
|
|
|
//
|
|
|
|
// - Each unit has a logical name.
|
|
|
|
// - The members of a set must have distinct logical names
|
|
|
|
// - Different units may have the same logical name;
|
2007-12-03 02:27:30 +00:00
|
|
|
// however, such units must be identical.
|
2007-10-30 22:31:13 +00:00
|
|
|
|
2007-12-05 19:13:21 +00:00
|
|
|
// A state in a course is described by an associative array
|
|
|
|
// mapping logical names to state structures.
|
|
|
|
// Typically this includes the logical name of the current child
|
|
|
|
// and info such as a sequence index.
|
2007-10-30 22:31:13 +00:00
|
|
|
|
|
|
|
abstract class BoltUnit {
|
|
|
|
public $name; // logical name.
|
2007-12-10 22:13:48 +00:00
|
|
|
public $title;
|
2007-12-05 19:13:21 +00:00
|
|
|
public $is_item;
|
2007-10-30 22:31:13 +00:00
|
|
|
|
2007-12-05 19:13:21 +00:00
|
|
|
abstract function walk(&$iter, $incr, &$frac_done);
|
2007-10-30 22:31:13 +00:00
|
|
|
// multi-purpose function for traversing a course.
|
2007-12-05 19:13:21 +00:00
|
|
|
// Create entry in $iter->state if not there.
|
|
|
|
// Recurse to first child.
|
|
|
|
// If first child is an item, set $iter->item
|
|
|
|
// If incr is set
|
|
|
|
// the bottom-level non-item unit should increment.
|
|
|
|
// return value: true if the caller should increment
|
2007-11-29 02:56:10 +00:00
|
|
|
// frac_done: Fraction done (of this unit and any subunits)
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// An iterator represents a user's position in a course.
|
2007-12-04 22:16:37 +00:00
|
|
|
// Its state is stored in the database,
|
|
|
|
// and the course may change underneath it.
|
2007-10-30 22:31:13 +00:00
|
|
|
//
|
|
|
|
class BoltIter {
|
2007-12-03 02:27:30 +00:00
|
|
|
public $top; // topmost unit
|
|
|
|
public $state; // state array
|
2007-12-05 19:13:21 +00:00
|
|
|
|
|
|
|
// the following are temps
|
2007-12-03 02:27:30 +00:00
|
|
|
public $item; // current item
|
2007-12-04 22:16:37 +00:00
|
|
|
public $frac_done; // fraction done
|
2007-10-30 22:31:13 +00:00
|
|
|
|
|
|
|
function __construct($top) {
|
|
|
|
$this->top = $top;
|
2007-12-04 22:16:37 +00:00
|
|
|
$this->state = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
function decode_state($encoded_state) {
|
|
|
|
$this->state = json_decode($encoded_state, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function encode_state() {
|
|
|
|
return json_encode($this->state);
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
|
2007-12-05 19:13:21 +00:00
|
|
|
// get current item and fraction done
|
2007-10-30 22:31:13 +00:00
|
|
|
//
|
2007-11-29 23:26:49 +00:00
|
|
|
function at() {
|
2007-12-05 19:13:21 +00:00
|
|
|
$this->top->walk($this, false, $this->frac_done);
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
|
2007-12-05 19:13:21 +00:00
|
|
|
// move to the next item, and return it in $this->item
|
|
|
|
// (null if course finished)
|
2007-10-30 22:31:13 +00:00
|
|
|
//
|
2007-11-29 23:26:49 +00:00
|
|
|
function next() {
|
2007-12-05 19:13:21 +00:00
|
|
|
$this->top->walk($this, true, $this->frac_done);
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-04 22:16:37 +00:00
|
|
|
class BoltSequence extends BoltUnit {
|
2007-10-30 22:31:13 +00:00
|
|
|
public $units;
|
|
|
|
function __construct($n, $u) {
|
|
|
|
$this->name = $n;
|
|
|
|
$this->units = $u;
|
2007-12-05 19:13:21 +00:00
|
|
|
$this->is_item = false;
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
|
2007-12-05 19:13:21 +00:00
|
|
|
function walk(&$iter, $incr, &$frac_done) {
|
2007-10-30 22:31:13 +00:00
|
|
|
//echo "call to walk() for $this->name: next: $next\n";
|
2007-11-29 02:56:10 +00:00
|
|
|
$n = count($this->units);
|
2007-12-05 19:13:21 +00:00
|
|
|
if (array_key_exists($this->name, $iter->state)) {
|
|
|
|
$state_rec = $iter->state[$this->name];
|
|
|
|
$child_name = $state_rec['child_name'];
|
|
|
|
|
|
|
|
// look up unit by name
|
2007-11-29 02:56:10 +00:00
|
|
|
//
|
|
|
|
$child = null;
|
2007-12-05 19:13:21 +00:00
|
|
|
for ($i=0; $i<$n; $i++) {
|
|
|
|
$c = $this->units[$i];
|
2007-12-03 02:27:30 +00:00
|
|
|
if ($c->name == $child_name) {
|
2007-11-29 02:56:10 +00:00
|
|
|
$child = $c;
|
|
|
|
break;
|
|
|
|
}
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
2007-12-03 02:27:30 +00:00
|
|
|
|
2007-11-29 02:56:10 +00:00
|
|
|
// if not there, look up by index
|
|
|
|
//
|
|
|
|
if (!$child) {
|
2007-12-05 19:13:21 +00:00
|
|
|
$i = $state_rec['i'];
|
2007-11-29 02:56:10 +00:00
|
|
|
if ($i >= $n) {
|
|
|
|
// and if index is too big, use last unit
|
|
|
|
//
|
|
|
|
$i = $n-1;
|
|
|
|
}
|
|
|
|
$child = $this->units[$i];
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
2007-11-29 02:56:10 +00:00
|
|
|
|
2007-12-05 19:13:21 +00:00
|
|
|
// at this point, $child is the current unit, and $i is its index
|
2007-11-29 02:56:10 +00:00
|
|
|
//
|
2007-12-05 19:13:21 +00:00
|
|
|
if ($incr) {
|
|
|
|
$my_inc = false;
|
|
|
|
if ($child->is_item) {
|
|
|
|
$my_inc = true;
|
2007-11-29 02:56:10 +00:00
|
|
|
} else {
|
2007-12-05 19:13:21 +00:00
|
|
|
$my_inc = $child->walk($iter, $incr, $frac_done);
|
2007-11-29 02:56:10 +00:00
|
|
|
}
|
2007-12-05 19:13:21 +00:00
|
|
|
if ($my_inc) {
|
2007-11-29 02:56:10 +00:00
|
|
|
$i++;
|
|
|
|
if ($i == $n) {
|
|
|
|
$frac_done = 1;
|
2007-12-05 19:13:21 +00:00
|
|
|
$state_rec['i'] = 0;
|
|
|
|
$iter->state[$this->name] = $state_rec;
|
2007-11-29 02:56:10 +00:00
|
|
|
return true;
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$i = 0;
|
|
|
|
}
|
|
|
|
$child = $this->units[$i];
|
2007-12-05 19:13:21 +00:00
|
|
|
$frac_done = $i/$n;
|
2007-12-04 22:16:37 +00:00
|
|
|
$state_rec = null;
|
|
|
|
$state_rec->i = $i;
|
|
|
|
$state_rec->child_name = $child->name;
|
2007-12-05 19:13:21 +00:00
|
|
|
$iter->state[$this->name] = $state_rec;
|
|
|
|
if ($child->is_item) {
|
|
|
|
$iter->item = $child;
|
2007-10-30 22:31:13 +00:00
|
|
|
} else {
|
2007-12-05 19:13:21 +00:00
|
|
|
$child->walk($iter, false, $f);
|
2007-11-29 02:56:10 +00:00
|
|
|
$frac_done += $f*(1/$n);
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class BoltItem extends BoltUnit {
|
|
|
|
public $filename;
|
2007-12-10 22:13:48 +00:00
|
|
|
function __construct($name, $title, $filename) {
|
2007-10-30 22:31:13 +00:00
|
|
|
$this->filename = $filename;
|
|
|
|
$this->name = $name;
|
2007-12-10 22:13:48 +00:00
|
|
|
$this->title = $title;
|
2007-12-05 19:13:21 +00:00
|
|
|
$this->is_item = true;
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
function begin() {
|
|
|
|
return array(new BoltFrame($this));
|
|
|
|
}
|
2007-12-05 19:13:21 +00:00
|
|
|
function walk(&$iter, $incr, &$frac_done) {
|
2007-10-30 22:31:13 +00:00
|
|
|
echo "SHOULDN'T BE HERE\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class BoltLesson extends BoltItem {
|
2007-11-29 23:26:49 +00:00
|
|
|
function is_exercise() {
|
|
|
|
return false;
|
|
|
|
}
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class BoltExercise extends BoltItem {
|
2007-11-29 23:26:49 +00:00
|
|
|
function is_exercise() {
|
|
|
|
return true;
|
|
|
|
}
|
2007-10-30 22:31:13 +00:00
|
|
|
}
|
|
|
|
|
2007-12-04 22:16:37 +00:00
|
|
|
class BoltRefreshInterval {
|
|
|
|
public $intervals;
|
|
|
|
function __construct($i) {
|
|
|
|
$this->intervals = $i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class BoltReview {
|
|
|
|
public $score;
|
|
|
|
public $unit;
|
|
|
|
function __construct($s, $u) {
|
|
|
|
$this->score = $s;
|
|
|
|
$this->unit = $u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function name($n) {
|
2007-12-10 22:13:48 +00:00
|
|
|
return array('name', $n);
|
|
|
|
}
|
|
|
|
|
|
|
|
function title($n) {
|
|
|
|
return array('title', $n);
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function filename($n) {
|
2007-12-10 22:13:48 +00:00
|
|
|
return array('filename', $n);
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function refresh_interval($i) {
|
2007-12-10 22:13:48 +00:00
|
|
|
return array('refresh_interval', $i);
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function review($s, $u) {
|
|
|
|
return new BoltReview($s, $u);
|
|
|
|
}
|
|
|
|
|
2007-12-07 23:23:25 +00:00
|
|
|
function exercise_set() {
|
|
|
|
$args = func_get_args();
|
|
|
|
foreach ($args as $arg) {
|
|
|
|
return $arg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function refresh() {
|
|
|
|
}
|
|
|
|
|
2007-12-04 22:16:37 +00:00
|
|
|
function lesson() {
|
|
|
|
$name = "";
|
|
|
|
$file = "";
|
2007-12-10 22:13:48 +00:00
|
|
|
$title = "";
|
2007-12-04 22:16:37 +00:00
|
|
|
$args = func_get_args();
|
|
|
|
foreach ($args as $arg) {
|
2007-12-10 22:13:48 +00:00
|
|
|
if (is_array($arg)) {
|
|
|
|
switch ($arg[0]) {
|
|
|
|
case 'name': $name = $arg[1]; break;
|
|
|
|
case 'title': $title = $arg[1]; break;
|
|
|
|
case 'filename': $file = $arg[1]; break;
|
|
|
|
default: echo "Unrecognized array arg: ", $arg[0], "\n"; break;
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
2007-12-10 22:13:48 +00:00
|
|
|
} else {
|
|
|
|
echo "unprocessed arg of class ".get_class($arg);
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!$name) {
|
|
|
|
error_page("Missing name in lesson");
|
|
|
|
}
|
2007-12-10 22:13:48 +00:00
|
|
|
if (!$title) {
|
|
|
|
$title = $name;
|
|
|
|
}
|
|
|
|
if (!$file) {
|
2007-12-04 22:16:37 +00:00
|
|
|
error_page("Missing filename in lesson");
|
|
|
|
}
|
2007-12-10 22:13:48 +00:00
|
|
|
return new BoltLesson($name, $title, $file);
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function exercise() {
|
|
|
|
$name = "";
|
|
|
|
$file = "";
|
2007-12-10 22:13:48 +00:00
|
|
|
$title = "";
|
2007-12-04 22:16:37 +00:00
|
|
|
$args = func_get_args();
|
|
|
|
foreach ($args as $arg) {
|
2007-12-10 22:13:48 +00:00
|
|
|
if (is_array($arg)) {
|
|
|
|
switch ($arg[0]) {
|
|
|
|
case 'name': $name = $arg[1]; break;
|
|
|
|
case 'title': $title = $arg[1]; break;
|
|
|
|
case 'filename': $file = $arg[1]; break;
|
|
|
|
default: echo "Unrecognized array arg: ", $arg[0], "\n"; break;
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-12-10 22:13:48 +00:00
|
|
|
if (!$name) {
|
|
|
|
error_page("Missing name in lesson");
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
2007-12-10 22:13:48 +00:00
|
|
|
if (!$title) {
|
|
|
|
$title = $name;
|
|
|
|
}
|
|
|
|
if (!$file) {
|
|
|
|
error_page("Missing filename in lesson");
|
|
|
|
}
|
|
|
|
return new BoltExercise($name, $title, $file);
|
2007-12-04 22:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function sequence() {
|
2007-12-02 21:11:17 +00:00
|
|
|
$args = func_get_args();
|
|
|
|
$units = array();
|
|
|
|
$name = "";
|
|
|
|
foreach ($args as $arg) {
|
2007-12-10 22:13:48 +00:00
|
|
|
if (is_array($arg)) {
|
|
|
|
switch ($arg[0]) {
|
|
|
|
case 'name': $name = $arg[1]; break;
|
|
|
|
case 'title': $title = $arg[1]; break;
|
|
|
|
default: echo "Unrecognized array arg: ", $arg[0], "\n"; break;
|
|
|
|
}
|
|
|
|
} else if (is_object($arg)) {
|
2007-12-02 21:11:17 +00:00
|
|
|
if (is_subclass_of($arg, "BoltUnit")) {
|
|
|
|
$units[] = $arg;
|
|
|
|
} else {
|
|
|
|
echo "Unrecognized arg";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new BoltSequence($name, $units);
|
|
|
|
}
|
|
|
|
|
2007-10-30 22:31:13 +00:00
|
|
|
function enum_course($course) {
|
|
|
|
$iter = new BoltIter($course);
|
|
|
|
while (1) {
|
|
|
|
$x = $iter->at();
|
|
|
|
if (!$x) break;
|
|
|
|
echo "at: $x->url\n";
|
|
|
|
$x = $iter->next();
|
|
|
|
if (!$x) break;
|
|
|
|
echo "next: $x->filename\n";
|
|
|
|
}
|
|
|
|
echo "course over\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
?>
|