package autotest.tko;
import autotest.common.JsonRpcCallback;
import autotest.common.JsonRpcProxy;
import autotest.common.Utils;
import autotest.common.CustomHistory.HistoryToken;
import autotest.common.spreadsheet.Spreadsheet;
import autotest.common.spreadsheet.SpreadsheetSelectionManager;
import autotest.common.spreadsheet.Spreadsheet.CellInfo;
import autotest.common.spreadsheet.Spreadsheet.SpreadsheetListener;
import autotest.common.ui.ContextMenu;
import autotest.common.ui.NotifyManager;
import autotest.common.ui.TableActionsPanel;
import autotest.common.ui.TableActionsPanel.TableActionsWithExportCsvListener;
import autotest.common.ui.TableSelectionPanel.SelectionPanelListener;
import autotest.tko.CommonPanel.CommonPanelListener;
import autotest.tko.TableView.TableSwitchListener;
import autotest.tko.TableView.TableViewConfig;
import autotest.tko.TkoSpreadsheetUtils.DrilldownType;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
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.Window;
import com.google.gwt.user.client.ui.Anchor;
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.MenuBar;
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 java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SpreadsheetView extends ConditionTabView
implements SpreadsheetListener, TableActionsWithExportCsvListener,
CommonPanelListener, SelectionPanelListener {
private static final String HISTORY_ONLY_LATEST = "show_only_latest";
public static final String DEFAULT_ROW = "kernel";
public static final String DEFAULT_COLUMN = "platform";
public static final String DEFAULT_DRILLDOWN = "job_tag";
private static final String HISTORY_SHOW_INCOMPLETE = "show_incomplete";
private static final String HISTORY_COLUMN = "column";
private static final String HISTORY_ROW = "row";
private static final String HISTORY_CONTENT = "content";
private static JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy();
private static JsonRpcProxy afeRpcProxy = JsonRpcProxy.getProxy(JsonRpcProxy.AFE_BASE_URL);
private TableSwitchListener listener;
protected Map<String,String[]> drilldownMap = new HashMap<String,String[]>();
private HeaderFieldCollection headerFields = commonPanel.getHeaderFields();
private SpreadsheetHeaderSelect rowSelect = new SpreadsheetHeaderSelect(headerFields);
private SpreadsheetHeaderSelectorView rowSelectDisplay = new SpreadsheetHeaderSelectorView();
private SpreadsheetHeaderSelect columnSelect = new SpreadsheetHeaderSelect(headerFields);
private SpreadsheetHeaderSelectorView columnSelectDisplay = new SpreadsheetHeaderSelectorView();
private ContentSelect contentSelect = new ContentSelect(headerFields);
private CheckBox showIncomplete = new CheckBox("Show incomplete tests");
private CheckBox showOnlyLatest = new CheckBox("Show only latest test per cell");
private Button queryButton = new Button("Query");
private TestGroupDataSource normalDataSource = TestGroupDataSource.getStatusCountDataSource();
private TestGroupDataSource latestDataSource = TestGroupDataSource.getLatestTestsDataSource();
private Spreadsheet spreadsheet = new Spreadsheet();
private SpreadsheetDataProcessor spreadsheetProcessor =
new SpreadsheetDataProcessor(spreadsheet);
private SpreadsheetSelectionManager selectionManager =
new SpreadsheetSelectionManager(spreadsheet, null);
private TableActionsPanel actionsPanel = new TableActionsPanel(false);
private Panel jobCompletionPanel = new SimplePanel();
private boolean currentShowIncomplete, currentShowOnlyLatest;
private boolean notYetQueried = true;
public SpreadsheetView(TableSwitchListener listener) {
this.listener = listener;
commonPanel.addListener(this);
rowSelect.bindDisplay(rowSelectDisplay);
columnSelect.bindDisplay(columnSelectDisplay);
}
@Override
public String getElementId() {
return "spreadsheet_view";
}
@Override
public void initialize() {
super.initialize();
setHeaderSelectField(rowSelect, DEFAULT_ROW);
setHeaderSelectField(columnSelect, DEFAULT_COLUMN);
actionsPanel.setActionsWithCsvListener(this);
actionsPanel.setSelectionListener(this);
actionsPanel.setVisible(false);
contentSelect.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
public void onValueChange(ValueChangeEvent<Boolean> event) {
if (event.getValue()) {
showOnlyLatest.setValue(true);
showOnlyLatest.setEnabled(false);
} else {
showOnlyLatest.setEnabled(true);
}
}
});
updateViewFromState();
queryButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
doQueryWithCommonPanelCheck();
updateHistory();
}
});
spreadsheet.setVisible(false);
spreadsheet.setListener(this);
Anchor swapLink = new Anchor("swap");
swapLink.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
SpreadsheetHeaderSelect.State rowState = rowSelect.getStateFromView();
rowSelect.loadFromState(columnSelect.getStateFromView());
columnSelect.loadFromState(rowState);
}
});
Panel filterOptions = new VerticalPanel();
filterOptions.add(showIncomplete);
filterOptions.add(showOnlyLatest);
addWidget(filterOptions, "ss_filter_options");
addWidget(rowSelectDisplay, "ss_row_select");
addWidget(columnSelectDisplay, "ss_column_select");
addWidget(contentSelect, "ss_additional_content");
addWidget(swapLink, "ss_swap");
addWidget(queryButton, "ss_query_controls");
addWidget(actionsPanel, "ss_actions");
addWidget(spreadsheet, "ss_spreadsheet");
addWidget(jobCompletionPanel, "ss_job_completion");
Window.addResizeHandler(new ResizeHandler() {
public void onResize(ResizeEvent event) {
if(spreadsheet.isVisible()) {
spreadsheet.fillWindow(true);
}
}
});
setupDrilldownMap();
}
private void setHeaderSelectField(SpreadsheetHeaderSelect headerSelect,
String defaultField) {
headerSelect.setSelectedItem(headerFields.getFieldBySqlName(defaultField));
}
protected TestSet getWholeTableTestSet() {
boolean isSingleTest = spreadsheetProcessor.getNumTotalTests() == 1;
if (isSingleTest) {
return getTestSet(spreadsheetProcessor.getLastCellInfo());
}
if (currentShowOnlyLatest) {
List<Integer> testIndices = spreadsheet.getAllTestIndices();
String filter = "test_idx IN (" + Utils.joinStrings(",", testIndices) + ")";
ConditionTestSet tests = new ConditionTestSet();
tests.addCondition(filter);
return tests;
}
return new ConditionTestSet(getFullConditionArgs());
}
protected void setupDrilldownMap() {
drilldownMap.put("platform", new String[] {"hostname", "test_name"});
drilldownMap.put("hostname", new String[] {"job_tag", "status"});
drilldownMap.put("kernel", new String[] {"test_name", "status"});
drilldownMap.put("test_name", new String[] {"subdir", "job_name", "job_tag"});
drilldownMap.put("status", new String[] {"reason", "job_tag"});
drilldownMap.put("job_owner", new String[] {"job_name", "job_tag"});
drilldownMap.put("test_finished_time", new String[] {"status", "job_tag"});
drilldownMap.put("DATE(test_finished_time)",
new String[] {"test_finished_time", "job_tag"});
drilldownMap.put("job_tag", new String[] {"subdir"});
}
protected void setSelectedHeader(HeaderSelect list, List<HeaderField> fields) {
list.setSelectedItems(fields);
}
@Override
public void refresh() {
notYetQueried = false;
actionsPanel.setVisible(true);
spreadsheet.setVisible(false);
selectionManager.clearSelection();
spreadsheet.clear();
setJobCompletionHtml(" ");
final JSONObject condition = getFullConditionArgs();
contentSelect.addToCondition(condition);
setLoading(true);
if (currentShowOnlyLatest) {
spreadsheetProcessor.setDataSource(latestDataSource);
} else {
spreadsheetProcessor.setDataSource(normalDataSource);
}
spreadsheetProcessor.setHeaders(rowSelect.getSelectedItems(),
columnSelect.getSelectedItems(),
getQueryParameters());
spreadsheetProcessor.refresh(condition, new Command() {
public void execute() {
condition.put("extra_info", null);
if (isJobFilteringCondition(condition)) {
showCompletionPercentage(condition);
} else {
setLoading(false);
}
}
});
}
private JSONObject getQueryParameters() {
JSONObject parameters = new JSONObject();
rowSelect.addQueryParameters(parameters);
columnSelect.addQueryParameters(parameters);
return parameters;
}
private JSONObject getFullConditionArgs() {
JSONObject args = commonPanel.getConditionArgs();
String condition = TkoUtils.getSqlCondition(args);
if (!condition.equals("")) {
condition = "(" + condition + ") AND ";
}
condition += "status != 'TEST_NA'";
if (!currentShowIncomplete) {
condition += " AND status != 'RUNNING'";
}
args.put("extra_where", new JSONString(condition));
return args;
}
private void updateStateFromView() {
rowSelect.updateStateFromView();
columnSelect.updateStateFromView();
currentShowIncomplete = showIncomplete.getValue();
currentShowOnlyLatest = showOnlyLatest.getValue();
commonPanel.updateStateFromView();
}
@Override
public void doQuery() {
List<HeaderField> rows = rowSelect.getSelectedItems();
List<HeaderField> columns = columnSelect.getSelectedItems();
if (rows.isEmpty() || columns.isEmpty()) {
NotifyManager.getInstance().showError("You must select row and column fields");
return;
}
updateStateFromView();
refresh();
}
private void showCompletionPercentage(JSONObject condition) {
rpcProxy.rpcCall("get_job_ids", condition, new JsonRpcCallback() {
@Override
public void onSuccess(JSONValue result) {
finishShowCompletionPercentage(result.isArray());
setLoading(false);
}
@Override
public void onError(JSONObject errorObject) {
super.onError(errorObject);
setLoading(false);
}
});
}
private void finishShowCompletionPercentage(JSONArray jobIds) {
final int jobCount = jobIds.size();
if (jobCount == 0) {
return;
}
JSONObject args = new JSONObject();
args.put("job__id__in", jobIds);
afeRpcProxy.rpcCall("get_hqe_percentage_complete", args, new JsonRpcCallback() {
@Override
public void onSuccess(JSONValue result) {
int percentage = (int) (result.isNumber().doubleValue() * 100);
StringBuilder message = new StringBuilder("Matching ");
if (jobCount == 1) {
message.append("job is ");
} else {
message.append("jobs are ");
}
message.append(percentage);
message.append("% complete");
setJobCompletionHtml(message.toString());
}
});
}
private void setJobCompletionHtml(String html) {
jobCompletionPanel.clear();
jobCompletionPanel.add(new HTML(html));
}
private boolean isJobFilteringCondition(JSONObject condition) {
return TkoUtils.getSqlCondition(condition).indexOf("job_tag") != -1;
}
@Override
public void onCellClicked(CellInfo cellInfo, boolean isRightClick) {
Event event = Event.getCurrentEvent();
TestSet testSet = getTestSet(cellInfo);
DrilldownType drilldownType = TkoSpreadsheetUtils.getDrilldownType(cellInfo);
if (isRightClick) {
if (!selectionManager.isEmpty()) {
testSet = getTestSet(selectionManager.getSelectedCells());
drilldownType = DrilldownType.DRILLDOWN_BOTH;
}
ContextMenu menu = getContextMenu(testSet, drilldownType);
menu.showAtWindow(event.getClientX(), event.getClientY());
return;
}
if (isSelectEvent(event)) {
selectionManager.toggleSelected(cellInfo);
return;
}
HistoryToken historyToken;
if (testSet.isSingleTest()) {
historyToken = listener.getSelectTestHistoryToken(testSet.getTestIndex());
} else {
historyToken = getDrilldownHistoryToken(testSet,
getDefaultDrilldownRow(drilldownType),
getDefaultDrilldownColumn(drilldownType));
}
openHistoryToken(historyToken);
}
private TestSet getTestSet(CellInfo cellInfo) {
return TkoSpreadsheetUtils.getTestSet(cellInfo, getFullConditionArgs(),
rowSelect.getSelectedItems(), columnSelect.getSelectedItems());
}
private TestSet getTestSet(List<CellInfo> cells) {
CompositeTestSet tests = new CompositeTestSet();
for (CellInfo cell : cells) {
tests.add(getTestSet(cell));
}
return tests;
}
private HistoryToken getDrilldownHistoryToken(TestSet tests, String newRowField,
String newColumnField) {
saveHistoryState();
commonPanel.refineCondition(tests);
rowSelect.setSelectedItem(headerFields.getFieldBySqlName(newRowField));
columnSelect.setSelectedItem(headerFields.getFieldBySqlName(newColumnField));
HistoryToken historyArguments = getHistoryArguments();
restoreHistoryState();
return historyArguments;
}
private void doDrilldown(TestSet tests, String newRowField, String newColumnField) {
History.newItem(getDrilldownHistoryToken(tests, newRowField, newColumnField).toString());
}
private String getDefaultDrilldownRow(DrilldownType type) {
return getDrilldownRows(type)[0];
}
private String getDefaultDrilldownColumn(DrilldownType type) {
return getDrilldownColumns(type)[0];
}
private ContextMenu getContextMenu(final TestSet tests, DrilldownType drilldownType) {
TestContextMenu menu = new TestContextMenu(tests, listener);
if (!menu.addViewDetailsIfSingleTest()) {
MenuBar drilldownMenu = menu.addSubMenuItem("Drill down");
fillDrilldownMenu(tests, drilldownType, drilldownMenu);
}
menu.addItem("View in table", new Command() {
public void execute() {
switchToTable(tests, false);
}
});
menu.addItem("Triage failures", new Command() {
public void execute() {
switchToTable(tests, true);
}
});
menu.addLabelItems();
return menu;
}
private void fillDrilldownMenu(final TestSet tests, DrilldownType drilldownType, MenuBar menu) {
for (final String rowField : getDrilldownRows(drilldownType)) {
for (final String columnField : getDrilldownColumns(drilldownType)) {
if (rowField.equals(columnField)) {
continue;
}
menu.addItem(rowField + " vs. " + columnField, new Command() {
public void execute() {
doDrilldown(tests, rowField, columnField);
}
});
}
}
}
private String[] getDrilldownFields(List<HeaderField> fields, DrilldownType type,
DrilldownType otherType) {
HeaderField lastField = fields.get(fields.size() - 1);
String lastFieldName = lastField.getSqlName();
if (type == otherType) {
return new String[] {lastFieldName};
} else {
if (lastField instanceof MachineLabelField) {
// treat machine label fields like platform, for the purpose of default drilldown
lastFieldName = "platform";
}
if (drilldownMap.containsKey(lastFieldName)) {
return drilldownMap.get(lastFieldName);
}
return new String[] {DEFAULT_DRILLDOWN};
}
}
private String[] getDrilldownRows(DrilldownType type) {
return getDrilldownFields(rowSelect.getSelectedItems(), type,
DrilldownType.DRILLDOWN_COLUMN);
}
private String[] getDrilldownColumns(DrilldownType type) {
return getDrilldownFields(columnSelect.getSelectedItems(), type,
DrilldownType.DRILLDOWN_ROW);
}
private void updateViewFromState() {
rowSelect.updateViewFromState();
columnSelect.updateViewFromState();
showIncomplete.setValue(currentShowIncomplete);
showOnlyLatest.setValue(currentShowOnlyLatest);
commonPanel.updateViewFromState();
}
@Override
public HistoryToken getHistoryArguments() {
HistoryToken arguments = super.getHistoryArguments();
if (!notYetQueried) {
rowSelect.addHistoryArguments(arguments, HISTORY_ROW);
columnSelect.addHistoryArguments(arguments, HISTORY_COLUMN);
contentSelect.addHistoryArguments(arguments, HISTORY_CONTENT);
arguments.put(HISTORY_SHOW_INCOMPLETE, Boolean.toString(currentShowIncomplete));
arguments.put(HISTORY_ONLY_LATEST, Boolean.toString(showOnlyLatest.getValue()));
commonPanel.addHistoryArguments(arguments);
}
return arguments;
}
@Override
public void handleHistoryArguments(Map<String, String> arguments) {
super.handleHistoryArguments(arguments);
commonPanel.handleHistoryArguments(arguments);
rowSelect.handleHistoryArguments(arguments, HISTORY_ROW);
columnSelect.handleHistoryArguments(arguments, HISTORY_COLUMN);
contentSelect.handleHistoryArguments(arguments, HISTORY_CONTENT);
currentShowIncomplete = Boolean.valueOf(arguments.get(HISTORY_SHOW_INCOMPLETE));
currentShowOnlyLatest = Boolean.valueOf(arguments.get(HISTORY_ONLY_LATEST));
updateViewFromState();
}
@Override
protected void fillDefaultHistoryValues(Map<String, String> arguments) {
Utils.setDefaultValue(arguments, HISTORY_ROW, DEFAULT_ROW);
Utils.setDefaultValue(arguments, HISTORY_COLUMN, DEFAULT_COLUMN);
Utils.setDefaultValue(arguments,
HISTORY_ROW + SpreadsheetHeaderSelect.HISTORY_FIXED_VALUES, "");
Utils.setDefaultValue(arguments,
HISTORY_COLUMN + SpreadsheetHeaderSelect.HISTORY_FIXED_VALUES, "");
Utils.setDefaultValue(arguments, HISTORY_SHOW_INCOMPLETE, Boolean.toString(false));
Utils.setDefaultValue(arguments, HISTORY_ONLY_LATEST, Boolean.toString(false));
}
private void switchToTable(final TestSet tests, boolean isTriageView) {
commonPanel.refineCondition(tests);
TableViewConfig config;
if (isTriageView) {
config = TableViewConfig.TRIAGE;
refineConditionForTriage();
} else {
config = TableViewConfig.DEFAULT;
}
listener.onSwitchToTable(config);
}
private void refineConditionForTriage() {
commonPanel.refineCondition("status != 'GOOD'");
}
public ContextMenu getActionMenu() {
TestSet tests;
if (selectionManager.isEmpty()) {
tests = getWholeTableTestSet();
} else {
tests = getTestSet(selectionManager.getSelectedCells());
}
return getContextMenu(tests, DrilldownType.DRILLDOWN_BOTH);
}
public void onExportCsv() {
JSONObject params = new JSONObject();
contentSelect.addToCondition(params);
TkoUtils.doCsvRequest(spreadsheetProcessor.getDataSource(),
spreadsheetProcessor.getCurrentQuery(), params);
}
public void onSelectAll(boolean ignored) {
selectionManager.selectAll();
}
public void onSelectNone() {
selectionManager.clearSelection();
}
@Override
protected boolean hasFirstQueryOccurred() {
return !notYetQueried;
}
private void setLoading(boolean loading) {
queryButton.setEnabled(!loading);
NotifyManager.getInstance().setLoading(loading);
}
@Override
public void onSetControlsVisible(boolean visible) {
TkoUtils.setElementVisible("ss_all_controls", visible);
if (isTabVisible()) {
spreadsheet.fillWindow(true);
}
}
@Override
public void onFieldsChanged() {
rowSelect.refreshFields();
columnSelect.refreshFields();
contentSelect.refreshFields();
}
}