- Initial checkin of Bossa code

svn path=/trunk/boinc/; revision=14716
This commit is contained in:
David Anderson 2008-02-11 23:38:31 +00:00
parent a323eba986
commit 51286a03dc
13 changed files with 312 additions and 57 deletions

View File

@ -2,11 +2,16 @@ create table bossa_app (
id integer not null auto_increment,
create_time integer not null,
name varchar(255) not null,
user_friendly_name varchar(255) not null,
description varchar(255) not null,
long_jobs tinyint not null,
start_url varchar(255) not null,
deprecated tinyint not null,
display_script varchar(255) not null,
backend_script varchar(255) not null,
hidden tinyint not null,
min_conf_sum double not null,
min_conf_frac double not null,
max_instances integer not null,
info text,
-- app-specific info, JSON
primary key(id)
);
@ -19,12 +24,10 @@ create table bossa_job (
batch integer not null,
time_estimate integer not null,
time_limit integer not null,
more_needed tinyint not null,
npending smallint not null,
nsuccess smallint not null,
nsuccess_needed smallint not null,
transition_time double not null,
nneeded integer not null,
primary key(id)
);
) engine=InnoDB;
create table bossa_job_inst (
id integer not null auto_increment,
@ -34,7 +37,7 @@ create table bossa_job_inst (
finish_time integer not null,
info text,
primary key(id)
);
) engine=InnoDB;
create table bossa_app_user (
app_id integer not null,

View File

@ -1,6 +1,11 @@
<?
$project_news = array(
array("Feb 11, 2008",
"Bulgarian BOINC users:
find forums and information in your language at
<a href=http://www.boinc-bulgaria.net>BOINC Bulgaria</a>."
),
array("Feb 7, 2008",
"<a href=http://biology.polytechnique.fr/proteinsathome>Proteins@Home</a>
has resumed operations.

View File

@ -170,6 +170,9 @@ language("Belgium (Dutch/French/English)", array(
site("http://www.boinc.be", "www.boinc.be"),
site("http://icewolves.plid.be", "IceWolves"),
));
language("Bulgarian", array(
site("http://www.boinc-bulgaria.net", "BOINC Bulgaria")
));
language("Catalan", array(
site("http://www.boinc.cat", "BOINC.cat"),
));

View File

@ -15,6 +15,9 @@ class Bossa {
if ($bji->user_id != $user->id) {
error_page("Bad user ID");
}
if ($bji->finish_time) {
error_page("You already finished this job");
}
$bj = BossaJob::lookup_id($bji->job_id);
if (!$bj) {
error_page("No such job");

View File

@ -22,18 +22,16 @@ class BossaDb extends DbConn {
}
return $instance;
}
static function escape_string($string) {
$db = self::get();
return $db->base_escape_string($string);
}
}
class BossaApp {
function insert() {
function insert($clause) {
$db = BossaDb::get();
if (!isset($this->long_jobs)) $this->long_jobs = 0;
$now = time();
$query = "insert into DBNAME.bossa_app (create_time, name, user_friendly_name, long_jobs, start_url) values ($now, '$this->name', '$this->user_friendly_name', $this->long_jobs, '$this->start_url')";
$result = $db->do_query($query);
if (!$result) return false;
$this->id = $db->insert_id();
return true;
return $db->insert('bossa_app', $clause);
}
static function lookup_name($name) {
@ -56,7 +54,7 @@ class BossaJob {
function insert() {
$db = BossaDb::get();
$now = time();
$query = "insert into DBNAME.bossa_job (create_time, name, app_id, info, batch, time_estimate, time_limit, more_needed, npending, nsuccess, nsuccess_needed) values ($now, '$this->name', $this->app_id, '$this->info', $this->batch, $this->time_estimate, $this->time_limit, 1, 0, 0, $this->nsuccess_needed)";
$query = "insert into DBNAME.bossa_job (create_time, name, app_id, info, batch, time_estimate, time_limit, nneeded) values ($now, '$this->name', $this->app_id, '$this->info', $this->batch, $this->time_estimate, $this->time_limit, $this->nneeded)";
$result = $db->do_query($query);
if (!$result) {
echo "$query\n";
@ -114,8 +112,9 @@ class BossaJobInst {
// this query skips jobs for which this user
// has already been assigned an instance
//
// TODO: put the following in a transaction
$db = BossaDb::get();
$query = "select bossa_job.* from DBNAME.bossa_job left join DBNAME.bossa_job_inst on bossa_job_inst.job_id = bossa_job.id where bossa_job.more_needed<>0 and bossa_job_inst.user_id is null limit 1";
$query = "select bossa_job.* from DBNAME.bossa_job left join DBNAME.bossa_job_inst on bossa_job_inst.job_id = bossa_job.id where bossa_job.nneeded>0 and bossa_job_inst.user_id is null limit 1";
$result = $db->do_query($query);
if (!$result) return null;
$job = mysql_fetch_object($result, 'BossaJob');
@ -130,6 +129,8 @@ class BossaJobInst {
echo mysql_error();
return null;
}
$job->update("nneeded=nneeded-1");
return $ji;
}

View File

@ -0,0 +1,40 @@
<?php
// two results are compatible if neither found an ellipse,
// or they both did and centers are within 20 pixels
//
function compare($r1, $r2) {
if ($r1->have_ellipse) {
if ($r2->have_ellipse) {
$dx = ($r1->cx - $r2->cx);
$dy = ($r1->cy - $r2->cy);
$dsq = $dx*$dx + $dy*$dy;
return ($dsq < 400);
} else return false;
} else {
return !$r2->have_ellipse;
}
}
// handle a completed job with the given consensus set
//
function handle_consensus($bj, $c) {
$res = $c[0];
if ($res->have_ellipse) {
$res->cx = 0;
$res->cy = 0;
foreach ($c as $r) {
$res->cx += $r->cx;
$res->cy += $r->cy;
}
$res->cx /= count($c);
$res->cy /= count($c);
}
$info = json_decode[$bj->info);
$info->result = $res;
$i = json_encode($info);
$bj->update("info='$i'");
}
?>

26
html/inc/dir_hier.inc Normal file
View File

@ -0,0 +1,26 @@
<?php
// PHP versions of functions in sched/sched_util.C
function filename_hash($filename, $fanout) {
$m = md5($filename);
$s = substr($m, 1, 7);
sscanf($s, "%x", $n);
return sprintf("%x", $n%$fanout);
}
function dir_hier_path($filename, $root, $fanout) {
$dir = filename_hash($filename, $fanout);
$dirpath = "$root/$dir";
if (!is_dir($dirpath)) {
mkdir($dirpath);
}
return "$dirpath/$filename";
}
function dir_hier_url($filename, $base, $fanout) {
$dir = filename_hash($filename, $fanout);
return "$base/$dir/$filename";
}
?>

120
html/ops/bossa_ops.php Normal file
View File

@ -0,0 +1,120 @@
<?php
require_once("../inc/bossa_db.inc");
require_once("../inc/util_ops.inc");
function show_bapp($app) {
echo "<tr>
<td>Name: $app->name<br>Description: $app->description<br>Created: ".date_str($app->create_time)."</td>
<td>$app->display_script</td>
<td>$app->backend_script</td>
<td>
";
if ($app->hidden) {
show_button("bossa_ops.php?action=unhide&app_id=$app->id", "Unhide", "Unhide this app");
} else {
show_button("bossa_ops.php?action=hide&app_id=$app->id", "Hide", "Hide this app");
}
}
function show_apps() {
$apps = BossaApp::enum();
start_table();
row1("Existing apps", 4);
table_header("Name/description", "Display script", "Backend script", "");
foreach ($apps as $app) {
show_bapp($app);
}
end_table();
}
function add_app_form() {
echo "
<form action=bossa_ops.php method=get>
";
start_table();
row1("Add app");
row2("Name<span class=note><br>Visible to users</span>", "<input name=app_name>");
row2("Description<span class=note><br>Visible to users</span>", "<textarea name=description cols=60></textarea>");
row2("Display script filename", "<input name=display_script>");
row2("Backend script filename", "<input name=backend_script>");
row2("Min confidence sum for consensus", "<input name=min_conf_sum>");
row2("Min confidence fraction for consensus", "<input name=min_conf_frac>");
row2("Max instances", "<input name=max_instances>");
row2("", "<input type=submit name=submit value=\"Create app\">");
end_table();
echo "</form>";
}
function user_settings() {
global $user;
$flags = $user->bossa->flags;
echo "<form action=bossa_ops.php method=get>";
start_table();
row1("User settings");
$x = ($flags&BOLT_FLAGS_SHOW_ALL)?"checked":"";
row2("Show hidden apps?", "<input type=checkbox name=show_all $x>");
$x = ($flags&BOLT_FLAGS_DEBUG)?"checked":"";
row2("Show debugging output?", "<input type=checkbox name=debug $x>");
row2("", "<input type=submit name=submit value=\"Update user\">");
end_table();
echo "</form>";
}
function show_all() {
admin_page_head("Bossa app administration");
show_apps();
echo "<p>";
add_app_form();
echo "<p>";
user_settings();
admin_page_tail();
}
$user = get_logged_in_user();
$submit = get_str('submit', true);
if ($submit == 'Create app') {
$name = BossaDb::escape_string(get_str('app_name'));
$description = BossaDb::escape_string(get_str('description'));
$display_script = get_str('display_script');
$backend_script = get_str('backend_script');
$min_conf_sum = get_str('min_conf_sum');
$min_conf_frac = get_str('min_conf_frac');
$max_instances = get_str('max_instances');
$now = time();
BossaApp::insert("(create_time, name, description, display_script, backend_script, min_conf_sum, min_conf_frac, max_instances) values ($now, '$name', '$description', '$display_script', '$backend_script', $min_conf_sum, $min_conf_frac, $max_instances)");
Header('Location: bossa_ops.php');
exit();
} else if ($submit == 'Update user') {
$flags = 0;
if (get_str('show_all', true)) $flags |= BOLT_FLAGS_SHOW_ALL;
if (get_str('debug', true)) $flags |= BOLT_FLAGS_DEBUG;
$user->bossa->update("flags=$flags");
$user->bossa->flags = $flags;
Header('Location: bossa_ops.php');
exit();
} else {
$action = get_str('action', true);
if ($action) {
$app_id = get_int('app_id');
$app = BoltApp::lookup_id($app_id);
if (!$app) error_page("no such app");
switch ($action) {
case 'hide':
$app->update("hidden=1");
break;
case 'unhide':
$app->update("hidden=0");
break;
default:
error_page("unknown action $action");
}
Header('Location: bossa_ops.php');
exit();
}
}
show_all();
?>

View File

@ -1,19 +0,0 @@
<?php
require_once("../inc/bossa_db.inc");
// Set up Bossa applications.
// Customize and rename this file.
$ba = new BossaApp();
$ba->name = 'bossa_test';
$ba->user_friendly_name = 'Simple pattern recognition';
$ba->start_url = 'bossa_example.php';
if ($ba->insert($ba)) {
echo "Added application '$ba->name'\n";
} else {
echo "Couldn't add '$ba->name': ", mysql_error(), "\n";
}
?>

View File

@ -0,0 +1,41 @@
<?php
require_once("../inc/bossa_db.inc");
var $apps;
function lookup_app($id) {
global $apps;
foreach ($apps as $app) {
if ($app->id == $id) return $app;
}
return null;
}
function handle_job($job) {
$app = lookup_app($job->app_id);
if (!$app) {
echo "Missing app: $job->app_id\n";
return;
}
$instances = BossaJobInst::enum("job_id=$job->id");
}
function do_pass() {
$now = time();
$jobs = BossaJob::enum("transition_time < $now");
foreach ($jobs as $job) {
handle_job($job);
}
}
function main() {
global $apps;
$apps = BossaApp::enum();
while (1) {
do_pass();
}
}
main();
?>

31
html/user/bossa_apps.php Normal file
View File

@ -0,0 +1,31 @@
<?php
require_once("../inc/util.inc");
require_once("../inc/bossa_db.inc");
function show_app($app) {
row2(
"$app->name<span class=note><br>$app->description</span>",
"<a href=bossa_get_job.php?bossa_app_id=$app->id>Get job</a>"
);
}
function show_apps() {
$apps = BossaApp::enum();
foreach ($apps as $app) {
if ($app->hidden) continue;
show_app($app);
}
}
function main() {
page_head("Bossa apps");
start_table();
show_apps();
end_table();
page_tail();
}
main();
?>

View File

@ -3,42 +3,43 @@
require_once("../inc/bossa.inc");
// Bossa example.
// Show the user an image and ask them whether it's a zero or one.
// Show the user an image and ask them to click on the ellipse
function show_job($bj, $bji) {
if ($bji->finish_time) {
error_page("You already finished this job");
}
$info = json_decode($bj->info);
$img_url = "http://boinc.berkeley.edu/images/number_".$info->number.".jpg";
print_r($info);
$img_url = $info->url;
echo "
<form method=get action=bossa_example.php>
<input type=hidden name=bji value=$bji->id>
<img src=$img_url>
Click on the center of the ellipse.
If you don't see one, click here:
<input type=submit name=submit value=None>
<br>
The picture shows a
<br><input type=radio name=response value=0> zero
<br><input type=radio name=response value=1> one
<br><input type=radio name=response value=2 checked> not sure
<br><br><input type=submit name=submit value=OK>
<input type=hidden name=bji value=$bji->id>
<input type=hidden name=completion value=1>
<input type=image name=pic src=$img_url>
</form>
";
}
function handle_job_completion($bj, $bji) {
$response = null;
$response->number = get_int('response');
if (get_str('submit', true)) {
$response->have_ellipse = 0;
} else {
$response->have_ellipse = 1;
$pic = $_GET['pic'];
$response->cx = $pic.x;
$response->cy = $pic.y;
}
$bji->info = json_encode($response);
$bji->completed($bj);
// show another job immediately
//
Bossa::show_next_job($bj);
Bossa::show_next_job($bj); // show another job immediately
}
Bossa::script_init($user, $bj, $bji);
if (isset($_GET['submit'])) {
if (isset($_GET['completion'])) {
handle_job_completion($bj, $bji);
} else {
show_job($bj, $bji);

View File

@ -17,7 +17,7 @@ if (!$app) {
$ji = BossaJobInst::assign($app, $user);
if ($ji) {
$url = $app->start_url."?bji=$ji->id";
$url = $app->display_script."?bji=$ji->id";
Header("Location: $url");
} else {
page_head("No jobs available");