- Add callback mechanism for exercises and exercise sets

- Default count for exercise sets is N, not 1

svn path=/trunk/boinc/; revision=16305
This commit is contained in:
David Anderson 2008-10-23 20:35:39 +00:00
parent 80c248fc38
commit 19dfe0ee71
7 changed files with 65 additions and 17 deletions

View File

@ -230,3 +230,7 @@ David Oct 20 2008
bolt_xset.inc
user/
bolt_sched.php
David Oct 23 2008
- Add callback mechanism for exercises and exercise sets
- Default count for exercise sets is N, not 1

View File

@ -4,7 +4,7 @@ create table bolt_user (
sex tinyint not null,
flags integer not null,
attrs text not null,
-- project-defined. Use JSON.
-- project-defined. Use serialized PHP object
primary key (user_id)
);

View File

@ -75,6 +75,12 @@ class BoltLesson extends BoltItem {
}
class BoltExercise extends BoltItem {
public $callback;
// called as func($student, $score, $query_string) after scoring
function __construct($filename, $title, $callback) {
parent::__construct($filename, $title);
$this->callback = $callback;
}
function is_exercise() {
return true;
}
@ -246,6 +252,10 @@ function filename($n) {
return array('filename', $n);
}
function callback($n) {
return array('callback', $n);
}
function lesson() {
$filename = "";
$title = "";
@ -274,11 +284,13 @@ function exercise() {
$filename = "";
$title = "";
$args = func_get_args();
$callback = null;
foreach ($args as $arg) {
if (is_array($arg)) {
switch ($arg[0]) {
case 'title': $title = $arg[1]; break;
case 'filename': $filename = $arg[1]; break;
case 'callback': $callback = $arg[1]; break;
default: echo "Unrecognized exercise parameter: ", $arg[0], "\n"; break;
}
}
@ -289,7 +301,7 @@ function exercise() {
if (!$filename) {
error_page("Missing filename in lesson");
}
return new BoltExercise($filename, $title);
return new BoltExercise($filename, $title, $callback);
}
require_once('../inc/bolt_seq.inc');

View File

@ -156,7 +156,7 @@ function image_rect($img, $rect) {
switch ($bolt_ex_mode) {
case BOLT_MODE_SHOW:
echo "<img src=$img";
echo "<img src=$img>";
break;
}
}

View File

@ -43,11 +43,11 @@ class BoltIter {
}
function decode_state($encoded_state) {
$this->state = json_decode($encoded_state, true);
$this->state = unserialize($encoded_state);
}
function encode_state() {
return json_encode($this->state);
return serialize($this->state);
}
// get current item and fraction done

View File

@ -19,10 +19,14 @@
class BoltExerciseSet extends BoltRandom {
public $repeats;
public $refresh;
function __construct($name, $units, $number, $repeats, $refresh) {
public $callback;
function __construct(
$name, $units, $number, $repeats, $refresh, $callback
) {
parent::__construct($name, $units, $number);
$this->repeats = $repeats;
$this->refresh = $refresh;
$this->callback = $callback;
}
// called when an exercise in this set has just been graded.
@ -33,7 +37,9 @@ class BoltExerciseSet extends BoltRandom {
// - repeat now
// - next
//
function xset_callback(&$iter, $score, $view_id, &$avg_score, &$repeat) {
function xset_record_score(
&$iter, $score, $view_id, &$avg_score, &$repeat
) {
global $course;
global $user;
@ -159,14 +165,16 @@ function exercise_set() {
$units = array();
$repeats = array();
$refresh = null;
$callback = null;
$name = "";
$number = 1;
$number = 0;
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;
case 'callback': $callback = $arg[1]; break;
default: echo "Unrecognized array arg: ", $arg[0], "\n"; break;
}
} else if (is_object($arg)) {
@ -179,9 +187,14 @@ function exercise_set() {
} else {
echo "Can't include object of type ".get_class($arg)." within an exercise set.";
}
} else {
echo "Unexpected arg to exercise_set(): "; print_r($arg);
}
}
return new BoltExerciseSet($name, $units, $number, $repeats, $refresh);
if ($number == 0) $number = count($units);
return new BoltExerciseSet(
$name, $units, $number, $repeats, $refresh, $callback
);
}
?>

View File

@ -386,7 +386,7 @@ case 'update_info':
break;
case 'prev':
$view = finalize_view($view_id, BOLT_ACTION_PREV);
debug_show_state(json_decode($view->state, true), "Initial");
debug_show_state(unserialize($view->state), "Initial");
if ($view->prev_view_id) {
$view = BoltView::lookup_id($view->prev_view_id);
$iter = new BoltIter($course_doc);
@ -408,7 +408,7 @@ case 'prev':
break;
case 'next': // "next" button in lesson or exercise answer page
$view = finalize_view($view_id, BOLT_ACTION_NEXT);
debug_show_state(json_decode($view->state, true), "Initial");
debug_show_state(unserialize($view->state), "Initial");
$iter = new BoltIter($course_doc);
$iter->decode_state($view->state);
@ -444,7 +444,7 @@ case 'next': // "next" button in lesson or exercise answer page
break;
case 'answer': // submit answer in exercise
$view = finalize_view($view_id, BOLT_ACTION_SUBMIT);
debug_show_state(json_decode($view->state, true), "Initial");
debug_show_state(unserialize($view->state), "Initial");
$iter = new BoltIter($course_doc);
$iter->decode_state($view->state);
$iter->at();
@ -467,12 +467,22 @@ case 'answer': // submit answer in exercise
$bolt_ex_score = 0;
$bolt_query_string = $item->query_string;
srand($view_id);
ob_start(); // turn on output buffering
ob_start(); // buffer output to avoid showing exercise text
require($item->filename);
ob_end_clean();
$bolt_ex_score /= $bolt_ex_index;
if ($item->callback) {
$user->bolt->attrs = unserialize($user->bolt->attrs);
call_user_func(
$item->callback, $user, $bolt_ex_score, $bolt_ex_query_string
);
$user->bolt->attrs = serialize($user->bolt->attrs);
$attrs = $user->bolt->attrs;
$user->bolt->update("attrs='$attrs'");
}
// make a record of the result
$qs = BoltDb::escape_string($_SERVER['QUERY_STRING']);
@ -488,11 +498,20 @@ case 'answer': // submit answer in exercise
$repeat = null;
$xset = $iter->xset;
if ($xset) {
$is_last = $xset->xset_callback($iter, $bolt_ex_score, $view->id, $avg_score, $repeat);
$is_last = $xset->xset_record_score(
$iter, $bolt_ex_score, $view->id, $avg_score, $repeat
);
if ($repeat) $repeat->avg_score = $avg_score;
if ($is_last) {
// if the exercise set if finished, make or update DB records
//
if ($xset->callback) {
$user->bolt->attrs = unserialize($user->bolt->attrs);
call_user_func($xset->callback, $user, $avg_score);
$user->bolt->attrs = serialize($user->bolt->attrs);
$attrs = $user->bolt->attrs;
$user->bolt->update("attrs='$attrs'");
}
$now = time();
$id = BoltXsetResult::insert("(create_time, user_id, course_id, name, score, view_id) values ($now, $user->id, $course->id, '$xset->name', $avg_score, $view_id)");
$refresh_intervals = $xset->refresh;
@ -545,7 +564,7 @@ case 'review':
// user chose to do review then repeat an exercise set
//
$view = finalize_view($view_id, BOLT_ACTION_REVIEW);
debug_show_state(json_decode($view->state, true), "Initial");
debug_show_state(unserialize($view->state), "Initial");
$iter = new BoltIter($course_doc);
$iter->decode_state($view->state);
$iter->at();
@ -567,7 +586,7 @@ case 'repeat':
// user chose to repeat an exercise set
//
$view = finalize_view($view_id, BOLT_ACTION_REPEAT);
debug_show_state(json_decode($view->state, true), "Initial");
debug_show_state(unserialize($view->state), "Initial");
$iter = new BoltIter($course_doc);
$iter->decode_state($view->state);
$iter->at();
@ -629,7 +648,7 @@ case 'resume':
break;
case 'question':
$view = finalize_view($view_id, BOLT_ACTION_QUESTION);
debug_show_state(json_decode($view->state, true), "Initial");
debug_show_state(unserialize($view->state), "Initial");
$now = time();
$question = BoltDb::escape_string(get_str('question'));
BoltQuestion::insert("(create_time, user_id, course_id, name, question, state) values ($now, $user->id, $course->id, '$view->item_name', '$question', 0)");