<?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/>. require_once("../inc/db_conn.inc"); require_once("../inc/util_basic.inc"); class BossaDb extends DbConn { public static $instance; static function get() { if (!isset($instance)) { $config = get_config(); $name = parse_config($config, '<bossa_db_name>'); if (!$name) { $name = parse_config($config, '<db_name>'); $user = parse_config($config, '<db_user>'); $passwd = parse_config($config, '<db_passwd>'); $host = parse_config($config, '<db_host>'); } else { $user = parse_config($config, '<bossa_db_user>'); $passwd = parse_config($config, '<bossa_db_passwd>'); $host = parse_config($config, '<bossa_db_host>'); } if ($host == null) { $host = "localhost"; } $instance = new DbConn(); $retval = $instance->init_conn($user, $passwd, $host, $name); if (!$retval) return null; } return $instance; } static function escape_string($string) { $db = self::get(); return $db->base_escape_string($string); } static function start_transaction() { //echo "start transaction\n"; return; $db = BossaDb::get(); $db->do_query("start transaction"); } static function commit() { //echo "commit\n"; return; $db = BossaDb::get(); $db->do_query("commit"); } } // use this as a local to ensure that transaction is committed // regardless of exceptions // class BossaTransaction { function __construct() { BossaDb::start_transaction(); } function __destruct() { BossaDb::commit(); } } class BossaUser { static $cache; static function lookup_userid($id) { $db = BossaDb::get(); return $db->lookup('bossa_user', 'BossaUser', "user_id=$id"); } static function insert($clause) { $db = BossaDb::get(); return $db->insert('bossa_user', $clause); } static function lookup(&$user) { if (!$user) return; if (isset($user->bossa)) return; if (isset(self::$cache[$user->id])) { $bossa = self::$cache[$user->id]; } else { $bossa = self::lookup_userid($user->id); if (!$bossa) { self::insert("(user_id) values ($user->id)"); $bossa = self::lookup_userid($user->id); } self::$cache[$user->id] = $bossa; } $user->bossa = $bossa; } function update($clause) { $db = BossaDb::get(); $clause = "$clause where user_id=$this->user_id"; return $db->update_aux('bossa_user', $clause); } // app-callable: // function get_opaque_data() { return unserialize($this->info); } function set_opaque_data($info) { $info = serialize($info); $this->update("info='$info'"); } } class BossaApp { static function insert($clause) { $db = BossaDb::get(); return $db->insert('bossa_app', $clause); } static function lookup($clause) { $db = BossaDb::get(); return $db->lookup('bossa_app', 'BossaApp', $clause); } static function lookup_id($id) { $db = BossaDb::get(); return $db->lookup_id($id, 'bossa_app', 'BossaApp'); } static function enum() { $db = BossaDb::get(); return $db->enum('bossa_app', 'BossaApp'); } function update($clause) { $db = BossaDb::get(); return $db->update($this, 'bossa_app', $clause); } } // values for bossa_job.state // define("BOSSA_JOB_EMBARGOED", 0); define("BOSSA_JOB_IN_PROGRESS", 1); define("BOSSA_JOB_DONE", 2); define("BOSSA_JOB_INCONCLUSIVE", 3); class BossaJob { static function insert($clause) { $db = BossaDb::get(); $ret = $db->insert('bossa_job', $clause); if (!$ret) return 0; return $db->insert_id(); } function update($clause) { $db = BossaDb::get(); return $db->update($this, 'bossa_job', $clause); } static function lookup_id($id) { $db = BossaDb::get(); return $db->lookup_id($id, 'bossa_job', 'BossaJob'); } static function enum($clause) { $db = BossaDb::get(); return $db->enum('bossa_job', 'BossaJob', $clause); } static function count($clause) { $db = BossaDb::get(); return $db->count('bossa_job', $clause); } // app-callable: // function get_opaque_data() { return unserialize($this->info); } function set_priority($x) { return $this->update("priority_0=$x"); } function get_instances() { return BossaJobInst::enum("job_id = $this->id"); } function get_finished_instances() { return BossaJobInst::enum("job_id = $this->id and finish_time>0"); } function set_state($s) { $this->update("state=$s"); } } class BossaJobInst { function insert($clause) { $db = BossaDb::get(); $ret = $db->insert('bossa_job_inst', $clause); if (!$ret) return 0; return $db->insert_id(); } static function lookup_id($id) { $db = BossaDb::get(); return $db->lookup_id($id, 'bossa_job_inst', 'BossaJobInst'); } static function enum($clause) { $db = BossaDb::get(); return $db->enum('bossa_job_inst', 'BossaJobInst', $clause); } function update($clause) { $db = BossaDb::get(); return $db->update($this, 'bossa_job_inst', $clause); } // Assign a job from the given app to the given user. // Returns the job instance or NULL. // static function assign($app, $user) { $db = BossaDb::get(); // first look for unfinished jobs previously assigned to this user // $query = "select * from ".$db->db_name.".bossa_job_inst where app_id=$app->id and user_id=$user->id and finish_time=0 limit 1"; $result = $db->do_query($query); if ($result) { $ji = mysql_fetch_object($result, 'BossaJobInst'); mysql_free_result($result); if ($ji) return $ji; } if ($app->calibration_frac && drand() < $app->calibration_frac) { $query = "select * from ".$db->db_name.".bossa_job where app_id=$app->id and (select count(*) from ".$db->db_name.".bossa_job_inst where job_id=bossa_job.id and user_id=$user->id) = 0 and state=1 and calibration=1 order by priority_0 desc limit 1"; $result = $db->do_query($query); if (!$result) return null; $job = mysql_fetch_object($result, 'BossaJob'); if (!$job) return null; $job->update("priority_0=priority_0-1"); mysql_free_result($result); } else { if (isset($user->bossa->category)) { $prio = "priority_".$user->bossa->category; } else { $prio = "priority_0"; } // skips jobs for which this user // has already been assigned an instance // $query = "select * from ".$db->db_name.".bossa_job where app_id=$app->id and (select count(*) from ".$db->db_name.".bossa_job_inst where job_id=bossa_job.id and user_id=$user->id) = 0 and state=1 and calibration=0 order by $prio desc limit 1"; $result = $db->do_query($query); if (!$result) return null; $job = mysql_fetch_object($result, 'BossaJob'); if (!$job) return null; mysql_free_result($result); } $now = time(); $clause = "(create_time, app_id, job_id, user_id, batch_id, calibration) values ($now, $app->id, $job->id, $user->id, $job->batch_id, $job->calibration)"; $id = BossaJobInst::insert($clause); return BossaJobInst::lookup_id($id); } function delete_aux($clause) { $db = BossaDb::get(); return $db->delete_aux('bossa_job_inst', $clause); } // app-callable functions // function set_opaque_data($info) { $info = serialize($info); return $this->update("info='$info'"); } function get_opaque_data() { return unserialize($this->info); } function get_user() { $user = BoincUser::lookup_id($this->user_id); BossaUser::lookup($user); return $user; } } class BossaBatch { function insert($clause) { $db = BossaDb::get(); $ret = $db->insert('bossa_batch', $clause); if (!$ret) return 0; return $db->insert_id(); } static function enum($clause) { $db = BossaDb::get(); return $db->enum('bossa_batch', 'BossaBatch', $clause); } static function lookup_id($id) { $db = BossaDb::get(); return $db->lookup_id($id, 'bossa_batch', 'BossaBatch'); } } ?>