Exemple #1
0
    def test_remove_master_course_staff_from_ccx_idempotent(self):
        """
        Test remove staff of master course from ccx course
        """
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))

        outbox = self.get_outbox()
        self.assertEqual(len(outbox), 0)
        add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)

        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(self.course, 'instructor')

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
            self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
            self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)

            # assert that role of staff and instructors of master course removed from ccx.
            remove_master_course_staff_from_ccx(
                self.course, self.ccx_locator, self.ccx.display_name, send_email=True
            )
            self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))

            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))

            for user in list_staff_master_course:
                self.assertNotIn(user, list_staff_ccx_course)
            for user in list_instructor_master_course:
                self.assertNotIn(user, list_instructor_ccx_course)

        # Run again
        remove_master_course_staff_from_ccx(self.course, self.ccx_locator, self.ccx.display_name)
        self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))

            for user in list_staff_master_course:
                self.assertNotIn(user, list_staff_ccx_course)
            for user in list_instructor_master_course:
                self.assertNotIn(user, list_instructor_ccx_course)
Exemple #2
0
    def test_remove_master_course_staff_from_ccx_idempotent(self):
        """
        Test remove staff of master course from ccx course
        """
        staff = self.make_staff()
        assert CourseStaffRole(self.course.id).has_user(staff)

        # adding instructor to master course.
        instructor = self.make_instructor()
        assert CourseInstructorRole(self.course.id).has_user(instructor)

        outbox = self.get_outbox()
        assert len(outbox) == 0
        add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)

        list_staff_master_course = list_with_level(self.course.id, 'staff')
        list_instructor_master_course = list_with_level(self.course.id, 'instructor')

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx.id, 'staff')
            assert len(list_staff_master_course) == len(list_staff_ccx_course)
            assert list_staff_master_course[0].email == list_staff_ccx_course[0].email

            list_instructor_ccx_course = list_with_level(course_ccx.id, 'instructor')
            assert len(list_instructor_ccx_course) == len(list_instructor_master_course)
            assert list_instructor_ccx_course[0].email == list_instructor_master_course[0].email

            # assert that role of staff and instructors of master course removed from ccx.
            remove_master_course_staff_from_ccx(
                self.course, self.ccx_locator, self.ccx.display_name, send_email=True
            )
            assert len(outbox) == (len(list_staff_master_course) + len(list_instructor_master_course))

            list_staff_ccx_course = list_with_level(course_ccx.id, 'staff')
            assert len(list_staff_master_course) != len(list_staff_ccx_course)

            list_instructor_ccx_course = list_with_level(course_ccx.id, 'instructor')
            assert len(list_instructor_ccx_course) != len(list_instructor_master_course)

            for user in list_staff_master_course:
                assert user not in list_staff_ccx_course
            for user in list_instructor_master_course:
                assert user not in list_instructor_ccx_course

        # Run again
        remove_master_course_staff_from_ccx(self.course, self.ccx_locator, self.ccx.display_name)
        assert len(outbox) == (len(list_staff_master_course) + len(list_instructor_master_course))

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx.id, 'staff')
            assert len(list_staff_master_course) != len(list_staff_ccx_course)

            list_instructor_ccx_course = list_with_level(course_ccx.id, 'instructor')
            assert len(list_instructor_ccx_course) != len(list_instructor_master_course)

            for user in list_staff_master_course:
                assert user not in list_staff_ccx_course
            for user in list_instructor_master_course:
                assert user not in list_instructor_ccx_course
    def test_remove_master_course_staff_from_ccx_idempotent(self):
        """
        Test remove staff of master course from ccx course
        """
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))

        outbox = self.get_outbox()
        self.assertEqual(len(outbox), 0)
        add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)

        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(self.course, 'instructor')

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
            self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
            self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)

            # assert that role of staff and instructors of master course removed from ccx.
            remove_master_course_staff_from_ccx(
                self.course, self.ccx_locator, self.ccx.display_name, send_email=True
            )
            self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))

            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))

            for user in list_staff_master_course:
                self.assertNotIn(user, list_staff_ccx_course)
            for user in list_instructor_master_course:
                self.assertNotIn(user, list_instructor_ccx_course)

        # Run again
        remove_master_course_staff_from_ccx(self.course, self.ccx_locator, self.ccx.display_name)
        self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))

            for user in list_staff_master_course:
                self.assertNotIn(user, list_staff_ccx_course)
            for user in list_instructor_master_course:
                self.assertNotIn(user, list_instructor_ccx_course)
Exemple #4
0
    def test_add_master_course_staff_to_ccx_idempotent(self):
        """
        Test add staff of master course to ccx course multiple time will
        not result in multiple enrollments.
        """
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(
            CourseInstructorRole(self.course.id).has_user(instructor))
        outbox = self.get_outbox()
        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(
            self.course, 'instructor')
        self.assertEqual(len(outbox), 0)

        # run the assignment the first time
        add_master_course_staff_to_ccx(self.course, self.ccx_locator,
                                       self.ccx.display_name)
        self.assertEqual(
            len(outbox),
            len(list_staff_master_course) + len(list_instructor_master_course))
        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            list_instructor_ccx_course = list_with_level(
                course_ccx, 'instructor')
        self.assertEqual(len(list_staff_master_course),
                         len(list_staff_ccx_course))
        for user in list_staff_master_course:
            self.assertIn(user, list_staff_ccx_course)
        self.assertEqual(len(list_instructor_master_course),
                         len(list_instructor_ccx_course))
        for user in list_instructor_master_course:
            self.assertIn(user, list_instructor_ccx_course)

        # run the assignment again
        add_master_course_staff_to_ccx(self.course, self.ccx_locator,
                                       self.ccx.display_name)
        # there are no new duplicated email
        self.assertEqual(
            len(outbox),
            len(list_staff_master_course) + len(list_instructor_master_course))
        # there are no duplicated staffs
        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            list_instructor_ccx_course = list_with_level(
                course_ccx, 'instructor')
        self.assertEqual(len(list_staff_master_course),
                         len(list_staff_ccx_course))
        for user in list_staff_master_course:
            self.assertIn(user, list_staff_ccx_course)
        self.assertEqual(len(list_instructor_master_course),
                         len(list_instructor_ccx_course))
        for user in list_instructor_master_course:
            self.assertIn(user, list_instructor_ccx_course)
    def test_add_master_course_staff_to_ccx(self):
        """
        Test add staff of master course to ccx course
        """
        # adding staff to master course.
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))

        add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name)

        # assert that staff and instructors of master course has staff and instructor roles on ccx
        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(self.course, 'instructor')

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
            self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
            self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)
Exemple #6
0
    def test_add_master_course_staff_to_ccx(self):
        """
        Test add staff of master course to ccx course
        """
        # adding staff to master course.
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(
            CourseInstructorRole(self.course.id).has_user(instructor))

        add_master_course_staff_to_ccx(self.course, self.ccx_locator,
                                       self.ccx.display_name)

        # assert that staff and instructors of master course has staff and instructor roles on ccx
        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(
            self.course, 'instructor')

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertEqual(len(list_staff_master_course),
                             len(list_staff_ccx_course))
            self.assertEqual(list_staff_master_course[0].email,
                             list_staff_ccx_course[0].email)

            list_instructor_ccx_course = list_with_level(
                course_ccx, 'instructor')
            self.assertEqual(len(list_instructor_ccx_course),
                             len(list_instructor_master_course))
            self.assertEqual(list_instructor_ccx_course[0].email,
                             list_instructor_master_course[0].email)
def ccx_gradebook(request, course, ccx=None):
    """
    Show the gradebook for this CCX.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, unicode(ccx.id))
    with ccx_course(ccx_key) as course:
        prep_course_for_grading(course, request)
        student_info, page = get_grade_book_page(
            request, course, course_key=ccx_key)

        return render_to_response(
            'courseware/gradebook.html', {
                'page':
                page,
                'page_url':
                reverse('ccx_gradebook', kwargs={'course_id': ccx_key}),
                'students':
                student_info,
                'course':
                course,
                'course_id':
                course.id,
                'staff_access':
                request.user.is_staff,
                'ordered_grades':
                sorted(
                    course.grade_cutoffs.items(),
                    key=lambda i: i[1],
                    reverse=True),
            })
Exemple #8
0
def ccx_gradebook(request, course, ccx=None):
    """
    Show the gradebook for this CCX.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, unicode(ccx.id))
    with ccx_course(ccx_key) as course:
        prep_course_for_grading(course, request)
        student_info, page = get_grade_book_page(request,
                                                 course,
                                                 course_key=ccx_key)

        return render_to_response(
            'courseware/gradebook.html', {
                'page':
                page,
                'page_url':
                reverse('ccx_gradebook', kwargs={'course_id': ccx_key}),
                'students':
                student_info,
                'course':
                course,
                'course_id':
                course.id,
                'staff_access':
                request.user.is_staff,
                'ordered_grades':
                sorted(course.grade_cutoffs.items(),
                       key=lambda i: i[1],
                       reverse=True),
            })
def ccx_gradebook(request, course, ccx=None):
    """
    Show the gradebook for this CCX.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, str(ccx.id))
    with ccx_course(ccx_key) as course:  # lint-amnesty, pylint: disable=redefined-argument-from-local
        student_info, page = get_grade_book_page(request,
                                                 course,
                                                 course_key=ccx_key)

        return render_to_response(
            'courseware/gradebook.html', {
                'page':
                page,
                'page_url':
                reverse('ccx_gradebook', kwargs={'course_id': ccx_key}),
                'students':
                student_info,
                'course':
                course,
                'course_id':
                course.id,
                'staff_access':
                request.user.is_staff,
                'ordered_grades':
                sorted(list(course.grade_cutoffs.items()),
                       key=lambda i: i[1],
                       reverse=True),
            })
def change_existing_ccx_coaches_to_staff(apps, schema_editor):
    """
    Modify all coaches of CCX courses so that they have the staff role on the
    CCX course they coach, but retain the CCX Coach role on the parent course.

    Arguments:
        apps (Applications): Apps in edX platform.
        schema_editor (SchemaEditor): For editing database schema (unused)

    """
    CustomCourseForEdX = apps.get_model('ccx', 'CustomCourseForEdX')
    db_alias = schema_editor.connection.alias
    if not db_alias == 'default':
        # This migration is not intended to run against the student_module_history database and
        # will fail if it does. Ensure that it'll only run against the default database.
        return
    list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
    for ccx in list_ccx:
        ccx_locator = CCXLocator.from_course_locator(ccx.course_id,
                                                     unicode(ccx.id))
        with ccx_course(ccx_locator) as course:
            coach = User.objects.get(id=ccx.coach.id)
            allow_access(course, coach, 'staff', send_email=False)
            revoke_access(course, coach, 'ccx_coach', send_email=False)
            log.info(
                'The CCX coach of CCX %s has been switched from "CCX Coach" to "Staff".',
                unicode(ccx_locator))
def revert_ccx_staff_to_coaches(apps, schema_editor):
    """
    Modify all staff on CCX courses so that they no longer have the staff role
    on the course that they coach.

    Arguments:
        apps (Applications): Apps in edX platform.
        schema_editor (SchemaEditor): For editing database schema (unused)

    """
    CustomCourseForEdX = apps.get_model('ccx', 'CustomCourseForEdX')
    db_alias = schema_editor.connection.alias
    if not db_alias == 'default':
        return
    list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
    for ccx in list_ccx:
        ccx_locator = CCXLocator.from_course_locator(ccx.course_id,
                                                     unicode(ccx.id))
        with ccx_course(ccx_locator) as course:
            coach = User.objects.get(id=ccx.coach.id)
            allow_access(course, coach, 'ccx_coach', send_email=False)
            revoke_access(course, coach, 'staff', send_email=False)
            log.info(
                'The CCX coach of CCX %s has been switched from "Staff" to "CCX Coach".',
                unicode(ccx_locator))
    def test_add_master_course_staff_to_ccx_idempotent(self):
        """
        Test add staff of master course to ccx course multiple time will
        not result in multiple enrollments.
        """
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
        outbox = self.get_outbox()
        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(self.course, 'instructor')
        self.assertEqual(len(outbox), 0)

        # run the assignment the first time
        add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name)
        self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))
        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
        self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
        for user in list_staff_master_course:
            self.assertIn(user, list_staff_ccx_course)
        self.assertEqual(len(list_instructor_master_course), len(list_instructor_ccx_course))
        for user in list_instructor_master_course:
            self.assertIn(user, list_instructor_ccx_course)

        # run the assignment again
        add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name)
        # there are no new duplicated email
        self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))
        # there are no duplicated staffs
        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
        self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
        for user in list_staff_master_course:
            self.assertIn(user, list_staff_ccx_course)
        self.assertEqual(len(list_instructor_master_course), len(list_instructor_ccx_course))
        for user in list_instructor_master_course:
            self.assertIn(user, list_instructor_ccx_course)
def ccx_grades_csv(request, course, ccx=None):
    """
    Download grades as CSV.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, str(ccx.id))
    with ccx_course(ccx_key) as course:  # lint-amnesty, pylint: disable=redefined-argument-from-local

        enrolled_students = User.objects.filter(
            courseenrollment__course_id=ccx_key,
            courseenrollment__is_active=1).order_by('username').select_related(
                "profile")
        grades = CourseGradeFactory().iter(enrolled_students, course)

        header = None
        rows = []
        for student, course_grade, __ in grades:
            if course_grade:
                # We were able to successfully grade this student for this
                # course.
                if not header:
                    # Encode the header row in utf-8 encoding in case there are
                    # unicode characters
                    header = [
                        section['label'].encode('utf-8')
                        if six.PY2 else section['label'] for section in
                        course_grade.summary['section_breakdown']
                    ]
                    rows.append(["id", "email", "username", "grade"] + header)

                percents = {
                    section['label']: section.get('percent', 0.0)
                    for section in course_grade.summary['section_breakdown']
                    if 'label' in section
                }

                row_percents = [percents.get(label, 0.0) for label in header]
                rows.append([
                    student.id,
                    student.email.encode('utf-8'),
                    student.username.encode('utf-8'), course_grade.percent
                ] + row_percents)

        buf = StringIO()
        writer = csv.writer(buf)
        for row in rows:
            writer.writerow(row)

        response = HttpResponse(buf.getvalue(), content_type='text/csv')
        response['Content-Disposition'] = 'attachment'

        return response
