[damned-lies] Add inactive translators category (fixes #634130)
- From: Claude Paroz <claudep src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [damned-lies] Add inactive translators category (fixes #634130)
- Date: Tue, 21 Dec 2010 09:11:30 +0000 (UTC)
commit 00809243f70bd323d30248a2a7b08452b423a190
Author: Adorilson Bezerra <adorilson gmail com>
Date: Tue Dec 21 10:04:52 2010 +0100
Add inactive translators category (fixes #634130)
common/models.py | 1 +
common/tests.py | 92 ++++++++++++++++++++
common/views.py | 3 +
.../0003_auto__add_field_role_is_active.py | 90 +++++++++++++++++++
teams/models.py | 28 +++++-
teams/tests.py | 92 +++++++++++++++++---
teams/views.py | 5 +
vertimus/models.py | 9 ++
vertimus/tests/__init__.py | 23 +++---
9 files changed, 317 insertions(+), 26 deletions(-)
---
diff --git a/common/models.py b/common/models.py
new file mode 100644
index 0000000..864a180
--- /dev/null
+++ b/common/models.py
@@ -0,0 +1 @@
+# This empty file is necessary to run the tests
diff --git a/common/tests.py b/common/tests.py
new file mode 100644
index 0000000..baab0a4
--- /dev/null
+++ b/common/tests.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2010 Adorilson Bezerra <adorilson gmail com>
+#
+# This file is part of Damned Lies.
+#
+# Damned Lies is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Damned Lies is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Damned Lies; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from datetime import datetime, timedelta
+
+from django.test import TestCase
+from django.test.client import Client
+
+from common import views
+from people.models import Person
+from teams.models import Team, Role
+
+class CommonTest(TestCase):
+
+ def setUp(self):
+ self.pn = Person(first_name='John', last_name='Note',
+ email='jn devnull com', username= 'jn', activation_key='a_activation_key')
+ self.pn.save()
+
+ self.pr = Person(first_name='John', last_name='Reviewer',
+ username='jr', date_joined=datetime.now()-timedelta(days=11),
+ activation_key='non-activated-key') # non-activeted person
+ self.pr.save()
+
+ self.pt = Person(first_name='John', last_name='Translator',
+ username='jt', last_login=datetime.now()-timedelta(days=30*6+1),) # inactive person
+ self.pt.save()
+
+ self.t1 = Team(name='fr', description='French')
+ self.t1.save()
+
+ self.t2 = Team(name='fr2', description='French')
+ self.t2.save()
+
+ self.r1 = Role(team=self.t1, person=self.pt)
+ self.r1.save()
+
+ self.r2 = Role(team=self.t2, person=self.pt)
+ self.r2.save()
+
+
+ def test_activate_account(self):
+ # Testing if is_active is False by default
+ self.pn = Person.objects.get(first_name='John', last_name='Note')
+ self.assertTrue(self.pn.is_active)
+
+ c = Client()
+
+ # Testing with a invalid activation key
+ response = c.get('/register/activate/a_invalid_key')
+ self.assertContains(response, 'Sorry, the key you provided is not valid.')
+
+ response = c.get('/register/activate/a_activation_key')
+ self.assertContains(response, 'Your account has been activated.')
+
+ self.pn = Person.objects.get(first_name='John', last_name='Note')
+ self.assertTrue(self.pn.is_active)
+
+ def test_house_keeping(self):
+ c = Client()
+ response = c.get('/register/activate/a_activation_key')
+ self.assertContains(response, 'Your account has been activated.')
+
+ # Testing if the non-activated accounts were deleted
+ jn = Person.objects.filter(first_name='John', last_name='Note')
+ self.assertEqual(jn.count(), 1)
+
+ jr = Person.objects.filter(first_name='John', last_name='Reviewer')
+ self.assertEqual(jr.count(), 0)
+
+ # Testing if the inactive roles were updated
+ jt = Person.objects.get(first_name='John', last_name='Translator')
+ for role in Role.objects.filter(person=jt):
+ self.assertFalse(role.is_active)
+
diff --git a/common/views.py b/common/views.py
index 71baaaf..02034e4 100644
--- a/common/views.py
+++ b/common/views.py
@@ -118,5 +118,8 @@ def activate_account(request, key):
except Person.DoesNotExist:
return render_to_response('error.html', {'error':"Sorry, the key you provided is not valid."})
person.activate()
+ #TODO: Here does not seem to be a good place for this.
+ # We should move this in a cron-like system
Person.clean_unactivated_accounts()
+ Role.inactivate_unused_roles()
return site_login(request, msgs=[_("Your account has been activated.")])
diff --git a/teams/migrations/0003_auto__add_field_role_is_active.py b/teams/migrations/0003_auto__add_field_role_is_active.py
new file mode 100644
index 0000000..1ccfaaa
--- /dev/null
+++ b/teams/migrations/0003_auto__add_field_role_is_active.py
@@ -0,0 +1,90 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'Role.is_active'
+ db.add_column('role', 'is_active', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'Role.is_active'
+ db.delete_column('role', 'is_active')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'people.person': {
+ 'Meta': {'ordering': "('username',)", 'object_name': 'Person', 'db_table': "'person'", '_ormbases': ['auth.User']},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'bugzilla_account': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'image': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'irc_nick': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '20', 'null': 'True', 'blank': 'True'}),
+ 'svn_account': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '20', 'null': 'True', 'blank': 'True'}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
+ 'webpage_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'teams.role': {
+ 'Meta': {'unique_together': "(('team', 'person'),)", 'object_name': 'Role', 'db_table': "'role'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Person']"}),
+ 'role': ('django.db.models.fields.CharField', [], {'default': "'translator'", 'max_length': '15'}),
+ 'team': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['teams.Team']"})
+ },
+ 'teams.team': {
+ 'Meta': {'ordering': "('description',)", 'object_name': 'Team', 'db_table': "'team'"},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailing_list': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'mailing_list_subscribe': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'members': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'teams'", 'symmetrical': 'False', 'through': "orm['teams.Role']", 'to': "orm['people.Person']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+ 'presentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'use_workflow': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'webpage_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['teams']
diff --git a/teams/models.py b/teams/models.py
index 0d85ee2..cc9aed6 100644
--- a/teams/models.py
+++ b/teams/models.py
@@ -19,6 +19,8 @@
# along with Damned Lies; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+from datetime import datetime, timedelta
+
from django.db import models
from django.core import mail
from django.utils import translation
@@ -37,7 +39,8 @@ class TeamManager(models.Manager):
roles.
"""
teams = self.all()
- roles = Role.objects.select_related("person").filter(role='coordinator')
+ roles = Role.objects.select_related("person").filter(role='coordinator',
+ is_active=True)
role_dict = {}
for role in roles:
@@ -60,7 +63,7 @@ class TeamManager(models.Manager):
reduce subsequent database access.
"""
teams = self.all()
- roles = Role.objects.select_related("person").all()
+ roles = Role.objects.select_related("person").filter(is_active=True)
role_dict = {}
for role in roles:
@@ -143,11 +146,13 @@ class Team(models.Model):
return None
def get_members_by_role_exact(self, role):
+ """ Return a list of active members """
try:
return self.roles[role]
except:
members = list(Person.objects.filter(role__team__id=self.id,
- role__role=role))
+ role__role=role,
+ role__is_active=True))
return members
def get_committers_exact(self):
@@ -167,7 +172,8 @@ class Team(models.Model):
members += self.roles[role]
except:
members = list(Person.objects.filter(role__team__id=self.id,
- role__role__in=roles))
+ role__role__in=roles,
+ role__is_active=True))
return members
def get_committers(self):
@@ -187,6 +193,12 @@ class Team(models.Model):
members = list(self.members.all())
return members
+ def get_inactive_members(self):
+ """ Return the inactive members """
+ members = list(Person.objects.filter(role__team__id=self.id,
+ role__is_active=False))
+ return members
+
def send_mail_to_coordinator(self, subject, message, messagekw={}):
""" Send a message to the coordinator, in her language if available
and if subject and message are lazy strings """
@@ -250,6 +262,7 @@ class Role(models.Model):
person = models.ForeignKey(Person)
role = models.CharField(max_length=15, choices=ROLE_CHOICES,
default='translator')
+ is_active = models.BooleanField(default=True)
class Meta:
db_table = 'role'
@@ -258,3 +271,10 @@ class Role(models.Model):
def __unicode__(self):
return "%s is %s in %s team" % (self.person.name, self.role,
self.team.description)
+
+ @classmethod
+ def inactivate_unused_roles(cls):
+ """ Inactivate the roles when login older than 180 days """
+ last_login = datetime.now() - timedelta(days=30*6)
+ cls.objects.filter(person__last_login__lt=last_login,
+ is_active=True).update(is_active=False)
diff --git a/teams/tests.py b/teams/tests.py
index 597dacb..b705aca 100644
--- a/teams/tests.py
+++ b/teams/tests.py
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
+
+from datetime import datetime, timedelta
+
from django.test import TestCase
from django.test.client import Client
from django.core.urlresolvers import reverse
@@ -8,8 +11,8 @@ from people.models import Person
from teams.models import Team, Role
from languages.models import Language
-class TeamTest(TestCase):
+class TeamsAndRolesTests(TestCase):
def setUp(self):
self.pn = Person(first_name='John', last_name='Nothing',
email='jn devnull com', username= 'jn')
@@ -25,7 +28,8 @@ class TeamTest(TestCase):
self.pr.save()
self.pc = Person(first_name='John', last_name='Committer',
- email='jc alinsudesonpleingre fr', username= 'jc')
+ email='jc alinsudesonpleingre fr', username= 'jc',
+ last_login=datetime.now()-timedelta(days=30*6-1)) #active person, but in limit date
self.pc.save()
self.pcoo = Person(first_name='John', last_name='Coordinator',
@@ -36,12 +40,18 @@ class TeamTest(TestCase):
self.t = Team(name='fr', description='French')
self.t.save()
+ self.t2 = Team(name='pt', description='Portuguese')
+ self.t2.save()
+
self.l = Language(name='French', locale='fr', team=self.t)
self.l.save()
self.role = Role(team=self.t, person=self.pt)
self.role.save()
+ self.role = Role(team=self.t2, person=self.pt, role='reviewer')
+ self.role.save()
+
self.role = Role(team=self.t, person=self.pr, role='reviewer')
self.role.save()
@@ -51,16 +61,36 @@ class TeamTest(TestCase):
self.role = Role(team=self.t, person=self.pcoo, role='coordinator')
self.role.save()
- def tearDown(self):
- for role in Role.objects.all():
- role.delete()
- self.pcoo.delete()
- self.pc.delete()
- self.pr.delete()
- self.pt.delete()
- self.pn.delete()
- self.t.delete()
+class TeamTest(TeamsAndRolesTests):
+ def setUp(self):
+ super(TeamTest, self).setUp()
+
+ def test_get_members_by_role_exact(self):
+ members = self.t.get_members_by_role_exact('committer')
+ t = Team.objects.get(name='fr')
+ self.assertEqual(len(members), 1)
+ self.assertEqual(members[0], self.pc)
+ role = Role.objects.get(person=self.pc, team=t)
+ role.is_active = False
+ role.save()
+
+ members = self.t.get_members_by_role_exact('committer')
+ self.assertEqual(len(members), 0)
+
+ def test_get_inactive_members(self):
+ members = self.t.get_inactive_members()
+ self.assertEqual(len(members), 0)
+
+ t = Team.objects.get(name='fr')
+ role = Role.objects.get(person=self.pc, team=t)
+ role.is_active = False
+ role.save()
+
+ members = self.t.get_inactive_members()
+ self.assertEqual(len(members), 1)
+ self.assertEqual(members[0], self.pc)
+
def run_roles_exact_test(self, team):
pcoo = team.get_coordinator()
self.assertEqual(pcoo, self.pcoo)
@@ -148,3 +178,43 @@ class TeamTest(TestCase):
team = Team.objects.get(name='fr')
self.assertEquals(team.webpage_url, u"http://www.gnomefr.org/")
+
+class RoleTest(TeamsAndRolesTests):
+
+ def setUp(self):
+ super(RoleTest, self).setUp()
+
+ self.pt.last_login = datetime.now()-timedelta(days=10) # active person
+ self.pt.save()
+
+ self.pr.last_login = datetime.now()-timedelta(days=30*6) # inactive person
+ self.pr.save()
+
+ self.pc.last_login = datetime.now()-timedelta(days=30*6-1) #active person, but in limit date
+ self.pc.save()
+
+ self.role = Role.objects.get(team=self.t, person=self.pt)
+ self.role2 = Role.objects.get(team=self.t2, person=self.pt)
+ self.role_inactive = Role.objects.get(team=self.t, person=self.pr)
+ self.role_limit_date = Role.objects.get(team=self.t, person=self.pc)
+
+ def test_inactivating_roles(self):
+ # Testing if is_active is True by default
+ self.assertTrue(self.role.is_active)
+ self.assertTrue(self.role2.is_active)
+ self.assertTrue(self.role_limit_date.is_active)
+ self.assertTrue(self.role_inactive.is_active)
+
+ Role.inactivate_unused_roles()
+
+ # Getting roles from database after update the unused roles
+ self.role = Role.objects.get(team=self.t, person=self.pt)
+ self.role2 = Role.objects.get(team=self.t2, person=self.pt)
+ self.role_inactive = Role.objects.get(team=self.t, person=self.pr)
+ self.role_limit_date = Role.objects.get(team=self.t, person=self.pc)
+
+ self.assertTrue(self.role.is_active)
+ self.assertTrue(self.role2.is_active)
+ self.assertTrue(self.role_limit_date.is_active)
+ self.assertFalse(self.role_inactive.is_active)
+
diff --git a/teams/views.py b/teams/views.py
index 0aaf7d5..9d90108 100644
--- a/teams/views.py
+++ b/teams/views.py
@@ -69,6 +69,11 @@ def team(request, team_slug):
'form': None,
'no_member': _("No translators")
},
+ {'title': _("Inactive members"),
+ 'members': team.get_inactive_members(),
+ 'form': None,
+ 'no_member': _("No inactive members")
+ },
)
except Team.DoesNotExist:
lang = get_object_or_404(Language, locale=team_slug)
diff --git a/vertimus/models.py b/vertimus/models.py
index 7eeba16..cd6ef37 100644
--- a/vertimus/models.py
+++ b/vertimus/models.py
@@ -34,6 +34,8 @@ from stats.utils import run_shell_command
from languages.models import Language
from people.models import Person
+from teams.models import Role
+
#
# States
#
@@ -620,6 +622,13 @@ class ActionUT(ActionAbstract):
new_state = self._new_state()
self.send_mail_new_state(state, new_state, (state.language.team.mailing_list,))
+
+ # Reactivating the role if needed
+ role = Role.objects.get(person=person, team=state.language.team)
+ if not role.is_active:
+ role.is_active = True
+ role.save()
+
return new_state
class ActionRP(ActionAbstract):
diff --git a/vertimus/tests/__init__.py b/vertimus/tests/__init__.py
index 15d5939..2087c75 100644
--- a/vertimus/tests/__init__.py
+++ b/vertimus/tests/__init__.py
@@ -26,12 +26,12 @@ from django.http import QueryDict
from django.utils.datastructures import MultiValueDict
from django.conf import settings
-from teams.tests import TeamTest
+from teams.tests import TeamsAndRolesTests
from stats.models import Module, Branch, Release, Category, Domain
from vertimus.models import *
from vertimus.forms import ActionForm
-class VertimusTest(TeamTest):
+class VertimusTest(TeamsAndRolesTests):
def setUp(self):
super(VertimusTest, self).setUp()
@@ -61,14 +61,6 @@ class VertimusTest(TeamTest):
dtype='ui', directory='po')
self.d.save()
- def tearDown(self):
- self.d.delete()
- self.c.delete()
- self.r.delete()
- self.b.delete()
- self.m.delete()
- super(VertimusTest, self).tearDown()
-
def test_state_none(self):
sdb = StateDb(branch=self.b, domain=self.d, language=self.l)
sdb.name = 'None'
@@ -239,16 +231,25 @@ class VertimusTest(TeamTest):
new_state.save()
def test_action_ut(self):
+ # Disabling the role
+ role = Role.objects.get(person=self.pt, team=self.l.team)
+ role.is_active = False
+ role.save()
+
state = StateDb(branch=self.b, domain=self.d, language=self.l, name='Translating', person=self.pt).get_state()
state.save()
test_file = ContentFile('test content')
test_file.name = 'mytestfile.po'
-
+
action = ActionAbstract.new_by_name('UT')
new_state = state.apply_action(action, self.pt, "Done by translator.", test_file)
new_state.save()
+ # Testing if the role was activated
+ role = Role.objects.get(person=self.pt, team=self.l.team)
+ self.assertTrue(role.is_active)
+
def test_action_rp(self):
state = StateDb(branch=self.b, domain=self.d, language=self.l, name='Translated').get_state()
state.save()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]