mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
wip: stats extension layout
New layout is done. Now it's time to open this extension to the others. see #333
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# coding: utf-8
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
class MailTrafic(object):
|
||||
pass
|
||||
|
||||
traffic_avg_template = {
|
||||
'name' : 'traffic',
|
||||
'title' : ugettext_lazy("Average normal traffic"),
|
||||
'width' : '540',
|
||||
'height' : '120',
|
||||
'vertlabel' : ugettext_lazy('msgs/min'),
|
||||
'cf' : 'AVERAGE',
|
||||
'vars' : {
|
||||
'sent' : { "type" : "AREA", "color" : "#00FF00",
|
||||
"legend" : ugettext_lazy("sent messages") },
|
||||
'recv' : { "type" : "AREA", "color" : "#0000FF",
|
||||
"legend" : ugettext_lazy("received messages") },
|
||||
}
|
||||
}
|
||||
|
||||
badtraffic_avg_template = {
|
||||
'name' : 'badtraffic',
|
||||
'title' : ugettext_lazy("Average bad traffic"),
|
||||
'width' : '540',
|
||||
'height' : '120',
|
||||
'vertlabel' : ugettext_lazy('msgs/min'),
|
||||
'cf' : 'AVERAGE',
|
||||
'vars' : {
|
||||
'bounced' : { "type" : "AREA", "color" : "#FFFF00",
|
||||
"legend" : ugettext_lazy("bounced messages") },
|
||||
'reject' : { "type" : "AREA", "color" : "#FF0000",
|
||||
"legend" : ugettext_lazy("rejected messages") },
|
||||
}
|
||||
}
|
||||
|
||||
size_avg_template = {
|
||||
'name' : 'size',
|
||||
'title' : ugettext_lazy("Average normal traffic size"),
|
||||
'width' : '540',
|
||||
'height' : '120',
|
||||
'vertlabel' : ugettext_lazy('bytes/min'),
|
||||
'cf' : 'AVERAGE',
|
||||
'vars' : {
|
||||
'size_recv' : { "type" : "AREA", "color" : "#FF9900",
|
||||
"legend" : ugettext_lazy("received size") },
|
||||
'size_sent' : { "type" : "AREA", "color" : "#339999",
|
||||
"legend" : ugettext_lazy("sent size") },
|
||||
}
|
||||
}
|
||||
@@ -1,57 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# coding: utf-8
|
||||
|
||||
import sys, os
|
||||
import time
|
||||
import datetime
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from modoboa.lib import parameters
|
||||
|
||||
traffic_avg_template = {
|
||||
'name' : 'traffic',
|
||||
'width' : '540',
|
||||
'height' : '100',
|
||||
'vertlabel' : 'msgs/min',
|
||||
'cf' : 'AVERAGE',
|
||||
'vars' : {
|
||||
'sent' : { "type" : "AREA", "color" : "#00FF00",
|
||||
"legend" : _("sent messages") },
|
||||
'recv' : { "type" : "AREA", "color" : "#0000FF",
|
||||
"legend" : _("received messages") },
|
||||
}
|
||||
}
|
||||
|
||||
badtraffic_avg_template = {
|
||||
'name' : 'badtraffic',
|
||||
'width' : '540',
|
||||
'height' : '100',
|
||||
'vertlabel' : _('msgs/min'),
|
||||
'cf' : 'AVERAGE',
|
||||
'vars' : {
|
||||
'bounced' : { "type" : "AREA", "color" : "#FFFF00",
|
||||
"legend" : _("bounced messages") },
|
||||
'reject' : { "type" : "AREA", "color" : "#FF0000",
|
||||
"legend" : _("rejected messages") },
|
||||
}
|
||||
}
|
||||
|
||||
size_avg_template = {
|
||||
'name' : 'size',
|
||||
'width' : '540',
|
||||
'height' : '100',
|
||||
'vertlabel' : _('bytes/min'),
|
||||
'cf' : 'AVERAGE',
|
||||
'vars' : {
|
||||
'size_recv' : { "type" : "AREA", "color" : "#FF9900",
|
||||
"legend" : _("received size") },
|
||||
'size_sent' : { "type" : "AREA", "color" : "#339999",
|
||||
"legend" : _("sent size") },
|
||||
}
|
||||
}
|
||||
|
||||
tpl = {'traffic':traffic_avg_template,
|
||||
'badtraffic':badtraffic_avg_template,
|
||||
'size':size_avg_template}
|
||||
|
||||
def str2Time(y, M, d, h="0", m="0", s="0"):
|
||||
"""Date conversion
|
||||
|
||||
@@ -77,7 +31,7 @@ class Grapher(object):
|
||||
self.rrd_rootdir = parameters.get_admin("RRD_ROOTDIR")
|
||||
self.img_rootdir = parameters.get_admin("IMG_ROOTDIR")
|
||||
|
||||
def process(self, target, suffix, start, end, tpl=traffic_avg_template):
|
||||
def process(self, target, suffix, start, end, tpl):
|
||||
import rrdtool
|
||||
|
||||
rrdfile = "%s/%s.rrd" % (self.rrd_rootdir, target)
|
||||
@@ -90,12 +44,17 @@ class Grapher(object):
|
||||
end = str(end)
|
||||
defs = []
|
||||
lines = []
|
||||
defs.append('COMMENT: %s %s %s %s\\c' % ("From", start, "to", end))
|
||||
defs.append('COMMENT:\\s')
|
||||
for v, d in tpl["vars"].iteritems():
|
||||
defs += [str('DEF:%s=%s:%s:%s' % (v, rrdfile, v, tpl['cf'])),
|
||||
str('CDEF:%spm=%s,60,*' % (v, v))]
|
||||
str('CDEF:%spm=%s,60,*' % (v, v)),
|
||||
str('VDEF:%s_total=%s,TOTAL' % (v, v))]
|
||||
type = d.has_key("type") and d["type"] or "LINE"
|
||||
lines += [str("%s:%spm%s:%s" % (type, v, d["color"],
|
||||
d["legend"].encode("utf-8")))]
|
||||
lines += [str("%s:%spm%s:%s:STACK" % (type, v, d["color"],
|
||||
(d["legend"].encode("utf8")).ljust(20)))]
|
||||
lines.append('GPRINT:%s_total:%s%%7.0lf%%s\\l' % (v, _("Total").encode('utf-8')))
|
||||
|
||||
params = defs + lines
|
||||
rrdtool.graph(str(path),
|
||||
"--imgformat", "PNG",
|
||||
@@ -107,12 +66,13 @@ class Grapher(object):
|
||||
"--lower-limit", "0",
|
||||
"--slope-mode",
|
||||
"--units-exponent", "0",
|
||||
"--title", tpl["title"].encode("utf-8"),
|
||||
*params)
|
||||
|
||||
if not os.path.exists(path):
|
||||
print "[graph] Impossible to create %s graph" %path
|
||||
|
||||
def make_defaults(self, target, tpl=traffic_avg_template):
|
||||
def make_defaults(self, target, tpl):
|
||||
end = "%d" % int(time.mktime(time.localtime()))
|
||||
self.process(target, "day", "-1day", end, tpl)
|
||||
self.process(target, "week", "-1week", end, tpl)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#fixed_content {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
#searchform {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#menubar {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#graphs {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#period {
|
||||
}
|
||||
|
||||
#custom-period {
|
||||
margin-left: 10px;
|
||||
}
|
||||
@@ -31,14 +31,6 @@ Stats.prototype = {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (navobj.params.period) {
|
||||
$("input[value=" + navobj.params.period + "]").attr("checked", "checked");
|
||||
if (navobj.params.period != "custom") {
|
||||
$("#custom-period").hide();
|
||||
}
|
||||
} else {
|
||||
$("#custom-period").hide();
|
||||
}
|
||||
if (navobj.params.start) {
|
||||
$("#id_from").attr("value", navobj.params.start);
|
||||
}
|
||||
@@ -55,8 +47,7 @@ Stats.prototype = {
|
||||
|
||||
listen: function() {
|
||||
$("#searchform").on("keypress", $.proxy(this.search_domain, this));
|
||||
$(document).on("change", "input[name=period]",
|
||||
$.proxy(this.change_period, this));
|
||||
$(".period_selector").click($.proxy(this.change_period, this));
|
||||
$("#customsend").on("click", $.proxy(this.customgraphs, this));
|
||||
},
|
||||
|
||||
@@ -66,15 +57,11 @@ Stats.prototype = {
|
||||
},
|
||||
|
||||
change_period: function(e) {
|
||||
var $input = $(e.target);
|
||||
e.preventDefault();
|
||||
var $link = $(e.target);
|
||||
|
||||
if ($input.attr("value") == "custom") {
|
||||
$("#custom-period").show();
|
||||
} else {
|
||||
$("#custom-period").hide();
|
||||
this.navobj.delparam("start").delparam("end");
|
||||
this.navobj.setparam("period", $input.attr("value")).update();
|
||||
}
|
||||
this.navobj.delparam("start").delparam("end");
|
||||
this.navobj.setparam("period", $link.attr("data-period")).update();
|
||||
},
|
||||
|
||||
graphs_cb: function(data) {
|
||||
@@ -82,7 +69,7 @@ Stats.prototype = {
|
||||
$("body").notify("error", data.respmsg);
|
||||
return;
|
||||
}
|
||||
$("#graphs").html(data.content);
|
||||
$("#graphs_mailstats").html(data.content);
|
||||
},
|
||||
|
||||
search_domain: function(e) {
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{% load i18n %}
|
||||
{% if start or end %}
|
||||
<h2>{% trans "Custom period : from" %} {{ start }} {% trans "to" %} {{ end }}</h2>
|
||||
{% endif %}
|
||||
{% for elems in graphs %}
|
||||
<h3>{% trans elems.label %}</h3>
|
||||
<p><img src="{{ MEDIA_URL }}stats/{{ elems.name }}_{{ domain }}_AVERAGE_{% ifequal period 'custom' %}{{ period_name }}{% else %}{{ period }}{% endifequal %}.png"
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "fixed.html" %}{% load i18n %}
|
||||
{% extends "twocols.html" %}{% load i18n %}
|
||||
{% block pagetitle %}{% trans "Graphical statistics" %}{% endblock %}
|
||||
{% block html_head %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}stats/css/stats.css" />
|
||||
@@ -13,24 +13,43 @@ $(document).ready(function() {
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block fixed_content %}
|
||||
<div id="menubar">
|
||||
<form id="searchform" class="form-search">
|
||||
<input class="input-medium search-query" type="text"
|
||||
value={% if squery %}"{{ squery }}"{% else %}"{% trans 'Search a domain' %}"{% endif %}
|
||||
id="searchquery" name="searchquery" />
|
||||
</form>
|
||||
<form id="period" class="form-inline">
|
||||
{% for p in periods %}<label class="radio"><input name="period" type="radio" value="{{ p.name }}"{% if p.name == period %}checked{% endif %} />{% trans p.label %}</label>
|
||||
{% block leftcol %}
|
||||
<div id="periods" class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">
|
||||
{% trans "Predefined time ranges" %}
|
||||
</li>
|
||||
{% for p in periods %}<li class="period">
|
||||
<a href="#" class="period_selector" data-period="{{ p.name }}">{% trans p.label %}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<span id="custom-period">
|
||||
<input id="id_from" class="input-small" name="date_from" value="{{ start }}" type="text" />
|
||||
<input id="id_to" class="input-small" name="date_to" value="{{ end }}" type="text" />
|
||||
<button class="btn" id="customsend">{% trans "Apply" %}</button>
|
||||
</span>
|
||||
</form>
|
||||
<li class="nav-header">
|
||||
{% trans "Custom time range" %}
|
||||
</li>
|
||||
<li>
|
||||
<form class="form-inline">
|
||||
<span id="custom-period">
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-indent-left"></i></span><input id="id_from" class="input-medium" name="date_from" value="{{ start }}" type="text" />
|
||||
</div>
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-indent-right"></i></span><input id="id_to" class="input-medium" name="date_to" value="{{ end }}" type="text" />
|
||||
</div>
|
||||
<button class="btn" id="customsend">{% trans "Apply" %}</button>
|
||||
</span>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block apparea %}
|
||||
<div id="graphs" class="tabbable">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#graphs_mailstats" data-toggle="tab">{% trans "Mail trafic" %}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="graphs_mailstats"></div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div id="graphs"></div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -6,16 +6,12 @@ from django.contrib.auth.decorators \
|
||||
import login_required, user_passes_test, permission_required
|
||||
from modoboa.extensions.stats.grapher import *
|
||||
from modoboa.lib.exceptions import *
|
||||
from graph_templates import *
|
||||
|
||||
graph_types = ['AVERAGE', 'MAX']
|
||||
graph_list = [{"name" : "traffic", "label" : ugettext_lazy("Average normal traffic")},
|
||||
{"name" : "badtraffic", "label" : ugettext_lazy("Average bad traffic")},
|
||||
{"name" : "size", "label" : ugettext_lazy("Average normal traffic size")}]
|
||||
periods = [{"name" : "day", "label" : ugettext_lazy("Day")},
|
||||
{"name" : "week", "label" : ugettext_lazy("Week")},
|
||||
{"name" : "month", "label" : ugettext_lazy("Month")},
|
||||
{"name" : "year", "label" : ugettext_lazy("Year")},
|
||||
{"name" : "custom", "label" : ugettext_lazy("Custom")}]
|
||||
{"name" : "year", "label" : ugettext_lazy("Year")}]
|
||||
|
||||
@login_required
|
||||
@permission_required("admin.view_mailboxes")
|
||||
@@ -31,7 +27,6 @@ def index(request):
|
||||
|
||||
period = request.GET.get("period", "day")
|
||||
return _render(request, 'stats/index.html', {
|
||||
"graphs" : graph_list,
|
||||
"periods" : periods,
|
||||
"period" : period,
|
||||
"selection" : "stats",
|
||||
@@ -45,7 +40,7 @@ def graphs(request):
|
||||
if not view:
|
||||
raise ModoboaException(_("Invalid request"))
|
||||
period = request.GET.get("period", "day")
|
||||
tplvars = dict(graphs=graph_list, period=period)
|
||||
tplvars = dict(graphs=[], period=period)
|
||||
if view == "global":
|
||||
if not request.user.is_superuser:
|
||||
raise PermDeniedException
|
||||
@@ -66,12 +61,13 @@ def graphs(request):
|
||||
end = request.GET["end"]
|
||||
G = Grapher()
|
||||
period_name = "%s_%s" % (start.replace('-',''), end.replace('-',''))
|
||||
for tpl_name in graph_list:
|
||||
for tpl in [traffic_avg_template, badtraffic_avg_template, size_avg_template]:
|
||||
tplvars['graphs'].append(tpl['name'])
|
||||
G.process(tplvars["domain"],
|
||||
period_name,
|
||||
str2Time(*start.split('-')),
|
||||
str2Time(*end.split('-')),
|
||||
tpl[tpl_name['name']])
|
||||
tpl)
|
||||
tplvars["period_name"] = period_name
|
||||
tplvars["start"] = start
|
||||
tplvars["end"] = end
|
||||
|
||||
Reference in New Issue
Block a user