| 2 | | |
| 3 | | To get work, a user goes to a particular Bossa-supplied page. |
| 4 | | There he sees a list of applications for which tasks are available |
| 5 | | and for which he is qualified, |
| 6 | | and links to courses for other applications. |
| 7 | | Online and offline applications are listed separately. |
| 8 | | Each application has an estimate of the time or other resources required to complete a job. |
| 9 | | |
| 10 | | Selecting an online application invokes the Bossa ''scheduler'' script, |
| 11 | | which selects a job instance suitable for the user, |
| 12 | | and redirects to its instance URL. |
| 13 | | |
| 14 | | Selecting an offline application invokes the Bossa scheduler, |
| 15 | | which selects a job and redirects to its instance-start URL. |
| 16 | | |
| 17 | | Team administrators are provided with an interface for getting |
| 18 | | offline jobs for the team. |
| 19 | | The scheduler allows a team to get instances only for applications |
| 20 | | for which some team member has the required skill. |
| 21 | | |
| 22 | | Users and teams are provided with an interface for seeing |
| 23 | | a list of pending offline jobs. |
| 24 | | They can indicate that one of them is completed; |
| 25 | | this takes them to the instance-complete URL for that job. |
| 85 | | |
| 86 | | == Creating a Bossa project == |
| 87 | | |
| 88 | | First, [ServerIntro set up a BOINC server] and [MakeProject create a project]. |
| 89 | | You'll need PHP 5.2 or later (for JSON functions). |
| 90 | | Say your project is called '''test_project''', your BOINC source directory is '''~/boinc''', |
| 91 | | and your BOINC projects directory is '''~/projects'''. |
| 92 | | |
| 93 | | Create Bossa's database tables as follows: |
| 94 | | {{{ |
| 95 | | cd ~/boinc/db |
| 96 | | mysql test_project < bossa_schema.sql |
| 97 | | mysql test_project < bossa_constraints.sql |
| 98 | | }}} |
| 99 | | |
| 100 | | Create a Bossa application as follows: |
| 101 | | {{{ |
| 102 | | cd ~/projects/test_project/html/ops |
| 103 | | php bossa_setup_example.php |
| 104 | | }}} |
| 105 | | |
| 106 | | bossa_setup_example.php contains: |
| 107 | | {{{ |
| 108 | | $ba = new BossaApp(); |
| 109 | | $ba->name = 'bossa_test'; |
| 110 | | $ba->user_friendly_name = 'Simple pattern recognition'; |
| 111 | | $ba->start_url = 'bossa_example.php'; |
| 112 | | |
| 113 | | if ($ba->insert($ba)) { |
| 114 | | echo "Added application '$ba->name'\n"; |
| 115 | | } else { |
| 116 | | echo "Couldn't add '$ba->name': ", mysql_error(), "\n"; |
| 117 | | } |
| 118 | | }}} |
| 119 | | You can edit this to change the application name |
| 120 | | and front-end script name, if you like. |
| 121 | | |
| 122 | | == Adding jobs == |
| 123 | | |
| 124 | | Typically you'll add jobs using a script. |
| 125 | | Here's an example ('''html/ops/bossa_make_jobs_example.php'''): |
| 126 | | {{{ |
| 127 | | 1 <?php |
| 128 | | 2 |
| 129 | | 3 require_once("../inc/bossa_db.inc"); |
| 130 | | 4 require_once("../inc/db.inc"); |
| 131 | | 5 |
| 132 | | 6 db_init(); |
| 133 | | 7 |
| 134 | | 8 function make_jobs() { |
| 135 | | 9 $appname = 'bossa_test'; |
| 136 | | 10 $app = BossaApp::lookup_name($appname); |
| 137 | | 11 if (!$app) { |
| 138 | | 12 echo "Application $appname not found\n"; |
| 139 | | 13 exit(1); |
| 140 | | 14 } |
| 141 | | 15 $job = new BossaJob; |
| 142 | | 16 $job->app_id = $app->id; |
| 143 | | 17 $job->batch = 0; |
| 144 | | 18 $job->time_estimate = 30; |
| 145 | | 19 $job->time_limit = 600; |
| 146 | | 20 $job->nsuccess_needed = 3; |
| 147 | | 21 for ($i=0; $i<10; $i++) { |
| 148 | | 22 $job->name = "job_$i"; |
| 149 | | 23 $info = null; |
| 150 | | 24 $info->number = $i % 2; |
| 151 | | 25 $job->info = json_encode($info); |
| 152 | | 26 if (!$job->insert()) { |
| 153 | | 27 echo "BossaJob::insert failed: ", mysql_error(), "\n"; |
| 154 | | 28 exit(1); |
| 155 | | 29 } |
| 156 | | 30 } |
| 157 | | 31 } |
| 158 | | 32 |
| 159 | | 33 make_jobs(); |
| 160 | | 34 echo "All done.\n"; |
| 161 | | 35 |
| 162 | | 36 ?> |
| 163 | | }}} |
| 164 | | |
| 165 | | This creates 10 jobs. |
| 166 | | Each job has an ''info'' field consisting of a JSON-encoded structure |
| 167 | | consisting of an integer (0 or 1). |
| 168 | | |
| 169 | | == Front-end scripts == |
| 170 | | |
| 171 | | You develop a '''front-end script''' to show a job instance to a user, |
| 172 | | and to handle a completed instance. |
| 173 | | It's handy to put both of these functions in a single file. |
| 174 | | A front-end script is called with the URL parameter '''bji''' set |
| 175 | | to a job instance ID. |
| 176 | | Here's an example ('''html/user/bossa_example.php'''): |
| 177 | | |
| 178 | | {{{ |
| 179 | | 1 <?php |
| 180 | | 2 |
| 181 | | 3 require_once("../inc/bossa.inc"); |
| 182 | | 4 |
| 183 | | 5 echo "foo"; |
| 184 | | 6 |
| 185 | | 7 // Bossa example. |
| 186 | | 8 // Show the user an image and ask them whether it's a zero or one. |
| 187 | | 9 |
| 188 | | 10 function show_job($bj, $bji) { |
| 189 | | 11 if ($bji->finish_time) { |
| 190 | | 12 error_page("You already finished this job"); |
| 191 | | 13 } |
| 192 | | 14 $info = json_decode($bj->info); |
| 193 | | 15 $img_url = "http://boinc.berkeley.edu/images/number_".$info->number.".jpg"; |
| 194 | | 16 echo " |
| 195 | | 17 <form method=get action=bossa_example.php> |
| 196 | | 18 <input type=hidden name=bji value=$bji->id> |
| 197 | | 19 <img src=$img_url> |
| 198 | | 20 <br> |
| 199 | | 21 The picture shows a |
| 200 | | 22 <br><input type=radio name=response value=0> zero |
| 201 | | 23 <br><input type=radio name=response value=1> one |
| 202 | | 24 <br><input type=radio name=response value=2 checked> not sure |
| 203 | | 25 <br><br><input type=submit name=submit value=OK> |
| 204 | | 26 </form> |
| 205 | | 27 "; |
| 206 | | 28 } |
| 207 | | 29 |
| 208 | | 30 function handle_job_completion($bj, $bji) { |
| 209 | | 31 $response = null; |
| 210 | | 32 $response->number = get_int('response'); |
| 211 | | 33 $bji->info = json_encode($response); |
| 212 | | 34 $bji->completed($bj); |
| 213 | | 35 |
| 214 | | 36 // show another job immediately |
| 215 | | 37 // |
| 216 | | 38 Bossa::show_next_job($bj); |
| 217 | | 39 } |
| 218 | | 40 |
| 219 | | 41 Bossa::script_init($user, $bj, $bji); |
| 220 | | 42 |
| 221 | | 43 if ($_GET['submit']) { |
| 222 | | 44 handle_job_completion($bj, $bji); |
| 223 | | 45 } else { |
| 224 | | 46 show_job($bj, $bji); |
| 225 | | 47 } |
| 226 | | 48 |
| 227 | | 49 ?> |
| 228 | | }}} |
| 229 | | |
| 230 | | Line 41:: |
| 231 | | Call a Bossa utility function to look up the job instance and make sure that it was issued to the logged-in user. The job instance, job, and user are returned. |
| 232 | | Line 43:: |
| 233 | | Branch according to whether we are showing a job or handling the completion of a job. |
| 234 | | Line 14:: |
| 235 | | If we're showing a job, decode its ''info'' structure to decide whether to show which picture to show. |
| 236 | | Lines 17-18:: |
| 237 | | Task completion will be handled by this script; arrange to pass the job instance ID. |
| 238 | | Lines 31-33:: |
| 239 | | Get the user's response, and encode it in JSON. |
| 240 | | Line 34:: |
| 241 | | Call a utility function that marks the job instance as completed and updates its database record. |
| 242 | | Line 38:: |
| 243 | | Call a utility function that gets another job (if one is available) and shows it to the user. |