| 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<SortSpec> tableSorts = new ArrayList<SortSpec>(); |
| |
| 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<HeaderField> fields = new ArrayList<HeaderField>(); |
| 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<SortSpec>(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<HeaderField> 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<String> groupFields = new ArrayList<String>(); |
| 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<SortSpec>(table.getSortSpecs()); |
| } |
| } |
| |
| private void restoreTableSorting() { |
| for (ListIterator<SortSpec> 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<SortSpec> 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<SortSpec>(); |
| 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<JSONObject> 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<String, String> arguments) { |
| super.handleHistoryArguments(arguments); |
| columnSelect.handleHistoryArguments(arguments, "columns"); |
| handleSortString(arguments.get("sort")); |
| updateViewFromState(); |
| } |
| |
| @Override |
| protected void fillDefaultHistoryValues(Map<String, String> 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<HeaderField> 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; |
| } |
| } |