Пример #1
0
    def get(self, request, course_key):
        """
        Returns a gradebook entry/entries (i.e. both course and subsection-level grade data)
        for all users enrolled in a course, or a single user enrolled in a course
        if a `username` parameter is provided.

        Args:
            request: A Django request object.
            course_key: The edx course opaque key of a course object.
        """
        course = get_course_by_id(course_key, depth=None)

        # We fetch the entire course structure up-front, and use this when iterating
        # over users to determine their subsection grades.  We purposely avoid fetching
        # the user-specific course structure for each user, because that is very expensive.
        course_data = CourseData(user=None, course=course)
        graded_subsections = list(grades_context.graded_subsections_for_course(course_data.collected_structure))

        if request.GET.get('username'):
            with self._get_user_or_raise(request, course_key) as grade_user:
                course_grade = CourseGradeFactory().read(grade_user, course)

            entry = self._gradebook_entry(grade_user, course, graded_subsections, course_grade)
            serializer = StudentGradebookEntrySerializer(entry)
            return Response(serializer.data)
        else:
            q_objects = []
            if request.GET.get('user_contains'):
                search_term = request.GET.get('user_contains')
                q_objects.append(
                    Q(user__username__icontains=search_term) |
                    Q(programcourseenrollment__program_enrollment__external_user_key__icontains=search_term) |
                    Q(user__email__icontains=search_term)
                )
            if request.GET.get('username_contains'):
                q_objects.append(Q(user__username__icontains=request.GET.get('username_contains')))
            if request.GET.get('cohort_id'):
                cohort = cohorts.get_cohort_by_id(course_key, request.GET.get('cohort_id'))
                if cohort:
                    q_objects.append(Q(user__in=cohort.users.all()))
                else:
                    q_objects.append(Q(user__in=[]))
            if request.GET.get('enrollment_mode'):
                q_objects.append(Q(mode=request.GET.get('enrollment_mode')))

            entries = []
            related_models = ['user']
            users = self._paginate_users(course_key, q_objects, related_models)

            with bulk_gradebook_view_context(course_key, users):
                for user, course_grade, exc in CourseGradeFactory().iter(
                    users, course_key=course_key, collected_block_structure=course_data.collected_structure
                ):
                    if not exc:
                        entry = self._gradebook_entry(user, course, graded_subsections, course_grade)
                        entries.append(entry)

            serializer = StudentGradebookEntrySerializer(entries, many=True)
            return self.get_paginated_response(serializer.data)
Пример #2
0
    def get(self, request, course_id):
        """
        Returns a gradebook entry/entries (i.e. both course and subsection-level grade data)
        for all users enrolled in a course, or a single user enrolled in a course
        if a `username` parameter is provided.

        Args:
            request: A Django request object.
            course_id: A string representation of a CourseKey object.
        """
        course_key = get_course_key(request, course_id)
        course = get_course_with_access(request.user, 'staff', course_key, depth=None)

        # We fetch the entire course structure up-front, and use this when iterating
        # over users to determine their subsection grades.  We purposely avoid fetching
        # the user-specific course structure for each user, because that is very expensive.
        course_data = CourseData(user=None, course=course)
        graded_subsections = list(graded_subsections_for_course(course_data.collected_structure))

        if request.GET.get('username'):
            with self._get_user_or_raise(request, course_key) as grade_user:
                course_grade = CourseGradeFactory().read(grade_user, course)

            entry = self._gradebook_entry(grade_user, course, graded_subsections, course_grade)
            serializer = StudentGradebookEntrySerializer(entry)
            return Response(serializer.data)
        else:
            filter_kwargs = {}
            related_models = []
            if request.GET.get('username_contains'):
                filter_kwargs['user__username__icontains'] = request.GET.get('username_contains')
                related_models.append('user')
            if request.GET.get('cohort_id'):
                cohort = cohorts.get_cohort_by_id(course_key, request.GET.get('cohort_id'))
                if cohort:
                    filter_kwargs['user__in'] = cohort.users.all()
                else:
                    filter_kwargs['user__in'] = []
            if request.GET.get('enrollment_mode'):
                filter_kwargs['mode'] = request.GET.get('enrollment_mode')

            entries = []
            users = self._paginate_users(course_key, filter_kwargs, related_models)

            with bulk_gradebook_view_context(course_key, users):
                for user, course_grade, exc in CourseGradeFactory().iter(
                    users, course_key=course_key, collected_block_structure=course_data.collected_structure
                ):
                    if not exc:
                        entries.append(self._gradebook_entry(user, course, graded_subsections, course_grade))

            serializer = StudentGradebookEntrySerializer(entries, many=True)
            return self.get_paginated_response(serializer.data)
Пример #3
0
 def mock_course_grade(self, user, **kwargs):
     """
     Helper function to return a mock CourseGrade object.
     """
     course_data = CourseData(user, course=self.course)
     course_grade = CourseGrade(user=user,
                                course_data=course_data,
                                **kwargs)
     course_grade.chapter_grades = OrderedDict([
         (self.chapter_1.location, {
             'sections': [
                 self.mock_subsection_grade(
                     self.subsections[self.chapter_1.location][0],
                     earned_all=1.0,
                     possible_all=2.0,
                     earned_graded=1.0,
                     possible_graded=2.0,
                 ),
                 self.mock_subsection_grade(
                     self.subsections[self.chapter_1.location][1],
                     earned_all=1.0,
                     possible_all=2.0,
                     earned_graded=1.0,
                     possible_graded=2.0,
                 ),
             ],
             'display_name':
             'Chapter 1',
         }),
         (self.chapter_2.location, {
             'sections': [
                 self.mock_subsection_grade(
                     self.subsections[self.chapter_2.location][0],
                     earned_all=1.0,
                     possible_all=2.0,
                     earned_graded=1.0,
                     possible_graded=2.0,
                 ),
                 self.mock_subsection_grade(
                     self.subsections[self.chapter_2.location][1],
                     earned_all=1.0,
                     possible_all=2.0,
                     earned_graded=1.0,
                     possible_graded=2.0,
                 ),
             ],
             'display_name':
             'Chapter 2',
         }),
     ])
     return course_grade
Пример #4
0
 def _create_subsection_grade(self, user_id, course_key, usage_key):
     """
     Given a user_id, course_key, and subsection usage_key,
     creates a new ``PersistentSubsectionGrade``.
     """
     course = get_course(course_key, depth=None)
     subsection = course.get_child(usage_key)
     if not subsection:
         raise Exception('Subsection with given usage_key does not exist.')
     user = USER_MODEL.objects.get(id=user_id)
     course_data = CourseData(user, course=course)
     subsection_grade = CreateSubsectionGrade(subsection,
                                              course_data.structure, {}, {})
     return subsection_grade.update_or_create_model(
         user, force_update_subsections=True)
Пример #5
0
 def _create_subsection_grade(self, user, course, subsection):
     course_data = CourseData(user, course=course)
     subsection_grade = CreateSubsectionGrade(subsection,
                                              course_data.structure, {}, {})
     return subsection_grade.update_or_create_model(
         user, force_update_subsections=True)
