Allow multiple teachers per school class

For that purpose, now the enabled solutions are stored on a per school class
basis instead of a per teacher basis
This commit is contained in:
Ramon Wenger 2020-03-02 17:37:29 +01:00
parent 0ac0f3d610
commit ab9da652cf
10 changed files with 88 additions and 20 deletions

View File

@ -0,0 +1,19 @@
# Generated by Django 2.1.15 on 2020-03-02 16:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0011_auto_20200302_1613'),
('books', '0016_auto_20191128_1601'),
]
operations = [
migrations.AddField(
model_name='module',
name='solutions_enabled_for',
field=models.ManyToManyField(to='users.SchoolClass'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.1.15 on 2020-03-02 16:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('books', '0017_module_solutions_enabled_for'),
]
operations = [
migrations.RemoveField(
model_name='module',
name='solutions_enabled_by',
),
]

View File

@ -7,7 +7,7 @@ from wagtail.images.edit_handlers import ImageChooserPanel
from books.blocks import DEFAULT_RICH_TEXT_FEATURES
from core.wagtail_utils import StrictHierarchyPage
from users.models import User
from users.models import SchoolClass
logger = logging.getLogger(__name__)
@ -32,7 +32,7 @@ class Module(StrictHierarchyPage):
teaser = models.TextField()
intro = RichTextField(features=DEFAULT_RICH_TEXT_FEATURES)
solutions_enabled_by = models.ManyToManyField(User)
solutions_enabled_for = models.ManyToManyField(SchoolClass)
content_panels = [
FieldPanel('title', classname="full title"),

View File

@ -21,14 +21,15 @@ class UpdateSolutionVisibility(relay.ClientIDMutation):
slug = args.get('slug')
enabled = args.get('enabled')
user = info.context.user
selected_class = user.selected_class()
if 'users.can_manage_school_class_content' not in user.get_role_permissions():
raise PermissionError()
module = Module.objects.get(slug=slug)
if enabled:
module.solutions_enabled_by.add(user)
module.solutions_enabled_for.add(selected_class)
else:
module.solutions_enabled_by.remove(user)
module.solutions_enabled_for.remove(selected_class)
module.save()
return cls(success=True, solutions_enabled=enabled)

View File

@ -145,8 +145,8 @@ class ModuleNode(DjangoObjectType):
return self.get_parent().specific
def resolve_solutions_enabled(self, info, **kwargs):
teacher = info.context.user.get_teacher()
return self.solutions_enabled_by.filter(pk=teacher.pk).exists() if teacher is not None else False
school_class = info.context.user.selected_class()
return self.solutions_enabled_for.filter(pk=school_class.pk).exists() if school_class is not None else False
def resolve_bookmark(self, info, **kwags):
return ModuleBookmark.objects.filter(

View File

@ -3,9 +3,9 @@ from graphene.test import Client
from graphql_relay import to_global_id
from api.schema import schema
from api.utils import get_graphql_mutation
from books.factories import BookFactory, TopicFactory, ModuleFactory, ChapterFactory, ContentBlockFactory
from books.models import ContentBlock
from books.factories import BookFactory, ContentBlockFactory
from users.models import User
from users.services import create_users
@ -30,6 +30,7 @@ class ModuleSolutionVisibilityTest(TestCase):
)
self.teacher = User.objects.get(username="teacher")
self.selected_class = self.teacher.selected_class()
self.student = User.objects.get(username="student1")
student_request = RequestFactory().get('/')
student_request.user = self.student
@ -41,13 +42,7 @@ class ModuleSolutionVisibilityTest(TestCase):
self.content_block_id = to_global_id('ContentBlockNode', content_block.pk)
self.update_mutation = mutation = """
mutation UpdateSolutionVisibility($input: UpdateSolutionVisibilityInput!) {
updateSolutionVisibility(input: $input) {
success
}
}
"""
self.update_mutation = get_graphql_mutation('updateSolutionVisibility.gql')
self.query = """
query ContentBlockQuery($id: ID!) {
@ -59,6 +54,8 @@ class ModuleSolutionVisibilityTest(TestCase):
"""
def test_hide_solutions_for_students_and_then_show_them(self):
self.assertEqual(self.student.selected_class(), self.teacher.selected_class())
result = self.student_client.execute(self.query, variables={
'id': self.content_block_id
})
@ -70,7 +67,7 @@ class ModuleSolutionVisibilityTest(TestCase):
'id': self.content_block_id
})
self.assertIsNone(result.get('errors'))
self.assertEqual(len(result.get('data').get('contentBlock').get('contents')), 1)
self.assertEqual(len(result.get('data').get('contentBlock').get('contents')), 0)
result = self.teacher_client.execute(self.update_mutation, variables={
'input': {
@ -81,7 +78,7 @@ class ModuleSolutionVisibilityTest(TestCase):
self.assertEqual(result.get('data').get('updateSolutionVisibility').get('success'), True)
self.assertEqual(self.module.solutions_enabled_by.filter(pk=self.teacher.pk).count(), 1)
self.assertEqual(self.module.solutions_enabled_for.filter(pk=self.selected_class.pk).count(), 1)
result = self.student_client.execute(self.query, variables={
'id': self.content_block_id

View File

@ -4,8 +4,8 @@ from users.models import User, Role
def are_solutions_enabled_for(user: User, module: Module):
teacher = user.users_in_same_school_class().filter(user_roles__role=Role.objects.get_default_teacher_role()).first()
return 'users.can_manage_school_class_content' in user.get_role_permissions() or user.is_superuser or (
teacher is not None and module.solutions_enabled_by.filter(pk=teacher.pk).exists())
school_class = user.selected_class()
return module.solutions_enabled_for.filter(pk=school_class.pk).exists()
def get_type_and_value(data):

View File

@ -8,5 +8,5 @@ class Command(BaseCommand):
self.stdout.write("Hiding solutions")
for module in Module.objects.all():
self.stdout.write("Hiding solutions in module {}.".format(module.title))
module.solutions_enabled_by.clear()
module.solutions_enabled_for.clear()
self.stdout.write("Hiding done")

View File

@ -0,0 +1,17 @@
# Generated by Django 2.1.15 on 2020-03-02 16:13
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('rooms', '0007_auto_20190808_0649'),
]
operations = [
migrations.AlterModelOptions(
name='moduleroomslug',
options={'verbose_name': 'Slug für Modul-Raum', 'verbose_name_plural': 'Slugs für Modul-Raum'},
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.1.15 on 2020-03-02 16:13
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0010_schoolclass_code'),
]
operations = [
migrations.AlterModelOptions(
name='schoolclass',
options={'verbose_name': 'Schulklasse', 'verbose_name_plural': 'Schulklassen'},
),
]