Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/ProjetSigma/backend into …
Browse files Browse the repository at this point in the history
…feature/improve_groups

Conflicts:
	sigma/urls.py
	sigma_core/models/group.py
	sigma_core/models/user.py
	sigma_core/tests/factories.py
  • Loading branch information
TheBirdie authored and TheBirdie committed Feb 1, 2016
2 parents c00174b + 5f1adf2 commit 0d5847f
Show file tree
Hide file tree
Showing 18 changed files with 540 additions and 27 deletions.
4 changes: 3 additions & 1 deletion sigma/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from sigma_core.views.user import UserViewSet
from sigma_core.views.group import GroupViewSet
from sigma_core.views.school import SchoolViewSet
from sigma_core.views.group_user import GroupUserViewSet
from sigma_core.views.group_member import GroupMemberViewSet
from sigma_core.views.group_member_value import GroupMemberValueViewSet
Expand All @@ -27,11 +28,12 @@

router = routers.DefaultRouter()

router.register(r'user', UserViewSet)
router.register(r'group', GroupViewSet)
router.register(r'group-field', GroupFieldViewSet)
router.register(r'group-member', GroupMemberViewSet)
router.register(r'group-member-value', GroupMemberValueViewSet)
router.register(r'school', SchoolViewSet)
router.register(r'user', UserViewSet)
router.register(r'validator', ValidatorViewSet)

urlpatterns = [
Expand Down
5 changes: 4 additions & 1 deletion sigma_core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
from django.contrib.auth.models import Group as AuthGroup

from sigma_core.models.user import User
from sigma_core.models.group import Group
from sigma_core.models.group import Group, GroupAcknowledgment
from sigma_core.models.school import School
from sigma_core.models.group_member import GroupMember


admin.site.unregister(AuthGroup)
admin.site.register(User)
admin.site.register(Group)
admin.site.register(School)
admin.site.register(GroupAcknowledgment)
admin.site.register(GroupMember)
24 changes: 24 additions & 0 deletions sigma_core/migrations/0010_school.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-01-26 13:41
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('sigma_core', '0009_auto_20160111_0057'),
]

operations = [
migrations.CreateModel(
name='School',
fields=[
('group_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='sigma_core.Group')),
('design', models.CharField(max_length=255)),
],
bases=('sigma_core.group',),
),
]
27 changes: 27 additions & 0 deletions sigma_core/migrations/0011_schoolgroup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-01-27 14:24
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('sigma_core', '0010_school'),
]

operations = [
migrations.CreateModel(
name='SchoolGroup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('validated', models.BooleanField(default=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='group_schools', to='sigma_core.Group')),
('school', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='school_groups', to='sigma_core.School')),
],
),
]
51 changes: 51 additions & 0 deletions sigma_core/migrations/0012_auto_20160127_1848.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-01-27 17:48
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('sigma_core', '0011_schoolgroup'),
]

operations = [
migrations.CreateModel(
name='GroupAcknowledgment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('validated', models.BooleanField(default=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
],
),
migrations.RemoveField(
model_name='schoolgroup',
name='group',
),
migrations.RemoveField(
model_name='schoolgroup',
name='school',
),
migrations.AddField(
model_name='group',
name='resp_school',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='sigma_core.School'),
),
migrations.DeleteModel(
name='SchoolGroup',
),
migrations.AddField(
model_name='groupacknowledgment',
name='asking_group',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='group_recognizers', to='sigma_core.Group'),
),
migrations.AddField(
model_name='groupacknowledgment',
name='validator_group',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='group_acknowledgments', to='sigma_core.Group'),
),
]
28 changes: 28 additions & 0 deletions sigma_core/migrations/0013_auto_20160201_1108.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-02-01 10:08
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('sigma_core', '0012_auto_20160127_1848'),
]

operations = [
migrations.AddField(
model_name='groupacknowledgment',
name='parent_group',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='subgroups', to='sigma_core.Group'),
preserve_default=False,
),
migrations.AddField(
model_name='groupacknowledgment',
name='subgroup',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='group_parents', to='sigma_core.Group'),
preserve_default=False,
),
]
22 changes: 22 additions & 0 deletions sigma_core/migrations/0014_auto_20160201_1109.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-02-01 10:09
from __future__ import unicode_literals

from django.db import migrations

def move_foreignkeys(apps, schema_editor):
GroupAcknowledgment = apps.get_model("sigma_core", "GroupAcknowledgment")
for ga in GroupAcknowledgment.objects.all():
ga.subgroup = ga.asking_group
ga.parent_group = ga.validator_group
ga.save()

class Migration(migrations.Migration):

dependencies = [
('sigma_core', '0013_auto_20160201_1108'),
]

operations = [
migrations.RunPython(move_foreignkeys),
]
23 changes: 23 additions & 0 deletions sigma_core/migrations/0015_auto_20160201_1109.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-02-01 10:09
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('sigma_core', '0014_auto_20160201_1109'),
]

operations = [
migrations.RemoveField(
model_name='groupacknowledgment',
name='asking_group',
),
migrations.RemoveField(
model_name='groupacknowledgment',
name='validator_group',
),
]
56 changes: 46 additions & 10 deletions sigma_core/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@


class Group(models.Model):
class Meta:
pass

