| <!-- The <cluster-page-sk> custom element declaration. |
| |
| The top level element for clustering traces. |
| |
| Attributes: |
| None. |
| |
| Events: |
| None. |
| |
| Methods: |
| None. |
| |
| --> |
| <link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-layout-classes.html"> |
| <link rel="import" href="/res/imp/bower_components/paper-checkbox/paper-checkbox.html"> |
| <link rel="import" href="/res/imp/bower_components/paper-spinner/paper-spinner.html"> |
| <link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html"> |
| <link rel="import" href="/res/imp/bower_components/iron-selector/iron-selector.html"> |
| |
| <link rel="import" href="/res/common/imp/details-summary.html"> |
| <link rel="import" href="/res/common/imp/sort.html" /> |
| <link rel="stylesheet" href="/res/common/css/md.css"> |
| |
| <dom-module id="cluster-page-sk"> |
| <style include="iron-flex iron-flex-alignment iron-positioning"> |
| day-range-sk { |
| display: block; |
| } |
| |
| label { |
| width: 4em; |
| display: inline-block; |
| text-align: right; |
| } |
| |
| #status { |
| display: inline-block; |
| margin: 0.5em; |
| } |
| |
| cluster-summary2-sk { |
| box-shadow: 4px 4px 10px 1px rgba(0,0,0,0.75); |
| display: block; |
| padding: 1em; |
| margin: 1em; |
| width: 80em; |
| } |
| |
| #advanced h2 { |
| display: inline-block; |
| margin: 0; |
| } |
| |
| #advanced { |
| height: 2em; |
| display: inline-block; |
| vertical-align: bottom; |
| } |
| |
| #inputs { |
| margin-left: 2em; |
| } |
| |
| #inputs paper-input { |
| width: 20em; |
| } |
| |
| .iron-selected { |
| background: #eee; |
| } |
| |
| iron-selector div { |
| width: 10em; |
| margin: 0.3em 1em; |
| padding: 0.2em; |
| } |
| |
| .info { |
| margin: 1em; |
| font-size: 18px; |
| color: #E7298A; |
| } |
| |
| paper-checkbox { |
| --paper-checkbox-checked-color: #1f78b4; |
| --paper-checkbox-checked-ink-color: #1f78b4; |
| } |
| |
| algo-select-sk { |
| width: 20em; |
| display: block; |
| } |
| </style> |
| <template> |
| <h2>Commit</h2> |
| <h3>Appears in Date Range</h3> |
| <div class="layout horizontal"> |
| <day-range-sk id=range on-day-range-change="_rangeChange"></day-range-sk> |
| <paper-spinner id=spinner></paper-spinner> |
| </div> |
| <h3>Commit</h3> |
| <div> |
| <commit-detail-picker-sk on-commit-selected="_commitSelected" id=commit></commit-picker-sk> |
| </div> |
| |
| <h2>Algorithm</h2> |
| <algo-select-sk on-algo-change=_algoChange algo="[[state.algo]]"></algo-select-sk> |
| |
| <h2>Query</h2> |
| <div class="layout horizontal"> |
| <query-sk id=query on-query-change="_queryChange" on-query-change-delayed="_queryChangeDelayed"></query-sk> |
| <div class="layout vertical" id=selections> |
| <h3>Selections</h3> |
| <query-summary-sk id=summary></query-summary-sk> |
| <div> |
| Matches: <span id=matches></span> |
| </div> |
| <button on-tap="_start" class=action id=start>Run</button> |
| <div class="layout horizontal center"> |
| <paper-spinner id=clusterSpinner></paper-spinner> |
| <span id=status></span> |
| </div> |
| </div> |
| </div> |
| |
| <details-sk> |
| <summary-sk id=advanced> |
| <h2>Advanced</h2> |
| </summary-sk> |
| <div id=inputs> |
| <paper-input type=number min=0 max=100 value="{{state.k}}" label="K (A value of 0 means the server chooses)."></paper-input> |
| <paper-input type=number min=1 max=25 value="{{state.radius}}" label="Number of commits to include on either side."></paper-input> |
| <paper-input type=number min=0 max=500 value="{{state.interesting}}" label="Clusters are interesting if regression score >= this."></paper-input> |
| <paper-checkbox checked="{{state.sparse}}">Data is sparse, so only include commits that have data.</paper-checkbox> |
| </div> |
| </details-sk> |
| |
| <h2>Results</h2> |
| <sort-sk target=clusters node_name="CLUSTER-SUMMARY2-SK"> |
| <button data-key="clustersize">Cluster Size </button> |
| <button data-key="stepregression" data-default=up>Regression </button> |
| <button data-key="stepsize">Step Size </button> |
| <button data-key="steplse">Least Squares</button> |
| </sort-sk> |
| <div id=clusters> |
| <template id=results is="dom-repeat" items="{{_summaries}}"> |
| <cluster-summary2-sk full_summary="[[item]]" notriage></cluster-summary2-sk> |
| </template> |
| <template is="dom-if" if="{{_isZeroLength(_summaries,_requestId)}}"> |
| <p class=info> |
| No clusters found. |
| </p> |
| </template> |
| </div> |
| </template> |
| </dom-module> |
| |
| <script> |
| Polymer({ |
| is: "cluster-page-sk", |
| |
| properties: { |
| _dataframe: { |
| type: Object, |
| value: function() { return { |
| traceset: {}, |
| }; }, |
| }, |
| // Keep track of whether a request is inflight to count the number of traces that match the current query. |
| _countInProgress: { |
| type: Boolean, |
| value: false, |
| }, |
| // The state that goes into the URL. |
| // |
| state: { |
| type: Object, |
| value: function() { return { |
| begin: Math.floor(Date.now()/1000 - 24*60*60), |
| end: Math.floor(Date.now()/1000), |
| source: "", |
| offset: -1, |
| radius: "" + sk.perf.radius, |
| query: "", |
| k: "" + 0, |
| algo: "kmeans", |
| interesting: "" + sk.perf.interesting, |
| sparse: false, |
| }; }, |
| }, |
| // The id of the current cluster request. Will be the empty string |
| // if there is no pending request. |
| _requestId: { |
| type: String, |
| value: "", |
| }, |
| _cids: { |
| type: Array, |
| value: function() { return [] }, |
| }, |
| // Keep track of whether a request is inflight to count the number of traces that match the current query. |
| _countInProgress: { |
| type: Boolean, |
| value: false, |
| }, |
| _requestId: { |
| type: String, |
| value: "", |
| } |
| }, |
| |
| ready: function() { |
| var tz = Intl.DateTimeFormat().resolvedOptions().timeZone; |
| sk.get("/_/initpage/?tz=" + tz).then(JSON.parse).then(function(json) { |
| this.$.query.key_order = sk.perf.key_order; |
| this.$.query.paramset = json.dataframe.paramset; |
| }.bind(this)).catch(sk.errorMessage); |
| |
| |
| // From this point on reflect the state to the URL. |
| sk.stateReflector(this, this._updateCommitSelections.bind(this)); |
| |
| this.$.clusters.addEventListener("open-keys", function(e) { |
| var query = { |
| keys: e.detail.shortcut, |
| begin: e.detail.begin, |
| end: e.detail.end, |
| xbaroffset: e.detail.xbar.offset, |
| num_commits: 50, |
| request_type: 1, |
| }; |
| window.open('/e/?' + sk.query.fromObject(query), '_blank'); |
| }.bind(this)); |
| }, |
| |
| // _catch for sk.post and sk.get requests around clustering. |
| _catch: function(msg) { |
| this._requestId = ""; |
| this.$.clusterSpinner.active = false; |
| this.$.start.disabled = false; |
| if (msg) { |
| sk.errorMessage(msg, 10000); |
| } |
| this.$.status.textContent = ""; |
| }, |
| |
| _start: function() { |
| if (this._requestId != "") { |
| sk.errorMessage("There is a pending query already running."); |
| return |
| } |
| var body = { |
| source: this.state.source, |
| offset: this.state.offset, |
| radius: +this.state.radius, |
| query: this.state.query, |
| k: +this.state.k, |
| tz: Intl.DateTimeFormat().resolvedOptions().timeZone, |
| algo: this.state.algo, |
| interesting: +this.state.interesting, |
| sparse: this.state.sparse, |
| }; |
| this._summaries = []; |
| this.$.results.render(); |
| this.$.clusterSpinner.active = true; |
| this.$.start.disabled = true; |
| sk.post("/_/cluster/start", JSON.stringify(body), "application/json").then(JSON.parse).then(function(json) { |
| this._requestId = json.id; |
| this._checkClusterRequestStatus(function(summaries) { |
| var fullSummaries = []; |
| summaries.summary.Clusters.forEach(function(cl) { |
| cl.ID = -1; |
| fullSummaries.push({ |
| summary: cl, |
| frame: summaries.frame, |
| }); |
| }); |
| this.set('_summaries', fullSummaries); |
| }.bind(this)); |
| }.bind(this)).catch(this._catch.bind(this)); |
| }, |
| |
| _algoChange: function(e) { |
| this.state.algo = e.detail.algo; |
| }, |
| |
| _checkClusterRequestStatus: function(cb) { |
| sk.get("/_/cluster/status/"+this._requestId).then(JSON.parse).then(function(json) { |
| if (json.state == "Running") { |
| this.$.status.textContent = json.message; |
| window.setTimeout(this._checkClusterRequestStatus.bind(this, cb), 300); |
| } else { |
| if (json.value) { |
| cb(json.value); |
| } |
| this._catch(json.message); |
| } |
| }.bind(this)).catch(this._catch.bind(this)); |
| }, |
| |
| _updateCommitSelections: function() { |
| this.$.range.begin = this.state.begin; |
| this.$.range.end = this.state.end; |
| this.$.query.current_query = this.state.query; |
| var body = { |
| begin: this.state.begin, |
| end: this.state.end, |
| source: this.state.source, |
| offset: this.state.offset, |
| }; |
| this.$.spinner.active = true; |
| sk.post("/_/cidRange/", JSON.stringify(body), "application/json").then(JSON.parse).then(function(cids) { |
| this.$.spinner.active = false; |
| cids.reverse(); |
| this._cids = cids; |
| this.$.commit.details = cids; |
| |
| var index = -1; |
| // Look for commit id in this._cids. |
| for (var i = 0; i < cids.length; i++) { |
| if (cids[i].source == this.state.source && cids[i].offset == this.state.offset) { |
| index = i; |
| break |
| } |
| } |
| // If there is then select via index. |
| if (index != -1) { |
| this.$.commit.selected = index; |
| } |
| |
| if (this.state.begin == 0) { |
| this.state.begin = cids[cids.length-1].ts; |
| this.$.range.begin = cids[cids.length-1].ts; |
| this.state.end = cids[0].ts; |
| this.$.range.end = cids[0].ts; |
| } |
| }.bind(this)).catch(function(msg) { |
| if (msg) { |
| sk.errorMessage(msg, 10000); |
| } |
| this.$.spinner.active = false; |
| }.bind(this)); |
| this._updateCount(); |
| }, |
| |
| _commitSelected: function(e) { |
| this.state.source = e.detail.commit.source; |
| this.state.offset = e.detail.commit.offset; |
| }, |
| |
| _queryChange: function(e) { |
| this.state.query = e.detail.q; |
| this.$.summary.selection = e.detail.q; |
| }, |
| |
| _queryChangeDelayed: function(e) { |
| this._updateCount(); |
| }, |
| |
| _rangeChange: function(e) { |
| if (!this.state) { |
| return |
| } |
| this.state.begin = e.detail.begin; |
| this.state.end = e.detail.end; |
| this._updateCommitSelections(); |
| }, |
| |
| _updateCount: function() { |
| if (this._countInProgress === true) { |
| return |
| } |
| this._countInProgress = true; |
| let body = { |
| q: this.$.query.current_query, |
| begin: this.state.begin, |
| end: this.state.end, |
| }; |
| sk.post("/_/count/", JSON.stringify(body)).then(JSON.parse).then(function(json) { |
| this._countInProgress = false; |
| this.$.matches.textContent = json.count; |
| if (json.paramset) { |
| this.$.query.paramset = json.paramset; |
| } |
| }.bind(this)).catch(function() { |
| this._countInProgress = false; |
| }); |
| }, |
| |
| _isZeroLength: function(ar, _requestId) { |
| return ar.length == 0 && this._requestId == ""; |
| } |
| |
| }); |
| </script> |