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

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

Issue 2204483002: Add UI to new botlist to show summary (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@bot-summary-api
Patch Set: add 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 12 matching lines...) Expand all
23 23
24 <link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-la yout-classes.html"> 24 <link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-la yout-classes.html">
25 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> 25 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html">
26 26
27 <link rel="import" href="/res/imp/common/sort-toggle.html"> 27 <link rel="import" href="/res/imp/common/sort-toggle.html">
28 <link rel="import" href="/res/imp/common/swarming-app.html"> 28 <link rel="import" href="/res/imp/common/swarming-app.html">
29 29
30 <link rel="import" href="bot-filters.html"> 30 <link rel="import" href="bot-filters.html">
31 <link rel="import" href="bot-list-data.html"> 31 <link rel="import" href="bot-list-data.html">
32 <link rel="import" href="bot-list-shared.html"> 32 <link rel="import" href="bot-list-shared.html">
33 33 <link rel="import" href="bot-list-summary.html">
34 34
35 <dom-module id="bot-list"> 35 <dom-module id="bot-list">
36 <template> 36 <template>
37 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style"> 37 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style">
38 bot-filters { 38 bot-filters, bot-list-summary {
39 margin-bottom: 5px; 39 margin-bottom: 5px;
40 margin-right: 5px;
40 } 41 }
41 .bot { 42 .bot {
42 margin:5px; 43 margin:5px;
43 max-width:400px; 44 max-width:400px;
44 min-height:100px; 45 min-height:100px;
45 min-width:300px; 46 min-width:300px;
46 } 47 }
47 table { 48 table {
48 border-collapse: collapse; 49 border-collapse: collapse;
49 margin-left: 5px; 50 margin-left: 5px;
(...skipping 19 matching lines...) Expand all
69 top: 0.4em; 70 top: 0.4em;
70 } 71 }
71 .bot-list th > span { 72 .bot-list th > span {
72 /* Leave space for sort-toggle*/ 73 /* Leave space for sort-toggle*/
73 padding-right: 30px; 74 padding-right: 30px;
74 } 75 }
75 </style> 76 </style>
76 77
77 <swarming-app 78 <swarming-app
78 auth_headers="{{auth_headers}}" 79 auth_headers="{{auth_headers}}"
80 signed_in="{{signed_in}}"
81
79 busy="[[busy]]" 82 busy="[[busy]]"
80 name="Swarming Bot List"> 83 name="Swarming Bot List">
81 84
82 <bot-filters 85 <h2 hidden$="[[signed_in]]">You must sign in to see anything useful.</h2>
83 primary_map="[[primary_map]]"
84 primary_arr="[[primary_arr]]"
85 86
86 columns="{{columns}}" 87 <div hidden$="[[_not(signed_in)]]">
87 filter="{{filter}}"
88 verbose="{{verbose}}">
89 </bot-filters>
90 88
91 <bot-list-data 89 <div class="horizontal layout">
92 auth_headers="[[auth_headers]]"
93 90
94 bots="{{bots}}" 91 <bot-filters
95 busy="{{busy}}" 92 primary_map="[[primary_map]]"
96 primary_map="{{primary_map}}" 93 primary_arr="[[primary_arr]]"
97 primary_arr="{{primary_arr}}">
98 </bot-list-data>
99 94
100 <table class="bot-list"> 95 columns="{{columns}}"
101 <thead on-sort_change="sortChange"> 96 filter="{{filter}}"
102 <!-- To allow for dynamic columns without having a lot of copy-pasted 97 verbose="{{verbose}}">
103 code, we break columns up into "special" and "plain" columns. Special 98 </bot-filters>
104 columns require some sort of HTML output (e.g. anchor tags) and plain 99
105 columns just output text. The plain columns use Polymer functions to 100 <bot-list-summary
106 insert their text [_header(), _column(), _deviceColumn()]. Polymer 101 fleet="[[fleet]]"
107 functions do not allow HTML (to avoid XSS), so special columns, like id 102 filtered_bots="[[filteredSortedBots]]">
108 and task are inserted in a fixed order. 103 </bot-list-summary>
109 --> 104
110 <th> 105 </div>
111 <span>Bot Id</span> 106
112 <sort-toggle 107 <bot-list-data
113 name="id" 108 auth_headers="[[auth_headers]]"
114 current="[[sort]]"> 109
115 </sort-toggle> 110 bots="{{bots}}"
116 </th> 111 busy="{{busy}}"
117 <!-- This wonky syntax is the proper way to listen to changes on an 112 fleet="{{fleet}}"
118 array (we are listening to all subproperties). The element returned is 113 primary_map="{{primary_map}}"
119 not of much use, so we'll ignore it in _hide() and use this.columns. 114 primary_arr="{{primary_arr}}">
115 </bot-list-data>
116
117 <table class="bot-list">
118 <thead on-sort_change="sortChange">
stephana 2016/08/02 14:00:29 There should be a <tr> element wrapping the <th> e
kjlubick 2016/08/03 12:52:30 Done.
119 <!-- To allow for dynamic columns without having a lot of copy-pasted
120 code, we break columns up into "special" and "plain" columns. Special
121 columns require some sort of HTML output (e.g. anchor tags) and plain
122 columns just output text. The plain columns use Polymer functions to
123 insert their text [_header(), _column(), _deviceColumn()]. Polymer
124 functions do not allow HTML (to avoid XSS), so special columns, like i d
125 and task are inserted in a fixed order.
120 --> 126 -->
121 <th hidden$="[[_hide('task', columns.*)]]"> 127 <th>
122 <span>Current Task</span> 128 <span>Bot Id</span>
123 <sort-toggle
124 name="task"
125 current="[[sort]]">
126 </sort-toggle>
127 </th>
128
129 <template is="dom-repeat"
130 items="[[plain_columns]]"
131 as="c">
132 <th hidden$="[[_hide(c)]]">
133 <span>[[_header(c)]]</span>
134 <sort-toggle 129 <sort-toggle
135 name="[[c]]" 130 name="id"
136 current="[[sort]]"> 131 current="[[sort]]">
137 </sort-toggle> 132 </sort-toggle>
138 </th> 133 </th>
139 </template> 134 <!-- This wonky syntax is the proper way to listen to changes on an
140 </thead> 135 array (we are listening to all subproperties). The element returned is
141 <tbody> 136 not of much use, so we'll ignore it in _hide() and use this.columns.
142 <template id="bot_table" is="dom-repeat" 137 -->
143 items="[[bots]]" 138 <th hidden$="[[_hide('task', columns.*)]]">
144 as="bot" 139 <span>Current Task</span>
145 initial-count=50 140 <sort-toggle
146 filter="_filterBotTable"> 141 name="task"
142 current="[[sort]]">
143 </sort-toggle>
144 </th>
147 145
148 <tr class$="[[_botClass(bot)]]"> 146 <template is="dom-repeat"
149 <td> 147 items="[[plain_columns]]"
150 <a class="center" 148 as="c">
151 href$="[[_botLink(bot.bot_id)]]" 149 <th hidden$="[[_hide(c)]]">
152 target="_blank"> 150 <span>[[_header(c)]]</span>
153 [[bot.bot_id]] 151 <sort-toggle
154 </a> 152 name="[[c]]"
155 </td> 153 current="[[sort]]">
156 <td hidden$="[[_hide('task', columns.*)]]"> 154 </sort-toggle>
157 <a href$="[[_taskLink(bot)]]">[[_taskId(bot)]]</a> 155 </th>
158 </td> 156 </template>
157 </thead>
158 <tbody>
159 <template id="bot_table" is="dom-repeat"
160 items="[[filteredSortedBots]]"
161 as="bot"
162 initial-count=50>
159 163
160 <template is="dom-repeat" 164 <tr class$="[[_botClass(bot)]]">
161 items="[[plain_columns]]" 165 <td>
162 as="c"> 166 <a class="center"
163 <td hidden$="[[_hide(c)]]"> 167 href$="[[_botLink(bot.bot_id)]]"
164 [[_column(c, bot, verbose)]] 168 target="_blank">
169 [[bot.bot_id]]
170 </a>
165 </td> 171 </td>
166 </template> 172 <td hidden$="[[_hide('task', columns.*)]]">
173 <a href$="[[_taskLink(bot)]]">[[_taskId(bot)]]</a>
174 </td>
167 175
168 </tr>
169 <template is="dom-repeat"
170 items="[[_devices(bot)]]"
171 as="device">
172 <tr hidden$="[[_hide('devices', columns.*)]]"
173 class$="[[_deviceClass(device)]]">
174 <td></td>
175 <td hidden$="[[_hide('task', columns.*)]]"></td>
176 <template is="dom-repeat" 176 <template is="dom-repeat"
177 items="[[plain_columns]]" 177 items="[[plain_columns]]"
178 as="c"> 178 as="c">
179 <td hidden$="[[_hide(c)]]"> 179 <td hidden$="[[_hide(c)]]">
180 [[_deviceColumn(c, device, verbose)]] 180 [[_column(c, bot, verbose)]]
181 </td> 181 </td>
182 </template> 182 </template>
183
183 </tr> 184 </tr>
184 </template> <!--devices repeat--> 185 <template is="dom-repeat"
185 </template> <!--bot-table repeat--> 186 items="[[_devices(bot)]]"
186 </tbody> 187 as="device">
187 </table> 188 <tr hidden$="[[_hide('devices', columns.*)]]"
189 class$="[[_deviceClass(device)]]">
190 <td></td>
191 <td hidden$="[[_hide('task', columns.*)]]"></td>
192 <template is="dom-repeat"
193 items="[[plain_columns]]"
194 as="c">
195 <td hidden$="[[_hide(c)]]">
196 [[_deviceColumn(c, device, verbose)]]
197 </td>
198 </template>
199 </tr>
200 </template> <!--devices repeat-->
201 </template> <!--bot-table repeat-->
202 </tbody>
203 </table>
204 </div>
188 205
189 </swarming-app> 206 </swarming-app>
190 207
191 </template> 208 </template>
192 <script> 209 <script>
193 (function(){ 210 (function(){
194 var special_columns = ["id", "task"]; 211 var special_columns = ["id", "task"];
195 212
196 var headerMap = { 213 var headerMap = {
197 // "id" and "task" are special, so they don't go here and have their 214 // "id" and "task" are special, so they don't go here and have their
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 return this._taskId(bot); 300 return this._taskId(bot);
284 }, 301 },
285 }; 302 };
286 303
287 Polymer({ 304 Polymer({
288 is: 'bot-list', 305 is: 'bot-list',
289 behaviors: [SwarmingBehaviors.BotListBehavior], 306 behaviors: [SwarmingBehaviors.BotListBehavior],
290 307
291 properties: { 308 properties: {
292 309
310 bots: {
311 type: Array,
312 },
313
293 columns: { 314 columns: {
294 type: Array, 315 type: Array,
295 }, 316 },
296 // Should have a property "filter" which is a function. 317
297 filter: { 318 filter: {
stephana 2016/08/02 14:00:29 If these are exposed properties then they should b
kjlubick 2016/08/03 12:52:30 Done.
298 type: Object, 319 type: Function,
320 value: function() {
321 return true;
322 },
323 },
324
325 filteredSortedBots: {
326 type: Array,
327 computed: "_filterAndSort(bots,filter.*,sort.*)"
299 }, 328 },
300 329
301 plain_columns: { 330 plain_columns: {
302 type: Array, 331 type: Array,
303 computed: "_stripSpecial(columns.*)", 332 computed: "_stripSpecial(columns.*)",
304 }, 333 },
305 334
306 // sort is an Object {name:String, direction:String}. 335 // sort is an Object {name:String, direction:String}.
307 sort: { 336 sort: {
308 type: Object, 337 type: Object,
338 value: function() {
339 return {
340 name: "id",
341 direction: "asc",
342 };
343 }
309 }, 344 },
310 345
311 verbose: { 346 verbose: {
312 type: Boolean, 347 type: Boolean,
313 } 348 }
314 }, 349 },
315 350
316 observers: [
317 '_reRender(filter.*)',
318 '_checkSorts(columns.*)'
319 ],
320
321 _botClass: function(bot) { 351 _botClass: function(bot) {
322 if (bot.is_dead) { 352 if (bot.is_dead) {
323 return "dead"; 353 return "dead";
324 } 354 }
325 if (bot.quarantined) { 355 if (bot.quarantined) {
326 return "quarantined"; 356 return "quarantined";
327 } 357 }
328 return ""; 358 return "";
329 }, 359 },
330 360
331 _botLink: function(id) { 361 _botLink: function(id) {
332 // TODO(kjlubick) Make this point to /newui/ when appropriate. 362 // TODO(kjlubick) Make this point to /newui/ when appropriate.
333 return "/restricted/bot/"+id; 363 return "/restricted/bot/"+id;
334 }, 364 },
335 365
336 // _checkSorts makes sure that if a column has been removed, the related
337 // sort is also removed.
338 _checkSorts: function() {
339 if (!this.sort) {
340 return;
341 }
342 this._reRender();
343 },
344 366
345 _column: function(col, bot) { 367 _column: function(col, bot) {
346 return columnMap[col].bind(this)(bot); 368 return columnMap[col].bind(this)(bot);
347 }, 369 },
348 370
349 _deviceColumn: function(col, device) { 371 _deviceColumn: function(col, device) {
350 if (col === "devices") { 372 if (col === "devices") {
351 var str = this._androidAlias(device); 373 var str = this._androidAlias(device);
352 if (device.okay) { 374 if (device.okay) {
353 str = this._applyAlias(this._deviceType(device), str); 375 str = this._applyAlias(this._deviceType(device), str);
354 } 376 }
355 str += " S/N:"; 377 str += " S/N:";
356 str += device.serial; 378 str += device.serial;
357 return str; 379 return str;
358 } 380 }
359 if (col === "status") { 381 if (col === "status") {
360 return device.state; 382 return device.state;
361 } 383 }
362 return ""; 384 return "";
363 }, 385 },
364 386
365 _deviceClass: function(device) { 387 _deviceClass: function(device) {
366 if (!device.okay) { 388 if (!device.okay) {
367 return "bad-device"; 389 return "bad-device";
368 } 390 }
369 return ""; 391 return "";
370 }, 392 },
371 393
372 _filterBotTable: function(bot) { 394 _filterAndSort: function(a,b,c) {
373 if (!this.filter || !this.filter.filter) { 395 // We intentionally sort this.bots (and not a copy) to allow users to
374 return true; 396 // "chain" sorts, that is, sort by one thing and then another, and
397 // have both orderings properly impact the list.
398 swarming.stableSort(this.bots, this._sortBotTable.bind(this));
399 var bots = this.bots;
400 if (this.filter) {
401 bots = bots.filter(this.filter.bind(this));
375 } 402 }
376 return this.filter.filter.bind(this)(bot); 403
404 return bots;
377 }, 405 },
378 406
379 _header: function(col){ 407 _header: function(col){
380 return headerMap[col]; 408 return headerMap[col];
381 }, 409 },
382 410
383 _hide: function(col) { 411 _hide: function(col) {
384 return this.columns.indexOf(col) === -1; 412 return this.columns.indexOf(col) === -1;
385 }, 413 },
386 414
(...skipping 14 matching lines...) Expand all
401 429
402 return dir * swarming.naturalCompare(botACol, botBCol); 430 return dir * swarming.naturalCompare(botACol, botBCol);
403 }, 431 },
404 432
405 sortChange: function(e) { 433 sortChange: function(e) {
406 // The event we get from sort-toggle tells us the name of what needs 434 // The event we get from sort-toggle tells us the name of what needs
407 // to be sorting and how to sort it. 435 // to be sorting and how to sort it.
408 if (!(e && e.detail && e.detail.name)) { 436 if (!(e && e.detail && e.detail.name)) {
409 return; 437 return;
410 } 438 }
439 // should trigger __filterAndSort
411 this.set("sort", e.detail); 440 this.set("sort", e.detail);
412 swarming.stableSort(this.bots, this._sortBotTable.bind(this));
413 this._reRender();
414 }, 441 },
415 442
416 // _stripSpecial removes the special columns and sorts the remaining 443 // _stripSpecial removes the special columns and sorts the remaining
417 // columns so they always appear in the same order, regardless of 444 // columns so they always appear in the same order, regardless of
418 // the order they are added. 445 // the order they are added.
419 _stripSpecial: function(){ 446 _stripSpecial: function(){
420 return this.columns.filter(function(c){ 447 return this.columns.filter(function(c){
421 return special_columns.indexOf(c) === -1; 448 return special_columns.indexOf(c) === -1;
422 }).sort(); 449 }).sort();
423 }, 450 },
424 451
425 _taskLink: function(data) { 452 _taskLink: function(data) {
426 if (data && data.task_id) { 453 if (data && data.task_id) {
427 return "/user/task/" + data.task_id; 454 return "/user/task/" + data.task_id;
428 } 455 }
429 return undefined; 456 return undefined;
430 } 457 }
431 458
432 }); 459 });
433 })(); 460 })();
434 </script> 461 </script>
435 </dom-module> 462 </dom-module>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698