[damned-lies] Add ability for authorized persons to refresh statistics



commit 2e612a8bae7fb79113ff81a708ffd8cb19dc2e71
Author: Claude Paroz <claude 2xlibre net>
Date:   Sat Mar 30 14:59:03 2013 +0100

    Add ability for authorized persons to refresh statistics
    
    Currently, team coordinators and members of the global i18n
    coordination team are allowed to proceed with this new action.

 common/static/img/refresh.png |  Bin 0 -> 827 bytes
 people/models.py              |   24 +++++++++++++++-------
 stats/models.py               |    7 +++++-
 stats/views.py                |   42 ++++++++++++++++++++++++++++++++++++++--
 templates/module_detail.html  |   10 ++++++++-
 urls.py                       |    4 +++
 6 files changed, 74 insertions(+), 13 deletions(-)
---
diff --git a/common/static/img/refresh.png b/common/static/img/refresh.png
new file mode 100644
index 0000000..3b80028
Binary files /dev/null and b/common/static/img/refresh.png differ
diff --git a/people/models.py b/people/models.py
index a6a7585..3e9e99f 100644
--- a/people/models.py
+++ b/people/models.py
@@ -21,6 +21,7 @@
 
 import datetime
 import re
+from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
 from django.utils.html import escape
 from django.utils.safestring import mark_safe
@@ -136,31 +137,38 @@ class Person(User):
         return Team.objects.filter(role__person__id=self.id).all()
 
     def is_coordinator(self, team):
-        try:
-            self.role_set.get(team__id=team.id, role='coordinator')
-            return True
-        except:
-            return False
+        """
+        If team is provided, tell if current Person is coordinator of that team.
+        If not provided (None), tell if current Person is coordinator of at least one team.
+        """
+        if team is None:
+            return self.role_set.filter(role='coordinator').exists()
+        else:
+            try:
+                self.role_set.get(team__id=team.id, role='coordinator')
+                return True
+            except ObjectDoesNotExist:
+                return False
 
     def is_committer(self, team):
         try:
             self.role_set.get(team__id=team.id, role__in=['committer', 'coordinator'])
             return True
-        except:
+        except ObjectDoesNotExist:
             return False
 
     def is_reviewer(self, team):
         try:
             self.role_set.get(team__id=team.id, role__in=['reviewer', 'committer', 'coordinator'])
             return True
-        except:
+        except ObjectDoesNotExist:
             return False
 
     def is_translator(self, team):
         try:
             self.role_set.get(team__id=team.id)
             return True
-        except:
+        except ObjectDoesNotExist:
             return False
 
     def is_maintainer_of(self, module):
diff --git a/stats/models.py b/stats/models.py
index 4069019..2e1db01 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -163,9 +163,9 @@ class ModuleLock(object):
     def __init__(self, mod):
         assert isinstance(mod, Module)
         self.module = mod
+        self.dirpath = os.path.join("/tmp", "updating-%s" % (self.module.name,))
 
     def __enter__(self):
-        self.dirpath = os.path.join("/tmp", "updating-%s" % (self.module.name,))
         while True:
             try:
                 os.mkdir(self.dirpath)
@@ -176,6 +176,11 @@ class ModuleLock(object):
     def __exit__(self, *exc_info):
         os.rmdir(self.dirpath)
 
+    @property
+    def is_locked(self):
+        return os.path.exists(self.dirpath)
+
+
 class Branch(models.Model):
     """ Branch of a module """
     name        = models.CharField(max_length=50)
diff --git a/stats/views.py b/stats/views.py
index caa2e78..0ce670f 100644
--- a/stats/views.py
+++ b/stats/views.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2008-2011 Claude Paroz <claude 2xlibre net>.
+# Copyright (c) 2008-2013 Claude Paroz <claude 2xlibre net>.
 #
 # This file is part of Damned Lies.
 #
@@ -24,12 +24,13 @@ from django.conf import settings
 from django.contrib.auth.decorators import login_required
 from django.contrib import messages
 from django.core import serializers
-from django.http import HttpResponse, Http404
+from django.core.urlresolvers import reverse
+from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect, Http404
 from django.shortcuts import render, get_object_or_404
 from django.utils.translation import ugettext as _
 
 from common.utils import MIME_TYPES, get_user_locale
-from stats.models import Statistics, FakeLangStatistics, Module, Branch, Category, Release
+from stats.models import Statistics, FakeLangStatistics, Module, ModuleLock, Branch, Category, Release
 from stats.forms import ModuleBranchForm
 from stats import utils
 from languages.models import Language
