handle_query_batch()   F
last analyzed

Complexity

Conditions 20
Paths 7168

Size

Total Lines 151
Code Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 2 Features 0
Metric Value
cc 20
eloc 105
c 7
b 2
f 0
nc 7168
nop 1
dl 0
loc 151
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
// TODO: speed this up by making list of app and user IDs
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...
315
// and doing lookup just once.
316
//
317
function fill_in_app_and_user_names(&$batches) {
318
    $apps = [];
319
    foreach ($batches as $batch) {
320
        if (array_key_exists($batch->app_id, $apps)) {
321
            $app = $apps[$batch->app_id];
322
        } else {
323
            $app = BoincApp::lookup_id($batch->app_id);
324
            $apps[$batch->app_id] = $app;
325
        }
326
        if ($app) {
327
            $batch->app_name = $app->name;
328
            if ($batch->description) {
329
                $batch->app_name .= ": $batch->description";
330
            }
331
        } else {
332
            $batch->app_name = "unknown";
333
        }
334
        $user = BoincUser::lookup_id($batch->user_id);
335
        if ($user) {
336
            $batch->user_name = $user->name;
337
        } else {
338
            $batch->user_name = "missing user $batch->user_id";
339
        }
340
    }
341
}
342
343
// show a set of batches: in progress, then completed, then aborted
344
//
345
function show_batches($batches, $order, $limit, $user, $app) {
346
    fill_in_app_and_user_names($batches);
347
    $batches = get_batches_params($batches);
348
    show_in_progress($batches, $order, $limit, $user, $app);
349
    show_complete($batches, $order, $limit, $user, $app);
350
    show_aborted($batches, $order, $limit, $user, $app);
351
}
352
353
// show links to per-app job submission forms
354
//
355
function show_submit_links($user) {
356
    global $remote_apps;
357
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
358
    if (!$user_submit) {
359
        error_page("Ask the project admins for permission to submit jobs");
360
    }
361
362
    page_head("Submit jobs");
363
364
    // show links to per-app job submission pages
365
    //
366
    foreach ($remote_apps as $area => $apps) {
367
        panel($area,
368
            function() use ($apps) {
369
                foreach ($apps as $app) {
370
                    if (empty($app->form)) continue;
371
                    // show app logo if available
372
                    if (!empty($app->logo)) {
373
                        echo sprintf(
374
                            '<a href=%s><img width=100 src=%s></a>&nbsp;',
375
                            $app->form, $app->logo
376
                        );
377
                    } else {
378
                        echo sprintf(
379
                            '<li><a href=%s>%s</a><p>',
380
                            $app->form, $app->long_name
381
                        );
382
                    }
383
                }
384
            }
385
        );
386
    }
387
388
    form_start('submit.php');
389
    form_input_hidden('action', 'update_only_own');
390
    form_radio_buttons(
391
        'Jobs you submit can run', 'only_own',
392
        [
393
            [0, 'on any computer'],
394
            [1, 'only on your computers']
0 ignored issues
show
Coding Style introduced by
There should be a trailing comma after the last value of an array declaration.
Loading history...
395
        ],
396
        $user->seti_id
397
    );
398
    form_submit('Update');
399
    form_end();
400
    page_tail();
401
}
402
403
// show batches of logged in user.
404
// They have manage access to these batches.
405
//
406
function handle_show_user_batches($user) {
407
    page_head("Your batches");
408
    $order = get_order();
409
    order_options('action=show_user_batches', $order);
410
    $batches = BoincBatch::enum("user_id = $user->id");
411
    show_batches($batches, $order, PAGE_SIZE, $user, null);
412
413
    page_tail();
414
}
415
416
function handle_update_only_own($user) {
417
    $val = get_int('only_own');
418
    $user->update("seti_id=$val");
419
    header("Location: submit.php");
420
}
421
422
// get list of app names of remote apps
423
//
424
function get_remote_app_names() {
425
    global $remote_apps;
426
    $x = [];
427
    foreach ($remote_apps as $category => $apps) {
428
        foreach ($apps as $app) {
429
            $x[] = $app->app_name;
430
        }
431
    }
432
    return array_unique($x);
433
}
434
435
// show links for everything the user has admin access to
436
//
437
function handle_admin($user) {
438
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
439
    if (!$user_submit) error_page('no access');
440
    if ($user_submit->manage_all) {
441
        // user can administer all apps
442
        //
443
        page_head("Job submission: manage all apps");
444
        echo "<li> <a href=submit.php?action=admin_all>View/manage all batches</a>
445
        ";
446
        $app_names = get_remote_app_names();
447
        foreach ($app_names as $app_name) {
448
            $app_name = BoincDb::escape_string($app_name);
449
            $app = BoincApp::lookup("name='$app_name'");
450
            echo "
451
                <li>$app->user_friendly_name<br>
452
                <ul>
453
                <li><a href=submit.php?action=show_batches_admin_app&app_id=$app->id>View/manage batches</a>
454
            ";
455
            if ($app_name == 'buda') {
456
                echo "
457
                    <li> <a href=buda.php>Manage BUDA apps and variants</a>
458
                ";
459
            } else {
460
                echo "
461
                    <li> <a href=manage_app.php?app_id=$app->id&amp;action=app_version_form>Manage app versions</a>
462
                ";
463
            }
464
            echo "
465
                </ul>
466
            ";
467
        }
468
    } else {
469
        // see if user can administer specific apps
470
        //
471
        page_head("Job submission: manage apps");
472
        $usas = BoincUserSubmitApp::enum("user_id=$user->id");
473
        foreach ($usas as $usa) {
474
            $app = BoincApp::lookup_id($usa->app_id);
475
            echo "<li>$app->user_friendly_name<br>
476
                <a href=submit.php?action=show_batches_admin_app&app_id=$app->id>Batches</a>
477
            ";
478
            if ($usa->manage) {
479
                echo "&middot;
480
                    <a href=manage_app.php?app_id=$app->id&action=app_version_form>Versions</a>
481
                ";
482
            }
483
        }
484
    }
485
    echo "</ul>\n";
486
    page_tail();
487
}
488
489
// show all batches for given app to administrator
490
//
491
function show_batches_admin_app($user) {
492
    $app_id = get_int("app_id");
493
    $app = BoincApp::lookup_id($app_id);
494
    if (!$app) error_page("no such app");
495
    if (!has_manage_access($user, $app_id)) {
496
        error_page('no access');
497
    }
498
499
    $order = get_order();
500
501
    page_head("Manage batches for $app->user_friendly_name");
502
    order_options("action=show_batches_admin_app&app_id=$app_id", $order);
503
    $batches = BoincBatch::enum("app_id = $app_id");
504
    show_batches($batches, $order, PAGE_SIZE, null, $app);
505
    page_tail();
506
}
507
508
function handle_admin_all($user) {
509
    $order = get_order();
510
    page_head("Administer batches (all apps and users)");
511
    order_options("action=admin_all", $order);
512
    $batches = BoincBatch::enum('');
513
    show_batches($batches, $order, PAGE_SIZE, null, null);
514
    page_tail();
515
}
516
517
518
// show the statics of mem/disk usage of jobs in a batch
519
//
520
function handle_batch_stats($user) {
521
    $batch_id = get_int('batch_id');
522
    $batch = BoincBatch::lookup_id($batch_id);
523
    $results = BoincResult::enum_fields(
524
        'peak_working_set_size, peak_swap_size, peak_disk_usage',
525
        sprintf('batch = %d and outcome=%d',
526
            $batch->id, RESULT_OUTCOME_SUCCESS
527
        )
528
    );
529
    page_head("Statistics for batch $batch_id");
530
    $n = 0;
531
    $wss_sum = 0;
532
    $swap_sum = 0;
533
    $disk_sum = 0;
534
    $wss_max = 0;
535
    $swap_max = 0;
536
    $disk_max = 0;
537
    foreach ($results as $r) {
538
        // pre-7.3.16 clients don't report usage info
539
        //
540
        if ($r->peak_working_set_size == 0) {
541
            continue;
542
        }
543
        $n++;
544
        $wss_sum += $r->peak_working_set_size;
545
        if ($r->peak_working_set_size > $wss_max) {
546
            $wss_max = $r->peak_working_set_size;
547
        }
548
        $swap_sum += $r->peak_swap_size;
549
        if ($r->peak_swap_size > $swap_max) {
550
            $swap_max = $r->peak_swap_size;
551
        }
552
        $disk_sum += $r->peak_disk_usage;
553
        if ($r->peak_disk_usage > $disk_max) {
554
            $disk_max = $r->peak_disk_usage;
555
        }
556
    }
557
    if ($n == 0) {
558
        echo "No qualifying results.";
559
        page_tail();
560
        return;
561
    }
562
    text_start(800);
563
    start_table('table-striped');
564
    row2("qualifying results", $n);
565
    row2("mean WSS", size_string($wss_sum/$n));
566
    row2("max WSS", size_string($wss_max));
567
    row2("mean swap", size_string($swap_sum/$n));
568
    row2("max swap", size_string($swap_max));
569
    row2("mean disk usage", size_string($disk_sum/$n));
570
    row2("max disk usage", size_string($disk_max));
571
    end_table();
572
    text_end();
573
    page_tail();
574
}
575
576
define('COLOR_SUCCESS', 'green');
577
define('COLOR_FAIL', 'red');
578
define('COLOR_IN_PROGRESS', 'deepskyblue');
579
define('COLOR_UNSENT', 'gray');
580
581
// return HTML for a color-coded batch progress bar
582
//
583
function progress_bar($batch, $wus, $width) {
584
    $nsuccess = $batch->njobs_success;
585
    $nerror = $batch->nerror_jobs;
586
    $nin_prog = $batch->njobs_in_prog;
587
    $nunsent = $batch->njobs - $nsuccess - $nerror - $nin_prog;
588
    $w_success = $width*$nsuccess/$batch->njobs;
589
    $w_fail = $width*$nerror/$batch->njobs;
590
    $w_prog = $width*$nin_prog/$batch->njobs;
591
    $w_unsent = $width*$nunsent/$batch->njobs;
592
    $x = '<table height=20><tr>';
593
    if ($w_fail) {
594
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_fail, COLOR_FAIL);
595
    }
