package autotest.tko; import autotest.common.StatusSummary; import autotest.common.Utils; import autotest.common.CustomHistory.HistoryToken; import autotest.common.table.DataTable; import autotest.common.table.DynamicTable; import autotest.common.table.RpcDataSource; import autotest.common.table.SelectionManager; import autotest.common.table.SimpleFilter; import autotest.common.table.TableDecorator; import autotest.common.table.DataSource.SortDirection; import autotest.common.table.DataSource.SortSpec; import autotest.common.table.DataTable.TableWidgetFactory; import autotest.common.table.DynamicTable.DynamicTableListener; import autotest.common.ui.ContextMenu; import autotest.common.ui.DoubleListSelector; import autotest.common.ui.MultiListSelectPresenter; import autotest.common.ui.NotifyManager; import autotest.common.ui.MultiListSelectPresenter.Item; import autotest.common.ui.TableActionsPanel.TableActionsWithExportCsvListener; import autotest.tko.CommonPanel.CommonPanelListener; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.History; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; public class TableView extends ConditionTabView implements DynamicTableListener, TableActionsWithExportCsvListener, ClickHandler, TableWidgetFactory, CommonPanelListener, MultiListSelectPresenter.GeneratorHandler { private static final int ROWS_PER_PAGE = 30; private static final String COUNT_NAME = "Count in group"; private static final String STATUS_COUNTS_NAME = "Test pass rate"; private static final String[] DEFAULT_COLUMNS = {"Test index", "Test name", "Job tag", "Hostname", "Status"}; private static final String[] TRIAGE_GROUP_COLUMNS = {"Test name", "Status", COUNT_NAME, "Reason"}; private static final String[] PASS_RATE_GROUP_COLUMNS = {"Hostname", STATUS_COUNTS_NAME}; private static final SortSpec[] TRIAGE_SORT_SPECS = { new SortSpec("test_name", SortDirection.ASCENDING), new SortSpec("status", SortDirection.ASCENDING), new SortSpec("reason", SortDirection.ASCENDING), }; private static enum GroupingType {NO_GROUPING, TEST_GROUPING, STATUS_COUNTS} /** * HeaderField representing a grouped count of some kind. */ private static class GroupCountField extends HeaderField { public GroupCountField(String name, String sqlName) { super(name, sqlName); } @Override public Item getItem() { return Item.createGeneratedItem(getName(), getSqlName()); } @Override public String getSqlCondition(String value) { throw new UnsupportedOperationException(); } @Override public boolean isUserSelectable() { return false; } } private GroupCountField groupCountField = new GroupCountField(COUNT_NAME, TestGroupDataSource.GROUP_COUNT_FIELD); private GroupCountField statusCountsField = new GroupCountField(STATUS_COUNTS_NAME, DataTable.WIDGET_COLUMN); private TestSelectionListener listener; private DynamicTable table; private TableDecorator tableDecorator; private SelectionManager selectionManager; private SimpleFilter sqlConditionFilter = new SimpleFilter(); private RpcDataSource testDataSource = new TestViewDataSource(); private TestGroupDataSource groupDataSource = TestGroupDataSource.getTestGroupDataSource(); private HeaderFieldCollection headerFields = commonPanel.getHeaderFields(); private HeaderSelect columnSelect = new HeaderSelect(headerFields, new HeaderSelect.State()); private DoubleListSelector columnSelectDisplay = new DoubleListSelector(); private CheckBox groupCheckbox = new CheckBox("Group by these columns and show counts"); private CheckBox statusGroupCheckbox = new CheckBox("Group by these columns and show pass rates"); private Button queryButton = new Button("Query"); private Panel tablePanel = new SimplePanel(); private List tableSorts = new ArrayList(); public enum TableViewConfig { DEFAULT, PASS_RATE, TRIAGE } public static interface TableSwitchListener extends TestSelectionListener { public void onSwitchToTable(TableViewConfig config); } public TableView(TestSelectionListener listener) { this.listener = listener; commonPanel.addListener(this); columnSelect.setGeneratorHandler(this); columnSelect.bindDisplay(columnSelectDisplay); } @Override public String getElementId() { return "table_view"; } @Override public void initialize() { super.initialize(); headerFields.add(groupCountField); headerFields.add(statusCountsField); selectColumnsByName(DEFAULT_COLUMNS); updateViewFromState(); queryButton.addClickHandler(this); groupCheckbox.addClickHandler(this); statusGroupCheckbox.addClickHandler(this); Panel columnPanel = new VerticalPanel(); columnPanel.add(columnSelectDisplay); columnPanel.add(groupCheckbox); columnPanel.add(statusGroupCheckbox); addWidget(columnPanel, "table_column_select"); addWidget(queryButton, "table_query_controls"); addWidget(tablePanel, "table_table"); } private void selectColumnsByName(String[] columnNames) { List fields = new ArrayList(); for (String name : columnNames) { fields.add(headerFields.getFieldByName(name)); } columnSelect.setSelectedItems(fields); cleanupSortsForNewColumns(); } public void setupDefaultView() { tableSorts.clear(); selectColumnsByName(DEFAULT_COLUMNS); updateViewFromState(); } public void setupJobTriage() { selectColumnsByName(TRIAGE_GROUP_COLUMNS); // need to copy it so we can mutate it tableSorts = new ArrayList(Arrays.asList(TRIAGE_SORT_SPECS)); updateViewFromState(); } public void setupPassRate() { tableSorts.clear(); selectColumnsByName(PASS_RATE_GROUP_COLUMNS); updateViewFromState(); } private void createTable() { String[][] columns = buildColumnSpecs(); table = new DynamicTable(columns, getDataSource()); table.addFilter(sqlConditionFilter); table.setRowsPerPage(ROWS_PER_PAGE); table.makeClientSortable(); table.setClickable(true); table.sinkRightClickEvents(); table.addListener(this); table.setWidgetFactory(this); restoreTableSorting(); tableDecorator = new TableDecorator(table); tableDecorator.addPaginators(); selectionManager = tableDecorator.addSelectionManager(false); tableDecorator.addTableActionsWithExportCsvListener(this); tablePanel.clear(); tablePanel.add(tableDecorator); selectionManager = new SelectionManager(table, false); } private String[][] buildColumnSpecs() { int numColumns = savedColumns().size(); String[][] columns = new String[numColumns][2]; int i = 0; for (HeaderField field : savedColumns()) { columns[i][0] = field.getSqlName(); columns[i][1] = field.getName(); i++; } return columns; } private List savedColumns() { return columnSelect.getSelectedItems(); } private RpcDataSource getDataSource() { GroupingType groupingType = getActiveGrouping(); if (groupingType == GroupingType.NO_GROUPING) { return testDataSource; } else if (groupingType == GroupingType.TEST_GROUPING) { groupDataSource = TestGroupDataSource.getTestGroupDataSource(); } else { groupDataSource = TestGroupDataSource.getStatusCountDataSource(); } updateGroupColumns(); return groupDataSource; } private void updateStateFromView() { commonPanel.updateStateFromView(); columnSelect.updateStateFromView(); } private void updateViewFromState() { commonPanel.updateViewFromState(); columnSelect.updateViewFromState(); } private void updateGroupColumns() { List groupFields = new ArrayList(); for (HeaderField field : savedColumns()) { if (!isGroupField(field)) { groupFields.add(field.getSqlName()); } } groupDataSource.setGroupColumns(groupFields.toArray(new String[0])); } private boolean isGroupField(HeaderField field) { return field instanceof GroupCountField; } private void saveTableSorting() { if (table != null) { // we need our own copy so we can modify it later tableSorts = new ArrayList(table.getSortSpecs()); } } private void restoreTableSorting() { for (ListIterator i = tableSorts.listIterator(tableSorts.size()); i.hasPrevious();) { SortSpec sortSpec = i.previous(); table.sortOnColumn(sortSpec.getField(), sortSpec.getDirection()); } } private void cleanupSortsForNewColumns() { // remove sorts on columns that we no longer have for (Iterator i = tableSorts.iterator(); i.hasNext();) { String attribute = i.next().getField(); if (!isAttributeSelected(attribute)) { i.remove(); } } if (tableSorts.isEmpty()) { // default to sorting on the first column HeaderField field = savedColumns().iterator().next(); SortSpec sortSpec = new SortSpec(field.getSqlName(), SortDirection.ASCENDING); tableSorts = new ArrayList(); tableSorts.add(sortSpec); } } private boolean isAttributeSelected(String attribute) { for (HeaderField field : savedColumns()) { if (field.getSqlName().equals(attribute)) { return true; } } return false; } @Override public void refresh() { createTable(); JSONObject condition = commonPanel.getConditionArgs(); sqlConditionFilter.setAllParameters(condition); table.refresh(); } @Override public void doQuery() { if (savedColumns().isEmpty()) { NotifyManager.getInstance().showError("You must select columns"); return; } updateStateFromView(); refresh(); } @Override public void onRowClicked(int rowIndex, JSONObject row, boolean isRightClick) { Event event = Event.getCurrentEvent(); TestSet testSet = getTestSet(row); if (isRightClick) { if (selectionManager.getSelectedObjects().size() > 0) { testSet = getTestSet(selectionManager.getSelectedObjects()); } ContextMenu menu = getContextMenu(testSet); menu.showAtWindow(event.getClientX(), event.getClientY()); return; } if (isSelectEvent(event)) { selectionManager.toggleSelected(row); return; } HistoryToken historyToken; if (isAnyGroupingEnabled()) { historyToken = getDrilldownHistoryToken(testSet); } else { historyToken = listener.getSelectTestHistoryToken(testSet.getTestIndex()); } openHistoryToken(historyToken); } private ContextMenu getContextMenu(final TestSet testSet) { TestContextMenu menu = new TestContextMenu(testSet, listener); if (!menu.addViewDetailsIfSingleTest() && isAnyGroupingEnabled()) { menu.addItem("Drill down", new Command() { public void execute() { doDrilldown(testSet); } }); } menu.addLabelItems(); return menu; } private HistoryToken getDrilldownHistoryToken(TestSet testSet) { saveHistoryState(); commonPanel.refineCondition(testSet); selectColumnsByName(DEFAULT_COLUMNS); HistoryToken historyToken = getHistoryArguments(); restoreHistoryState(); return historyToken; } private void doDrilldown(TestSet testSet) { History.newItem(getDrilldownHistoryToken(testSet).toString()); } private TestSet getTestSet(JSONObject row) { if (!isAnyGroupingEnabled()) { return new SingleTestSet((int) row.get("test_idx").isNumber().doubleValue()); } ConditionTestSet testSet = new ConditionTestSet(commonPanel.getConditionArgs()); for (HeaderField field : savedColumns()) { if (isGroupField(field)) { continue; } String value = Utils.jsonToString(row.get(field.getSqlName())); testSet.addCondition(field.getSqlCondition(value)); } return testSet; } private TestSet getTestSet(Collection selectedObjects) { CompositeTestSet compositeSet = new CompositeTestSet(); for (JSONObject row : selectedObjects) { compositeSet.add(getTestSet(row)); } return compositeSet; } public void onTableRefreshed() { selectionManager.refreshSelection(); saveTableSorting(); updateHistory(); } private void setCheckboxesEnabled() { assert !(groupCheckbox.getValue() && statusGroupCheckbox.getValue()); groupCheckbox.setEnabled(true); statusGroupCheckbox.setEnabled(true); if (groupCheckbox.getValue()) { statusGroupCheckbox.setEnabled(false); } else if (statusGroupCheckbox.getValue()) { groupCheckbox.setEnabled(false); } } private void updateFieldsFromCheckboxes() { columnSelect.deselectItemInView(groupCountField); columnSelect.deselectItemInView(statusCountsField); if (groupCheckbox.getValue()) { columnSelect.selectItemInView(groupCountField); } else if (statusGroupCheckbox.getValue()) { columnSelect.selectItemInView(statusCountsField); } } private void updateCheckboxesFromFields() { groupCheckbox.setValue(false); statusGroupCheckbox.setValue(false); GroupingType grouping = getGroupingFromFields( columnSelect.getStateFromView().getSelectedFields()); if (grouping == GroupingType.TEST_GROUPING) { groupCheckbox.setValue(true); } else if (grouping == GroupingType.STATUS_COUNTS) { statusGroupCheckbox.setValue(true); } setCheckboxesEnabled(); } public ContextMenu getActionMenu() { TestSet tests; if (selectionManager.isEmpty()) { tests = getWholeTableSet(); } else { tests = getTestSet(selectionManager.getSelectedObjects()); } return getContextMenu(tests); } private ConditionTestSet getWholeTableSet() { return new ConditionTestSet(commonPanel.getConditionArgs()); } @Override public HistoryToken getHistoryArguments() { HistoryToken arguments = super.getHistoryArguments(); if (table != null) { columnSelect.addHistoryArguments(arguments, "columns"); arguments.put("sort", Utils.joinStrings(",", tableSorts)); commonPanel.addHistoryArguments(arguments); } return arguments; } @Override public void handleHistoryArguments(Map arguments) { super.handleHistoryArguments(arguments); columnSelect.handleHistoryArguments(arguments, "columns"); handleSortString(arguments.get("sort")); updateViewFromState(); } @Override protected void fillDefaultHistoryValues(Map arguments) { HeaderField defaultSortField = headerFields.getFieldByName(DEFAULT_COLUMNS[0]); Utils.setDefaultValue(arguments, "sort", defaultSortField.getSqlName()); Utils.setDefaultValue(arguments, "columns", Utils.joinStrings(",", Arrays.asList(DEFAULT_COLUMNS))); } private void handleSortString(String sortString) { tableSorts.clear(); String[] components = sortString.split(","); for (String component : components) { tableSorts.add(SortSpec.fromString(component)); } } public void onClick(ClickEvent event) { if (event.getSource() == queryButton) { doQueryWithCommonPanelCheck(); updateHistory(); } else if (event.getSource() == groupCheckbox || event.getSource() == statusGroupCheckbox) { updateFieldsFromCheckboxes(); setCheckboxesEnabled(); } } @Override public void onRemoveGeneratedItem(Item generatedItem) { updateCheckboxesFromFields(); } private boolean isAnyGroupingEnabled() { return getActiveGrouping() != GroupingType.NO_GROUPING; } private GroupingType getGroupingFromFields(List fields) { for (HeaderField field : fields) { if (field.getName().equals(COUNT_NAME)) { return GroupingType.TEST_GROUPING; } if (field.getName().equals(STATUS_COUNTS_NAME)) { return GroupingType.STATUS_COUNTS; } } return GroupingType.NO_GROUPING; } /** * Get grouping currently active for displayed table. */ private GroupingType getActiveGrouping() { return getGroupingFromFields(savedColumns()); } public Widget createWidget(int row, int cell, JSONObject rowObject) { assert getActiveGrouping() == GroupingType.STATUS_COUNTS; StatusSummary statusSummary = StatusSummary.getStatusSummary( rowObject, TestGroupDataSource.PASS_COUNT_FIELD, TestGroupDataSource.COMPLETE_COUNT_FIELD, TestGroupDataSource.INCOMPLETE_COUNT_FIELD, TestGroupDataSource.GROUP_COUNT_FIELD); SimplePanel panel = new SimplePanel(); panel.add(new HTML(statusSummary.formatContents())); panel.getElement().addClassName(statusSummary.getCssClass()); return panel; } @Override protected boolean hasFirstQueryOccurred() { return table != null; } @Override public void onSetControlsVisible(boolean visible) { TkoUtils.setElementVisible("table_all_controls", visible); } @Override public void onFieldsChanged() { columnSelect.refreshFields(); } public void onExportCsv() { JSONObject extraParams = new JSONObject(); extraParams.put("columns", buildCsvColumnSpecs()); TkoUtils.doCsvRequest((RpcDataSource) table.getDataSource(), table.getCurrentQuery(), extraParams); } private JSONArray buildCsvColumnSpecs() { String[][] columnSpecs = buildColumnSpecs(); JSONArray jsonColumnSpecs = new JSONArray(); for (String[] columnSpec : columnSpecs) { JSONArray jsonColumnSpec = Utils.stringsToJSON(Arrays.asList(columnSpec)); jsonColumnSpecs.set(jsonColumnSpecs.size(), jsonColumnSpec); } return jsonColumnSpecs; } }