diff --git a/db/bossa_schema.sql b/db/bossa_schema.sql index 8dcbf4200b..afd0abf44a 100644 --- a/db/bossa_schema.sql +++ b/db/bossa_schema.sql @@ -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, diff --git a/doc/boinc_news.php b/doc/boinc_news.php index e18b819202..08f7e7c7cb 100644 --- a/doc/boinc_news.php +++ b/doc/boinc_news.php @@ -1,6 +1,11 @@ BOINC Bulgaria." +), array("Feb 7, 2008", "Proteins@Home has resumed operations. diff --git a/doc/links.php b/doc/links.php index afbd13c7e5..713a4a9811 100644 --- a/doc/links.php +++ b/doc/links.php @@ -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"), )); diff --git a/html/inc/bossa.inc b/html/inc/bossa.inc index 2093ed4efb..664365c9ee 100644 --- a/html/inc/bossa.inc +++ b/html/inc/bossa.inc @@ -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"); diff --git a/html/inc/bossa_db.inc b/html/inc/bossa_db.inc index e1ede7bc56..5884d92710 100644 --- a/html/inc/bossa_db.inc +++ b/html/inc/bossa_db.inc @@ -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; } diff --git a/html/inc/bossa_example.inc b/html/inc/bossa_example.inc new file mode 100644 index 0000000000..2f22a839b2 --- /dev/null +++ b/html/inc/bossa_example.inc @@ -0,0 +1,40 @@ +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'"); +} + +?> diff --git a/html/inc/dir_hier.inc b/html/inc/dir_hier.inc new file mode 100644 index 0000000000..f835895e10 --- /dev/null +++ b/html/inc/dir_hier.inc @@ -0,0 +1,26 @@ + diff --git a/html/ops/bossa_ops.php b/html/ops/bossa_ops.php new file mode 100644 index 0000000000..9a976ff6af --- /dev/null +++ b/html/ops/bossa_ops.php @@ -0,0 +1,120 @@ + + Name: $app->name
Description: $app->description
Created: ".date_str($app->create_time)." + $app->display_script + $app->backend_script + + "; + 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 " +
+ "; + start_table(); + row1("Add app"); + row2("Name
Visible to users
", ""); + row2("Description
Visible to users
", ""); + row2("Display script filename", ""); + row2("Backend script filename", ""); + row2("Min confidence sum for consensus", ""); + row2("Min confidence fraction for consensus", ""); + row2("Max instances", ""); + row2("", ""); + end_table(); + echo "
"; +} + +function user_settings() { + global $user; + $flags = $user->bossa->flags; + echo "
"; + start_table(); + row1("User settings"); + $x = ($flags&BOLT_FLAGS_SHOW_ALL)?"checked":""; + row2("Show hidden apps?", ""); + $x = ($flags&BOLT_FLAGS_DEBUG)?"checked":""; + row2("Show debugging output?", ""); + row2("", ""); + end_table(); + echo "
"; +} + +function show_all() { + admin_page_head("Bossa app administration"); + show_apps(); + echo "

"; + add_app_form(); + echo "

"; + 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(); + +?> diff --git a/html/ops/bossa_setup_example.php b/html/ops/bossa_setup_example.php deleted file mode 100644 index 9f6a49d528..0000000000 --- a/html/ops/bossa_setup_example.php +++ /dev/null @@ -1,19 +0,0 @@ -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"; -} - -?> diff --git a/html/ops/bossa_transitioner.php b/html/ops/bossa_transitioner.php new file mode 100644 index 0000000000..b4116b2dfb --- /dev/null +++ b/html/ops/bossa_transitioner.php @@ -0,0 +1,41 @@ +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(); +?> diff --git a/html/user/bossa_apps.php b/html/user/bossa_apps.php new file mode 100644 index 0000000000..dbfdc587bb --- /dev/null +++ b/html/user/bossa_apps.php @@ -0,0 +1,31 @@ +name
$app->description
", + "id>Get job" + ); +} + +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(); + +?> diff --git a/html/user/bossa_example.php b/html/user/bossa_example.php index d71a4a6c8c..52a11b2a0a 100644 --- a/html/user/bossa_example.php +++ b/html/user/bossa_example.php @@ -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 "

- id> - + Click on the center of the ellipse. + If you don't see one, click here: +
- The picture shows a -
zero -
one -
not sure -

+ id> + +
"; } 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); diff --git a/html/user/bossa_get_job.php b/html/user/bossa_get_job.php index 5d1460b2c4..b35f04f02f 100644 --- a/html/user/bossa_get_job.php +++ b/html/user/bossa_get_job.php @@ -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");