示例#1
0
def course_grade(learner, course):
    """
    Compatibility function to retrieve course grades

    Returns the course grade for the specified learner and course
    """
    if RELEASE_LINE == 'ginkgo':
        return CourseGradeFactory().create(learner, course)
    else:  # Assume Hawthorn or greater
        return CourseGradeFactory().read(learner, course)
示例#2
0
 def test_cert_already_generated(self):
     with mock.patch(
             'lms.djangoapps.certificates.signals.generate_certificate.apply_async',
             return_value=None) as mock_generate_certificate_apply_async:
         grade_factory = CourseGradeFactory()
         # Create the certificate
         GeneratedCertificate.eligible_certificates.create(
             user=self.user,
             course_id=self.course.id,
             status=CertificateStatuses.downloadable)
         # Certs are not re-fired after passing
         with mock_passing_grade():
             grade_factory.update(self.user, self.course)
             mock_generate_certificate_apply_async.assert_not_called()
示例#3
0
    def test_cert_generation_on_photo_verification_self_paced(self):
        with mock.patch(
                'lms.djangoapps.certificates.signals.generate_certificate.apply_async',
                return_value=None) as mock_generate_certificate_apply_async:
            with mock_passing_grade():
                grade_factory = CourseGradeFactory()
                grade_factory.update(self.user_one, self.course_one)

            with waffle.waffle().override(waffle.SELF_PACED_ONLY, active=True):
                mock_generate_certificate_apply_async.assert_not_called()
                attempt = SoftwareSecurePhotoVerification.objects.create(
                    user=self.user_one, status='submitted')
                attempt.approve()
                mock_generate_certificate_apply_async.assert_called_with(
                    student=self.user_one, course_key=self.course_one.id)
示例#4
0
    def report(self):
        """
        Generates a report for the Ed2go completion report endpoint.

        Returns:
            A dictionary containing the report values.
        """
        course_grade = CourseGradeFactory().create(self.user,
                                                   get_course(self.course_key))
        persistent_grade = PersistentCourseGrade.objects.filter(
            user_id=self.user.id, course_id=self.course_key).first()

        return {
            c.REP_REGISTRATION_KEY:
            self.registration_key,
            c.REP_PERCENT_PROGRESS:
            round(self.progress * 100, 2),
            c.REP_LAST_ACCESS_DT:
            self.user.last_login.strftime('%Y-%m-%dT%H:%M:%SZ'),
            c.REP_COURSE_PASSED:
            str(course_grade.passed).lower(),
            c.REP_PERCENT_OVERALL_SCORE:
            course_grade.percent,
            c.REP_COMPLETION_DT:
            persistent_grade.passed_timestamp if persistent_grade else '',
            c.REP_TIME_SPENT:
            format_timedelta(
                CourseSession.total_time(user=self.user,
                                         course_key=self.course_key)),
        }
    def check_state(self,
                    user,
                    descriptor,
                    expected_score,
                    expected_max_score,
                    expected_attempts=1):
        """
        Check that the StudentModule state contains the expected values.

        The student module is found for the test course, given the `username` and problem `descriptor`.

        Values checked include the number of attempts, the score, and the max score for a problem.
        """
        module = self.get_student_module(user.username, descriptor)
        self.assertEqual(module.grade, expected_score)
        self.assertEqual(module.max_grade, expected_max_score)
        state = json.loads(module.state)
        attempts = state['attempts']
        self.assertEqual(attempts, expected_attempts)
        if attempts > 0:
            self.assertIn('correct_map', state)
            self.assertIn('student_answers', state)
            self.assertGreater(len(state['correct_map']), 0)
            self.assertGreater(len(state['student_answers']), 0)

        # assume only one problem in the subsection and the grades
        # are in sync.
        expected_subsection_grade = expected_score

        course_grade = CourseGradeFactory().create(user, self.course)
        self.assertEquals(
            course_grade.graded_subsections_by_format['Homework'][
                self.problem_section.location].graded_total.earned,
            expected_subsection_grade,
        )