@@ -62,6 +63,7 @@ def module(request, module_name):
         'branches': branches,
         'non_standard_repo_msg' : _(settings.VCS_HOME_WARNING),
         'can_edit_branches': mod.can_edit_branches(request.user),
+        'can_refresh': can_refresh_branch(request.user),
         'user_language': get_user_locale(request)
     }
     return render(request, 'module_detail.html', context)
@@ -146,6 +148,27 @@ def module_edit_branches(request, module_name):
     }
     return render(request, 'module_edit_branches.html', context)
 
+
+def branch_refresh(request, branch_id):
+    if not can_refresh_branch(request.user):
+        return HttpResponseForbidden("You are not allowed to refresh this branch's stats.")
+
+    branch = get_object_or_404(Branch, pk=branch_id)
+    if ModuleLock(branch.module).is_locked:
+        messages.info(request, "A statistics update is already running for branch %s of module %s" % (
+            branch.name, branch.module.name))
+    else:
+        try:
+            branch.update_stats(force=True)
+        except Exception as exc:
+            messages.error(request, "An error occurred while updating statistics for branch %s of module 
%s:<br>%s" % (
+                branch.name, branch.module.name, exc))
+        else:
+            messages.success(request, "Statistics successfully updated for branch %s of module %s" % (
+                branch.name, branch.module.name))
+    return HttpResponseRedirect(reverse('module', args=[branch.module.name]))
+
+
 def docimages(request, module_name, potbase, branch_name, langcode):
     mod = get_object_or_404(Module, name=module_name)
     try:
@@ -280,3 +303,16 @@ def compare_by_releases(request, dtype, rels_to_compare):
         'stats': stats
     }
     return render(request, 'release_compare.html', context)
+
+
+# ********** Utility function **********
+def can_refresh_branch(user):
+    """ Return True if user is authorized to force statistics refresh """
+    if not user.is_authenticated():
+        return False
+    if user.has_perm('stats.change_module'):
+        return True
+    person = Person.get_by_user(user)
+    if person.is_coordinator(team=None):
+        return True
+    return False
diff --git a/templates/module_detail.html b/templates/module_detail.html
index 80e30e6..58e92f3 100644
--- a/templates/module_detail.html
+++ b/templates/module_detail.html
@@ -26,6 +26,10 @@ $(document).ready(function() {
                 $(this).children('img').attr("src", "{{ STATIC_URL }}img/open.png");
             }
         });
+        $(".refresh").click(function(event) {
+            resp = confirm("You are about to force statistics refresh for that branch. This action can take 
several minutes. Are you sure you want to proceed?");
+            return resp;
+        });
     }
 );
 </script>
@@ -93,7 +97,7 @@ $(document).ready(function() {
 
   <!-- Main loop through branches -->
   {% for branch in branches %}
-    <hr />
+    <hr>
     <h2><a href="." class="branch" id="{{ branch.name }}">
     {% if forloop.counter < 3 %}
       <img src="{{ STATIC_URL }}img/open.png" /></a>
@@ -104,6 +108,10 @@ $(document).ready(function() {
     {% if branch.get_vcs_web_url %}
       <a href="{{ branch.get_vcs_web_url }}" title="{% trans 'Browse Repository' %}"><img src="{{ STATIC_URL 
}}img/repository-icon.png" /></a>
     {% endif %}
+    {% if can_refresh %}
+      <a href="{% url 'branch_refresh' branch.pk %}" title="{% trans 'Refresh branch statistics' %}" 
class="refresh">
+        <img src="{{ STATIC_URL }}img/refresh.png"></a>
+    {% endif %}
     </h2>
     {% if forloop.counter < 3 %}
       <div id="div-{{ branch.name }}" class="loaded">
diff --git a/urls.py b/urls.py
index daafdd5..a541845 100644
--- a/urls.py
+++ b/urls.py
@@ -80,6 +80,10 @@ urlpatterns += patterns('stats.views',
         regex = r'^module/(?P<module_name>[\w\-\+]+)/branch/(?P<branch_name>[\w\-\.]+)/$',
         view = 'module_branch'),
     url(
+        regex = r'^branch/(?P<branch_id>\d+)/refresh/$',
+        view = 'branch_refresh',
+        name = 'branch_refresh'),
+    url(
         regex = 
r'^module/(?P<module_name>[\w\-\+]+)/(?P<potbase>[\w~-]+)/(?P<branch_name>[\w\-\.]+)/(?P<langcode>[\w 
]+)/images/$',
         view = 'docimages',
         name = 'docimages'),


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]