Commit 00809243 authored by Adorilson Bezerra's avatar Adorilson Bezerra Committed by Claude Paroz

Add inactive translators category (fixes #634130)

parent 7ba08a98
# This empty file is necessary to run the tests
# -*- 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)
......@@ -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.")])
# 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']
......@@ -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)
# -*- 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)
......@@ -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)
......
......@@ -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):
......
......@@ -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()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment