Commit 78f2f657 authored by Thom Wiggers's avatar Thom Wiggers 📐
Browse files

Merge branch 'committees/manipulate' into 'master'

Manipulate committees

 Implements: manipulatie memberships fixen
*  if voorzitter verandert moet het een nieuw membership worden en het oude deactiveren.
 *  Verlaten van een commissie moet niet het deleten, maar het inactive maken (misschien maar gewoon met de hand doen though)



See issue #11

See merge request !4
parents e40e9753 9c7f490b
from django.utils import timezone import logging
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.db import models from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from members.models import Member from members.models import Member
logger = logging.getLogger(__name__)
class Committee(models.Model): class Committee(models.Model):
"""A committee""" """A committee"""
...@@ -48,7 +52,7 @@ class ActiveMembershipManager(models.Manager): ...@@ -48,7 +52,7 @@ class ActiveMembershipManager(models.Manager):
"""Get only active memberships""" """Get only active memberships"""
def get_queryset(self): def get_queryset(self):
"""Get the currently active committee memberships""" """Get the currently active committee memberships"""
return super().get_queryset().exclude(until__lt=timezone.now()) return super().get_queryset().exclude(until__lt=timezone.now().date())
class CommitteeMembership(models.Model): class CommitteeMembership(models.Model):
...@@ -87,14 +91,22 @@ class CommitteeMembership(models.Model): ...@@ -87,14 +91,22 @@ class CommitteeMembership(models.Model):
default=False, default=False,
) )
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.pk is None:
self._was_chair = bool(self.chair)
else:
self._was_chair = False
@property @property
def is_active(self): def is_active(self):
"""Is this membership currently active""" """Is this membership currently active"""
return self.until is None or self.until > timezone.now() return self.until is None or self.until > timezone.now().date()
def clean(self): def clean(self):
"""Validation""" """Validation"""
if self.until and self.until > timezone.now(): if self.until and self.until > timezone.now().date():
raise ValidationError({ raise ValidationError({
'until': _("Membership expiration date can't be in the future:" 'until': _("Membership expiration date can't be in the future:"
" '{}'").format(self.until) " '{}'").format(self.until)
...@@ -110,6 +122,7 @@ class CommitteeMembership(models.Model): ...@@ -110,6 +122,7 @@ class CommitteeMembership(models.Model):
# Check if a committee has more than one chair # Check if a committee has more than one chair
chairs = (CommitteeMembership.active_memberships chairs = (CommitteeMembership.active_memberships
.filter(committee=self.committee) .filter(committee=self.committee)
.exclude(member=self.member)
.filter(chair=True) .filter(chair=True)
.count()) .count())
if chairs >= 1 and self.chair: if chairs >= 1 and self.chair:
...@@ -118,12 +131,35 @@ class CommitteeMembership(models.Model): ...@@ -118,12 +131,35 @@ class CommitteeMembership(models.Model):
_('This committee already has a chair')}) _('This committee already has a chair')})
# check if this member is already in the committee # check if this member is already in the committee
members = (self.committee.members if self.pk is None:
.filter(pk=self.member.pk) members = (self.committee.members
.count()) .filter(pk=self.member.pk)
if members >= 1: .count())
raise ValidationError({ if members >= 1:
'member': _('This member is already in the committee')}) raise ValidationError({
'member': _('This member is already in the committee')})
def save(self, *args, **kwargs):
"""Save the instance"""
# If the chair changed and we're still active, we create a new instance
# Inactive instances should be handled manually
if (self.pk is not None and self._was_chair != self.chair and
not self.until):
logger.info("Creating new membership instance")
self.until = timezone.now().date()
super().save(*args, **kwargs)
self.pk = None # forces INSERT
self.since = self.until # Set since date to older expiration
self.until = None
super().save(*args, **kwargs)
def delete(self, *args, **kwargs):
"""Deactivates active memberships, deletes inactive ones"""
if self.is_active:
self.until = timezone.now().date()
self.save()
else:
super().delete(*args, **kwargs)
def __str__(self): def __str__(self):
return "{} membership of {} since {}".format(self.member, return "{} membership of {} since {}".format(self.member,
......
...@@ -11,15 +11,14 @@ from members.models import Member ...@@ -11,15 +11,14 @@ from members.models import Member
class CommitteeMembersTest(TestCase): class CommitteeMembersTest(TestCase):
fixtures = ['members.json', 'committees.json'] fixtures = ['members.json', 'committees.json']
@classmethod def setUp(self):
def setUpTestData(cls): # Don't use setUpTestData because delete() will cause problems
cls.testcie = Committee.objects.get(name='testcie1') self.testcie = Committee.objects.get(name='testcie1')
cls.testuser = Member.objects.get(pk=1) self.testuser = Member.objects.get(pk=1)
self.m = CommitteeMembership(committee=self.testcie,
cls.m = CommitteeMembership(committee=cls.testcie, member=self.testuser,
member=cls.testuser, chair=False)
chair=False) self.m.save()
cls.m.save()
def test_unique(self): def test_unique(self):
with self.assertRaises(IntegrityError): with self.assertRaises(IntegrityError):
...@@ -43,56 +42,80 @@ class CommitteeMembersTest(TestCase): ...@@ -43,56 +42,80 @@ class CommitteeMembersTest(TestCase):
def test_until_date(self): def test_until_date(self):
m = CommitteeMembership(committee=self.testcie, m = CommitteeMembership(committee=self.testcie,
member=self.testuser, member=self.testuser,
until=timezone.now().replace(year=2000), until=timezone.now().date().replace(year=2000),
chair=False) chair=False)
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
m.clean() m.clean()
m.since = timezone.now().replace(year=1900) m.since = timezone.now().date().replace(year=1900)
m.clean() m.clean()
def test_inactive(self): def test_inactive(self):
self.assertTrue(self.m.is_active) self.assertTrue(self.m.is_active)
self.m.until = timezone.now().replace(year=1900) self.m.until = timezone.now().date().replace(year=1900)
self.assertFalse(self.m.is_active) self.assertFalse(self.m.is_active)
def test_delete(self):
self.m.delete()
self.assertIsNotNone(self.m.until)
self.assertIsNotNone(self.m.pk)
self.m.delete()
self.assertIsNone(self.m.pk)
class CommitteeMembersChairTest(TestCase): class CommitteeMembersChairTest(TestCase):
fixtures = ['members.json', 'committees.json'] fixtures = ['members.json', 'committees.json']
@classmethod
def setUpTestData(cls):
testcie = Committee.objects.get(name='testcie1')
testuser = Member.objects.get(pk=1)
cls.m1 = CommitteeMembership(committee=testcie,
member=testuser,
chair=True)
cls.m1.full_clean()
cls.m1.save()
def setUp(self): def setUp(self):
self.testcie = Committee.objects.get(name='testcie1') self.testcie = Committee.objects.get(name='testcie1')
self.testuser = Member.objects.get(pk=1) self.testuser = Member.objects.get(pk=1)
self.testuser2 = Member.objects.get(pk=2)
self.m1 = CommitteeMembership(committee=self.testcie,
member=self.testuser,
chair=True)
self.m1.full_clean()
self.m1.save()
def test_second_chair_fails(self): def test_second_chair_fails(self):
testuser2 = Member.objects.get(pk=2)
m = CommitteeMembership(committee=self.testcie, m = CommitteeMembership(committee=self.testcie,
member=testuser2, member=self.testuser2,
chair=True) chair=True)
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
m.full_clean() m.full_clean()
def test_inactive_chair(self): def test_inactive_chair(self):
testuser2 = Member.objects.get(pk=2) self.m1.until = timezone.now().date().replace(year=1900)
self.m1.until = timezone.now().replace(year=1900)
self.m1.save() self.m1.save()
m = CommitteeMembership(committee=self.testcie, m = CommitteeMembership(committee=self.testcie,
member=testuser2, member=self.testuser2,
chair=True) chair=True)
m.full_clean() m.full_clean()
def test_clean_self_chair(self):
self.m1.chair = True
self.m1.full_clean()
def test_change_chair(self):
pk = self.m1.pk
original_chair = self.m1.chair
self.m1.save()
self.assertEqual(self.m1.pk, pk, "new object created")
self.m1.chair = not original_chair
self.m1.save()
self.assertNotEqual(self.m1.pk, pk, "No new object created")
def test_change_chair_inactive(self):
pk = self.m1.pk
original_chair = self.m1.chair
self.m1.until = timezone.now().date()
self.m1.save()
self.assertEqual(self.m1.pk, pk, "new object created")
self.m1.chair = not original_chair
self.m1.save()
self.assertEqual(self.m1.pk, pk, "No new object created")
class BackendTest(TestCase): class PermissionsBackendTest(TestCase):
fixtures = ['members.json', 'committees.json'] fixtures = ['members.json', 'committees.json']
@classmethod @classmethod
......
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