Commit 15e1a7f2 authored by Vincent Pelletier's avatar Vincent Pelletier

Support displaying several graphs for comparison.

Allows dropping use of fixed positioning, as it is not usable on smaller
screens.
Require clicking to display in-table graphs instead of hovering.
Make graphs dragable (add a dependency on jquery-ui).
Move apachedex-specific javascript to a separate file.
Move apachedex-specific css to a separate file.
Lazily render graphs to speed-up page loading with many graphs.
Also, fix tooltip coordinates inside positioned blocks.
Also, apply formatting to tooltip y coordinate.
parent 1e92a142
...@@ -420,30 +420,35 @@ class ERP5SiteStats(GenericSiteStats): ...@@ -420,30 +420,35 @@ class ERP5SiteStats(GenericSiteStats):
append(getApdexStatsAsHtml(data_total, self.threshold, True)) append(getApdexStatsAsHtml(data_total, self.threshold, True))
for date in column_list: for date in column_list:
append(getApdexStatsAsHtml(data_dict[date], self.threshold)) append(getApdexStatsAsHtml(data_dict[date], self.threshold))
def hiddenGraph(data_dict): def hiddenGraph(data_dict, title):
append('<td class="text group_right hover">') append('<td class="text group_right hidden_graph">')
data = getDataPoints(data_dict) data = getDataPoints(data_dict)
if len(data) > 1: if len(data) > 1:
append('+<div class="hover_target">') append('<span class="action" onclick="toggleGraph(this)">+</span>'
'<div class="positioner"><div class="container">'
'<div class="title">%s</div>'
'<div class="action close" onclick="hideGraph(this)">close</div>' %
title
)
append(graphPair( append(graphPair(
prepareDataForGraph(data, date_format, placeholder_delta), prepareDataForGraph(data, date_format, placeholder_delta),
date_format, date_format,
graph_period, graph_period,
)) ))
append('</div>') append('</div></div>')
append('</td>') append('</td>')
for module_id, data_dict in sorted(filtered_module.iteritems(), for module_id, data_dict in sorted(filtered_module.iteritems(),
key=ITEMGETTER0): key=ITEMGETTER0):
append('<tr class="group_top"><th rowspan="2">%s</th>' append('<tr class="group_top"><th rowspan="2">%s</th>'
'<th>module</th>' % module_id) '<th>module</th>' % module_id)
hiddenGraph(self.module[module_id][False]) hiddenGraph(self.module[module_id][False], module_id + ' (module)')
apdexAsColumns(data_dict[False]) apdexAsColumns(data_dict[False])
append('</tr><tr class="group_bottom"><th>document</th>') append('</tr><tr class="group_bottom"><th>document</th>')
hiddenGraph(self.module[module_id][True]) hiddenGraph(self.module[module_id][True], module_id + ' (document)')
apdexAsColumns(data_dict[True]) apdexAsColumns(data_dict[True])
append('</tr>') append('</tr>')
append('<tr class="group_top group_bottom"><th colspan="2">(none)</th>') append('<tr class="group_top group_bottom"><th colspan="2">(none)</th>')
hiddenGraph(self.no_module) hiddenGraph(self.no_module, '(none)')
apdexAsColumns(filtered_no_module) apdexAsColumns(filtered_no_module)
append('</tr></table>') append('</tr></table>')
append(super(ERP5SiteStats, self).asHTML(date_format, append(super(ERP5SiteStats, self).asHTML(date_format,
...@@ -715,34 +720,16 @@ def main(): ...@@ -715,34 +720,16 @@ def main():
out = open(args.out, 'w') out = open(args.out, 'w')
with out: with out:
out.write('<!DOCTYPE html>\n<html><head><meta charset="utf-8">' out.write('<!DOCTYPE html>\n<html><head><meta charset="utf-8">'
'<title>Stats</title><style>' '<title>Stats</title>')
'.stats th, .stats td { border: solid 1px #000; } ' if getattr(args, 'js_embed', True):
'.stats th { text-align: center; } ' out.write('<style>')
'.stats td { text-align: right; } ' out.write(getResource('apachedex.css'))
'.stats th.text, .stats td.text { text-align: left; } ' out.write('</style>')
'.stats td.no_hit { color: #ccc; } ' else:
'.stats_erp5 td { border-style: dotted; } ' out.write('<link rel="stylesheet" type="text/css" '
'.stats_erp5 tr.group_top td { border-top-style: solid; } ' 'href="%s/apachedex.css"/>' % args.js)
'.stats_erp5 tr.group_bottom td { border-bottom-style: solid; } '
'.stats_erp5 td.group_left { border-left-style: solid; } '
'.stats_erp5 td.group_right { border-right-style: solid; } '
'.stats_erp5 .overall_right { border-right-width: .2em; } '
'.hover_target { visibility: hidden; position: fixed; '
'top: 0; right: 0; background-color: #fff; border: 1px solid #000; } '
'.hover:hover .hover_target { visibility: visible; } '
'table.stats { border-collapse: collapse; } '
'.problem { background-color: #f00; color: white; } '
'.warning { background-color: #f80; color: white; } '
'h1 { background-color: #ccc; } '
'h2 { background-color: #eee; } '
'.axisLabels { color: rgb(84,84,84) !important; }'
'.flot-x-axis .tickLabel { text-align: center; } '
'.tooltip { position: absolute; display: none; padding: 0.1em; '
'border: 1px solid #000; background-color: #fff; opacity: 0.80; } '
'.tooltip .x br { display: none; }'
'</style>')
for script in ('jquery.js', 'jquery.flot.js', 'jquery.flot.time.js', for script in ('jquery.js', 'jquery.flot.js', 'jquery.flot.time.js',
'jquery.flot.axislabels.js'): 'jquery.flot.axislabels.js', 'jquery-ui.js', 'apachedex.js'):
if getattr(args, 'js_embed', True): if getattr(args, 'js_embed', True):
out.write('<script type="text/javascript">//<![CDATA[\n') out.write('<script type="text/javascript">//<![CDATA[\n')
out.write(getResource(script)) out.write(getResource(script))
...@@ -750,42 +737,6 @@ def main(): ...@@ -750,42 +737,6 @@ def main():
else: else:
out.write('<script type="text/javascript" src="%s/%s"></script>' % ( out.write('<script type="text/javascript" src="%s/%s"></script>' % (
args.js, script)) args.js, script))
out.write('<script type="text/javascript">$(function() {'
'$(".graph").each(function (i){'
'var container = $(this);'
'var previousIndex = null;'
'var tooltip = container.next(".tooltip");'
'var plot = $.plot('
'container,'
'$.parseJSON(container.attr("data-points")),'
'$.parseJSON(container.attr("data-options")));'
'function formatValue(axis, value) {'
'var result = "";'
'if (axis.options.axisLabel) {'
'result = axis.options.axisLabel + " : ";'
'}'
'return result + axis.tickFormatter(value, axis);'
'}'
'container.bind("plothover", function (event, pos, item) {'
'if (item) {'
'if (previousIndex != item.dataIndex) {'
'previousIndex = item.dataIndex;'
'tooltip.css("left", item.pageX + 5 + "px");'
'tooltip.css("top", item.pageY + 5 + "px");'
'tooltip.find(".x").html(formatValue('
'item.series.xaxis, item.datapoint[0]));'
'tooltip.find(".y").text(item.datapoint[1]);'
'tooltip.show();'
'}'
'} else {'
'if (previousIndex != null) {'
'tooltip.hide();'
'previousIndex = null;'
'}'
'}'
'});'
'});'
'});</script>')
out.write('</head><body><h1>Overall</h1><h2>Parameters</h2>' out.write('</head><body><h1>Overall</h1><h2>Parameters</h2>'
'<table class="stats">') '<table class="stats">')
for caption, value in ( for caption, value in (
......
.stats th,
.stats td {
border: solid 1px #000;
}
.stats th {
text-align: center;
}
.stats td {
text-align: right;
}
.stats th.text,
.stats td.text {
text-align: left;
}
.stats td.no_hit {
color: #ccc;
}
.stats_erp5 td {
border-style: dotted;
}
.stats_erp5 tr.group_top td {
border-top-style: solid;
}
.stats_erp5 tr.group_bottom td {
border-bottom-style: solid;
}
.stats_erp5 td.group_left {
border-left-style: solid;
}
.stats_erp5 td.group_right {
border-right-style: solid;
}
.stats_erp5 .overall_right {
border-right-width: .2em;
}
.hidden_graph .positioner {
position: absolute;
left: 50%;
}
.hidden_graph .container {
display: none;
position: absolute;
left: -301px;
background-color: #fff;
border: 1px solid #000;
}
.hidden_graph:hover .container {
visibility: visible;
}
.action {
text-decoration: underline;
color: blue;
}
.hidden_graph .title {
float: left;
}
.hidden_graph .close {
float: right;
}
table.stats {
border-collapse: collapse;
}
.problem {
background-color: #f00;
color: white;
}
.warning {
background-color: #f80;
color: white;
}
h1 {
background-color: #ccc;
}
h2 {
background-color: #eee;
}
.axisLabels {
color: rgb(84,84,84) !important;
}
.flot-x-axis .tickLabel {
text-align: center;
}
.tooltip {
position: absolute;
display: none;
padding: 0.1em;
border: 1px solid #000;
background-color: #fff;
opacity: 0.80;
}
.tooltip .x br {
display: none;
}
function formatValue(axis, value) {
var result = "";
if (axis.options.axisLabel) {
result = axis.options.axisLabel + " : ";
}
return result + axis.tickFormatter(value, axis);
}
function updateGraphTooltip(event, pos, item, previousIndex, tooltip, plot) {
if (item) {
if (previousIndex != item.dataIndex) {
previousIndex = item.dataIndex;
var plot_offset = plot.getPlotOffset();
var offset = plot.offset();
tooltip.find(".x").html(formatValue(
item.series.xaxis, item.datapoint[0]));
tooltip.find(".y").html(formatValue(
item.series.yaxis, item.datapoint[1]));
tooltip.css("left", item.pageX - offset.left + plot_offset.left + 5
+ "px");
tooltip.show();
// query offsetHeight *after* making the tooltip visible
tooltip.css("top", item.pageY - offset.top + plot_offset.top - 5
- tooltip.prop("offsetHeight") + "px");
}
} else {
if (previousIndex != null) {
tooltip.hide();
previousIndex = null;
}
}
return previousIndex;
}
function renderGraph(container) {
var container = $(container);
var previousIndex = null;
var tooltip = container.next(".tooltip");
var plot = $.plot(
container,
$.parseJSON(container.attr("data-points")),
$.parseJSON(container.attr("data-options"))
);
tooltip.detach();
container.append(tooltip);
container.bind("plothover", function (event, pos, item) {
previousIndex = updateGraphTooltip(event, pos, item, previousIndex,
tooltip, plot);
});
}
function toggleGraph(node) {
var container = $(node).parent().find(".container");
// Note: toggling *after* rendering cause layout problems with flot.
container.toggle();
if (container.attr("data-rendered-marker") == null) {
container.attr("data-rendered-marker", "rendered");
container.find(".graph").each(function (i){renderGraph(this)});
}
}
function hideGraph(node) {
$(node).parent().hide();
}
$(function() {
$(".graph:visible").each(function (i){renderGraph(this)});
$(".hidden_graph .container").draggable();
});
...@@ -7,6 +7,7 @@ import urllib ...@@ -7,6 +7,7 @@ import urllib
FLOT_SHA = 'aefe4e729b2d14efe6e8c0db359cb0e9aa6aae52' FLOT_SHA = 'aefe4e729b2d14efe6e8c0db359cb0e9aa6aae52'
FLOT_AXISLABELS_SHA = '80453cd7fb8a9cad084cf6b581034ada3339dbf8' FLOT_AXISLABELS_SHA = '80453cd7fb8a9cad084cf6b581034ada3339dbf8'
JQUERY_VERSION = '1.9.1' JQUERY_VERSION = '1.9.1'
JQUERY_UI_VERSION = '1.10.2'
DEPS = { DEPS = {
'jquery.flot.js': ( 'jquery.flot.js': (
...@@ -26,6 +27,10 @@ DEPS = { ...@@ -26,6 +27,10 @@ DEPS = {
'http://code.jquery.com/jquery-%s.min.js' % JQUERY_VERSION, 'http://code.jquery.com/jquery-%s.min.js' % JQUERY_VERSION,
'397754ba49e9e0cf4e7c190da78dda05', '397754ba49e9e0cf4e7c190da78dda05',
), ),
'jquery-ui.js': (
'http://code.jquery.com/ui/%s/jquery-ui.min.js' % JQUERY_UI_VERSION,
'3e6acb1e6426ef90d2e786a006a4ea28',
),
} }
def download(url, filename, hexdigest): def download(url, filename, hexdigest):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment