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 8
Bugs 2 Features 0
Metric Value
cc 20
eloc 105
c 8
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
// get params of in-progress batches; they might not be in progress anymore.
160
//
161
function get_batches_params($batches) {
162
    $b = [];
163
    foreach ($batches as $batch) {
164
        if ($batch->state == BATCH_STATE_IN_PROGRESS) {
165
            $wus = BoincWorkunit::enum_fields(
166
                'id, name, rsc_fpops_est, canonical_credit, canonical_resultid, error_mask',
167
                "batch = $batch->id"
168
            );
169
            $b[] = get_batch_params($batch, $wus);
170
        } else {
171
            $b[] = $batch;
172
        }
173
    }
174
    return $b;
175
}
176
177
function state_count($batches, $state) {
178
    $n = 0;
179
    foreach ($batches as $batch) {
180
        if ($batch->state == $state) $n++;
181
    }
182
    return $n;
183
}
184
185
function show_all_batches_link($batches, $state, $limit, $user, $app) {
186
    $n = state_count($batches, $state);
187
    if ($n > $limit) {
188
        if ($user) $userid = $user->id;
189
        else $userid = 0;
190
        if ($app) $appid = $app->id;
191
        else $appid = 0;
192
193
        echo "Showing the most recent $limit of $n batches.
194
            <a href=submit.php?action=show_all_batches&state=$state&userid=$userid&appid=$appid>Show all $n</a>
195
            <p>
196
        ";
197
    }
198
}
199
200
// show in-progress batches.
201
//
202
function show_in_progress($all_batches, $order, $limit, $user, $app) {
203
    $batches = batches_in_state($all_batches, BATCH_STATE_IN_PROGRESS);
204
    sort_batches($batches, in_progress_order($order));
205
    echo sprintf('<h3>Batches in progress (%d)</h3>', count($batches));
206
    $first = true;
207
    $n = 0;
208
    foreach ($batches as $batch) {
209
        if ($limit && $n == $limit) break;
210
        $n++;
211
        if ($first) {
212
            $first = false;
213
            if ($limit) {
214
                show_all_batches_link(
215
                    $batches, BATCH_STATE_IN_PROGRESS, $limit, $user, $app
216
                );
217
            }
218
            form_start('submit.php');
219
            form_input_hidden('action', 'abort_selected');
220
            start_table('table-striped');
221
            $x = [
222
                "Name",
223
                "ID",
224
                "User",
225
                "App",
226
                "# jobs",
227
                "Progress",
228
                "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...
229
            ];
230
            row_heading_array($x);
231
        }
232
        $pct_done = (int)($batch->fraction_done*100);
233
        $x = [
234
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
235
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
236
            $batch->user_name,
237
            $batch->app_name,
238
            $batch->njobs,
239
            "$pct_done%",
240
            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...
241
        ];
242
        row_array($x);
243
    }
244
    if ($first) {
245
        echo "<p>None.\n";
246
    } else {
247
        end_table();
248
        form_end();
249
    }
250
}
251
252
function show_complete($all_batches, $order, $limit, $user, $app) {
253
    $batches = batches_in_state($all_batches, BATCH_STATE_COMPLETE);
254
    sort_batches($batches, $order);
255
    echo sprintf('<h3>Completed batches (%d)</h3>', count($batches));
256
    $first = true;
257
    $n = 0;
258
    foreach ($batches as $batch) {
259
        if ($limit && $n == $limit) break;
260
        $n++;
261
        if ($first) {
262
            $first = false;
263
            if ($limit) {
264
                show_all_batches_link($batches, BATCH_STATE_COMPLETE, $limit, $user, $app);
265
            }
266
            form_start('submit.php', 'get');
267
            form_input_hidden('action', 'retire_multi');
268
            start_table('table-striped');
269
            table_header(
270
                "Name", "ID", "User", "App", "# Jobs", "Submitted", "Completed", "Select"
271
            );
272
        }
273
        table_row(
274
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
275
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
276
            $batch->user_name,
277
            $batch->app_name,
278
            $batch->njobs,
279
            local_time_str($batch->create_time),
280
            local_time_str($batch->completion_time),
281
            sprintf('<input type=checkbox name=retire_%d>', $batch->id)
282
        );
283
    }
284
    if ($first) {
285
        echo "<p>None.\n";
286
    } else {
287
        end_table();
288
        form_submit('Retire selected batches');
289
        form_end();
290
    }
291
}
292
293
function show_aborted($all_batches, $order, $limit, $user, $app) {
294
    $batches = batches_in_state($all_batches, BATCH_STATE_ABORTED);
295
    if (!$batches) return;
296
    sort_batches($batches, $order);
297
    echo sprintf('<h3>Aborted batches (%d)</h3>', count($batches));
298
    $first = true;
299
    $n = 0;
300
    foreach ($batches as $batch) {
301
        if ($limit && $n == $limit) break;
302
        $n++;
303
        if ($first) {
304
            $first = false;
305
            if ($limit) {
306
                show_all_batches_link($batches, BATCH_STATE_ABORTED, $limit, $user, $app);
307
            }
308
            form_start('submit.php', 'get');
309
            form_input_hidden('action', 'retire_multi');
310
            start_table();
311
            table_header("Name", "ID", "User", "App", "# Jobs", "Submitted", "Aborted", 'Select');
312
        }
313
        table_row(
314
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
315
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
316
            $batch->user_name,
317
            $batch->app_name,
318
            $batch->njobs,
319
            local_time_str($batch->create_time),
320
            local_time_str($batch->completion_time),
321
            sprintf('<input type=checkbox name=retire_%d>', $batch->id)
322
        );
323
    }
324
    if (!$first) {
325
        end_table();
326
        form_submit('Retire selected batches');
327
        form_end();
328
    }
329
}
330
331
// fill in the app and user names in list of batches
332
// 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...
333
// and doing lookup just once.
334
//
335
function fill_in_app_and_user_names(&$batches) {
336
    $apps = [];
337
    foreach ($batches as $batch) {
338
        if (array_key_exists($batch->app_id, $apps)) {
339
            $app = $apps[$batch->app_id];
340
        } else {
341
            $app = BoincApp::lookup_id($batch->app_id);
342
            $apps[$batch->app_id] = $app;
343
        }
344
        if ($app) {
345
            $batch->app_name = $app->name;
346
            if ($batch->description) {
347
                $batch->app_name .= ": $batch->description";
348
            }
349
        } else {
350
            $batch->app_name = "unknown";
351
        }
352
        $user = BoincUser::lookup_id($batch->user_id);
353
        if ($user) {
354
            $batch->user_name = $user->name;
355
        } else {
356
            $batch->user_name = "missing user $batch->user_id";
357
        }
358
    }
359
}
360
361
// show a set of batches: in progress, then completed, then aborted
362
//
363
function show_batches($batches, $order, $limit, $user, $app) {
364
    fill_in_app_and_user_names($batches);
365
    $batches = get_batches_params($batches);
366
    show_in_progress($batches, $order, $limit, $user, $app);
367
    show_complete($batches, $order, $limit, $user, $app);
368
    show_aborted($batches, $order, $limit, $user, $app);
369
}
370
371
// show links to per-app job submission forms
372
//
373
function show_submit_links($user) {
374
    global $remote_apps;
375
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
376
    if (!$user_submit) {
377
        error_page("Ask the project admins for permission to submit jobs");
378
    }
379
380
    page_head("Submit jobs");
381
382
    // show links to per-app job submission pages
383
    //
384
    foreach ($remote_apps as $area => $apps) {
385
        panel($area,
386
            function() use ($apps) {
387
                foreach ($apps as $app) {
388
                    if (empty($app->form)) continue;
389
                    // show app logo if available
390
                    if (!empty($app->logo)) {
391
                        echo sprintf(
392
                            '<a href=%s><img width=100 src=%s></a>&nbsp;',
393
                            $app->form, $app->logo
394
                        );
395
                    } else {
396
                        echo sprintf(
397
                            '<li><a href=%s>%s</a><p>',
398
                            $app->form, $app->long_name
399
                        );
400
                    }
401
                }
402
            }
403
        );
404
    }
405
406
    form_start('submit.php');
407
    form_input_hidden('action', 'update_only_own');
408
    form_radio_buttons(
409
        'Jobs you submit can run', 'only_own',
410
        [
411
            [0, 'on any computer'],
412
            [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...
413
        ],
414
        $user->seti_id
415
    );
416
    form_submit('Update');
417
    form_end();
418
    page_tail();
419
}
420
421
// show batches of logged in user.
422
// They have manage access to these batches.
423
//
424
function handle_show_user_batches($user) {
425
    page_head("Batches");
426
    $order = get_order();
427
    order_options('action=show_user_batches', $order);
428
    $batches = BoincBatch::enum("user_id = $user->id");
429
    get_batches_params($batches);
430
    show_batches($batches, $order, PAGE_SIZE, $user, null);
431
432
    page_tail();
433
}
434
435
function handle_update_only_own($user) {
436
    $val = get_int('only_own');
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 "<li> <a href=submit.php?action=admin_all>View/manage all batches</a>
464
        ";
465
        $app_names = get_remote_app_names();
466
        foreach ($app_names as $app_name) {
467
            $app_name = BoincDb::escape_string($app_name);
468
            $app = BoincApp::lookup("name='$app_name'");
469
            echo "
470
                <li>$app->user_friendly_name<br>
471
                <ul>
472
                <li><a href=submit.php?action=show_batches_admin_app&app_id=$app->id>View/manage batches</a>
473
            ";
474
            if ($app_name == 'buda') {
475
                echo "
476
                    <li> <a href=buda.php>Manage BUDA apps and variants</a>
477
                ";
478
            } else {
479
                echo "
480
                    <li> <a href=manage_app.php?app_id=$app->id&amp;action=app_version_form>Manage app versions</a>
481
                ";
482
            }
483
            echo "
484
                </ul>
485
            ";
486
        }
487
    } else {
488
        // see if user can administer specific apps
489
        //
490
        page_head("Job submission: manage apps");
491
        $usas = BoincUserSubmitApp::enum("user_id=$user->id");
492
        foreach ($usas as $usa) {
493
            $app = BoincApp::lookup_id($usa->app_id);
494
            echo "<li>$app->user_friendly_name<br>
495
                <a href=submit.php?action=show_batches_admin_app&app_id=$app->id>Batches</a>
496
            ";
497
            if ($usa->manage) {
498
                echo "&middot;
499
                    <a href=manage_app.php?app_id=$app->id&action=app_version_form>Versions</a>
500
                ";
501
            }
502
        }
503
    }
504
    echo "</ul>\n";
505
    page_tail();
506
}
507
508
// show all batches for given app to administrator
509
//
510
function show_batches_admin_app($user) {
511
    $app_id = get_int("app_id");
512
    $app = BoincApp::lookup_id($app_id);
513
    if (!$app) error_page("no such app");
514
    if (!has_manage_access($user, $app_id)) {
515
        error_page('no access');
516
    }
517
518
    $order = get_order();
519
520
    page_head("Manage batches for $app->user_friendly_name");
521
    order_options("action=show_batches_admin_app&app_id=$app_id", $order);
522
    $batches = BoincBatch::enum("app_id = $app_id");
523
    show_batches($batches, $order, PAGE_SIZE, null, $app);
524
    page_tail();
525
}
526
527
function handle_admin_all($user) {
528
    $order = get_order();
529
    page_head("Administer batches (all apps)");
530
    order_options("action=admin_all", $order);
531
    $batches = BoincBatch::enum('');
532
    show_batches($batches, $order, PAGE_SIZE, null, null);
533
    page_tail();
534
}
535
536
537
// show the statics of mem/disk usage of jobs in a batch
538
//
539
function handle_batch_stats($user) {
540
    $batch_id = get_int('batch_id');
541
    $batch = BoincBatch::lookup_id($batch_id);
542
    $results = BoincResult::enum_fields(
543
        'peak_working_set_size, peak_swap_size, peak_disk_usage',
544
        sprintf('batch = %d and outcome=%d',
545
            $batch->id, RESULT_OUTCOME_SUCCESS
546
        )
547
    );
548
    page_head("Statistics for batch $batch_id");
549
    $n = 0;
550
    $wss_sum = 0;
551
    $swap_sum = 0;
552
    $disk_sum = 0;
553
    $wss_max = 0;
554
    $swap_max = 0;
555
    $disk_max = 0;
556
    foreach ($results as $r) {
557
        // pre-7.3.16 clients don't report usage info
558
        //
559
        if ($r->peak_working_set_size == 0) {
560
            continue;
561
        }
562
        $n++;
563
        $wss_sum += $r->peak_working_set_size;
564
        if ($r->peak_working_set_size > $wss_max) {
565
            $wss_max = $r->peak_working_set_size;
566
        }
567
        $swap_sum += $r->peak_swap_size;
568
        if ($r->peak_swap_size > $swap_max) {
569
            $swap_max = $r->peak_swap_size;
570
        }
571
        $disk_sum += $r->peak_disk_usage;
572
        if ($r->peak_disk_usage > $disk_max) {
573
            $disk_max = $r->peak_disk_usage;
574
        }
575
    }
576
    if ($n == 0) {
577
        echo "No qualifying results.";
578
        page_tail();
579
        return;
580
    }
581
    text_start(800);
582
    start_table('table-striped');
583
    row2("qualifying results", $n);
584
    row2("mean WSS", size_string($wss_sum/$n));
585
    row2("max WSS", size_string($wss_max));
586
    row2("mean swap", size_string($swap_sum/$n));
587
    row2("max swap", size_string($swap_max));
588
    row2("mean disk usage", size_string($disk_sum/$n));
589
    row2("max disk usage", size_string($disk_max));
590
    end_table();
591
    text_end();
592
    page_tail();
593
}
594
595
define('COLOR_SUCCESS', 'green');
596
define('COLOR_FAIL', 'red');
597
define('COLOR_IN_PROGRESS', 'deepskyblue');
598
define('COLOR_UNSENT', 'gray');
599
600
// return HTML for a color-coded batch progress bar
601
//
602
function progress_bar($batch, $wus, $width) {
603
    $nsuccess = $batch->njobs_success;
604
    $nerror = $batch->nerror_jobs;
605
    $nin_prog = $batch->njobs_in_prog;
606
    $nunsent = $batch->njobs - $nsuccess - $nerror - $nin_prog;
607
    $w_success = $width*$nsuccess/$batch->njobs;
608
    $w_fail = $width*$nerror/$batch->njobs;
609
    $w_prog = $width*$nin_prog/$batch->njobs;
610
    $w_unsent = $width*$nunsent/$batch->njobs;
611
    $x = '<table height=20><tr>';
612
    if ($w_fail) {
613
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_fail, COLOR_FAIL);
614
    }
