*Stats* extension ported to jquery/bootstrap.

see #204
This commit is contained in:
Antoine Nguyen
2012-04-12 14:52:46 +02:00
parent e113a7cb82
commit c6dbb5f7a7
7 changed files with 205 additions and 151 deletions
+3 -4
View File
@@ -35,13 +35,12 @@ def load():
def destroy():
events.unregister("AdminMenuDisplay", menu)
@events.observe("AdminMenuDisplay")
@events.observe("UserMenuDisplay")
def menu(target, user):
if target == "admin_menu_box":
if target == "top_menu":
return [
{"name" : "stats",
"label" : ugettext("Statistics"),
"url" : reverse('fullindex'),
"img" : static_url("pics/graph.png")}
"url" : reverse('fullindex')}
]
return []
+24
View File
@@ -0,0 +1,24 @@
#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;
}
+114
View File
@@ -0,0 +1,114 @@
var Stats = function(options) {
this.initialize(options);
};
Stats.prototype = {
constructor: Stats,
defaults: {
deflocation: "graphs/?view=global"
},
initialize: function(options) {
this.options = $.extend({}, this.defaults, options);
this.options.defcallback = $.proxy(this.graphs_cb, this);
var navobj = new History(this.options);
this.navobj = navobj;
if (navobj.params.view != undefined) {
$("#searchquery").attr("value", navobj.params.view);
}
$("#searchquery").focus(function() {
$(this).attr("value", "");
}).blur(function() {
var $this = $(this);
if ($this.attr("value") == "") {
if (navobj.params["view"] == "global") {
$this.attr("value", gettext("Search a domain"));
} else {
$this.attr("value", navobj.params.view);
}
}
});
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);
}
if (navobj.params.end) {
$("#id_to").attr("value", navobj.params.end);
}
$("#custom-period input").datepicker({
format: 'yyyy-mm-dd'
});
this.register_nav_callbacks();
this.listen();
},
listen: function() {
$("#searchform").on("keypress", $.proxy(this.search_domain, this));
$(document).on("change", "input[name=period]",
$.proxy(this.change_period, this));
$("#customsend").on("click", $.proxy(this.customgraphs, this));
},
register_nav_callbacks: function() {
this.navobj.register_callback("graphs",
$.proxy(this.graphs_cb, this));
},
change_period: function(e) {
var $input = $(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();
}
},
graphs_cb: function(data) {
if (data.status == "ko") {
$("body").notify("error", data.respmsg);
return;
}
$("#graphs").html(data.content);
},
search_domain: function(e) {
var $input = $(e.target);
switch (e.which) {
case 13:
e.preventDefault();
var domain = ($input.attr("value") == "") ? "global" : $input.attr("value");
this.navobj.setparam("view", domain).update();
break;
}
},
customgraphs: function(e) {
e.preventDefault();
var $fromdate = $("#id_from");
var $todate = $("#id_to");
if ($fromdate.attr("value") == "" || $todate.attr("value") == "") {
return;
}
this.navobj.setparams({
period: "custom",
start: $fromdate.attr("value"),
end: $todate.attr("value")
}).update();
}
};
+26 -106
View File
@@ -1,115 +1,35 @@
{% extends "admin/domains_header.html" %}
{% extends "fixed.html" %}
{% load i18n %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/calendar.css" />
<style type="text/css">
#selectors {
}
#periodbox, #domainbox {
float: left;
margin-right: 20px;
}
#graphs {
margin-top: 15px;
float: none;
}
#customperiod {
display: none;
border: 1px solid #dddddd;
float: left;
padding: 3px;
margin-top: -3px;
}
</style>
<script type="text/javascript" src="{{ MEDIA_URL }}js/calendar.js"></script>
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}stats/css/stats.css" />
<script type="text/javascript" src="{{ MEDIA_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}stats/js/stats.js"></script>
<script type="text/javascript">
window.addEvent("domready", function() {
var myCal = new Calendar({
dateTravelStart: 'Y-m-d',
dateTravelEnd : 'Y-m-d'
}, {
direction: 0, // -1 past, 0 past + future, 1 future
pad: 1,
offset: 1, // first day of the week: 0 = sunday, 1 = monday, etc..
tweak: {x: -147, y: 18} // tweak calendar positioning
});
getgraph_callback = function(resp) {
if (resp.status == "ok") {
$("graphs").set("html", resp.content);
} else {
infobox.error(resp.error);
}
};
get_period = function() {
if ($("period").value != "custom") {
$("customperiod").setStyle("display", "none");
return "period=" + $("period").value;
}
$("customperiod").setStyle("display", "block");
if ($("dateTravelStart").value == "" || $("dateTravelEnd").value == "") {
return "";
}
return "period=custom"
+ "&start=" + $("dateTravelStart").value
+ "&end=" + $("dateTravelEnd").value;
};
get_graph = function(evt) {
var url = "{% url getgraph_root %}" + $("domain").value + "/";
var p = get_period();
if (p == "") {
return;
}
new Request.JSON.mdb({url : url + "?" + p, noCache: true,
onSuccess : getgraph_callback}).get();
};
$("domain").addEvent("change", get_graph);
$("period").addEvent("change", get_graph);
$("customsend").addEvent("click", get_graph);
$(document).ready(function() {
var stats = new Stats({
graphurl: "{% url modoboa.extensions.stats.views.graphs %}"
});
});
</script>
{% endblock %}
{% block pagetitle %}{% trans "Statistics" %}{% endblock %}
{% block subcontent %}
<div id="selectors">
<div id="domainbox">
<label for="domain">{% trans "Domain:" %}</label>
<select id="domain" name="domain">
{% if user.is_superuser %}
<option value="global" {% ifequal domain "global" %}selected{% endifequal %}>{% trans "Global" %}</option>
{% endif %}
{% for d in domains %}
<option value="{{ d.id }}" {% ifequal d.name domain %}selected{% endifequal %}>{{ d.name }}</option>
{% endfor %}
</select>
</div>
<div id="periodbox">
<label for="period">{% trans "Period:" %}</label>
<select id="period" name="period">
{% for p in periods %}
<option value="{{ p.name }}" {% ifequal p.name period %}selected{% endifequal %}>{% trans p.label %}</option>
{% endfor %}
</select>
</div>
<div id="customperiod">
{% trans "Start" %}: <input id="dateTravelStart" name="start"
value="{{ start }}" type="text"/>
{% trans "End" %}: <input id="dateTravelEnd" name="end"
value="{{ end }}" type="text" />
<input type="button" id="customsend" value="ok">
</div>
</div>
<div class="spacer"></div>
<div id="graphs">
{% include "stats/graphs.html" %}
{% 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>
{% 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>
</div>
<hr>
<div id="graphs"></div>
{% endblock %}
+1 -2
View File
@@ -2,8 +2,7 @@ from django.conf.urls.defaults import *
urlpatterns = patterns('modoboa.extensions.stats.views',
url(r'^$', 'index', name='fullindex'),
url(r'^graph/$', "getgraph", name="getgraph_root"),
url(r'^graph/(?P<dom_id>\w+)/$', "getgraph", name="getgraph"),
url(r'^graphs/$', "graphs"),
)
+33 -29
View File
@@ -1,15 +1,15 @@
# -*- coding: utf-8 -*-
# coding: utf-8
from django.http import HttpResponse
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, ugettext_noop
from django.utils import simplejson
from modoboa.lib.webutils import _render, _render_to_string, getctx
from modoboa.lib.webutils import _render, _render_to_string, ajax_simple_response
from modoboa.lib.permissions import check_domain_access
from modoboa.admin.models import Domain, Mailbox
from django.contrib.auth.decorators \
import login_required, user_passes_test, permission_required
from modoboa.extensions.stats.grapher import *
from modoboa.lib.exceptions import *
graph_types = ['AVERAGE', 'MAX']
graph_list = [{"name" : "traffic", "label" : ugettext_noop("Average normal traffic")},
@@ -41,39 +41,43 @@ def index(request):
})
@login_required
@permission_required("admin.view_mailboxes")
def getgraph(request, dom_id):
@user_passes_test(lambda u: u.group != "SimpleUsers")
def graphs(request):
view = request.GET.get("view", None)
if not view:
raise ModoboaException(_("Invalid request"))
period = request.GET.get("period", "day")
if dom_id == "global":
tplvars = dict(graphs=graph_list, period=period)
if view == "global":
if not request.user.is_superuser:
raise PermDeniedError(_("you're not allowed to see those statistics"))
domain = dom_id
tplvars.update(domain=view)
else:
domain = Domain.objects.get(pk=dom_id)
try:
domain = Domain.objects.get(name=view)
except Domain.DoesNotExist:
raise ModoboaException(_("Domain not found. Please enter a full name"))
if not request.user.can_access(domain):
raise PermDeniedError(_("You don't have access to this domain"))
tplvars.update(domain=domain.name)
ctx = None
tplvars = {"graphs" : graph_list, "period" : period, "domain" : domain.name}
if period == "custom":
if not request.GET.has_key("start") or not request.GET.has_key("end"):
ctx = getctx("ko", error=_("Bad custom period"))
else:
start = request.GET["start"]
end = request.GET["end"]
G = Grapher()
period_name = "%s_%s" % (start.replace('-',''), end.replace('-',''))
for tpl_name in graph_list:
G.process(domain.name,
period_name,
str2Time(*start.split('-')),
str2Time(*end.split('-')),
tpl[tpl_name['name']])
tplvars["period_name"] = period_name
tplvars["start"] = start
tplvars["end"] = end
if ctx is None:
content = _render_to_string(request, "stats/graphs.html", tplvars)
ctx = getctx("ok", content=content)
raise ModoboaException(_("Bad custom period"))
start = request.GET["start"]
end = request.GET["end"]
G = Grapher()
period_name = "%s_%s" % (start.replace('-',''), end.replace('-',''))
for tpl_name in graph_list:
G.process(tplvars["domain"],
period_name,
str2Time(*start.split('-')),
str2Time(*end.split('-')),
tpl[tpl_name['name']])
tplvars["period_name"] = period_name
tplvars["start"] = start
tplvars["end"] = end
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
return ajax_simple_response(dict(
status="ok", content=_render_to_string(request, "stats/graphs.html", tplvars)
))
+4 -10
View File
@@ -31,7 +31,7 @@ History.prototype = {
load_params: function() {
var rawqs = window.location.hash.substr(1);
if (rawqs.indexOf('?')) {
if (rawqs.indexOf('?') == -1) {
this.params = {};
return;
}
@@ -118,12 +118,6 @@ History.prototype = {
}
},
deleteParam: function(str) {
if (this.params[str] != undefined) {
delete this.params[str];
}
},
updateparams: function(str) {
if (str.charAt(0) == '?') {
str = str.substring(1);
@@ -179,10 +173,10 @@ History.prototype = {
},
delparam: function(name) {
if (!this.params.has(name)) {
return false;
if (this.params[name] == undefined) {
return this;
}
this.params.erase(name);
delete(this.params[name]);
return this;
},