def ccx_grades_csv(request, course, ccx=None):
    """
    Download grades as CSV.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, unicode(ccx.id))
    with ccx_course(ccx_key) as course:
        prep_course_for_grading(course, request)

        enrolled_students = User.objects.filter(
            courseenrollment__course_id=ccx_key,
            courseenrollment__is_active=1).order_by('username').select_related(
                "profile")
        grades = CourseGradeFactory().iter(course, enrolled_students)

        header = None
        rows = []
        for student, course_grade, __ in grades:
            if course_grade:
                # We were able to successfully grade this student for this
                # course.
                if not header:
                    # Encode the header row in utf-8 encoding in case there are
                    # unicode characters
                    header = [
                        section['label'].encode('utf-8') for section in
                        course_grade.summary[u'section_breakdown']
                    ]
                    rows.append(["id", "email", "username", "grade"] + header)

                percents = {
                    section['label']: section.get('percent', 0.0)
                    for section in course_grade.summary[u'section_breakdown']
                    if 'label' in section
                }

                row_percents = [percents.get(label, 0.0) for label in header]
                rows.append([
                    student.id, student.email, student.username, course_grade.
                    percent
                ] + row_percents)

        buf = StringIO()
        writer = csv.writer(buf)
        for row in rows:
            writer.writerow(row)

        response = HttpResponse(buf.getvalue(), content_type='text/csv')
        response['Content-Disposition'] = 'attachment'

        return response
Exemple #15
0
def ccx_grades_csv(request, course, ccx=None):
    """
    Download grades as CSV.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, ccx.id)
    with ccx_course(ccx_key) as course:
        prep_course_for_grading(course, request)

        enrolled_students = User.objects.filter(
            courseenrollment__course_id=ccx_key,
            courseenrollment__is_active=1).order_by('username').select_related(
                "profile")
        grades = iterate_grades_for(course, enrolled_students)

        header = None
        rows = []
        for student, gradeset, __ in grades:
            if gradeset:
                # We were able to successfully grade this student for this
                # course.
                if not header:
                    # Encode the header row in utf-8 encoding in case there are
                    # unicode characters
                    header = [
                        section['label'].encode('utf-8')
                        for section in gradeset[u'section_breakdown']
                    ]
                    rows.append(["id", "email", "username", "grade"] + header)

                percents = {
                    section['label']: section.get('percent', 0.0)
                    for section in gradeset[u'section_breakdown']
                    if 'label' in section
                }

                row_percents = [percents.get(label, 0.0) for label in header]
                rows.append([
                    student.id, student.email, student.username,
                    gradeset['percent']
                ] + row_percents)

        buf = StringIO()
        writer = csv.writer(buf)
        for row in rows:
            writer.writerow(row)

        response = HttpResponse(buf.getvalue(), content_type='text/csv')
        response['Content-Disposition'] = 'attachment'

        return response
    def test_ccx_tab_visibility_for_staff_ccx_course(self):
        """
        Staff can access coach dashboard on ccx course.
        """
        self.make_coach()
        ccx = self.make_ccx()
        ccx_key = CCXLocator.from_course_locator(self.course.id, unicode(ccx.id))
        staff = self.make_staff()

        with ccx_course(ccx_key) as course_ccx:
            allow_access(course_ccx, staff, 'staff')
            self.assertTrue(self.check_ccx_tab(course_ccx, staff))
    def test_ccx_tab_visibility_for_instructor_ccx_course(self):
        """
        Instructor can access coach dashboard on ccx course.
        """
        self.make_coach()
        ccx = self.make_ccx()
        ccx_key = CCXLocator.from_course_locator(self.course.id, unicode(ccx.id))
        instructor = self.make_instructor()

        with ccx_course(ccx_key) as course_ccx:
            allow_access(course_ccx, instructor, 'instructor')
            self.assertTrue(self.check_ccx_tab(course_ccx, instructor))
Exemple #18
0
    def test_ccx_tab_visibility_for_staff_ccx_course(self):
        """
        Staff can access coach dashboard on ccx course.
        """
        self.make_coach()
        ccx = self.make_ccx()
        ccx_key = CCXLocator.from_course_locator(self.course.id, str(ccx.id))
        staff = self.make_staff()

        with ccx_course(ccx_key) as course_ccx:
            allow_access(course_ccx, staff, 'staff')
            assert self.check_ccx_tab(course_ccx, staff)
Exemple #19
0
    def test_ccx_tab_visibility_for_instructor_ccx_course(self):
        """
        Instructor can access coach dashboard on ccx course.
        """
        self.make_coach()
        ccx = self.make_ccx()
        ccx_key = CCXLocator.from_course_locator(self.course.id, str(ccx.id))
        instructor = self.make_instructor()

        with ccx_course(ccx_key) as course_ccx:
            allow_access(course_ccx, instructor, 'instructor')
            assert self.check_ccx_tab(course_ccx, instructor)
Exemple #20
0
def dashboard(request, course, ccx=None):
    """
    Display the CCX Coach Dashboard.
    """
    # right now, we can only have one ccx per user and course
    # so, if no ccx is passed in, we can sefely redirect to that
    if ccx is None:
        ccx = get_ccx_for_coach(course, request.user)
        if ccx:
            url = reverse('ccx_coach_dashboard',
                          kwargs={
                              'course_id':
                              CCXLocator.from_course_locator(
                                  course.id, ccx.id)
                          })
            return redirect(url)

    context = {
        'course': course,
        'ccx': ccx,
    }
    context.update(get_ccx_creation_dict(course))

    if ccx:
        ccx_locator = CCXLocator.from_course_locator(course.id,
                                                     unicode(ccx.id))
        # At this point we are done with verification that current user is ccx coach.
        assign_coach_role_to_ccx(ccx_locator, request.user, course.id)

        schedule = get_ccx_schedule(course, ccx)
        grading_policy = get_override_for_ccx(ccx, course, 'grading_policy',
                                              course.grading_policy)
        context['schedule'] = json.dumps(schedule, indent=4)
        context['save_url'] = reverse('save_ccx',
                                      kwargs={'course_id': ccx_locator})
        context['ccx_members'] = CourseEnrollment.objects.filter(
            course_id=ccx_locator, is_active=True)
        context['gradebook_url'] = reverse('ccx_gradebook',
                                           kwargs={'course_id': ccx_locator})
        context['grades_csv_url'] = reverse('ccx_grades_csv',
                                            kwargs={'course_id': ccx_locator})
        context['grading_policy'] = json.dumps(grading_policy, indent=4)
        context['grading_policy_url'] = reverse(
            'ccx_set_grading_policy', kwargs={'course_id': ccx_locator})

        with ccx_course(ccx_locator) as course:
            context['course'] = course

    else:
        context['create_ccx_url'] = reverse('create_ccx',
                                            kwargs={'course_id': course.id})
    return render_to_response('ccx/coach_dashboard.html', context)
def dashboard(request, course, ccx=None):
    """
    Display the CCX Coach Dashboard.
    """
    # right now, we can only have one ccx per user and course
    # so, if no ccx is passed in, we can sefely redirect to that
    if ccx is None:
        ccx = get_ccx_for_coach(course, request.user)
        if ccx:
            url = reverse(
                'ccx_coach_dashboard',
                kwargs={
                    'course_id':
                    CCXLocator.from_course_locator(course.id, unicode(ccx.id))
                })
            return redirect(url)

    context = {
        'course': course,
        'ccx': ccx,
    }
    context.update(get_ccx_creation_dict(course))

    if ccx:
        ccx_locator = CCXLocator.from_course_locator(course.id,
                                                     unicode(ccx.id))
        # At this point we are done with verification that current user is ccx coach.
        assign_staff_role_to_ccx(ccx_locator, request.user, course.id)
        schedule = get_ccx_schedule(course, ccx)
        grading_policy = get_override_for_ccx(ccx, course, 'grading_policy',
                                              course.grading_policy)
        context['schedule'] = json.dumps(schedule, indent=4)
        context['save_url'] = reverse(
            'save_ccx', kwargs={'course_id': ccx_locator})
        context['ccx_members'] = CourseEnrollment.objects.filter(
            course_id=ccx_locator, is_active=True)
        context['gradebook_url'] = reverse(
            'ccx_gradebook', kwargs={'course_id': ccx_locator})
        context['grades_csv_url'] = reverse(
            'ccx_grades_csv', kwargs={'course_id': ccx_locator})
        context['grading_policy'] = json.dumps(grading_policy, indent=4)
        context['grading_policy_url'] = reverse(
            'ccx_set_grading_policy', kwargs={'course_id': ccx_locator})

        with ccx_course(ccx_locator) as course:
            context['course'] = course

    else:
        context['create_ccx_url'] = reverse(
            'create_ccx', kwargs={'course_id': course.id})
    return render_to_response('ccx/coach_dashboard.html', context)
