Chromium Code Reviews| Index: appengine/swarming/ui/res/imp/tasklist/task-list-summary.html |
| diff --git a/appengine/swarming/ui/res/imp/tasklist/task-list-summary.html b/appengine/swarming/ui/res/imp/tasklist/task-list-summary.html |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..99f300c4628ee4fb75fff33a144ecf46219c480e |
| --- /dev/null |
| +++ b/appengine/swarming/ui/res/imp/tasklist/task-list-summary.html |
| @@ -0,0 +1,307 @@ |
| +<!-- |
| + This in an HTML Import-able file that contains the definition |
| + of the following elements: |
| + |
| + <task-list-summary> |
| + |
| + Usage: |
| + |
| + <task-list-summary></task-list-summary> |
| + |
| + This element summarizes and displays the results of the current query. |
| + It links to some global queries (e.g. all running tasks) and some |
| + sub queries (e.g. all pending tasks that match the rest of the |
| + specified tags.) |
| + |
| + Properties: |
| + // inputs |
| + auth_headers: Object, the OAuth2 header to include in the request. This |
| + should come from swarming-app. |
| + columns: Array<String>, the columns the user has selected. Used to create |
| + the links. |
| + count_params: Object, representing the query params sent to the server based |
| + on all the filterable items (e.g. tags). See task-filters for the |
| + schema. |
| + num_tasks: Number, The number of tasks shown (after filtering). |
| + sort: String, the user's current sort string. Used to create the links. |
| + |
| + // outputs |
| + busy: Boolean, if there are any network requests pending. |
| + |
| + Methods: |
| + None. |
| + |
| + Events: |
| + None. |
| +--> |
| + |
| +<link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-layout-classes.html"> |
| + |
| +<link rel="import" href="/res/imp/common/swarming-app.html"> |
| +<link rel="import" href="/res/imp/common/common-behavior.html"> |
| + |
| +<dom-module id="task-list-summary"> |
| + <template> |
| + <style include="swarming-app-style iron-flex"> |
| + :host { |
| + display: block; |
| + border-left: 1px solid black; |
| + padding: 5px 5px; |
| + font-family: sans-serif; |
| + } |
| + .header { |
| + font-size: 1.2em; |
| + font-weight: bold; |
| + } |
| + .column.left { |
| + margin-left: 10px; |
| + } |
| + .right { |
| + text-align: right; |
| + } |
| + .left { |
| + text-align: left; |
| + } |
| + </style> |
| + |
| + <div class="horizontal layout"> |
| + |
| + <div class="column"> |
| + <table> |
| + <thead> |
| + <th class="header right" colspan=2>Selected</th> |
| + </thead> |
| + <tr> |
| + <td class="right"> |
| + Displayed: |
| + </td> |
| + <td class="left">[[num_tasks]]</td> |
| + </tr> |
| + <tr title="By default, these counts are from the last 2 days"> |
| + <td class="right" > |
| + Total: |
| + </td> |
| + <td class="left">[[_selected_exact.count]]</td> |
| + </tr> |
| + <template is="dom-repeat" items="[[_selected_summary]]" as="item" index-as="idx"> |
| + <tr title="By default, these counts are from the last 2 days"> |
| + <td class="right"> |
| + <a href$="[[_makeURL(item.name,'true',columns.*,sort)]]">[[item.human]]</a>: |
| + </td> |
| + <td class="left">[[_idx(_selected_counts, idx, _selected_counts.*)]]</td> |
| + </tr> |
| + </template> |
| + </table> |
| + </div> |
| + |
| + <div class="left column"> |
| + <table> |
| + <thead> |
| + <th class="header right" colspan=2>All Tasks in Time Range</th> |
| + </thead> |
| + <template is="dom-repeat" items="[[_all_summary]]" as="item" index-as="idx"> |
| + <tr title="By default, this is the last 2 days"> |
| + <td class="right"> |
| + <a href$="[[_makeURL(item.name,'',columns.*,sort)]]">[[item.human]]</a>: |
| + </td> |
| + <td class="left">[[_idx(_all_counts, idx, _all_counts.*)]]</td> |
| + </tr> |
| + </template> |
| + </table> |
| + </div> |
| + |
| + </div> |
| + |
| + </template> |
| + <script> |
| + (function(){ |
| + var ALL_TASKS_SUMMARY = [ |
| + {name:"ALL", human:"All"}, |
| + {name:"BOT_DIED", human:"Bot Died"}, |
| + {name:"CANCELED", human:"Canceled"}, |
| + {name:"COMPLETED_SUCCESS", human:"Completed (Success)"}, |
| + {name:"COMPLETED_FAILURE", human:"Completed (Failure)"}, |
| + {name:"DEDUPED", human:"Deduplicated"}, |
| + {name:"EXPIRED", human:"Expired"}, |
| + {name:"PENDING", human:"Pending"}, |
| + {name:"RUNNING", human:"Running"}, |
| + {name:"TIMED_OUT", human:"Timed Out"}, |
| + ]; |
| + var SELECTED_TASKS_SUMMARY = [ |
| + {name:"BOT_DIED", human:"Bot Died"}, |
| + {name:"CANCELED", human:"Canceled"}, |
| + {name:"COMPLETED_SUCCESS", human:"Completed (Success)"}, |
| + {name:"COMPLETED_FAILURE", human:"Completed (Failure)"}, |
| + {name:"DEDUPED", human:"Deduplicated"}, |
| + {name:"EXPIRED", human:"Expired"}, |
| + {name:"PENDING", human:"Pending"}, |
| + {name:"RUNNING", human:"Running"}, |
| + {name:"TIMED_OUT", human:"Timed Out"}, |
| + ]; |
| + Polymer({ |
| + is: 'task-list-summary', |
| + |
| + behaviors: [SwarmingBehaviors.CommonBehavior], |
| + |
| + properties: { |
| + auth_headers: { |
| + type: Object, |
| + }, |
| + busy: { |
| + type: Boolean, |
| + computed: "_anyBusy(_busyArr1.*,_busyArr2.*,_busy3)", |
| + notify: true, |
| + }, |
| + count_params: { |
| + type: Object, |
| + }, |
| + columns: { |
| + type: Array, |
| + }, |
| + num_tasks: { |
| + type: Number, |
| + }, |
| + sort: { |
| + type: String, |
| + }, |
| + |
| + _busyArr1: { |
| + type:Array, |
| + value: function() { |
| + return []; |
| + } |
| + }, |
| + _busyArr2: { |
| + type:Array, |
| + value: function() { |
| + return []; |
| + } |
| + }, |
| + _busy3: { |
| + type: Boolean, |
| + value: false, |
| + }, |
| + _all_counts: { |
| + type: Array, |
| + value: function() { |
| + return []; |
| + } |
| + }, |
| + _all_summary: { |
| + type: Array, |
| + value: function() { |
| + return ALL_TASKS_SUMMARY; |
| + } |
| + }, |
| + _selected_counts: { |
| + type: Array, |
| + value: function() { |
| + return []; |
| + } |
| + }, |
| + _selected_exact: { |
| + type: Object, |
| + }, |
| + _selected_summary: { |
| + type: Array, |
| + value: function() { |
| + return SELECTED_TASKS_SUMMARY; |
| + } |
| + }, |
| + |
| + }, |
| + |
| + observers: [ |
| + "_recountEverything(auth_headers.*,count_params.*)" |
| + ], |
| + |
| + _anyBusy: function() { |
| + for (var i = 0; i<this._busyArr1.length; i++) { |
| + if (this._busyArr1[i].status) { |
| + return true; |
| + } |
| + } |
| + for (var i = 0; i<this._busyArr2.length; i++) { |
| + if (this._busyArr2[i].status) { |
| + return true; |
| + } |
| + } |
| + return this._busy3; |
| + }, |
| + |
| + _idx: function(obj, idx) { |
| + return obj && obj[idx] && obj[idx].count; |
| + }, |
| + |
| + _recountEverything: function() { |
| + if (!this.auth_headers || !this.count_params) { |
| + return; |
| + } |
| + // convert to seconds because API uses seconds. |
| + var now = (new Date()).getTime()/1000; |
| + var last2Days = now - 2 * 24 * 60 * 60; |
|
M-A Ruel
2017/02/06 20:04:32
While this works great on the staging instance, th
kjlubick
2017/02/06 21:18:02
Backed it off to 12 hours. Testing on prod shows
|
| + |
| + // TODO(kjlubick): Once users can specify their own times, respect those limits here. |
| + |
| + var queryObj = { |
| + start: [last2Days], |
| + }; |
| + |
| + for (var i = 0; i < ALL_TASKS_SUMMARY.length; i++) { |
| + if (this._all_counts.length < ALL_TASKS_SUMMARY.length) { |
| + this.push("_all_counts", {}); |
| + } |
| + queryObj.state = [ALL_TASKS_SUMMARY[i].name]; |
| + this._getJsonAsyncArr(i, "_all_counts","/api/swarming/v1/tasks/count","_busyArr1", |
| + this.auth_headers, queryObj); |
| + } |
| + |
| + queryObj = JSON.parse(JSON.stringify(this.count_params)); |
| + queryObj.start = [last2Days]; |
| + this._getJsonAsync("_selected_exact","/api/swarming/v1/tasks/count","_busy3", |
| + this.auth_headers, queryObj); |
| + |
| + for (var j = 0; j < SELECTED_TASKS_SUMMARY.length; j++) { |
| + if (this._selected_counts.length < SELECTED_TASKS_SUMMARY.length) { |
| + this.push("_selected_counts", {}); |
| + } |
| + queryObj.state = [SELECTED_TASKS_SUMMARY[j].name]; |
| + this._getJsonAsyncArr(j, "_selected_counts","/api/swarming/v1/tasks/count","_busyArr2", |
| + this.auth_headers, queryObj); |
| + } |
| + }, |
| + |
| + // _makeURL creates a task-list url that keeps the columns and sort requirements the same |
| + // while changing which state is represented. The preserveOthers signifies if other |
| + // filtering parameters (e.g. tags) should be kept as well. |
| + _makeURL: function(state, preserveOthers) { |
| + var fstr = "state:"+state; |
| + if (preserveOthers) { |
| + var fstr = encodeURIComponent(fstr); |
| + var url = window.location.href; |
| + if (url.indexOf(fstr+"&") !== -1) { |
| + // The state filter is already on the list. |
| + return undefined; |
| + } |
| + if (url.indexOf("f=state") === -1) { |
| + return url + "&f=" + fstr; |
| + } |
| + // Things can't be in multiple states at once - so replace it. |
| + // %3A is url encoded colon (:) |
| + return url.replace(/f=state%3A[A-Z_]+/, `f=state%3A${fstr}`); |
| + } |
| + var params = { |
| + s: [this.sort], |
| + c: this.columns, |
| + } |
| + if (state) { |
| + params["f"] = [fstr]; |
| + } |
| + |
| + return window.location.href.split('?')[0] + '?' + sk.query.fromParamSet(params); |
| + }, |
| + |
| + }); |
| + })(); |
| + </script> |
| +</dom-module> |