- Initial checkin for Bolt

svn path=/trunk/boinc/; revision=13998
This commit is contained in:
David Anderson 2007-10-30 22:31:13 +00:00
parent 105d6aaae3
commit 26138742c6
11 changed files with 481 additions and 4 deletions

View File

@ -10182,3 +10182,22 @@ David 30 Oct 2007
forum_thread.php
account_finish_action.php
account_finish.php
David 30 Oct 2007
- initial checkin for Bolt
db/
bolt_constraints.sql
bolt_db.sql
html/
inc/
bolt.inc
bolt_db.inc
bolt_ex.inc
bossa_db.inc
ops/
bolt_setup_sample.php
user/
bolt.php
bolt_course_sample.php
bolt_sched.php

8
db/bolt_constraints.sql Normal file
View File

@ -0,0 +1,8 @@
alter table bolt_course
add unique(name);
alter table bolt_enrollment
add unique(user_id, course_id);
alter table bolt_view
add index bv_cs(course_id, start_time);

26
db/bolt_schema.sql Normal file
View File

@ -0,0 +1,26 @@
create table bolt_course (
id integer not null auto_increment,
create_time integer not null,
short_name varchar(255) not null,
name varchar(255) not null,
description text not null,
doc_file varchar(255) not null,
primary key (id)
);
create table bolt_enrollment (
create_time integer not null,
user_id integer not null,
course_id integer not null,
state text not null
);
create table bolt_view (
id integer not null auto_increment,
user_id integer not null,
course_id integer not null,
item_name varchar(255) not null,
start_time integer not null,
end_time integer not null,
primary key (id)
);

185
html/inc/bolt.inc Normal file
View File

@ -0,0 +1,185 @@
<?php
error_reporting(E_ALL);
ini_set('display_startup_errors', true);
// 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;
// however, such units should be identical.
class BoltFrame {
public $state;
// a data structure that's specific to the unit type,
// e.g. a loop counter
// Typically this includes the logical name of the current child.
// Normally this is implied by the state;
// however, it may differ if the course structure has changed.
// In general the unit should restart in this case
function __construct($state=null) {
$this->state = $state;
}
}
abstract class BoltUnit {
public $name; // logical name.
abstract function walk($old_stack, &$new_stack, $next, &$item);
// multi-purpose function for traversing a course.
// if $old_stack is null
// set up initial state for this unit.
// append frames to $new_stack for this unit and descendants
// $next is ignored
// $item is the initial item
// return is ignored
// else
// The first frame of $old_stack is for this unit.
// Check for name mismatch (if changed course).
// Append frames to $new_stack for this unit and descendants
// if $next, the bottom-level non-item unit should increment.
// return value: true if the caller should increment
abstract function is_item();
}
// An iterator represents a user's position in a course.
// It is stored in the database, and the course may change underneath it.
//
class BoltIter {
public $stack; // array of BoltFrame
public $top;
// point to the start of a course; set up stack.
//
function __construct($top) {
$this->top = $top;
$this->stack = null;
}
// get current item
//
function at() {
$new_stack = array();
$this->top->walk($this->stack, $new_stack, false, $item);
$this->stack = $new_stack;
return $item;
}
// move to the next item (and return it)
// return true if we're off the end
//
function next() {
$item = null;
$new_stack = array();
$this->top->walk($this->stack, $new_stack, true, $item);
$this->stack = $new_stack;
return $item;
}
}
class BoltSeq extends BoltUnit {
public $units;
function __construct($n, $u) {
$this->name = $n;
$this->units = $u;
}
function walk($old_stack, &$new_stack, $next, &$item) {
//echo "call to walk() for $this->name: next: $next\n";
if ($old_stack) {
//echo "old stack: \n";
//var_dump($old_stack);
//echo "------------\n";
$frame = $old_stack[0];
$state = $frame->state;
$i = $state->i;
$restart = false;
if ($i >= count($this->units)) {
echo "index too large - restarting\n";
$restart = true;
}
$child = $this->units[$i];
if ($state->child_name != $child->name) {
echo "bad name - restarting\n";
$restart = true;
}
if (!$restart) {
if ($next) {
$inc = false;
if ($child->is_item()) {
$inc = true;
} else {
array_shift($old_stack);
$inc = $child->walk($old_stack, $new_stack, true, &$item);
}
if ($inc) {
$i++;
if ($i == count($this->units)) {
return true;
}
}
}
}
} else {
$restart = true;
}
if ($restart) {
$i = 0;
}
$child = $this->units[$i];
$state->i = $i;
$state->child_name = $child->name;
$frame = new BoltFrame($state);
$new_stack[] = $frame;
if ($child->is_item()) {
$item = $child;
} else {
$child->walk(null, $new_stack, false, $item);
}
}
function is_item() {
return false;
}
}
class BoltItem extends BoltUnit {
public $filename;
function __construct($name, $filename) {
$this->filename = $filename;
$this->name = $name;
}
function begin() {
return array(new BoltFrame($this));
}
function unit_list() {
return array(&$this);
}
function is_item() {
return true;
}
function walk($old_stack, &$new_stack, $next, &$item) {
echo "SHOULDN'T BE HERE\n";
}
}
class BoltLesson extends BoltItem {
}
class BoltExercise extends BoltItem {
}
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";
}
?>

