blob: 480689de72e94fdae07fbc77948844042bd21fff [file] [log] [blame]
<!-- The <cluster-page-sk> custom element declaration.
Displays the cluster view for a single test.
Attributes:
None
Events:
None
Methods:
pageSelected - Called by the router when view is visible.
pageDeselected - Called by the router when the view is no longer visible.
-->
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="bower_components/paper-button/paper-button.html">
<link rel="import" href="bower_components/iron-pages/iron-pages.html">
<link rel="import" href="bower_components/paper-tabs/paper-tabs.html">
<link rel="import" href="bower_components/paper-tabs/paper-tab.html">
<link rel="import" href="../common/imp/sort.html">
<link rel="import" href="../common/imp/paramset.html">
<link rel="import" href="digest-details-sk.html">
<link rel="import" href="cluster-sk.html">
<link rel="import" href="activity-sk.html">
<link rel="import" href="test-summary-sk.html">
<link rel="import" href="shared-styles.html">
<link rel="import" href="search-controls-sk.html">
<dom-module id="cluster-page-sk">
<template>
<style include="iron-flex iron-flex-alignment"></style>
<style include="shared-styles">
#clusterContainer {
margin-left: 1em;
margin-top: 1em;
}
.searchControlsWrapper {
padding: 1em 0 0 1em;
}
.clusterRendering {
min-width: 500pt;
}
</style>
<div class="vertical layout">
<div class="searchControlsWrapper">
<search-controls-sk id="searchControls"></search-controls-sk>
</div>
<div id="clusterContainer" class="horizontal layout">
<div class="clusterRendering">
<activity-sk id="activityMain"></activity-sk>
<cluster-sk id="clusterView" disabled="{{_zooming}}"></cluster-sk>
</div>
<div>
<paper-tabs id="tabs" selected={{_currentTab}}>
<paper-tab>Parameters</paper-tab>
<paper-tab>Details</paper-tab>
</paper-tabs>
<iron-pages id="pages" selected="{{_currentTab}}">
<div>
<paramset-sk clickable id=parameters></paramset-sk>
</div>
<div>
<div hidden$="{{_lenIsNot(_digestList, 0)}}">
There are no selected digests.
</div>
<div hidden$="{{_lenIsNot(_digestList, 1)}}">
<activity-sk id="activityDetail"></activity-sk>
<digest-details-sk id="oneDigest"
mode="detail"
details="[[_digestDetails.digest]]"
commits="[[_digestDetails.commits]]">
</digest-details-sk>
</div>
<div hidden$="{{_lenIsNot(_digestList, 2)}}">
<activity-sk id="activityDiff"></activity-sk>
<digest-details-sk id="diffTwo"
mode="diff"
details="[[_diffData.left]]"
right="[[_diffData.right]]"
diff="[[_diffData.diff]]">
</digest-details-sk>
</div>
<div hidden$="{{_hideBulkTriage(_digestList)}}">
<h3>Bulk Triage</h3>
<triage-sk value="{{_triageStatus}}"
id="bulkTriage">
</triage-sk>
<h3>Param Set of Selected Digests</h3>
<paramset-sk id="bulkParamset" clickable></paramset-sk>
</div>
</div>
</iron-pages>
</div>
</div>
<zoom-dialog-sk opened="{{_zooming}}"></zoom-dialog-sk>
</template>
<script>
Polymer({
is: "cluster-page-sk",
behaviors: [gold.ZoomTargetBehavior, gold.PageStateBehavior],
properties: {
_currentTab: {
type: Number,
value: 0
},
_digestList: {
type: Array,
value: function() { return []; }
},
_triageStatus: "untriaged"
},
ready: function() {
this.listen(this.$.clusterContainer, 'paramset-key-click', '_handleParamsetKeyClick');
this.listen(this.$.clusterContainer, 'paramset-key-value-click', '_handleParamsetValueClick');
this.listen(this.$.clusterContainer, 'digest-select', '_handleDigestsSelected');
this.listen(this.$.bulkTriage, 'change', '_handleBulkTriageChange');
this.listen(this, 'triage', '_handleTriage');
this.listen(this.$.searchControls, 'state-change', '_handleStateChange');
this._setDefaultState(gold.defaultSearchState, true);
},
pageSelected: function(ctx) {
this.$.clusterView.startUse();
this._loadParamset();
// Initialize the state and set values of the controls.
this._initState(ctx, this._getDefaultStateWithCorpus());
this.$.searchControls.setState(this._state);
this._loadClusterDiff();
},
pageDeselected: function() {
this.$.clusterView.endUse();
},
_loadParamset: function() {
sk.get("/json/paramset").then(JSON.parse).then(function (json) {
this.$.searchControls.setParamSet(json);
}.bind(this)).catch(sk.errorMessage);
},
_loadClusterDiff: function() {
gold.loadWithActivity(this, "/json/clusterdiff" + window.location.search, this.$.activityMain, function(json) {
this._data = json;
this.$.clusterView.setData(json);
this.$.parameters.setParamSets([json.paramsetsUnion]);
}.bind(this));
},
_handleStateChange: function(ev) {
// Reload the page with the new state in the query parameters.
this._redirectToState(ev.detail);
},
_handleParamsetKeyClick: function(ev) {
ev.stopPropagation();
var text = {};
var key = ev.detail.key;
Object.keys(this._data.paramsetByDigest).forEach(function(digest) {
var value = this._data.paramsetByDigest[digest][key];
if (value) {
text[digest] = value.join(", ");
}
}.bind(this));
this.$.clusterView.setTextNodes(text, ev.detail.ctrl);
},
_handleParamsetValueClick: function(ev) {
ev.stopPropagation();
var text = {};
var value = ev.detail.value;
var key = ev.detail.key;
Object.keys(this._data.paramsetByDigest).forEach(function(digest) {
var paramset = this._data.paramsetByDigest[digest];
if (paramset[key] && (paramset[key].indexOf(value) !== -1)) {
text[digest] = value;
}
}.bind(this));
this.$.clusterView.setTextNodes(text, ev.detail.ctrl);
},
_handleBulkTriageChange: function(ev) {
ev.stopPropagation();
var detail = gold.makeTriageQuery(this._data.test, this._digestList, ev.detail);
this.fire('triage', detail);
},
_handleTriage: function(ev) {
ev.stopPropagation();
sk.post('/json/triage', JSON.stringify(ev.detail)).then(function() {
var change = gold.flattenTriageQuery(ev.detail);
var ele = this.$.clusterView;
// 'change' contains triples of (testname, digests, status)
// since the triaging was successful we set the corresponding nodes
// in the cluster view to the new status.
for(var i =0; i < change.length; i++) {
ele.newTriageStatus(change[i][1], change[i][2]);
}
}.bind(this)).catch(sk.errorMessage);
},
_handleDigestsSelected: function(ev) {
ev.stopPropagation();
this.set('_digestList', ev.detail);
// If there are no digests do nothing.
if (this._digestList.length == 0) {
return;
}
// Switch to the digest tab.
this._currentTab = 1;
var url;
if (this._digestList.length == 1) {
// Show detailed information about the one selected digest.
url = "/json/details" + gold.detailQuery(this._data.test, this._digestList[0]);
this.$.oneDigest.clear();
gold.loadWithActivity(this, url, this.$.activityDetail, '_digestDetails');
} else if (this._digestList.length == 2) {
// Show a diff between the two selected digests.
url = "/json/diff" + gold.diffQuery(this._data.test, this._digestList[0], this._digestList[1]);
this.$.diffTwo.clear();
gold.loadWithActivity(this, url, this.$.activityDiff, '_diffData');
} else {
// Show the option to bulk triage all selected digests.
this._setBulkTriage(this._digestList);
}
},
_setBulkTriage: function(selected) {
this.set("_triageStatus", "untriaged");
var paramset = {};
var data = this._data;
selected.forEach(function(digest) {
var p = data.paramsetByDigest[digest];
Object.keys(p).forEach(function(key) {
var existingValues = paramset[key] || [];
var newValues = p[key];
newValues.forEach(function(value) {
if (existingValues.indexOf(value) == -1) {
existingValues.push(value);
}
});
paramset[key] = existingValues;
});
});
// Sort the param values.
Object.keys(paramset).forEach(function(key) {
paramset[key].sort();
});
this.$.bulkParamset.setParamSets([paramset]);
},
_hideBulkTriage: function(digestList) {
return digestList.length <= 2;
},
_lenIsNot: function(digestList, len) {
return digestList.length !== len;
}
});
</script>
</dom-module>