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