handle_query_batch()   F
last analyzed

Complexity

Conditions 21
Paths 14336

Size

Total Lines 154
Code Lines 107

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 21
eloc 107
c 5
b 0
f 0
nc 14336
nop 1
dl 0
loc 154
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
// This file is part of BOINC.
4
// http://boinc.berkeley.edu
5
// Copyright (C) 2024 University of California
6
//
7
// BOINC is free software; you can redistribute it and/or modify it
8
// under the terms of the GNU Lesser General Public License
9
// as published by the Free Software Foundation,
10
// either version 3 of the License, or (at your option) any later version.
11
//
12
// BOINC is distributed in the hope that it will be useful,
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
// See the GNU Lesser General Public License for more details.
16
//
17
// You should have received a copy of the GNU Lesser General Public License
18
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
19
20
// remote job submission functions.
21
//
22
// A 'remote app' is one where jobs are submitted remotely:
23
// - via web RPCs
24
// - via forms on the project web site
25
//
26
// In both cases only users with permission can submit; either
27
// - a user_submit record with submit_all set
28
// - a user_submit_app record
29
//
30
// These apps are described in $remote_apps in project.inc
31
//
32
// This page provides several functions involving remote apps
33
// ('me' means logged-in user)
34
//
35
//  show_all_batches
36
//      show list of batches visible to me, possibly filtered by
37
//          submitting user
38
//          app
39
//          state (in progress, completed etc.)
40
//  show_user_batches
41
//      show my batches
42
//  show_batches_admin_app
43
//      show all batches for a given app to admin
44
//  admin_all
45
//      show all batches to admin
46
//  batch_stats
47
//      show WSS, disk usage stats for a batch
48
//  query_batch
49
//      show list of jobs in a batch
50
//  query_job
51
//      show job details and instances
52
//  retire_batch
53
//      retire a batch
54
//  retire_multi
55
//      retire multiple batches
56
//  abort_batch
57
//      abort a batch
58
//  admin
59
//      show index of admin functions
60
//  update_only_own
61
//      control whether my jobs should run only on my computers
62
63
require_once("../inc/submit_db.inc");
64
require_once("../inc/util.inc");
65
require_once("../inc/result.inc");
66
require_once("../inc/submit_util.inc");
67
require_once("../project/project.inc");
68
require_once('../project/remote_apps.inc');
69
70
display_errors();
71
72
// in general, if there can be lots of something,
73
// show this many and a link to show all.
74
// TODO: jobs in a batch
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
75
//
76
define("PAGE_SIZE", 20);
77
78
function return_link() {
79
    echo "<p><a href=submit.php?action=show_user_batches>Return to batches page</a>\n";
80
}
81
82
// return subset of batches in given state
83
//
84
function batches_in_state($all_batches, $state) {
85
    $batches = [];
86
    foreach ($all_batches as $batch) {
87
        if ($batch->state != $state) continue;
88
        $batches[] = $batch;
89
    }
90
    return $batches;
91
}
92
93
function sort_batches(&$batches, $order) {
94
    switch ($order) {
95
    case 'sub_asc':
96
        $f = function($a, $b) {
97
            return (int)($a->create_time - $b->create_time);
98
        };
99
        break;
100
    case 'sub_desc':
101
        $f = function($a, $b) {
102
            return (int)($b->create_time - $a->create_time);
103
        };
104
        break;
105
    case 'comp_asc':
106
        $f = function($a, $b) {
107
            return (int)($a->completion_time - $b->completion_time);
108
        };
109
        break;
110
    case 'comp_desc':
111
        $f = function($a, $b) {
112
            return (int)($b->completion_time - $a->completion_time);
113
        };
114
        break;
115
    }
116
    usort($batches, $f);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $f does not seem to be defined for all execution paths leading up to this point.
Loading history...
117
}
118
119
// in progress batches don't have completion time
120
//
121
function in_progress_order($order) {
122
    switch ($order) {
123
    case 'comp_asc': return 'sub_asc';
124
    case 'comp_desc': return 'sub_desc';
125
    }
126
    return $order;
127
}
128
129
// show order options
130
// sub_asc, sub_desc: submission time ( = ID order)
131
// comp_asc, comp_desc: completion time
132
133
function order_options($url_args, $order) {
134
    $url = "submit.php?$url_args";
135
    echo sprintf(
136
        'Order by: submission time (%s, %s) or completion time (%s, %s)',
137
        order_item($url, $order, 'sub_asc', 'ascending'),
138
        order_item($url, $order, 'sub_desc', 'descending'),
139
        order_item($url, $order, 'comp_asc', 'ascending'),
140
        order_item($url, $order, 'comp_desc', 'descending')
141
    );
142
}
143
144
function order_item($url, $cur_order, $order, $label) {
145
    if ($cur_order == $order) {
146
        return $label;
147
    } else {
148
        $url .= "&order=$order";
149
        return "<a href=$url>$label</a>";
150
    }
151
}
152
153
function get_order() {
154
    $order = get_str('order', true);
155
    if (!$order) $order = 'sub_desc';
156
    return $order;
157
}
158
159
function state_count($batches, $state) {
160
    $n = 0;
161
    foreach ($batches as $batch) {
162
        if ($batch->state == $state) $n++;
163
    }
164
    return $n;
165
}
166
167
function show_all_batches_link($batches, $state, $limit, $user, $app) {
168
    $n = state_count($batches, $state);
169
    if ($n > $limit) {
170
        if ($user) $userid = $user->id;
171
        else $userid = 0;
172
        if ($app) $appid = $app->id;
173
        else $appid = 0;
174
175
        echo "Showing the most recent $limit of $n batches.
176
            <a href=submit.php?action=show_all_batches&state=$state&userid=$userid&appid=$appid>Show all $n</a>
177
            <p>
178
        ";
179
    }
180
}
181
182
// show in-progress batches.
183
//
184
function show_in_progress($all_batches, $order, $limit, $user, $app) {
185
    $batches = batches_in_state($all_batches, BATCH_STATE_IN_PROGRESS);
186
    sort_batches($batches, in_progress_order($order));
187
    echo sprintf('<h3>Batches in progress (%d)</h3>', count($batches));
188
    $first = true;
189
    $n = 0;
190
    foreach ($batches as $batch) {
191
        if ($limit && $n == $limit) break;
192
        $n++;
193
        if ($first) {
194
            $first = false;
195
            if ($limit) {
196
                show_all_batches_link(
197
                    $batches, BATCH_STATE_IN_PROGRESS, $limit, $user, $app
198
                );
199
            }
200
            form_start('submit.php');
201
            form_input_hidden('action', 'abort_selected');
202
            start_table('table-striped');
203
            $x = [
204
                "Name",
205
                "ID",
206
                "User",
207
                "App",
208
                "# jobs",
209
                "Progress",
210
                "Submitted"
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
211
            ];
212
            row_heading_array($x);
213
        }
214
        $pct_done = (int)($batch->fraction_done*100);
215
        $x = [
216
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
217
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
218
            $batch->user_name,
219
            $batch->app_name,
220
            $batch->njobs,
221
            "$pct_done%",
222
            local_time_str($batch->create_time)
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
223
        ];
224
        row_array($x);
225
    }
226
    if ($first) {
227
        echo "<p>None.\n";
228
    } else {
229
        end_table();
230
        form_end();
231
    }
232
}
233
234
function show_complete($all_batches, $order, $limit, $user, $app) {
235
    $batches = batches_in_state($all_batches, BATCH_STATE_COMPLETE);
236
    sort_batches($batches, $order);
237
    echo sprintf('<h3>Completed batches (%d)</h3>', count($batches));
238
    $first = true;
239
    $n = 0;
240
    foreach ($batches as $batch) {
241
        if ($limit && $n == $limit) break;
242
        $n++;
243
        if ($first) {
244
            $first = false;
245
            if ($limit) {
246
                show_all_batches_link($batches, BATCH_STATE_COMPLETE, $limit, $user, $app);
247
            }
248
            form_start('submit.php', 'get');
249
            form_input_hidden('action', 'retire_multi');
250
            start_table('table-striped');
251
            table_header(
252
                "Name", "ID", "User", "App", "# Jobs", "Submitted", "Completed", "Select"
253
            );
254
        }
255
        table_row(
256
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
257
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
258
            $batch->user_name,
259
            $batch->app_name,
260
            $batch->njobs,
261
            local_time_str($batch->create_time),
262
            local_time_str($batch->completion_time),
263
            sprintf('<input type=checkbox name=retire_%d>', $batch->id)
264
        );
265
    }
266
    if ($first) {
267
        echo "<p>None.\n";
268
    } else {
269
        end_table();
270
        form_submit('Retire selected batches');
271
        form_end();
272
    }
273
}
274
275
function show_aborted($all_batches, $order, $limit, $user, $app) {
276
    $batches = batches_in_state($all_batches, BATCH_STATE_ABORTED);
277
    if (!$batches) return;
278
    sort_batches($batches, $order);
279
    echo sprintf('<h3>Aborted batches (%d)</h3>', count($batches));
280
    $first = true;
281
    $n = 0;
282
    foreach ($batches as $batch) {
283
        if ($limit && $n == $limit) break;
284
        $n++;
285
        if ($first) {
286
            $first = false;
287
            if ($limit) {
288
                show_all_batches_link($batches, BATCH_STATE_ABORTED, $limit, $user, $app);
289
            }
290
            form_start('submit.php', 'get');
291
            form_input_hidden('action', 'retire_multi');
292
            start_table();
293
            table_header("Name", "ID", "User", "App", "# Jobs", "Submitted", "Aborted", 'Select');
294
        }
295
        table_row(
296
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
297
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
298
            $batch->user_name,
299
            $batch->app_name,
300
            $batch->njobs,
301
            local_time_str($batch->create_time),
302
            local_time_str($batch->completion_time),
303
            sprintf('<input type=checkbox name=retire_%d>', $batch->id)
304
        );
305
    }
306
    if (!$first) {
307
        end_table();
308
        form_submit('Retire selected batches');
309
        form_end();
310
    }
311
}
312
313
// fill in the app and user names in list of batches
314
//
315
function fill_in_app_and_user_names(&$batches) {
316
    $apps = [];
317
    $users = [];
318
    foreach ($batches as $batch) {
319
        if (array_key_exists($batch->app_id, $apps)) {
320
            $app = $apps[$batch->app_id];
321
        } else {
322
            $app = BoincApp::lookup_id($batch->app_id);
323
            $apps[$batch->app_id] = $app;
324
        }
325
        if ($app) {
326
            $batch->app_name = $app->name;
327
            if ($batch->description) {
328
                $batch->app_name .= " ($batch->description)";
329
            }
330
        } else {
331
            $batch->app_name = "unknown";
332
        }
333
334
        if (array_key_exists($batch->user_id, $users)) {
335
            $user = $users[$batch->user_id];
336
        } else {
337
            $user = BoincUser::lookup_id($batch->user_id);
338
            $users[$batch->user_id] = $user;
339
        }
340
        if ($user) {
341
            $batch->user_name = $user->name;
342
        } else {
343
            $batch->user_name = "missing user $batch->user_id";
344
        }
345
    }
346
}
347
348
// show a set of batches: in progress, then completed, then aborted
349
//
350
function show_batches($batches, $order, $limit, $user, $app) {
351
    fill_in_app_and_user_names($batches);
352
    $batches = get_batches_params($batches);
353
    show_in_progress($batches, $order, $limit, $user, $app);
354
    show_complete($batches, $order, $limit, $user, $app);
355
    show_aborted($batches, $order, $limit, $user, $app);
356
}
357
358
// show links to per-app job submission forms
359
//
360
function show_submit_links($user) {
361
    global $remote_apps;
362
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
363
    if (!$user_submit) {
364
        error_page("Ask the project admins for permission to submit jobs");
365
    }
366
367
    page_head("Submit jobs");
368
369
    // show links to per-app job submission pages
370
    //
371
    foreach ($remote_apps as $area => $apps) {
372
        panel($area,
373
            function() use ($apps) {
374
                foreach ($apps as $app) {
375
                    if (empty($app->form)) continue;
376
                    // show app logo if available
377
                    if (!empty($app->logo)) {
378
                        echo sprintf(
379
                            '<a href=%s><img width=100 src=%s></a>&nbsp;',
380
                            $app->form, $app->logo
381
                        );
382
                    } else {
383
                        echo sprintf(
384
                            '<a href=%s>%s</a><p>',
385
                            $app->form, $app->long_name
386
                        );
387
                    }
388
                }
389
            }
390
        );
391
    }
392
393
    form_start('submit.php');
394
    form_input_hidden('action', 'update_only_own');
395
    form_radio_buttons(
396
        'Jobs you submit can run', 'only_own',
397
        [
398
            [0, 'on any computer'],
399
            [1, 'only on your computers (and no other jobs will run there)']
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
400
        ],
401
        $user->seti_id
402
    );
403
    form_submit('Update');
404
    form_end();
405
    page_tail();
406
}
407
408
// show batches of logged in user.
409
// They have manage access to these batches.
410
//
411
function handle_show_user_batches($user) {
412
    page_head("Your batches");
413
    $order = get_order();
414
    order_options('action=show_user_batches', $order);
415
    $batches = BoincBatch::enum("user_id = $user->id");
416
    show_batches($batches, $order, PAGE_SIZE, $user, null);
417
418
    page_tail();
419
}
420
421
function handle_update_only_own($user) {
422
    if (!parse_bool(get_config(), 'enable_assignment')) {
423
        error_page(
424
            'Job assignment is not enabled in the project config file.
425
            Please ask the project admins to enable it.'
426
        );
427
        return;
428
    }
429
    $val = get_int('only_own');
430
    if ($val) {
431
        if (BoincHost::count("userid=$user->id") == 0) {
432
            error_page(
433
                "You don't have any computers running BOINC and attached to this project."
434
            );
435
        }
436
    }
437
    $user->update("seti_id=$val");
438
    header("Location: submit.php");
439
}
440
441
// get list of app names of remote apps
442
//
443
function get_remote_app_names() {
444
    global $remote_apps;
445
    $x = [];
446
    foreach ($remote_apps as $category => $apps) {
447
        foreach ($apps as $app) {
448
            $x[] = $app->app_name;
449
        }
450
    }
451
    return array_unique($x);
452
}
453
454
// show links for everything the user has admin access to
455
//
456
function handle_admin($user) {
457
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
458
    if (!$user_submit) error_page('no access');
459
    if ($user_submit->manage_all) {
460
        // user can administer all apps
461
        //
462
        page_head("Job submission: manage all apps");
463
        echo '<ul>';
464
        echo "<li> <a href=submit.php?action=admin_all>View/manage all batches</a>
465
        ";
466
        $app_names = get_remote_app_names();
467
        foreach ($app_names as $app_name) {
468
            $app_name = BoincDb::escape_string($app_name);
469
            $app = BoincApp::lookup("name='$app_name'");
470
            echo "
471
                <li>$app->user_friendly_name<br>
472
                <ul>
473
                <li><a href=submit.php?action=show_batches_admin_app&app_id=$app->id>View/manage batches</a>
474
            ";
475
            if ($app_name == 'buda') {
476
                echo "
477
                    <li> <a href=buda.php>Manage BUDA apps and variants</a>
478
                ";
479
            } else {
480
                echo "
481
                    <li> <a href=manage_app.php?app_id=$app->id&amp;action=app_version_form>Manage app versions</a>
482
                ";
483
            }
484
            echo "
485
                </ul>
486
            ";
487
        }
488
    } else {
489
        // see if user can administer specific apps
490
        //
491
        page_head("Job submission: manage apps");
492
        echo '<ul>';
493
        $usas = BoincUserSubmitApp::enum("user_id=$user->id");
494
        foreach ($usas as $usa) {
495
            $app = BoincApp::lookup_id($usa->app_id);
496
            echo "<li>$app->user_friendly_name<br>
497
                <a href=submit.php?action=show_batches_admin_app&app_id=$app->id>Batches</a>
498
            ";
499
            if ($usa->manage) {
500
                echo "&middot;
501
                    <a href=manage_app.php?app_id=$app->id&action=app_version_form>Versions</a>
502
                ";
503
            }
504
        }
505
    }
506
    echo "</ul>\n";
507
    page_tail();
508
}
509
510
// show all batches for given app to administrator
511
//
512
function show_batches_admin_app($user) {
513
    $app_id = get_int("app_id");
514
    $app = BoincApp::lookup_id($app_id);
515
    if (!$app) error_page("no such app");
516
    if (!has_manage_access($user, $app_id)) {
517
        error_page('no access');
518
    }
519
520
    $order = get_order();
521
522
    page_head("Manage batches for $app->user_friendly_name");
523
    order_options("action=show_batches_admin_app&app_id=$app_id", $order);
524
    $batches = BoincBatch::enum("app_id = $app_id");
525
    show_batches($batches, $order, PAGE_SIZE, null, $app);
526
    page_tail();
527
}
528
529
function handle_admin_all($user) {
530
    $order = get_order();
531
    page_head("Administer batches (all apps and users)");
532
    order_options("action=admin_all", $order);
533
    $batches = BoincBatch::enum('');
534
    show_batches($batches, $order, PAGE_SIZE, null, null);
535
    page_tail();
536
}
537
538
539
// show the statics of mem/disk usage of jobs in a batch
540
//
541
function handle_batch_stats($user) {
542
    $batch_id = get_int('batch_id');
543
    $batch = BoincBatch::lookup_id($batch_id);
544
    $results = BoincResult::enum_fields(
545
        'peak_working_set_size, peak_swap_size, peak_disk_usage',
546
        sprintf('batch = %d and outcome=%d',
547
            $batch->id, RESULT_OUTCOME_SUCCESS
548
        )
549
    );
550
    page_head("Statistics for batch $batch_id");
551
    $n = 0;
552
    $wss_sum = 0;
553
    $swap_sum = 0;
554
    $disk_sum = 0;
555
    $wss_max = 0;
556
    $swap_max = 0;
557
    $disk_max = 0;
558
    foreach ($results as $r) {
559
        // pre-7.3.16 clients don't report usage info
560
        //
561
        if ($r->peak_working_set_size == 0) {
562
            continue;
563
        }
564
        $n++;
565
        $wss_sum += $r->peak_working_set_size;
566
        if ($r->peak_working_set_size > $wss_max) {
567
            $wss_max = $r->peak_working_set_size;
568
        }
569
        $swap_sum += $r->peak_swap_size;
570
        if ($r->peak_swap_size > $swap_max) {
571
            $swap_max = $r->peak_swap_size;
572
        }
573
        $disk_sum += $r->peak_disk_usage;
574
        if ($r->peak_disk_usage > $disk_max) {
575
            $disk_max = $r->peak_disk_usage;
576
        }
577
    }
578
    if ($n == 0) {
579
        echo "No qualifying results.";
580
        page_tail();
581
        return;
582
    }
583
    text_start(800);
584
    start_table('table-striped');
585
    row2("qualifying results", $n);
586
    row2("mean WSS", size_string($wss_sum/$n));
587
    row2("max WSS", size_string($wss_max));
588
    row2("mean swap", size_string($swap_sum/$n));
589
    row2("max swap", size_string($swap_max));
590
    row2("mean disk usage", size_string($disk_sum/$n));
591
    row2("max disk usage", size_string($disk_max));
592
    end_table();
593
    text_end();
594
    page_tail();
595
}
596
597
define('COLOR_SUCCESS', 'green');
598
define('COLOR_FAIL', 'red');
599
define('COLOR_IN_PROGRESS', 'deepskyblue');
600
define('COLOR_UNSENT', 'gray');
601
602
// return HTML for a color-coded batch progress bar
603
//
604
function progress_bar($batch, $wus, $width) {
605
    $nsuccess = $batch->njobs_success;
606
    $nerror = $batch->nerror_jobs;
607
    $nin_prog = $batch->njobs_in_prog;
608
    $nunsent = $batch->njobs - $nsuccess - $nerror - $nin_prog;
609
    $w_success = $width*$nsuccess/$batch->njobs;
610
    $w_fail = $width*$nerror/$batch->njobs;
611
    $w_prog = $width*$nin_prog/$batch->njobs;
612
    $w_unsent = $width*$nunsent/$batch->njobs;
613
    $x = '<table height=20><tr>';
614
    if ($w_fail) {
615
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_fail, COLOR_FAIL);
616
    }
