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