SpreadsheetView.java revision d9e04c1a7950691cc348e70fa2470f8c414ae94f
1package autotest.tko; 2 3import autotest.common.JsonRpcCallback; 4import autotest.common.JsonRpcProxy; 5import autotest.common.Utils; 6import autotest.common.CustomHistory.HistoryToken; 7import autotest.common.ui.ContextMenu; 8import autotest.common.ui.NotifyManager; 9import autotest.common.ui.RightClickTable; 10import autotest.common.ui.SimpleHyperlink; 11import autotest.common.ui.TableActionsPanel; 12import autotest.common.ui.TableActionsPanel.TableActionsWithExportCsvListener; 13import autotest.common.ui.TableSelectionPanel.SelectionPanelListener; 14import autotest.tko.CommonPanel.CommonPanelListener; 15import autotest.tko.Spreadsheet.CellInfo; 16import autotest.tko.Spreadsheet.Header; 17import autotest.tko.Spreadsheet.SpreadsheetListener; 18import autotest.tko.TableView.TableSwitchListener; 19import autotest.tko.TableView.TableViewConfig; 20import autotest.tko.TkoUtils.FieldInfo; 21 22import com.google.gwt.json.client.JSONArray; 23import com.google.gwt.json.client.JSONObject; 24import com.google.gwt.json.client.JSONString; 25import com.google.gwt.json.client.JSONValue; 26import com.google.gwt.user.client.Command; 27import com.google.gwt.user.client.Event; 28import com.google.gwt.user.client.History; 29import com.google.gwt.user.client.Window; 30import com.google.gwt.user.client.WindowResizeListener; 31import com.google.gwt.user.client.ui.Button; 32import com.google.gwt.user.client.ui.ChangeListener; 33import com.google.gwt.user.client.ui.CheckBox; 34import com.google.gwt.user.client.ui.ClickListener; 35import com.google.gwt.user.client.ui.HTML; 36import com.google.gwt.user.client.ui.MenuBar; 37import com.google.gwt.user.client.ui.Panel; 38import com.google.gwt.user.client.ui.SimplePanel; 39import com.google.gwt.user.client.ui.VerticalPanel; 40import com.google.gwt.user.client.ui.Widget; 41 42import java.util.HashMap; 43import java.util.List; 44import java.util.Map; 45 46public class SpreadsheetView extends ConditionTabView 47 implements SpreadsheetListener, TableActionsWithExportCsvListener, 48 CommonPanelListener, SelectionPanelListener { 49 private static final String HISTORY_ONLY_LATEST = "show_only_latest"; 50 public static final String DEFAULT_ROW = "kernel"; 51 public static final String DEFAULT_COLUMN = "platform"; 52 53 private static final String HISTORY_SHOW_INCOMPLETE = "show_incomplete"; 54 private static final String HISTORY_COLUMN = "column"; 55 private static final String HISTORY_ROW = "row"; 56 private static final String HISTORY_CONTENT = "content"; 57 58 private static enum DrilldownType {DRILLDOWN_ROW, DRILLDOWN_COLUMN, DRILLDOWN_BOTH} 59 60 private static JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy(); 61 private static JsonRpcProxy afeRpcProxy = JsonRpcProxy.getProxy(JsonRpcProxy.AFE_BASE_URL); 62 private TableSwitchListener listener; 63 protected Map<String,String[]> drilldownMap = new HashMap<String,String[]>(); 64 private Map<String, HeaderField> headerFieldMap = new HashMap<String, HeaderField>(); 65 66 private HeaderSelect rowSelect = new HeaderSelect(); 67 private HeaderSelectorView rowSelectDisplay = new HeaderSelectorView(); 68 private HeaderSelect columnSelect = new HeaderSelect(); 69 private HeaderSelectorView columnSelectDisplay = new HeaderSelectorView(); 70 private ContentSelect contentSelect = new ContentSelect(); 71 private CheckBox showIncomplete = new CheckBox("Show incomplete tests"); 72 private CheckBox showOnlyLatest = new CheckBox("Show only latest test per cell"); 73 private Button queryButton = new Button("Query"); 74 private TestGroupDataSource normalDataSource = TestGroupDataSource.getStatusCountDataSource(); 75 private TestGroupDataSource latestDataSource = TestGroupDataSource.getLatestTestsDataSource(); 76 private Spreadsheet spreadsheet = new Spreadsheet(); 77 private SpreadsheetDataProcessor spreadsheetProcessor = 78 new SpreadsheetDataProcessor(spreadsheet); 79 private SpreadsheetSelectionManager selectionManager = 80 new SpreadsheetSelectionManager(spreadsheet, null); 81 private TableActionsPanel actionsPanel = new TableActionsPanel(false); 82 private Panel jobCompletionPanel = new SimplePanel(); 83 private boolean currentShowIncomplete, currentShowOnlyLatest; 84 private boolean notYetQueried = true; 85 public SpreadsheetView(TableSwitchListener listener) { 86 this.listener = listener; 87 commonPanel.addListener(this); 88 } 89 90 @Override 91 public String getElementId() { 92 return "spreadsheet_view"; 93 } 94 95 @Override 96 public void initialize() { 97 super.initialize(); 98 normalDataSource.setSkipNumResults(true); 99 latestDataSource.setSkipNumResults(true); 100 101 actionsPanel.setActionsWithCsvListener(this); 102 actionsPanel.setSelectionListener(this); 103 actionsPanel.setVisible(false); 104 105 rowSelect.bindDisplay(rowSelectDisplay); 106 columnSelect.bindDisplay(columnSelectDisplay); 107 108 for (FieldInfo fieldInfo : TkoUtils.getFieldList("group_fields")) { 109 HeaderField field = new SimpleHeaderField(fieldInfo.name, fieldInfo.field); 110 headerFieldMap.put(fieldInfo.field, field); 111 rowSelect.addItem(field); 112 columnSelect.addItem(field); 113 contentSelect.addItem(field); 114 } 115 rowSelect.selectItem(headerFieldMap.get(DEFAULT_ROW)); 116 columnSelect.selectItem(headerFieldMap.get(DEFAULT_COLUMN)); 117 118 contentSelect.setChangeListener(new ChangeListener() { 119 public void onChange(Widget sender) { 120 if (contentSelect.hasSelection()) { 121 showOnlyLatest.setChecked(true); 122 showOnlyLatest.setEnabled(false); 123 } else { 124 showOnlyLatest.setEnabled(true); 125 } 126 } 127 }); 128 129 updateViewFromState(); 130 131 queryButton.addClickListener(new ClickListener() { 132 public void onClick(Widget sender) { 133 doQuery(); 134 updateHistory(); 135 } 136 }); 137 138 spreadsheet.setVisible(false); 139 spreadsheet.setListener(this); 140 141 SimpleHyperlink swapLink = new SimpleHyperlink("swap"); 142 swapLink.addClickListener(new ClickListener() { 143 public void onClick(Widget sender) { 144 List<HeaderField> newRows = columnSelect.getSelectedItems(); 145 setSelectedHeader(columnSelect, rowSelect.getSelectedItems()); 146 setSelectedHeader(rowSelect, newRows); 147 } 148 }); 149 150 Panel filterOptions = new VerticalPanel(); 151 filterOptions.add(showIncomplete); 152 filterOptions.add(showOnlyLatest); 153 154 addWidget(filterOptions, "ss_filter_options"); 155 addWidget(rowSelectDisplay, "ss_row_select"); 156 addWidget(columnSelectDisplay, "ss_column_select"); 157 addWidget(contentSelect, "ss_additional_content"); 158 addWidget(swapLink, "ss_swap"); 159 addWidget(queryButton, "ss_query_controls"); 160 addWidget(actionsPanel, "ss_actions"); 161 addWidget(spreadsheet, "ss_spreadsheet"); 162 addWidget(jobCompletionPanel, "ss_job_completion"); 163 164 Window.addWindowResizeListener(new WindowResizeListener() { 165 public void onWindowResized(int width, int height) { 166 if(spreadsheet.isVisible()) 167 spreadsheet.fillWindow(true); 168 } 169 }); 170 171 setupDrilldownMap(); 172 } 173 174 protected TestSet getWholeTableTestSet() { 175 boolean isSingleTest = spreadsheetProcessor.getNumTotalTests() == 1; 176 if (isSingleTest) { 177 return getTestSet(spreadsheetProcessor.getLastCellInfo()); 178 } 179 180 if (currentShowOnlyLatest) { 181 List<Integer> testIndices = spreadsheet.getAllTestIndices(); 182 String filter = "test_idx IN (" + Utils.joinStrings(",", testIndices) + ")"; 183 ConditionTestSet tests = new ConditionTestSet(); 184 tests.addCondition(filter); 185 return tests; 186 } 187 188 return new ConditionTestSet(getFullConditionArgs()); 189 } 190 191 protected void setupDrilldownMap() { 192 drilldownMap.put("platform", new String[] {"hostname", "test_name"}); 193 drilldownMap.put("hostname", new String[] {"job_tag", "status"}); 194 drilldownMap.put("job_tag", new String[] {"job_tag"}); 195 196 drilldownMap.put("kernel", new String[] {"test_name", "status"}); 197 drilldownMap.put("test_name", new String[] {"job_name", "job_tag"}); 198 199 drilldownMap.put("status", new String[] {"reason", "job_tag"}); 200 drilldownMap.put("reason", new String[] {"job_tag"}); 201 202 drilldownMap.put("job_owner", new String[] {"job_name", "job_tag"}); 203 drilldownMap.put("job_name", new String[] {"job_tag"}); 204 205 drilldownMap.put("test_finished_time", new String[] {"status", "job_tag"}); 206 drilldownMap.put("DATE(test_finished_time)", 207 new String[] {"test_finished_time", "job_tag"}); 208 } 209 210 protected void setSelectedHeader(HeaderSelect list, List<HeaderField> fields) { 211 list.selectItems(fields); 212 } 213 214 @Override 215 public void refresh() { 216 notYetQueried = false; 217 actionsPanel.setVisible(true); 218 spreadsheet.setVisible(false); 219 selectionManager.clearSelection(); 220 spreadsheet.clear(); 221 setJobCompletionHtml(" "); 222 223 final JSONObject condition = getFullConditionArgs(); 224 225 contentSelect.addToCondition(condition); 226 227 setLoading(true); 228 if (currentShowOnlyLatest) { 229 spreadsheetProcessor.setDataSource(latestDataSource); 230 } else { 231 spreadsheetProcessor.setDataSource(normalDataSource); 232 } 233 spreadsheetProcessor.setHeaders(rowSelect.getSelectedItems(), 234 columnSelect.getSelectedItems(), 235 getQueryParameters()); 236 spreadsheetProcessor.refresh(condition, new Command() { 237 public void execute() { 238 condition.put("extra_info", null); 239 240 if (isJobFilteringCondition(condition)) { 241 showCompletionPercentage(condition); 242 } else { 243 setLoading(false); 244 } 245 } 246 }); 247 } 248 249 private JSONObject getQueryParameters() { 250 JSONObject parameters = new JSONObject(); 251 rowSelect.addQueryParameters(parameters); 252 columnSelect.addQueryParameters(parameters); 253 return parameters; 254 } 255 256 private JSONObject getFullConditionArgs() { 257 JSONObject args = commonPanel.getConditionArgs(); 258 String condition = TkoUtils.getSqlCondition(args); 259 if (!condition.equals("")) { 260 condition = "(" + condition + ") AND "; 261 } 262 condition += "status != 'TEST_NA'"; 263 if (!currentShowIncomplete) { 264 condition += " AND status != 'RUNNING'"; 265 } 266 args.put("extra_where", new JSONString(condition)); 267 return args; 268 } 269 270 private void updateStateFromView() { 271 rowSelect.updateStateFromView(); 272 columnSelect.updateStateFromView(); 273 currentShowIncomplete = showIncomplete.isChecked(); 274 currentShowOnlyLatest = showOnlyLatest.isChecked(); 275 commonPanel.updateStateFromView(); 276 } 277 278 public void doQuery() { 279 List<HeaderField> rows = rowSelect.getSelectedItems(); 280 List<HeaderField> columns = columnSelect.getSelectedItems(); 281 if (rows.isEmpty() || columns.isEmpty()) { 282 NotifyManager.getInstance().showError("You must select row and column fields"); 283 return; 284 } 285 if (!rowSelect.checkMachineLabelHeaders() || !columnSelect.checkMachineLabelHeaders()) { 286 NotifyManager.getInstance().showError( 287 "You must enter labels for all machine label fields"); 288 return; 289 } 290 291 updateStateFromView(); 292 refresh(); 293 } 294 295 private void showCompletionPercentage(JSONObject condition) { 296 rpcProxy.rpcCall("get_job_ids", condition, new JsonRpcCallback() { 297 @Override 298 public void onSuccess(JSONValue result) { 299 finishShowCompletionPercentage(result.isArray()); 300 setLoading(false); 301 } 302 303 @Override 304 public void onError(JSONObject errorObject) { 305 super.onError(errorObject); 306 setLoading(false); 307 } 308 }); 309 } 310 311 private void finishShowCompletionPercentage(JSONArray jobIds) { 312 final int jobCount = jobIds.size(); 313 if (jobCount == 0) { 314 return; 315 } 316 317 JSONObject args = new JSONObject(); 318 args.put("job__id__in", jobIds); 319 afeRpcProxy.rpcCall("get_hqe_percentage_complete", args, new JsonRpcCallback() { 320 @Override 321 public void onSuccess(JSONValue result) { 322 int percentage = (int) (result.isNumber().doubleValue() * 100); 323 StringBuilder message = new StringBuilder("Matching "); 324 if (jobCount == 1) { 325 message.append("job is "); 326 } else { 327 message.append("jobs are "); 328 } 329 message.append(percentage); 330 message.append("% complete"); 331 setJobCompletionHtml(message.toString()); 332 } 333 }); 334 } 335 336 private void setJobCompletionHtml(String html) { 337 jobCompletionPanel.clear(); 338 jobCompletionPanel.add(new HTML(html)); 339 } 340 341 private boolean isJobFilteringCondition(JSONObject condition) { 342 return TkoUtils.getSqlCondition(condition).indexOf("job_tag") != -1; 343 } 344 345 public void onCellClicked(CellInfo cellInfo) { 346 Event event = Event.getCurrentEvent(); 347 TestSet testSet = getTestSet(cellInfo); 348 DrilldownType drilldownType = getDrilldownType(cellInfo); 349 if (RightClickTable.isRightClick(event)) { 350 if (!selectionManager.isEmpty()) { 351 testSet = getTestSet(selectionManager.getSelectedCells()); 352 drilldownType = DrilldownType.DRILLDOWN_BOTH; 353 } 354 ContextMenu menu = getContextMenu(testSet, drilldownType); 355 menu.showAtWindow(event.getClientX(), event.getClientY()); 356 return; 357 } 358 359 if (isSelectEvent(event)) { 360 selectionManager.toggleSelected(cellInfo); 361 return; 362 } 363 364 HistoryToken historyToken; 365 if (testSet.isSingleTest()) { 366 historyToken = listener.getSelectTestHistoryToken(testSet.getTestIndex()); 367 } else { 368 historyToken = getDrilldownHistoryToken(testSet, 369 getDefaultDrilldownRow(drilldownType), 370 getDefaultDrilldownColumn(drilldownType)); 371 } 372 openHistoryToken(historyToken); 373 } 374 375 private DrilldownType getDrilldownType(CellInfo cellInfo) { 376 if (cellInfo.row == null) { 377 // column header 378 return DrilldownType.DRILLDOWN_COLUMN; 379 } 380 if (cellInfo.column == null) { 381 // row header 382 return DrilldownType.DRILLDOWN_ROW; 383 } 384 return DrilldownType.DRILLDOWN_BOTH; 385 } 386 387 private TestSet getTestSet(CellInfo cellInfo) { 388 boolean isSingleTest = cellInfo.testCount == 1; 389 if (isSingleTest) { 390 return new SingleTestSet(cellInfo.testIndex, getFullConditionArgs()); 391 } 392 393 ConditionTestSet testSet = new ConditionTestSet(getFullConditionArgs()); 394 if (cellInfo.row != null) { 395 setSomeFields(testSet, rowSelect.getSelectedItems(), cellInfo.row); 396 } 397 if (cellInfo.column != null) { 398 setSomeFields(testSet, columnSelect.getSelectedItems(), cellInfo.column); 399 } 400 return testSet; 401 } 402 403 private void setSomeFields(ConditionTestSet testSet, List<HeaderField> allFields, 404 Header values) { 405 for (int i = 0; i < values.size(); i++) { 406 HeaderField field = allFields.get(i); 407 String value = values.get(i); 408 testSet.addCondition(field.getSqlCondition(value)); 409 } 410 } 411 412 private TestSet getTestSet(List<CellInfo> cells) { 413 CompositeTestSet tests = new CompositeTestSet(); 414 for (CellInfo cell : cells) { 415 tests.add(getTestSet(cell)); 416 } 417 return tests; 418 } 419 420 private HistoryToken getDrilldownHistoryToken(TestSet tests, String newRowField, 421 String newColumnField) { 422 saveHistoryState(); 423 commonPanel.refineCondition(tests); 424 rowSelect.selectItem(headerFieldMap.get(newRowField)); 425 columnSelect.selectItem(headerFieldMap.get(newColumnField)); 426 HistoryToken historyArguments = getHistoryArguments(); 427 restoreHistoryState(); 428 return historyArguments; 429 } 430 431 private void doDrilldown(TestSet tests, String newRowField, String newColumnField) { 432 History.newItem(getDrilldownHistoryToken(tests, newRowField, newColumnField).toString()); 433 } 434 435 private String getDefaultDrilldownRow(DrilldownType type) { 436 return getDrilldownRows(type)[0]; 437 } 438 439 private String getDefaultDrilldownColumn(DrilldownType type) { 440 return getDrilldownColumns(type)[0]; 441 } 442 443 private ContextMenu getContextMenu(final TestSet tests, DrilldownType drilldownType) { 444 TestContextMenu menu = new TestContextMenu(tests, listener); 445 446 if (!menu.addViewDetailsIfSingleTest()) { 447 MenuBar drilldownMenu = menu.addSubMenuItem("Drill down"); 448 fillDrilldownMenu(tests, drilldownType, drilldownMenu); 449 } 450 451 menu.addItem("View in table", new Command() { 452 public void execute() { 453 switchToTable(tests, false); 454 } 455 }); 456 menu.addItem("Triage failures", new Command() { 457 public void execute() { 458 switchToTable(tests, true); 459 } 460 }); 461 462 menu.addLabelItems(); 463 return menu; 464 } 465 466 private void fillDrilldownMenu(final TestSet tests, DrilldownType drilldownType, MenuBar menu) { 467 for (final String rowField : getDrilldownRows(drilldownType)) { 468 for (final String columnField : getDrilldownColumns(drilldownType)) { 469 if (rowField.equals(columnField)) { 470 continue; 471 } 472 menu.addItem(rowField + " vs. " + columnField, new Command() { 473 public void execute() { 474 doDrilldown(tests, rowField, columnField); 475 } 476 }); 477 } 478 } 479 } 480 481 private String[] getDrilldownFields(List<HeaderField> fields, DrilldownType type, 482 DrilldownType otherType) { 483 HeaderField lastField = fields.get(fields.size() - 1); 484 String lastFieldName = lastField.getSqlName(); 485 if (type == otherType) { 486 return new String[] {lastFieldName}; 487 } else { 488 if (lastField instanceof MachineLabelField) { 489 // treat machine label fields like platform, for the purpose of default drilldown 490 lastFieldName = "platform"; 491 } 492 return drilldownMap.get(lastFieldName); 493 } 494 } 495 496 private String[] getDrilldownRows(DrilldownType type) { 497 return getDrilldownFields(rowSelect.getSelectedItems(), type, 498 DrilldownType.DRILLDOWN_COLUMN); 499 } 500 501 private String[] getDrilldownColumns(DrilldownType type) { 502 return getDrilldownFields(columnSelect.getSelectedItems(), type, 503 DrilldownType.DRILLDOWN_ROW); 504 } 505 506 private void updateViewFromState() { 507 rowSelect.updateViewFromState(); 508 columnSelect.updateViewFromState(); 509 showIncomplete.setChecked(currentShowIncomplete); 510 showOnlyLatest.setChecked(currentShowOnlyLatest); 511 commonPanel.updateViewFromState(); 512 } 513 514 @Override 515 public HistoryToken getHistoryArguments() { 516 HistoryToken arguments = super.getHistoryArguments(); 517 if (!notYetQueried) { 518 rowSelect.addHistoryArguments(arguments, HISTORY_ROW); 519 columnSelect.addHistoryArguments(arguments, HISTORY_COLUMN); 520 contentSelect.addHistoryArguments(arguments, HISTORY_CONTENT); 521 arguments.put(HISTORY_SHOW_INCOMPLETE, Boolean.toString(currentShowIncomplete)); 522 arguments.put(HISTORY_ONLY_LATEST, Boolean.toString(showOnlyLatest.isChecked())); 523 commonPanel.addHistoryArguments(arguments); 524 } 525 return arguments; 526 } 527 528 @Override 529 public void handleHistoryArguments(Map<String, String> arguments) { 530 super.handleHistoryArguments(arguments); 531 commonPanel.handleHistoryArguments(arguments); 532 rowSelect.handleHistoryArguments(arguments, HISTORY_ROW); 533 columnSelect.handleHistoryArguments(arguments, HISTORY_COLUMN); 534 contentSelect.handleHistoryArguments(arguments, HISTORY_CONTENT); 535 536 currentShowIncomplete = Boolean.valueOf(arguments.get(HISTORY_SHOW_INCOMPLETE)); 537 currentShowOnlyLatest = Boolean.valueOf(arguments.get(HISTORY_ONLY_LATEST)); 538 updateViewFromState(); 539 } 540 541 @Override 542 protected void fillDefaultHistoryValues(Map<String, String> arguments) { 543 Utils.setDefaultValue(arguments, HISTORY_ROW, DEFAULT_ROW); 544 Utils.setDefaultValue(arguments, HISTORY_COLUMN, DEFAULT_COLUMN); 545 Utils.setDefaultValue(arguments, HISTORY_ROW + HeaderSelect.HISTORY_FIXED_VALUES, ""); 546 Utils.setDefaultValue(arguments, HISTORY_COLUMN + HeaderSelect.HISTORY_FIXED_VALUES, ""); 547 Utils.setDefaultValue(arguments, HISTORY_SHOW_INCOMPLETE, Boolean.toString(false)); 548 Utils.setDefaultValue(arguments, HISTORY_ONLY_LATEST, Boolean.toString(false)); 549 } 550 551 private void switchToTable(final TestSet tests, boolean isTriageView) { 552 commonPanel.refineCondition(tests); 553 TableViewConfig config; 554 if (isTriageView) { 555 config = TableViewConfig.TRIAGE; 556 refineConditionForTriage(); 557 } else { 558 config = TableViewConfig.DEFAULT; 559 } 560 listener.onSwitchToTable(config); 561 } 562 563 private void refineConditionForTriage() { 564 commonPanel.refineCondition("status != 'GOOD'"); 565 } 566 567 public ContextMenu getActionMenu() { 568 TestSet tests; 569 if (selectionManager.isEmpty()) { 570 tests = getWholeTableTestSet(); 571 } else { 572 tests = getTestSet(selectionManager.getSelectedCells()); 573 } 574 return getContextMenu(tests, DrilldownType.DRILLDOWN_BOTH); 575 } 576 577 public void onExportCsv() { 578 JSONObject params = new JSONObject(); 579 contentSelect.addToCondition(params); 580 TkoUtils.doCsvRequest(spreadsheetProcessor.getDataSource(), params); 581 } 582 583 public void onSelectAll(boolean ignored) { 584 selectionManager.selectAll(); 585 } 586 587 public void onSelectNone() { 588 selectionManager.clearSelection(); 589 } 590 591 @Override 592 protected boolean hasFirstQueryOccurred() { 593 return !notYetQueried; 594 } 595 596 private void setLoading(boolean loading) { 597 queryButton.setEnabled(!loading); 598 NotifyManager.getInstance().setLoading(loading); 599 } 600 601 public void onSetControlsVisible(boolean visible) { 602 TkoUtils.setElementVisible("ss_all_controls", visible); 603 if (isTabVisible()) { 604 spreadsheet.fillWindow(true); 605 } 606 } 607} 608