#########################
# Constants and choices #
#########################
ADMINISTRATOR_RANK = 10

VIS_PUBLIC = 'public'
Expand All @@ -32,10 +32,16 @@ class Meta:
(TYPE_SCHOOL, 'School')
)

##########
# Fields #
##########
name = models.CharField(max_length=254)
visibility = models.CharField(max_length=64, choices=VISIBILITY_CHOICES, default=VIS_PRIVATE)
type = models.CharField(max_length=64, choices=TYPE_CHOICES, default=TYPE_BASIC)

# The school responsible of the group in case of admin conflict (can be null for non-school-related groups)
resp_school = models.ForeignKey('School', null=True, blank=True, on_delete=models.SET_NULL)

# The permission a member has upon joining
# A value of -1 means that no one can join the group.
# A value of 0 means that anyone can request to join the group
Expand All @@ -59,15 +65,22 @@ class Meta:
# - fields (model GroupField)
# TODO: Determine whether 'memberships' fields needs to be retrieved every time or not...

@property
def subgroups(self):
return [ga.subgroup for ga in self.subgroups.filter(validated=True).select_related('subgroup')]

#################
# Model methods #
#################
def can_anyone_join(self):
return self.default_member_rank >= 0

def __str__(self):
return "%s (%s)" % (self.name, self.get_type_display())

################################################################
# PERMISSIONS #
################################################################
###############
# Permissions #
###############

# Perms for admin site
def has_perm(self, perm, obj=None):
Expand All @@ -76,7 +89,7 @@ def has_perm(self, perm, obj=None):
def has_module_perms(self, app_label):
return True

# Permissions
# DRY Permissions
@staticmethod
def has_read_permission(request):
"""
Expand All @@ -100,12 +113,21 @@ def has_create_permission(request):
"""
Everybody can create a private group. For other types, user must be school admin or sigma admin.
"""
#TODO: Adapt after School model implementation.
from sigma_core.models.school import School
group_type = request.data.get('type', None)
return group_type == Group.TYPE_BASIC
if group_type == Group.TYPE_BASIC:
return True

resp_school = request.data.get('resp_school', None)
try:
school = School.objects.get(pk=resp_school)
except School.DoesNotExist:
school = None
return school is not None and request.user.has_group_admin_perm(school)

@allow_staff_or_superuser
def has_object_write_permission(self, request):
return False
return request.user.has_group_admin_perm(self)

@allow_staff_or_superuser
def has_object_update_permission(self, request):
Expand All @@ -117,3 +139,17 @@ def has_object_update_permission(self, request):
@allow_staff_or_superuser
def has_object_invite_permission(self, request):
return request.user.can_invite(self)


class GroupAcknowledgment(models.Model):
subgroup = models.ForeignKey(Group, related_name='group_parents')
parent_group = models.ForeignKey(Group, related_name='subgroups')
validated = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)

def __str__(self):
if self.validated:
return "Group %s acknowledged by Group %s" % (self.subgroup.__str__(), self.parent_group.__str__())
else:
return "Group %s awaiting for acknowledgment by Group %s since %s" % (self.subgroup.__str__(), self.parent_group.__str__(), self.created.strftime("%Y-%m-%d %H:%M"))
48 changes: 48 additions & 0 deletions sigma_core/models/school.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from django.db import models

from dry_rest_permissions.generics import allow_staff_or_superuser

from sigma_core.models.group import Group


class School(Group):
design = models.CharField(max_length=255)

def save(self, *args, **kwargs):
"""
Schools are special groups: some params cannot be specified by user.
"""
self.visibility = Group.VIS_PUBLIC
self.type = Group.TYPE_SCHOOL
self.default_member_rank = -1
self.req_rank_invite = Group.ADMINISTRATOR_RANK
self.req_rank_kick = Group.ADMINISTRATOR_RANK
self.req_rank_accept_join_requests = Group.ADMINISTRATOR_RANK
self.req_rank_promote = Group.ADMINISTRATOR_RANK
self.req_rank_demote = Group.ADMINISTRATOR_RANK
self.req_rank_modify_group_infos = Group.ADMINISTRATOR_RANK
self.resp_school = None

return super(School, self).save(*args, **kwargs)

# Permissions
@staticmethod
def has_read_permission(request):
"""
Schools list is visible by everybody.
"""
return True

def has_object_read_permission(self, request):
"""
Schools are only visible by members.
"""
return request.user.is_group_member(self)

@staticmethod
@allow_staff_or_superuser
def has_create_permission(request):
"""
Schools can be created by Sigma admin only.
"""
return False
7 changes: 3 additions & 4 deletions sigma_core/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,9 @@ def has_group_admin_perm(self, group):
mem = self.get_group_membership(group)
return mem is not None and mem.perm_rank == Group.ADMINISTRATOR_RANK


################################################################
# PERMISSIONS #
################################################################
###############
# Permissions #
###############

# Perms for admin site
def has_perm(self, perm, obj=None):
Expand Down
1 change: 1 addition & 0 deletions sigma_core/serializers/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ class GroupSerializer(BasicGroupSerializer):
class Meta:
model = Group

subgroups = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
memberships = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
Loading

0 comments on commit 0d5847f

Please sign in to comment.