Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(896)

Side by Side Diff: appengine/swarming/elements/res/imp/botlist/bot-list.html

Issue 2227803002: Mirror filters and sort preferences to url-params (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@use-dimensions
Patch Set: Tweak docs Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 <!-- 1 <!--
2 Copyright 2016 The LUCI Authors. All rights reserved. 2 Copyright 2016 The LUCI Authors. All rights reserved.
3 Use of this source code is governed under the Apache License, Version 2.0 3 Use of this source code is governed under the Apache License, Version 2.0
4 that can be found in the LICENSE file. 4 that can be found in the LICENSE file.
5 5
6 This in an HTML Import-able file that contains the definition 6 This in an HTML Import-able file that contains the definition
7 of the following elements: 7 of the following elements:
8 8
9 <bot-list> 9 <bot-list>
10 10
(...skipping 10 matching lines...) Expand all
21 21
22 Events: 22 Events:
23 None. 23 None.
24 --> 24 -->
25 25
26 <link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-la yout-classes.html"> 26 <link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-la yout-classes.html">
27 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> 27 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html">
28 28
29 <link rel="import" href="/res/imp/common/sort-toggle.html"> 29 <link rel="import" href="/res/imp/common/sort-toggle.html">
30 <link rel="import" href="/res/imp/common/swarming-app.html"> 30 <link rel="import" href="/res/imp/common/swarming-app.html">
31 <link rel="import" href="/res/imp/common/url-param.html">
31 32
32 <link rel="import" href="bot-filters.html"> 33 <link rel="import" href="bot-filters.html">
33 <link rel="import" href="bot-list-data.html"> 34 <link rel="import" href="bot-list-data.html">
34 <link rel="import" href="bot-list-shared.html"> 35 <link rel="import" href="bot-list-shared.html">
35 <link rel="import" href="bot-list-summary.html"> 36 <link rel="import" href="bot-list-summary.html">
36 37
37 <dom-module id="bot-list"> 38 <dom-module id="bot-list">
38 <template> 39 <template>
39 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style"> 40 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style">
40 bot-filters, bot-list-summary { 41 bot-filters, bot-list-summary {
(...skipping 29 matching lines...) Expand all
70 position: absolute; 71 position: absolute;
71 right: 0; 72 right: 0;
72 top: 0.4em; 73 top: 0.4em;
73 } 74 }
74 .bot-list th > span { 75 .bot-list th > span {
75 /* Leave space for sort-toggle*/ 76 /* Leave space for sort-toggle*/
76 padding-right: 30px; 77 padding-right: 30px;
77 } 78 }
78 </style> 79 </style>
79 80
81 <url-param name="sort"
82 value="{{_sortstr}}"
83 default_value="id:asc">
84 </url-param>
85
80 <swarming-app 86 <swarming-app
81 client_id="[[client_id]]" 87 client_id="[[client_id]]"
82 auth_headers="{{_auth_headers}}" 88 auth_headers="{{_auth_headers}}"
83 signed_in="{{_signed_in}}" 89 signed_in="{{_signed_in}}"
84 90
85 busy="[[_busy]]" 91 busy="[[_busy]]"
86 name="Swarming Bot List"> 92 name="Swarming Bot List">
87 93
88 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> 94 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2>
89 95
90 <div hidden$="[[_not(_signed_in)]]"> 96 <div hidden$="[[_not(_signed_in)]]">
91 97
92 <div class="horizontal layout"> 98 <div class="horizontal layout">
93 99
94 <bot-filters 100 <bot-filters
101 dimensions="[[_dimensions]]"
95 primary_map="[[_primary_map]]" 102 primary_map="[[_primary_map]]"
96 primary_arr="[[_primary_arr]]" 103 primary_arr="[[_primary_arr]]"
97 104
98 columns="{{_columns}}" 105 columns="{{_columns}}"
99 dimensions="{{_dimensions}}" 106 query_params="{{_query_params}}"
100 filter="{{_filter}}" 107 filter="{{_filter}}"
101 verbose="{{_verbose}}"> 108 verbose="{{_verbose}}">
102 </bot-filters> 109 </bot-filters>
103 110
104 <bot-list-summary 111 <bot-list-summary
112 columns="[[_columns]]"
105 fleet="[[_fleet]]" 113 fleet="[[_fleet]]"
106 filtered_bots="[[_filteredSortedBots]]"> 114 filtered_bots="[[_filteredSortedBots]]"
115 sort="[[_sortstr]]"
116 verbose="[[_verbose]]">
107 </bot-list-summary> 117 </bot-list-summary>
108 118
109 </div> 119 </div>
110 120
111 <bot-list-data 121 <bot-list-data
112 auth_headers="[[_auth_headers]]" 122 auth_headers="[[_auth_headers]]"
113 dimensions="[[_dimensions]]" 123 query_params="[[_query_params]]"
114 124
115 bots="{{_bots}}" 125 bots="{{_bots}}"
116 busy="{{_busy}}" 126 busy="{{_busy}}"
127 dimensions="{{_dimensions}}"
117 fleet="{{_fleet}}" 128 fleet="{{_fleet}}"
118 primary_map="{{_primary_map}}" 129 primary_map="{{_primary_map}}"
119 primary_arr="{{_primary_arr}}"> 130 primary_arr="{{_primary_arr}}">
120 </bot-list-data> 131 </bot-list-data>
121 132
122 <table class="bot-list"> 133 <table class="bot-list">
123 <thead on-sort_change="_sortChange"> 134 <thead on-sort_change="_sortChange">
124 <!-- To allow for dynamic columns without having a lot of copy-pasted 135 <!-- To allow for dynamic columns without having a lot of copy-pasted
125 code, we break columns up into "special" and "plain" columns. Special 136 code, we break columns up into "special" and "plain" columns. Special
126 columns require some sort of HTML output (e.g. anchor tags) and plain 137 columns require some sort of HTML output (e.g. anchor tags) and plain
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 <script> 227 <script>
217 (function(){ 228 (function(){
218 var special_columns = ["id", "task"]; 229 var special_columns = ["id", "task"];
219 230
220 var headerMap = { 231 var headerMap = {
221 // "id" and "task" are special, so they don't go here and have their 232 // "id" and "task" are special, so they don't go here and have their
222 // headers hard-coded below. 233 // headers hard-coded below.
223 "android_devices": "Android Devices", 234 "android_devices": "Android Devices",
224 "cores": "Cores", 235 "cores": "Cores",
225 "cpu": "CPU", 236 "cpu": "CPU",
237 "device": "Non-android Device",
226 "device_os": "Device OS", 238 "device_os": "Device OS",
227 "device_type": "Device Type", 239 "device_type": "Device Type",
228 "disk_space": "Free Space (MB)", 240 "disk_space": "Free Space (MB)",
229 "gpu": "GPU", 241 "gpu": "GPU",
230 "os": "OS", 242 "os": "OS",
231 "pool": "Pool", 243 "pool": "Pool",
232 "status": "Status", 244 "status": "Status",
245 "xcode_version": "XCode Version",
233 }; 246 };
234 247
235 // This maps column name to a function that will return the content for a 248 // This maps column name to a function that will return the content for a
236 // given bot. These functions are bound to this element, and have access 249 // given bot. These functions are bound to this element, and have access
237 // to all functions defined here and in bot-list-shared. 250 // to all functions defined here and in bot-list-shared. If a column
251 // is not listed here, a sane default will be used (see _column()).
238 var columnMap = { 252 var columnMap = {
239 android_devices: function(bot) { 253 android_devices: function(bot) {
240 var devs = this._attribute(bot, "android_devices", "0"); 254 var devs = this._attribute(bot, "android_devices", "0");
241 if (this._verbose) { 255 if (this._verbose) {
242 return devs.join(" | ") + " devices available"; 256 return devs.join(" | ") + " devices available";
243 } 257 }
244 // max() works on strings as long as they can be coerced to Number. 258 // max() works on strings as long as they can be coerced to Number.
245 return Math.max(...devs) + " devices available"; 259 return Math.max(...devs) + " devices available";
246 }, 260 },
247 cores: function(bot){ 261 device_type: function(bot) {
248 var cores = this._attribute(bot, "cores"); 262 var dt = this._attribute(bot, "device_type", "none");
249 if (this._verbose){ 263 dt = dt[0];
250 return cores.join(" | "); 264 var alias = this._androidAlias(dt);
265 if (alias === "unknown") {
266 return dt;
251 } 267 }
252 return cores[0]; 268 return this._applyAlias(dt, alias);
253 },
254 cpu: function(bot){
255 var cpus = this._attribute(bot, "cpu");
256 if (this._verbose){
257 return cpus.join(" | ");
258 }
259 return cpus[0];
260 },
261 device_os: function(bot){
262 var os = this._attribute(bot, "device_os", "none");
263 if (this._verbose) {
264 return os.join(" | ");
265 }
266 return os[0];
267 },
268 device_type: function(bot){
269 var dt = this._attribute(bot, "device_type", "none");
270 if (this._verbose) {
271 return dt.join(" | ");
272 }
273 return dt[0];
274 }, 269 },
275 disk_space: function(bot) { 270 disk_space: function(bot) {
276 var aliased = []; 271 var aliased = [];
277 bot.disks.forEach(function(disk){ 272 bot.disks.forEach(function(disk){
278 var alias = swarming.humanBytes(disk.mb, swarming.MB); 273 var alias = sk.human.bytes(disk.mb, swarming.MB);
279 aliased.push(this._applyAlias(disk.mb, disk.id + " "+ alias)); 274 aliased.push(this._applyAlias(disk.mb, disk.id + " "+ alias));
280 }.bind(this)); 275 }.bind(this));
281 if (this._verbose) { 276 if (this._verbose) {
282 return aliased.join(" | "); 277 return aliased.join(" | ");
283 } 278 }
284 return aliased[0]; 279 return aliased[0];
285 }, 280 },
286 gpu: function(bot){ 281 gpu: function(bot){
287 var gpus = this._attribute(bot, "gpu", "none") 282 var gpus = this._attribute(bot, "gpu", "none")
288 var verbose = [] 283 var verbose = []
(...skipping 15 matching lines...) Expand all
304 } 299 }
305 }.bind(this)) 300 }.bind(this))
306 if (this._verbose) { 301 if (this._verbose) {
307 return verbose.join(" | "); 302 return verbose.join(" | ");
308 } 303 }
309 return named.join(" | "); 304 return named.join(" | ");
310 }, 305 },
311 id: function(bot) { 306 id: function(bot) {
312 return bot.bot_id; 307 return bot.bot_id;
313 }, 308 },
314 os: function(bot) {
315 var os = this._attribute(bot, "os");
316 if (this._verbose){
317 return os.join(" | ");
318 }
319 return os[0];
320 },
321 pool: function(bot) { 309 pool: function(bot) {
322 var pool = this._attribute(bot, "pool"); 310 var pool = this._attribute(bot, "pool");
323 return pool.join(" | "); 311 return pool.join(" | ");
324 }, 312 },
325 status: function(bot) { 313 status: function(bot) {
326 // If a bot is both dead and quarantined, show the deadness over the 314 // If a bot is both dead and quarantined, show the deadness over the
327 // quarentinedness. 315 // quarentinedness.
328 if (bot.is_dead) { 316 if (bot.is_dead) {
329 return "Dead. Last seen " + swarming.diffDate(bot.last_seen_ts) + 317 return "Dead. Last seen " + sk.human.diffDate(bot.last_seen_ts) +
330 " ago"; 318 " ago";
331 } 319 }
332 if (bot.quarantined) { 320 if (bot.quarantined) {
333 return "Quarantined: " + this._attribute(bot, "quarantined"); 321 return "Quarantined: " + this._attribute(bot, "quarantined");
334 } 322 }
335 return "Alive"; 323 return "Alive";
336 }, 324 },
337 task: function(bot){ 325 task: function(bot){
338 return this._taskId(bot); 326 return this._taskId(bot);
339 }, 327 },
(...skipping 16 matching lines...) Expand all
356 return "unknown"; 344 return "unknown";
357 }, 345 },
358 status: function(device) { 346 status: function(device) {
359 return device.state; 347 return device.state;
360 } 348 }
361 } 349 }
362 350
363 // specialSort defines any custom sorting rules. By default, a 351 // specialSort defines any custom sorting rules. By default, a
364 // naturalCompare of the column content is done. 352 // naturalCompare of the column content is done.
365 var specialSort = { 353 var specialSort = {
366 device_type: function(dir, botA, botB) { 354 android_devices: function(dir, botA, botB) {
367 // We sort on the number of attached devices. Note that this 355 // We sort on the number of attached devices. Note that this
368 // may not be the same as android_devices, because _devices().length 356 // may not be the same as android_devices, because _devices().length
369 // counts all devices plugged into the bot, whereas android_devices 357 // counts all devices plugged into the bot, whereas android_devices
370 // counts just devices ready for work. 358 // counts just devices ready for work.
371 var botACol = this._devices(botA).length; 359 var botACol = this._devices(botA).length;
372 var botBCol = this._devices(botB).length; 360 var botBCol = this._devices(botB).length;
373 return dir * swarming.naturalCompare(botACol, botBCol); 361 return dir * swarming.naturalCompare(botACol, botBCol);
374 }, 362 },
375 disk_space: function(dir, botA, botB) { 363 disk_space: function(dir, botA, botB) {
376 // We sort based on the raw number of MB of the first disk. 364 // We sort based on the raw number of MB of the first disk.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
411 }, 399 },
412 400
413 _plain_columns: { 401 _plain_columns: {
414 type: Array, 402 type: Array,
415 computed: "_stripSpecial(_columns.*)", 403 computed: "_stripSpecial(_columns.*)",
416 }, 404 },
417 405
418 // _sort is an Object {name:String, direction:String}. 406 // _sort is an Object {name:String, direction:String}.
419 _sort: { 407 _sort: {
420 type: Object, 408 type: Object,
421 value: function() { 409 computed: "_makeObject(_sortstr)",
422 return {
423 name: "id",
424 direction: "asc",
425 };
426 }
427 }, 410 },
428 411
429 _verbose: { 412 _verbose: {
430 type: Boolean, 413 type: Boolean,
431 } 414 }
432 }, 415 },
433 416
434 _botClass: function(bot) { 417 _botClass: function(bot) {
435 if (bot.is_dead) { 418 if (bot.is_dead) {
436 return "dead"; 419 return "dead";
437 } 420 }
438 if (bot.quarantined) { 421 if (bot.quarantined) {
439 return "quarantined"; 422 return "quarantined";
440 } 423 }
441 return ""; 424 return "";
442 }, 425 },
443 426
444 _botLink: function(id) { 427 _botLink: function(id) {
445 // TODO(kjlubick) Make this point to /newui/ when appropriate. 428 // TODO(kjlubick) Make this point to /newui/ when appropriate.
446 return "/restricted/bot/"+id; 429 return "/restricted/bot/"+id;
447 }, 430 },
448 431
449 432
450 _column: function(col, bot) { 433 _column: function(col, bot) {
451 return columnMap[col].bind(this)(bot); 434 var f = columnMap[col];
435 if (!f) {
436 f = function(bot) {
437 var c = this._attribute(bot, col, "none");
438 if (this._verbose) {
439 return c.join(" | ");
440 }
441 return c[0];
442 }
443 }
444 return f.bind(this)(bot);
452 }, 445 },
453 446
454 _androidAliasDevice: function(device) { 447 _androidAliasDevice: function(device) {
455 if (device.notReady) { 448 if (device.notReady) {
456 return UNAUTHENTICATED.toUpperCase(); 449 return UNAUTHENTICATED.toUpperCase();
457 } 450 }
458 return this._androidAlias(this._deviceType(device)); 451 return this._androidAlias(this._deviceType(device));
459 }, 452 },
460 453
461 _deviceColumn: function(col, device) { 454 _deviceColumn: function(col, device) {
(...skipping 18 matching lines...) Expand all
480 swarming.stableSort(this._bots, this._sortBotTable.bind(this)); 473 swarming.stableSort(this._bots, this._sortBotTable.bind(this));
481 var bots = this._bots; 474 var bots = this._bots;
482 if (this._filter) { 475 if (this._filter) {
483 bots = bots.filter(this._filter.bind(this)); 476 bots = bots.filter(this._filter.bind(this));
484 } 477 }
485 478
486 return bots; 479 return bots;
487 }, 480 },
488 481
489 _header: function(col){ 482 _header: function(col){
490 return headerMap[col]; 483 return headerMap[col] || col;
491 }, 484 },
492 485
493 _hide: function(col) { 486 _hide: function(col) {
494 return this._columns.indexOf(col) === -1; 487 return this._columns.indexOf(col) === -1;
495 }, 488 },
496 489
490 _makeObject: function(sortstr){
491 if (!sortstr) {
492 return undefined;
493 }
494 var pieces = sortstr.split(":");
495 if (pieces.length != 2) {
496 // fail safe
497 return {name: "id", direction:"desc"};
498 }
499 return {
500 name: pieces[0],
501 direction: pieces[1],
502 }
503 },
504
497 _reRender: function(filter, sort) { 505 _reRender: function(filter, sort) {
498 this.$.bot_table.render(); 506 this.$.bot_table.render();
499 }, 507 },
500 508
501 _sortBotTable: function(botA, botB) { 509 _sortBotTable: function(botA, botB) {
502 if (!this._sort) { 510 if (!this._sort) {
503 return 0; 511 return 0;
504 } 512 }
505 var dir = 1; 513 var dir = 1;
506 if (this._sort.direction === "desc") { 514 if (this._sort.direction === "desc") {
507 dir = -1; 515 dir = -1;
508 } 516 }
509 var sort = specialSort[this._sort.name]; 517 var sort = specialSort[this._sort.name];
510 if (sort) { 518 if (sort) {
511 return sort.bind(this)(dir, botA, botB); 519 return sort.bind(this)(dir, botA, botB);
512 } 520 }
513 // Default to a natural compare of the columns. 521 // Default to a natural compare of the columns.
514 var botACol = this._column(this._sort.name, botA); 522 var botACol = this._column(this._sort.name, botA);
515 var botBCol = this._column(this._sort.name, botB); 523 var botBCol = this._column(this._sort.name, botB);
516 524
517 return dir * swarming.naturalCompare(botACol, botBCol); 525 return dir * swarming.naturalCompare(botACol, botBCol);
518 }, 526 },
519 527
520 _sortChange: function(e) { 528 _sortChange: function(e) {
521 // The event we get from sort-toggle tells us the name of what needs 529 // The event we get from sort-toggle tells us the name of what needs
522 // to be sorting and how to sort it. 530 // to be sorting and how to sort it.
523 if (!(e && e.detail && e.detail.name)) { 531 if (!(e && e.detail && e.detail.name)) {
524 return; 532 return;
525 } 533 }
526 // should trigger __filterAndSort 534 // should trigger the computation of _sort and __filterAndSort
527 this.set("_sort", e.detail); 535 this.set("_sortstr", e.detail.name +":"+e.detail.direction);
528 }, 536 },
529 537
530 // _stripSpecial removes the special columns and sorts the remaining 538 // _stripSpecial removes the special columns and sorts the remaining
531 // columns so they always appear in the same order, regardless of 539 // columns so they always appear in the same order, regardless of
532 // the order they are added. 540 // the order they are added.
533 _stripSpecial: function(){ 541 _stripSpecial: function(){
534 return this._columns.filter(function(c){ 542 return this._columns.filter(function(c){
535 return special_columns.indexOf(c) === -1; 543 return special_columns.indexOf(c) === -1;
536 }).sort(); 544 }).sort();
537 }, 545 },
538 546
539 _taskLink: function(data) { 547 _taskLink: function(data) {
540 if (data && data.task_id) { 548 if (data && data.task_id) {
541 return "/user/task/" + data.task_id; 549 return "/user/task/" + data.task_id;
542 } 550 }
543 return undefined; 551 return undefined;
544 } 552 }
545 553
546 }); 554 });
547 })(); 555 })();
548 </script> 556 </script>
549 </dom-module> 557 </dom-module>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698