示例#6
0
 def _rows_for_users(self, context, users):
     """
     Returns a list of rows for the given users for this report.
     """
     certificate_whitelist = CertificateWhitelist.objects.filter(
         course_id=context.course_id, whitelist=True)
     whitelisted_user_ids = [
         entry.user_id for entry in certificate_whitelist
     ]
     success_rows, error_rows = [], []
     for user, course_grade, err_msg in CourseGradeFactory().iter(
             users, course_key=context.course_id):
         if not course_grade:
             # An empty gradeset means we failed to grade a student.
             error_rows.append([user.id, user.username, err_msg])
         else:
             success_rows.append(
                 [user.id, user.email, user.username] +
                 self._user_grade_results(course_grade, context) +
                 self._user_cohort_group_names(user, context) +
                 self._user_experiment_group_names(user, context) +
                 self._user_team_names(user, context) +
                 self._user_verification_mode(user, context) +
                 self._user_certificate_info(user, context, course_grade,
                                             whitelisted_user_ids))
     return success_rows, error_rows
示例#7
0
def _listen_for_track_change(sender, user, **kwargs):  # pylint: disable=unused-argument
    """
    Catches a track change signal, determines user status,
    calls fire_ungenerated_certificate_task for passing grades
    """
    if (not waffle.waffle().is_enabled(waffle.SELF_PACED_ONLY)
            and not waffle.waffle().is_enabled(waffle.INSTRUCTOR_PACED_ONLY)):
        return
    user_enrollments = CourseEnrollment.enrollments_for_user(user=user)
    grade_factory = CourseGradeFactory()
    for enrollment in user_enrollments:
        if grade_factory.read(user=user, course=enrollment.course).passed:
            if fire_ungenerated_certificate_task(user, enrollment.course.id):
                log.info(
                    u'Certificate generation task initiated for {user} : {course} via track change'
                    .format(user=user.id, course=enrollment.course.id))
示例#8
0
def get_grade_book_page(request, course, course_key):
    """
    Get student records per page along with page information i.e current page, total pages and
    offset information.
    """
    # Unsanitized offset
    current_offset = request.GET.get('offset', 0)
    enrolled_students = User.objects.filter(
        courseenrollment__course_id=course_key,
        courseenrollment__is_active=1
    ).order_by('username').select_related("profile")

    total_students = enrolled_students.count()
    page = calculate_page_info(current_offset, total_students)
    offset = page["offset"]
    total_pages = page["total_pages"]

    if total_pages > 1:
        # Apply limit on queryset only if total number of students are greater then MAX_STUDENTS_PER_PAGE_GRADE_BOOK.
        enrolled_students = enrolled_students[offset: offset + MAX_STUDENTS_PER_PAGE_GRADE_BOOK]

    with modulestore().bulk_operations(course.location.course_key):
        student_info = [
            {
                'username': student.username,
                'id': student.id,
                'email': student.email,
                'grade_summary': CourseGradeFactory().create(student, course).summary
            }
            for student in enrolled_students
        ]
    return student_info, page
示例#9
0
    def _rows_for_users(self, context, users):
        """
        Returns a list of rows for the given users for this report.
        """
        with modulestore().bulk_operations(context.course_id):
            bulk_context = _CourseGradeBulkContext(context, users)

            success_rows, error_rows = [], []
            for user, course_grade, error in CourseGradeFactory().iter(
                    users,
                    course=context.course,
                    collected_block_structure=context.course_structure,
                    course_key=context.course_id,
            ):
                if not course_grade:
                    # An empty gradeset means we failed to grade a student.
                    error_rows.append([user.id, user.username, error.message])
                else:
                    success_rows.append(
                        [user.id, user.email, user.username] +
                        self._user_grade_results(course_grade, context) +
                        self._user_cohort_group_names(user, context) +
                        self._user_experiment_group_names(user, context) +
                        self._user_team_names(user, bulk_context.teams) +
                        self._user_verification_mode(
                            user, context, bulk_context.enrollments) +
                        self._user_certificate_info(
                            user, context, course_grade, bulk_context.certs) +
                        [_user_enrollment_status(user, context.course_id)])
            return success_rows, error_rows
示例#10
0
    def test_has_zero_weights(self, enable_vertical):
        """
        Test that course grade at start is 0
        """
        tree = {
            "a": {
                "b": ("Homework", {
                    "c": (1., {
                        "d": (0., 1.),
                        "e": (0., 1.)
                    }),
                    "f": (0., {
                        "g": (0., 1.),
                        "h": (1., 1.)
                    }),
                    "i": (4., {
                        "j": (1., 1.),
                        "k": (1., 1.)
                    }),
                }),
            }
        }
        self._update_grading_policy()
        self._build_from_tree(tree)
        self._enable_if_needed(enable_vertical)
        self._check_tree(tree, self.course)
        # TODO: somehow .create doesn't work. Why?
        # pc = CourseGradeFactory().create(self.request.user, self.course).percent
        pc = CourseGradeFactory().create(self.request.user,
                                         self.course).percent

        model_calculated_pc = self._grade_tree(tree, enable_vertical)
        self.assertEqual(pc, model_calculated_pc)
        expected_pc = 0.8 if enable_vertical else 0.5
        self.assertEqual(pc, expected_pc)
    def handle(self, *args, **options):

        course_id = options.get('course_id')

        users_updated = 0

        course_key = CourseKey.from_string(course_id)
        users = CourseEnrollment.objects.users_enrolled_in(course_key)
        course = modulestore().get_course(course_key, depth=None)

        if course:
            # For each user...
            for user in users:
                is_passed = False
                try:
                    course_grade = CourseGradeFactory().create(user, course)
                    is_passed = course_grade.passed
                    StudentGradebook.objects.filter(
                        user=user,
                        course_id=course_key).update(is_passed=is_passed)
                except Exception as ex:  # pylint: disable=broad-except
                    log.info(
                        "Failed to update pass status for user %s in course %s. Error: %s",
                        user.id, course_key, ex.message)

                users_updated += 1
                log.info(
                    "Gradebook entry updated in Course %s for User id %s with pass status: %s",
                    course.id, user.id, is_passed)
        else:
            log.info("Course with course id %s does not exist", course_id)
        log.info("%d users have their pass status updated", users_updated)
示例#12
0
def _listen_for_track_change(sender, user, **kwargs):  # pylint: disable=unused-argument
    """
    Catches a track change signal, determines user status,
    calls fire_ungenerated_certificate_task for passing grades
    """
    if not auto_certificate_generation_enabled():
        return

    user_enrollments = CourseEnrollment.enrollments_for_user(user=user)
    grade_factory = CourseGradeFactory()
    for enrollment in user_enrollments:
        if grade_factory.read(user=user,
                              course=enrollment.course_overview).passed:
            if fire_ungenerated_certificate_task(user, enrollment.course_id):
                log.info(
                    u'Certificate generation task initiated for {user} : {course} via track change'
                    .format(user=user.id, course=enrollment.course_id))
示例#13
0
def send_composite_outcome(user_id, course_id, assignment_id, version):
    """
    Calculate and transmit the score for a composite module (such as a
    vertical).

    A composite module may contain multiple problems, so we need to
    calculate the total points earned and possible for all child problems. This
    requires calculating the scores for the whole course, which is an expensive
    operation.

    Callers should be aware that the score calculation code accesses the latest
    scores from the database. This can lead to a race condition between a view
    that updates a user's score and the calculation of the grade. If the Celery
    task attempts to read the score from the database before the view exits (and
    its transaction is committed), it will see a stale value. Care should be
    taken that this task is not triggered until the view exits.

    The GradedAssignment model has a version_number field that is incremented
    whenever the score is updated. It is used by this method for two purposes.
    First, it allows the task to exit if it detects that it has been superseded
    by another task that will transmit the score for the same assignment.
    Second, it prevents a race condition where two tasks calculate different
    scores for a single assignment, and may potentially update the campus LMS
    in the wrong order.
    """
    assignment = GradedAssignment.objects.get(id=assignment_id)
    if version != assignment.version_number:
        log.info(
            "Score passback for GradedAssignment %s skipped. More recent score available.",
            assignment.id
        )
        return
    course_key = CourseKey.from_string(course_id)
    mapped_usage_key = assignment.usage_key.map_into_course(course_key)
    user = User.objects.get(id=user_id)
    course = modulestore().get_course(course_key, depth=0)
    course_grade = CourseGradeFactory().create(user, course)
    earned, possible = course_grade.score_for_module(mapped_usage_key)
    if possible == 0:
        weighted_score = 0
    else:
        weighted_score = float(earned) / float(possible)

    assignment = GradedAssignment.objects.get(id=assignment_id)
    if assignment.version_number == version:
        outcomes.send_score_update(assignment, weighted_score)