86
html/inc/bolt_db.inc Normal file
View File

@ -0,0 +1,86 @@
<?php
require_once("../inc/db_conn.inc");
require_once("../inc/util.inc");
class BoltDb extends DbConn {
public static $instance;
static function get() {
if (!isset($instance)) {
$config = get_config();
$user = parse_config($config, '<bolt_db_user>');
$passwd = parse_config($config, '<bolt_db_passwd>');
$host = parse_config($config, '<bolt_db_host>');
$name = parse_config($config, '<bolt_db_name>');
if ($host == null) {
$host = "localhost";
}
$instance = new DbConn();
$retval = $instance->init_conn($user, $passwd, $host, $name);
if (!$retval) return null;
}
return $instance;
}
}
class BoltCourse {
function insert() {
$db = BoltDb::get();
$now = time();
$query = "insert into DBNAME.bolt_course (create_time, short_name, name, description, doc_file) values ($now, '$this->short_name', '$this->name', '$this->description', '$this->doc_file')";
$result = $db->do_query($query);
if (!$result) return false;
$this->id = $db->insert_id();
return true;
}
static function lookup_id($id) {
$db = BoltDb::get();
return $db->lookup_id($id, 'bolt_course', 'BoltCourse');
}
static function enum() {
$db = BoltDb::get();
return $db->enum('bolt_course', 'BoltCourse');
}
}
class BoltEnrollment {
function insert() {
$db = BoltDb::get();
$now = time();
$query = "insert into DBNAME.bolt_enrollment (create_time, user_id, course_id) values ($now, $this->user_id, $this->course_id)";
return $db->do_query($query);
}
function lookup($userid, $courseid) {
$db = BoltDb::get();
return $db->lookup('bolt_enrollment', 'BoltEnrollment', "user_id=$userid and course_id=$courseid");
}
function update_aux($clause) {
$db = BoltDb::get();
$db->update_aux('bolt_enrollment', $clause);
}
}
class BoltView {
function insert() {
$db = BoltDb::get();
$now = time();
$query = "insert into DBNAME.bolt_view (user_id, course_id, item_name, start_time) values ($this->user_id, $this->course_id, '$this->item_name', $now)";
$result = $db->do_query($query);
if (!$result) return false;
$this->id = $db->insert_id();
return true;
}
static function lookup_id($id) {
$db = BoltDb::get();
return $db->lookup_id($id, 'bolt_view', 'BoltView');
}
function update($clause) {
$db = BoltDb::get();
$db->update($this, 'bolt_view', $clause);
}
}
?>

8
html/inc/bolt_ex.inc Normal file
View File

@ -0,0 +1,8 @@
<?php
function bolt_exclusive_choice($choices) {
foreach ($choices as $choice) {
echo "<br><input name=q type=radio> $choice";
}
}
?>

View File

