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

Side by Side Diff: appengine/swarming/elements/build/elements.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: tidy up and add task cancelling 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 <!DOCTYPE html><html><head><!-- 1 <!DOCTYPE html><html><head><!--
2 @license 2 @license
3 Copyright (c) 2016 The Polymer Project Authors. All rights reserved. 3 Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt 4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also 7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 --><!-- 9 --><!--
10 @license 10 @license
(...skipping 14592 matching lines...) Expand 10 before | Expand all | Expand 10 after
14603 }, 14603 },
14604 auth_headers: { 14604 auth_headers: {
14605 type: Object, 14605 type: Object,
14606 notify: true, 14606 notify: true,
14607 }, 14607 },
14608 signed_in: { 14608 signed_in: {
14609 type: Boolean, 14609 type: Boolean,
14610 value: false, 14610 value: false,
14611 notify:true, 14611 notify:true,
14612 }, 14612 },
14613 permissions: {
14614 type: Object,
14615 value: function() {
14616 // TODO(kjlubick): Make this call the whoami endpoint after signing in.
14617 return {
14618 can_cancel_task: true,
14619 }
14620 },
14621 notify: true,
14622 },
14613 14623
14614 busy: { 14624 busy: {
14615 type: Boolean, 14625 type: Boolean,
14616 }, 14626 },
14617 name: { 14627 name: {
14618 type: String, 14628 type: String,
14619 }, 14629 },
14620 }, 14630 },
14621 14631
14622 }); 14632 });
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
14676 }, 14686 },
14677 14687
14678 }); 14688 });
14679 </script> 14689 </script>
14680 </dom-module><script> 14690 </dom-module><script>
14681 window.SwarmingBehaviors = window.SwarmingBehaviors || {}; 14691 window.SwarmingBehaviors = window.SwarmingBehaviors || {};
14682 (function(){ 14692 (function(){
14683 // This behavior wraps up all the shared swarming functionality. 14693 // This behavior wraps up all the shared swarming functionality.
14684 SwarmingBehaviors.CommonBehavior = { 14694 SwarmingBehaviors.CommonBehavior = {
14685 14695
14696 // _getJsonAsync makes an XHR to a url, parses the response as JSON
14697 // and sticks the resulting object into the property with the name given
14698 // by "bindTo". If busy is defined, the property with that name will be
14699 // set to true while the request is in flight and false afterwards.
14700 // request headers (e.g. authentication) and query params will be used if
14701 // provided. On error, bindTo will be set to false. It is not set to
14702 // undefined because computed values in Polymer don't fire if a property
14703 // is undefined. Clients should check that bindTo is not falsey.
14704 _getJsonAsync: function(bindTo, url, busy, headers, params) {
14705 if (!bindTo || !url) {
14706 console.log("Need at least a polymer element to bind to and a url");
14707 return;
14708 }
14709 if (busy) {
14710 this.set(busy, true);
14711 }
14712 url = url + "?" + sk.query.fromParamSet(params);
14713 sk.request("GET", url, "", headers).then(JSON.parse).then(function(json) {
14714 this.set(bindTo, json);
14715 if (busy) {
14716 this.set(busy, false);
14717 }
14718 }.bind(this)).catch(function(reason){
14719 console.log("Reason for failure of request to " + url, reason);
14720 this.set(bindTo, false);
14721 if (busy) {
14722 this.set(busy, false);
14723 }
14724 }.bind(this));
14725 },
14726
14686 _not: function(a) { 14727 _not: function(a) {
14687 return !a; 14728 return !a;
14688 }, 14729 },
14689 14730
14690 _or: function() { 14731 _or: function() {
14691 var result = false; 14732 var result = false;
14692 // can't use .foreach, as arguments isn't really an Array. 14733 // can't use .foreach, as arguments isn't really an Array.
14693 for (var i = 0; i < arguments.length; i++) { 14734 for (var i = 0; i < arguments.length; i++) {
14694 result = result || arguments[i]; 14735 result = result || arguments[i];
14695 } 14736 }
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
14850 _stripSpecial: function(){ 14891 _stripSpecial: function(){
14851 return this._columns.filter(function(c) { 14892 return this._columns.filter(function(c) {
14852 return this._specialColumns.indexOf(c) === -1; 14893 return this._specialColumns.indexOf(c) === -1;
14853 }.bind(this)).sort(); 14894 }.bind(this)).sort();
14854 }, 14895 },
14855 14896
14856 // Common columns shared between tasklist and botlist 14897 // Common columns shared between tasklist and botlist
14857 _commonColumns: function() { 14898 _commonColumns: function() {
14858 // return a fresh object so all elements have their own copy 14899 // return a fresh object so all elements have their own copy
14859 return { 14900 return {
14860 android_devices: function(bot) {
14861 var devs = this._attribute(bot, "android_devices", "0");
14862 if (this._verbose) {
14863 return devs.join(" | ") + " devices available";
14864 }
14865 // max() works on strings as long as they can be coerced to Number.
14866 return Math.max(...devs) + " devices available";
14867 },
14868 device_type: function(bot) { 14901 device_type: function(bot) {
14869 var dt = this._attribute(bot, "device_type", "none"); 14902 var dt = this._attribute(bot, "device_type", "none");
14870 dt = dt[0]; 14903 dt = dt[0];
14871 var alias = swarming.alias.android(dt); 14904 var alias = swarming.alias.android(dt);
14872 if (alias === "unknown") { 14905 if (alias === "unknown") {
14873 return dt; 14906 return dt;
14874 } 14907 }
14875 return swarming.alias.apply(dt, alias); 14908 return swarming.alias.apply(dt, alias);
14876 }, 14909 },
14877 gpu: function(bot){ 14910 gpu: function(bot){
(...skipping 9248 matching lines...) Expand 10 before | Expand all | Expand 10 after
24126 "device_type": "Device Type", 24159 "device_type": "Device Type",
24127 "disk_space": "Free Space (MB)", 24160 "disk_space": "Free Space (MB)",
24128 "gpu": "GPU", 24161 "gpu": "GPU",
24129 "os": "OS", 24162 "os": "OS",
24130 "pool": "Pool", 24163 "pool": "Pool",
24131 "status": "Status", 24164 "status": "Status",
24132 "xcode_version": "XCode Version", 24165 "xcode_version": "XCode Version",
24133 }; 24166 };
24134 24167
24135 var columnMap = { 24168 var columnMap = {
24169 android_devices: function(bot) {
24170 var devs = this._attribute(bot, "android_devices", "0");
24171 if (this._verbose) {
24172 return devs.join(" | ") + " devices available";
24173 }
24174 // max() works on strings as long as they can be coerced to Number.
24175 return Math.max(...devs) + " devices available";
24176 },
24136 disk_space: function(bot) { 24177 disk_space: function(bot) {
24137 var aliased = []; 24178 var aliased = [];
24138 bot.disks.forEach(function(disk){ 24179 bot.disks.forEach(function(disk){
24139 var alias = sk.human.bytes(disk.mb, swarming.MB); 24180 var alias = sk.human.bytes(disk.mb, swarming.MB);
24140 aliased.push(swarming.alias.apply(disk.mb, disk.id + " "+ alias)); 24181 aliased.push(swarming.alias.apply(disk.mb, disk.id + " "+ alias));
24141 }.bind(this)); 24182 }.bind(this));
24142 if (this._verbose) { 24183 if (this._verbose) {
24143 return aliased.join(" | "); 24184 return aliased.join(" | ");
24144 } 24185 }
24145 return aliased[0]; 24186 return aliased[0];
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
24291 _taskLink: function(data) { 24332 _taskLink: function(data) {
24292 if (data && data.task_id) { 24333 if (data && data.task_id) {
24293 return "/user/task/" + data.task_id; 24334 return "/user/task/" + data.task_id;
24294 } 24335 }
24295 return undefined; 24336 return undefined;
24296 } 24337 }
24297 24338
24298 }); 24339 });
24299 })(); 24340 })();
24300 </script> 24341 </script>
24301 </dom-module><dom-module id="task-filters" assetpath="/res/imp/tasklist/"> 24342 </dom-module><script>
24343 /** @polymerBehavior Polymer.PaperButtonBehavior */
24344 Polymer.PaperButtonBehaviorImpl = {
24345 properties: {
24346 /**
24347 * The z-depth of this element, from 0-5. Setting to 0 will remove the
24348 * shadow, and each increasing number greater than 0 will be "deeper"
24349 * than the last.
24350 *
24351 * @attribute elevation
24352 * @type number
24353 * @default 1
24354 */
24355 elevation: {
24356 type: Number,
24357 reflectToAttribute: true,
24358 readOnly: true
24359 }
24360 },
24361
24362 observers: [
24363 '_calculateElevation(focused, disabled, active, pressed, receivedFocusFrom Keyboard)',
24364 '_computeKeyboardClass(receivedFocusFromKeyboard)'
24365 ],
24366
24367 hostAttributes: {
24368 role: 'button',
24369 tabindex: '0',
24370 animated: true
24371 },
24372
24373 _calculateElevation: function() {
24374 var e = 1;
24375 if (this.disabled) {
24376 e = 0;
24377 } else if (this.active || this.pressed) {
24378 e = 4;
24379 } else if (this.receivedFocusFromKeyboard) {
24380 e = 3;
24381 }
24382 this._setElevation(e);
24383 },
24384
24385 _computeKeyboardClass: function(receivedFocusFromKeyboard) {
24386 this.toggleClass('keyboard-focus', receivedFocusFromKeyboard);
24387 },
24388
24389 /**
24390 * In addition to `IronButtonState` behavior, when space key goes down,
24391 * create a ripple down effect.
24392 *
24393 * @param {!KeyboardEvent} event .
24394 */
24395 _spaceKeyDownHandler: function(event) {
24396 Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event);
24397 // Ensure that there is at most one ripple when the space key is held down .
24398 if (this.hasRipple() && this.getRipple().ripples.length < 1) {
24399 this._ripple.uiDownAction();
24400 }
24401 },
24402
24403 /**
24404 * In addition to `IronButtonState` behavior, when space key goes up,
24405 * create a ripple up effect.
24406 *
24407 * @param {!KeyboardEvent} event .
24408 */
24409 _spaceKeyUpHandler: function(event) {
24410 Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event);
24411 if (this.hasRipple()) {
24412 this._ripple.uiUpAction();
24413 }
24414 }
24415 };
24416
24417 /** @polymerBehavior */
24418 Polymer.PaperButtonBehavior = [
24419 Polymer.IronButtonState,
24420 Polymer.IronControlState,
24421 Polymer.PaperRippleBehavior,
24422 Polymer.PaperButtonBehaviorImpl
24423 ];
24424 </script>
24425 <style is="custom-style">
24426
24427 :root {
24428
24429 --shadow-transition: {
24430 transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
24431 };
24432
24433 --shadow-none: {
24434 box-shadow: none;
24435 };
24436
24437 /* from http://codepen.io/shyndman/pen/c5394ddf2e8b2a5c9185904b57421cdb */
24438
24439 --shadow-elevation-2dp: {
24440 box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
24441 0 1px 5px 0 rgba(0, 0, 0, 0.12),
24442 0 3px 1px -2px rgba(0, 0, 0, 0.2);
24443 };
24444
24445 --shadow-elevation-3dp: {
24446 box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
24447 0 1px 8px 0 rgba(0, 0, 0, 0.12),
24448 0 3px 3px -2px rgba(0, 0, 0, 0.4);
24449 };
24450
24451 --shadow-elevation-4dp: {
24452 box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
24453 0 1px 10px 0 rgba(0, 0, 0, 0.12),
24454 0 2px 4px -1px rgba(0, 0, 0, 0.4);
24455 };
24456
24457 --shadow-elevation-6dp: {
24458 box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14),
24459 0 1px 18px 0 rgba(0, 0, 0, 0.12),
24460 0 3px 5px -1px rgba(0, 0, 0, 0.4);
24461 };
24462
24463 --shadow-elevation-8dp: {
24464 box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
24465 0 3px 14px 2px rgba(0, 0, 0, 0.12),
24466 0 5px 5px -3px rgba(0, 0, 0, 0.4);
24467 };
24468
24469 --shadow-elevation-12dp: {
24470 box-shadow: 0 12px 16px 1px rgba(0, 0, 0, 0.14),
24471 0 4px 22px 3px rgba(0, 0, 0, 0.12),
24472 0 6px 7px -4px rgba(0, 0, 0, 0.4);
24473 };
24474
24475 --shadow-elevation-16dp: {
24476 box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14),
24477 0 6px 30px 5px rgba(0, 0, 0, 0.12),
24478 0 8px 10px -5px rgba(0, 0, 0, 0.4);
24479 };
24480
24481 }
24482
24483 </style>
24484 <dom-module id="paper-material-shared-styles" assetpath="/res/imp/bower_componen ts/paper-material/">
24302 <template> 24485 <template>
24303 <style> 24486 <style>
24304 :host { 24487 :host {
24305 display: block; 24488 display: block;
24489 position: relative;
24490 }
24491
24492 :host([elevation="1"]) {
24493 @apply(--shadow-elevation-2dp);
24494 }
24495
24496 :host([elevation="2"]) {
24497 @apply(--shadow-elevation-4dp);
24498 }
24499
24500 :host([elevation="3"]) {
24501 @apply(--shadow-elevation-6dp);
24502 }
24503
24504 :host([elevation="4"]) {
24505 @apply(--shadow-elevation-8dp);
24506 }
24507
24508 :host([elevation="5"]) {
24509 @apply(--shadow-elevation-16dp);
24306 } 24510 }
24307 </style> 24511 </style>
24512 </template>
24513 </dom-module>
24514
24515
24516 <dom-module id="paper-material" assetpath="/res/imp/bower_components/paper-mater ial/">
24517 <template>
24518 <style include="paper-material-shared-styles"></style>
24519 <style>
24520 :host([animated]) {
24521 @apply(--shadow-transition);
24522 }
24523 </style>
24524
24525 <content></content>
24526 </template>
24527 </dom-module>
24528 <script>
24529 Polymer({
24530 is: 'paper-material',
24531
24532 properties: {
24533 /**
24534 * The z-depth of this element, from 0-5. Setting to 0 will remove the
24535 * shadow, and each increasing number greater than 0 will be "deeper"
24536 * than the last.
24537 *
24538 * @attribute elevation
24539 * @type number
24540 * @default 1
24541 */
24542 elevation: {
24543 type: Number,
24544 reflectToAttribute: true,
24545 value: 1
24546 },
24547
24548 /**
24549 * Set this to true to animate the shadow when setting a new
24550 * `elevation` value.
24551 *
24552 * @attribute animated
24553 * @type boolean
24554 * @default false
24555 */
24556 animated: {
24557 type: Boolean,
24558 reflectToAttribute: true,
24559 value: false
24560 }
24561 }
24562 });
24563 </script>
24564
24565
24566 <dom-module id="paper-button" assetpath="/res/imp/bower_components/paper-button/ ">
24567 <template strip-whitespace="">
24568 <style include="paper-material">
24569 :host {
24570 @apply(--layout-inline);
24571 @apply(--layout-center-center);
24572 position: relative;
24573 box-sizing: border-box;
24574 min-width: 5.14em;
24575 margin: 0 0.29em;
24576 background: transparent;
24577 -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
24578 -webkit-tap-highlight-color: transparent;
24579 font: inherit;
24580 text-transform: uppercase;
24581 outline-width: 0;
24582 border-radius: 3px;
24583 -moz-user-select: none;
24584 -ms-user-select: none;
24585 -webkit-user-select: none;
24586 user-select: none;
24587 cursor: pointer;
24588 z-index: 0;
24589 padding: 0.7em 0.57em;
24590
24591 @apply(--paper-font-common-base);
24592 @apply(--paper-button);
24593 }
24594
24595 :host([raised].keyboard-focus) {
24596 font-weight: bold;
24597 @apply(--paper-button-raised-keyboard-focus);
24598 }
24599
24600 :host(:not([raised]).keyboard-focus) {
24601 font-weight: bold;
24602 @apply(--paper-button-flat-keyboard-focus);
24603 }
24604
24605 :host([disabled]) {
24606 background: #eaeaea;
24607 color: #a8a8a8;
24608 cursor: auto;
24609 pointer-events: none;
24610
24611 @apply(--paper-button-disabled);
24612 }
24613
24614 paper-ripple {
24615 color: var(--paper-button-ink-color);
24616 }
24617 </style>
24618
24619 <content></content>
24620 </template>
24621
24622 <script>
24623 Polymer({
24624 is: 'paper-button',
24625
24626 behaviors: [
24627 Polymer.PaperButtonBehavior
24628 ],
24629
24630 properties: {
24631 /**
24632 * If true, the button should be styled with a shadow.
24633 */
24634 raised: {
24635 type: Boolean,
24636 reflectToAttribute: true,
24637 value: false,
24638 observer: '_calculateElevation'
24639 }
24640 },
24641
24642 _calculateElevation: function() {
24643 if (!this.raised) {
24644 this._setElevation(0);
24645 } else {
24646 Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this);
24647 }
24648 }
24649
24650 /**
24651 Fired when the animation finishes.
24652 This is useful if you want to wait until
24653 the ripple animation finishes to perform some action.
24654
24655 @event transitionend
24656 Event param: {{node: Object}} detail Contains the animated node.
24657 */
24658 });
24659 </script>
24660 </dom-module>
24661 <dom-module id="task-filters" assetpath="/res/imp/tasklist/">
24662 <template>
24663 <style is="custom-style" include="iron-flex iron-flex-alignment iron-positio ning query-column-filter-style">
24664
24665 </style>
24666
24667 <url-param name="filters" value="{{_filters}}" default_values="[]" multi="">
24668 </url-param>
24669 <url-param name="columns" value="{{columns}}" default_values="[&quot;name&qu ot;,&quot;state&quot;,&quot;created_ts&quot;,&quot;user&quot;]" multi="">
24670 </url-param>
24671 <url-param name="query" value="{{_query}}" default_value="">
24672 </url-param>
24673 <url-param name="limit" default_value="200" value="{{limit}}">
24674 </url-param>
24675
24676 <div class="container horizontal layout">
24677
24678
24679 <div class="narrow-down-selector">
24680 <div>
24681 <paper-input id="filter" label="Search columns and filters" placeholde r="gpu nvidia" value="{{_query}}">
24682 </paper-input>
24683 </div>
24684
24685 <div class="selector side-by-side" title="This shows all task tags and o ther interesting task properties. Mark the check box to add as a column. Select the row to see filter options.">
24686 <iron-selector attr-for-selected="label" selected="{{_primarySelected} }">
24687 <template is="dom-repeat" items="[[_primaryItems]]" as="item">
24688 <div class="selectable item horizontal layout" label="[[item]]">
24689
24690 <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(ite m,_query)]]</span>[[_afterBold(item,_query)]]</span>
24691 <span class="flex"></span>
24692 <paper-checkbox noink="" disabled$="[[_cantToggleColumn(item)]]" checked="[[_columnState(item,columns.*)]]" on-change="_toggleColumn">
24693 </paper-checkbox>
24694 </div>
24695 </template>
24696 </iron-selector>
24697 </div>
24698
24699 <div class="selector side-by-side" title="These are all options (if any) that the task list can be filtered on.">
24700 <template is="dom-repeat" id="secondaryList" items="[[_secondaryItems] ]" as="item">
24701 <div class="item horizontal layout" label="[[item]]">
24702
24703 <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(item, _query)]]</span>[[_afterBold(item,_query)]]</span>
24704 <span class="flex"></span>
24705 <iron-icon class="icons" icon="icons:arrow-forward" hidden="[[_can tAddFilter(_primarySelected,item,_filters.*)]]" on-tap="_addFilter">
24706 </iron-icon>
24707 </div>
24708 </template>
24709 </div>
24710
24711 <div class="selector side-by-side" title="These tag filters are AND'd to gether and applied to all tasks.">
24712 <template is="dom-repeat" items="[[_filters]]" as="fil">
24713 <div class="item horizontal layout" label="[[fil]]">
24714 <span>[[fil]]</span>
24715 <span class="flex"></span>
24716 <iron-icon class="icons" icon="icons:remove-circle-outline" hidden ="[[_cantRemoveFilter(fil,_filters.*)]]" on-tap="_removeFilter">
24717 </iron-icon>
24718 </div>
24719 </template>
24720 </div>
24721
24722 <div class="side-by-side">
24723 <paper-input id="limit" label="Limit Results" auto-validate="" min="0" max="1000" pattern="[0-9]+" value="{{limit}}">
24724 </paper-input>
24725 </div>
24726 </div>
24727
24728 </div>
24308 24729
24309 </template> 24730 </template>
24310 <script> 24731 <script>
24311 (function(){ 24732 (function(){
24733 // see query-column-filter for more documentation on these properties.
24734 var filterMap = {
24735 state: function(task, s) {
24736 var state = this._attribute(task, "state")[0];
24737 if (s === state) {
24738 return true;
24739 }
24740 if (s === "PENDING_RUNNING") {
24741 return state === "PENDING" || state === "RUNNING";
24742 }
24743 var failure = this._attribute(task, "failure", false)[0];
24744 if (s === "COMPLETED_SUCCESS") {
24745 return state === "COMPLETED" && !failure;
24746 }
24747 if (s === "COMPLETED_FAILURE") {
24748 return state === "COMPLETED" && failure;
24749 }
24750 var tryNum = this._attribute(task, "try_number", "-1")[0];
24751 if (s === "DEDUPED") {
24752 return state === "COMPLETED" && tryNum === "0";
24753 }
24754
24755 },
24756
24757 };
24312 Polymer({ 24758 Polymer({
24313 is: 'task-filters', 24759 is: 'task-filters',
24314 24760
24761 behaviors: [SwarmingBehaviors.QueryColumnFilter],
24762
24315 properties: { 24763 properties: {
24316 // output 24764 // output
24317 columns: { 24765 columns: {
24318 type: Array, 24766 type: Array,
24319 value: function() {
24320 return ["name", "state", "user"];
24321 },
24322 notify: true,
24323 },
24324 filter: {
24325 type: Function,
24326 value: function() {
24327 return function(){
24328 return true;
24329 };
24330 },
24331 notify: true, 24767 notify: true,
24332 }, 24768 },
24333 query_params: { 24769 query_params: {
24334 type: Object, 24770 type: Object,
24771 computed: "_extractQueryParams(_filters.*, limit)",
24335 notify: true, 24772 notify: true,
24336 }, 24773 },
24337 verbose: { 24774
24338 type: Boolean, 24775 // for QueryColumnFilter
24339 value: true, 24776 _filterMap: {
24340 notify: true, 24777 type: Object,
24341 }, 24778 value: function() {
24342 } 24779 var base = this._commonFilters();
24780 for (var attr in filterMap) {
24781 base[attr] = filterMap[attr];
24782 }
24783 return base;
24784 },
24785 }
24786 },
24787
24788 _cantToggleColumn: function(col) {
24789 // Don't allow the name column to be removed, as the task list is
24790 // basically meaningless without it.
24791 return !col || col === "name" ;
24792 },
24793
24794 _extractQueryParams: function() {
24795 var params = {};
24796 var tags = [];
24797 this._filters.forEach(function(f) {
24798 var split = f.split(this.FILTER_SEP, 1)
24799 var col = split[0];
24800 var rest = f.substring(col.length + this.FILTER_SEP.length);
24801 if (col === "state") {
24802 params["state"] = [rest];
24803 } else {
24804 tags.push(col + this.FILTER_SEP + swarming.alias.unapply(rest))
24805 }
24806 }.bind(this));
24807 params["tags"] = tags;
24808 var lim = Math.floor(this.limit)
24809 if (Number.isInteger(lim)) {
24810 // Clamp the limit
24811 lim = Math.max(lim, 1);
24812 lim = Math.min(1000, lim);
24813 params["limit"] = [lim];
24814 // not !== because limit could be the string "900"
24815 if (this.limit != lim) {
24816 this.set("limit", lim);
24817 }
24818 }
24819 return params;
24820 }
24821
24343 }); 24822 });
24344 })(); 24823 })();
24345 </script> 24824 </script>
24346 </dom-module><dom-module id="task-list-data" assetpath="/res/imp/tasklist/"> 24825 </dom-module><dom-module id="task-list-data" assetpath="/res/imp/tasklist/">
24347 <template>
24348 <iron-ajax id="tasklist" url="/_ah/api/swarming/v1/tasks/list" headers="[[au th_headers]]" params="[[query_params]]" handle-as="json" last-response="{{_list} }" loading="{{_busy1}}">
24349 </iron-ajax>
24350
24351
24352 </template>
24353 <script> 24826 <script>
24354 (function(){ 24827 (function(){
24355 Polymer({ 24828 Polymer({
24356 is: 'task-list-data', 24829 is: 'task-list-data',
24357 24830
24358 behaviors: [SwarmingBehaviors.CommonBehavior], 24831 behaviors: [
24832 SwarmingBehaviors.CommonBehavior,
24833 ],
24359 24834
24360 properties: { 24835 properties: {
24361 // inputs 24836 // inputs
24362 auth_headers: { 24837 auth_headers: {
24363 type: Object, 24838 type: Object,
24364 observer: "signIn", 24839 observer: "signIn",
24365 }, 24840 },
24366 query_params: { 24841 query_params: {
24367 type: Object, 24842 type: Object,
24843 observer: "_request",
24368 }, 24844 },
24369 24845
24370 //outputs 24846 // outputs
24371 busy: { 24847 busy: {
24372 type: Boolean, 24848 type: Boolean,
24373 computed: "_or(_busy1)", 24849 computed: "_or(_busy1,_busy2,_busy3)",
24850 notify: true,
24851 },
24852 primary_map: {
24853 type: Object,
24854 computed: "_primaryMap(_tags,_dimensions,tasks)",
24855 notify: true,
24856 },
24857 primary_arr: {
24858 type: Array,
24859 computed: "_primaryArr(primary_map)",
24374 notify: true, 24860 notify: true,
24375 }, 24861 },
24376 tasks: { 24862 tasks: {
24377 type: Array, 24863 type: Array,
24378 computed: "_tasks(_list)", 24864 computed: "_tasks(_list)",
24379 notify: true, 24865 notify: true,
24866 },
24867
24868 // private
24869 _busy1: {
24870 type: Boolean,
24871 value: false
24872 },
24873 _busy2: {
24874 type: Boolean,
24875 value: false
24876 },
24877 _busy3: {
24878 type: Boolean,
24879 value: false
24880 },
24881 _dimensions: {
24882 type: Object,
24883 },
24884 _list: {
24885 type: Object,
24886 },
24887 _tags: {
24888 type: Object,
24889 },
24890 },
24891
24892 signIn: function(){
24893 this._getJsonAsync("_tags", "/_ah/api/swarming/v1/tasks/tags",
24894 "_busy2", this.auth_headers);
24895 this._getJsonAsync("_dimensions","/_ah/api/swarming/v1/bots/dimensions",
24896 "_busy3", this.auth_headers);
24897
24898 this._request();
24899 },
24900
24901 _primaryArr: function(map) {
24902 var arr = Object.keys(map);
24903 arr.sort();
24904 return arr;
24905 },
24906
24907 _primaryMap: function(tags, dims, tasks) {
24908 tags = (tags && tags.tasks_tags) || [];
24909 dims = (dims && dims.bots_dimensions) || [];
24910 tasks = tasks || [];
24911 var map = {};
24912 // We combine all the tags reported by the tags endpoint, all known
24913 // dimensions from the dimensions endpoint, and the tags seen in the
24914 // returned tasks, just in case they didn't show up in the first two.
24915 // This way a user can filter by what the data actually has and can
24916 // discover new tags to filter by.
24917 tags.forEach(function(t) {
24918 if (!map[t.key]) {
24919 map[t.key] = {};
24920 }
24921 var values = t.value || [];
24922 values.forEach(function(v) {
24923 map[t.key][v] = true;
24924 })
24925 });
24926
24927 dims.forEach(function(d) {
24928 var vals = d.value;
24929 if (!map[d.key]) {
24930 map[d.key] = {};
24931 }
24932 vals.forEach(function(v) {
24933 map[d.key][v] = true;
24934 })
24935 });
24936
24937 tasks.forEach(function(t) {
24938 Object.keys(t.tagMap).forEach(function(k) {
24939 var v = t.tagMap[k];
24940 if (!map[k]) {
24941 map[k] = {};
24942 }
24943 map[k][v] = true;
24944 });
24945 });
24946
24947 // Turn the Map<Object,Map<Boolean>> into a Map<Object,Array<String>>
24948 // with all of the aliases applied.
24949 var pMap = {};
24950 for (key in map) {
24951 var values = Object.keys(map[key]);
24952 if (swarming.alias.DIMENSIONS_WITH_ALIASES.indexOf(key) === -1) {
24953 pMap[key] = values;
24954 } else if (key === "gpu") {
24955 var gpus = [];
24956 values.forEach(function(g){
24957 var alias = swarming.alias.gpu(g);
24958 if (alias !== "unknown") {
24959 gpus.push(swarming.alias.apply(g, alias));
24960 } else {
24961 gpus.push(g);
24962 }
24963 }.bind(this));
24964 pMap["gpu"] = gpus;
24965 } else if (key === "device_type") {
24966 var devs = [];
24967 values.forEach(function(dt){
24968 var alias = swarming.alias.android(dt);
24969 if (alias !== "unknown") {
24970 devs.push(swarming.alias.apply(dt, alias));
24971 } else {
24972 devs.push(dt);
24973 }
24974 }.bind(this));
24975 pMap["device_type"] = devs;
24976 } else {
24977 console.log("Unknown alias type: ", d);
24978 }
24380 } 24979 }
24980
24981 // Add some options that might not show up.
24982 pMap["android_devices"].push("0");
24983 pMap["device_os"].push("none");
24984 pMap["device_type"].push("none");
24985 pMap["user"].push("none");
24986
24987 // Custom filter options
24988 pMap["name"] = [];
24989 pMap["state"] = ["PENDING", "RUNNING", "PENDING_RUNNING", "COMPLETED",
24990 "COMPLETED_SUCCESS","COMPLETED_FAILURE", "EXPIRED", "TIMED_OUT",
24991 "BOT_DIED", "CANCELED", "ALL", "DEDUPED"];
24992 pMap["abandoned_ts"] = [];
24993 pMap["completed_ts"] = [];
24994 pMap["costs_usd"] = [];
24995 pMap["created_ts"] = [];
24996 pMap["deduped_from"] = [];
24997 pMap["duration"] = [];
24998 pMap["modified_ts"] = [];
24999 pMap["server_versions"] = [];
25000 pMap["started_ts"] = [];
25001
25002 return pMap;
24381 }, 25003 },
24382 signIn: function(){ 25004
24383 // Auto on iron-ajax means to automatically re-make the request if 25005 _request: function() {
24384 // the url or the query params change. Auto does not trigger if the 25006 // wait until the user has logged in before requesting this.
24385 // [auth] headers change, so we wait until the user is signed in 25007 if (!this.auth_headers) {
24386 // before making any requests. 25008 return;
24387 this.$.tasklist.auto = true; 25009 }
25010 this._getJsonAsync("_list", "/_ah/api/swarming/v1/tasks/list",
25011 "_busy1", this.auth_headers, this.query_params);
24388 }, 25012 },
24389 25013
24390 _tasks: function() { 25014 _tasks: function() {
24391 if (!this._list || !this._list.items) { 25015 if (!this._list || !this._list.items) {
24392 return []; 25016 return [];
24393 } 25017 }
24394 // Do any preprocessing here 25018 // Do any preprocessing here
25019 this._list.items.forEach(function(t) {
25020 var tagMap = {};
25021 t.tags.forEach(function(tag) {
25022 var split = tag.split(":", 1)
25023 var key = split[0];
25024 var rest = tag.substring(key.length + 1);
25025 tagMap[key] = rest;
25026 });
25027 t.tagMap = tagMap;
25028 });
24395 return this._list.items; 25029 return this._list.items;
24396 } 25030 }
24397 }); 25031 });
24398 })(); 25032 })();
24399 </script> 25033 </script>
24400 </dom-module> 25034 </dom-module>
24401 <dom-module id="task-list" assetpath="/res/imp/tasklist/"> 25035 <dom-module id="task-list" assetpath="/res/imp/tasklist/">
24402 <template> 25036 <template>
24403 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style dynamic-table-style"> 25037 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style dynamic-table-style">
24404 25038 task-filters {
25039 margin-bottom: 8px;
25040 margin-right: 10px;
25041 }
24405 .task-list th > span { 25042 .task-list th > span {
24406 /* Leave space for sort-toggle*/ 25043 /* Leave space for sort-toggle*/
24407 padding-right: 30px; 25044 padding-right: 30px;
24408 } 25045 }
25046
25047 /* These colors are from buildbot */
25048 .failed {
25049 background-color: #ffdddd;
25050 }
25051 .died {
25052 background-color: #cccccc;
25053 }
25054 .exception {
25055 background-color: #edd2ff;
25056 }
25057 .pending {
25058 background-color: #fffc6c;
25059 }
24409 </style> 25060 </style>
24410 25061
24411 <url-param name="sort" value="{{_sortstr}}" default_value="id:asc"> 25062 <url-param name="sort" value="{{_sortstr}}" default_value="id:asc">
24412 </url-param> 25063 </url-param>
24413 25064
24414 <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" sig ned_in="{{_signed_in}}" busy="[[_busy]]" name="Swarming Task List"> 25065 <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" per missions="{{_permissions}}" signed_in="{{_signed_in}}" busy="[[_busy]]" name="Sw arming Task List">
24415 25066
24416 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> 25067 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2>
24417 25068
24418 <div hidden$="[[_not(_signed_in)]]"> 25069 <div hidden$="[[_not(_signed_in)]]">
24419 <task-list-data auth_headers="[[_auth_headers]]" query_params="[[_query_ params]]" tasks="{{_items}}" busy="{{_busy}}"> 25070 <task-list-data auth_headers="[[_auth_headers]]" query_params="[[_query_ params]]" tasks="{{_items}}" busy="{{_busy}}" primary_map="{{_primary_map}}" pri mary_arr="{{_primary_arr}}">
24420 </task-list-data> 25071 </task-list-data>
24421 25072
25073 <paper-toast id="toast"></paper-toast>
25074
24422 <div class="horizontal layout"> 25075 <div class="horizontal layout">
24423 25076
24424 <task-filters columns="{{_columns}}" query_params="{{_query_params}}" filter="{{_filter}}" verbose="{{_verbose}}"> 25077 <task-filters primary_map="[[_primary_map]]" primary_arr="[[_primary_a rr]]" columns="{{_columns}}" query_params="{{_query_params}}" filter="{{_filter} }">
24425 </task-filters> 25078 </task-filters>
24426 25079
24427 </div> 25080 </div>
24428 25081
24429 <table class="task-list"> 25082 <table class="task-list">
24430 <thead on-sort_change="_sortChange"> 25083 <thead on-sort_change="_sortChange">
24431 25084
24432 <tr> 25085 <tr>
24433 <th> 25086 <th>
24434 <span>Task Name</span> 25087 <span>Task Name</span>
24435 <sort-toggle name="name" current="[[_sort]]"> 25088 <sort-toggle name="name" current="[[_sort]]">
24436 </sort-toggle> 25089 </sort-toggle>
24437 </th> 25090 </th>
24438 25091
24439 <th hidden$="[[_hide('state', _columns.*)]]"> 25092 <th hidden$="[[_hide('state', _columns.*)]]">
24440 <span>Status</span> 25093 <span>State</span>
24441 <sort-toggle name="state" current="[[_sort]]"> 25094 <sort-toggle name="state" current="[[_sort]]">
24442 </sort-toggle> 25095 </sort-toggle>
24443 </th> 25096 </th>
24444 25097
25098 <th hidden$="[[_hide('deduped_from', _columns.*)]]">
25099 <span>Deduped from</span>
25100 <sort-toggle name="deduped_from" current="[[_sort]]">
25101 </sort-toggle>
25102 </th>
25103
24445 <template is="dom-repeat" items="[[_plainColumns]]" as="c"> 25104 <template is="dom-repeat" items="[[_plainColumns]]" as="c">
24446 <th hidden$="[[_hide(c)]]"> 25105 <th hidden$="[[_hide(c)]]">
24447 <span>[[_header(c)]]</span> 25106 <span>[[_header(c)]]</span>
24448 <sort-toggle name="[[c]]" current="[[_sort]]"> 25107 <sort-toggle name="[[c]]" current="[[_sort]]">
24449 </sort-toggle> 25108 </sort-toggle>
24450 </th> 25109 </th>
24451 </template> 25110 </template>
24452 </tr> 25111 </tr>
24453 </thead> 25112 </thead>
24454 <tbody> 25113 <tbody>
24455 <template id="tasks_table" is="dom-repeat" items="[[_filteredSortedI tems]]" as="task" initial-count="50"> 25114 <template id="tasks_table" is="dom-repeat" items="[[_filteredSortedI tems]]" as="task" initial-count="50">
24456 25115
24457 <tr class$="[[_taskClass(task)]]"> 25116 <tr class$="[[_taskClass(task)]]">
24458 <td> 25117 <td>
24459 <a class="center" href$="[[_taskLink(task)]]" target="_blank"> 25118 <a class="center" href$="[[_taskLink(task.task_id)]]" target=" _blank">
24460 [[task.name]] 25119 [[task.name]]
24461 </a> 25120 </a>
24462 </td> 25121 </td>
24463 <td hidden$="[[_hide('state', _columns.*)]]"> 25122 <td hidden$="[[_hide('state', _columns.*)]]">
24464 [[_column('state', task, _verbose)]] 25123 [[_column('state', task)]]
24465 25124 <paper-button raised="" hidden$="[[_cannotCancel(task,_permiss ions)]]" on-tap="_cancelTask">
25125 Cancel
25126 </paper-button>
25127 </td>
25128 <td hidden$="[[_hide('deduped_from', _columns.*)]]">
25129 <a class="center" href$="[[_taskLink(task.deduped_from)]]" tar get="_blank">
25130 [[_column('deduped_from',task)]]
25131 </a>
24466 </td> 25132 </td>
24467 25133
24468 <template is="dom-repeat" items="[[_plainColumns]]" as="c"> 25134 <template is="dom-repeat" items="[[_plainColumns]]" as="c">
24469 <td hidden$="[[_hide(c)]]"> 25135 <td hidden$="[[_hide(c)]]">
24470 [[_column(c, task, _verbose)]] 25136 [[_column(c, task)]]
24471 </td> 25137 </td>
24472 </template> 25138 </template>
24473 25139
24474 </tr> 25140 </tr>
24475 </template> 25141 </template>
24476 </tbody> 25142 </tbody>
24477 </table> 25143 </table>
24478 </div> 25144 </div>
24479 25145
24480 </swarming-app> 25146 </swarming-app>
24481 25147
24482 </template> 25148 </template>
24483 <script> 25149 <script>
24484 (function(){ 25150 (function(){
24485 var specialColumns = ["name", "state"]; 25151 var specialColumns = ["deduped_from", "name", "state"];
24486 var columnMap = {}; 25152 var columnMap = {
25153 costs_usd: function(task) {
25154 return this._attribute(task, "costs_usd", 0)[0];
25155 },
25156 state: function(task) {
25157 var state = this._attribute(task, "state")[0];
25158 if (state === "COMPLETED") {
25159
25160 if (this._attribute(task, "failure", false)[0]) {
25161 return "COMPLETED (FAILURE)";
25162 }
25163 var tryNum = this._attribute(task, "try_number", "-1")[0];
25164 if (tryNum === "0") {
25165 return "COMPLETED (DEDUPED)";
25166 }
25167 return "COMPLETED (SUCCESS)";
25168 }
25169 return state;
25170 },
25171 };
24487 var headerMap = { 25172 var headerMap = {
24488 "user": "Requesting User", 25173 "user": "Requesting User",
24489 }; 25174 };
24490 var specialSort = {}; 25175 var specialSort = {};
24491 25176
24492 Polymer({ 25177 Polymer({
24493 is: 'task-list', 25178 is: 'task-list',
24494 behaviors: [ 25179 behaviors: [
24495 SwarmingBehaviors.DynamicTableBehavior, 25180 SwarmingBehaviors.DynamicTableBehavior,
24496 ], 25181 ],
24497 25182
24498 properties: { 25183 properties: {
24499 client_id: { 25184 client_id: {
24500 type: String, 25185 type: String,
24501 }, 25186 },
24502 25187
24503 // For dynamic table. 25188 // For dynamic table.
24504 _columnMap: { 25189 _columnMap: {
24505 type: Object, 25190 type: Object,
24506 value: columnMap, 25191 value: function() {
25192 var base = this._commonColumns();
25193 for (var attr in columnMap) {
25194 base[attr] = columnMap[attr];
25195 }
25196 return base;
25197 },
24507 }, 25198 },
24508 _headerMap: { 25199 _headerMap: {
24509 type: Object, 25200 type: Object,
24510 value: headerMap, 25201 value: headerMap,
24511 }, 25202 },
24512 _specialColumns: { 25203 _specialColumns: {
24513 type: Array, 25204 type: Array,
24514 value: specialColumns, 25205 value: specialColumns,
24515 }, 25206 },
24516 _specialSort: { 25207 _specialSort: {
24517 type: Object, 25208 type: Object,
24518 value: specialSort, 25209 value: specialSort,
24519 }, 25210 },
24520 }, 25211 },
24521 25212
24522 _attribute: function(task, col, def) { 25213 _attribute: function(task, col, def) {
24523 var retVal = task[col] || [def]; 25214 if (def === undefined) {
25215 def = "none";
25216 }
25217 var retVal = this._tag(task, col) || task[col] || [def];
24524 if (!Array.isArray(retVal)) { 25218 if (!Array.isArray(retVal)) {
24525 return [retVal]; 25219 return [retVal];
24526 } 25220 }
24527 return retVal; 25221 return retVal;
24528 }, 25222 },
24529 25223
24530 _taskLink: function(task) { 25224 _cannotCancel: function(task, permissions) {
25225 return !(permissions && permissions.can_cancel_task &&
25226 this._column("state", task) === "PENDING");
25227 },
25228
25229 _cancelTask: function(e) {
25230 var task = e.model.task;
25231 if (!task || !task.task_id) {
25232 console.log("Missing task info", task);
25233 return
25234 }
25235 var id = task.task_id
25236
25237 // Keep toast displayed until we hear back from the cancel.
25238 this.$.toast.duration = 0;
25239 this.$.toast.text="Canceling task " + id;
25240 this.$.toast.open();
25241 var url = "/_ah/api/swarming/v1/task/" + id +"/cancel";
25242 sk.request("POST", url, undefined, this._auth_headers).then(function(res ponse) {
25243 this.$.toast.close();
25244 this.$.toast.show({
25245 text: "Request sent. Response: "+response,
25246 duration: 3000,
25247 });
25248 }.bind(this)).catch(function(reason) {
25249 console.log("Cancellation failed", reason);
25250 this.$.toast.close();
25251 this.$.toast.show({
25252 text: "Request failed. Reason: "+reason,
25253 duration: 3000,
25254 });
25255 }.bind(this));
25256 },
25257
25258 _tag: function(task, col) {
25259 if (!task || !task.tagMap) {
25260 return undefined;
25261 }
25262 return task.tagMap[col];
25263 },
25264
25265 _taskLink: function(taskId) {
25266 if (!taskId) {
25267 return undefined;
25268 }
24531 // TODO(kjlubick) Make this point to /newui/ when appropriate. 25269 // TODO(kjlubick) Make this point to /newui/ when appropriate.
24532 return "/restricted/task/"+task.task_id; 25270 return "/restricted/task/"+taskId;
24533 }, 25271 },
24534 25272
24535 _taskClass: function(task) { 25273 _taskClass: function(task) {
24536 // TODO(kjlubick): Color tasks? 25274 var state = this._column("state", task);
25275 if (state === "CANCELED" ||state === "TIMED_OUT" || state === "EXPIRED") {
25276 return "exception";
25277 }
25278 if (state === "BOT_DIED") {
25279 return "died";
25280 }
25281 if (state === "COMPLETED (FAILURE)") {
25282 return "failed";
25283 }
25284 if (state === "RUNNING" || state === "PENDING") {
25285 return "pending";
25286 }
24537 return ""; 25287 return "";
24538 } 25288 }
24539 25289
24540 }); 25290 });
24541 })(); 25291 })();
24542 </script> 25292 </script>
24543 </dom-module></div></body></html> 25293 </dom-module></div></body></html>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698