From 19dfe0ee7165e4fa82deca1df762b988da83cdd0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 23 Oct 2008 20:35:39 +0000 Subject: [PATCH] - Add callback mechanism for exercises and exercise sets - Default count for exercise sets is N, not 1 svn path=/trunk/boinc/; revision=16305 --- bolt_checkin_notes.txt | 4 ++++ db/bolt_schema.sql | 2 +- html/inc/bolt.inc | 14 +++++++++++++- html/inc/bolt_ex.inc | 2 +- html/inc/bolt_sched.inc | 4 ++-- html/inc/bolt_xset.inc | 21 +++++++++++++++++---- html/user/bolt_sched.php | 35 +++++++++++++++++++++++++++-------- 7 files changed, 65 insertions(+), 17 deletions(-) diff --git a/bolt_checkin_notes.txt b/bolt_checkin_notes.txt index 28c8639890..c1b8ef3d81 100644 --- a/bolt_checkin_notes.txt +++ b/bolt_checkin_notes.txt @@ -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 diff --git a/db/bolt_schema.sql b/db/bolt_schema.sql index 63c35f5c61..c5419c50f4 100644 --- a/db/bolt_schema.sql +++ b/db/bolt_schema.sql @@ -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) ); diff --git a/html/inc/bolt.inc b/html/inc/bolt.inc index 46b61298f0..5c0ba3019a 100644 --- a/html/inc/bolt.inc +++ b/html/inc/bolt.inc @@ -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'); diff --git a/html/inc/bolt_ex.inc b/html/inc/bolt_ex.inc index 054a48ad30..294daca346 100644 --- a/html/inc/bolt_ex.inc +++ b/html/inc/bolt_ex.inc @@ -156,7 +156,7 @@ function image_rect($img, $rect) { switch ($bolt_ex_mode) { case BOLT_MODE_SHOW: - echo ""; break; } } diff --git a/html/inc/bolt_sched.inc b/html/inc/bolt_sched.inc index 6345d6ac59..7f3c46b687 100644 --- a/html/inc/bolt_sched.inc +++ b/html/inc/bolt_sched.inc @@ -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 diff --git a/html/inc/bolt_xset.inc b/html/inc/bolt_xset.inc index 547f5411c5..6cebd13fa1 100644 --- a/html/inc/bolt_xset.inc +++ b/html/inc/bolt_xset.inc @@ -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 + ); } ?> diff --git a/html/user/bolt_sched.php b/html/user/bolt_sched.php index 60078c4d0b..29e522fa32 100644 --- a/html/user/bolt_sched.php +++ b/html/user/bolt_sched.php @@ -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)");