| <!-- 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="cluster-sk.html"> |
| <link rel="import" href="activity-sk.html"> |
| <link rel="import" href="shared-styles.html"> |
| |
| <dom-module id="cluster-page-sk"> |
| <template> |
| <style include="iron-flex iron-flex-alignment"></style> |
| <style include="shared-styles"> |
| #clusterContainer { |
| padding: 0.5em; |
| } |
| .searchControlsWrapper { |
| padding: 1em 0 0 1em; |
| } |
| |
| digest-details-sk { |
| min-width: 800px; |
| max-width: 900px; |
| } |
| |
| .clusterRendering { |
| min-width: 400px; |
| border: 1px solid #ddd; |
| } |
| </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]]" |
| metric="combined"> |
| </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> |
| </template> |
| |
| <script> |
| Polymer({ |
| is: "cluster-page-sk", |
| |
| behaviors: [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.$.searchControls, 'search-controls-sk-change', '_handleStateChange'); |
| this.listen(this, 'zoom-dialog-opened', '_handleZoomOpened'); |
| this.listen(this, 'zoom-dialog-closed', '_handleZoomClosed'); |
| 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()); |
| |
| // Extract corpus from query and remove from it. |
| const queryParamSet = sk.query.toParamSet(this._state.query); |
| const corpus = queryParamSet.source_type[0]; |
| delete queryParamSet["source_type"]; |
| |
| // Set search-control-sk's search criteria. |
| const searchCriteria = { |
| corpus: corpus, |
| leftHandTraceFilter: queryParamSet, |
| rightHandTraceFilter: sk.query.toParamSet(this._state.rquery), |
| includePositiveDigests: this._state.pos, |
| includeNegativeDigests: this._state.neg, |
| includeUntriagedDigests: this._state.unt, |
| includeDigestsNotAtHead: !this._state.head, |
| includeIgnoredDigests: this._state.include, |
| minRGBADelta: this._state.frgbamin, |
| maxRGBADelta: this._state.frgbamax, |
| mustHaveReferenceImage: this._state.fref, |
| sortOrder: this._state.sort === 'asc' ? 'ascending' : 'descending' |
| }; |
| this.$.searchControls.searchCriteria = searchCriteria; |
| |
| this._loadClusterDiff(); |
| }, |
| |
| pageDeselected: function() { |
| this.$.clusterView.endUse(); |
| }, |
| |
| _loadParamset: function() { |
| sk.get("/json/paramset").then(JSON.parse).then(function (json) { |
| // Extract corpora from response. |
| this.$.searchControls.corpora = json.source_type; |
| |
| // Delete source_type and set the paramSet. |
| const paramSetCopy = JSON.parse(JSON.stringify(json)); |
| const corpora = paramSetCopy.source_type; |
| delete paramSetCopy["source_type"]; |
| this.$.searchControls.paramSet = paramSetCopy; |
| }.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.paramsets = [json.paramsetsUnion]; |
| }.bind(this)); |
| }, |
| |
| _handleStateChange: function(ev) { |
| const searchCriteria = ev.detail; |
| |
| // Make a copy of the current state. |
| const newState = JSON.parse(JSON.stringify(this._state)); |
| |
| // Set query fields. |
| const queryParamSet = searchCriteria.leftHandTraceFilter; |
| queryParamSet.source_type = [searchCriteria.corpus]; |
| newState.query = sk.query.fromParamSet(queryParamSet); |
| newState.rquery = sk.query.fromParamSet(searchCriteria.rightHandTraceFilter); |
| |
| // Set remaining fields. |
| newState.pos = searchCriteria.includePositiveDigests; |
| newState.neg = searchCriteria.includeNegativeDigests; |
| newState.unt = searchCriteria.includeUntriagedDigests; |
| newState.head = !searchCriteria.includeDigestsNotAtHead; |
| newState.include = searchCriteria.includeIgnoredDigests; |
| newState.frgbamin = searchCriteria.minRGBADelta; |
| newState.frgbamax = searchCriteria.maxRGBADelta; |
| newState.fref = searchCriteria.mustHaveReferenceImage; |
| newState.sort = searchCriteria.sortOrder === 'ascending' ? 'asc' : 'desc'; |
| |
| // Reload the page with the new state in the query parameters. |
| this._redirectToState(newState); |
| }, |
| |
| _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 = new gold.TriageQuery(this._data.test, this._digestList, ev.detail); |
| sk.post('/json/triage', JSON.stringify(detail)).then(function() { |
| var change = gold.flattenTriageQuery(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.details = {}; |
| 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.details = {}; |
| gold.loadWithActivity(this, url, this.$.activityDiff, '_diffData'); |
| } else { |
| // Show the option to bulk triage all selected digests. |
| this._setBulkTriage(this._digestList); |
| } |
| }, |
| |
| _handleZoomClosed: function () { |
| this._zooming = false; |
| }, |
| |
| _handleZoomOpened: function () { |
| this._zooming = true; |
| }, |
| |
| _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.paramsets = { |
| paramsets: [paramset], |
| }; |
| }, |
| |
| _hideBulkTriage: function(digestList) { |
| return digestList.length <= 2; |
| }, |
| |
| _lenIsNot: function(digestList, len) { |
| return digestList.length !== len; |
| } |
| }); |
| </script> |
| </dom-module> |