示例#14
0
def _listen_for_track_change(sender, user, **kwargs):  # pylint: disable=unused-argument
    """
    Catches a track change signal, determines user status,
    calls fire_ungenerated_certificate_task for passing grades
    """
    if not auto_certificate_generation_enabled():
        return

    user_enrollments = CourseEnrollment.enrollments_for_user(user=user)
    grade_factory = CourseGradeFactory()
    for enrollment in user_enrollments:
        if grade_factory.read(user=user, course=enrollment.course_overview).passed:
            if fire_ungenerated_certificate_task(user, enrollment.course_id):
                log.info(u'Certificate generation task initiated for {user} : {course} via track change'.format(
                    user=user.id,
                    course=enrollment.course_id
                ))
示例#15
0
文件: tasks.py 项目: eliesmr4/myedx
def send_composite_outcome(user_id, course_id, assignment_id, version):
    """
    Calculate and transmit the score for a composite module (such as a
    vertical).

    A composite module may contain multiple problems, so we need to
    calculate the total points earned and possible for all child problems. This
    requires calculating the scores for the whole course, which is an expensive
    operation.

    Callers should be aware that the score calculation code accesses the latest
    scores from the database. This can lead to a race condition between a view
    that updates a user's score and the calculation of the grade. If the Celery
    task attempts to read the score from the database before the view exits (and
    its transaction is committed), it will see a stale value. Care should be
    taken that this task is not triggered until the view exits.

    The GradedAssignment model has a version_number field that is incremented
    whenever the score is updated. It is used by this method for two purposes.
    First, it allows the task to exit if it detects that it has been superseded
    by another task that will transmit the score for the same assignment.
    Second, it prevents a race condition where two tasks calculate different
    scores for a single assignment, and may potentially update the campus LMS
    in the wrong order.
    """
    assignment = GradedAssignment.objects.get(id=assignment_id)
    if version != assignment.version_number:
        log.info(
            "Score passback for GradedAssignment %s skipped. More recent score available.",
            assignment.id)
        return
    course_key = CourseKey.from_string(course_id)
    mapped_usage_key = assignment.usage_key.map_into_course(course_key)
    user = User.objects.get(id=user_id)
    course = modulestore().get_course(course_key, depth=0)
    course_grade = CourseGradeFactory().create(user, course)
    earned, possible = course_grade.score_for_module(mapped_usage_key)
    if possible == 0:
        weighted_score = 0
    else:
        weighted_score = float(earned) / float(possible)

    assignment = GradedAssignment.objects.get(id=assignment_id)
    if assignment.version_number == version:
        outcomes.send_score_update(assignment, weighted_score)
 def test_cert_generation_on_passing_instructor_paced(self):
     with mock.patch(
         'lms.djangoapps.certificates.signals.generate_certificate.apply_async',
         return_value=None
     ) as mock_generate_certificate_apply_async:
         with waffle.waffle().override(waffle.INSTRUCTOR_PACED_ONLY, active=True):
             grade_factory = CourseGradeFactory()
             with mock_get_score(0, 2):
                 grade_factory.update(self.user, self.ip_course)
                 mock_generate_certificate_apply_async.assert_not_called(
                     student=self.user,
                     course_key=self.ip_course.id
                 )
             with mock_get_score(1, 2):
                 grade_factory.update(self.user, self.ip_course)
                 mock_generate_certificate_apply_async.assert_called(
                     student=self.user,
                     course_key=self.ip_course.id
                 )
             # Certs are not re-fired after passing
             with mock_get_score(2, 2):
                 grade_factory.update(self.user, self.ip_course)
                 mock_generate_certificate_apply_async.assert_not_called(
                     student=self.user,
                     course_key=self.ip_course.id
                 )
def get_grade_summary(user_id, course):
    """
    Return the grade for the given student in the addressed course.
    """
    try:
        return CourseGradeFactory().create(
            User.objects.all().filter(id=user_id).first(), course).summary
    except PermissionDenied:
        return None
示例#18
0
 def _add_entrance_exam_to_context(self, courseware_context):
     """
     Adds entrance exam related information to the given context.
     """
     if course_has_entrance_exam(self.course) and getattr(self.chapter, 'is_entrance_exam', False):
         courseware_context['entrance_exam_passed'] = user_has_passed_entrance_exam(self.effective_user, self.course)
         courseware_context['entrance_exam_current_score'] = get_entrance_exam_score_ratio(
             CourseGradeFactory().create(self.effective_user, self.course),
             get_entrance_exam_usage_key(self.course),
         )
示例#19
0
    def assert_course_grade(self, user, expected_percent):
        """
        Verifies the given user's course grade is the expected percentage.
        Also verifies the user's grade information contains values for
        all problems in the course, whether or not they are currently
        gated.
        """
        course_grade = CourseGradeFactory().create(user, self.course)
        for prob in [self.gating_prob1, self.gated_prob2, self.prob3]:
            self.assertIn(prob.location, course_grade.problem_scores)

        self.assertEquals(course_grade.percent, expected_percent)
示例#20
0
 def setUp(self):
     super(LearnerTrackChangeCertsTest, self).setUp()
     self.course_one = CourseFactory.create(self_paced=True)
     self.user_one = UserFactory.create()
     self.enrollment_one = CourseEnrollmentFactory(
         user=self.user_one,
         course_id=self.course_one.id,
         is_active=True,
         mode='verified',
     )
     self.user_two = UserFactory.create()
     self.course_two = CourseFactory.create(self_paced=False)
     self.enrollment_two = CourseEnrollmentFactory(
         user=self.user_two,
         course_id=self.course_two.id,
         is_active=True,
         mode='verified')
     with mock_passing_grade():
         grade_factory = CourseGradeFactory()
         grade_factory.update(self.user_one, self.course_one)
         grade_factory.update(self.user_two, self.course_two)
示例#21
0
文件: views.py 项目: eliesmr4/myedx
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(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') 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
示例#22
0
 def handle(self, *args, **options):
     course_id = options['course']
     log.info('Fetching ungraded students for %s.', course_id)
     ungraded = GeneratedCertificate.objects.filter(  # pylint: disable=no-member
         course_id__exact=course_id).filter(grade__exact='')
     course = courses.get_course_by_id(course_id)
     for cert in ungraded:
         # grade the student
         grade = CourseGradeFactory().create(cert.user, course)
         log.info('grading %s - %s', cert.user, grade.percent)
         cert.grade = grade.percent
         if not options['noop']:
             cert.save()
示例#23
0
def _listen_for_id_verification_status_changed(sender, user, **kwargs):  # pylint: disable=unused-argument
    """
    Catches a track change signal, determines user status,
    calls fire_ungenerated_certificate_task for passing grades
    """
    if not auto_certificate_generation_enabled():
        return

    user_enrollments = CourseEnrollment.enrollments_for_user(user=user)
    grade_factory = CourseGradeFactory()
    expected_verification_status, _ = SoftwareSecurePhotoVerification.user_status(
        user)
    for enrollment in user_enrollments:
        if grade_factory.read(user=user,
                              course=enrollment.course_overview).passed:
            if fire_ungenerated_certificate_task(user, enrollment.course_id,
                                                 expected_verification_status):
                message = (
                    u'Certificate generation task initiated for {user} : {course} via track change '
                    + u'with verification status of {status}')
                log.info(
                    message.format(user=user.id,
                                   course=enrollment.course_id,
                                   status=expected_verification_status))
示例#24
0
    def test_drop_last_element_with_several_ss(self, enable_vertical):
        """
        Tests that when we drop last value from subsection in vertical grading, it improves the
        gain
        """
        tree = {
            "a": {
                "b1": ("Homework", {
                    "c1": (1., {
                        "d1": (1., 1.),
                        "e1": (0., 1.)
                    }),
                }),
                "b2": ("Homework", {
                    "c2": (1., {
                        "d2": (1., 1.),
                        "e2": (0., 1.)
                    }),
                }),
            }
        }
        DROP_COUNT = 1
        grading_policy = {
            "GRADER": [
                {
                    "type": "Homework",
                    "min_count": 0,
                    "drop_count": DROP_COUNT,
                    "short_label": "HW",
                    "weight": 1.,
                },
            ],
            "GRADE_CUTOFFS": {
                "Pass": 0.5,
            },
        }

        self._build_from_tree(tree)
        self._update_grading_policy(grading_policy)
        self._enable_if_needed(enable_vertical)
        self._check_tree(tree, self.course)

        pc = CourseGradeFactory().create(self.request.user,
                                         self.course).percent
        model_calculated_pc = self._grade_tree(tree, enable_vertical)
        self.assertEqual(pc, model_calculated_pc)

        self.assertEqual(pc, 0.5)
示例#25
0
 def test_drop_optimal(self, enable_vertical):
     """
     Tests that unit drops chooses optimal solution:
     We should drop c4, because we'll get (1 + 0)/(1+1) against
     (1 + 1) / (1 + 4) if drop c3 instead
     """
     tree = {
         "a": {
             "b1": ("Homework", { #NW 0.5; W: 0.5
                 "c1": (1., {"d1": (1., 1.), "e1": (1., 1.)}), #W: 0.5/1
             }),
             "b2": ("Homework", { #NW 0.5; W: 0.5
                 "c2": (1., {"d2": (1., 1.), "e2": (1., 1.)}), #W 1/1
                 "c3": (1., {"d3": (0., 1.)}), # 0/1
                 "c4": (4., {"d4": (1., 1.), "e41": (0., 1.), "e42": (0., 1.), "e43": (0., 1.) }),
             }),
         }
     }
     DROP_COUNT = 1
     grading_policy = {
         "GRADER": [
             {
                 "type": "Homework",
                 "min_count": 0,
                 "drop_count": DROP_COUNT,
                 "short_label": "HW",
                 "weight": 1.,
             },
         ],
         "GRADE_CUTOFFS": {
             "Pass": 0.5,
         },
     }
     self._build_from_tree(tree)
     self._update_grading_policy(grading_policy)
     self._enable_if_needed(enable_vertical)
     self._check_tree(tree, self.course)
     pc = CourseGradeFactory().create(self.request.user,
                                      self.course).percent
     model_calculated_pc = self._grade_tree(tree, enable_vertical)
     self.assertEqual(pc, model_calculated_pc)
     #drop with weight 4
     grade_round = lambda x: round(x + 0.05 / 100, 2)
     expected_pc = grade_round(
         (1. * 1 + 1 * 1 + 0 * 1) / (1 + 1 + 1)) if enable_vertical else 1
     self.assertEqual(pc, expected_pc)
示例#26
0
 def test_cert_generation_on_passing_self_paced(self):
     with mock.patch(
             'lms.djangoapps.certificates.signals.generate_certificate.apply_async',
             return_value=None) as mock_generate_certificate_apply_async:
         with waffle.waffle().override(waffle.SELF_PACED_ONLY, active=True):
             grade_factory = CourseGradeFactory()
             with mock_get_score(0, 2):
                 grade_factory.update(self.user, self.course)
                 mock_generate_certificate_apply_async.assert_not_called(
                     student=self.user, course_key=self.course.id)
             with mock_get_score(1, 2):
                 grade_factory.update(self.user, self.course)
                 mock_generate_certificate_apply_async.assert_called(
                     student=self.user, course_key=self.course.id)
             # Certs are not re-fired after passing
             with mock_get_score(2, 2):
                 grade_factory.update(self.user, self.course)
                 mock_generate_certificate_apply_async.assert_not_called(
                     student=self.user, course_key=self.course.id)
示例#27
0
    def test_drop_last_element(self, enable_vertical):
        """
        Tests that when we drop everything from the course by grading_policy,
        course grade is 0
        """
        tree = {
            "a": {
                "b1": (
                    "Homework",
                    {  # NW 0.5; W: 0.8
                        "c1": (1., {
                            "d1": (1., 1.),
                            "e1": (0., 1.)
                        }),  # W:0/1
                    }),
            }
        }
        DROP_COUNT = 1
        grading_policy = {
            "GRADER": [
                {
                    "type": "Homework",
                    "min_count": 0,
                    "drop_count": DROP_COUNT,
                    "short_label": "HW",
                    "weight": 1.,
                },
            ],
            "GRADE_CUTOFFS": {
                "Pass": 0.5,
            },
        }

        self._build_from_tree(tree)
        self._update_grading_policy(grading_policy)
        self._enable_if_needed(enable_vertical)
        self._check_tree(tree, self.course)

        pc = CourseGradeFactory().create(self.request.user,
                                         self.course).percent
        model_calculated_pc = self._grade_tree(tree, enable_vertical)
        self.assertEqual(pc, model_calculated_pc)
        self.assertEqual(pc, 0)
示例#28
0
def generate_user_gradebook(course_key, user):
    """
    Recalculates the specified user's gradebook entry
    """
    with modulestore().bulk_operations(course_key):
        course_descriptor = get_course(course_key, depth=None)
        course_grade = CourseGradeFactory().create(user, course_descriptor)
        grade_summary = course_grade.summary
        is_passed = course_grade.passed
        progress_summary = make_courseware_summary(course_grade)
        grading_policy = course_descriptor.grading_policy
        grade = grade_summary['percent']
        proforma_grade = calculate_proforma_grade(course_grade, grading_policy)

    progress_summary = get_json_data(progress_summary)
    grade_summary = get_json_data(grade_summary)
    grading_policy = get_json_data(grading_policy)
    gradebook_entry, created = StudentGradebook.objects.get_or_create(
        user=user,
        course_id=course_key,
        defaults={
            'grade': grade,
            'proforma_grade': proforma_grade,
            'progress_summary': progress_summary,
            'grade_summary': grade_summary,
            'grading_policy': grading_policy,
            'is_passed': is_passed,
        })

    if gradebook_entry.grade != grade or \
            gradebook_entry.proforma_grade != proforma_grade or \
            gradebook_entry.is_passed != is_passed:
        gradebook_entry.grade = grade
        gradebook_entry.proforma_grade = proforma_grade
        gradebook_entry.progress_summary = progress_summary
        gradebook_entry.grade_summary = grade_summary
        gradebook_entry.grading_policy = grading_policy
        gradebook_entry.is_passed = is_passed
        gradebook_entry.save()
        invalid_user_data_cache('grade', course_key, user.id)

    return gradebook_entry
示例#29
0
 def test_cert_generation_on_passing_instructor_paced(self):
     with mock.patch(
             'lms.djangoapps.certificates.signals.generate_certificate.apply_async',
             return_value=None) as mock_generate_certificate_apply_async:
         with waffle.waffle().override(waffle.INSTRUCTOR_PACED_ONLY,
                                       active=True):
             grade_factory = CourseGradeFactory()
             # Not passing
             grade_factory.update(self.user, self.ip_course)
             mock_generate_certificate_apply_async.assert_not_called()
             # Certs fired after passing
             with mock_passing_grade():
                 grade_factory.update(self.user, self.ip_course)
                 mock_generate_certificate_apply_async.assert_called_with(
                     student=self.user, course_key=self.ip_course.id)
示例#30
0
    def test_percent(self, enable_vertical):
        tree = {
            "a": {
                "b": ("Homework", {
                    "d": (0., {
                        "h": (2., 5.),
                        "i": (3., 5.),
                    }),
                    "e": (1., {
                        "j": (0., 1.),
                        "k": (None, None),
                        "l": (1., 3)
                    }),
                    "f": (0., {
                        "m": (None, None)
                    })
                }),
                "c": ("Homework", {
                    "g": (0, {
                        "n": (6., 10)
                    })
                })
            }
        }
        self._build_from_tree(tree)
        self._enable_if_needed(enable_vertical)

        self._check_tree(tree, self.course)
        course_grade = CourseGradeFactory().create(self.request.user,
                                                   self.course)
        pc = course_grade.percent

        subsection_grades = [(2. + 3. + 0. + 1.) / (5. + 5. + 1. + 3.),
                             6. / 10]
        expected_grade = sum(subsection_grades) / len(subsection_grades)
        if enable_vertical:
            expected_grade = 0.25
        expected_pc = round(expected_grade * 100 + 0.05) / 100
        self.assertEqual(pc, expected_pc)
        model_calculated_pc = self._grade_tree(tree, enable_vertical)
        self.assertEqual(pc, model_calculated_pc)
示例#31
0
 def test_drop_over_min_count(self, enable_vertical):
     """
     Tests that when we have mincount > 0 we still drop worst element
     """
     tree = {
         "a": {
             "b1": ("Homework", {
                 "c1": (1., {
                     "d1": (0., 1.),
                     "e1": (1., 1.)
                 }),
             }),
         }
     }
     DROP_COUNT = 1
     MIN_COUNT = 1
     grading_policy = {
         "GRADER": [
             {
                 "type": "Homework",
                 "min_count": MIN_COUNT,
                 "drop_count": DROP_COUNT,
                 "short_label": "HW",
                 "weight": 1.,
             },
         ],
         "GRADE_CUTOFFS": {
             "Pass": 0.5,
         },
     }
     self._update_grading_policy(grading_policy)
     self._build_from_tree(tree)
     self._enable_if_needed(enable_vertical)
     self._check_tree(tree, self.course)
     pc = CourseGradeFactory().create(self.request.user,
                                      self.course).percent
     model_calculated_pc = self._grade_tree(tree, enable_vertical)
     self.assertEqual(pc, model_calculated_pc)
     expected_pc = 0.
     self.assertEqual(pc, expected_pc)
示例#32
0
    def test_several_subsections(self, enable_vertical):
        """
        Test with several subsections, both graded and not
        """
        tree = {
            "a": {
                "b": ("Homework", {
                    "c": (1., {
                        "d": (0., 1.),
                        "e": (0., 1.)
                    }),
                }),
                "f": ("", {
                    "g": (1., {
                        "h": (1., 1.),
                        "i": (1., 1.)
                    }),
                }),
                "j": ("Homework", {
                    "l": (1., {
                        "m": (1, 1)
                    }),
                    "n": (0., {
                        "o": (1, 1)
                    })
                })
            }
        }
        self._update_grading_policy()
        self._build_from_tree(tree)
        self._enable_if_needed(enable_vertical)
        self._check_tree(tree, self.course)

        pc = CourseGradeFactory().create(self.request.user,
                                         self.course).percent
        model_calculated_pc = self._grade_tree(tree, enable_vertical)
        self.assertEqual(pc, model_calculated_pc)

        expected_pc = 0.5
        self.assertEqual(pc, expected_pc)
示例#33
0
 def test_cert_generation_on_passing_self_paced(self):
     with mock.patch(
             'lms.djangoapps.certificates.signals.generate_certificate.apply_async',
             return_value=None) as mock_generate_certificate_apply_async:
         with waffle.waffle().override(waffle.SELF_PACED_ONLY, active=True):
             grade_factory = CourseGradeFactory()
             # Not passing
             grade_factory.update(self.user, self.course)
             mock_generate_certificate_apply_async.assert_not_called()
             # Certs fired after passing
             with mock_passing_grade():
                 grade_factory.update(self.user, self.course)
                 mock_generate_certificate_apply_async.assert_called_with(
                     kwargs={
                         'student': unicode(self.user.id),
                         'course_key': unicode(self.course.id),
                     })