Merge pull request #1021 from modoboa/feature/move_global_stats_to_dashboard

Move global statistics to admin dashboard.
This commit is contained in:
Antoine Nguyen
2017-02-07 14:17:04 +01:00
committed by GitHub
13 changed files with 119 additions and 129 deletions
+23
View File
@@ -300,3 +300,26 @@ def grant_access_to_all_objects(sender, account, role, **kwargs):
account, model.objects.all(),
ContentType.objects.get_for_model(model)
)
@receiver(core_signals.extra_admin_dashboard_widgets)
def add_widgets_to_admin_dashboard(sender, user, **kwargs):
"""Add admin widgets to dashboard."""
if user.is_superuser:
context = {
"domains_counter": models.Domain.objects.count(),
"domain_aliases_counter": models.DomainAlias.objects.count(),
"identities_counter": (
core_models.User.objects.count() +
models.Alias.objects.filter(internal=False).count()),
}
template = "admin/_global_statistics_widget.html"
else:
context = {
"domains": models.Domain.objects.get_for_admin(user)}
template = "admin/_per_domain_statistics_widget.html"
return [{
"column": "right",
"template": template,
"context": context
}]
@@ -0,0 +1,20 @@
{% load i18n %}
<div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">{% trans "Global statistics" %}</h3></div>
<table class="table">
<tr>
<th>{% trans "Type" %}</th>
<th>{% trans "Quantity" %}</th>
</tr>
<tr>
<td>{% trans "Domains" %}</td><td>{{ domains_counter }}</td>
</tr>
<tr>
<td>{% trans "Domain aliases" %}</td><td>{{ domain_aliases_counter }}</td>
</tr>
<tr>
<td>{% trans "Identities" %}</td><td>{{ identities_counter }}</td>
</tr>
</table>
</div>
@@ -0,0 +1,17 @@
{% load i18n %}
<div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">{% trans "Per-domain statistics" %}</h3></div>
<table class="table">
<tr>
<th>{% trans "Domain" %}</th>
<th>{% trans "Identities" %}</th>
</tr>
{% for domain in domains %}
<tr>
<td><a href="{% url 'admin:domain_detail' domain.pk %}">{{ domain.name }}</a></td>
<td>{{ domain.identities_count }}</td>
</tr>
{% endfor %}
</table>
</div>
@@ -1,63 +0,0 @@
{% extends "admin/header.html" %}
{% load i18n %}
{% load admin_tags %}
{% block leftcol %}
<ul class="nav nav-sidebar">
{% domains_menu "statistics" user False %}
</ul>
{% get_extra_admin_content user "leftcol" selection %}
{% endblock %}
{% block apparea %}
<h2>{% trans "Statistics" %} <small>{% trans "Domains and identities" %}</small></h2>
<hr>
<div class="row">
{% if user.role == "SuperAdmins" %}
<div class="col-xs-12 col-sm-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">{% trans "Global counters" %}</h3></div>
<div class="panel-body"></div>
<table class="table">
<tr>
<th>{% trans "Type" %}</th>
<th>{% trans "Quantity" %}</th>
</tr>
<tr>
<td>{% trans "Domains" %}</td><td>{{ domains_counter }}</td>
</tr>
<tr>
<td>{% trans "Domain aliases" %}</td><td>{{ domain_aliases_counter }}</td>
</tr>
<tr>
<td>{% trans "Identities" %}</td><td>{{ identities_counter }}</td>
</tr>
</table>
</div>
</div>
{% endif %}
<div class="col-xs-12 col-sm-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">{% trans "Per-domain counters" %}</h3></div>
<div class="panel-body"></div>
<table class="table">
<tr>
<th>{% trans "Domain" %}</th>
<th>{% trans "Identities" %}</th>
</tr>
{% for domain in domains %}
<tr>
<td>{{ domain.name }}</td><td>{{ domain.identities_count }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
{% endblock %}
-5
View File
@@ -40,11 +40,6 @@ def domains_menu(selection, user, ajax_mode=True):
"img": "fa fa-user",
"class": "ajaxnav navigation",
"url": domain_list_url},
{"name": "statistics",
"label": _("Statistics"),
"img": "fa fa-line-chart",
"class": "navigation",
"url": reverse("admin:domain_statistics")}
]
if user.has_perm("admin.add_domain"):
extra_entries = signals.extra_domain_menu_entries.send(
+6 -7
View File
@@ -203,17 +203,16 @@ class DomainTestCase(ModoTestCase):
response = self.client.get(url)
self.assertIn("Resources usage", response.content)
def test_domain_statitics_view(self):
"""Test statistics display."""
url = reverse("admin:domain_statistics")
def test_statitics_widget(self):
"""Test statistics display in dashboard."""
url = reverse("core:dashboard")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn("Global counters", response.content)
self.assertIn("Per-domain counters", response.content)
self.assertIn("Global statistics", response.content)
self.client.force_login(
User.objects.get(username="admin@test.com"))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertNotIn("Global counters", response.content)
self.assertIn("Per-domain counters", response.content)
self.assertNotIn("Global statistics", response.content)
self.assertIn("Per-domain statistics", response.content)
-2
View File
@@ -18,8 +18,6 @@ urlpatterns = patterns(
name="mx_domain_detail"),
url(r'^domains/list/$', '_domains', name="_domain_list"),
url(r'^domains/flatlist/$', 'domains_list', name="domain_flat_list"),
url(r'^domains/stats/$', views.DomainStatisticsView.as_view(),
name="domain_statistics"),
url(r'^domains/new/', 'newdomain', name='domain_add'),
url(r'^domains/(?P<dom_id>\d+)/edit/$', 'editdomain',
name="domain_change"),
+1 -2
View File
@@ -3,7 +3,7 @@
from .alias import AliasDetailView
from .identity import AccountDetailView
from .dns import DNSBLDomainDetailView, MXDomainDetailView
from .domain import DomainDetailView, DomainStatisticsView
from .domain import DomainDetailView
__all__ = [
@@ -11,6 +11,5 @@ __all__ = [
"AliasDetailView",
"DNSBLDomainDetailView",
"DomainDetailView",
"DomainStatisticsView",
"MXDomainDetailView",
]
+2 -27
View File
@@ -14,7 +14,6 @@ from django.contrib.auth.decorators import (
from reversion import revisions as reversion
from modoboa.core.models import User
from modoboa.core import signals as core_signals
from modoboa.lib.web_utils import (
_render_to_string, render_to_json_response
@@ -26,9 +25,7 @@ from modoboa.lib.listing import get_sort_order, get_listing_page
from ..forms import DomainForm, DomainWizard
from ..lib import get_domains
from ..models import (
Domain, DomainAlias, Mailbox, Alias
)
from ..models import Domain, Mailbox
from .. import signals
@@ -156,29 +153,6 @@ def deldomain(request, dom_id):
return render_to_json_response(msg)
class DomainStatisticsView(
auth_mixins.PermissionRequiredMixin, generic.TemplateView):
"""A simple page to present domain statistics."""
template_name = "admin/domain_statistics.html"
permission_required = "admin.view_domains"
def get_context_data(self, **kwargs):
"""Add counters."""
context = super(DomainStatisticsView, self).get_context_data(**kwargs)
if self.request.user.role == "SuperAdmins":
context.update({
"domains_counter": Domain.objects.count(),
"domain_aliases_counter": DomainAlias.objects.count(),
"identities_counter": (
User.objects.count() +
Alias.objects.filter(internal=False).count()),
})
context.update({
"domains": Domain.objects.get_for_admin(self.request.user)})
return context
class DomainDetailView(
auth_mixins.PermissionRequiredMixin, generic.DetailView):
"""DetailView for Domain."""
@@ -208,6 +182,7 @@ class DomainDetailView(
for widget in widgets:
context["templates"][widget["column"]].append(
widget["template"])
# FIXME: can raise conflicts...
context.update(widget["context"])
return context
+2
View File
@@ -15,6 +15,8 @@ allow_password_change = django.dispatch.Signal(providing_args=["user"])
can_create_object = django.dispatch.Signal(
providing_args=["context", "object_type", "count"])
extra_account_actions = django.dispatch.Signal(providing_args=["account"])
extra_admin_dashboard_widgets = django.dispatch.Signal(
providing_args=["user"])
extra_admin_menu_entries = django.dispatch.Signal(
providing_args=["location", "user"])
extra_role_permissions = django.dispatch.Signal(providing_args=["role"])
@@ -0,0 +1,23 @@
{% load i18n %}
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">{% trans "Latest news" %}</h3>
</div>
<table class="table">
<tbody>
{% for post in news %}
<tr>
<td><a href="{{ post.link }}" target="_blank">{{ post.title }}</a></td>
<td class="text-right">{{ post.published|date:"DATE_FORMAT" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="panel-body text-center">
{% blocktrans trimmed %}
Visit the <a href="https://modoboa.org/blog/" target="_blank">official weblog</a> for more information.
{% endblocktrans %}
</div>
</div>
+8 -21
View File
@@ -10,27 +10,14 @@
<div class="row">
<div class="col-xs-12 col-sm-6">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">{% trans "Latest news" %}</h3>
</div>
<table class="table">
<tbody>
{% for post in news %}
<tr>
<td><a href="{{ post.link }}" target="_blank">{{ post.title }}</a></td>
<td class="text-right">{{ post.published|date:"DATE_FORMAT" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="panel-body text-center">
{% blocktrans trimmed %}
Visit the <a href="https://modoboa.org/blog/" target="_blank">official weblog</a> for more information.
{% endblocktrans %}
</div>
</div>
{% for widget in widgets.left %}
{% include widget %}
{% endfor %}
</div>
<div class="col-xs-12 col-sm-6">
{% for widget in widgets.right %}
{% include widget %}
{% endfor %}
</div>
</div>
+17 -2
View File
@@ -7,6 +7,8 @@ from django.views import generic
from django.contrib.auth import mixins as auth_mixins
from .. import signals
class DashboardView(auth_mixins.AccessMixin, generic.TemplateView):
"""Dashboard view."""
@@ -15,14 +17,17 @@ class DashboardView(auth_mixins.AccessMixin, generic.TemplateView):
def dispatch(self, request, *args, **kwargs):
"""Check if user can access dashboard."""
if not request.user.is_admin:
if not request.user.is_authenticated() or not request.user.is_admin:
return self.handle_no_permission()
return super(DashboardView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
"""Add context variables."""
context = super(DashboardView, self).get_context_data(**kwargs)
context.update({"selection": "dashboard"})
context.update({
"selection": "dashboard", "widgets": {"left": [], "right": []}
})
# Fetch latest news
if self.request.user.language == "fr":
lang = "fr"
else:
@@ -33,5 +38,15 @@ class DashboardView(auth_mixins.AccessMixin, generic.TemplateView):
for entry in posts["entries"][:5]:
entry["published"] = parser.parse(entry["published"])
entries.append(entry)
context["widgets"]["left"].append("core/_latest_news_widget.html")
context.update({"news": entries})
# Extra widgets
result = signals.extra_admin_dashboard_widgets.send(
sender=self.__class__, user=self.request.user)
for receiver, widgets in result:
for widget in widgets:
context["widgets"][widget["column"]].append(
widget["template"])
# FIXME: can raise conflicts...
context.update(widget["context"])
return context