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