Пример #6
0
    def get(self, request, course_key):
        """
        Returns a gradebook entry/entries (i.e. both course and subsection-level grade data)
        for all users enrolled in a course, or a single user enrolled in a course
        if a `username` parameter is provided.

        Args:
            request: A Django request object.
            course_key: The edx course opaque key of a course object.
        """
        course = get_course_by_id(course_key, depth=None)

        # We fetch the entire course structure up-front, and use this when iterating
        # over users to determine their subsection grades.  We purposely avoid fetching
        # the user-specific course structure for each user, because that is very expensive.
        course_data = CourseData(user=None, course=course)
        graded_subsections = list(
            grades_context.graded_subsections_for_course(
                course_data.collected_structure))

        if request.GET.get('username'):
            with self._get_user_or_raise(request, course_key) as grade_user:
                course_grade = CourseGradeFactory().read(
                    grade_user,
                    course,
                    collected_block_structure=course_data.collected_structure)
            entry = self._gradebook_entry(grade_user, course,
                                          graded_subsections, course_grade)
            serializer = StudentGradebookEntrySerializer(entry)
            return Response(serializer.data)
        else:
            q_objects = []
            annotations = {}
            if request.GET.get('user_contains'):
                search_term = request.GET.get('user_contains')
                q_objects.append(
                    Q(user__username__icontains=search_term) |
                    Q(programcourseenrollment__program_enrollment__external_user_key__icontains
                      =search_term) | Q(user__email__icontains=search_term))
            if request.GET.get('username_contains'):
                q_objects.append(
                    Q(user__username__icontains=request.GET.get(
                        'username_contains')))
            if request.GET.get('cohort_id'):
                cohort = cohorts.get_cohort_by_id(course_key,
                                                  request.GET.get('cohort_id'))
                if cohort:
                    q_objects.append(Q(user__in=cohort.users.all()))
                else:
                    q_objects.append(Q(user__in=[]))
            if request.GET.get('enrollment_mode'):
                q_objects.append(Q(mode=request.GET.get('enrollment_mode')))
            if request.GET.get('assignment') and (
                    request.GET.get('assignment_grade_max')
                    or request.GET.get('assignment_grade_min')):
                subqueryset = PersistentSubsectionGrade.objects.annotate(
                    effective_grade_percentage=Case(
                        When(override__isnull=False,
                             then=(F('override__earned_graded_override') /
                                   F('override__possible_graded_override')) *
                             100),
                        default=(F('earned_graded') / F('possible_graded')) *
                        100))
                grade_conditions = {
                    'effective_grade_percentage__range':
                    (request.GET.get('assignment_grade_min', 0),
                     request.GET.get('assignment_grade_max', 100))
                }
                annotations['selected_assignment_grade_in_range'] = Exists(
                    subqueryset.filter(course_id=OuterRef('course'),
                                       user_id=OuterRef('user'),
                                       usage_key=UsageKey.from_string(
                                           request.GET.get('assignment')),
                                       **grade_conditions))
                q_objects.append(Q(selected_assignment_grade_in_range=True))
            if request.GET.get('course_grade_min') or request.GET.get(
                    'course_grade_max'):
                grade_conditions = {}
                q_object = Q()
                course_grade_min = request.GET.get('course_grade_min')
                if course_grade_min:
                    course_grade_min = float(
                        request.GET.get('course_grade_min')) / 100
                    grade_conditions['percent_grade__gte'] = course_grade_min

                if request.GET.get('course_grade_max'):
                    course_grade_max = float(
                        request.GET.get('course_grade_max')) / 100
                    grade_conditions['percent_grade__lte'] = course_grade_max

                if not course_grade_min or course_grade_min == 0:
                    subquery_grade_absent = ~Exists(
                        PersistentCourseGrade.objects.filter(
                            course_id=OuterRef('course'),
                            user_id=OuterRef('user_id'),
                        ))

                    annotations['course_grade_absent'] = subquery_grade_absent
                    q_object |= Q(course_grade_absent=True)

                subquery_grade_in_range = Exists(
                    PersistentCourseGrade.objects.filter(
                        course_id=OuterRef('course'),
                        user_id=OuterRef('user_id'),
                        **grade_conditions))
                annotations['course_grade_in_range'] = subquery_grade_in_range
                q_object |= Q(course_grade_in_range=True)

                q_objects.append(q_object)

            entries = []
            related_models = ['user']
            users = self._paginate_users(course_key,
                                         q_objects,
                                         related_models,
                                         annotations=annotations)

            users_counts = self._get_users_counts(course_key,
                                                  q_objects,
                                                  annotations=annotations)

            with bulk_gradebook_view_context(course_key, users):
                for user, course_grade, exc in CourseGradeFactory().iter(
                        users,
                        course_key=course_key,
                        collected_block_structure=course_data.
                        collected_structure):
                    if not exc:
                        entry = self._gradebook_entry(user, course,
                                                      graded_subsections,
                                                      course_grade)
                        entries.append(entry)

            serializer = StudentGradebookEntrySerializer(entries, many=True)
            return self.get_paginated_response(serializer.data, **users_counts)
Пример #7
0
    def setUpClass(cls):
        super(GradebookViewTestBase, cls).setUpClass()
        cls.namespaced_url = 'grades_api:v1:course_gradebook'
        cls.waffle_flag = waffle_flags()[WRITABLE_GRADEBOOK]

        cls.course = CourseFactory.create(display_name='test-course', run='run-1')
        cls.course_key = cls.course.id
        cls.course_overview = CourseOverviewFactory.create(id=cls.course.id)

        cls.chapter_1 = ItemFactory.create(
            category='chapter',
            parent_location=cls.course.location,
            display_name="Chapter 1",
        )
        cls.chapter_2 = ItemFactory.create(
            category='chapter',
            parent_location=cls.course.location,
            display_name="Chapter 2",
        )
        cls.subsections = {
            cls.chapter_1.location: [
                ItemFactory.create(
                    category='sequential',
                    parent_location=cls.chapter_1.location,
                    due=datetime(2017, 12, 18, 11, 30, 00),
                    display_name='HW 1',
                    format='Homework',
                    graded=True,
                ),
                ItemFactory.create(
                    category='sequential',
                    parent_location=cls.chapter_1.location,
                    due=datetime(2017, 12, 18, 11, 30, 00),
                    display_name='Lab 1',
                    format='Lab',
                    graded=True,
                ),
            ],
            cls.chapter_2.location: [
                ItemFactory.create(
                    category='sequential',
                    parent_location=cls.chapter_2.location,
                    due=datetime(2017, 12, 18, 11, 30, 00),
                    display_name='HW 2',
                    format='Homework',
                    graded=True,
                ),
                ItemFactory.create(
                    category='sequential',
                    parent_location=cls.chapter_2.location,
                    due=datetime(2017, 12, 18, 11, 30, 00),
                    display_name='Lab 2',
                    format='Lab',
                    graded=True,
                ),
            ],
        }
        cls.course_data = CourseData(None, course=cls.course)
        # we have to force the collection of course data from the block_structure API
        # so that CourseGrade.course_data objects can later have a non-null effective_structure
        _ = cls.course_data.collected_structure