615
    if ($w_success) {
616
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_success, COLOR_SUCCESS);
617
    }
618
    if ($w_prog) {
619
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_prog, COLOR_IN_PROGRESS);
620
    }
621
    if ($w_unsent) {
622
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_unsent, COLOR_UNSENT);
623
    }
624
    $x .= sprintf('</tr></table>
625
        <strong>
626
        <font color=%s>%d failed</font> &middot;
627
        <font color=%s>%d completed</font> &middot;
628
        <font color=%s>%d in progress</font> &middot;
629
        <font color=%s>%d unsent</font>
630
        </strong>',
631
        COLOR_FAIL, $nerror,
632
        COLOR_SUCCESS, $nsuccess,
633
        COLOR_IN_PROGRESS, $nin_prog,
634
        COLOR_UNSENT, $nunsent
635
    );
636
    return $x;
637
}
638
639
// show the details of an existing batch.
640
// $user has access to abort/retire the batch
641
// and to get its output files
642
//
643
function handle_query_batch($user) {
644
    $batch_id = get_int('batch_id');
645
    $status = get_int('status', true);
646
    $batch = BoincBatch::lookup_id($batch_id);
647
    $app = BoincApp::lookup_id($batch->app_id);
648
    $wus = BoincWorkunit::enum_fields(
649
        'id, name, rsc_fpops_est, canonical_credit, canonical_resultid, error_mask',
650
        "batch = $batch->id"
651
    );
652
    $batch = get_batch_params($batch, $wus);
653
    if ($batch->user_id == $user->id) {
654
        $owner = $user;
655
    } else {
656
        $owner = BoincUser::lookup_id($batch->user_id);
657
    }
658
659
    $is_assim_move = is_assim_move($app);
660
661
    page_head("Batch $batch_id");
662
    text_start(800);
663
    start_table();
664
    row2("name", $batch->name);
665
    if ($batch->description) {
666
        row2('description', $batch->description);
667
    }
668
    if ($owner) {
669
        row2('submitter', $owner->name);
670
    }
671
    row2("application", $app?$app->name:'---');
672
    row2("state", batch_state_string($batch->state));
673
    //row2("# jobs", $batch->njobs);
674
    //row2("# error jobs", $batch->nerror_jobs);
675
    //row2("logical end time", time_str($batch->logical_end_time));
676
    if ($batch->expire_time) {
677
        row2("expiration time", time_str($batch->expire_time));
678
    }
679
    if ($batch->njobs) {
680
        row2('progress', progress_bar($batch, $wus, 600));
681
    }
682
    if ($batch->completion_time) {
683
        row2("completed", local_time_str($batch->completion_time));
684
    }
685
    row2("GFLOP/hours, estimated", number_format(credit_to_gflop_hours($batch->credit_estimate), 2));
686
    row2("GFLOP/hours, actual", number_format(credit_to_gflop_hours($batch->credit_canonical), 2));
687
    if (!$is_assim_move) {
688
        row2("Total size of output files",
689
            size_string(batch_output_file_size($batch->id))
690
        );
691
    }
692
    end_table();
693
    echo "<p>";
694
695
    if ($is_assim_move) {
696
        $url = "get_output3.php?action=get_batch&batch_id=$batch->id";
697
    } else {
698
        $url = "get_output2.php?cmd=batch&batch_id=$batch->id";
699
    }
700
    echo "<p>";
701
    show_button($url, "Get zipped output files");
702
    echo "<p>";
703
    switch ($batch->state) {
704
    case BATCH_STATE_IN_PROGRESS:
705
        show_button(
706
            "submit.php?action=abort_batch&batch_id=$batch_id",
707
            "Abort batch"
708
        );
709
        break;
710
    case BATCH_STATE_COMPLETE:
711
    case BATCH_STATE_ABORTED:
712
        show_button(
713
            "submit.php?action=retire_batch&batch_id=$batch_id",
714
            "Retire batch"
715
        );
716
        break;
717
    }
718
    echo "<p>
719
        <h3>Completed jobs</h3>
720
        <ul>
721
        <li>
722
        <a href=submit_stats.php?action=flops_graph&batch_id=$batch_id>Job runtimes</a>
723
        <li>
724
        <a href=submit.php?action=batch_stats&batch_id=$batch_id>Memory/disk usage</a>
725
        <li>
726
        <a href=submit_stats.php?action=show_hosts&batch_id=$batch_id>Grouped by host</a>
727
        </ul>
728
        <h3>Failed jobs</h3>
729
        <ul>
730
        <li>
731
        <a href=submit_stats.php?action=err_host&batch_id=$batch_id>Grouped by host</a>
732
        <li>
733
        <a href=submit_stats.php?action=err_code&batch_id=$batch_id>Grouped by exit code</a>
734
        </ul>
735
    ";
736
737
    echo "<h2>Jobs</h2>\n";
738
    $url = "submit.php?action=query_batch&batch_id=$batch_id";
739
    echo "Show: ";
740
    echo sprintf('
741
        <a href=%s&status=%d>failed</a> &middot;
742
        <a href=%s&status=%d>completed</a> &middot;
743
        <a href=%s&status=%d>in progress</a> &middot;
744
        <a href=%s&status=%d>unsent</a> &middot;
745
        <a href=%s>all</a>
746
        <p>',
747
        $url, WU_ERROR,
748
        $url, WU_SUCCESS,
749
        $url, WU_IN_PROGRESS,
750
        $url, WU_UNSENT,
751
        $url
752
    );
753
754
    start_table();
755
    $x = [
756
        "Name <br><small>click for details</small>",
757
        "status",
758
        "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...
759
    ];
760
    row_heading_array($x);
761
    foreach($wus as $wu) {
762
        if ($status && $wu->status != $status) continue;
763
        $y = '';
764
        $c = '---';
765
        switch($wu->status) {
766
        case WU_SUCCESS:
767
            $resultid = $wu->canonical_resultid;
768
            $y = sprintf('<font color="%s">completed</font>', COLOR_SUCCESS);
769
            $c = number_format(
770
                credit_to_gflop_hours($wu->canonical_credit), 2
771
            );
772
            break;
773
        case WU_ERROR:
774
            $y = sprintf('<font color="%s">failed</font>', COLOR_FAIL);
775
            break;
776
        case WU_IN_PROGRESS:
777
            $y = sprintf('<font color="%s">in progress</font>', COLOR_IN_PROGRESS);
778
            break;
779
        case WU_UNSENT:
780
            $y = sprintf('<font color="%s">unsent</font>', COLOR_UNSENT);
781
            break;
782
        }
783
        $x = [
784
            "<a href=submit.php?action=query_job&wuid=$wu->id>$wu->name</a>",
785
            $y,
786
            $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...
787
        ];
788
        row_array($x);
789
    }
790
    end_table();
791
    return_link();
792
    text_end();
793
    page_tail();
794
}
795
796
// Does the assimilator for the given app move output files
797
// to a results/<batchid>/ directory?
798
// This info is stored in the $remote_apps data structure in project.inc
799
//
800
function is_assim_move($app) {
801
    global $remote_apps;
802
    foreach ($remote_apps as $category => $apps) {
803
        foreach ($apps as $web_app) {
804
            if ($web_app->app_name == $app->name) {
805
                return $web_app->is_assim_move;
806
            }
807
        }
808
    }
809
    return false;
810
}
811
812
// show the details of a job, including links to see the output files
813
//
814
function handle_query_job($user) {
815
    $wuid = get_int('wuid');
816
    $wu = BoincWorkunit::lookup_id($wuid);
817
    if (!$wu) error_page("no such job");
818
819
    $app = BoincApp::lookup_id($wu->appid);
820
    $is_assim_move = is_assim_move($app);
821
822
    page_head("Job '$wu->name'");
823
    text_start(800);
824
825
    echo "
826
        <li><a href=workunit.php?wuid=$wuid>Job details</a>
827
        <p>
828
        <li><a href=submit.php?action=query_batch&batch_id=$wu->batch>Batch details</a>
829
    ";
830
    $d = "<foo>$wu->xml_doc</foo>";
831
    $x = simplexml_load_string($d);
832
    $x = $x->workunit;
833
    //echo "foo: $x->command_line";
834
835
    echo "<h2>Job instances</h2>\n";
836
    start_table('table-striped');
837
    table_header(
838
        "ID<br><small>click for details and stderr</small>",
839
        "State",
840
        "Output files"
841
    );
842
    $results = BoincResult::enum("workunitid=$wuid");
843
    $upload_dir = parse_config(get_config(), "<upload_dir>");
844
    $fanout = parse_config(get_config(), "<uldl_dir_fanout>");
845
    foreach($results as $result) {
846
        $x = [
847
            "<a href=result.php?resultid=$result->id>$result->id</a>",
848
            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...
849
        ];
850
        $i = 0;
851
        if ($result->server_state == RESULT_SERVER_STATE_OVER) {
852
            $phys_names = get_outfile_phys_names($result);
853
            $log_names = get_outfile_log_names($result);
854
            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...
855
                if ($is_assim_move) {
856
                    // file is in
857
                    // project/results/<batchid>/<wu_name>__file_<log_name>
858
                    $path = sprintf('results/%s/%s__file_%s',
859
                        $wu->batch, $wu->name, $log_names[$i]
860
                    );
861
                    $name = $log_names[$i];
862
                    // don't show 'view' link if it's a .zip
863
                    $y = "$name: ";
864
                    if (!strstr($name, '.zip')) {
865
                        $y .= "<a href=get_output3.php?action=get_file&path=$path>view</a> &middot; ";
866
                    }
867
                    $y .= "<a href=get_output3.php?action=get_file&path=$path&download=1>download</a>";
868
                    $x[] = $y;
869
                } else {
870
                    $path = dir_hier_path(
871
                        $phys_names[$i], $upload_dir, $fanout
872
                    );
873
                    if (file_exists($path)) {
874
                        $url = sprintf(
875
                            'get_output2.php?cmd=result&result_id=%d&file_num=%d',
876
                            $result->id, $i
877
                        );
878
                        $s = stat($path);
879
                        $size = $s['size'];
880
                        $x[] = sprintf('<a href=%s>%s</a> (%s bytes)<br/>',
881
                            $url,
882
                            $log_names[$i],
883
                            number_format($size)
884
                        );
885
                    } else {
886
                        $x[] = sprintf("file '%s' is missing", $log_names[$i]);
887
                    }
888
                }
889
            }
890
        } else {
891
            $x[] = '---';
892
        }
893
        row_array($x);
894
    }
895
    end_table();
896
897
    // show input files
898
    //
899
    echo "<h2>Input files</h2>\n";
900
    $x = "<in>".$wu->xml_doc."</in>";
901
    $x = simplexml_load_string($x);
902
    start_table('table-striped');
903
    table_header("Name<br><small>(click to view)</small>", "Size (bytes)");
904
    foreach ($x->workunit->file_ref as $fr) {
905
        $pname = (string)$fr->file_name;
906
        $lname = (string)$fr->open_name;
907
        foreach ($x->file_info as $fi) {
908
            if ((string)$fi->name == $pname) {
909
                table_row(
910
                    "<a href=$fi->url>$lname</a>",
911
                    $fi->nbytes
912
                );
913
                break;
914
            }
915
        }
916
    }
917
918
    end_table();
919
    text_end();
920
    return_link();
921
    page_tail();
922
}
923
924
// is user allowed to retire or abort this batch?
925
//
926
function has_access($user, $batch) {
927
    if ($user->id == $batch->user_id) return true;
928
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
929
    if ($user_submit->manage_all) return true;
930
    $usa = BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$batch->app_id");
931
    if ($usa->manage) return true;
932
    return false;
933
}
934
935
function handle_abort_batch($user) {
936
    $batch_id = get_int('batch_id');
937
    $batch = BoincBatch::lookup_id($batch_id);
938
    if (!$batch) error_page("no such batch");
939
    if (!has_access($user, $batch)) {
940
        error_page("no access");
941
    }
942
943
    if (get_int('confirmed', true)) {
944
        abort_batch($batch);
945
        page_head("Batch aborted");
946
        return_link();
947
        page_tail();
948
    } else {
949
        page_head("Confirm abort batch");
950
        echo "
951
            Aborting a batch will cancel all unstarted jobs.
952
            Are you sure you want to do this?
953
            <p>
954
        ";
955
        show_button(
956
            "submit.php?action=abort_batch&batch_id=$batch_id&confirmed=1",
957
            "Yes - abort batch"
958
        );
959
        return_link();
960
        page_tail();
961
    }
962
}
963
964
function handle_retire_batch($user) {
965
    $batch_id = get_int('batch_id');
966
    $batch = BoincBatch::lookup_id($batch_id);
967
    if (!$batch) error_page("no such batch");
968
    if (!has_access($user, $batch)) {
969
        error_page("no access");
970
    }
971
972
    if (get_int('confirmed', true)) {
973
        retire_batch($batch);
974
        page_head("Batch $batch_id retired");
975
        return_link();
976
        page_tail();
977
    } else {
978
        page_head("Confirm retire batch");
979
        echo "
980
            Retiring a batch will remove all of its output files.
981
            Are you sure you want to do this?
982
            <p>
983
        ";
984
        show_button(
985
            "submit.php?action=retire_batch&batch_id=$batch_id&confirmed=1",
986
            "Yes - retire batch"
987
        );
988
        return_link();
989
        page_tail();
990
    }
991
}
992
993
// retire multiple batches
994
//
995
function handle_retire_multi($user) {
996
    $batches = BoincBatch::enum(
997
        sprintf('state=%d', BATCH_STATE_COMPLETE)
998
    );
999
    page_head('Retiring batches');
1000
    foreach ($batches as $batch) {
1001
        if (!has_access($user, $batch)) {
1002
            continue;
1003
        }
1004
        $x = sprintf('retire_%d', $batch->id);
1005
        if (get_str($x, true) == 'on') {
1006
            retire_batch($batch);
1007
            echo "<p>retired batch $batch->id ($batch->name)\n";
1008
        }
1009
    }
1010
    return_link();
1011
    page_tail();
1012
}
1013
1014
// given a list of batches, show the ones in a given state
1015
//
1016
function show_batches_in_state($batches, $state, $url_args, $order) {
1017
    switch ($state) {
1018
    case BATCH_STATE_IN_PROGRESS:
1019
        page_head("Batches in progress");
1020
        order_options($url_args, $order);
1021
        show_in_progress($batches, $order, 0, null, null);
1022
        break;
1023
    case BATCH_STATE_COMPLETE:
1024
        page_head("Completed batches");
1025
        order_options($url_args, $order);
1026
        show_complete($batches, $order, 0, null, null);
1027
        break;
1028
    case BATCH_STATE_ABORTED:
1029
        page_head("Aborted batches");
1030
        order_options($url_args, $order);
1031
        show_aborted($batches, $order, 0, null, null);
1032
        break;
1033
    }
1034
    page_tail();
1035
}
1036
1037
// show all batches visible to user, possibly limited by user/app/state
1038
function handle_show_all_batches($user) {
1039
    $userid = get_int("userid");
1040
    $appid = get_int("appid");
1041
    $state = get_int("state");
1042
    $order = get_order();
1043
    $url_args = "action=show_all_batches&state=$state&userid=$userid&appid=$appid";
1044
    if ($userid) {
1045
        // user looking at their own batches
1046
        //
1047
        if ($userid != $user->id) error_page("wrong user");
1048
        $batches = BoincBatch::enum("user_id=$user->id and state=$state");
1049
        fill_in_app_and_user_names($batches);
1050
        show_batches_in_state($batches, $state, $url_args, $order);
1051
    } else {
1052
        // admin looking at batches
1053
        //
1054
        if (!has_manage_access($user, $appid)) {
1055
            error_page('no access');
1056
        }
1057
        if ($appid) {
1058
            $app = BoincApp::lookup_id($appid);
1059
            if (!$app) error_page("no such app");
1060
            $batches = BoincBatch::enum("app_id=$appid and state=$state");
1061
        } else {
1062
            $batches = BoincBatch::enum("state=$state");
1063
        }
1064
        fill_in_app_and_user_names($batches);
1065
        show_batches_in_state($batches, $state, $url_args, $order);
1066
    }
1067
}
1068
1069
$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...
1070
1071
$action = get_str('action', true);
1072
1073
switch ($action) {
1074
1075
// links to job submission forms
1076
case '': show_submit_links($user); break;
1077
1078
// show lists of batches
1079
case 'show_all_batches': handle_show_all_batches($user); break;
1080
case 'show_user_batches': handle_show_user_batches($user); break;
1081
case 'show_batches_admin_app': show_batches_admin_app($user); break;
1082
case 'admin_all': handle_admin_all($user); break;
1083
1084
// show info about a batch or job
1085
case 'batch_stats': handle_batch_stats($user); break;
1086
case 'query_batch': handle_query_batch($user); break;
1087
case 'query_job': handle_query_job($user); break;
1088
1089
// operations on batches
1090
case 'retire_batch': handle_retire_batch($user); break;
1091
case 'retire_multi': handle_retire_multi($user); break;
1092
case 'abort_batch': handle_abort_batch($user); break;
1093
1094
// access control
1095
case 'admin': handle_admin($user); break;
1096
1097
// 'run jobs only on my computers' flag (stored in user.seti_id)
1098
case 'update_only_own': handle_update_only_own($user); break;
1099
1100
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...
1101
    error_page("no such action $action");
1102
}
1103
1104
?>
1105