Exemple #22
0
def edit_ccx_context(course, ccx, user, **kwargs):
    ccx_locator = CCXLocator.from_course_locator(course.id, unicode(ccx.pk))

    schedule = get_ccx_schedule(course, ccx)
    grading_policy = get_override_for_ccx(ccx, course, 'grading_policy',
                                          course.grading_policy)

    context = {}
    context['ccx_locator'] = ccx_locator
    context['modify_access_url'] = reverse('modify_access',
                                           kwargs={'course_id': ccx_locator})
    context['schedule'] = json.dumps(schedule, indent=4)
    context['save_url'] = reverse('save_ccx',
                                  kwargs={'course_id': ccx_locator})

    non_student_user_ids = CourseAccessRole.objects.filter(
        course_id=ccx_locator).values_list('user_id', flat=True)
    ccx_student_enrollments = CourseEnrollment.objects.filter(
        course_id=ccx_locator,
        is_active=True).exclude(user_id__in=non_student_user_ids)

    context['ccx_student_enrollments'] = ccx_student_enrollments
    context['gradebook_url'] = reverse('ccx_gradebook',
                                       kwargs={'course_id': ccx_locator})
    context['grades_csv_url'] = reverse('ccx_grades_csv',
                                        kwargs={'course_id': ccx_locator})
    context['grading_policy'] = json.dumps(grading_policy, indent=4)
    context['grading_policy_url'] = reverse('ccx_set_grading_policy',
                                            kwargs={'course_id': ccx_locator})
    context['STATE_CHOICES'] = STATE_CHOICES

    all_facilitators = get_facilitators(ccx.affiliate)
    added_facilitator_user_ids = CourseAccessRole.objects.filter(
        course_id=ccx_locator,
        role=AffiliateMembership.CCX_COACH).values_list('user_id', flat=True)
    context['added_facilitators'] = all_facilitators.filter(
        id__in=added_facilitator_user_ids)
    context['not_added_facilitators'] = all_facilitators.exclude(
        id__in=added_facilitator_user_ids)

    with ccx_course(ccx_locator) as course:
        context['course'] = course

    context['edit_ccx_url'] = reverse('edit_ccx',
                                      kwargs={'course_id': ccx_locator})
    context['edit_ccx_dasboard_url'] = reverse(
        'ccx_edit_course_view', kwargs={'course_id': ccx_locator})

    return context
