add personal group, default_group to users and add group info to session on backend
authorymh <ymh.work@gmail.com>
Mon, 31 Jul 2017 23:18:38 +0200
changeset 131 adad5563603c
parent 130 78246db1cbac
child 132 906a6c7c7943
add personal group, default_group to users and add group info to session on backend
src/irinotes/settings.py
src/notes/api/serializers/auth.py
src/notes/api/serializers/core.py
src/notes/locale/en/LC_MESSAGES/django.po
src/notes/locale/fr/LC_MESSAGES/django.po
src/notes/migrations/0001_initial.py
src/notes/models/auth.py
src/notes/models/core.py
src/notes/signals.py
src/notes/tests/api/auth.py
src/notes/tests/api/note.py
src/notes/tests/api/session.py
src/notes/tests/models/auth.py
src/requirements/dev.txt
--- a/src/irinotes/settings.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/irinotes/settings.py	Mon Jul 31 23:18:38 2017 +0200
@@ -197,11 +197,11 @@
             'level': LOG_LEVEL,
             'propagate': True,
         },
-        'django.db.backends': {
-            'handlers': ['file'],
-            'level': LOG_LEVEL,
-            'propagate': True,
-        },
+        # 'django.db.backends': {
+        #     'handlers': ['file'],
+        #     'level': LOG_LEVEL,
+        #     'propagate': True,
+        # },
         'irinotes': {
             'handlers': ['file'],
             'level': LOG_LEVEL,
--- a/src/notes/api/serializers/auth.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/api/serializers/auth.py	Mon Jul 31 23:18:38 2017 +0200
@@ -10,11 +10,12 @@
 
 class GroupSerializer(serializers.ModelSerializer):
     owner = serializers.CharField(source='profile.owner.username', read_only=True)
+    is_personal = serializers.BooleanField(source='profile.is_personal', read_only=True)
     description = serializers.CharField(source='profile.description')
 
     class Meta:
         model = Group
-        fields = ['name', 'owner', 'description']
+        fields = ['name', 'owner', 'is_personal', 'description']
 
 
 class DetailGroupSerializer(GroupSerializer):
@@ -27,7 +28,8 @@
 
     class Meta:
         model = Group
-        fields = ['name', 'owner', 'description', 'users']
+        fields = ['name', 'owner', 'description', 'users', 'is_personal']
+
 
 class WriteGroupSerializer(serializers.ModelSerializer):
     '''
--- a/src/notes/api/serializers/core.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/api/serializers/core.py	Mon Jul 31 23:18:38 2017 +0200
@@ -3,8 +3,10 @@
 """
 import logging
 
+from django.contrib.auth.models import Group
+from rest_framework import serializers
+
 from notes.models import Note, Session
-from rest_framework import serializers
 
 logger = logging.getLogger(__name__)
 
@@ -87,40 +89,63 @@
 
     owner = serializers.SlugRelatedField(
         read_only=True, slug_field='username', default=serializers.CurrentUserDefault())
+    group = serializers.SlugRelatedField(read_only=True, slug_field='name')
 
     class Meta:
         model = Session
         fields = (
             'ext_id', 'version', 'date', 'created', 'updated',
-            'owner', 'title', 'description', 'protocol'
+            'owner', 'title', 'description', 'protocol', 'group'
         )
-        read_only_fields = ('ext_id', 'version', 'created', 'updated', 'owner')
+        read_only_fields = ('ext_id', 'version', 'created', 'updated', 'owner', 'group')
 
 
 class DetailSessionSerializer(serializers.ModelSerializer):
 
     owner = serializers.SlugRelatedField(read_only=True, slug_field='username')
     notes = DetailNoteSerializer(many=True, read_only=True)
+    group = serializers.SlugRelatedField(slug_field='name', read_only=True)
+
+    class Meta:
+        model = Session
+        fields = (
+            'ext_id', 'version', 'date', 'created', 'updated',
+            'owner', 'title', 'description', 'protocol', 'group',
+            'notes'
+        )
+        read_only_fields = ('ext_id', 'version', 'created', 'updated', 'owner', 'group')
+
+class CreateSessionSerializer(serializers.ModelSerializer):
+
+    owner = serializers.SlugRelatedField(
+        read_only=True, slug_field='username', default=serializers.CurrentUserDefault())
+    group = serializers.SlugRelatedField(slug_field='name', queryset=Group.objects.all(), required=False)
 
     class Meta:
         model = Session
         fields = (
             'ext_id', 'version', 'date', 'created', 'updated',
-            'owner', 'title', 'description', 'protocol',
-            'notes'
-        )
-        read_only_fields = ('ext_id', 'version', 'created', 'updated', 'owner')
-
-class CreateSessionSerializer(serializers.ModelSerializer):
-
-    owner = serializers.SlugRelatedField(
-        read_only=True, slug_field='username', default=serializers.CurrentUserDefault())
-
-
-    class Meta:
-        model = Session
-        fields = (
-            'ext_id', 'version', 'date', 'created', 'updated',
-            'owner', 'title', 'description', 'protocol'
+            'owner', 'title', 'description', 'protocol', 'group'
         )
         read_only_fields = ('version', 'created', 'updated', 'owner')
+
+    def validate(self, data):
+        data = super().validate(data)
+
+        group = data.get('group')
+        owner = data.get('owner')
+
+        if group is None:
+            if owner and owner.profile and owner.profile.default_group:
+                group = owner.profile.default_group
+            if group is None and owner:
+                group = Group.objects.filter(profile__owner_personal=owner).first()
+
+        if group is None:
+            raise serializers.ValidationError("group field is required or default group or personal group could not be found for owner")
+        elif not owner in group.user_set.all():
+            raise serializers.ValidationError("Owner must be in group")
+
+        data['group'] = group
+        return data
+
--- a/src/notes/locale/en/LC_MESSAGES/django.po	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/locale/en/LC_MESSAGES/django.po	Mon Jul 31 23:18:38 2017 +0200
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-21 10:51+0000\n"
+"POT-Creation-Date: 2017-07-31 10:40+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,27 +16,31 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: models/auth.py:11
+#: models/auth.py:12
 msgid "User"
 msgstr "User"
 
-#: models/auth.py:12
+#: models/auth.py:13
 msgid "Users"
 msgstr "Users"
 
-#: models/auth.py:17
+#: models/auth.py:23
+msgid "UserProfile|default_group"
+msgstr "default group"
+
+#: models/auth.py:26
 msgid "UserProfile"
 msgstr "User profile"
 
-#: models/auth.py:18
+#: models/auth.py:27
 msgid "UserProfiles"
 msgstr "User profiles"
 
-#: models/auth.py:25
+#: models/auth.py:48
 msgid "GroupProfile"
 msgstr "Group profile"
 
-#: models/auth.py:26
+#: models/auth.py:49
 msgid "GroupProfiles"
 msgstr "Group profiles"
 
@@ -96,66 +100,70 @@
 msgid "Categories"
 msgstr "Categories"
 
-#: models/core.py:17
+#: models/core.py:19
 msgid "Session"
 msgstr "Session"
 
-#: models/core.py:18
+#: models/core.py:20
 msgid "Sessions"
 msgstr "Sessions"
 
-#: models/core.py:20
+#: models/core.py:22
 msgid "Session|date"
 msgstr "date"
 
-#: models/core.py:29
+#: models/core.py:31
 msgid "Session|title"
 msgstr "title"
 
-#: models/core.py:34
+#: models/core.py:36
 msgid "Session|description"
 msgstr "description"
 
-#: models/core.py:39
+#: models/core.py:41
+msgid "Session|group"
+msgstr "group"
+
+#: models/core.py:46
 msgid "Session|protocol"
 msgstr "protocol"
 
-#: models/core.py:48
+#: models/core.py:61
 msgid "Note"
 msgstr "Note"
 
-#: models/core.py:49
+#: models/core.py:62
 msgid "Notes"
 msgstr "Notes"
 
-#: models/core.py:52
+#: models/core.py:68
 msgid "Note|tc_start"
 msgstr "start timecode"
 
-#: models/core.py:53
+#: models/core.py:69
 msgid "Note|tc_end"
 msgstr "end timeocde"
 
-#: models/core.py:58
+#: models/core.py:74
 msgid "Note|session"
 msgstr "session"
 
-#: models/core.py:63
+#: models/core.py:79
 msgid "Note|plain"
 msgstr "text plain"
 
-#: models/core.py:68
+#: models/core.py:84
 msgid "Note|html"
 msgstr "text html"
 
-#: models/core.py:73
+#: models/core.py:89
 msgid "Note|raw"
 msgstr "text raw"
 
-#: models/core.py:78
+#: models/core.py:94
 msgid "Note|margin_note"
 msgstr "margin note"
 
-#: models/core.py:83
+#: models/core.py:99
 msgid "Note|categorization"
 msgstr "categorization"
--- a/src/notes/locale/fr/LC_MESSAGES/django.po	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/locale/fr/LC_MESSAGES/django.po	Mon Jul 31 23:18:38 2017 +0200
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-21 10:51+0000\n"
+"POT-Creation-Date: 2017-07-31 10:40+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,27 +17,31 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-#: models/auth.py:11
+#: models/auth.py:12
 msgid "User"
 msgstr "Utilisateur"
 
-#: models/auth.py:12
+#: models/auth.py:13
 msgid "Users"
 msgstr "Utilisateurs"
 
-#: models/auth.py:17
+#: models/auth.py:23
+msgid "UserProfile|default_group"
+msgstr "Groupe par défaut"
+
+#: models/auth.py:26
 msgid "UserProfile"
 msgstr "Profil utilisateur"
 
-#: models/auth.py:18
+#: models/auth.py:27
 msgid "UserProfiles"
 msgstr "Profils utilisateur"
 
-#: models/auth.py:25
+#: models/auth.py:48
 msgid "GroupProfile"
 msgstr "Profile groupe"
 
-#: models/auth.py:26
+#: models/auth.py:49
 msgid "GroupProfiles"
 msgstr "Profiles groupe"
 
@@ -97,66 +101,70 @@
 msgid "Categories"
 msgstr "Categories"
 
-#: models/core.py:17
+#: models/core.py:19
 msgid "Session"
 msgstr "Session"
 
-#: models/core.py:18
+#: models/core.py:20
 msgid "Sessions"
 msgstr "Sessions"
 
-#: models/core.py:20
+#: models/core.py:22
 msgid "Session|date"
 msgstr "date"
 
-#: models/core.py:29
+#: models/core.py:31
 msgid "Session|title"
 msgstr "titre"
 
-#: models/core.py:34
+#: models/core.py:36
 msgid "Session|description"
 msgstr "description"
 
-#: models/core.py:39
+#: models/core.py:41
+msgid "Session|group"
+msgstr "groupe"
+
+#: models/core.py:46
 msgid "Session|protocol"
 msgstr "protocole"
 
-#: models/core.py:48
+#: models/core.py:61
 msgid "Note"
 msgstr "Note"
 
-#: models/core.py:49
+#: models/core.py:62
 msgid "Notes"
 msgstr "Notes"
 
-#: models/core.py:52
+#: models/core.py:68
 msgid "Note|tc_start"
 msgstr "timecode début"
 
-#: models/core.py:53
+#: models/core.py:69
 msgid "Note|tc_end"
 msgstr "timecode fin"
 
-#: models/core.py:58
+#: models/core.py:74
 msgid "Note|session"
 msgstr "session"
 
-#: models/core.py:63
+#: models/core.py:79
 msgid "Note|plain"
 msgstr "texte seul"
 
-#: models/core.py:68
+#: models/core.py:84
 msgid "Note|html"
 msgstr "texte html"
 
-#: models/core.py:73
+#: models/core.py:89
 msgid "Note|raw"
 msgstr "texte brut"
 
-#: models/core.py:78
+#: models/core.py:94
 msgid "Note|margin_note"
 msgstr "note de marge"
 
-#: models/core.py:83
+#: models/core.py:99
 msgid "Note|categorization"
 msgstr "catégorisation"
--- a/src/notes/migrations/0001_initial.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/migrations/0001_initial.py	Mon Jul 31 23:18:38 2017 +0200
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.11.2 on 2017-07-19 13:26
+# Generated by Django 1.11.2 on 2017-07-31 13:32
 from __future__ import unicode_literals
 
 import colorful.fields
@@ -68,6 +68,7 @@
                 ('description', models.TextField(blank=True, null=True)),
                 ('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to='auth.Group')),
                 ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+                ('owner_personal', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='personal_group', to=settings.AUTH_USER_MODEL)),
             ],
             options={
                 'verbose_name': 'GroupProfile',
@@ -124,6 +125,7 @@
                 ('title', models.TextField(blank=True, null=True, verbose_name='Session|title')),
                 ('description', models.TextField(blank=True, null=True, verbose_name='Session|description')),
                 ('protocol', models.TextField(blank=True, null=True, verbose_name='Session|protocol')),
+                ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group', verbose_name='Session|group')),
                 ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
             ],
             options={
@@ -135,7 +137,8 @@
             name='UserProfile',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+                ('default_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group', verbose_name='UserProfile|default_group')),
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
             ],
             options={
                 'verbose_name': 'UserProfile',
--- a/src/notes/models/auth.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/models/auth.py	Mon Jul 31 23:18:38 2017 +0200
@@ -7,13 +7,20 @@
 
 
 class User(AbstractUser):
+
     class Meta:
         verbose_name = _('User')
         verbose_name_plural = _('Users')
 
 
 class UserProfile(models.Model):
-    user = models.OneToOneField(User, on_delete=models.CASCADE)
+    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
+
+    # TODO:
+    default_group = models.ForeignKey(
+        Group,
+        null=False,
+        verbose_name=_("UserProfile|default_group"))
 
     class Meta:
         verbose_name = _('UserProfile')
@@ -26,6 +33,17 @@
     description = models.TextField(null=True, blank=True)
     # TODO: manage when user is deleted: put first user as owner. delete group if empty
     owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
+    # TODO: can not delete group when user is not deleted.
+    # TODO: other users cannot set as owner
+    # TODO: delete personal group when user is deleted
+    # TODO: change group name when user change name
+    # indicate that this group is a personal group.
+    # used this instead of a boolean because it allows to easily enforce that a user has only one personal group
+    owner_personal = models.OneToOneField(User, on_delete=models.CASCADE, null=True, related_name='personal_group')
+
+    @property
+    def is_personal(self):
+        return self.owner == self.owner_personal
 
     class Meta:
         verbose_name = _('GroupProfile')
--- a/src/notes/models/core.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/models/core.py	Mon Jul 31 23:18:38 2017 +0200
@@ -3,6 +3,7 @@
 """
 from auditlog.registry import auditlog
 from django.conf import settings
+from django.contrib.auth.models import Group
 from django.db import models
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _
@@ -34,6 +35,11 @@
         blank=True,
         verbose_name=_('Session|description')
     )
+    group = models.ForeignKey(
+        Group,
+        null=False,
+        verbose_name=_('Session|group')
+    )
     protocol = models.TextField(
         null=True,
         blank=True,
--- a/src/notes/signals.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/signals.py	Mon Jul 31 23:18:38 2017 +0200
@@ -1,10 +1,17 @@
 """
 Signals for notes app
 """
+import logging
+
+from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Group
 from django.db.models.signals import post_save
 from django.dispatch import receiver
-from django.contrib.auth.models import Group
-from notes.models import GroupProfile
+
+from notes.models import GroupProfile, UserProfile
+
+User = get_user_model()
+logger = logging.getLogger(__name__)
 
 @receiver(post_save, sender=Group, dispatch_uid="group_created_signal")
 def group_saved_callback(sender, instance, **kwargs):
@@ -13,3 +20,33 @@
         profile = GroupProfile(group=instance)
         profile.save()
 
+@receiver(post_save, sender=User, dispatch_uid="user_created_signal")
+def user_saved_callback(sender, instance, **kwargs):
+    created = kwargs.pop('created')
+    if not instance:
+        return
+    base_group_name = instance.username + " group"
+    group_name = base_group_name
+    if created:
+        # create personal group
+        personal_group = None
+        personal_group_profile = GroupProfile.objects.filter(owner=instance, owner_personal=instance).first()
+        if personal_group_profile is None:
+            # find a new name
+            i = 1
+            while Group.objects.filter(name=group_name).exists(): # should always ends...
+                group_name = "%s %s" % (base_group_name, i)
+                i += 1
+            personal_group = Group.objects.create(name=group_name)
+            personal_group.user_set.add(instance)
+            personal_group_profile = personal_group.profile
+            personal_group_profile.description = "%s personal group" % instance.username
+            personal_group_profile.owner = instance
+            personal_group_profile.owner_personal = instance
+            personal_group_profile.save()
+        else:
+            personal_group = personal_group_profile.group
+
+        UserProfile.objects.create(user=instance, default_group=personal_group)
+    # else we do nothing, because we do not know if the group's name was changed or not
+    # and we do not know the user older name
--- a/src/notes/tests/api/auth.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/tests/api/auth.py	Mon Jul 31 23:18:38 2017 +0200
@@ -20,12 +20,12 @@
         User = get_user_model()
         self.user1 = User.objects.create_user(
             username='test_user1',
-            email='test_user@emial.com',
+            email='test_user@email.com',
             password='top_secret'
         )
         self.user2 = User.objects.create_user(
             username='test_user2',
-            email='test_user@emial.com',
+            email='test_user@email.com',
             password='top_secret'
         )
 
@@ -58,14 +58,24 @@
 
         self.assertEqual(response.status_code, status.HTTP_200_OK)
 
-        self.assertEqual(2, len(response.data), "Must have 2 groups")
-        for group_def in response.data:
-            self.assertIn('name', group_def.keys())
-            self.assertIn('owner', group_def.keys())
-            self.assertIn('description', group_def.keys())
+        resp_json = response.json()
+        self.assertIn('count', resp_json)
+        self.assertIn('results', resp_json)
+        self.assertEqual(4, resp_json['count'], "Must have 4 groups")
+        self.assertEqual(4, len(resp_json['results']), "Must have 4 groups")
+
+        for group_def in resp_json['results']:
+            self.assertIn('name', group_def)
+            self.assertIn('owner', group_def)
+            self.assertIn('is_personal', group_def)
+            self.assertIn('description', group_def)
             self.assertIn(group_def.get('owner'), ['test_user1', 'test_user2'])
-            self.assertIn(group_def.get('description'), ['This is the group 1', 'This is the group 2'])
-            self.assertIn(group_def.get('name'), ['group1', 'group2'])
+            self.assertIn(group_def.get('description'), [
+                'This is the group 1',
+                'This is the group 2',
+                'test_user1 personal group',
+                'test_user2 personal group'])
+            self.assertIn(group_def.get('name'), ['group1', 'group2', 'test_user1 group', 'test_user2 group'])
 
     def test_create_group(self):
         url = reverse('auth_group-list')
--- a/src/notes/tests/api/note.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/tests/api/note.py	Mon Jul 31 23:18:38 2017 +0200
@@ -43,6 +43,7 @@
             title="a new session 1",
             description="Description 1",
             protocol="[]",
+            group=user1.profile.default_group,
             owner=user1
         )
 
@@ -50,6 +51,7 @@
             title="a new session 2",
             description="Description 2",
             protocol="[]",
+            group=user2.profile.default_group,
             owner=user2
         )
 
@@ -57,6 +59,7 @@
             title="a new session 3",
             description="Description 3",
             protocol="[]",
+            group=user3.profile.default_group,
             owner=user3
         )
 
--- a/src/notes/tests/api/session.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/tests/api/session.py	Mon Jul 31 23:18:38 2017 +0200
@@ -6,12 +6,13 @@
 from uuid import uuid4
 
 from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Group
 from django.urls import reverse
 from django.utils import timezone
 from rest_framework import status
 from rest_framework.test import APITransactionTestCase
 
-from notes.models import Session, Note
+from notes.models import Session, Note, GroupProfile
 
 logger = logging.getLogger(__name__)
 
@@ -19,12 +20,12 @@
 
     def setUp(self):
         User = get_user_model()
-        user1 = User.objects.create_user(
+        self.user1 = User.objects.create_user(
             username='test_user1',
             email='test_user@emial.com',
             password='top_secret'
         )
-        user2 = User.objects.create_user(
+        self.user2 = User.objects.create_user(
             username='test_user2',
             email='test_user@emial.com',
             password='top_secret'
@@ -35,24 +36,38 @@
             password='top_secret'
         )
 
+        self.group1 = Group(name='group1')
+        self.group1.save()
+        self.group1.user_set.add(self.user1)
+        self.group1.user_set.add(self.user2)
+        self.group1.profile.owner = self.user1
+        self.group1.profile.description = "This is the group 1"
+        self.group1.profile.save()
+
+        self.user2.profile.default_group = self.group1
+        self.user2.profile.save()
+
         self.session1 = Session.objects.create(
             title="a new session 1",
             description="Description 1",
             protocol="[]",
-            owner=user1
+            group=self.user1.profile.default_group,
+            owner=self.user1
         )
 
         self.session2 = Session.objects.create(
             title="a new session 2",
             description="Description 2",
             protocol="[]",
-            owner=user2
+            group=self.user2.profile.default_group,
+            owner=self.user2
         )
 
         self.session3 = Session.objects.create(
             title="a new session 3",
             description="Description 3",
             protocol="[]",
+            group=user3.profile.default_group,
             owner=user3
         )
 
@@ -60,6 +75,7 @@
             title="a new session 4",
             description="Description 4",
             protocol="[]",
+            group=user3.profile.default_group,
             owner=user3
         )
 
@@ -150,14 +166,89 @@
         response = self.client.post(url, {
             'title': "a new session",
             'description': "description of the session",
-            'protocol': "[]"
+            'protocol': "[]",
+            'group': 'group1'
         }, format='json')
 
-
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
         json = response.json()
         self.assertIn('ext_id', json)
 
+        session = Session.objects.get(ext_id=json['ext_id'])
+        self.assertIsNotNone(session)
+        self.assertEqual(self.group1, session.group)
+
+
+    def test_create_session_no_group(self):
+        url = reverse('notes:session-list')
+        self.client.login(username='test_user1', password='top_secret')
+        response = self.client.post(url, {
+            'title': "a new session",
+            'description': "description of the session",
+            'protocol': "[]",
+        }, format='json')
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED, "Error when creating session %r" % response.json())
+        json = response.json()
+        self.assertIn('ext_id', json)
+
+        session = Session.objects.get(ext_id=json['ext_id'])
+        self.assertIsNotNone(session)
+        user1_personal_group = Group.objects.get(profile__owner_personal=self.user1)
+        self.assertEqual(user1_personal_group, session.group)
+
+    def test_create_session_no_group_default(self):
+        url = reverse('notes:session-list')
+        self.client.login(username='test_user2', password='top_secret')
+        response = self.client.post(url, {
+            'title': "a new session",
+            'description': "description of the session",
+            'protocol': "[]",
+        }, format='json')
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED, "Error when creating session %r" % response.json())
+        json = response.json()
+        self.assertIn('ext_id', json)
+
+        session = Session.objects.get(ext_id=json['ext_id'])
+        self.assertIsNotNone(session)
+        self.assertEqual(self.group1, session.group)
+
+
+    def test_create_session_no_group_default(self):
+        url = reverse('notes:session-list')
+        self.client.login(username='test_user2', password='top_secret')
+        response = self.client.post(url, {
+            'title': "a new session",
+            'description': "description of the session",
+            'protocol': "[]",
+        }, format='json')
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED, "Error when creating session %r" % response.json())
+        json = response.json()
+        self.assertIn('ext_id', json)
+
+        session = Session.objects.get(ext_id=json['ext_id'])
+        self.assertIsNotNone(session)
+        self.assertEqual(self.group1, session.group)
+
+
+    def test_create_session_bad_group(self):
+        url = reverse('notes:session-list')
+        self.client.login(username='test_user3', password='top_secret')
+        response = self.client.post(url, {
+            'title': "a new session",
+            'description': "description of the session",
+            'protocol': "[]",
+            'group': "group1"
+        }, format='json')
+
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        json = response.json()
+        self.assertIn('non_field_errors', json)
+        self.assertIn('Owner must be in group', json['non_field_errors'])
+
+
     def test_create_session_with_ext_id(self):
         url = reverse('notes:session-list')
         self.client.login(username='test_user1', password='top_secret')
@@ -166,6 +257,7 @@
             'ext_id': ext_id,
             'title': "a new session",
             'description': "description of the session",
+            'group': 'group1',
             'protocol': "[]"
         }, format='json')
 
--- a/src/notes/tests/models/auth.py	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/notes/tests/models/auth.py	Mon Jul 31 23:18:38 2017 +0200
@@ -20,6 +20,10 @@
             email='test_user@emial.com',
             password='top_secret'
         )
+
+        group = Group(name='test_user2 group')
+        group.save()
+
         self.user2 = User.objects.create_user(
             username='test_user2',
             email='test_user@emial.com',
@@ -32,3 +36,23 @@
         self.assertIsNotNone(self.group)
         self.assertIsNotNone(self.group.profile)
 
+    def test_create_personal_group(self):
+        self.assertIsNotNone(self.user1.profile)
+        self.assertIsNotNone(self.user2.profile)
+        default_group1 = self.user1.profile.default_group
+        self.assertIsNotNone(default_group1)
+        self.assertIsNotNone(default_group1.profile)
+        self.assertTrue(default_group1.profile.is_personal)
+        self.assertEqual(default_group1.profile.owner, self.user1)
+        self.assertEqual(default_group1.profile.owner_personal, self.user1)
+
+    def test_create_personal_group_new_name(self):
+        default_group2 = self.user2.profile.default_group
+        self.assertIsNotNone(default_group2)
+        self.assertEqual(default_group2.name, "test_user2 group 1")
+
+    def test_create_personal_group(self):
+        personal_group = Group.objects.get(profile__owner_personal=self.user1)
+        self.assertIsNotNone(personal_group)
+        self.assertIn(self.user1, personal_group.user_set.all())
+
--- a/src/requirements/dev.txt	Sun Jul 30 01:02:09 2017 +0200
+++ b/src/requirements/dev.txt	Mon Jul 31 23:18:38 2017 +0200
@@ -1,1 +1,3 @@
 -r base.txt
+pylint-django
+pylint