Add new test for school class read only

Also clean up some code
This commit is contained in:
Ramon Wenger 2021-07-28 16:19:42 +02:00
parent 462348dd32
commit d071afbd67
11 changed files with 62 additions and 48 deletions

View File

@ -24,7 +24,7 @@ class UpdateSolutionVisibility(relay.ClientIDMutation):
slug = args.get('slug')
enabled = args.get('enabled')
user = info.context.user
selected_class = user.selected_class()
selected_class = user.selected_class
if 'users.can_manage_school_class_content' not in user.get_role_permissions():
raise PermissionError()

View File

@ -45,7 +45,7 @@ class ModuleNode(DjangoObjectType):
return self.get_parent().specific
def resolve_solutions_enabled(self, info, **kwargs):
school_class = info.context.user.selected_class()
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):

View File

@ -30,7 +30,7 @@ class ModuleSolutionVisibilityTest(TestCase):
)
self.teacher = User.objects.get(username="teacher")
self.selected_class = self.teacher.selected_class()
self.selected_class = self.teacher.selected_class
self.student = User.objects.get(username="student1")
student_request = RequestFactory().get('/')
student_request.user = self.student
@ -54,7 +54,7 @@ class ModuleSolutionVisibilityTest(TestCase):
"""
def test_hide_solutions_for_students_and_then_show_them(self):
self.assertEqual(self.student.selected_class(), self.teacher.selected_class())
self.assertEqual(self.student.selected_class, self.teacher.selected_class)
student_result = self.student_client.execute(self.query, variables={
'id': self.content_block_id

View File

@ -3,7 +3,7 @@ from users.models import User
def are_solutions_enabled_for(user: User, module: Module):
school_class = user.selected_class()
school_class = user.selected_class
return module.solutions_enabled_for.filter(pk=school_class.pk).exists()

View File

@ -370,7 +370,6 @@ type CreateTeamPayload {
}
type CustomMutation {
redeemCoupon(input: CouponInput!): CouponPayload
spellCheck(input: SpellCheckInput!): SpellCheckPayload
addNote(input: AddNoteInput!): AddNotePayload
updateNote(input: UpdateNoteInput!): UpdateNotePayload
@ -920,6 +919,7 @@ type SchoolClassNode implements Node {
rooms(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, appearance: String): RoomNodeConnection!
pk: Int
members: [ClassMemberNode]
readOnly: Boolean
}
type SchoolClassNodeConnection {

View File

@ -7,9 +7,11 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser, Permission
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils.functional import cached_property
from django.utils.timezone import make_aware, is_aware
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from typing import Union
from users.licenses import MYSKILLBOX_LICENSES
from users.managers import RoleManager, UserRoleManager, UserManager, LicenseManager
@ -75,7 +77,8 @@ class User(AbstractUser):
def is_teacher(self):
return self.user_roles.filter(role__key='teacher').exists()
def selected_class(self):
@cached_property
def selected_class(self) -> Union['SchoolClass', None]:
try:
settings = UserSetting.objects.get(user=self)
return settings.selected_class

View File

@ -4,7 +4,7 @@ import graphene
from django.db.models import Q
from django.utils.dateformat import format
from django_filters import FilterSet, OrderingFilter
from graphene import relay, ObjectType
from graphene import ObjectType, relay
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from graphql_relay import to_global_id
@ -13,13 +13,14 @@ from basicknowledge.models import BasicKnowledge
from basicknowledge.queries import InstrumentNode
from books.models import Module
from books.schema.queries import ModuleNode
from users.models import User, SchoolClass, SchoolClassMember, Team
from users.models import SchoolClass, SchoolClassMember, Team, User
class SchoolClassNode(DjangoObjectType):
pk = graphene.Int()
members = graphene.List('users.schema.ClassMemberNode')
code = graphene.String()
read_only = graphene.Boolean()
class Meta:
model = SchoolClass
@ -32,13 +33,19 @@ class SchoolClassNode(DjangoObjectType):
def resolve_members(self, info, **kwargs):
return SchoolClassMember.objects.filter(school_class=self)
def resolve_code(self, info, **kwargs):
def resolve_code(self: SchoolClass, info, **kwargs):
if not info.context.user.is_teacher():
return None
if self.code is None:
self.generate_code()
return self.code
@staticmethod
def resolve_read_only(root: SchoolClass, info, **kwargs):
user = info.context.user
member = SchoolClassMember.objects.get(user=user, school_class=root)
return not member.active
class TeamNode(DjangoObjectType):
class Meta:
@ -107,25 +114,28 @@ class PrivateUserNode(DjangoObjectType):
def resolve_permissions(self, info):
return self.get_all_permissions()
def resolve_selected_class(self, info):
return self.selected_class()
@staticmethod
def resolve_selected_class(root: User, info):
return root.selected_class
def resolve_expiry_date(self, info):
if not self.hep_id: # concerns users that already have an (old) account
@staticmethod
def resolve_expiry_date(root: User, info):
if not root.hep_id: # concerns users that already have an (old) account
return format(datetime(2020, 7, 31), 'U') # just set some expiry date
else:
return self.license_expiry_date
return root.license_expiry_date
def resolve_is_teacher(self, info):
return self.is_teacher()
def resolve_is_teacher(root: User, info):
return root.is_teacher()
def resolve_school_classes(self, info):
if self.selected_class() is None: # then we don't have any class to return
@staticmethod
def resolve_school_classes(root: User, info):
if root.selected_class is None: # then we don't have any class to return
return SchoolClass.objects.none()
return SchoolClass.objects.filter(
Q(schoolclassmember__active=True, schoolclassmember__user=self) | Q(pk=self.selected_class().pk)).distinct()
Q(schoolclassmember__active=True, schoolclassmember__user=root) | Q(pk=root.selected_class.pk)).distinct()
def resolve_old_classes(self, info):
def resolve_old_classes(self: User, info):
return SchoolClass.objects.filter(schoolclassmember__active=False, schoolclassmember__user=self)
def resolve_recent_modules(self, info, **kwargs):

View File

@ -41,7 +41,7 @@ class JoinSchoolClassTest(TestCase):
self.assertEqual(result['success'], True)
self.assertEqual(result['schoolClass']['name'], 'Klasse 2B')
self.assertEqual(self.user.school_classes.count(), 2)
self.assertEqual(self.user.selected_class().code, 'YYYY')
self.assertEqual(self.user.selected_class.code, 'YYYY')
def test_class_already_joined(self):
code = 'YYYY'

View File

@ -82,3 +82,5 @@ class MySchoolClasses(TestCase):
self.assertEqual(len(old_classes), 1)

View File

@ -6,6 +6,7 @@ from graphql_relay import to_global_id
from api.schema import schema
from api.utils import get_graphql_mutation, get_object
from core.factories import UserFactory, TeacherFactory
from core.tests.base_test import SkillboxTestCase
from users.models import SchoolClass, User
from users.services import create_users
@ -38,18 +39,14 @@ class SchoolClassesTest(TestCase):
self.assertEqual(f'{self.prefix} {user.pk}', class_name)
class ModifySchoolClassTest(TestCase):
class ModifySchoolClassTest(SkillboxTestCase):
def setUp(self):
create_users()
self.teacher = User.objects.get(username='teacher')
self.student = User.objects.get(username='student1')
request = RequestFactory().get('/')
request.user = self.teacher
student_request = RequestFactory().get('/')
student_request.user = self.student
self.client = Client(schema=schema, context_value=request)
self.student_client = Client(schema=schema, context_value=student_request)
self.client = self.get_client(user=self.teacher)
self.student_client = self.get_client(user=self.student)
def test_update_school_class(self):
class_name = 'The Colbert Show'
@ -115,7 +112,7 @@ class ModifySchoolClassTest(TestCase):
school_class = get_object(SchoolClass, id)
self.assertEqual(school_class.name, class_name)
self.assertEqual(school_class.get_teacher(), self.teacher)
self.assertEqual(self.teacher.selected_class().name, class_name)
self.assertEqual(self.teacher.selected_class.name, class_name)
def test_create_school_class_fail(self):
self.assertEqual(SchoolClass.objects.count(), 2)

View File

@ -1,29 +1,18 @@
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase, RequestFactory
from graphene.test import Client
from graphql_relay import to_global_id
from api.schema import schema
from core.factories import UserFactory
from core.tests.base_test import SkillboxTestCase
from users.factories import SchoolClassFactory
from users.models import UserSetting
from users.models import SchoolClassMember, UserSetting
class UserSettingTests(TestCase):
class UserSettingTests(SkillboxTestCase):
def setUp(self):
self.user = UserFactory(username='aschi')
self.class1 = SchoolClassFactory(users=[self.user])
self.class2 = SchoolClassFactory(users=[self.user])
self.class3 = SchoolClassFactory(users=[])
request = RequestFactory().get('/')
request.user = self.user
# adding session
middleware = SessionMiddleware()
middleware.process_request(request)
request.session.save()
self.client = Client(schema=schema, context_value=request)
def make_mutation(self, class_id):
mutation = '''
@ -37,7 +26,7 @@ class UserSettingTests(TestCase):
}
'''
return self.client.execute(mutation, variables={
return self.get_client(user=self.user).execute(mutation, variables={
'input': {
'id': to_global_id('SchoolClassNode', class_id)
}
@ -50,18 +39,20 @@ class UserSettingTests(TestCase):
selectedClass {
name
id
readOnly
}
}
}
'''
return self.client.execute(query)
return self.get_client(user=self.user).execute(query)
def test_selects_first_class_on_first_call(self):
result = self.make_query()
first_class = self.user.school_classes.first()
self.assertIsNone(result.get('errors'))
self.assertEqual(result.get('data').get('me').get('selectedClass').get('name'), first_class.name)
self.assertFalse(result.get('data').get('me').get('selectedClass').get('readOnly'))
def test_returns_selected_class(self):
selected_class = self.user.school_classes.all()[1]
@ -73,7 +64,6 @@ class UserSettingTests(TestCase):
selected_class.name)
def test_user_can_select_class(self):
selected_class = self.user.school_classes.first()
setting = UserSetting.objects.create(user=self.user, selected_class=selected_class)
setting.save()
@ -87,7 +77,6 @@ class UserSettingTests(TestCase):
selected_class.name)
def test_user_can_select_class_even_no_settings_exist(self):
selected_class = self.user.school_classes.all()[1]
mutation_result = self.make_mutation(selected_class.pk)
self.assertIsNone(mutation_result.get('errors'))
@ -106,4 +95,17 @@ class UserSettingTests(TestCase):
mutation_result = self.make_mutation(self.class3.pk)
self.assertIsNotNone(mutation_result.get('errors'))
def test_inactive_class_as_selected_class(self):
selected_class = self.class2
membership = SchoolClassMember.objects.get(user=self.user, school_class=selected_class)
self.assertTrue(membership.active)
membership.active = False
membership.save()
setting = UserSetting.objects.create(user=self.user, selected_class=selected_class)
setting.save()
result = self.make_query()
self.assertIsNone(result.get('errors'))
self.assertTrue(result.get('data').get('me').get('selectedClass').get('readOnly'))