596
    if ($w_success) {
597
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_success, COLOR_SUCCESS);
598
    }
599
    if ($w_prog) {
600
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_prog, COLOR_IN_PROGRESS);
601
    }
602
    if ($w_unsent) {
603
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_unsent, COLOR_UNSENT);
604
    }
605
    $x .= sprintf('</tr></table>
606
        <strong>
607
        <font color=%s>%d failed</font> &middot;
608
        <font color=%s>%d completed</font> &middot;
609
        <font color=%s>%d in progress</font> &middot;
610
        <font color=%s>%d unsent</font>
611
        </strong>',
612
        COLOR_FAIL, $nerror,
613
        COLOR_SUCCESS, $nsuccess,
614
        COLOR_IN_PROGRESS, $nin_prog,
615
        COLOR_UNSENT, $nunsent
616
    );
617
    return $x;
618
}
619
620
// show the details of an existing batch.
621
// $user has access to abort/retire the batch
622
// and to get its output files
623
//
624
function handle_query_batch($user) {
625
    $batch_id = get_int('batch_id');
626
    $status = get_int('status', true);
627
    $batch = BoincBatch::lookup_id($batch_id);
628
    $app = BoincApp::lookup_id($batch->app_id);
629
    $wus = BoincWorkunit::enum_fields(
630
        'id, name, rsc_fpops_est, canonical_credit, canonical_resultid, error_mask',
631
        "batch = $batch->id"
632
    );
633
    $batch = get_batch_params($batch, $wus);
634
    if ($batch->user_id == $user->id) {
635
        $owner = $user;
636
    } else {
637
        $owner = BoincUser::lookup_id($batch->user_id);
638
    }
639
640
    $is_assim_move = is_assim_move($app);
641
642
    page_head("Batch $batch_id");
643
    text_start(800);
644
    start_table();
645
    row2("name", $batch->name);
646
    if ($batch->description) {
647
        row2('description', $batch->description);
648
    }
649
    if ($owner) {
650
        row2('submitter', $owner->name);
651
    }
652
    row2("application", $app?$app->name:'---');
653
    row2("state", batch_state_string($batch->state));
654
    //row2("# jobs", $batch->njobs);
655
    //row2("# error jobs", $batch->nerror_jobs);
656
    //row2("logical end time", time_str($batch->logical_end_time));
657
    if ($batch->expire_time) {
658
        row2("expiration time", time_str($batch->expire_time));
659
    }
660
    if ($batch->njobs) {
661
        row2('progress', progress_bar($batch, $wus, 600));
662
    }
663
    if ($batch->completion_time) {
664
        row2("completed", local_time_str($batch->completion_time));
665
    }
666
    row2("GFLOP/hours, estimated", number_format(credit_to_gflop_hours($batch->credit_estimate), 2));
667
    row2("GFLOP/hours, actual", number_format(credit_to_gflop_hours($batch->credit_canonical), 2));
668
    if (!$is_assim_move) {
669
        row2("Total size of output files",
670
            size_string(batch_output_file_size($batch->id))
671
        );
672
    }
673
    end_table();
674
    echo "<p>";
675
676
    if ($is_assim_move) {
677
        $url = "get_output3.php?action=get_batch&batch_id=$batch->id";
678
    } else {
679
        $url = "get_output2.php?cmd=batch&batch_id=$batch->id";
680
    }
681
    echo "<p>";
682
    show_button($url, "Get zipped output files");
683
    echo "<p>";
684
    switch ($batch->state) {
685
    case BATCH_STATE_IN_PROGRESS:
686
        show_button(
687
            "submit.php?action=abort_batch&batch_id=$batch_id",
688
            "Abort batch"
689
        );
690
        break;
691
    case BATCH_STATE_COMPLETE:
692
    case BATCH_STATE_ABORTED:
693
        show_button(
694
            "submit.php?action=retire_batch&batch_id=$batch_id",
695
            "Retire batch"
696
        );
697
        break;
698
    }
699
    echo "<p>
700
        <h3>Completed jobs</h3>
701
        <ul>
702
        <li>
703
        <a href=submit_stats.php?action=flops_graph&batch_id=$batch_id>Job runtimes</a>
704
        <li>
705
        <a href=submit.php?action=batch_stats&batch_id=$batch_id>Memory/disk usage</a>
706
        <li>
707
        <a href=submit_stats.php?action=show_hosts&batch_id=$batch_id>Grouped by host</a>
708
        </ul>
709
        <h3>Failed jobs</h3>
710
        <ul>
711
        <li>
712
        <a href=submit_stats.php?action=err_host&batch_id=$batch_id>Grouped by host</a>
713
        <li>
714
        <a href=submit_stats.php?action=err_code&batch_id=$batch_id>Grouped by exit code</a>
715
        </ul>
716
    ";
717
718
    echo "<h2>Jobs</h2>\n";
719
    $url = "submit.php?action=query_batch&batch_id=$batch_id";
720
    echo "Show: ";
721
    echo sprintf('
722
        <a href=%s&status=%d>failed</a> &middot;
723
        <a href=%s&status=%d>completed</a> &middot;
724
        <a href=%s&status=%d>in progress</a> &middot;
725
        <a href=%s&status=%d>unsent</a> &middot;
726
        <a href=%s>all</a>
727
        <p>',
728
        $url, WU_ERROR,
729
        $url, WU_SUCCESS,
730
        $url, WU_IN_PROGRESS,
731
        $url, WU_UNSENT,
732
        $url
733
    );
734
735
    start_table();
736
    $x = [
737
        "Name <br><small>click for details</small>",
738
        "status",
739
        "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...
740
    ];
741
    row_heading_array($x);
742
    foreach($wus as $wu) {
743
        if ($status && $wu->status != $status) continue;
744
        $y = '';
745
        $c = '---';
746
        switch($wu->status) {
747
        case WU_SUCCESS:
748
            $resultid = $wu->canonical_resultid;
749
            $y = sprintf('<font color="%s">completed</font>', COLOR_SUCCESS);
750
            $c = number_format(
751
                credit_to_gflop_hours($wu->canonical_credit), 2
752
            );
753
            break;
754
        case WU_ERROR:
755
            $y = sprintf('<font color="%s">failed</font>', COLOR_FAIL);
756
            break;
757
        case WU_IN_PROGRESS:
758
            $y = sprintf('<font color="%s">in progress</font>', COLOR_IN_PROGRESS);
759
            break;
760
        case WU_UNSENT:
761
            $y = sprintf('<font color="%s">unsent</font>', COLOR_UNSENT);
762
            break;
763
        }
764
        $x = [
765
            "<a href=submit.php?action=query_job&wuid=$wu->id>$wu->name</a>",
766
            $y,
767
            $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...
768
        ];
769
        row_array($x);
770
    }
771
    end_table();
772
    return_link();
773
    text_end();
774
    page_tail();
775
}
776
777
// Does the assimilator for the given app move output files
778
// to a results/<batchid>/ directory?
779
// This info is stored in the $remote_apps data structure in project.inc
780
//
781
function is_assim_move($app) {
782
    global $remote_apps;
783
    foreach ($remote_apps as $category => $apps) {
784
        foreach ($apps as $web_app) {
785
            if ($web_app->app_name == $app->name) {
786
                return $web_app->is_assim_move;
787
            }
788
        }
789
    }
790
    return false;
791
}
792
793
// show the details of a job, including links to see the output files
794
//
795
function handle_query_job($user) {
796
    $wuid = get_int('wuid');
797
    $wu = BoincWorkunit::lookup_id($wuid);
798
    if (!$wu) error_page("no such job");
799
800
    $app = BoincApp::lookup_id($wu->appid);
801
    $is_assim_move = is_assim_move($app);
802
803
    page_head("Job '$wu->name'");
804
    text_start(800);
805
806
    echo "
807
        <li><a href=workunit.php?wuid=$wuid>Job details</a>
808
        <p>
809
        <li><a href=submit.php?action=query_batch&batch_id=$wu->batch>Batch details</a>
810
    ";
811
    $d = "<foo>$wu->xml_doc</foo>";
812
    $x = simplexml_load_string($d);
813
    $x = $x->workunit;
814
    //echo "foo: $x->command_line";
815
816
    echo "<h2>Job instances</h2>\n";
817
    start_table('table-striped');
818
    table_header(
819
        "ID<br><small>click for details and stderr</small>",
820
        "State",
821
        "Output files"
822
    );
823
    $results = BoincResult::enum("workunitid=$wuid");
824
    $upload_dir = parse_config(get_config(), "<upload_dir>");
825
    $fanout = parse_config(get_config(), "<uldl_dir_fanout>");
826
    foreach($results as $result) {
827
        $x = [
828
            "<a href=result.php?resultid=$result->id>$result->id</a>",
829
            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...
830
        ];
831
        $i = 0;
832
        if ($result->server_state == RESULT_SERVER_STATE_OVER) {
833
            $phys_names = get_outfile_phys_names($result);
834
            $log_names = get_outfile_log_names($result);
835
            for ($i=0; $i<count($phys_names); $i++) {
0 ignored issues
show
Coding Style Performance introduced by
The use of count() inside a loop condition is not allowed; assign the return value to a variable and use the variable in the loop condition instead
Loading history...
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
836
                if ($is_assim_move) {
837
                    // file is in
838
                    // project/results/<batchid>/<wu_name>__file_<log_name>
839
                    $path = sprintf('results/%s/%s__file_%s',
840
                        $wu->batch, $wu->name, $log_names[$i]
841
                    );
842
                    $name = $log_names[$i];
843
                    // don't show 'view' link if it's a .zip
844
                    $y = "$name: ";
845
                    if (!strstr($name, '.zip')) {
846
                        $y .= "<a href=get_output3.php?action=get_file&path=$path>view</a> &middot; ";
847
                    }
848
                    $y .= "<a href=get_output3.php?action=get_file&path=$path&download=1>download</a>";
849
                    $x[] = $y;
850
                } else {
851
                    $path = dir_hier_path(
852
                        $phys_names[$i], $upload_dir, $fanout
853
                    );
854
                    if (file_exists($path)) {
855
                        $url = sprintf(
856
                            'get_output2.php?cmd=result&result_id=%d&file_num=%d',
857
                            $result->id, $i
858
                        );
859
                        $s = stat($path);
860
                        $size = $s['size'];
861
                        $x[] = sprintf('<a href=%s>%s</a> (%s bytes)<br/>',
862
                            $url,
863
                            $log_names[$i],
864
                            number_format($size)
865
                        );
866
                    } else {
867
                        $x[] = sprintf("file '%s' is missing", $log_names[$i]);
868
                    }
869
                }
870
            }
871
        } else {
872
            $x[] = '---';
873
        }
874
        row_array($x);
875
    }
876
    end_table();
877
878
    // show input files
879
    //
880
    echo "<h2>Input files</h2>\n";
881
    $x = "<in>".$wu->xml_doc."</in>";
882
    $x = simplexml_load_string($x);
883
    start_table('table-striped');
884
    table_header("Name<br><small>(click to view)</small>", "Size (bytes)");
885
    foreach ($x->workunit->file_ref as $fr) {
886
        $pname = (string)$fr->file_name;
887
        $lname = (string)$fr->open_name;
888
        foreach ($x->file_info as $fi) {
889
            if ((string)$fi->name == $pname) {
890
                table_row(
891
                    "<a href=$fi->url>$lname</a>",
892
                    $fi->nbytes
893
                );
894
                break;
895
            }
896
        }
897
    }
898
899
    end_table();
900
    text_end();
901
    return_link();
902
    page_tail();
903
}
904
905
// is user allowed to retire or abort this batch?
906
//
907
function has_access($user, $batch) {
908
    if ($user->id == $batch->user_id) return true;
909
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
910
    if ($user_submit->manage_all) return true;
911
    $usa = BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$batch->app_id");
912
    if ($usa->manage) return true;
913
    return false;
914
}
915
916
function handle_abort_batch($user) {
917
    $batch_id = get_int('batch_id');
918
    $batch = BoincBatch::lookup_id($batch_id);
919
    if (!$batch) error_page("no such batch");
920
    if (!has_access($user, $batch)) {
921
        error_page("no access");
922
    }
923
924
    if (get_int('confirmed', true)) {
925
        abort_batch($batch);
926
        page_head("Batch aborted");
927
        return_link();
928
        page_tail();
929
    } else {
930
        page_head("Confirm abort batch");
931
        echo "
932
            Aborting a batch will cancel all unstarted jobs.
933
            Are you sure you want to do this?
934
            <p>
935
        ";
936
        show_button(
937
            "submit.php?action=abort_batch&batch_id=$batch_id&confirmed=1",
938
            "Yes - abort batch"
939
        );
940
        return_link();
941
        page_tail();
942
    }
943
}
944
945
function handle_retire_batch($user) {
946
    $batch_id = get_int('batch_id');
947
    $batch = BoincBatch::lookup_id($batch_id);
948
    if (!$batch) error_page("no such batch");
949
    if (!has_access($user, $batch)) {
950
        error_page("no access");
951
    }
952
953
    if (get_int('confirmed', true)) {
954
        retire_batch($batch);
955
        page_head("Batch $batch_id retired");
956
        return_link();
957
        page_tail();
958
    } else {
959
        page_head("Confirm retire batch");
960
        echo "
961
            Retiring a batch will remove all of its output files.
962
            Are you sure you want to do this?
963
            <p>
964
        ";
965
        show_button(
966
            "submit.php?action=retire_batch&batch_id=$batch_id&confirmed=1",
967
            "Yes - retire batch"
968
        );
969
        return_link();
970
        page_tail();
971
    }
972
}
973
974
// retire multiple batches
975
//
976
function handle_retire_multi($user) {
977
    $batches = BoincBatch::enum(
978
        sprintf('state=%d', BATCH_STATE_COMPLETE)
979
    );
980
    page_head('Retiring batches');
981
    foreach ($batches as $batch) {
982
        if (!has_access($user, $batch)) {
983
            continue;
984
        }
985
        $x = sprintf('retire_%d', $batch->id);
986
        if (get_str($x, true) == 'on') {
987
            retire_batch($batch);
988
            echo "<p>retired batch $batch->id ($batch->name)\n";
989
        }
990
    }
991
    return_link();
992
    page_tail();
993
}
994
995
// given a list of batches, show the ones in a given state
996
//
997
function show_batches_in_state($batches, $state, $url_args, $order) {
998
    switch ($state) {
999
    case BATCH_STATE_IN_PROGRESS:
1000
        page_head("Batches in progress");
1001
        order_options($url_args, $order);
1002
        show_in_progress($batches, $order, 0, null, null);
1003
        break;
1004
    case BATCH_STATE_COMPLETE:
1005
        page_head("Completed batches");
1006
        order_options($url_args, $order);
1007
        show_complete($batches, $order, 0, null, null);
1008
        break;
1009
    case BATCH_STATE_ABORTED:
1010
        page_head("Aborted batches");
1011
        order_options($url_args, $order);
1012
        show_aborted($batches, $order, 0, null, null);
1013
        break;
1014
    }
1015
    page_tail();
1016
}
1017
1018
// show all batches visible to user, possibly limited by user/app/state
1019
function handle_show_all_batches($user) {
1020
    $userid = get_int("userid");
1021
    $appid = get_int("appid");
1022
    $state = get_int("state");
1023
    $order = get_order();
1024
    $url_args = "action=show_all_batches&state=$state&userid=$userid&appid=$appid";
1025
    if ($userid) {
1026
        // user looking at their own batches
1027
        //
1028
        if ($userid != $user->id) error_page("wrong user");
1029
        $batches = BoincBatch::enum("user_id=$user->id and state=$state");
1030
        fill_in_app_and_user_names($batches);
1031
        show_batches_in_state($batches, $state, $url_args, $order);
1032
    } else {
1033
        // admin looking at batches
1034
        //
1035
        if (!has_manage_access($user, $appid)) {
1036
            error_page('no access');
1037
        }
1038
        if ($appid) {
1039
            $app = BoincApp::lookup_id($appid);
1040
            if (!$app) error_page("no such app");
1041
            $batches = BoincBatch::enum("app_id=$appid and state=$state");
1042
        } else {
1043
            $batches = BoincBatch::enum("state=$state");
1044
        }
1045
        fill_in_app_and_user_names($batches);
1046
        show_batches_in_state($batches, $state, $url_args, $order);
1047
    }
1048
}
1049
1050
$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...
1051
1052
$action = get_str('action', true);
1053
1054
switch ($action) {
1055
1056
// links to job submission forms
1057
case '': show_submit_links($user); break;
1058
1059
// show lists of batches
1060
case 'show_all_batches': handle_show_all_batches($user); break;
1061
case 'show_user_batches': handle_show_user_batches($user); break;
1062
case 'show_batches_admin_app': show_batches_admin_app($user); break;
1063
case 'admin_all': handle_admin_all($user); break;
1064
1065
// show info about a batch or job
1066
case 'batch_stats': handle_batch_stats($user); break;
1067
case 'query_batch': handle_query_batch($user); break;
1068
case 'query_job': handle_query_job($user); break;
1069
1070
// operations on batches
1071
case 'retire_batch': handle_retire_batch($user); break;
1072
case 'retire_multi': handle_retire_multi($user); break;
1073
case 'abort_batch': handle_abort_batch($user); break;
1074
1075
// access control
1076
case 'admin': handle_admin($user); break;
1077
1078
// 'run jobs only on my computers' flag (stored in user.seti_id)
1079
case 'update_only_own': handle_update_only_own($user); break;
1080
1081
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...
1082
    error_page("no such action $action");
1083
}
1084
1085
?>
1086