package com.griddynamics.jagger.webclient.client.components;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.i18n.shared.DateTimeFormat;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.griddynamics.jagger.dbapi.dto.SessionDataDto;
import com.griddynamics.jagger.dbapi.dto.*;
import com.griddynamics.jagger.dbapi.model.MetricNode;
import com.griddynamics.jagger.dbapi.model.MetricRankingProvider;
import com.griddynamics.jagger.dbapi.model.WebClientProperties;
import com.griddynamics.jagger.webclient.client.SessionDataService;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.griddynamics.jagger.dbapi.dto.SummaryIntegratedDto;
import com.griddynamics.jagger.webclient.client.resources.JaggerResources;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.TreeStore;
import com.sencha.gxt.data.shared.event.StoreAddEvent;
import com.sencha.gxt.widget.core.client.event.BeforeCollapseItemEvent;
import com.sencha.gxt.widget.core.client.event.CellDoubleClickEvent;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.treegrid.TreeGrid;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* User: kirilkadurilka
* Date: 26.03.13
* Time: 12:30
* Panel that contains table of metrics in comparison mod (multiple session selected)
*/
public class SessionComparisonPanel extends VerticalPanel {
private final String TEST_DESCRIPTION = "testDescription";
private final String TEST_NAME = "testName";
// property to render in Metric column
private final String NAME = "name";
@SuppressWarnings("all")
private final String METRIC = "Metric";
private final String SESSION_HEADER = "Session ";
private final String SESSION_INFO_ID = "sessionInfo";
@SuppressWarnings("all")
private final String COMMENT = "Comment";
@SuppressWarnings("all")
private final String USER_COMMENT = "User Comment";
@SuppressWarnings("all")
private final String SESSION_TAGS = "Tags";
@SuppressWarnings("all")
private final int MIN_COLUMN_WIDTH = 200;
@SuppressWarnings("all")
private final String ONE_HUNDRED_PERCENTS = "100%";
@SuppressWarnings("all")
private final String START_DATE = "Start Date";
@SuppressWarnings("all")
private final String END_DATE = "End Date";
@SuppressWarnings("all")
private final String ACTIVE_KERNELS = "Active Kernels";
@SuppressWarnings("all")
private final String TASKS_EXECUTED = "Tasks Executed";
@SuppressWarnings("all")
private final String TASKS_FAILED = "Tasks Failed";
private final String TEST_INFO = "Test Info";
private final double METRIC_COLUMN_WIDTH_FACTOR = 1.5;
private final UserCommentBox userCommentBox;
private final TagBox tagBox;
private Set<SessionDataDto> chosenSessions;
private final String WHITE_SPACE_NORMAL = "white-space: normal";
private TreeGrid<TreeItem> treeGrid;
private TreeStore<TreeItem> treeStore = new TreeStore<TreeItem>(new ModelKeyProvider<TreeItem>() {
@Override
public String getKey(TreeItem item) {
return String.valueOf(item.getKey());
}
});
private HashMap<MetricNode, SummaryIntegratedDto> cache = new HashMap<MetricNode, SummaryIntegratedDto>();
private WebClientProperties webClientProperties;
private List<TagDto> allTags;
private boolean allTagsLoadComplete = false;
public HashMap<MetricNode, SummaryIntegratedDto> getCachedMetrics() {
return cache;
}
private final DateTimeFormat dateFormatter;
public SessionComparisonPanel(
Set<SessionDataDto> chosenSessions,
int width,
WebClientProperties webClientProperties,
DateTimeFormat dateFormatter) {
this.dateFormatter = dateFormatter;
setWidth(ONE_HUNDRED_PERCENTS);
setHeight(ONE_HUNDRED_PERCENTS);
this.chosenSessions = chosenSessions;
this.webClientProperties = webClientProperties;
init(this.chosenSessions, width);
userCommentBox = new UserCommentBox(webClientProperties.getUserCommentMaxLength());
userCommentBox.setTreeGrid(treeGrid);
tagBox = new TagBox();
tagBox.setTreeGrid(treeGrid);
if (webClientProperties.isTagsStoreAvailable())
allTags();
}
private void init(Set<SessionDataDto> chosenSessions, int width) {
int colWidth = calculateWidth(chosenSessions.size(), width);
treeStore.clear();
List<ColumnConfig<TreeItem, ?>> columns = new ArrayList<ColumnConfig<TreeItem, ?>>();
//sort sessions by number sessionId
SortedSet<SessionDataDto> sortedSet = new TreeSet<SessionDataDto>(new Comparator<SessionDataDto>() {
@Override
public int compare(SessionDataDto o, SessionDataDto o2) {
return (Long.parseLong(o.getSessionId()) - Long.parseLong(o2.getSessionId())) > 0 ? 1 : -1;
}
});
sortedSet.addAll(chosenSessions);
ColumnConfig<TreeItem, String> nameColumn =
new ColumnConfig<TreeItem, String>(new MapValueProvider(NAME), (int) (colWidth * METRIC_COLUMN_WIDTH_FACTOR));
nameColumn.setHeader(METRIC);
nameColumn.setSortable(false);
nameColumn.setMenuDisabled(true);
columns.add(nameColumn);
for (SessionDataDto session : sortedSet) {
ColumnConfig<TreeItem, String> column = new ColumnConfig<TreeItem, String>(
new MapValueProvider(SESSION_HEADER + session.getSessionId())
);
column.setHeader(SESSION_HEADER + session.getSessionId());
column.setWidth(colWidth);
column.setSortable(false);
column.setCell(new AbstractCell<String>() {
@Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
if (value != null) {
sb.appendHtmlConstant(value);
}
}
});
column.setMenuDisabled(true);
column.setColumnTextStyle(new SafeStyles() {
@Override
public String asString() {
return WHITE_SPACE_NORMAL;
}
});
columns.add(column);
}
ColumnModel<TreeItem> cm = new ColumnModel<TreeItem>(columns);
treeGrid = new NoIconsTreeGrid(treeStore, cm, nameColumn);
treeGrid.addBeforeCollapseHandler(new BeforeCollapseItemEvent.BeforeCollapseItemHandler<TreeItem>() {
@Override
public void onBeforeCollapse(BeforeCollapseItemEvent<TreeItem> event) {
event.setCancelled(true);
}
});
treeGrid.setAutoExpand(true);
treeGrid.getView().setStripeRows(true);
treeGrid.setMinColumnWidth(MIN_COLUMN_WIDTH);
treeGrid.setAllowTextSelection(true);
treeGrid.getView().setForceFit(true);
treeStore.addStoreAddHandler(new StoreAddEvent.StoreAddHandler<TreeItem>() {
@Override
public void onAdd(StoreAddEvent<TreeItem> event) {
for (TreeItem item : event.getItems()) {
treeGrid.setExpanded(item, true);
}
}
});
if (webClientProperties.isUserCommentEditAvailable()) {
treeGrid.addCellDoubleClickHandler(new CellDoubleClickEvent.CellDoubleClickHandler() {
@Override
public void onCellClick(CellDoubleClickEvent event) {
TreeItem item = treeGrid.findNode(treeGrid.getTreeView().getRow(event.getRowIndex())).getModel();
if (item.getKey().equals(USER_COMMENT) && event.getCellIndex() > 0) {
SessionDataDto currentSession = defineCurrentSession(event);
userCommentBox.popUp(currentSession,
item.get(SESSION_HEADER + currentSession.getSessionId()),
item
);
}
}
});
}
if (webClientProperties.isTagsAvailable()) {
treeGrid.addCellDoubleClickHandler(new CellDoubleClickEvent.CellDoubleClickHandler() {
@Override
public void onCellClick(CellDoubleClickEvent event) {
TreeItem item = treeGrid.findNode(treeGrid.getTreeView().getRow(event.getRowIndex())).getModel();
if (item.getKey().equals(SESSION_TAGS) && event.getCellIndex() > 0) {
SessionDataDto currentSession = defineCurrentSession(event);
if (allTagsLoadComplete)
tagBox.popUpForEdit(currentSession,
item, allTags);
}
}
});
}
add(treeGrid);
}
private SessionDataDto defineCurrentSession(CellDoubleClickEvent event) {
String sessionId = treeGrid.getColumnModel().getColumn(event.getCellIndex()).getHeader().asString();
String sessionData_id = sessionId.substring(sessionId.indexOf(' ') + 1);
SessionDataDto currentSession = null;
for (SessionDataDto sessionDataDto : SessionComparisonPanel.this.chosenSessions) {
if (sessionDataDto.getSessionId().equals(sessionData_id)) {
currentSession = sessionDataDto;
break;
}
}
return currentSession;
}
/**
* calculates width for columns
*
* @param size number of chosen sessions
* @param width is offset width of parent container
* @return width of Session * column
*/
private int calculateWidth(int size, int width) {
int colWidth = (int) (width / (size + METRIC_COLUMN_WIDTH_FACTOR));
if (colWidth < MIN_COLUMN_WIDTH)
colWidth = MIN_COLUMN_WIDTH;
return colWidth;
}
public void addSessionInfo() {
TreeItem sessionInfo = new TreeItem(SESSION_INFO_ID);
sessionInfo.put(NAME, "Session Info");
treeStore.insert(0, sessionInfo);
String tagsStr = "";
TreeItem itemActiveKernels = new TreeItem(ACTIVE_KERNELS);
TreeItem itemTaskExecuted = new TreeItem(TASKS_EXECUTED);
TreeItem itemTaskFailed = new TreeItem(TASKS_FAILED);
TreeItem itemDateStart = new TreeItem(START_DATE);
TreeItem itemDateEnd = new TreeItem(END_DATE);
TreeItem itemComment = new TreeItem(COMMENT);
TreeItem itemUserComment = new TreeItem(USER_COMMENT);
TreeItem itemTags = new TreeItem(SESSION_TAGS);
itemActiveKernels.put(NAME, ACTIVE_KERNELS);
itemTaskExecuted.put(NAME, TASKS_EXECUTED);
itemTaskFailed.put(NAME, TASKS_FAILED);
itemDateStart.put(NAME, START_DATE);
itemDateEnd.put(NAME, END_DATE);
itemComment.put(NAME, COMMENT);
if (webClientProperties.isUserCommentStoreAvailable())
itemUserComment.put(NAME, USER_COMMENT);
if (webClientProperties.isTagsStoreAvailable())
itemTags.put(NAME, SESSION_TAGS);
for (SessionDataDto session : chosenSessions) {
itemActiveKernels.put(SESSION_HEADER + session.getSessionId(), session.getActiveKernelsCount() + "");
itemTaskExecuted.put(SESSION_HEADER + session.getSessionId(), session.getTasksExecuted() + "");
itemTaskFailed.put(SESSION_HEADER + session.getSessionId(), session.getTasksFailed() + "");
itemDateStart.put(SESSION_HEADER + session.getSessionId(), dateFormatter.format(session.getStartDate()));
itemDateEnd.put(SESSION_HEADER + session.getSessionId(), dateFormatter.format(session.getEndDate()));
itemComment.put(SESSION_HEADER + session.getSessionId(), session.getComment());
if (webClientProperties.isUserCommentStoreAvailable()) {
String userComment = session.getUserComment() == null ? "" : session.getUserComment();
itemUserComment.put(SESSION_HEADER + session.getSessionId(), userComment);
}
if (webClientProperties.isTagsStoreAvailable()) {
for (int i = 0; i < session.getTags().size(); i++) {
if (i == session.getTags().size() - 1)
tagsStr += session.getTags().get(i).getName();
else
tagsStr += session.getTags().get(i).getName() + ", ";
}
itemTags.put(SESSION_HEADER + session.getSessionId(), tagsStr);
tagsStr = "";
}
}
treeStore.add(sessionInfo, itemComment);
if (webClientProperties.isUserCommentStoreAvailable())
treeStore.add(sessionInfo, itemUserComment);
if (webClientProperties.isTagsStoreAvailable())
treeStore.add(sessionInfo, itemTags);
treeStore.add(sessionInfo, itemDateStart);
treeStore.add(sessionInfo, itemDateEnd);
treeStore.add(sessionInfo, itemActiveKernels);
treeStore.add(sessionInfo, itemTaskExecuted);
treeStore.add(sessionInfo, itemTaskFailed);
}
public void removeSessionInfo() {
TreeItem sessionInfo = treeStore.findModelWithKey(SESSION_INFO_ID);
if (sessionInfo != null)
treeStore.remove(sessionInfo);
}
private void allTags() {
allTags = new ArrayList<TagDto>();
SessionDataService.Async.getInstance().getAllTags(new AsyncCallback<List<TagDto>>() {
@Override
public void onFailure(Throwable throwable) {
new ExceptionPanel("Fail to fetch all tags from the database: " + throwable.getMessage());
}
@Override
public void onSuccess(List<TagDto> tagDtos) {
allTags.addAll(tagDtos);
allTagsLoadComplete = true;
}
});
}
// // to make columns fit 100% width if grid created not on Summary Tab
public void refresh() {
treeGrid.getView().refresh(true);
}
// clear everything but Session Information
public void clearTreeStore() {
for (TreeItem root : treeStore.getRootItems()) {
if (root.getKey().equals(SESSION_INFO_ID)) {
continue;
}
for (TreeItem test : treeStore.getChildren(root)) {
for (TreeItem item : treeStore.getChildren(test)) {
if (TEST_INFO.equals(item.get(NAME))) {
continue;
}
removeWithParent(item);
}
}
}
}
public void addMetricRecord(SummarySingleDto metricDto) {
TreeItem record = new TreeItem(metricDto);
addItemToStore(record, metricDto);
}
public void addMetricRecords(Map<MetricNode, SummaryIntegratedDto> loaded) {
cache.putAll(loaded);
List<SummarySingleDto> loadedSorted = new ArrayList<SummarySingleDto>();
for (SummaryIntegratedDto summaryMetricDto : loaded.values()) {
for (SummarySingleDto metricDto : summaryMetricDto.getSummarySingleDtoList()) {
loadedSorted.add(metricDto);
}
}
MetricRankingProvider.sortMetrics(loadedSorted);
for (SummarySingleDto metricDto : loadedSorted) {
addMetricRecord(metricDto);
}
}
private void addItemToStore(TreeItem record, SummarySingleDto metricDto) {
TreeItem taskItem = getTestItem(metricDto.getMetricName().getTest());
for (TreeItem rec : treeStore.getChildren(taskItem)) {
if (rec.getKey().equals(record.getKey())) {
return;
}
}
treeStore.add(taskItem, record);
}
public void removeRecords(List<SummaryIntegratedDto> list) {
List<SummarySingleDto> list2 = new ArrayList<SummarySingleDto>(list.size());
for (SummaryIntegratedDto smd : list) {
list2.addAll(smd.getSummarySingleDtoList());
}
for (SummarySingleDto metric : list2) {
removeRecord(metric);
}
}
private void removeRecord(SummarySingleDto metric) {
TreeItem testItem = getTestItem(metric.getMetricName().getTest());
String key = testItem.getKey() + metric.getMetricName().getMetricName();
for (TreeItem item : treeStore.getChildren(testItem)) {
if (item.getKey().equals(key)) {
removeWithParent(item);
return;
}
}
}
private String getItemKey(MetricNameDto metricName) {
return metricName.getTest().getDescription() + metricName.getTest().getTaskName() + metricName.getMetricName();
}
private void removeWithParent(TreeItem toRemove) {
TreeItem parent = treeStore.getParent(toRemove);
treeStore.remove(toRemove);
if (parent != null && !treeStore.hasChildren(parent)) {
removeWithParent(parent);
}
}
public void addTestInfo(TaskDataDto test, Map<String, TestInfoDto> testInfoMap) {
TreeItem testItem = getTestItem(test);
String testInfoId = testItem.getKey() + TEST_INFO;
if (treeStore.findModelWithKey(testInfoId) != null) {
return;
}
String testItemName = getTestItemName(test);
TreeItem testInfo = new TreeItem(testInfoId);
testInfo.put(NAME, TEST_INFO);
testInfo.put(TEST_DESCRIPTION, test.getDescription());
testInfo.put(TEST_NAME, testItemName);
treeStore.insert(testItem, 0, testInfo);
TreeItem clock = new TreeItem(testItem.getKey() + "Clock");
clock.put(NAME, "Clock");
clock.put(TEST_DESCRIPTION, test.getDescription());
clock.put(TEST_NAME, testItemName);
clock.put(TEST_INFO, TEST_INFO);
for (SessionDataDto session : chosenSessions) {
if (testInfoMap.get(session.getSessionId()) != null)
clock.put(SESSION_HEADER + session.getSessionId(),
testInfoMap.get(session.getSessionId()).getClock() + " (" +
testInfoMap.get(session.getSessionId()).getClockValue().toString() + ")");
}
treeStore.add(testInfo, clock);
TreeItem termination = new TreeItem(testItem.getKey() + "Termination");
termination.put(NAME, "Termination");
termination.put(TEST_DESCRIPTION, test.getDescription());
termination.put(TEST_NAME, testItemName);
termination.put(TEST_INFO, TEST_INFO);
for (SessionDataDto session : chosenSessions) {
if (testInfoMap.get(session.getSessionId()) != null)
termination.put(SESSION_HEADER + session.getSessionId(), testInfoMap.get(session.getSessionId()).getTermination());
}
treeStore.add(testInfo, termination);
TreeItem startTime = new TreeItem(testItem.getKey() + "Start time");
startTime.put(NAME, "Start time");
startTime.put(TEST_DESCRIPTION, test.getDescription());
startTime.put(TEST_NAME, testItemName);
startTime.put(TEST_INFO, TEST_INFO);
for (SessionDataDto session : chosenSessions) {
if (testInfoMap.get(session.getSessionId()) != null) {
Date date = testInfoMap.get(session.getSessionId()).getStartTime();
startTime.put(SESSION_HEADER + session.getSessionId(), dateFormatter.format(date));
}
}
treeStore.add(testInfo, startTime);
}
public void removeTestInfo(TaskDataDto test) {
String id = getTestItemId(test);
TreeItem testItem = treeStore.findModelWithKey(id);
if (testItem == null) {
return;
}
TreeItem testInfo = treeStore.getFirstChild(testItem);
if (testInfo != null && TEST_INFO.equals(testInfo.get(NAME)))
removeWithParent(testInfo);
}
private TreeItem getTestItem(TaskDataDto tdd) {
String key = getTestItemId(tdd);
TreeItem taskItem = treeStore.findModelWithKey(key);
if (taskItem != null) {
return taskItem;
}
TreeItem description = getTestDescriptionItem(tdd.getDescription());
taskItem = new TreeItem(key);
taskItem.put(NAME, getTestItemName(tdd));
taskItem.put(TEST_DESCRIPTION, tdd.getDescription());
treeStore.add(description, taskItem);
return taskItem;
}
private String getTestItemName(TaskDataDto tdd) {
if (webClientProperties.isShowOnlyMatchedTests())
return tdd.getTaskName();
else
return tdd.getTaskName() + " from sessions: " + tdd.getSessionIds();
}
private TreeItem getTestDescriptionItem(String descriptionStr) {
for (TreeItem item : treeStore.getRootItems()) {
if (descriptionStr.equals(item.get(NAME))) {
return item;
}
}
TreeItem description = new TreeItem(descriptionStr);
description.put(NAME, descriptionStr);
treeStore.add(description);
return description;
}
private String getTestItemId(TaskDataDto tdd) {
StringBuilder sessionIds = new StringBuilder();
for (String id : tdd.getSessionIds()) {
sessionIds.append(id);
}
return tdd.getDescription() + tdd.getTaskName() + sessionIds.toString();
}
private class NoIconsTreeGrid extends TreeGrid<TreeItem> {
public NoIconsTreeGrid(TreeStore<TreeItem> store, ColumnModel<TreeItem> cm, ColumnConfig<TreeItem, ?> treeColumn) {
super(store, cm, treeColumn);
}
@Override
protected ImageResource calculateIconStyle(TreeItem model) {
return null;
}
}
public class TreeItem extends HashMap<String, String> {
protected String key;
private String getKey() {
return key;
}
@SuppressWarnings("unused")
public TreeItem() {}
public TreeItem(String key) {
this.key = key;
}
public TreeItem(SummarySingleDto metricDto) {
MetricNameDto metricName = metricDto.getMetricName();
this.key = getTestItemId(metricDto.getMetricName().getTest()) + metricDto.getMetricName().getMetricName();
put(NAME, metricName.getMetricDisplayName());
put(TEST_DESCRIPTION, metricName.getTest().getDescription());
put(TEST_NAME, getItemKey(metricName));
for (SummaryMetricValueDto metricValue : metricDto.getValues()) {
String value = metricValue.getValueRepresentation();
// highlight results according to decision when available
if (metricValue.getDecision() != null) {
String toolTip = "Decision for metric during test run. Green - value in limits. Yellow - value crossed warning limits. Red - value outside limits";
switch (metricValue.getDecision()) {
case OK:
value = "<p title=\"" + toolTip + "\" style=\"color:green;font-weight:700;display:inline;\">" + value + "</p>";
break;
case WARNING:
value = "<p title=\"" + toolTip + "\" style=\"color:#B8860B;font-weight:700;display:inline;\">" + value + "</p>";
break;
default:
value = "<p title=\"" + toolTip + "\" style=\"color:red;font-weight:700;display:inline;\">" + value + "</p>";
break;
}
}
put(SESSION_HEADER + metricValue.getSessionId(),value);
}
}
}
private class MapValueProvider implements ValueProvider<TreeItem, String> {
private String field;
public MapValueProvider(String field) {
this.field = field;
}
@Override
public String getValue(TreeItem object) {
String penImageResource = "<img src=\"" + JaggerResources.INSTANCE.getPencilImage().getSafeUri().asString() + "\" height=\"15\" width=\"15\">"
+ "<ins font-size='10px'>double click to edit</ins><br><br>";
String toShow;
// Only for columns with data
if (!field.equals(NAME)) {
if (webClientProperties.isUserCommentEditAvailable()) {
if (object.get(NAME).equals(USER_COMMENT)) {
toShow = object.get(field).replaceAll("\n", "<br>");
return penImageResource + toShow;
}
}
if (webClientProperties.isTagsAvailable()) {
if (object.get(NAME).equals(SESSION_TAGS)) {
toShow = object.get(field).replaceAll("\n", "<br>");
return penImageResource + toShow;
}
}
}
return object.get(field);
}
@Override
public void setValue(TreeItem object, String value) {
object.put(field, value);
}
@Override
public String getPath() {
return field;
}
}
}