blob: 9a21372eb6095b25fdd6ee444c5cfa089dabe5fb [file] [log] [blame]
/**
* @module modules/cluster-summary2-sk
* @description <h2><code>cluster-summary2-sk</code></h2>
*
* @evt open-keys - An event that is fired when the user clicks the 'View on
* dashboard' button that contains the shortcut id, and the timestamp range of
* traces in the details that should be opened in the explorer, and the xbar
* location specified as a serialized cid.CommitID, for example:
*
* {
* shortcut: 'X1129832198312',
* begin: 1476982874,
* end: 1476987166,
* xbar: {'source':'master','offset':24750,'timestamp':1476985844},
* }
*
* @evt triaged - An event generated when the 'Update' button is pressed, which
* contains the new triage status. The detail contains the cid and triage
* status, for example:
*
* {
* cid: {
* source: 'master',
* offset: 25004,
* },
* triage: {
* status: 'negative',
* messge: 'This is a regression in ...',
* },
* }
*
* @attr {Boolean} notriage - If true then don't display the triage controls.
*
* @example
*/
import { define } from 'elements-sk/define';
import { html } from 'lit-html';
import 'elements-sk/styles/buttons';
import 'elements-sk/collapse-sk';
import '../commit-detail-panel-sk';
import '../plot-simple-sk';
import '../triage2-sk';
import '../word-cloud-sk';
import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow';
import { errorMessage } from 'elements-sk/errorMessage';
import { ElementSk } from '../../../infra-sk/modules/ElementSk';
import { Login } from '../../../infra-sk/modules/login';
function _trunc(value) {
return (+value).toPrecision(3);
}
const template = (ele) => html`
<div class='regression ${ele._statusClass()}'>
Regression: <span>${_trunc(ele._summary.step_fit.regression)}</span>
</div>
<div class=stats>
<div class=labelled>Cluster Size: <span>${ele._summary.num}</span></div>
<div class=labelled>Least Squares Error: <span>${_trunc(ele._summary.step_fit.least_squares)}</span></div>
<div class=labelled>Step Size: <span>${_trunc(ele._summary.step_fit.step_size)}</span></div>
</div>
<plot-simple-sk class=plot width=800 height=250 specialevents @trace_selected=${ele._traceSelected}></plot-simple-sk>
<div id=status class=${ele._hiddenClass()}>
<p class=disabledMessage>You must be logged in to change the status.</p>
<triage2-sk value=${ele._triage.status} @change=${(e) => { ele._triage.status = e.detail; }}></triage2-sk>
<input type=text .value=${ele._triage.message} @change=${(e) => { ele._triage.message = e.target.value; }} label=Message>
<button class=action @click=${ele._update}>Update</button>
</div>
<commit-detail-panel-sk id=commits></commit-detail-panel-sk>
<div class=actions>
<button id=shortcut @click=${ele._openShortcut}>View on dashboard</button>
<a id=permalink class=${ele._hiddenClass()} href=${ele._permaLink()}>Permlink</a>
<a id=rangelink href='' target=_blank></a>
<button @click=${ele._toggleWordCloud}>Word Cloud</button>
</div>
<collapse-sk class=wordCloudCollapse closed>
<word-cloud-sk .items=${ele._summary.param_summaries2}></word-cloud-sk>
</collapse-sk>
`;
export class ClusterSummary2Sk extends ElementSk {
constructor() {
super(template);
this._summary = {
num: 0,
step_fit: {
regression: 0,
least_squares: 0,
step_size: 0,
},
param_summaries2: [],
};
this.triage = {
status: '',
message: '',
};
}
connectedCallback() {
super.connectedCallback();
this._upgradeProperty('full_summary');
this._upgradeProperty('triage');
this._render();
this._wordCloud = this.querySelector('.wordCloudCollapse');
this._status = this.querySelector('#status');
this._graph = this.querySelector('plot-simple-sk');
this._rangelink = this.querySelector('#rangelink');
this._commits = this.querySelector('#commits');
Login.then((status) => {
this._status.classList.toggle('disabled', status.Email === '');
}).catch(errorMessage);
this.full_summary = this.full_summary;
this.triage = this.triage;
}
_update() {
const cid = this._summary.step_point;
const detail = {
cid,
triage: this.triage,
};
this.dispatchEvent(new CustomEvent('triaged', { detail, bubbles: true }));
}
_openShortcut() {
const detail = {
shortcut: this._summary.shortcut,
begin: this._frame.dataframe.header[0].timestamp,
end:
this._frame.dataframe.header[this._frame.dataframe.header.length - 1]
.timestamp + 1,
xbar: this._summary.step_point,
};
this.dispatchEvent(new CustomEvent('open-keys', { detail, bubbles: true }));
}
/**
* Look up the commit ids for the given offsets and sources.
*
* @param {Array} cids - An array of serialized cid.CommitID.
* @returns {Promise} A Promise that resolves the cids and returns an Array of serialized cid.CommitDetails.
*/
static _lookupCids(cids) {
return fetch('/_/cid/', {
method: 'POST',
body: JSON.stringify(cids),
headers: {
'Content-Type': 'application/json',
},
}).then(jsonOrThrow);
}
_traceSelected(e) {
const h = this._frame.dataframe.header[e.detail.x];
ClusterSummary2Sk._lookupCids([h])
.then((json) => {
this._commits.details = json;
})
.catch(errorMessage);
}
_toggleWordCloud() {
this._wordCloud.closed = !this._wordCloud.closed;
}
_hiddenClass() {
return this.hasAttribute('notriage') ? 'hidden' : '';
}
_permaLink() {
// Bounce to the triage page, but with the time range narrowed to
// contain just the step_point commit.
if (!this._summary || !this._summary.step_point) {
return '';
}
const begin = this._summary.step_point.timestamp;
const end = begin + 1;
return `/t/?begin=${begin}&end=${end}&subset=all`;
}
_statusClass() {
if (!this._summary) {
return '';
}
const status = this._summary.step_fit.status || '';
return status.toLowerCase();
}
/** @prop full_summary {string} A serialized:
*
* {
* summary: cluster2.ClusterSummary,
* frame: dataframe.FrameResponse,
* }
*
*/
get full_summary() {
return this._full_summary;
}
set full_summary(val) {
if (!val) {
return;
}
if (!val.frame) {
return;
}
this._full_summary = val;
this._summary = val.summary;
this._frame = val.frame;
if (!this._graph) {
return;
}
// Set the data- attributes used for sorting cluster summaries.
this.dataset.clustersize = this._summary.num;
this.dataset.steplse = this._summary.step_fit.least_squares;
this.dataset.stepsize = this._summary.step_fit.step_size;
this.dataset.stepregression = this._summary.step_fit.regression;
// We take in a ClusterSummary, but need to transform all that data
// into a format that plot-sk can handle.
this._graph.removeAll();
const labels = [];
this.full_summary.frame.dataframe.header.forEach((header) => {
labels.push(new Date(header.timestamp * 1000));
});
this._graph.addLines({ centroid: this._summary.centroid }, labels);
// Set the x-bar but only if status != uninteresting.
if (this._summary.step_fit.status !== 'Uninteresting') {
// Loop through the dataframe header to find the location we should
// place the x-bar at.
const step = this._summary.step_point;
let xbar = -1;
this._frame.dataframe.header.forEach((h, i) => {
if (h.source === step.source && h.offset === step.offset) {
xbar = i;
}
});
if (xbar !== -1) {
this._graph.xbar = xbar;
}
// Populate rangelink.
if (window.sk.perf.commit_range_url !== '') {
// First find the commit at step_fit, and the next previous commit that has data.
let prevCommit = xbar - 1;
while (prevCommit > 0 && this._summary.centroid[prevCommit] === 1e32) {
prevCommit -= 1;
}
const cids = [
this._frame.dataframe.header[prevCommit],
this._frame.dataframe.header[xbar],
];
// Run those through cid lookup to get the hashes.
ClusterSummary2Sk._lookupCids(cids)
.then((json) => {
// Create the URL.
let url = window.sk.perf.commit_range_url;
url = url.replace('{begin}', json[0].hash);
url = url.replace('{end}', json[1].hash);
// Now populate link, including text and href.
this._rangelink.href = url;
this._rangelink.innerText = 'Commits At Step';
})
.catch(errorMessage);
} else {
this._rangelink.href = '';
this._rangelink.innerText = '';
}
} else {
this._rangelink.href = '';
this._rangelink.innerText = '';
}
this._render();
}
/** @prop triage {string} The triage status of the cluster.
* Something of the form:
*
* {
* status: 'untriaged',
* message: 'This is a regression.',
* }
*
*/
get triage() {
return this._triage;
}
set triage(val) {
if (!val) {
return;
}
this._triage = val;
this._render();
}
}
define('cluster-summary2-sk', ClusterSummary2Sk);