mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
@@ -21,6 +21,8 @@ class Stats(ModoExtension):
|
||||
needs_media = True
|
||||
|
||||
def load(self):
|
||||
events.registerEvent("GetGraphSets")
|
||||
|
||||
parameters.register_admin("LOGFILE", type="string",
|
||||
deflt="/var/log/mail.log",
|
||||
help=ugettext_lazy("Path to log file used to collect statistics"))
|
||||
@@ -45,3 +47,10 @@ def menu(target, user):
|
||||
"label" : _("Statistics"),
|
||||
"url" : reverse('fullindex')}
|
||||
]
|
||||
|
||||
@events.observe("GetGraphSets")
|
||||
def get_default_graph_sets():
|
||||
from graph_templates import MailTraffic
|
||||
|
||||
gset = MailTraffic()
|
||||
return {gset.html_id: gset}
|
||||
|
||||
@@ -2,50 +2,73 @@
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
class MailTrafic(object):
|
||||
pass
|
||||
class Graph(object):
|
||||
width = 540
|
||||
height = 120
|
||||
cf = 'AVERAGE'
|
||||
|
||||
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") },
|
||||
}
|
||||
}
|
||||
def __init__(self, **ds):
|
||||
self.vars = {}
|
||||
for dsname, dsdef in ds.iteritems():
|
||||
self.vars[dsname] = dsdef
|
||||
|
||||
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") },
|
||||
}
|
||||
}
|
||||
@property
|
||||
def display_name(self):
|
||||
return self.__class__.__name__.lower()
|
||||
|
||||
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") },
|
||||
}
|
||||
}
|
||||
class AvgTraffic(Graph):
|
||||
title = ugettext_lazy('Average normal traffic')
|
||||
vertlabel = ugettext_lazy('msgs/min')
|
||||
|
||||
def __init__(self):
|
||||
super(AvgTraffic, self).__init__(
|
||||
sent={'type' : 'AREA', 'color' : '#00FF00',
|
||||
"legend" : ugettext_lazy("sent messages")},
|
||||
recv={"type" : "AREA", "color" : "#0000FF",
|
||||
"legend" : ugettext_lazy("received messages")}
|
||||
)
|
||||
|
||||
class AvgBadTraffic(Graph):
|
||||
title = ugettext_lazy('Average bad traffic')
|
||||
vertlabel = ugettext_lazy('msgs/min')
|
||||
|
||||
def __init__(self):
|
||||
super(AvgBadTraffic, self).__init__(
|
||||
bounced={'type' : 'AREA', 'color' : '#FFFF00',
|
||||
"legend" : ugettext_lazy("bounced messages")},
|
||||
reject={"type" : "AREA", "color" : "#FF0000",
|
||||
"legend" : ugettext_lazy("rejected messages")}
|
||||
)
|
||||
|
||||
class AvgTrafficSize(Graph):
|
||||
title = ugettext_lazy('Average normal traffic size')
|
||||
vertlabel = ugettext_lazy('bytes/min')
|
||||
|
||||
def __init__(self):
|
||||
super(AvgTrafficSize, self).__init__(
|
||||
size_recv={'type' : 'AREA', 'color' : '#FF9900',
|
||||
"legend" : ugettext_lazy("received size")},
|
||||
size_sent={"type" : "AREA", "color" : "#339999",
|
||||
"legend" : ugettext_lazy("sent size")}
|
||||
)
|
||||
|
||||
|
||||
class GraphSet(object):
|
||||
title = None
|
||||
graphs = []
|
||||
|
||||
@property
|
||||
def html_id(self):
|
||||
return self.__class__.__name__.lower()
|
||||
|
||||
def get_graphs(self):
|
||||
if not hasattr(self, '__graph_instances'):
|
||||
self.__graph_instances = map(lambda gc: gc(), self.graphs)
|
||||
return self.__graph_instances
|
||||
|
||||
def get_graph_names(self):
|
||||
return map(lambda g: g.display_name, self.get_graphs())
|
||||
|
||||
class MailTraffic(GraphSet):
|
||||
title = ugettext_lazy('Mail traffic')
|
||||
graphs = [AvgTraffic, AvgBadTraffic, AvgTrafficSize]
|
||||
|
||||
@@ -28,8 +28,8 @@ from optparse import make_option
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.admin.models import Domain
|
||||
from modoboa.extensions.stats import Stats
|
||||
from modoboa.extensions.stats.grapher import Grapher
|
||||
from modoboa.extensions.stats.graph_templates import *
|
||||
from modoboa.extensions.stats.grapher import *
|
||||
from modoboa.extensions.stats.graph_templates import MailTraffic
|
||||
|
||||
rrdstep = 60
|
||||
xpoints = 540
|
||||
@@ -213,7 +213,7 @@ class LogParser(object):
|
||||
se = int(int(se) / rrdstep) # rrd step is one-minute => se = 0
|
||||
|
||||
if prev_se != se or prev_mi != mi or prev_ho != ho:
|
||||
cur_t = grapher.str2Time(self.year(mo), mo, da, ho, mi, se)
|
||||
cur_t = str2Time(self.year(mo), mo, da, ho, mi, se)
|
||||
cur_t = cur_t - cur_t % rrdstep
|
||||
prev_mi = mi
|
||||
prev_ho = ho
|
||||
@@ -274,10 +274,8 @@ class LogParser(object):
|
||||
for t in sorted(data.keys()):
|
||||
self.update_rrd(dom, t)
|
||||
|
||||
G.make_defaults(dom, tpl=traffic_avg_template)
|
||||
G.make_defaults(dom, tpl=badtraffic_avg_template)
|
||||
G.make_defaults(dom, tpl=size_avg_template)
|
||||
|
||||
for graph_tpl in MailTraffic().get_graphs():
|
||||
G.make_defaults(dom, graph_tpl)
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Log file parser'
|
||||
|
||||
@@ -69,7 +69,7 @@ Stats.prototype = {
|
||||
$("body").notify("error", data.respmsg);
|
||||
return;
|
||||
}
|
||||
$("#graphs_mailstats").html(data.content);
|
||||
$(".tab-pane.active").html(data.content);
|
||||
},
|
||||
|
||||
search_domain: function(e) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
$(document).ready(function() {
|
||||
var stats = new Stats({
|
||||
graphurl: "{% url modoboa.extensions.stats.views.graphs %}",
|
||||
deflocation: "{{ deflocation }}"
|
||||
deflocation: "{{ deflocation|safe }}"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -44,12 +44,14 @@ $(document).ready(function() {
|
||||
{% 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>
|
||||
<ul class="nav nav-tabs">{% for id, gset in graph_sets.items %}
|
||||
<li {% if forloop.first %}class="active"{% endif %}>
|
||||
<a href="#graphs_{{ gset.html_id }}" data-toggle="tab">{{ gset.title }}</a>
|
||||
</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>{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ 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 modoboa.lib import events
|
||||
from graph_templates import *
|
||||
|
||||
periods = [{"name" : "day", "label" : ugettext_lazy("Day")},
|
||||
@@ -16,27 +17,36 @@ periods = [{"name" : "day", "label" : ugettext_lazy("Day")},
|
||||
@login_required
|
||||
@permission_required("admin.view_mailboxes")
|
||||
def index(request):
|
||||
deflocation = ""
|
||||
"""
|
||||
FIXME: how to select a default graph set ?
|
||||
"""
|
||||
deflocation = "graphs/?gset=mailtraffic&"
|
||||
if request.user.is_superuser:
|
||||
deflocation = "graphs/?view=global"
|
||||
deflocation += "view=global"
|
||||
else:
|
||||
if len(request.user.get_domains()):
|
||||
deflocation = "graphs/?view=%s" % request.user.get_domains()[0].name
|
||||
deflocation += "view=%s" % request.user.get_domains()[0].name
|
||||
else:
|
||||
raise ModoboaException(_("No statistics available"))
|
||||
|
||||
period = request.GET.get("period", "day")
|
||||
graph_sets = events.raiseDictEvent('GetGraphSets')
|
||||
return _render(request, 'stats/index.html', {
|
||||
"periods" : periods,
|
||||
"period" : period,
|
||||
"selection" : "stats",
|
||||
"deflocation" : deflocation
|
||||
"deflocation" : deflocation,
|
||||
"graph_sets" : graph_sets
|
||||
})
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.group != "SimpleUsers")
|
||||
def graphs(request):
|
||||
view = request.GET.get("view", None)
|
||||
gset = request.GET.get("gset", None)
|
||||
gsets = events.raiseDictEvent("GetGraphSets")
|
||||
if not gset in gsets:
|
||||
raise ModoboaException(_("Unknown graphic sets"))
|
||||
if not view:
|
||||
raise ModoboaException(_("Invalid request"))
|
||||
period = request.GET.get("period", "day")
|
||||
@@ -61,19 +71,15 @@ def graphs(request):
|
||||
end = request.GET["end"]
|
||||
G = Grapher()
|
||||
period_name = "%s_%s" % (start.replace('-',''), end.replace('-',''))
|
||||
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)
|
||||
for tpl in gsets[gset].get_graphs():
|
||||
tplvars['graphs'].append(tpl.display_name)
|
||||
G.process(tplvars["domain"], period_name, str2Time(*start.split('-')),
|
||||
str2Time(*end.split('-')), tpl)
|
||||
tplvars["period_name"] = period_name
|
||||
tplvars["start"] = start
|
||||
tplvars["end"] = end
|
||||
else:
|
||||
# FIXME
|
||||
tplvars['graphs'] = ['traffic', 'badtraffic', 'size']
|
||||
tplvars['graphs'] = gsets[gset].get_graph_names()
|
||||
|
||||
return ajax_simple_response(dict(
|
||||
status="ok", content=_render_to_string(request, "stats/graphs.html", tplvars)
|
||||
|
||||
+29
-2
@@ -63,6 +63,14 @@ events = [
|
||||
|
||||
callbacks = {}
|
||||
|
||||
def registerEvent(name):
|
||||
"""Register a new event
|
||||
|
||||
:param name: the event's name
|
||||
"""
|
||||
if not name in events:
|
||||
events.append(name)
|
||||
|
||||
def register(event, callback):
|
||||
"""Register a plugin callback for a specific event
|
||||
|
||||
@@ -71,8 +79,9 @@ def register(event, callback):
|
||||
:param event: the event to listen to
|
||||
:param callback: a function to execute when the event is raised
|
||||
"""
|
||||
if not event in events:
|
||||
return 0
|
||||
# if not event in events:
|
||||
# print "Event %s not registered" % event
|
||||
# return 0
|
||||
if not event in callbacks.keys():
|
||||
callbacks[event] = {}
|
||||
fullname = "%s.%s" % (callback.__module__, callback.__name__)
|
||||
@@ -172,3 +181,21 @@ def raiseQueryEvent(event, *args):
|
||||
for name, callback in callbacks[event].iteritems():
|
||||
result += callback(*args)
|
||||
return result
|
||||
|
||||
def raiseDictEvent(event, *args):
|
||||
"""Raise a specific event and return result as a dictionnary
|
||||
|
||||
Any additional keyword argument will be passed to registered
|
||||
callbacks. Callback answers must be dictionnaries.
|
||||
|
||||
:param event: the event to raise
|
||||
:return: a dictionnary
|
||||
"""
|
||||
result = {}
|
||||
if not event in events or not event in callbacks.keys():
|
||||
return result
|
||||
for name, callback in callbacks[event].iteritems():
|
||||
tmp = callback(*args)
|
||||
for k, v in tmp.iteritems():
|
||||
result[k] = v
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user