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

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

Issue 2408743002: Move elements/ to ui/ (Closed)
Patch Set: Created 4 years, 2 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
(Empty)
1 <!--
2 Copyright 2016 The LUCI Authors. All rights reserved.
3 Use of this source code is governed under the Apache License, Version 2.0
4 that can be found in the LICENSE file.
5
6 This in an HTML Import-able file that contains the definition
7 of the following elements:
8
9 <task-list>
10
11 task-list creats a dynamic table for viewing swarming tasks. Columns can be
12 dynamically filtered and it supports client-side filtering.
13
14 This is a top-level element.
15
16 Properties:
17 client_id: String, Oauth 2.0 client id. It will be set by server-side
18 template evaluation.
19
20 Methods:
21 None.
22
23 Events:
24 None.
25 -->
26
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-button/paper-button.htm l">
30 <link rel="import" href="/res/imp/bower_components/paper-dialog/paper-dialog.htm l">
31 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html">
32
33 <link rel="import" href="/res/imp/common/dynamic-table-behavior.html">
34 <link rel="import" href="/res/imp/common/error-toast.html">
35 <link rel="import" href="/res/imp/common/pageable-data.html">
36 <link rel="import" href="/res/imp/common/sort-toggle.html">
37 <link rel="import" href="/res/imp/common/swarming-app.html">
38 <link rel="import" href="/res/imp/common/task-behavior.html">
39 <link rel="import" href="/res/imp/common/url-param.html">
40
41 <link rel="import" href="task-filters.html">
42 <link rel="import" href="task-list-data.html">
43
44 <dom-module id="task-list">
45 <template>
46 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style dynamic-table-style task-style">
47 task-filters {
48 margin-bottom: 8px;
49 margin-right: 10px;
50 }
51 .task-list th > span {
52 /* Leave space for sort-toggle*/
53 padding-right: 30px;
54 }
55 </style>
56
57 <url-param name="s"
58 value="{{_sortstr}}"
59 default_value="created_ts:desc">
60 </url-param>
61
62 <swarming-app
63 client_id="[[client_id]]"
64 auth_headers="{{_auth_headers}}"
65 permissions="{{_permissions}}"
66 signed_in="{{_signed_in}}"
67 server_details="{{_server_details}}"
68 busy="[[_or(_busy1,_busy2)]]"
69 name="Swarming Task List">
70
71 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2>
72
73 <div hidden$="[[_not(_signed_in)]]">
74 <task-list-data
75 id="data"
76 auth_headers="[[_auth_headers]]"
77 query_params="[[_query_params]]"
78 tasks="[[_items]]"
79 busy="{{_busy1)}}"
80 primary_map="{{_primary_map}}"
81 primary_arr="{{_primary_arr}}">
82 </task-list-data>
83
84 <div class="horizontal layout">
85 <task-filters
86 primary_map="[[_primary_map]]"
87 primary_arr="[[_primary_arr]]"
88 columns="{{_columns}}"
89 query_params="{{_query_params}}"
90 filter="{{_filter}}">
91 </task-filters>
92 </div>
93
94 <table class="task-list">
95 <thead on-sort_change="_sortChange">
96 <!-- To allow for dynamic columns without having a lot of copy-pasted
97 code, we break columns up into "special" and "plain" columns. Special
98 columns require some sort of HTML output (e.g. anchor tags) and plain
99 columns just output text. The plain columns use Polymer functions to
100 insert their text [_header(), _column(), _deviceColumn()]. Polymer
101 functions do not allow HTML (to avoid XSS), so special columns, like i d
102 and task are inserted in a fixed order.
103 -->
104 <tr>
105 <th>
106 <span>Task Name</span>
107 <sort-toggle
108 name="name"
109 current="[[_sort]]">
110 </sort-toggle>
111 </th>
112 <!-- This wonky syntax is the proper way to listen to changes on a n
113 array (we are listening to all subproperties). The element returne d is
114 not of much use, so we'll ignore it in _hide() and use this._colum ns.
115 -->
116 <th hidden$="[[_hide('state', _columns.*)]]">
117 <span>State</span>
118 <sort-toggle
119 name="state"
120 current="[[_sort]]">
121 </sort-toggle>
122 </th>
123
124 <th hidden$="[[_hide('bot', _columns.*)]]">
125 <span>Bot Assigned</span>
126 <sort-toggle
127 name="bot"
128 current="[[_sort]]">
129 </sort-toggle>
130 </th>
131
132 <th hidden$="[[_hide('deduped_from', _columns.*)]]">
133 <span>Deduped from</span>
134 <sort-toggle
135 name="deduped_from"
136 current="[[_sort]]">
137 </sort-toggle>
138 </th>
139
140 <th hidden$="[[_hide('sk_revision', _columns.*)]]">
141 <span>Skia Revision</span>
142 <sort-toggle
143 name="sk_revision"
144 current="[[_sort]]">
145 </sort-toggle>
146 </th>
147
148 <template
149 is="dom-repeat"
150 items="[[_plainColumns]]"
151 as="c">
152 <th hidden$="[[_hide(c)]]">
153 <span>[[_header(c)]]</span>
154 <sort-toggle
155 name="[[c]]"
156 current="[[_sort]]">
157 </sort-toggle>
158 </th>
159 </template>
160 </tr>
161 </thead>
162 <tbody>
163 <template
164 id="tasks_table"
165 is="dom-repeat"
166 items="[[_filteredSortedItems]]"
167 as="task"
168 initial-count=50>
169
170 <tr class$="[[_taskClass(task)]]">
171 <td>
172 <a
173 class="center"
174 href$="[[_taskLink(task.task_id)]]"
175 target="_blank">
176 [[task.name]]
177 </a>
178 </td>
179 <td hidden$="[[_hide('state', _columns.*)]]">
180 [[_column('state', task)]]
181 <paper-button
182 raised
183 hidden$="[[_cannotCancel(task,_permissions)]]"
184 on-tap="_promptCancel">
185 Cancel
186 </paper-button>
187 </td>
188 <td hidden$="[[_hide('bot', _columns.*)]]">
189 <a
190 class="center"
191 href$="[[_botLink(task.bot_id)]]"
192 target="_blank">
193 [[_column('bot',task)]]
194 </a>
195 </td>
196 <td hidden$="[[_hide('deduped_from', _columns.*)]]">
197 <a
198 class="center"
199 href$="[[_taskLink(task.deduped_from)]]"
200 target="_blank">
201 [[_column('deduped_from',task)]]
202 </a>
203 </td>
204
205 <td hidden$="[[_hide('sk_revision', _columns.*)]]">
206 <a
207 class="center"
208 href$="[[_skLink(task, _server_details.sk_revision_prefix) ]]"
209 target="_blank">
210 [[_column('sk_revision',task)]]
211 </a>
212 </td>
213
214 <template
215 is="dom-repeat"
216 items="[[_plainColumns]]"
217 as="c">
218 <td hidden$="[[_hide(c)]]">
219 [[_column(c, task)]]
220 </td>
221 </template>
222
223 </tr>
224 </template> <!--tasks_table repeat-->
225 </tbody>
226 </table>
227
228 <pageable-data
229 id="page_tasks"
230 busy="{{_busy2}}"
231 label="Show more tasks"
232 output="{{_items}}"
233 parse="[[_parseTasks]]">
234 </pageable-data>
235 </div>
236 </swarming-app>
237
238 <paper-dialog id="prompt" modal on-iron-overlay-closed="_promptClosed">
239 <h2>Are you sure?</h2>
240 <div>Are you sure you want to [[_dialogPrompt]]?</div>
241 <div class="buttons">
242 <paper-button dialog-dismiss autofocus>No</paper-button>
243 <paper-button dialog-confirm>Yes</paper-button>
244 </div>
245 </paper-dialog>
246
247 <error-toast></error-toast>
248
249 </template>
250 <script>
251 (function(){
252 var specialColumns = ["deduped_from", "name", "state", "bot", "sk_revision"] ;
253
254 // Given a time attribute like "abandoned_ts", humanTime returns a function
255 // that returns the human-friendly version of that attribute. The human
256 // friendly time was created in task-list-data.
257 function humanTime(attr) {
258 return function(task) {
259 return this._attribute(task, "human_" + attr)[0];
260 }
261 }
262 var columnMap = {
263 abandoned_ts: humanTime("abandoned_ts"),
264 bot: function(task) {
265 return this._attribute(task, "bot_id")[0];
266 },
267 completed_ts: humanTime("completed_ts"),
268 costs_usd: function(task) {
269 return this._attribute(task, "costs_usd", 0)[0];
270 },
271 created_ts: humanTime("created_ts"),
272 duration: humanTime("duration"),
273 modified_ts: humanTime("modified_ts"),
274 sk_revision: function(task) {
275 var r = this._attribute(task, "sk_revision")[0];
276 return r.substring(0,8);
277 },
278 started_ts: humanTime("started_ts"),
279 state: function(task) {
280 var state = this._attribute(task, "state")[0];
281 if (state === "COMPLETED") {
282
283 if (this._attribute(task, "failure", false)[0]) {
284 return "COMPLETED (FAILURE)";
285 }
286 var tryNum = this._attribute(task, "try_number", "-1")[0];
287 if (tryNum === "0") {
288 return "COMPLETED (DEDUPED)";
289 }
290 return "COMPLETED (SUCCESS)";
291 }
292 return state;
293 },
294 };
295 var headerMap = {
296 "user": "Requesting User",
297 };
298
299 // Given a time attribute like "abandoned_ts", sortableTime returns a functi on
300 // that compares the tasks based on the attribute. This is used for sorting .
301 function sortableTime(attr) {
302 // sort times based on the string they come with, formatted like
303 // "2016-08-16T13:12:40.606300" which sorts correctly. Locale time
304 // (used in the columns), does not.
305 return function(dir, a, b) {
306 var aCol = this._attribute(a, attr, "0")[0];
307 var bCol = this._attribute(b, attr, "0")[0];
308
309 return dir * (aCol - bCol);
310 }
311 }
312 var specialSort = {
313 abandoned_ts: sortableTime("abandoned_ts"),
314 completed_ts: sortableTime("completed_ts"),
315 created_ts: sortableTime("created_ts"),
316 duration: sortableTime("duration"),
317 modified_ts: sortableTime("modified_ts"),
318 started_ts: sortableTime("started_ts"),
319 };
320
321 Polymer({
322 is: 'task-list',
323 behaviors: [
324 SwarmingBehaviors.DynamicTableBehavior,
325 SwarmingBehaviors.TaskBehavior,
326 ],
327
328 properties: {
329 client_id: {
330 type: String,
331 },
332
333 _busy1: {
334 type: Boolean,
335 value: false
336 },
337 _busy2: {
338 type: Boolean,
339 value: false
340 },
341 _parseTasks: {
342 type: Function,
343 value: function() {
344 return this.$.data.parseTasks.bind(this);
345 }
346 },
347 // The task id to cancel if the prompt is accepted.
348 _toCancel: {
349 type: String,
350 },
351
352 // For dynamic table.
353 _columnMap: {
354 type: Object,
355 value: function() {
356 var base = this._commonColumns();
357 for (var attr in columnMap) {
358 base[attr] = columnMap[attr];
359 }
360 return base;
361 },
362 },
363 _headerMap: {
364 type: Object,
365 value: headerMap,
366 },
367 _specialColumns: {
368 type: Array,
369 value: specialColumns,
370 },
371 _specialSort: {
372 type: Object,
373 value: specialSort,
374 },
375 },
376
377 observers:["reload(_query_params,_auth_headers)"],
378
379 _attribute: function(task, col, def) {
380 if (def === undefined) {
381 def = "none";
382 }
383 var retVal = this._tag(task, col) || task[col] || [def];
384 if (!Array.isArray(retVal)) {
385 return [retVal];
386 }
387 return retVal;
388 },
389
390 _cannotCancel: function(task, permissions) {
391 return !(permissions && permissions.cancel_task &&
392 this._column("state", task) === "PENDING");
393 },
394
395 _cancelTask: function() {
396 var url = "/_ah/api/swarming/v1/task/" + this._toCancel +"/cancel";
397 swarming.postWithToast(url, "Canceling task " + this._toCancel, this._au th_headers);
398 this.set("_toCancel", "");
399 },
400
401 _promptClosed: function(e) {
402 if (e.detail.confirmed) {
403 this._cancelTask();
404 }
405 },
406
407 _promptCancel: function(e) {
408 var task = e.model.task;
409 if (!task || !task.task_id) {
410 console.log("Missing task info", task);
411 return
412 }
413 this.set("_toCancel", task.task_id);
414 this.set("_dialogPrompt", 'cancel task "'+ task.name +'"');
415 this.$.prompt.open();
416 },
417
418 reload: function() {
419 if (!this._auth_headers || !this._query_params) {
420 return;
421 }
422 var url = "/_ah/api/swarming/v1/tasks/list?" + sk.query.fromParamSet(thi s._query_params);
423 this.$.page_tasks.load(url,this._auth_headers);
424 },
425
426 _skLink: function(task, sk_revision_prefix) {
427 var r = this._attribute(task, "sk_revision")[0];
428 if (r === "none" || !sk_revision_prefix) {
429 return false;
430 }
431 return sk_revision_prefix + r;
432 },
433
434 _tag: function(task, col) {
435 if (!task || !task.tagMap) {
436 return undefined;
437 }
438 return task.tagMap[col];
439 },
440
441 _taskClass: function(task) {
442 return this.stateClass(this._column("state", task));
443 }
444 });
445 })();
446 </script>
447 </dom-module>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698