Add original creator to custom content blocks
This commit is contained in:
parent
c8edbf9b13
commit
6646b328b7
|
|
@ -1,2 +1,2 @@
|
||||||
nodejs 12.22.1
|
nodejs 12.22.1
|
||||||
python 3.8.5
|
python 3.8.10
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Generated by Django 2.2.23 on 2021-06-03 13:05
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('books', '0029_auto_20210511_1301'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='contentblock',
|
||||||
|
name='original_creator',
|
||||||
|
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='snapshot',
|
||||||
|
name='objective_groups',
|
||||||
|
field=models.ManyToManyField(related_name='_snapshot_objective_groups_+', through='books.ObjectiveGroupSnapshot', to='objectives.ObjectiveGroup'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 2.2.23 on 2021-06-03 13:06
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('books', '0030_auto_20210603_1305'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='contentblock',
|
||||||
|
name='original_creator',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -42,6 +42,7 @@ class ContentBlock(StrictHierarchyPage):
|
||||||
# blocks with owner are hidden by default, need to be shown for each class
|
# blocks with owner are hidden by default, need to be shown for each class
|
||||||
visible_for = models.ManyToManyField(SchoolClass, related_name='visible_content_blocks')
|
visible_for = models.ManyToManyField(SchoolClass, related_name='visible_content_blocks')
|
||||||
user_created = models.BooleanField(default=False)
|
user_created = models.BooleanField(default=False)
|
||||||
|
original_creator = models.ForeignKey(User, null=True, blank=True, default=None, on_delete=models.SET_NULL)
|
||||||
|
|
||||||
bookmarks = models.ManyToManyField(User, through=ContentBlockBookmark, related_name='bookmarked_content_blocks')
|
bookmarks = models.ManyToManyField(User, through=ContentBlockBookmark, related_name='bookmarked_content_blocks')
|
||||||
|
|
||||||
|
|
@ -133,7 +134,8 @@ class ContentBlockSnapshot(ContentBlock):
|
||||||
contents=self.contents,
|
contents=self.contents,
|
||||||
type=self.type,
|
type=self.type,
|
||||||
title=self.title,
|
title=self.title,
|
||||||
owner=owner
|
owner=owner,
|
||||||
|
original_creator=self.original_creator
|
||||||
)
|
)
|
||||||
self.add_sibling(instance=cb, pos='right')
|
self.add_sibling(instance=cb, pos='right')
|
||||||
# some wagtail magic
|
# some wagtail magic
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,8 @@ class SnapshotManager(models.Manager):
|
||||||
snapshot=snapshot,
|
snapshot=snapshot,
|
||||||
contents=content_block.contents,
|
contents=content_block.contents,
|
||||||
type=content_block.type,
|
type=content_block.type,
|
||||||
title=content_block.title
|
title=content_block.title,
|
||||||
|
original_creator=content_block.owner
|
||||||
)
|
)
|
||||||
content_block.add_sibling(instance=new_content_block, pos='right')
|
content_block.add_sibling(instance=new_content_block, pos='right')
|
||||||
revision = new_content_block.save_revision()
|
revision = new_content_block.save_revision()
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ def is_solution_and_hidden_for_user(type, user, module):
|
||||||
class ContentBlockNode(DjangoObjectType, HiddenAndVisibleForMixin):
|
class ContentBlockNode(DjangoObjectType, HiddenAndVisibleForMixin):
|
||||||
mine = graphene.Boolean()
|
mine = graphene.Boolean()
|
||||||
bookmarks = graphene.List(ContentBlockBookmarkNode)
|
bookmarks = graphene.List(ContentBlockBookmarkNode)
|
||||||
|
original_creator = graphene.Field('users.schema.PublicUserNode')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ContentBlock
|
model = ContentBlock
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@ query ModulesQuery($slug: String, $id: ID) {
|
||||||
contentBlocks {
|
contentBlocks {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
|
originalCreator {
|
||||||
|
id
|
||||||
|
fullName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
visibleFor {
|
visibleFor {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,8 @@ class CreateSnapshotTestCase(SkillboxTestCase):
|
||||||
school_class_name in [school_class['name'] for school_class in
|
school_class_name in [school_class['name'] for school_class in
|
||||||
custom_objective.get('visibleFor')])
|
custom_objective.get('visibleFor')])
|
||||||
|
|
||||||
|
return module
|
||||||
|
|
||||||
def _compare_content_blocks(self, content_blocks):
|
def _compare_content_blocks(self, content_blocks):
|
||||||
self.assertEqual(len(content_blocks), 4)
|
self.assertEqual(len(content_blocks), 4)
|
||||||
first, second, third, fourth = content_blocks
|
first, second, third, fourth = content_blocks
|
||||||
|
|
@ -203,7 +205,10 @@ class CreateSnapshotTestCase(SkillboxTestCase):
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.assertIsNone(result.get('errors'))
|
self.assertIsNone(result.get('errors'))
|
||||||
self._test_module_visibility(client, school_class_name)
|
module = self._test_module_visibility(client, school_class_name)
|
||||||
|
original_creator = module['chapters'][0]['contentBlocks'][2].get('originalCreator')
|
||||||
|
self.assertIsNotNone(original_creator)
|
||||||
|
self.assertEqual(original_creator.get('id'), to_global_id('PublicUserNode', self.teacher.pk))
|
||||||
|
|
||||||
def test_display_snapshot_module(self):
|
def test_display_snapshot_module(self):
|
||||||
self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class,
|
self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class,
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,13 @@ from graphene_django.filter import DjangoFilterConnectionField
|
||||||
from api.utils import get_object, get_by_id_or_slug
|
from api.utils import get_object, get_by_id_or_slug
|
||||||
from rooms.models import Room, RoomEntry, ModuleRoomSlug
|
from rooms.models import Room, RoomEntry, ModuleRoomSlug
|
||||||
from users.models import SchoolClass
|
from users.models import SchoolClass
|
||||||
from users.schema import UserNode
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RoomEntryNode(DjangoObjectType):
|
class RoomEntryNode(DjangoObjectType):
|
||||||
pk = graphene.Int()
|
pk = graphene.Int()
|
||||||
author = UserNode()
|
author = graphene.Field('users.schema.PublicUserNode')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RoomEntry
|
model = RoomEntry
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class TeamNode(DjangoObjectType):
|
||||||
interfaces = (relay.Node,)
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
pk = graphene.Int()
|
pk = graphene.Int()
|
||||||
members = graphene.List('users.schema.UserNode')
|
members = graphene.List('users.schema.PublicUserNode')
|
||||||
|
|
||||||
def resolve_pk(self, *args, **kwargs):
|
def resolve_pk(self, *args, **kwargs):
|
||||||
return self.id
|
return self.id
|
||||||
|
|
@ -68,7 +68,28 @@ class RecentModuleFilter(FilterSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserNode(DjangoObjectType):
|
class PublicUserNode(DjangoObjectType):
|
||||||
|
is_me = graphene.Boolean()
|
||||||
|
full_name = graphene.String(required=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
only_fields = ['full_name', 'first_name', 'last_name', 'avatar_url']
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_is_me(parent: User, info, **kwargs):
|
||||||
|
return info.context.user.pk == parent.pk
|
||||||
|
|
||||||
|
class PrivateUserNode(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
filter_fields = ['username', 'email']
|
||||||
|
only_fields = ['username', 'email', 'first_name', 'last_name', 'school_classes', 'last_module',
|
||||||
|
'last_topic', 'avatar_url',
|
||||||
|
'selected_class', 'expiry_date', 'onboarding_visited', 'team']
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
pk = graphene.Int()
|
pk = graphene.Int()
|
||||||
permissions = graphene.List(graphene.String)
|
permissions = graphene.List(graphene.String)
|
||||||
selected_class = graphene.Field(SchoolClassNode)
|
selected_class = graphene.Field(SchoolClassNode)
|
||||||
|
|
@ -77,15 +98,6 @@ class UserNode(DjangoObjectType):
|
||||||
old_classes = DjangoFilterConnectionField(SchoolClassNode)
|
old_classes = DjangoFilterConnectionField(SchoolClassNode)
|
||||||
recent_modules = DjangoFilterConnectionField(ModuleNode, filterset_class=RecentModuleFilter)
|
recent_modules = DjangoFilterConnectionField(ModuleNode, filterset_class=RecentModuleFilter)
|
||||||
team = graphene.Field(TeamNode)
|
team = graphene.Field(TeamNode)
|
||||||
is_me = graphene.Boolean()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
filter_fields = ['username', 'email']
|
|
||||||
only_fields = ['username', 'email', 'first_name', 'last_name', 'school_classes', 'last_module',
|
|
||||||
'last_topic', 'avatar_url',
|
|
||||||
'selected_class', 'expiry_date', 'onboarding_visited', 'team']
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
def resolve_pk(self, info, **kwargs):
|
def resolve_pk(self, info, **kwargs):
|
||||||
return self.id
|
return self.id
|
||||||
|
|
@ -121,16 +133,13 @@ class UserNode(DjangoObjectType):
|
||||||
def resolve_team(self, info, **kwargs):
|
def resolve_team(self, info, **kwargs):
|
||||||
return self.team
|
return self.team
|
||||||
|
|
||||||
def resolve_is_me(self, info, **kwargs):
|
|
||||||
return info.context.user.pk == self.pk
|
|
||||||
|
|
||||||
|
|
||||||
class ClassMemberNode(ObjectType):
|
class ClassMemberNode(ObjectType):
|
||||||
"""
|
"""
|
||||||
We need to build this ourselves, because we want the active property on the node, because providing it on the
|
We need to build this ourselves, because we want the active property on the node, because providing it on the
|
||||||
Connection or Edge for a UserNodeConnection is difficult.
|
Connection or Edge for a UserNodeConnection is difficult.
|
||||||
"""
|
"""
|
||||||
user = graphene.Field(UserNode)
|
user = graphene.Field('users.schema.PublicUserNode')
|
||||||
active = graphene.Boolean()
|
active = graphene.Boolean()
|
||||||
first_name = graphene.String()
|
first_name = graphene.String()
|
||||||
last_name = graphene.String()
|
last_name = graphene.String()
|
||||||
|
|
@ -138,7 +147,7 @@ class ClassMemberNode(ObjectType):
|
||||||
id = graphene.ID()
|
id = graphene.ID()
|
||||||
|
|
||||||
def resolve_id(self, *args):
|
def resolve_id(self, *args):
|
||||||
return to_global_id('UserNode', self.user.pk)
|
return to_global_id('PublicUserNode', self.user.pk)
|
||||||
|
|
||||||
def resolve_active(self, *args):
|
def resolve_active(self, *args):
|
||||||
return self.active
|
return self.active
|
||||||
|
|
@ -157,8 +166,8 @@ class ClassMemberNode(ObjectType):
|
||||||
|
|
||||||
|
|
||||||
class UsersQuery(object):
|
class UsersQuery(object):
|
||||||
me = graphene.Field(UserNode)
|
me = graphene.Field(PrivateUserNode)
|
||||||
all_users = DjangoFilterConnectionField(UserNode)
|
all_users = DjangoFilterConnectionField(PrivateUserNode)
|
||||||
my_activity = DjangoFilterConnectionField(ModuleNode)
|
my_activity = DjangoFilterConnectionField(ModuleNode)
|
||||||
my_instrument_activity = DjangoFilterConnectionField(InstrumentNode)
|
my_instrument_activity = DjangoFilterConnectionField(InstrumentNode)
|
||||||
|
|
||||||
|
|
@ -179,8 +188,8 @@ class UsersQuery(object):
|
||||||
|
|
||||||
|
|
||||||
class AllUsersQuery(object):
|
class AllUsersQuery(object):
|
||||||
me = graphene.Field(UserNode)
|
me = graphene.Field(PrivateUserNode)
|
||||||
all_users = DjangoFilterConnectionField(UserNode)
|
all_users = DjangoFilterConnectionField(PrivateUserNode)
|
||||||
|
|
||||||
def resolve_all_users(self, info, **kwargs):
|
def resolve_all_users(self, info, **kwargs):
|
||||||
if not info.context.user.is_superuser:
|
if not info.context.user.is_superuser:
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,11 @@ class JoinSchoolClassTest(TestCase):
|
||||||
]
|
]
|
||||||
create_users(user_data)
|
create_users(user_data)
|
||||||
teacher = User.objects.get(username='emily.sands')
|
teacher = User.objects.get(username='emily.sands')
|
||||||
self.teacher_id = to_global_id('UserNode', teacher.pk)
|
self.teacher_id = to_global_id('PublicUserNode', teacher.pk)
|
||||||
student = User.objects.get(username='adam.groff')
|
student = User.objects.get(username='adam.groff')
|
||||||
self.student_id = to_global_id('UserNode', student.pk)
|
self.student_id = to_global_id('PublicUserNode', student.pk)
|
||||||
other_student = User.objects.get(username='eric.effiong')
|
other_student = User.objects.get(username='eric.effiong')
|
||||||
self.other_student_id = to_global_id('UserNode', other_student.pk)
|
self.other_student_id = to_global_id('PublicUserNode', other_student.pk)
|
||||||
|
|
||||||
school_class = SchoolClass.objects.get(name=self.school_class_name)
|
school_class = SchoolClass.objects.get(name=self.school_class_name)
|
||||||
self.school_class_id = to_global_id('SchoolClassNode', school_class.pk)
|
self.school_class_id = to_global_id('SchoolClassNode', school_class.pk)
|
||||||
|
|
@ -115,7 +115,7 @@ class JoinSchoolClassTest(TestCase):
|
||||||
result = self.client.execute(self.mutation, variables={
|
result = self.client.execute(self.mutation, variables={
|
||||||
'input': {
|
'input': {
|
||||||
'schoolClass': to_global_id('SchoolClassNode', school_class.id),
|
'schoolClass': to_global_id('SchoolClassNode', school_class.id),
|
||||||
'member': to_global_id('UserNode', student.id),
|
'member': to_global_id('PublicUserNode', student.id),
|
||||||
'active': False
|
'active': False
|
||||||
}
|
}
|
||||||
}, context=self.teacher_context)
|
}, context=self.teacher_context)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue