Home | History | Annotate | Download | only in tko
      1 package autotest.tko;
      2 
      3 import autotest.common.StatusSummary;
      4 import autotest.common.Utils;
      5 import autotest.common.CustomHistory.HistoryToken;
      6 import autotest.common.table.DataTable;
      7 import autotest.common.table.DynamicTable;
      8 import autotest.common.table.RpcDataSource;
      9 import autotest.common.table.SelectionManager;
     10 import autotest.common.table.SimpleFilter;
     11 import autotest.common.table.TableDecorator;
     12 import autotest.common.table.DataSource.SortDirection;
     13 import autotest.common.table.DataSource.SortSpec;
     14 import autotest.common.table.DataTable.TableWidgetFactory;
     15 import autotest.common.table.DynamicTable.DynamicTableListener;
     16 import autotest.common.ui.ContextMenu;
     17 import autotest.common.ui.DoubleListSelector;
     18 import autotest.common.ui.MultiListSelectPresenter;
     19 import autotest.common.ui.NotifyManager;
     20 import autotest.common.ui.MultiListSelectPresenter.Item;
     21 import autotest.common.ui.TableActionsPanel.TableActionsWithExportCsvListener;
     22 import autotest.tko.CommonPanel.CommonPanelListener;
     23 
     24 import com.google.gwt.event.dom.client.ClickEvent;
     25 import com.google.gwt.event.dom.client.ClickHandler;
     26 import com.google.gwt.json.client.JSONArray;
     27 import com.google.gwt.json.client.JSONObject;
     28 import com.google.gwt.user.client.Command;
     29 import com.google.gwt.user.client.Event;
     30 import com.google.gwt.user.client.History;
     31 import com.google.gwt.user.client.ui.Button;
     32 import com.google.gwt.user.client.ui.CheckBox;
     33 import com.google.gwt.user.client.ui.HTML;
     34 import com.google.gwt.user.client.ui.Panel;
     35 import com.google.gwt.user.client.ui.SimplePanel;
     36 import com.google.gwt.user.client.ui.VerticalPanel;
     37 import com.google.gwt.user.client.ui.Widget;
     38 
     39 import java.util.ArrayList;
     40 import java.util.Arrays;
     41 import java.util.Collection;
     42 import java.util.Iterator;
     43 import java.util.List;
     44 import java.util.ListIterator;
     45 import java.util.Map;
     46 
     47 public class TableView extends ConditionTabView
     48                        implements DynamicTableListener, TableActionsWithExportCsvListener,
     49                                   ClickHandler, TableWidgetFactory, CommonPanelListener,
     50                                   MultiListSelectPresenter.GeneratorHandler {
     51     private static final int ROWS_PER_PAGE = 30;
     52     private static final String COUNT_NAME = "Count in group";
     53     private static final String STATUS_COUNTS_NAME = "Test pass rate";
     54     private static final String[] DEFAULT_COLUMNS =
     55         {"Test index", "Test name", "Job tag", "Hostname", "Status"};
     56     private static final String[] TRIAGE_GROUP_COLUMNS =
     57         {"Test name", "Status", COUNT_NAME, "Reason"};
     58     private static final String[] PASS_RATE_GROUP_COLUMNS =
     59         {"Hostname", STATUS_COUNTS_NAME};
     60     private static final SortSpec[] TRIAGE_SORT_SPECS = {
     61         new SortSpec("test_name", SortDirection.ASCENDING),
     62         new SortSpec("status", SortDirection.ASCENDING),
     63         new SortSpec("reason", SortDirection.ASCENDING),
     64     };
     65 
     66     private static enum GroupingType {NO_GROUPING, TEST_GROUPING, STATUS_COUNTS}
     67 
     68     /**
     69      * HeaderField representing a grouped count of some kind.
     70      */
     71     private static class GroupCountField extends HeaderField {
     72         public GroupCountField(String name, String sqlName) {
     73             super(name, sqlName);
     74         }
     75 
     76         @Override
     77         public Item getItem() {
     78             return Item.createGeneratedItem(getName(), getSqlName());
     79         }
     80 
     81         @Override
     82         public String getSqlCondition(String value) {
     83             throw new UnsupportedOperationException();
     84         }
     85 
     86         @Override
     87         public boolean isUserSelectable() {
     88             return false;
     89         }
     90     }
     91 
     92     private GroupCountField groupCountField =
     93         new GroupCountField(COUNT_NAME, TestGroupDataSource.GROUP_COUNT_FIELD);
     94     private GroupCountField statusCountsField =
     95         new GroupCountField(STATUS_COUNTS_NAME, DataTable.WIDGET_COLUMN);
     96 
     97     private TestSelectionListener listener;
     98 
     99     private DynamicTable table;
    100     private TableDecorator tableDecorator;
    101     private SelectionManager selectionManager;
    102     private SimpleFilter sqlConditionFilter = new SimpleFilter();
    103     private RpcDataSource testDataSource = new TestViewDataSource();
    104     private TestGroupDataSource groupDataSource = TestGroupDataSource.getTestGroupDataSource();
    105 
    106     private HeaderFieldCollection headerFields = commonPanel.getHeaderFields();
    107     private HeaderSelect columnSelect = new HeaderSelect(headerFields, new HeaderSelect.State());
    108 
    109     private DoubleListSelector columnSelectDisplay = new DoubleListSelector();
    110     private CheckBox groupCheckbox = new CheckBox("Group by these columns and show counts");
    111     private CheckBox statusGroupCheckbox =
    112         new CheckBox("Group by these columns and show pass rates");
    113     private Button queryButton = new Button("Query");
    114     private Panel tablePanel = new SimplePanel();
    115 
    116     private List<SortSpec> tableSorts = new ArrayList<SortSpec>();
    117 
    118     public enum TableViewConfig {
    119         DEFAULT, PASS_RATE, TRIAGE
    120     }
    121 
    122     public static interface TableSwitchListener extends TestSelectionListener {
    123         public void onSwitchToTable(TableViewConfig config);
    124     }
    125 
    126     public TableView(TestSelectionListener listener) {
    127         this.listener = listener;
    128         commonPanel.addListener(this);
    129         columnSelect.setGeneratorHandler(this);
    130         columnSelect.bindDisplay(columnSelectDisplay);
    131     }
    132 
    133     @Override
    134     public String getElementId() {
    135         return "table_view";
    136     }
    137 
    138     @Override
    139     public void initialize() {
    140         super.initialize();
    141 
    142         headerFields.add(groupCountField);
    143         headerFields.add(statusCountsField);
    144 
    145         selectColumnsByName(DEFAULT_COLUMNS);
    146         updateViewFromState();
    147 
    148         queryButton.addClickHandler(this);
    149         groupCheckbox.addClickHandler(this);
    150         statusGroupCheckbox.addClickHandler(this);
    151 
    152         Panel columnPanel = new VerticalPanel();
    153         columnPanel.add(columnSelectDisplay);
    154         columnPanel.add(groupCheckbox);
    155         columnPanel.add(statusGroupCheckbox);
    156 
    157         addWidget(columnPanel, "table_column_select");
    158         addWidget(queryButton, "table_query_controls");
    159         addWidget(tablePanel, "table_table");
    160     }
    161 
    162     private void selectColumnsByName(String[] columnNames) {
    163         List<HeaderField> fields = new ArrayList<HeaderField>();
    164         for (String name : columnNames) {
    165             fields.add(headerFields.getFieldByName(name));
    166         }
    167         columnSelect.setSelectedItems(fields);
    168         cleanupSortsForNewColumns();
    169     }
    170 
    171     public void setupDefaultView() {
    172         tableSorts.clear();
    173         selectColumnsByName(DEFAULT_COLUMNS);
    174         updateViewFromState();
    175     }
    176 
    177     public void setupJobTriage() {
    178         selectColumnsByName(TRIAGE_GROUP_COLUMNS);
    179         // need to copy it so we can mutate it
    180         tableSorts = new ArrayList<SortSpec>(Arrays.asList(TRIAGE_SORT_SPECS));
    181         updateViewFromState();
    182     }
    183 
    184     public void setupPassRate() {
    185         tableSorts.clear();
    186         selectColumnsByName(PASS_RATE_GROUP_COLUMNS);
    187         updateViewFromState();
    188     }
    189 
    190     private void createTable() {
    191         String[][] columns = buildColumnSpecs();
    192 
    193         table = new DynamicTable(columns, getDataSource());
    194         table.addFilter(sqlConditionFilter);
    195         table.setRowsPerPage(ROWS_PER_PAGE);
    196         table.makeClientSortable();
    197         table.setClickable(true);
    198         table.sinkRightClickEvents();
    199         table.addListener(this);
    200         table.setWidgetFactory(this);
    201         restoreTableSorting();
    202 
    203         tableDecorator = new TableDecorator(table);
    204         tableDecorator.addPaginators();
    205         selectionManager = tableDecorator.addSelectionManager(false);
    206         tableDecorator.addTableActionsWithExportCsvListener(this);
    207         tablePanel.clear();
    208         tablePanel.add(tableDecorator);
    209 
    210         selectionManager = new SelectionManager(table, false);
    211     }
    212 
    213     private String[][] buildColumnSpecs() {
    214         int numColumns = savedColumns().size();
    215         String[][] columns = new String[numColumns][2];
    216         int i = 0;
    217         for (HeaderField field : savedColumns()) {
    218             columns[i][0] = field.getSqlName();
    219             columns[i][1] = field.getName();
    220             i++;
    221         }
    222         return columns;
    223     }
    224 
    225     private List<HeaderField> savedColumns() {
    226         return columnSelect.getSelectedItems();
    227     }
    228 
    229     private RpcDataSource getDataSource() {
    230         GroupingType groupingType = getActiveGrouping();
    231         if (groupingType == GroupingType.NO_GROUPING) {
    232             return testDataSource;
    233         } else if (groupingType == GroupingType.TEST_GROUPING) {
    234             groupDataSource = TestGroupDataSource.getTestGroupDataSource();
    235         } else {
    236             groupDataSource = TestGroupDataSource.getStatusCountDataSource();
    237         }
    238 
    239         updateGroupColumns();
    240         return groupDataSource;
    241     }
    242 
    243     private void updateStateFromView() {
    244         commonPanel.updateStateFromView();
    245         columnSelect.updateStateFromView();
    246     }
    247 
    248     private void updateViewFromState() {
    249         commonPanel.updateViewFromState();
    250         columnSelect.updateViewFromState();
    251     }
    252 
    253     private void updateGroupColumns() {
    254         List<String> groupFields = new ArrayList<String>();
    255         for (HeaderField field : savedColumns()) {
    256             if (!isGroupField(field)) {
    257                 groupFields.add(field.getSqlName());
    258             }
    259         }
    260 
    261         groupDataSource.setGroupColumns(groupFields.toArray(new String[0]));
    262     }
    263 
    264     private boolean isGroupField(HeaderField field) {
    265         return field instanceof GroupCountField;
    266     }
    267 
    268     private void saveTableSorting() {
    269         if (table != null) {
    270             // we need our own copy so we can modify it later
    271             tableSorts = new ArrayList<SortSpec>(table.getSortSpecs());
    272         }
    273     }
    274 
    275     private void restoreTableSorting() {
    276         for (ListIterator<SortSpec> i = tableSorts.listIterator(tableSorts.size());
    277              i.hasPrevious();) {
    278             SortSpec sortSpec = i.previous();
    279             table.sortOnColumn(sortSpec.getField(), sortSpec.getDirection());
    280         }
    281     }
    282 
    283     private void cleanupSortsForNewColumns() {
    284         // remove sorts on columns that we no longer have
    285         for (Iterator<SortSpec> i = tableSorts.iterator(); i.hasNext();) {
    286             String attribute = i.next().getField();
    287             if (!isAttributeSelected(attribute)) {
    288                 i.remove();
    289             }
    290         }
    291 
    292         if (tableSorts.isEmpty()) {
    293             // default to sorting on the first column
    294             HeaderField field = savedColumns().iterator().next();
    295             SortSpec sortSpec = new SortSpec(field.getSqlName(), SortDirection.ASCENDING);
    296             tableSorts = new ArrayList<SortSpec>();
    297             tableSorts.add(sortSpec);
    298         }
    299     }
    300 
    301     private boolean isAttributeSelected(String attribute) {
    302         for (HeaderField field : savedColumns()) {
    303             if (field.getSqlName().equals(attribute)) {
    304                 return true;
    305             }
    306         }
    307         return false;
    308     }
    309 
    310     @Override
    311     public void refresh() {
    312         createTable();
    313         JSONObject condition = commonPanel.getConditionArgs();
    314         sqlConditionFilter.setAllParameters(condition);
    315         table.refresh();
    316     }
    317 
    318     @Override
    319     public void doQuery() {
    320         if (savedColumns().isEmpty()) {
    321             NotifyManager.getInstance().showError("You must select columns");
    322             return;
    323         }
    324         updateStateFromView();
    325         refresh();
    326     }
    327 
    328     @Override
    329     public void onRowClicked(int rowIndex, JSONObject row, boolean isRightClick) {
    330         Event event = Event.getCurrentEvent();
    331         TestSet testSet = getTestSet(row);
    332         if (isRightClick) {
    333             if (selectionManager.getSelectedObjects().size() > 0) {
    334                 testSet = getTestSet(selectionManager.getSelectedObjects());
    335             }
    336             ContextMenu menu = getContextMenu(testSet);
    337             menu.showAtWindow(event.getClientX(), event.getClientY());
    338             return;
    339         }
    340 
    341         if (isSelectEvent(event)) {
    342             selectionManager.toggleSelected(row);
    343             return;
    344         }
    345 
    346         HistoryToken historyToken;
    347         if (isAnyGroupingEnabled()) {
    348             historyToken = getDrilldownHistoryToken(testSet);
    349         } else {
    350             historyToken = listener.getSelectTestHistoryToken(testSet.getTestIndex());
    351         }
    352         openHistoryToken(historyToken);
    353     }
    354 
    355     private ContextMenu getContextMenu(final TestSet testSet) {
    356         TestContextMenu menu = new TestContextMenu(testSet, listener);
    357 
    358         if (!menu.addViewDetailsIfSingleTest() && isAnyGroupingEnabled()) {
    359             menu.addItem("Drill down", new Command() {
    360                 public void execute() {
    361                     doDrilldown(testSet);
    362                 }
    363             });
    364         }
    365 
    366         menu.addLabelItems();
    367         return menu;
    368     }
    369 
    370     private HistoryToken getDrilldownHistoryToken(TestSet testSet) {
    371         saveHistoryState();
    372         commonPanel.refineCondition(testSet);
    373         selectColumnsByName(DEFAULT_COLUMNS);
    374         HistoryToken historyToken = getHistoryArguments();
    375         restoreHistoryState();
    376         return historyToken;
    377     }
    378 
    379     private void doDrilldown(TestSet testSet) {
    380         History.newItem(getDrilldownHistoryToken(testSet).toString());
    381     }
    382 
    383     private TestSet getTestSet(JSONObject row) {
    384         if (!isAnyGroupingEnabled()) {
    385             return new SingleTestSet((int) row.get("test_idx").isNumber().doubleValue());
    386         }
    387 
    388         ConditionTestSet testSet = new ConditionTestSet(commonPanel.getConditionArgs());
    389         for (HeaderField field : savedColumns()) {
    390             if (isGroupField(field)) {
    391                 continue;
    392             }
    393 
    394             String value = Utils.jsonToString(row.get(field.getSqlName()));
    395             testSet.addCondition(field.getSqlCondition(value));
    396         }
    397         return testSet;
    398     }
    399 
    400     private TestSet getTestSet(Collection<JSONObject> selectedObjects) {
    401         CompositeTestSet compositeSet = new CompositeTestSet();
    402         for (JSONObject row : selectedObjects) {
    403             compositeSet.add(getTestSet(row));
    404         }
    405         return compositeSet;
    406     }
    407 
    408     public void onTableRefreshed() {
    409         selectionManager.refreshSelection();
    410         saveTableSorting();
    411         updateHistory();
    412     }
    413 
    414     private void setCheckboxesEnabled() {
    415         assert !(groupCheckbox.getValue() && statusGroupCheckbox.getValue());
    416 
    417         groupCheckbox.setEnabled(true);
    418         statusGroupCheckbox.setEnabled(true);
    419         if (groupCheckbox.getValue()) {
    420             statusGroupCheckbox.setEnabled(false);
    421         } else if (statusGroupCheckbox.getValue()) {
    422             groupCheckbox.setEnabled(false);
    423         }
    424     }
    425 
    426     private void updateFieldsFromCheckboxes() {
    427         columnSelect.deselectItemInView(groupCountField);
    428         columnSelect.deselectItemInView(statusCountsField);
    429 
    430         if (groupCheckbox.getValue()) {
    431             columnSelect.selectItemInView(groupCountField);
    432         } else if (statusGroupCheckbox.getValue()) {
    433             columnSelect.selectItemInView(statusCountsField);
    434         }
    435     }
    436 
    437     private void updateCheckboxesFromFields() {
    438         groupCheckbox.setValue(false);
    439         statusGroupCheckbox.setValue(false);
    440 
    441         GroupingType grouping = getGroupingFromFields(
    442             columnSelect.getStateFromView().getSelectedFields());
    443         if (grouping == GroupingType.TEST_GROUPING) {
    444             groupCheckbox.setValue(true);
    445         } else if (grouping == GroupingType.STATUS_COUNTS) {
    446             statusGroupCheckbox.setValue(true);
    447         }
    448 
    449         setCheckboxesEnabled();
    450     }
    451 
    452     public ContextMenu getActionMenu() {
    453         TestSet tests;
    454         if (selectionManager.isEmpty()) {
    455             tests = getWholeTableSet();
    456         } else {
    457             tests = getTestSet(selectionManager.getSelectedObjects());
    458         }
    459         return getContextMenu(tests);
    460     }
    461 
    462     private ConditionTestSet getWholeTableSet() {
    463         return new ConditionTestSet(commonPanel.getConditionArgs());
    464     }
    465 
    466     @Override
    467     public HistoryToken getHistoryArguments() {
    468         HistoryToken arguments = super.getHistoryArguments();
    469         if (table != null) {
    470             columnSelect.addHistoryArguments(arguments, "columns");
    471             arguments.put("sort", Utils.joinStrings(",", tableSorts));
    472             commonPanel.addHistoryArguments(arguments);
    473         }
    474         return arguments;
    475     }
    476 
    477     @Override
    478     public void handleHistoryArguments(Map<String, String> arguments) {
    479         super.handleHistoryArguments(arguments);
    480         columnSelect.handleHistoryArguments(arguments, "columns");
    481         handleSortString(arguments.get("sort"));
    482         updateViewFromState();
    483     }
    484 
    485     @Override
    486     protected void fillDefaultHistoryValues(Map<String, String> arguments) {
    487         HeaderField defaultSortField = headerFields.getFieldByName(DEFAULT_COLUMNS[0]);
    488         Utils.setDefaultValue(arguments, "sort", defaultSortField.getSqlName());
    489         Utils.setDefaultValue(arguments, "columns",
    490                         Utils.joinStrings(",", Arrays.asList(DEFAULT_COLUMNS)));
    491     }
    492 
    493     private void handleSortString(String sortString) {
    494         tableSorts.clear();
    495         String[] components = sortString.split(",");
    496         for (String component : components) {
    497             tableSorts.add(SortSpec.fromString(component));
    498         }
    499     }
    500 
    501     public void onClick(ClickEvent event) {
    502         if (event.getSource() == queryButton) {
    503             doQueryWithCommonPanelCheck();
    504             updateHistory();
    505         } else if (event.getSource() == groupCheckbox || event.getSource() == statusGroupCheckbox) {
    506             updateFieldsFromCheckboxes();
    507             setCheckboxesEnabled();
    508         }
    509     }
    510 
    511     @Override
    512     public void onRemoveGeneratedItem(Item generatedItem) {
    513         updateCheckboxesFromFields();
    514     }
    515 
    516     private boolean isAnyGroupingEnabled() {
    517         return getActiveGrouping() != GroupingType.NO_GROUPING;
    518     }
    519 
    520     private GroupingType getGroupingFromFields(List<HeaderField> fields) {
    521         for (HeaderField field : fields) {
    522             if (field.getName().equals(COUNT_NAME)) {
    523                 return GroupingType.TEST_GROUPING;
    524             }
    525             if (field.getName().equals(STATUS_COUNTS_NAME)) {
    526                 return GroupingType.STATUS_COUNTS;
    527             }
    528         }
    529         return GroupingType.NO_GROUPING;
    530     }
    531 
    532     /**
    533      * Get grouping currently active for displayed table.
    534      */
    535     private GroupingType getActiveGrouping() {
    536         return getGroupingFromFields(savedColumns());
    537     }
    538 
    539     public Widget createWidget(int row, int cell, JSONObject rowObject) {
    540         assert getActiveGrouping() == GroupingType.STATUS_COUNTS;
    541         StatusSummary statusSummary = StatusSummary.getStatusSummary(
    542             rowObject,
    543             TestGroupDataSource.PASS_COUNT_FIELD,
    544             TestGroupDataSource.COMPLETE_COUNT_FIELD,
    545             TestGroupDataSource.INCOMPLETE_COUNT_FIELD,
    546             TestGroupDataSource.GROUP_COUNT_FIELD);
    547         SimplePanel panel = new SimplePanel();
    548         panel.add(new HTML(statusSummary.formatContents()));
    549         panel.getElement().addClassName(statusSummary.getCssClass());
    550         return panel;
    551     }
    552 
    553     @Override
    554     protected boolean hasFirstQueryOccurred() {
    555         return table != null;
    556     }
    557 
    558     @Override
    559     public void onSetControlsVisible(boolean visible) {
    560         TkoUtils.setElementVisible("table_all_controls", visible);
    561     }
    562 
    563     @Override
    564     public void onFieldsChanged() {
    565         columnSelect.refreshFields();
    566     }
    567 
    568     public void onExportCsv() {
    569         JSONObject extraParams = new JSONObject();
    570         extraParams.put("columns", buildCsvColumnSpecs());
    571         TkoUtils.doCsvRequest((RpcDataSource) table.getDataSource(), table.getCurrentQuery(),
    572                               extraParams);
    573     }
    574 
    575     private JSONArray buildCsvColumnSpecs() {
    576         String[][] columnSpecs = buildColumnSpecs();
    577         JSONArray jsonColumnSpecs = new JSONArray();
    578         for (String[] columnSpec : columnSpecs) {
    579             JSONArray jsonColumnSpec = Utils.stringsToJSON(Arrays.asList(columnSpec));
    580             jsonColumnSpecs.set(jsonColumnSpecs.size(), jsonColumnSpec);
    581         }
    582         return jsonColumnSpecs;
    583     }
    584 }
    585