Bolt stuff

svn path=/trunk/boinc/; revision=16370
This commit is contained in:
David Anderson 2008-10-30 22:58:33 +00:00
parent 6caccb258b
commit 562d7ac2d7
7 changed files with 317 additions and 190 deletions

View File

@ -271,3 +271,20 @@ David Oct 30 2008
user/
bolt.css
bolt_sched.php
David Oct 30 2008
- moved snapshot code to its own file
- added link to questions in Map
- added "mode" to question table
db/
bolt_schema.sql
html/
inc/
bolt_snap.inc
bolt_util.inc
ops/
bolt_map.php
bolt_compare.php
user/
bolt_sched.php

View File

@ -145,6 +145,8 @@ create table bolt_question (
course_id integer not null,
name varchar(255) not null,
-- logical name of item where question was asked
mode integer not null,
-- distinguishes exercise show/answer
question text not null,
state integer not null,
-- whether question has been handled

229
html/inc/bolt_snap.inc Normal file
View File

@ -0,0 +1,229 @@
<?php
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
////// stuff related to snapshots
//
// There are 2 kinds of snapshots: "compare" and "map".
//
// A "compare snapshot" is a condensed representation of the results
// for a particular select/xset pair.
// Namely, it's an array whose elements contain
// bolt_user: the user
// xset_result: the user's first completion of the xset
// select_finished: the user's last completion of the select before this
//
// A "map snapshot" is:
// - an assoc array "views" mapping unit name to a list of views
// - an assoc array "results" mapping unit name to a list of results
// - an assoc array "xset_results" mapping unit name to a list of xset results
// - an assoc array "questions" mapping unit name to a list of questions
// - an assoc array "users" mapping user ID to user record
//
function compare_snapshot_filename($course_id, $select_name, $xset_name) {
@mkdir("../bolt_snap");
$x = urlencode($course_id."_".$select_name."_".$xset_name);
return "../bolt_snap/compare_snapshot_$x";
}
function write_compare_snapshot($course_id, $select_name, $xset_name, $dur) {
$now = time();
$start = $now - $dur*86400;
$xrs = BoltXsetResult::enum(
"course_id=$course_id and name='$xset_name' and create_time >= $start"
);
$sfs = BoltSelectFinished::enum(
"course_id=$course_id and name='$select_name' and end_time >= $start"
);
// make an array $a, keyed by user ID, of earliest xset result
//
$a = array();
foreach ($xrs as $xr) {
$uid = $xr->user_id;
if (!array_key_exists($uid, $a) || $xr->create_time < $a[$uid]->xr->create_time) {
$x = null;
$x->xr = $xr;
$a[$uid] = $x;
}
}
// now scan select finishes, and for each user find last one before xset
//
foreach ($sfs as $sf) {
$uid = $sf->user_id;
if (!array_key_exists($uid, $a)) {
echo "no xset result";
continue;
}
$x = $a[$uid];
$xr = $x->xr;
if ($sf->end_time > $xr->create_time) {
//echo "select finish is too late";
continue;
}
if (!isset($x->sf) || $sf->end_time > $x->sf->end_time) {
$x->sf = $sf;
$a[$uid] = $x;
}
}
// cull array elements for which we didn't find a select finish.
// Look up user records for other elements.
//
foreach ($a as $uid=>$x) {
if (!isset($x->sf)) {
unset($a[$uid]);
} else {
$user = BoincUser::lookup_id($uid);
BoltUser::lookup($user);
$x->user = $user;
$a[$uid] = $x;
}
}
$filename = compare_snapshot_filename($course_id, $select_name, $xset_name);
$f = fopen($filename, "w");
$s = null;
$s->recs = $a;
$s->dur = $dur;
$s->time = $now;
fwrite($f, serialize($s));
fclose($f);
return $s;
}
function read_compare_snapshot($course_id, $select_name, $xset_name) {
$filename = compare_snapshot_filename($course_id, $select_name, $xset_name);
$f = @fopen($filename, "r");
if (!$f) return null;
$x = fread($f, filesize($filename));
fclose($f);
return unserialize($x);
}
function map_snapshot_filename($course_id) {
return "../bolt_snap/map_snapshot_".$course_id;
}
function write_map_snapshot($course_id, $dur) {
$now = time();
$start = $now - $dur*86400;
$views = array();
$results = array();
$xset_results = array();
$users = array();
$questions = array();
$vs = BoltView::enum("course_id=$course_id and start_time>$start");
foreach ($vs as $v) {
if (array_key_exists($v->item_name, $views)) {
$x = $views[$v->item_name];
$x[] = $v;
$views[$v->item_name] = $x;
} else {
$views[$v->item_name] = array($v);
}
if (!array_key_exists($v->user_id, $users)) {
$user = BoincUser::lookup_id($v->user_id);
BoltUser::lookup($user);
$users[$v->user_id] = $user;
}
}
$rs = BoltResult::enum("course_id=$course_id and create_time>$start");
foreach ($rs as $r) {
if (array_key_exists($r->item_name, $results)) {
$x = $results[$r->item_name];
$x[] = $r;
$results[$r->item_name] = $x;
} else {
$results[$r->item_name] = array($r);
}
if (!array_key_exists($r->user_id, $users)) {
$user = BoincUser::lookup_id($r->user_id);
BoltUser::lookup($user);
$users[$r->user_id] = $user;
}
}
$xrs = BoltXsetResult::enum("course_id=$course_id and create_time>$start");
foreach ($xrs as $xr) {
if (array_key_exists($xr->name, $xset_results)) {
$x = $xset_results[$xr->name];
$x[] = $xr;
$xset_results[$xr->name] = $x;
} else {
$xset_results[$xr->name] = array($xr);
}
if (!array_key_exists($xr->user_id, $users)) {
$user = BoincUser::lookup_id($xr->user_id);
BoltUser::lookup($user);
$users[$xr->user_id] = $user;
}
}
$qs = BoltQuestion::enum("course_id=$course_id and create_time>$start");
foreach ($qs as $q) {
if (array_key_exists($q->name, $questions)) {
$x = $questions[$q->name];
$x[] = $q;
$questions[$q->name] = $x;
} else {
$questions[$q->name] = array($q);
}
if (!array_key_exists($q->user_id, $users)) {
$user = BoincUser::lookup_id($q->user_id);
BoltUser::lookup($user);
$users[$q->user_id] = $user;
}
}
$y = null;
$y->views = $views;
$y->results = $results;
$y->xset_results = $xset_results;
$y->users = $users;
$y->questions = $questions;
$y->dur = $dur;
$y->time = $now;
$filename = map_snapshot_filename($course_id);
$f = fopen($filename, "w");
fwrite($f, serialize($y));
fclose($f);
return $y;
}
function read_map_snapshot($course_id) {
$filename = map_snapshot_filename($course_id);
$f = @fopen($filename, "r");
if (!$f) return null;
$x = fread($f, filesize($filename));
fclose($f);
return unserialize($x);
}
?>

View File

@ -126,191 +126,6 @@ function request_info($user, $course) {
page_tail();
}
////// stuff related to snapshots
function compare_snapshot_filename($course_id, $select_name, $xset_name) {
@mkdir("../bolt_snap");
$x = urlencode($course_id."_".$select_name."_".$xset_name);
return "../bolt_snap/compare_snapshot_$x";
}
// a "snapshot" is a condensed representation of the results
// for a particular select/xset pair.
// Namely, it's an array whose elements contain
// bolt_user: the user
// xset_result: the user's first completion of the xset
// select_finished: the user's last completion of the select before this
function write_compare_snapshot($course_id, $select_name, $xset_name, $dur) {
$now = time();
$start = $now - $dur*86400;
$xrs = BoltXsetResult::enum(
"course_id=$course_id and name='$xset_name' and create_time >= $start"
);
$sfs = BoltSelectFinished::enum(
"course_id=$course_id and name='$select_name' and end_time >= $start"
);
// make an array $a, keyed by user ID, of earliest xset result
//
$a = array();
foreach ($xrs as $xr) {
$uid = $xr->user_id;
if (!array_key_exists($uid, $a) || $xr->create_time < $a[$uid]->xr->create_time) {
$x = null;
$x->xr = $xr;
$a[$uid] = $x;
}
}
// now scan select finishes, and for each user find last one before xset
//
foreach ($sfs as $sf) {
$uid = $sf->user_id;
if (!array_key_exists($uid, $a)) {
echo "no xset result";
continue;
}
$x = $a[$uid];
$xr = $x->xr;
if ($sf->end_time > $xr->create_time) {
//echo "select finish is too late";
continue;
}
if (!isset($x->sf) || $sf->end_time > $x->sf->end_time) {
$x->sf = $sf;
$a[$uid] = $x;
}
}
// cull array elements for which we didn't find a select finish.
// Look up user records for other elements.
//
foreach ($a as $uid=>$x) {
if (!isset($x->sf)) {
unset($a[$uid]);
} else {
$user = BoincUser::lookup_id($uid);
BoltUser::lookup($user);
$x->user = $user;
$a[$uid] = $x;
}
}
$filename = compare_snapshot_filename($course_id, $select_name, $xset_name);
$f = fopen($filename, "w");
$s = null;
$s->recs = $a;
$s->dur = $dur;
$s->time = $now;
fwrite($f, serialize($s));
fclose($f);
return $s;
}
function read_compare_snapshot($course_id, $select_name, $xset_name) {
$filename = compare_snapshot_filename($course_id, $select_name, $xset_name);
$f = @fopen($filename, "r");
if (!$f) return null;
$x = fread($f, filesize($filename));
fclose($f);
return unserialize($x);
}
function map_snapshot_filename($course_id) {
return "../bolt_snap/map_snapshot_".$course_id;
}
// a "map snapshot" is:
// - an assoc array "views" mapping unit name to a list of views
// - an assoc array "results" mapping unit name to a list of results
// - an assoc array "xset_results" mapping unit name to a list of xset results
// - an assoc array "users" mapping user ID to user record
//
function write_map_snapshot($course_id, $dur) {
$now = time();
$start = $now - $dur*86400;
$views = array();
$results = array();
$xset_results = array();
$users = array();
$vs = BoltView::enum("course_id=$course_id and start_time>$start");
foreach ($vs as $v) {
if (array_key_exists($v->item_name, $views)) {
$x = $views[$v->item_name];
$x[] = $v;
$views[$v->item_name] = $x;
} else {
$views[$v->item_name] = array($v);
}
if (!array_key_exists($v->user_id, $users)) {
$user = BoincUser::lookup_id($v->user_id);
BoltUser::lookup($user);
$users[$v->user_id] = $user;
}
}
$rs = BoltResult::enum("course_id=$course_id and create_time>$start");
foreach ($rs as $r) {
if (array_key_exists($r->item_name, $results)) {
$x = $results[$r->item_name];
$x[] = $r;
$results[$r->item_name] = $x;
} else {
$results[$r->item_name] = array($r);
}
if (!array_key_exists($r->user_id, $users)) {
$user = BoincUser::lookup_id($r->user_id);
BoltUser::lookup($user);
$users[$r->user_id] = $user;
}
}
$xrs = BoltXsetResult::enum("course_id=$course_id and create_time>$start");
foreach ($xrs as $xr) {
if (array_key_exists($xr->name, $xset_results)) {
$x = $xset_results[$xr->name];
$x[] = $xr;
$xset_results[$xr->name] = $x;
} else {
$xset_results[$xr->name] = array($xr);
}
if (!array_key_exists($xr->user_id, $users)) {
$user = BoincUser::lookup_id($xr->user_id);
BoltUser::lookup($user);
$users[$xr->user_id] = $user;
}
}
$y = null;
$y->views = $views;
$y->results = $results;
$y->xset_results = $xset_results;
$y->users = $users;
$y->dur = $dur;
$y->time = $now;
$filename = map_snapshot_filename($course_id);
$f = fopen($filename, "w");
fwrite($f, serialize($y));
fclose($f);
return $y;
}
function read_map_snapshot($course_id) {
$filename = map_snapshot_filename($course_id);
$f = @fopen($filename, "r");
if (!$f) return null;
$x = fread($f, filesize($filename));
fclose($f);
return unserialize($x);
}
////// Statistics
// compute the mean and stdev of an array

View File

@ -33,6 +33,7 @@ require_once("../inc/bolt_db.inc");
require_once("../inc/bolt_util.inc");
require_once("../inc/bolt.inc");
require_once("../inc/bolt_cat.inc");
require_once("../inc/bolt_snap.inc");
function compare_case(
$title, $select_unit, $snap, $filter, $filter_cat, $breakdown, $breakdown_cat

View File

@ -49,6 +49,7 @@ require_once("../inc/bolt_db.inc");
require_once("../inc/bolt_cat.inc");
require_once("../inc/bolt_util.inc");
require_once("../inc/bolt.inc");
require_once("../inc/bolt_snap.inc");
// the following are to minimize argument passing
@ -108,7 +109,8 @@ function spaces($level) {
return $x;
}
// used to filter arrays of views or xset_results
// filter arrays of anything that has a user_id field
// (view, xset_result, question)
//
function filter_array($array) {
global $snap, $filter, $filter_cat, $breakdown, $breakdown_cat;
@ -171,6 +173,20 @@ function outcomes($views) {
return $x;
}
function get_nquestions($unit, $mode) {
global $snap;
if (array_key_exists($unit->name, $snap->questions)) {
$a = filter_array($snap->questions[$unit->name]);
$n = 0;
foreach ($a as $q) {
if ($q->mode == $mode) $n++;
}
return $n;
}
return 0;
}
function get_views($unit, $mode) {
global $snap;
@ -217,7 +233,7 @@ $rownum = 0;
function show_unit_row($unit, $class, $level, $is_answer) {
global $breakdown, $breakdown_cat;
global $rownum;
global $rownum, $course_id;
$a = $is_answer?" (answer)":"";
$j = ($rownum++)%2;
@ -243,21 +259,36 @@ function show_unit_row($unit, $class, $level, $is_answer) {
}
switch ($class) {
case "BoltLesson":
$views = get_views($unit, BOLT_MODE_LESSON);
$mode = BOLT_MODE_LESSON;
$views = get_views($unit, $mode);
$n = count($views);
$out = outcomes($views);
$t = avg_time($views);
echo "<td>$n</td>";
$n = get_nquestions($unit, $mode);
if ($n) {
echo "<td><a href=bolt_map.php?action=questions&course_id=$course_id&name=$unit->name&mode=$mode>$n</a></td>\n";
} else {
echo "<td>0</td>\n";
}
echo outcome_graph($out, 200);
echo empty_cell();
echo time_graph($t, 200);
break;
case "BoltExercise":
$views = get_views($unit, $is_answer?BOLT_MODE_ANSWER:BOLT_MODE_SHOW);
$mode = $is_answer?BOLT_MODE_ANSWER:BOLT_MODE_SHOW;
$views = get_views($unit, $mode);
$n = count($views);
$out = outcomes($views);
$t = avg_time($views);
echo "<td>$n</td>";
$n = get_nquestions($unit, $mode);
if ($n) {
echo "<td><a href=bolt_map.php?action=questions&course_id=$course_id&name=$unit->name&mode=$mode>$n</a></td>\n";
} else {
echo "<td>0</td>\n";
}
echo outcome_graph($out, 200);
if ($is_answer) {
echo empty_cell();
@ -273,6 +304,7 @@ function show_unit_row($unit, $class, $level, $is_answer) {
$n = count($xr);
echo "<td>$n</td>";
echo empty_cell();
echo empty_cell();
$score = avg_score($xr);
echo score_graph($score, 200);
echo empty_cell();
@ -282,6 +314,7 @@ function show_unit_row($unit, $class, $level, $is_answer) {
echo empty_cell();
echo empty_cell();
echo empty_cell();
echo empty_cell();
}
echo "</tr>\n";
}
@ -369,6 +402,7 @@ function show_map() {
}
echo "
<th>Views</th>
<th>Questions</th>
<th>Outcome<br>
<span class=green>Next</span>
<span class=yellow>Back</span>
@ -398,6 +432,32 @@ function show_map() {
page_tail();
}
function show_questions() {
$name = get_str('name');
global $course_id;
$snap = read_map_snapshot($course_id);
$qs = $snap->questions[$name];
page_head("Questions about $name");
start_table();
echo "<tr>
<th>When</th>
<th>Who</th>
<th>Question</th>
</tr>
";
foreach ($qs as $q) {
$user = $snap->users[$q->user_id];
echo "<tr>
<td>".time_str($q->create_time)."</td>
<td><a href=student>$user->name</td>
<td>$q->question</td>
</tr>
";
}
end_table();
page_tail();
}
$course_id = get_int('course_id');
$course = BoltCourse::lookup_id($course_id);
if (!$course) error_page("no course");
@ -414,6 +474,9 @@ case "snap_action":
case "map":
show_map();
break;
case "questions":
show_questions();
break;
default:
error_page("Unknown action $action");
}

View File

@ -642,7 +642,7 @@ case 'question':
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)");
BoltQuestion::insert("(create_time, user_id, course_id, name, mode, question, state) values ($now, $user->id, $course->id, '$view->item_name', $view->mode, '$question', 0)");
page_header();
echo "
Thanks; we have recorded your question.