617
    if ($w_success) {
618
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_success, COLOR_SUCCESS);
619
    }
620
    if ($w_prog) {
621
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_prog, COLOR_IN_PROGRESS);
622
    }
623
    if ($w_unsent) {
624
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_unsent, COLOR_UNSENT);
625
    }
626
    $x .= sprintf('</tr></table>
627
        <strong>
628
        <font color=%s>%d failed</font> &middot;
629
        <font color=%s>%d completed</font> &middot;
630
        <font color=%s>%d in progress</font> &middot;
631
        <font color=%s>%d unsent</font>
632
        </strong>',
633
        COLOR_FAIL, $nerror,
634
        COLOR_SUCCESS, $nsuccess,
635
        COLOR_IN_PROGRESS, $nin_prog,
636
        COLOR_UNSENT, $nunsent
637
    );
638
    return $x;
639
}
640
641
// show the details of an existing batch.
642
// $user has access to abort/retire the batch
643
// and to get its output files
644
//
645
function handle_query_batch($user) {
646
    $batch_id = get_int('batch_id');
647
    $status = get_int('status', true);
648
    $batch = BoincBatch::lookup_id($batch_id);
649
    if (!$batch) error_page('no batch');
650
    $app = BoincApp::lookup_id($batch->app_id);
651
    $wus = BoincWorkunit::enum_fields(
652
        'id, name, rsc_fpops_est, canonical_credit, canonical_resultid, error_mask',
653
        "batch = $batch->id"
654
    );
655
    $batch = get_batch_params($batch, $wus);
656
    if ($batch->user_id == $user->id) {
657
        $owner = $user;
658
    } else {
659
        $owner = BoincUser::lookup_id($batch->user_id);
660
    }
661
662
    $is_assim_move = is_assim_move($app);
663
664
    page_head("Batch $batch_id");
665
    text_start(800);
666
    start_table();
667
    row2("name", $batch->name);
668
    if ($batch->description) {
669
        row2('description', $batch->description);
670
    }
671
    if ($owner) {
672
        row2('submitter',
673
            "<a href=show_user.php?userid=$owner->id>$owner->name</a>"
674
        );
675
    }
676
    row2("application", $app?$app->name:'---');
677
    row2("state", batch_state_string($batch->state));
678
    //row2("# jobs", $batch->njobs);
679
    //row2("# error jobs", $batch->nerror_jobs);
680
    //row2("logical end time", time_str($batch->logical_end_time));
681
    if ($batch->expire_time) {
682
        row2("expiration time", time_str($batch->expire_time));
683
    }
684
    if ($batch->njobs) {
685
        row2('progress', progress_bar($batch, $wus, 600));
686
    }
687
    if ($batch->completion_time) {
688
        row2("completed", local_time_str($batch->completion_time));
689
    }
690
    row2("GFLOP/hours, estimated", number_format(credit_to_gflop_hours($batch->credit_estimate), 2));
691
    row2("GFLOP/hours, actual", number_format(credit_to_gflop_hours($batch->credit_canonical), 2));
692
    if (!$is_assim_move) {
693
        row2("Total size of output files",
694
            size_string(batch_output_file_size($batch->id))
695
        );
696
    }
697
    end_table();
698
    echo "<p>";
699
700
    if ($is_assim_move) {
701
        $url = "get_output3.php?action=get_batch&batch_id=$batch->id";
702
    } else {
703
        $url = "get_output2.php?cmd=batch&batch_id=$batch->id";
704
    }
705
    echo "<p>";
706
    show_button($url, "Get zipped output files");
707
    echo "<p>";
708
    switch ($batch->state) {
709
    case BATCH_STATE_IN_PROGRESS:
710
        show_button(
711
            "submit.php?action=abort_batch&batch_id=$batch_id",
712
            "Abort batch"
713
        );
714
        break;
715
    case BATCH_STATE_COMPLETE:
716
    case BATCH_STATE_ABORTED:
717
        show_button(
718
            "submit.php?action=retire_batch&batch_id=$batch_id",
719
            "Retire batch"
720
        );
721
        break;
722
    }
723
    echo "<p>
724
        <h3>Completed jobs</h3>
725
        <ul>
726
        <li>
727
        <a href=submit_stats.php?action=flops_graph&batch_id=$batch_id>Job runtimes</a>
728
        <li>
729
        <a href=submit.php?action=batch_stats&batch_id=$batch_id>Memory/disk usage</a>
730
        <li>
731
        <a href=submit_stats.php?action=show_hosts&batch_id=$batch_id>Grouped by host</a>
732
        </ul>
733
        <h3>Failed jobs</h3>
734
        <ul>
735
        <li>
736
        <a href=submit_stats.php?action=err_host&batch_id=$batch_id>Grouped by host</a>
737
        <li>
738
        <a href=submit_stats.php?action=err_code&batch_id=$batch_id>Grouped by exit code</a>
739
        </ul>
740
    ";
741
742
    echo "<h2>Jobs</h2>\n";
743
    $url = "submit.php?action=query_batch&batch_id=$batch_id";
744
    echo "Show: ";
745
    echo sprintf('
746
        <a href=%s&status=%d>failed</a> &middot;
747
        <a href=%s&status=%d>completed</a> &middot;
748
        <a href=%s&status=%d>in progress</a> &middot;
749
        <a href=%s&status=%d>unsent</a> &middot;
750
        <a href=%s>all</a>
751
        <p>',
752
        $url, WU_ERROR,
753
        $url, WU_SUCCESS,
754
        $url, WU_IN_PROGRESS,
755
        $url, WU_UNSENT,
756
        $url
757
    );
758
759
    start_table();
760
    $x = [
761
        "Name <br><small>click for details</small>",
762
        "status",
763
        "GFLOPS-hours"
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
764
    ];
765
    row_heading_array($x);
766
    foreach($wus as $wu) {
767
        if ($status && $wu->status != $status) continue;
768
        $y = '';
769
        $c = '---';
770
        switch($wu->status) {
771
        case WU_SUCCESS:
772
            $resultid = $wu->canonical_resultid;
773
            $y = sprintf('<font color="%s">completed</font>', COLOR_SUCCESS);
774
            $c = number_format(
775
                credit_to_gflop_hours($wu->canonical_credit), 2
776
            );
777
            break;
778
        case WU_ERROR:
779
            $y = sprintf('<font color="%s">failed</font>', COLOR_FAIL);
780
            break;
781
        case WU_IN_PROGRESS:
782
            $y = sprintf('<font color="%s">in progress</font>', COLOR_IN_PROGRESS);
783
            break;
784
        case WU_UNSENT:
785
            $y = sprintf('<font color="%s">unsent</font>', COLOR_UNSENT);
786
            break;
787
        }
788
        $x = [
789
            "<a href=submit.php?action=query_job&wuid=$wu->id>$wu->name</a>",
790
            $y,
791
            $c
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
792
        ];
793
        row_array($x);
794
    }
795
    end_table();
796
    return_link();
797
    text_end();
798
    page_tail();
799
}
800
801
// Does the assimilator for the given app move output files
802
// to a results/<batchid>/ directory?
803
// This info is stored in the $remote_apps data structure in project.inc
804
//
805
function is_assim_move($app) {
806
    global $remote_apps;
807
    foreach ($remote_apps as $category => $apps) {
808
        foreach ($apps as $web_app) {
809
            if ($web_app->app_name == $app->name) {
810
                return $web_app->is_assim_move;
811
            }
812
        }
813
    }
814
    return false;
815
}
816
817
// show the details of a job, including links to see the output files
818
//
819
function handle_query_job($user) {
820
    $wuid = get_int('wuid');
821
    $wu = BoincWorkunit::lookup_id($wuid);
822
    if (!$wu) error_page("no such job");
823
824
    $app = BoincApp::lookup_id($wu->appid);
825
    $is_assim_move = is_assim_move($app);
826
827
    page_head("Job '$wu->name'");
828
    text_start(800);
829
830
    echo "
831
        <ul>
832
        <li><a href=workunit.php?wuid=$wuid>Job details</a>
833
        <p>
834
        <li><a href=submit.php?action=query_batch&batch_id=$wu->batch>Batch details</a>
835
        </ul>
836
    ";
837
    $d = "<foo>$wu->xml_doc</foo>";
838
    $x = simplexml_load_string($d);
839
    $x = $x->workunit;
840
    //echo "foo: $x->command_line";
841
842
    echo "<h2>Job instances</h2>\n";
843
    start_table('table-striped');
844
    table_header(
845
        "ID<br><small>click for details and stderr</small>",
846
        "State",
847
        "Output files"
848
    );
849
    $results = BoincResult::enum("workunitid=$wuid");
850
    $upload_dir = parse_config(get_config(), "<upload_dir>");
851
    $fanout = parse_config(get_config(), "<uldl_dir_fanout>");
852
    foreach ($results as $result) {
853
        $x = [
854
            "<a href=result.php?resultid=$result->id>$result->id</a>",
855
            state_string($result)
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
856
        ];
857
        if ($is_assim_move) {
858
            if ($result->id == $wu->canonical_resultid) {
859
                $log_names = get_outfile_log_names($result);
860
                $nfiles = count($log_names);
861
                for ($i=0; $i<$nfiles; $i++) {
862
                    $name = $log_names[$i];
863
                    // don't show 'view' link if it's a .zip
864
                    $y = "$name: ";
865
                    if (!strstr($name, '.zip')) {
866
                        $y .= sprintf(
867
                            '<a href=get_output3.php?action=get_file&result_id=%d&index=%d>view</a> &middot; ',
868
                            $result->id, $i
869
                        );
870
                    }
871
                    $y .= sprintf(
872
                        '<a href=get_output3.php?action=get_file&result_id=%d&index=%d&download=1>download</a>',
873
                        $result->id, $i
874
                    );
875
                    $x[] = $y;
876
                }
877
            } else {
878
                $x[] = '---';
879
            }
880
        } else {
881
            if ($result->server_state == RESULT_SERVER_STATE_OVER) {
882
                $phys_names = get_outfile_phys_names($result);
883
                $log_names = get_outfile_log_names($result);
884
                $nfiles = count($log_names);
885
                for ($i=0; $i<$nfiles; $i++) {
886
                    $path = dir_hier_path(
887
                        $phys_names[$i], $upload_dir, $fanout
888
                    );
889
                    if (file_exists($path)) {
890
                        $url = sprintf(
891
                            'get_output2.php?cmd=result&result_id=%d&file_num=%d',
892
                            $result->id, $i
893
                        );
894
                        $s = stat($path);
895
                        $size = $s['size'];
896
                        $x[] = sprintf('<a href=%s>%s</a> (%s bytes)<br/>',
897
                            $url,
898
                            $log_names[$i],
899
                            number_format($size)
900
                        );
901
                    } else {
902
                        $x[] = sprintf("file '%s' is missing", $log_names[$i]);
903
                    }
904
                }
905
            } else {
906
                $x[] = '---';
907
            }
908
        }
909
        row_array($x);
910
    }
911
    end_table();
912
913
    // show input files
914
    //
915
    echo "<h2>Input files</h2>\n";
916
    $x = "<in>".$wu->xml_doc."</in>";
917
    $x = simplexml_load_string($x);
918
    if ($x->workunit->file_ref) {
919
        start_table('table-striped');
920
        table_header("Name<br><small>(click to view)</small>", "Size (bytes)");
921
        foreach ($x->workunit->file_ref as $fr) {
922
            $pname = (string)$fr->file_name;
923
            $lname = (string)$fr->open_name;
924
            foreach ($x->file_info as $fi) {
925
                if ((string)$fi->name == $pname) {
926
                    table_row(
927
                        "<a href=$fi->url>$lname</a>",
928
                        $fi->nbytes
929
                    );
930
                    break;
931
                }
932
            }
933
        }
934
        end_table();
935
    } else {
936
        echo "The job has no input files.<p>";
937
    }
938
939
    text_end();
940
    return_link();
941
    page_tail();
942
}
943
944
// is user allowed to retire or abort this batch?
945
//
946
function has_access($user, $batch) {
947
    if ($user->id == $batch->user_id) return true;
948
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
949
    if ($user_submit->manage_all) return true;
950
    $usa = BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$batch->app_id");
951
    if ($usa->manage) return true;
952
    return false;
953
}
954
955
function handle_abort_batch($user) {
956
    $batch_id = get_int('batch_id');
957
    $batch = BoincBatch::lookup_id($batch_id);
958
    if (!$batch) error_page("no such batch");
959
    if (!has_access($user, $batch)) {
960
        error_page("no access");
961
    }
962
963
    if (get_int('confirmed', true)) {
964
        abort_batch($batch);
965
        page_head("Batch $batch_id aborted");
966
        return_link();
967
        page_tail();
968
    } else {
969
        page_head("Confirm abort batch");
970
        echo "
971
            Aborting a batch will cancel all unstarted jobs.
972
            Are you sure you want to do this?
973
            <p>
974
        ";
975
        show_button(
976
            "submit.php?action=abort_batch&batch_id=$batch_id&confirmed=1",
977
            "Yes - abort batch"
978
        );
979
        return_link();
980
        page_tail();
981
    }
982
}
983
984
function handle_retire_batch($user) {
985
    $batch_id = get_int('batch_id');
986
    $batch = BoincBatch::lookup_id($batch_id);
987
    if (!$batch) error_page("no such batch");
988
    if (!has_access($user, $batch)) {
989
        error_page("no access");
990
    }
991
992
    if (get_int('confirmed', true)) {
993
        retire_batch($batch);
994
        page_head("Batch $batch_id retired");
995
        return_link();
996
        page_tail();
997
    } else {
998
        page_head("Confirm retire batch");
999
        echo "
1000
            Retiring a batch will remove all of its output files.
1001
            Are you sure you want to do this?
1002
            <p>
1003
        ";
1004
        show_button(
1005
            "submit.php?action=retire_batch&batch_id=$batch_id&confirmed=1",
1006
            "Yes - retire batch"
1007
        );
1008
        return_link();
1009
        page_tail();
1010
    }
1011
}
1012
1013
// retire multiple batches
1014
//
1015
function handle_retire_multi($user) {
1016
    $batches = BoincBatch::enum(
1017
        sprintf('state in (%d, %d)', BATCH_STATE_COMPLETE, BATCH_STATE_ABORTED)
1018
    );
1019
    page_head('Retiring batches');
1020
    foreach ($batches as $batch) {
1021
        if (!has_access($user, $batch)) {
1022
            continue;
1023
        }
1024
        $x = sprintf('retire_%d', $batch->id);
1025
        if (get_str($x, true) == 'on') {
1026
            retire_batch($batch);
1027
            echo "<p>retired batch $batch->id ($batch->name)\n";
1028
        }
1029
    }
1030
    return_link();
1031
    page_tail();
1032
}
1033
1034
// given a list of batches, show the ones in a given state
1035
//
1036
function show_batches_in_state($batches, $state, $url_args, $order) {
1037
    switch ($state) {
1038
    case BATCH_STATE_IN_PROGRESS:
1039
        page_head("Batches in progress");
1040
        order_options($url_args, $order);
1041
        show_in_progress($batches, $order, 0, null, null);
1042
        break;
1043
    case BATCH_STATE_COMPLETE:
1044
        page_head("Completed batches");
1045
        order_options($url_args, $order);
1046
        show_complete($batches, $order, 0, null, null);
1047
        break;
1048
    case BATCH_STATE_ABORTED:
1049
        page_head("Aborted batches");
1050
        order_options($url_args, $order);
1051
        show_aborted($batches, $order, 0, null, null);
1052
        break;
1053
    }
1054
    page_tail();
1055
}
1056
1057
// show all batches visible to user, possibly limited by user/app/state
1058
function handle_show_all_batches($user) {
1059
    $userid = get_int("userid");
1060
    $appid = get_int("appid");
1061
    $state = get_int("state");
1062
    $order = get_order();
1063
    $url_args = "action=show_all_batches&state=$state&userid=$userid&appid=$appid";
1064
    if ($userid) {
1065
        // user looking at their own batches
1066
        //
1067
        if ($userid != $user->id) error_page("wrong user");
1068
        $batches = BoincBatch::enum("user_id=$user->id and state=$state");
1069
        fill_in_app_and_user_names($batches);
1070
        $batches = get_batches_params($batches);
1071
        show_batches_in_state($batches, $state, $url_args, $order);
1072
    } else {
1073
        // admin looking at batches
1074
        //
1075
        if (!has_manage_access($user, $appid)) {
1076
            error_page('no access');
1077
        }
1078
        if ($appid) {
1079
            $app = BoincApp::lookup_id($appid);
1080
            if (!$app) error_page("no such app");
1081
            $batches = BoincBatch::enum("app_id=$appid and state=$state");
1082
        } else {
1083
            $batches = BoincBatch::enum("state=$state");
1084
        }
1085
        fill_in_app_and_user_names($batches);
1086
        $batches = get_batches_params($batches);
1087
        show_batches_in_state($batches, $state, $url_args, $order);
1088
    }
1089
}
1090
1091
$user = get_logged_in_user();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $user is correct as get_logged_in_user() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1092
1093
$action = get_str('action', true);
1094
1095
switch ($action) {
1096
1097
// links to job submission forms
1098
case '': show_submit_links($user); break;
1099
1100
// show lists of batches
1101
case 'show_all_batches': handle_show_all_batches($user); break;
1102
case 'show_user_batches': handle_show_user_batches($user); break;
1103
case 'show_batches_admin_app': show_batches_admin_app($user); break;
1104
case 'admin_all': handle_admin_all($user); break;
1105
1106
// show info about a batch or job
1107
case 'batch_stats': handle_batch_stats($user); break;
1108
case 'query_batch': handle_query_batch($user); break;
1109
case 'query_job': handle_query_job($user); break;
1110
1111
// operations on batches
1112
case 'retire_batch': handle_retire_batch($user); break;
1113
case 'retire_multi': handle_retire_multi($user); break;
1114
case 'abort_batch': handle_abort_batch($user); break;
1115
1116
// access control
1117
case 'admin': handle_admin($user); break;
1118
1119
// 'run jobs only on my computers' flag (stored in user.seti_id)
1120
case 'update_only_own': handle_update_only_own($user); break;
1121
1122
default:
0 ignored issues
show
Coding Style introduced by
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
Coding Style introduced by
DEFAULT case must have a breaking statement
Loading history...
1123
    error_page("no such action $action");
1124
}
1125
1126
?>
1127