Exemple #23
0
def ccx_grades_csv(request, course, ccx=None):
    """
    Download grades as CSV.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, ccx.id)
    with ccx_course(ccx_key) as course:
        prep_course_for_grading(course, request)

        enrolled_students = (
            User.objects.filter(courseenrollment__course_id=ccx_key, courseenrollment__is_active=1)
            .order_by("username")
            .select_related("profile")
        )
        grades = iterate_grades_for(course, enrolled_students)

        header = None
        rows = []
        for student, gradeset, __ in grades:
            if gradeset:
                # We were able to successfully grade this student for this
                # course.
                if not header:
                    # Encode the header row in utf-8 encoding in case there are
                    # unicode characters
                    header = [section["label"].encode("utf-8") for section in gradeset[u"section_breakdown"]]
                    rows.append(["id", "email", "username", "grade"] + header)

                percents = {
                    section["label"]: section.get("percent", 0.0)
                    for section in gradeset[u"section_breakdown"]
                    if "label" in section
                }

                row_percents = [percents.get(label, 0.0) for label in header]
                rows.append([student.id, student.email, student.username, gradeset["percent"]] + row_percents)

        buf = StringIO()
        writer = csv.writer(buf)
        for row in rows:
            writer.writerow(row)

        response = HttpResponse(buf.getvalue(), content_type="text/csv")
        response["Content-Disposition"] = "attachment"

        return response
Exemple #24
0
    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))

        # assert that staff and instructors of master course has staff and instructor roles on ccx
        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(self.course, 'instructor')

        with ccx_course(course_key) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
            self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
            self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)
Exemple #25
0
    def test_remove_master_course_staff_from_ccx(self):
        """
        Test remove staff of master course to ccx course
        """
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))

        add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)

        list_staff_master_course = list_with_level(self.course, "staff")
        list_instructor_master_course = list_with_level(self.course, "instructor")

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, "staff")
            self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
            self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)

            list_instructor_ccx_course = list_with_level(course_ccx, "instructor")
            self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
            self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)

            # assert that role of staff and instructors of master course removed from ccx.
            remove_master_course_staff_from_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)
            list_staff_ccx_course = list_with_level(course_ccx, "staff")
            self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))

            list_instructor_ccx_course = list_with_level(course_ccx, "instructor")
            self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))

            for user in list_staff_master_course:
                self.assertNotIn(user, list_staff_ccx_course)
            for user in list_instructor_master_course:
                self.assertNotIn(user, list_instructor_ccx_course)
Exemple #26
0
def ccx_gradebook(request, course, ccx=None):
    """
    Show the gradebook for this CCX.
    """
    if not ccx:
        raise Http404

    ccx_key = CCXLocator.from_course_locator(course.id, ccx.id)
    with ccx_course(ccx_key) as course:
        prep_course_for_grading(course, request)
        student_info, page = get_grade_book_page(request, course, course_key=ccx_key)

        return render_to_response(
            "courseware/gradebook.html",
            {
                "page": page,
                "page_url": reverse("ccx_gradebook", kwargs={"course_id": ccx_key}),
                "students": student_info,
                "course": course,
                "course_id": course.id,
                "staff_access": request.user.is_staff,
                "ordered_grades": sorted(course.grade_cutoffs.items(), key=lambda i: i[1], reverse=True),
            },
        )
Exemple #27
0
    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': str(self.course.id)})

        response = self.client.post(url, {'name': ccx_name})
        assert response.status_code == 302
        url = response.get('location')
        response = self.client.get(url)
        assert response.status_code == 200

        # Get the ccx_key
        path = six.moves.urllib.parse.urlparse(url).path
        resolver = resolve(path)
        ccx_key = resolver.kwargs['course_id']

        course_key = CourseKey.from_string(ccx_key)

        assert CourseEnrollment.is_enrolled(self.coach, course_key)
        assert re.search('id="ccx-schedule"', response.content.decode('utf-8'))

        # 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')
        assert course_enrollments == settings.CCX_MAX_STUDENTS_ALLOWED
        # check if the course display name is properly set
        course_display_name = get_override_for_ccx(ccx, self.course, 'display_name')
        assert course_display_name == ccx_name

        # check if the course display name is properly set in modulestore
        course_display_name = self.mstore.get_course(ccx.locator).display_name
        assert course_display_name == ccx_name

        # assert ccx creator has role=staff
        role = CourseStaffRole(course_key)
        assert role.has_user(self.coach)

        # assert that staff and instructors of master course has staff and instructor roles on ccx
        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(self.course, 'instructor')

        # assert that forum roles are seeded
        assert are_permissions_roles_seeded(course_key)
        assert has_forum_access(self.coach.username, course_key, FORUM_ROLE_ADMINISTRATOR)

        with ccx_course(course_key) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            # The "Coach" in the parent course becomes "Staff" on the CCX, so the CCX should have 1 "Staff"
            # user more than the parent course
            assert (len(list_staff_master_course) + 1) == len(list_staff_ccx_course)
            assert list_staff_master_course[0].email in [ccx_staff.email for ccx_staff in list_staff_ccx_course]
            # Make sure the "Coach" on the parent course is "Staff" on the CCX
            assert self.coach in list_staff_ccx_course

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            assert len(list_instructor_ccx_course) == len(list_instructor_master_course)
            assert list_instructor_ccx_course[0].email == list_instructor_master_course[0].email