@ -1,17 +1,23 @@
<?php
require_once("../inc/db_conn.inc");
require_once("../inc/util.inc");
class BossaDb extends DbConn {
public static $instance;
static function get() {
if (!isset($instance)) {
$config = get_config();
$user = parse_config($config, '<bossa_db_user>');
$passwd = parse_config($config, '<bossa_db_passwd>');
$host = parse_config($config, '<bossa_db_host>');
$name = parse_config($config, '<bossa_db_name>');
if ($host == null) {
$host = "localhost";
}
$instance = new DbConn();
$retval = $instance->init_conn(
"<bossa_db_user>", "<bossa_db_passwd>",
"<bossa_db_host>", "<bossa_db_name>"
);
$retval = $instance->init_conn($user, $passwd, $host, $name);
if (!$retval) return null;
}
return $instance;

View File

@ -0,0 +1,17 @@
<?php
require_once("../inc/bolt_db.inc");
$c = new BoltCourse();
$c->short_name = 'test_course';
$c->name = 'A test course';
$c->description = 'This course is a demonstration of Bolt';
$c->doc_file = 'bolt_course_sample.php';
if ($c->insert()) {
echo "all done\n";
} else {
echo "database error\n";
}
?>

25
html/user/bolt.php Normal file
View File

@ -0,0 +1,25 @@
<?php
require_once("../inc/bolt_db.inc");
require_once("../inc/util.inc");
page_head("Courses");
$user = get_logged_in_user(true);
$courses = BoltCourse::enum();
foreach ($courses as $course) {
$e = BoltEnrollment::lookup($user->id, $course->id);
echo "$course->name <a href=bolt_sched.php?course_id=$course->id&action=start>start</a>
";
if ($e) {
echo "<a href=bolt_sched.php?course_id=$course->id>resume</a>
";
}
echo "
<dd>$course->description
";
}
page_tail();
?>

View File

@ -0,0 +1,18 @@
<?php
return new BoltSeq(
'course',
array(
new BoltLesson('lesson 1', 'bolt_sample_lesson1.php'),
new BoltLesson('lesson 2', 'bolt_sample_lesson2.php'),
new BoltExercise('exercise 1', 'bolt_sample_exercise1.php'),
new BoltSeq(
'inner seq',
array(
new BoltLesson('lesson 3', 'bolt_sample_lesson3.php')
)
)
)
);
?>

79
html/user/bolt_sched.php Normal file
View File

@ -0,0 +1,79 @@
<?php
// Bolt scheduler. POST args:
// course_id: course ID
// action:
// 'show' or none: show current (or first) item
// 'next': go to next lesson
// answers:
// JSON represenation of exercise answers
require_once("../inc/bolt.inc");
require_once("../inc/bolt_db.inc");
require_once("../inc/util.inc");
$user = get_logged_in_user();
$course_id = get_int('course_id');
$view_id = get_int('view_id', true);
$action = get_str('action', true);
$course = BoltCourse::lookup_id($course_id);
if (!$course) {
error_page("no such course");
}
$course_doc = require_once($course->doc_file);
if ($view_id) {
$view = BoltView::lookup_id($view_id);
if ($view && $view->user_id == $user->id && !$view->end_time) {
$now = time();
$view->update("end_time=$now");
}
}
$e = BoltEnrollment::lookup($user->id, $course_id);
if ($e) {
$iter = new BoltIter($course_doc);
$iter->stack = json_decode($e->state);
if ($action == 'next') {
$item = $iter->next();
$state = json_encode($iter->stack);
$e->update_aux("state='$state' where user_id=$user->id and course_id=$course_id");
} else if ($action == 'start') {
$iter->stack = null;
$item = $iter->at();
$state = json_encode($iter->stack);
$e->update_aux("state='$state' where user_id=$user->id and course_id=$course_id");
} else {
$item = $iter->at();
}
} else {
$iter = new BoltIter($course_doc);
$item = $iter->at();
$e = new BoltEnrollment($course);
$e->user_id = $user->id;
$e->course_id = $course_id;
$e->state = json_encode($iter->stack);
$e->insert();
}
if (!$item) {
page_head("Done with course");
echo "All done!";
page_tail();
exit();
}
$view = new BoltView();
$view->user_id = $user->id;
$view->course_id = $course_id;
$view->item_name = $item->name;
$view->insert();
require_once($item->filename);
echo "<p><a href=bolt_sched.php?course_id=$course_id&action=next&view_id=$view->id>Next</a>";
?>