mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
First attempt to integrate nvd3.
This commit is contained in:
@@ -2,8 +2,11 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from lxml import etree
|
||||
from lxml import objectify
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.sysutils import exec_cmd
|
||||
|
||||
periods = [{"name": "day", "label": ugettext_lazy("Day")},
|
||||
{"name": "week", "label": ugettext_lazy("Week")},
|
||||
@@ -85,6 +88,39 @@ class Grapher(object):
|
||||
if not os.path.exists(path):
|
||||
print "[graph] Impossible to create %s graph" % path
|
||||
|
||||
def export(self, target, suffix, start, end, graph_tpl):
|
||||
rrdfile = "%s/%s.rrd" % (self.rrd_rootdir, target)
|
||||
if not os.path.exists(rrdfile):
|
||||
print 'pouet'
|
||||
return []
|
||||
start = str(start)
|
||||
end = str(end)
|
||||
defs = []
|
||||
result = []
|
||||
for v, d in graph_tpl.vars.iteritems():
|
||||
result += [{'key': d['legend'].encode('utf-8'), 'values': [], 'color': d['color']}]
|
||||
defs += [str('DEF:%s=%s:%s:%s' % (v, rrdfile, v, graph_tpl.cf)),
|
||||
str('CDEF:%spm=%s,60,*' % (v, v)),
|
||||
str('XPORT:%spm:"%s"' % (v, d['legend'].encode('utf-8')))]
|
||||
|
||||
cmd = "rrdtool xport --start %s --end %s " % (start, end)
|
||||
cmd += " ".join(defs)
|
||||
code, output = exec_cmd(cmd)
|
||||
tree = etree.fromstring(output)
|
||||
start = int(tree.xpath('/xport/meta/start')[0].text)
|
||||
end = int(tree.xpath('/xport/meta/end')[0].text)
|
||||
step = int(tree.xpath('/xport/meta/step')[0].text)
|
||||
for pos, r in enumerate(tree.xpath('/xport/data/row')):
|
||||
ts = int(r.find('t').text) - start
|
||||
for vindex, value in enumerate(r.findall('v')):
|
||||
if value.text == 'NaN':
|
||||
result[vindex]['values'].append({'x': pos, 'y': 0})
|
||||
else:
|
||||
result[vindex]['values'].append(
|
||||
{'x': pos, 'y': float(value.text)}
|
||||
)
|
||||
return result
|
||||
|
||||
def make_defaults(self, target, graph_tpl):
|
||||
end = "%d" % int(time.mktime(time.localtime()))
|
||||
self.process(target, "day", "-1day", end, graph_tpl)
|
||||
|
||||
@@ -0,0 +1,769 @@
|
||||
|
||||
/********************
|
||||
* HTML CSS
|
||||
*/
|
||||
|
||||
|
||||
.chartWrap {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/********************
|
||||
Box shadow and border radius styling
|
||||
*/
|
||||
.nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip {
|
||||
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
|
||||
-webkit-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/********************
|
||||
* TOOLTIP CSS
|
||||
*/
|
||||
|
||||
.nvtooltip {
|
||||
position: absolute;
|
||||
background-color: rgba(255,255,255,1.0);
|
||||
padding: 1px;
|
||||
border: 1px solid rgba(0,0,0,.2);
|
||||
z-index: 10000;
|
||||
|
||||
font-family: Arial;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
pointer-events: none;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/*Give tooltips that old fade in transition by
|
||||
putting a "with-transitions" class on the container div.
|
||||
*/
|
||||
.nvtooltip.with-transitions, .with-transitions .nvtooltip {
|
||||
transition: opacity 250ms linear;
|
||||
-moz-transition: opacity 250ms linear;
|
||||
-webkit-transition: opacity 250ms linear;
|
||||
|
||||
transition-delay: 250ms;
|
||||
-moz-transition-delay: 250ms;
|
||||
-webkit-transition-delay: 250ms;
|
||||
}
|
||||
|
||||
.nvtooltip.x-nvtooltip,
|
||||
.nvtooltip.y-nvtooltip {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.nvtooltip h3 {
|
||||
margin: 0;
|
||||
padding: 4px 14px;
|
||||
line-height: 18px;
|
||||
font-weight: normal;
|
||||
background-color: rgba(247,247,247,0.75);
|
||||
text-align: center;
|
||||
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
|
||||
-webkit-border-radius: 5px 5px 0 0;
|
||||
-moz-border-radius: 5px 5px 0 0;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
.nvtooltip p {
|
||||
margin: 0;
|
||||
padding: 5px 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nvtooltip span {
|
||||
display: inline-block;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.nvtooltip table {
|
||||
margin: 6px;
|
||||
border-spacing:0;
|
||||
}
|
||||
|
||||
|
||||
.nvtooltip table td {
|
||||
padding: 2px 9px 2px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.nvtooltip table td.key {
|
||||
font-weight:normal;
|
||||
}
|
||||
.nvtooltip table td.value {
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nvtooltip table tr.highlight td {
|
||||
padding: 1px 9px 1px 0;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.nvtooltip table td.legend-color-guide div {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.nvtooltip .footer {
|
||||
padding: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.nvtooltip-pending-removal {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
* SVG CSS
|
||||
*/
|
||||
|
||||
|
||||
svg {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* Trying to get SVG to act like a greedy block in all browsers */
|
||||
display: block;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
|
||||
svg text {
|
||||
font: normal 12px Arial;
|
||||
}
|
||||
|
||||
svg .title {
|
||||
font: bold 14px Arial;
|
||||
}
|
||||
|
||||
.nvd3 .nv-background {
|
||||
fill: white;
|
||||
fill-opacity: 0;
|
||||
/*
|
||||
pointer-events: none;
|
||||
*/
|
||||
}
|
||||
|
||||
.nvd3.nv-noData {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Brush
|
||||
*/
|
||||
|
||||
.nv-brush .extent {
|
||||
fill-opacity: .125;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Legend
|
||||
*/
|
||||
|
||||
.nvd3 .nv-legend .nv-series {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nvd3 .nv-legend .disabled circle {
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Axes
|
||||
*/
|
||||
.nvd3 .nv-axis {
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis path {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-opacity: .75;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis path.domain {
|
||||
stroke-opacity: .75;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis.nv-x path.domain {
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis line {
|
||||
fill: none;
|
||||
stroke: #e5e5e5;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis .zero line,
|
||||
/*this selector may not be necessary*/ .nvd3 .nv-axis line.zero {
|
||||
stroke-opacity: .75;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis .nv-axisMaxMin text {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nvd3 .x .nv-axis .nv-axisMaxMin text,
|
||||
.nvd3 .x2 .nv-axis .nv-axisMaxMin text,
|
||||
.nvd3 .x3 .nv-axis .nv-axisMaxMin text {
|
||||
text-anchor: middle
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Brush
|
||||
*/
|
||||
|
||||
.nv-brush .resize path {
|
||||
fill: #eee;
|
||||
stroke: #666;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Bars
|
||||
*/
|
||||
|
||||
.nvd3 .nv-bars .negative rect {
|
||||
zfill: brown;
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars rect {
|
||||
zfill: steelblue;
|
||||
fill-opacity: .75;
|
||||
|
||||
transition: fill-opacity 250ms linear;
|
||||
-moz-transition: fill-opacity 250ms linear;
|
||||
-webkit-transition: fill-opacity 250ms linear;
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars rect.hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars .hover rect {
|
||||
fill: lightblue;
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars text {
|
||||
fill: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars .hover text {
|
||||
fill: rgba(0,0,0,1);
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Bars
|
||||
*/
|
||||
|
||||
.nvd3 .nv-multibar .nv-groups rect,
|
||||
.nvd3 .nv-multibarHorizontal .nv-groups rect,
|
||||
.nvd3 .nv-discretebar .nv-groups rect {
|
||||
stroke-opacity: 0;
|
||||
|
||||
transition: fill-opacity 250ms linear;
|
||||
-moz-transition: fill-opacity 250ms linear;
|
||||
-webkit-transition: fill-opacity 250ms linear;
|
||||
}
|
||||
|
||||
.nvd3 .nv-multibar .nv-groups rect:hover,
|
||||
.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,
|
||||
.nvd3 .nv-discretebar .nv-groups rect:hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
.nvd3 .nv-discretebar .nv-groups text,
|
||||
.nvd3 .nv-multibarHorizontal .nv-groups text {
|
||||
font-weight: bold;
|
||||
fill: rgba(0,0,0,1);
|
||||
stroke: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
/***********
|
||||
* Pie Chart
|
||||
*/
|
||||
|
||||
.nvd3.nv-pie path {
|
||||
stroke-opacity: 0;
|
||||
transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
-moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
-webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
|
||||
}
|
||||
|
||||
.nvd3.nv-pie .nv-slice text {
|
||||
stroke: #000;
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
.nvd3.nv-pie path {
|
||||
stroke: #fff;
|
||||
stroke-width: 1px;
|
||||
stroke-opacity: 1;
|
||||
}
|
||||
|
||||
.nvd3.nv-pie .hover path {
|
||||
fill-opacity: .7;
|
||||
}
|
||||
.nvd3.nv-pie .nv-label {
|
||||
pointer-events: none;
|
||||
}
|
||||
.nvd3.nv-pie .nv-label rect {
|
||||
fill-opacity: 0;
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
/**********
|
||||
* Lines
|
||||
*/
|
||||
|
||||
.nvd3 .nv-groups path.nv-line {
|
||||
fill: none;
|
||||
stroke-width: 1.5px;
|
||||
/*
|
||||
stroke-linecap: round;
|
||||
shape-rendering: geometricPrecision;
|
||||
|
||||
transition: stroke-width 250ms linear;
|
||||
-moz-transition: stroke-width 250ms linear;
|
||||
-webkit-transition: stroke-width 250ms linear;
|
||||
|
||||
transition-delay: 250ms
|
||||
-moz-transition-delay: 250ms;
|
||||
-webkit-transition-delay: 250ms;
|
||||
*/
|
||||
}
|
||||
|
||||
.nvd3 .nv-groups path.nv-line.nv-thin-line {
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
|
||||
.nvd3 .nv-groups path.nv-area {
|
||||
stroke: none;
|
||||
/*
|
||||
stroke-linecap: round;
|
||||
shape-rendering: geometricPrecision;
|
||||
|
||||
stroke-width: 2.5px;
|
||||
transition: stroke-width 250ms linear;
|
||||
-moz-transition: stroke-width 250ms linear;
|
||||
-webkit-transition: stroke-width 250ms linear;
|
||||
|
||||
transition-delay: 250ms
|
||||
-moz-transition-delay: 250ms;
|
||||
-webkit-transition-delay: 250ms;
|
||||
*/
|
||||
}
|
||||
|
||||
.nvd3 .nv-line.hover path {
|
||||
stroke-width: 6px;
|
||||
}
|
||||
|
||||
/*
|
||||
.nvd3.scatter .groups .point {
|
||||
fill-opacity: 0.1;
|
||||
stroke-opacity: 0.1;
|
||||
}
|
||||
*/
|
||||
|
||||
.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point {
|
||||
fill-opacity: 0;
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point {
|
||||
fill-opacity: .5 !important;
|
||||
stroke-opacity: .5 !important;
|
||||
}
|
||||
|
||||
|
||||
.with-transitions .nvd3 .nv-groups .nv-point {
|
||||
transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
-moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
-webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
|
||||
}
|
||||
|
||||
.nvd3.nv-scatter .nv-groups .nv-point.hover,
|
||||
.nvd3 .nv-groups .nv-point.hover {
|
||||
stroke-width: 7px;
|
||||
fill-opacity: .95 !important;
|
||||
stroke-opacity: .95 !important;
|
||||
}
|
||||
|
||||
|
||||
.nvd3 .nv-point-paths path {
|
||||
stroke: #aaa;
|
||||
stroke-opacity: 0;
|
||||
fill: #eee;
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.nvd3 .nv-indexLine {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Distribution
|
||||
*/
|
||||
|
||||
.nvd3 .nv-distribution {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Scatter
|
||||
*/
|
||||
|
||||
/* **Attempting to remove this for useVoronoi(false), need to see if it's required anywhere
|
||||
.nvd3 .nv-groups .nv-point {
|
||||
pointer-events: none;
|
||||
}
|
||||
*/
|
||||
|
||||
.nvd3 .nv-groups .nv-point.hover {
|
||||
stroke-width: 20px;
|
||||
stroke-opacity: .5;
|
||||
}
|
||||
|
||||
.nvd3 .nv-scatter .nv-point.hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
.nv-group.hover .nv-point {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/**********
|
||||
* Stacked Area
|
||||
*/
|
||||
|
||||
.nvd3.nv-stackedarea path.nv-area {
|
||||
fill-opacity: .7;
|
||||
/*
|
||||
stroke-opacity: .65;
|
||||
fill-opacity: 1;
|
||||
*/
|
||||
stroke-opacity: 0;
|
||||
|
||||
transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
|
||||
-moz-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
|
||||
-webkit-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
|
||||
|
||||
/*
|
||||
transition-delay: 500ms;
|
||||
-moz-transition-delay: 500ms;
|
||||
-webkit-transition-delay: 500ms;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
.nvd3.nv-stackedarea path.nv-area.hover {
|
||||
fill-opacity: .9;
|
||||
/*
|
||||
stroke-opacity: .85;
|
||||
*/
|
||||
}
|
||||
/*
|
||||
.d3stackedarea .groups path {
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
.nvd3.nv-stackedarea .nv-groups .nv-point {
|
||||
stroke-opacity: 0;
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
.nvd3.nv-stackedarea .nv-groups .nv-point.hover {
|
||||
stroke-width: 20px;
|
||||
stroke-opacity: .75;
|
||||
fill-opacity: 1;
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Line Plus Bar
|
||||
*/
|
||||
|
||||
.nvd3.nv-linePlusBar .nv-bar rect {
|
||||
fill-opacity: .75;
|
||||
}
|
||||
|
||||
.nvd3.nv-linePlusBar .nv-bar rect:hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Bullet
|
||||
*/
|
||||
|
||||
.nvd3.nv-bullet { font: 10px sans-serif; }
|
||||
.nvd3.nv-bullet .nv-measure { fill-opacity: .8; }
|
||||
.nvd3.nv-bullet .nv-measure:hover { fill-opacity: 1; }
|
||||
.nvd3.nv-bullet .nv-marker { stroke: #000; stroke-width: 2px; }
|
||||
.nvd3.nv-bullet .nv-markerTriangle { stroke: #000; fill: #fff; stroke-width: 1.5px; }
|
||||
.nvd3.nv-bullet .nv-tick line { stroke: #666; stroke-width: .5px; }
|
||||
.nvd3.nv-bullet .nv-range.nv-s0 { fill: #eee; }
|
||||
.nvd3.nv-bullet .nv-range.nv-s1 { fill: #ddd; }
|
||||
.nvd3.nv-bullet .nv-range.nv-s2 { fill: #ccc; }
|
||||
.nvd3.nv-bullet .nv-title { font-size: 14px; font-weight: bold; }
|
||||
.nvd3.nv-bullet .nv-subtitle { fill: #999; }
|
||||
|
||||
|
||||
.nvd3.nv-bullet .nv-range {
|
||||
fill: #bababa;
|
||||
fill-opacity: .4;
|
||||
}
|
||||
.nvd3.nv-bullet .nv-range:hover {
|
||||
fill-opacity: .7;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Sparkline
|
||||
*/
|
||||
|
||||
.nvd3.nv-sparkline path {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus g.nv-hoverValue {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-hoverValue line {
|
||||
stroke: #333;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus,
|
||||
.nvd3.nv-sparklineplus g {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.nvd3 .nv-hoverArea {
|
||||
fill-opacity: 0;
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-xValue,
|
||||
.nvd3.nv-sparklineplus .nv-yValue {
|
||||
/*
|
||||
stroke: #666;
|
||||
*/
|
||||
stroke-width: 0;
|
||||
font-size: .9em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-yValue {
|
||||
stroke: #f66;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-maxValue {
|
||||
stroke: #2ca02c;
|
||||
fill: #2ca02c;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-minValue {
|
||||
stroke: #d62728;
|
||||
fill: #d62728;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-currentValue {
|
||||
/*
|
||||
stroke: #444;
|
||||
fill: #000;
|
||||
*/
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
/**********
|
||||
* historical stock
|
||||
*/
|
||||
|
||||
.nvd3.nv-ohlcBar .nv-ticks .nv-tick {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover {
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive {
|
||||
stroke: #2ca02c;
|
||||
}
|
||||
|
||||
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative {
|
||||
stroke: #d62728;
|
||||
}
|
||||
|
||||
.nvd3.nv-historicalStockChart .nv-axis .nv-axislabel {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nvd3.nv-historicalStockChart .nv-dragTarget {
|
||||
fill-opacity: 0;
|
||||
stroke: none;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.nvd3 .nv-brush .extent {
|
||||
/*
|
||||
cursor: ew-resize !important;
|
||||
*/
|
||||
fill-opacity: 0 !important;
|
||||
}
|
||||
|
||||
.nvd3 .nv-brushBackground rect {
|
||||
stroke: #000;
|
||||
stroke-width: .4;
|
||||
fill: #fff;
|
||||
fill-opacity: .7;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Indented Tree
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* TODO: the following 3 selectors are based on classes used in the example. I should either make them standard and leave them here, or move to a CSS file not included in the library
|
||||
*/
|
||||
.nvd3.nv-indentedtree .name {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.nvd3.nv-indentedtree .clickable {
|
||||
color: #08C;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nvd3.nv-indentedtree span.clickable:hover {
|
||||
color: #005580;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
.nvd3.nv-indentedtree .nv-childrenCount {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.nvd3.nv-indentedtree .nv-treeicon {
|
||||
cursor: pointer;
|
||||
/*
|
||||
cursor: n-resize;
|
||||
*/
|
||||
}
|
||||
|
||||
.nvd3.nv-indentedtree .nv-treeicon.nv-folded {
|
||||
cursor: pointer;
|
||||
/*
|
||||
cursor: s-resize;
|
||||
*/
|
||||
}
|
||||
|
||||
/**********
|
||||
* Parallel Coordinates
|
||||
*/
|
||||
|
||||
.nvd3 .background path {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-opacity: .4;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .foreground path {
|
||||
fill: none;
|
||||
stroke: steelblue;
|
||||
stroke-opacity: .7;
|
||||
}
|
||||
|
||||
.nvd3 .brush .extent {
|
||||
fill-opacity: .3;
|
||||
stroke: #fff;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .axis line, .axis path {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .axis text {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
/****
|
||||
Interactive Layer
|
||||
*/
|
||||
.nvd3 .nv-interactiveGuideLine {
|
||||
pointer-events:none;
|
||||
}
|
||||
.nvd3 line.nv-guideline {
|
||||
stroke: #ccc;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
svg {
|
||||
height: 400px;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@ Stats.prototype = {
|
||||
constructor: Stats,
|
||||
|
||||
defaults: {
|
||||
deflocation: "graphs/",
|
||||
deflocation: "d3graphs/",
|
||||
language: "en"
|
||||
},
|
||||
|
||||
@@ -80,9 +80,26 @@ Stats.prototype = {
|
||||
},
|
||||
|
||||
graphs_cb: function(data) {
|
||||
if (data.content) {
|
||||
/*if (data.content) {
|
||||
$(".tab-pane.active").html(data.content);
|
||||
}
|
||||
}*/
|
||||
var $container = $("#graphs_mailtraffic");
|
||||
$.each(data.graphs, function(id, data) {
|
||||
var chart = nv.models.stackedAreaChart();
|
||||
//var $svg = $("<svg />", {id: id});
|
||||
|
||||
//$container.append($svg);
|
||||
chart.xAxis.axisLabel('Time (s)')
|
||||
.tickFormat(d3.format(',r'));
|
||||
chart.yAxis.axisLabel('Mail')
|
||||
.tickFormat(d3.format('.02f'));
|
||||
d3.select('#' + id + ' svg')
|
||||
.datum(data)
|
||||
.transition().duration(500)
|
||||
.call(chart);
|
||||
|
||||
//nv.utils.windowResize(chart.update);
|
||||
});
|
||||
},
|
||||
|
||||
search_domain: function(value) {
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
{% for name in graphs %}
|
||||
<p><img src="{{ MEDIA_URL }}stats/{{ name }}_{{ domain }}_AVERAGE_{% ifequal period 'custom' %}{{ period_name }}{% else %}{{ period }}{% endifequal %}.png"
|
||||
alt="{% trans 'No statistics available' %}" /></p>
|
||||
<div id="chart_{{ name }}"><svg></svg></div>
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
{% extends "twocols.html" %}{% load i18n %}{% load url from future %}
|
||||
{% block pagetitle %}{% trans "Graphical statistics" %}{% endblock %}
|
||||
{% block html_head %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}stats/css/nv.d3.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}stats/css/stats.css" />
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/autocompleter.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}stats/js/stats.js"></script>
|
||||
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}stats/js/nv.d3.js"></script>
|
||||
<script type="text/javascript">
|
||||
function get_domains_list(container) {
|
||||
$.getJSON("{% url 'modoboa.extensions.admin.views.domain.domains_list' %}", function(data) {
|
||||
@@ -69,7 +72,11 @@ $(document).ready(function() {
|
||||
</li>
|
||||
</ul>{% endfor %}
|
||||
<div class="tab-content">{% for id, gset in graph_sets.items %}
|
||||
<div class="tab-pane {% if forloop.first %}active{% endif %}" id="graphs_{{ gset.html_id }}"></div>
|
||||
<div class="tab-pane {% if forloop.first %}active{% endif %}" id="graphs_{{ gset.html_id }}">
|
||||
<div id="avgtraffic"><svg /></div>
|
||||
<div id="avgtrafficsize"><svg /></div>
|
||||
<div id="badtraffic"><svg /></div>
|
||||
</div>
|
||||
</div>{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -5,4 +5,5 @@ urlpatterns = patterns(
|
||||
'modoboa.extensions.stats.views',
|
||||
url(r'^$', 'index', name='fullindex'),
|
||||
url(r'^graphs/$', "graphs"),
|
||||
url(r'^d3graphs/$', "export"),
|
||||
)
|
||||
|
||||
@@ -22,7 +22,7 @@ def index(request):
|
||||
"""
|
||||
FIXME: how to select a default graph set ?
|
||||
"""
|
||||
deflocation = "graphs/?gset=mailtraffic"
|
||||
deflocation = "d3graphs/?gset=mailtraffic"
|
||||
if not request.user.is_superuser:
|
||||
if not Domain.objects.get_for_admin(request.user).count():
|
||||
raise NotFound(_("No statistics available"))
|
||||
@@ -79,6 +79,11 @@ def graphs(request):
|
||||
tplvars["domain"], period_name, str2Time(*expr.split(start)),
|
||||
str2Time(*expr.split(end)), tpl
|
||||
)
|
||||
G.export(
|
||||
tplvars["domain"], period_name, str2Time(*expr.split(start)),
|
||||
str2Time(*expr.split(end)), tpl
|
||||
)
|
||||
|
||||
tplvars["period_name"] = period_name
|
||||
tplvars["start"] = start
|
||||
tplvars["end"] = end
|
||||
@@ -88,3 +93,52 @@ def graphs(request):
|
||||
return render_to_json_response({
|
||||
'content': _render_to_string(request, "stats/graphs.html", tplvars)
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.group != "SimpleUsers")
|
||||
def export(request):
|
||||
gset = request.GET.get("gset", None)
|
||||
gsets = events.raiseDictEvent("GetGraphSets")
|
||||
if not gset in gsets:
|
||||
raise NotFound(_("Unknown graphic set"))
|
||||
searchq = request.GET.get("searchquery", None)
|
||||
period = request.GET.get("period", "day")
|
||||
tplvars = dict(graphs={}, period=period)
|
||||
if searchq in [None, "global"]:
|
||||
if not request.user.is_superuser:
|
||||
if not Domain.objects.get_for_admin(request.user).count():
|
||||
return render_to_json_response({})
|
||||
tplvars.update(
|
||||
domain=Domain.objects.get_for_admin(request.user)[0].name
|
||||
)
|
||||
else:
|
||||
tplvars.update(domain="global")
|
||||
else:
|
||||
domain = Domain.objects.filter(name__contains=searchq)
|
||||
if domain.count() != 1:
|
||||
return render_to_json_response({})
|
||||
if not request.user.can_access(domain[0]):
|
||||
raise PermDeniedException
|
||||
tplvars.update(domain=domain[0].name)
|
||||
if period == "custom":
|
||||
if not "start" in request.GET or not "end" in request.GET:
|
||||
raise BadRequest(_("Bad custom period"))
|
||||
start = request.GET["start"]
|
||||
end = request.GET["end"]
|
||||
G = Grapher()
|
||||
expr = re.compile(r'[:\- ]')
|
||||
period_name = "%s_%s" % (expr.sub('', start), expr.sub('', end))
|
||||
for tpl in gsets[gset].get_graphs():
|
||||
tplvars['graphs'][tpl.display_name] = G.export(
|
||||
tplvars["domain"], period_name, str2Time(*expr.split(start)),
|
||||
str2Time(*expr.split(end)), tpl
|
||||
)
|
||||
|
||||
tplvars["period_name"] = period_name
|
||||
tplvars["start"] = start
|
||||
tplvars["end"] = end
|
||||
else:
|
||||
tplvars['graphs'] = gsets[gset].get_graph_names()
|
||||
|
||||
return render_to_json_response(tplvars)
|
||||
|
||||
Reference in New Issue
Block a user