def test_create_ccx(self): """ Create CCX. Follow redirect to coach dashboard, confirm we see the coach dashboard for the new CCX. """ self.make_coach() url = reverse('create_ccx', kwargs={'course_id': unicode(self.course.id)}) response = self.client.post(url, {'name': 'New CCX'}) self.assertEqual(response.status_code, 302) url = response.get('location') # pylint: disable=no-member response = self.client.get(url) self.assertEqual(response.status_code, 200) # Get the ccx_key path = urlparse.urlparse(url).path resolver = resolve(path) ccx_key = resolver.kwargs['course_id'] course_key = CourseKey.from_string(ccx_key) self.assertTrue(CourseEnrollment.is_enrolled(self.coach, course_key)) self.assertTrue(re.search('id="ccx-schedule"', response.content)) # check if the max amount of student that can be enrolled has been overridden ccx = CustomCourseForEdX.objects.get() course_enrollments = get_override_for_ccx( ccx, self.course, 'max_student_enrollments_allowed') self.assertEqual(course_enrollments, settings.CCX_MAX_STUDENTS_ALLOWED) # assert ccx creator has role=ccx_coach role = CourseCcxCoachRole(course_key) self.assertTrue(role.has_user(self.coach))
def test_create_ccx(self, ccx_name='New CCX'): """ Create CCX. Follow redirect to coach dashboard, confirm we see the coach dashboard for the new CCX. """ self.make_coach() url = reverse( 'create_ccx', kwargs={'course_id': unicode(self.course.id)}) response = self.client.post(url, {'name': ccx_name}) self.assertEqual(response.status_code, 302) url = response.get('location') # pylint: disable=no-member response = self.client.get(url) self.assertEqual(response.status_code, 200) # Get the ccx_key path = urlparse.urlparse(url).path resolver = resolve(path) ccx_key = resolver.kwargs['course_id'] course_key = CourseKey.from_string(ccx_key) self.assertTrue(CourseEnrollment.is_enrolled(self.coach, course_key)) self.assertTrue(re.search('id="ccx-schedule"', response.content)) # check if the max amount of student that can be enrolled has been overridden ccx = CustomCourseForEdX.objects.get() course_enrollments = get_override_for_ccx(ccx, self.course, 'max_student_enrollments_allowed') self.assertEqual(course_enrollments, settings.CCX_MAX_STUDENTS_ALLOWED) # assert ccx creator has role=ccx_coach role = CourseCcxCoachRole(course_key) self.assertTrue(role.has_user(self.coach))
def has_ccx_coach_role(user, course_key): """ Check if user is a coach on this ccx. Arguments: user (User): the user whose descriptor access we are checking. course_key (CCXLocator): Key to CCX. Returns: bool: whether user is a coach on this ccx or not. """ if hasattr(course_key, 'ccx'): ccx_id = course_key.ccx role = CourseCcxCoachRole(course_key) if role.has_user(user): list_ccx = CustomCourseForEdX.objects.filter( course_id=course_key.to_course_locator(), coach=user ) if list_ccx.exists(): coach_ccx = list_ccx[0] return str(coach_ccx.id) == ccx_id else: raise CCXLocatorValidationException("Invalid CCX key. To verify that " "user is a coach on CCX, you must provide key to CCX") return False
def test_post_list(self): """ Test the creation of a CCX """ outbox = self.get_outbox() data = { 'master_course_id': self.master_course_key_str, 'max_students_allowed': 111, 'display_name': 'CCX Test Title', 'coach_email': self.coach.email } resp = self.client.post(self.list_url, data, format='json', HTTP_AUTHORIZATION=self.auth) self.assertEqual(resp.status_code, status.HTTP_201_CREATED) # check if the response has at least the same data of the request for key, val in data.iteritems(): self.assertEqual(resp.data.get(key), val) # pylint: disable=no-member self.assertIn('ccx_course_id', resp.data) # pylint: disable=no-member # check that the new CCX actually exists course_key = CourseKey.from_string(resp.data.get('ccx_course_id')) # pylint: disable=no-member ccx_course = CustomCourseForEdX.objects.get(pk=course_key.ccx) self.assertEqual( unicode(CCXLocator.from_course_locator(ccx_course.course.id, ccx_course.id)), resp.data.get('ccx_course_id') # pylint: disable=no-member ) # check that the coach user has coach role on the master course coach_role_on_master_course = CourseCcxCoachRole(self.master_course_key) self.assertTrue(coach_role_on_master_course.has_user(self.coach)) # check that the coach has been enrolled in the ccx ccx_course_object = courses.get_course_by_id(course_key) self.assertTrue( CourseEnrollment.objects.filter(course_id=ccx_course_object.id, user=self.coach).exists() ) # check that an email has been sent to the coach self.assertEqual(len(outbox), 1) self.assertIn(self.coach.email, outbox[0].recipients()) # pylint: disable=no-member
def test_patch_detail(self): """ Test for successful patch """ outbox = self.get_outbox() # create a new coach new_coach = AdminFactory.create() data = { 'max_students_allowed': 111, 'display_name': 'CCX Title', 'coach_email': new_coach.email } resp = self.client.patch(self.detail_url, data, format='json', HTTP_AUTHORIZATION=self.auth) self.assertEqual(resp.status_code, status.HTTP_204_NO_CONTENT) ccx_from_db = CustomCourseForEdX.objects.get(id=self.ccx.id) self.assertEqual(ccx_from_db.max_student_enrollments_allowed, data['max_students_allowed']) self.assertEqual(ccx_from_db.display_name, data['display_name']) self.assertEqual(ccx_from_db.coach.email, data['coach_email']) # check that the coach user has coach role on the master course coach_role_on_master_course = CourseCcxCoachRole(self.master_course_key) self.assertTrue(coach_role_on_master_course.has_user(new_coach)) # check that the coach has been enrolled in the ccx ccx_course_object = courses.get_course_by_id(self.ccx_key) self.assertTrue( CourseEnrollment.objects.filter(course_id=ccx_course_object.id, user=new_coach).exists() ) # check that an email has been sent to the coach self.assertEqual(len(outbox), 1) self.assertIn(new_coach.email, outbox[0].recipients()) # pylint: disable=no-member
def wrapper(request, course_id): """ Wraps the view function, performing access check, loading the course, and modifying the view's call signature. """ course_key = CourseKey.from_string(course_id) ccx = None if isinstance(course_key, CCXLocator): ccx_id = course_key.ccx ccx = CustomCourseForEdX.objects.get(pk=ccx_id) course_key = ccx.course_id role = CourseCcxCoachRole(course_key) if not role.has_user(request.user): return HttpResponseForbidden( _('You must be a CCX Coach to access this view.')) course = get_course_by_id(course_key, depth=None) # if there is a ccx, we must validate that it is the ccx for this coach if ccx is not None: coach_ccx = get_ccx_for_coach(course, request.user) if coach_ccx is None or coach_ccx.id != ccx.id: return HttpResponseForbidden( _('You must be the coach for this ccx to access this view') ) return view(request, course, ccx)
def has_ccx_coach_role(user, course_key): """ Check if user is a coach on this ccx. Arguments: user (User): the user whose descriptor access we are checking. course_key (CCXLocator): Key to CCX. Returns: bool: whether user is a coach on this ccx or not. """ if hasattr(course_key, 'ccx'): ccx_id = course_key.ccx role = CourseCcxCoachRole(course_key) if role.has_user(user): list_ccx = CustomCourseForEdX.objects.filter( course_id=course_key.to_course_locator(), coach=user) if list_ccx.exists(): coach_ccx = list_ccx[0] return str(coach_ccx.id) == ccx_id else: raise CCXLocatorValidationException( "Invalid CCX key. To verify that " "user is a coach on CCX, you must provide key to CCX") return False
def wrapper(request, course_id): """ Wraps the view function, performing access check, loading the course, and modifying the view's call signature. """ course_key = CourseKey.from_string(course_id) ccx = None if isinstance(course_key, CCXLocator): ccx_id = course_key.ccx ccx = CustomCourseForEdX.objects.get(pk=ccx_id) course_key = ccx.course_id role = CourseCcxCoachRole(course_key) if not role.has_user(request.user): return HttpResponseForbidden( _('You must be a CCX Coach to access this view.')) course = get_course_by_id(course_key, depth=None) # if there is a ccx, we must validate that it is the ccx for this coach if ccx is not None: coach_ccx = get_ccx_for_coach(course, request.user) if coach_ccx is None or coach_ccx.id != ccx.id: return HttpResponseForbidden( _('You must be the coach for this ccx to access this view') ) return view(request, course, ccx)
def assign_coach_role_to_ccx(ccx_locator, user, master_course_id): """ Check if user has ccx_coach role on master course then assign him coach role on ccx only if role is not already assigned. Because of this coach can open dashboard from master course as well as ccx. :param ccx_locator: CCX key :param user: User to whom we want to assign role. :param master_course_id: Master course key """ coach_role_on_master_course = CourseCcxCoachRole(master_course_id) # check if user has coach role on master course if coach_role_on_master_course.has_user(user): # Check if user has coach role on ccx. role = CourseCcxCoachRole(ccx_locator) if not role.has_user(user): # assign user role coach on ccx with ccx_course(ccx_locator) as course: allow_access(course, user, "ccx_coach", send_email=False)
def assign_coach_role_to_ccx(ccx_locator, user, master_course_id): """ Check if user has ccx_coach role on master course then assign him coach role on ccx only if role is not already assigned. Because of this coach can open dashboard from master course as well as ccx. :param ccx_locator: CCX key :param user: User to whom we want to assign role. :param master_course_id: Master course key """ coach_role_on_master_course = CourseCcxCoachRole(master_course_id) # check if user has coach role on master course if coach_role_on_master_course.has_user(user): # Check if user has coach role on ccx. role = CourseCcxCoachRole(ccx_locator) if not role.has_user(user): # assign user role coach on ccx with ccx_course(ccx_locator) as course: allow_access(course, user, "ccx_coach", send_email=False)
def is_enabled(cls, course, user=None): """ Returns true if CCX has been enabled and the specified user is a coach """ if not user: return True if not settings.FEATURES.get('CUSTOM_COURSES_EDX', False) or not course.enable_ccx: return False role = CourseCcxCoachRole(course.id) return role.has_user(user)
def wrapper(request, course_id): """ Wraps the view function, performing access check, loading the course, and modifying the view's call signature. """ course_key = CourseKey.from_string(course_id) role = CourseCcxCoachRole(course_key) if not role.has_user(request.user): return HttpResponseForbidden(_("You must be a CCX Coach to access this view.")) course = get_course_by_id(course_key, depth=None) return view(request, course)
def is_enabled(cls, course, user=None): """ Returns true if CCX has been enabled and the specified user is a coach """ if not user: return True if not settings.FEATURES.get('CUSTOM_COURSES_EDX', False) or not course.enable_ccx: return False role = CourseCcxCoachRole(course.id) return role.has_user(user)
def wrapper(request, course_id): """ Wraps the view function, performing access check, loading the course, and modifying the view's call signature. """ course_key = CourseKey.from_string(course_id) role = CourseCcxCoachRole(course_key) if not role.has_user(request.user): return HttpResponseForbidden( _('You must be a CCX Coach to access this view.')) course = get_course_by_id(course_key, depth=None) return view(request, course)
def is_enabled(cls, course, user=None): """ Returns true if CCX has been enabled and the specified user is a coach """ if not settings.FEATURES.get('CUSTOM_COURSES_EDX', False) or not course.enable_ccx: # If ccx is not enable do not show ccx coach tab. return False if has_access(user, 'staff', course) or has_access(user, 'instructor', course): # if user is staff or instructor then he can always see ccx coach tab. return True # check if user has coach access. role = CourseCcxCoachRole(course.id) return role.has_user(user)
def is_enabled(cls, course, user=None): """ Returns true if CCX has been enabled and the specified user is a coach """ if not settings.FEATURES.get('CUSTOM_COURSES_EDX', False) or not course.enable_ccx: # If ccx is not enable do not show ccx coach tab. return False if hasattr(course.id, 'ccx') and bool(user.has_perm(VIEW_CCX_COACH_DASHBOARD, course)): return True # check if user has coach access. role = CourseCcxCoachRole(course.id) return role.has_user(user)
def is_enabled(cls, course, user=None): """ Returns true if CCX has been enabled and the specified user is a coach """ if not settings.FEATURES.get('CUSTOM_COURSES_EDX', False) or not course.enable_ccx: # If ccx is not enable do not show ccx coach tab. return False if has_access(user, 'staff', course) or has_access( user, 'instructor', course): # if user is staff or instructor then he can always see ccx coach tab. return True # check if user has coach access. role = CourseCcxCoachRole(course.id) return role.has_user(user)
def is_enabled(cls, course, user=None): """ Returns true if CCX has been enabled and the specified user is a coach """ if not settings.FEATURES.get('CUSTOM_COURSES_EDX', False): # If ccx is not enable do not show ccx coach tab. return False if not hasattr(course.id, 'ccx'): # If is main Facilitator Guide course return False if not AffiliateMembership.objects.filter(member=user).exists(): return False if has_access(user, 'staff', course) or has_access(user, 'instructor', course): # if user is staff or instructor then he can always see ccx coach tab. return True # check if user has coach access. role = CourseCcxCoachRole(course.id) return role.has_user(user)
def test_post_list(self): """ Test the creation of a CCX """ outbox = self.get_outbox() data = { 'master_course_id': self.master_course_key_str, 'max_students_allowed': 111, 'display_name': 'CCX Test Title', 'coach_email': self.coach.email } resp = self.client.post(self.list_url, data, format='json', HTTP_AUTHORIZATION=self.auth) self.assertEqual(resp.status_code, status.HTTP_201_CREATED) # check if the response has at least the same data of the request for key, val in data.iteritems(): self.assertEqual(resp.data.get(key), val) # pylint: disable=no-member self.assertIn('ccx_course_id', resp.data) # pylint: disable=no-member # check that the new CCX actually exists course_key = CourseKey.from_string(resp.data.get('ccx_course_id')) # pylint: disable=no-member ccx_course = CustomCourseForEdX.objects.get(pk=course_key.ccx) self.assertEqual( unicode( CCXLocator.from_course_locator(ccx_course.course.id, ccx_course.id)), resp.data.get('ccx_course_id') # pylint: disable=no-member ) # check that the coach user has coach role on the master course coach_role_on_master_course = CourseCcxCoachRole( self.master_course_key) self.assertTrue(coach_role_on_master_course.has_user(self.coach)) # check that the coach has been enrolled in the ccx ccx_course_object = courses.get_course_by_id(course_key) self.assertTrue( CourseEnrollment.objects.filter(course_id=ccx_course_object.id, user=self.coach).exists()) # check that an email has been sent to the coach self.assertEqual(len(outbox), 1) self.assertIn(self.coach.email, outbox[0].recipients()) # pylint: disable=no-member
def wrapper(request, course_id): """ Wraps the view function, performing access check, loading the course, and modifying the view's call signature. """ course_key = CourseKey.from_string(course_id) ccx = None if isinstance(course_key, CCXLocator): ccx_id = course_key.ccx try: ccx = CustomCourseForEdX.objects.get(pk=ccx_id) except CustomCourseForEdX.DoesNotExist: raise Http404 if ccx: course_key = ccx.course_id course = get_course_by_id(course_key, depth=None) if not course.enable_ccx: raise Http404 else: is_staff = has_access(request.user, 'staff', course) is_instructor = has_access(request.user, 'instructor', course) if is_staff or is_instructor: # if user is staff or instructor then he can view ccx coach dashboard. return view(request, course, ccx) else: # if there is a ccx, we must validate that it is the ccx for this coach role = CourseCcxCoachRole(course_key) if not role.has_user(request.user): return HttpResponseForbidden( _('You must be a CCX Coach to access this view.')) elif ccx is not None: coach_ccx = get_ccx_by_ccx_id(course, request.user, ccx.id) if coach_ccx is None: return HttpResponseForbidden( _('You must be the coach for this ccx to access this view' )) return view(request, course, ccx)
def wrapper(request, course_id): """ Wraps the view function, performing access check, loading the course, and modifying the view's call signature. """ course_key = CourseKey.from_string(course_id) ccx = None if isinstance(course_key, CCXLocator): ccx_id = course_key.ccx try: ccx = CustomCourseForEdX.objects.get(pk=ccx_id) except CustomCourseForEdX.DoesNotExist: raise Http404 if ccx: course_key = ccx.course_id course = get_course_by_id(course_key, depth=None) if not course.enable_ccx: raise Http404 else: is_staff = has_access(request.user, 'staff', course) is_instructor = has_access(request.user, 'instructor', course) if is_staff or is_instructor: # if user is staff or instructor then he can view ccx coach dashboard. return view(request, course, ccx) else: # if there is a ccx, we must validate that it is the ccx for this coach role = CourseCcxCoachRole(course_key) if not role.has_user(request.user): return HttpResponseForbidden( _('You must be a CCX Coach to access this view.')) elif ccx is not None: coach_ccx = get_ccx_by_ccx_id(course, request.user, ccx.id) if coach_ccx is None: return HttpResponseForbidden( _('You must be the coach for this ccx to access this view' )) return view(request, course, ccx)
def has_ccx_coach_role(user, course_key): """ Check if user is a coach on this ccx. Arguments: user (User): the user whose descriptor access we are checking. course_key (CCXLocator): Key to CCX. Returns: bool: whether user is a coach on this ccx or not. """ if hasattr(course_key, 'ccx'): ccx_id = course_key.ccx role = CourseCcxCoachRole(course_key) return role.has_user(user) else: raise CCXLocatorValidationException( "Invalid CCX key. To verify that " "user is a coach on CCX, you must provide key to CCX") return False
def can_display(self, course, settings, *args, **kw): """ Since we don't get the user here, we use a thread local defined in the ccx overrides to get it, then use the course to get the coach role and find out if the user is one. """ user_is_coach = False if settings.FEATURES.get('CUSTOM_COURSES_EDX', False): from opaque_keys.edx.locations import SlashSeparatedCourseKey from student.roles import CourseCcxCoachRole # pylint: disable=import-error from ccx.overrides import get_current_request # pylint: disable=import-error course_id = course.id.to_deprecated_string() course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) role = CourseCcxCoachRole(course_key) request = get_current_request() if request is not None: user_is_coach = role.has_user(request.user) super_can_display = super(CcxCoachTab, self).can_display( course, settings, *args, **kw ) return user_is_coach and super_can_display
def can_display(self, course, settings, *args, **kw): """ Since we don't get the user here, we use a thread local defined in the ccx overrides to get it, then use the course to get the coach role and find out if the user is one. """ user_is_coach = False if settings.FEATURES.get('CUSTOM_COURSES_EDX', False): from opaque_keys.edx.locations import SlashSeparatedCourseKey from student.roles import CourseCcxCoachRole # pylint: disable=import-error from ccx.overrides import get_current_request # pylint: disable=import-error course_id = course.id.to_deprecated_string() course_key = SlashSeparatedCourseKey.from_deprecated_string( course_id) role = CourseCcxCoachRole(course_key) request = get_current_request() if request is not None: user_is_coach = role.has_user(request.user) super_can_display = super(CcxCoachTab, self).can_display(course, settings, *args, **kw) return user_is_coach and super_can_display