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

Side by Side Diff: appengine/swarming/elements/res/imp/tasklist/task-list.html

Issue 2266133002: Add filter to task-list (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@extract-filters
Patch Set: Fix default sort Created 4 years, 3 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 <task-list> 9 <task-list>
10 10
11 task-list creats a dynamic table for viewing swarming tasks. Columns can be 11 task-list creats a dynamic table for viewing swarming tasks. Columns can be
12 dynamically filtered and it supports client-side filtering. 12 dynamically filtered and it supports client-side filtering.
13 13
14 This is a top-level element. 14 This is a top-level element.
15 15
16 Properties: 16 Properties:
17 client_id: String, Oauth 2.0 client id. It will be set by server-side 17 client_id: String, Oauth 2.0 client id. It will be set by server-side
18 template evaluation. 18 template evaluation.
19 19
20 Methods: 20 Methods:
21 None. 21 None.
22 22
23 Events: 23 Events:
24 None. 24 None.
25 --> 25 -->
26 26
27 <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/iron-flex-layout/iron-flex-la yout-classes.html">
28 <link rel="import" href="/res/imp/bower_components/paper-button/paper-button.htm l">
29 <link rel="import" href="/res/imp/bower_components/paper-toast/paper-toast.html" >
28 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> 30 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html">
29 31
30 <link rel="import" href="/res/imp/common/dynamic-table-behavior.html"> 32 <link rel="import" href="/res/imp/common/dynamic-table-behavior.html">
31 <link rel="import" href="/res/imp/common/sort-toggle.html"> 33 <link rel="import" href="/res/imp/common/sort-toggle.html">
32 <link rel="import" href="/res/imp/common/swarming-app.html"> 34 <link rel="import" href="/res/imp/common/swarming-app.html">
33 <link rel="import" href="/res/imp/common/url-param.html"> 35 <link rel="import" href="/res/imp/common/url-param.html">
34 36
35 <link rel="import" href="task-filters.html"> 37 <link rel="import" href="task-filters.html">
36 <link rel="import" href="task-list-data.html"> 38 <link rel="import" href="task-list-data.html">
37 39
38 <dom-module id="task-list"> 40 <dom-module id="task-list">
39 <template> 41 <template>
40 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style dynamic-table-style"> 42 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style dynamic-table-style">
41 43 task-filters {
44 margin-bottom: 8px;
45 margin-right: 10px;
46 }
42 .task-list th > span { 47 .task-list th > span {
43 /* Leave space for sort-toggle*/ 48 /* Leave space for sort-toggle*/
44 padding-right: 30px; 49 padding-right: 30px;
45 } 50 }
51
52 /* These colors are from buildbot */
53 .failed {
54 background-color: #ffdddd;
55 }
56 .died {
57 background-color: #cccccc;
58 }
59 .exception {
60 background-color: #edd2ff;
61 }
62 .pending {
63 background-color: #fffc6c;
64 }
46 </style> 65 </style>
47 66
48 <url-param name="sort" 67 <url-param name="sort"
49 value="{{_sortstr}}" 68 value="{{_sortstr}}"
50 default_value="id:asc"> 69 default_value="created_ts:desc">
51 </url-param> 70 </url-param>
52 71
53 <swarming-app 72 <swarming-app
54 client_id="[[client_id]]" 73 client_id="[[client_id]]"
55 auth_headers="{{_auth_headers}}" 74 auth_headers="{{_auth_headers}}"
75 permissions="{{_permissions}}"
56 signed_in="{{_signed_in}}" 76 signed_in="{{_signed_in}}"
57 busy="[[_busy]]" 77 busy="[[_busy]]"
58 name="Swarming Task List"> 78 name="Swarming Task List">
59 79
60 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> 80 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2>
61 81
62 <div hidden$="[[_not(_signed_in)]]"> 82 <div hidden$="[[_not(_signed_in)]]">
63 <task-list-data 83 <task-list-data
64 auth_headers="[[_auth_headers]]" 84 auth_headers="[[_auth_headers]]"
65 query_params="[[_query_params]]" 85 query_params="[[_query_params]]"
66 tasks="{{_items}}" 86 tasks="{{_items}}"
67 busy="{{_busy}}"> 87 busy="{{_busy}}"
88 primary_map="{{_primary_map}}"
89 primary_arr="{{_primary_arr}}">
68 </task-list-data> 90 </task-list-data>
69 91
92 <paper-toast id="toast"></paper-toast>
93
70 <div class="horizontal layout"> 94 <div class="horizontal layout">
71 95
72 <task-filters 96 <task-filters
97 primary_map="[[_primary_map]]"
98 primary_arr="[[_primary_arr]]"
73 columns="{{_columns}}" 99 columns="{{_columns}}"
74 query_params="{{_query_params}}" 100 query_params="{{_query_params}}"
75 filter="{{_filter}}" 101 filter="{{_filter}}">
76 verbose="{{_verbose}}">
77 </task-filters> 102 </task-filters>
78 103
79 </div> 104 </div>
80 105
81 <table class="task-list"> 106 <table class="task-list">
82 <thead on-sort_change="_sortChange"> 107 <thead on-sort_change="_sortChange">
83 <!-- To allow for dynamic columns without having a lot of copy-pasted 108 <!-- To allow for dynamic columns without having a lot of copy-pasted
84 code, we break columns up into "special" and "plain" columns. Special 109 code, we break columns up into "special" and "plain" columns. Special
85 columns require some sort of HTML output (e.g. anchor tags) and plain 110 columns require some sort of HTML output (e.g. anchor tags) and plain
86 columns just output text. The plain columns use Polymer functions to 111 columns just output text. The plain columns use Polymer functions to
87 insert their text [_header(), _column(), _deviceColumn()]. Polymer 112 insert their text [_header(), _column(), _deviceColumn()]. Polymer
88 functions do not allow HTML (to avoid XSS), so special columns, like i d 113 functions do not allow HTML (to avoid XSS), so special columns, like i d
89 and task are inserted in a fixed order. 114 and task are inserted in a fixed order.
90 --> 115 -->
91 <tr> 116 <tr>
92 <th> 117 <th>
93 <span>Task Name</span> 118 <span>Task Name</span>
94 <sort-toggle 119 <sort-toggle
95 name="name" 120 name="name"
96 current="[[_sort]]"> 121 current="[[_sort]]">
97 </sort-toggle> 122 </sort-toggle>
98 </th> 123 </th>
99 <!-- This wonky syntax is the proper way to listen to changes on a n 124 <!-- This wonky syntax is the proper way to listen to changes on a n
100 array (we are listening to all subproperties). The element returne d is 125 array (we are listening to all subproperties). The element returne d is
101 not of much use, so we'll ignore it in _hide() and use this._colum ns. 126 not of much use, so we'll ignore it in _hide() and use this._colum ns.
102 --> 127 -->
103 <th hidden$="[[_hide('state', _columns.*)]]"> 128 <th hidden$="[[_hide('state', _columns.*)]]">
104 <span>Status</span> 129 <span>State</span>
105 <sort-toggle 130 <sort-toggle
106 name="state" 131 name="state"
107 current="[[_sort]]"> 132 current="[[_sort]]">
108 </sort-toggle> 133 </sort-toggle>
109 </th> 134 </th>
110 135
136 <th hidden$="[[_hide('deduped_from', _columns.*)]]">
137 <span>Deduped from</span>
138 <sort-toggle
139 name="deduped_from"
140 current="[[_sort]]">
141 </sort-toggle>
142 </th>
143
111 <template 144 <template
112 is="dom-repeat" 145 is="dom-repeat"
113 items="[[_plainColumns]]" 146 items="[[_plainColumns]]"
114 as="c"> 147 as="c">
115 <th hidden$="[[_hide(c)]]"> 148 <th hidden$="[[_hide(c)]]">
116 <span>[[_header(c)]]</span> 149 <span>[[_header(c)]]</span>
117 <sort-toggle 150 <sort-toggle
118 name="[[c]]" 151 name="[[c]]"
119 current="[[_sort]]"> 152 current="[[_sort]]">
120 </sort-toggle> 153 </sort-toggle>
121 </th> 154 </th>
122 </template> 155 </template>
123 </tr> 156 </tr>
124 </thead> 157 </thead>
125 <tbody> 158 <tbody>
126 <template 159 <template
127 id="tasks_table" 160 id="tasks_table"
128 is="dom-repeat" 161 is="dom-repeat"
129 items="[[_filteredSortedItems]]" 162 items="[[_filteredSortedItems]]"
130 as="task" 163 as="task"
131 initial-count=50> 164 initial-count=50>
132 165
133 <tr class$="[[_taskClass(task)]]"> 166 <tr class$="[[_taskClass(task)]]">
134 <td> 167 <td>
135 <a 168 <a
136 class="center" 169 class="center"
137 href$="[[_taskLink(task)]]" 170 href$="[[_taskLink(task.task_id)]]"
138 target="_blank"> 171 target="_blank">
139 [[task.name]] 172 [[task.name]]
140 </a> 173 </a>
141 </td> 174 </td>
142 <td hidden$="[[_hide('state', _columns.*)]]"> 175 <td hidden$="[[_hide('state', _columns.*)]]">
143 [[_column('state', task, _verbose)]] 176 [[_column('state', task)]]
144 <!-- TODO(kjlubick): Add button to stop pending.--> 177 <paper-button
178 raised
179 hidden$="[[_cannotCancel(task,_permissions)]]"
180 on-tap="_cancelTask">
181 Cancel
182 </paper-button>
183 </td>
184 <td hidden$="[[_hide('deduped_from', _columns.*)]]">
185 <a
186 class="center"
187 href$="[[_taskLink(task.deduped_from)]]"
188 target="_blank">
189 [[_column('deduped_from',task)]]
190 </a>
145 </td> 191 </td>
146 192
147 <template 193 <template
148 is="dom-repeat" 194 is="dom-repeat"
149 items="[[_plainColumns]]" 195 items="[[_plainColumns]]"
150 as="c"> 196 as="c">
151 <td hidden$="[[_hide(c)]]"> 197 <td hidden$="[[_hide(c)]]">
152 [[_column(c, task, _verbose)]] 198 [[_column(c, task)]]
153 </td> 199 </td>
154 </template> 200 </template>
155 201
156 </tr> 202 </tr>
157 </template> <!--tasks_table repeat--> 203 </template> <!--tasks_table repeat-->
158 </tbody> 204 </tbody>
159 </table> 205 </table>
160 </div> 206 </div>
161 207
162 </swarming-app> 208 </swarming-app>
163 209
164 </template> 210 </template>
165 <script> 211 <script>
166 (function(){ 212 (function(){
167 var specialColumns = ["name", "state"]; 213 var specialColumns = ["deduped_from", "name", "state"];
168 var columnMap = {}; 214 var columnMap = {
215 costs_usd: function(task) {
216 return this._attribute(task, "costs_usd", 0)[0];
217 },
218 state: function(task) {
219 var state = this._attribute(task, "state")[0];
220 if (state === "COMPLETED") {
221
222 if (this._attribute(task, "failure", false)[0]) {
223 return "COMPLETED (FAILURE)";
224 }
225 var tryNum = this._attribute(task, "try_number", "-1")[0];
226 if (tryNum === "0") {
227 return "COMPLETED (DEDUPED)";
228 }
229 return "COMPLETED (SUCCESS)";
230 }
231 return state;
232 },
233 };
169 var headerMap = { 234 var headerMap = {
170 "user": "Requesting User", 235 "user": "Requesting User",
171 }; 236 };
172 var specialSort = {}; 237 var specialSort = {};
173 238
174 Polymer({ 239 Polymer({
175 is: 'task-list', 240 is: 'task-list',
176 behaviors: [ 241 behaviors: [
177 SwarmingBehaviors.DynamicTableBehavior, 242 SwarmingBehaviors.DynamicTableBehavior,
178 ], 243 ],
179 244
180 properties: { 245 properties: {
181 client_id: { 246 client_id: {
182 type: String, 247 type: String,
183 }, 248 },
184 249
185 // For dynamic table. 250 // For dynamic table.
186 _columnMap: { 251 _columnMap: {
187 type: Object, 252 type: Object,
188 value: columnMap, 253 value: function() {
254 var base = this._commonColumns();
255 for (var attr in columnMap) {
256 base[attr] = columnMap[attr];
257 }
258 return base;
259 },
189 }, 260 },
190 _headerMap: { 261 _headerMap: {
191 type: Object, 262 type: Object,
192 value: headerMap, 263 value: headerMap,
193 }, 264 },
194 _specialColumns: { 265 _specialColumns: {
195 type: Array, 266 type: Array,
196 value: specialColumns, 267 value: specialColumns,
197 }, 268 },
198 _specialSort: { 269 _specialSort: {
199 type: Object, 270 type: Object,
200 value: specialSort, 271 value: specialSort,
201 }, 272 },
202 }, 273 },
203 274
204 _attribute: function(task, col, def) { 275 _attribute: function(task, col, def) {
205 var retVal = task[col] || [def]; 276 if (def === undefined) {
277 def = "none";
278 }
279 var retVal = this._tag(task, col) || task[col] || [def];
206 if (!Array.isArray(retVal)) { 280 if (!Array.isArray(retVal)) {
207 return [retVal]; 281 return [retVal];
208 } 282 }
209 return retVal; 283 return retVal;
210 }, 284 },
211 285
212 _taskLink: function(task) { 286 _cannotCancel: function(task, permissions) {
287 return !(permissions && permissions.can_cancel_task &&
288 this._column("state", task) === "PENDING");
289 },
290
291 _cancelTask: function(e) {
292 var task = e.model.task;
293 if (!task || !task.task_id) {
294 console.log("Missing task info", task);
295 return
296 }
297 var id = task.task_id
298
299 // Keep toast displayed until we hear back from the cancel.
300 this.$.toast.duration = 0;
301 this.$.toast.text="Canceling task " + id;
302 this.$.toast.open();
303 var url = "/_ah/api/swarming/v1/task/" + id +"/cancel";
304 sk.request("POST", url, undefined, this._auth_headers).then(function(res ponse) {
305 this.$.toast.close();
306 this.$.toast.show({
307 text: "Request sent. Response: "+response,
308 duration: 3000,
309 });
310 }.bind(this)).catch(function(reason) {
311 console.log("Cancellation failed", reason);
312 this.$.toast.close();
313 this.$.toast.show({
314 text: "Request failed. Reason: "+reason,
315 duration: 3000,
316 });
317 }.bind(this));
318 },
319
320 _tag: function(task, col) {
321 if (!task || !task.tagMap) {
322 return undefined;
323 }
324 return task.tagMap[col];
325 },
326
327 _taskLink: function(taskId) {
328 if (!taskId) {
329 return undefined;
330 }
213 // TODO(kjlubick) Make this point to /newui/ when appropriate. 331 // TODO(kjlubick) Make this point to /newui/ when appropriate.
214 return "/restricted/task/"+task.task_id; 332 return "/restricted/task/"+taskId;
215 }, 333 },
216 334
217 _taskClass: function(task) { 335 _taskClass: function(task) {
218 // TODO(kjlubick): Color tasks? 336 var state = this._column("state", task);
337 if (state === "CANCELED" ||state === "TIMED_OUT" || state === "EXPIRED") {
338 return "exception";
339 }
340 if (state === "BOT_DIED") {
341 return "died";
342 }
343 if (state === "COMPLETED (FAILURE)") {
344 return "failed";
345 }
346 if (state === "RUNNING" || state === "PENDING") {
347 return "pending";
348 }
219 return ""; 349 return "";
220 } 350 }
221 351
222 }); 352 });
223 })(); 353 })();
224 </script> 354 </script>
225 </dom-module> 355 </dom-module>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698