Beispiel #1
0
    def test_certificate_info_for_user(self, allow_certificate, whitelisted,
                                       grade, output):
        """
        Verify that certificate_info_for_user works.
        """
        student = UserFactory()
        student.profile.allow_certificate = allow_certificate
        student.profile.save()

        # for instructor paced course
        certificate_info = certificate_info_for_user(
            student,
            self.instructor_paced_course.id,
            grade,
            whitelisted,
            user_certificate=None)
        self.assertEqual(certificate_info, output)

        # for self paced course
        certificate_info = certificate_info_for_user(student,
                                                     self.self_paced_course.id,
                                                     grade,
                                                     whitelisted,
                                                     user_certificate=None)
        self.assertEqual(certificate_info, output)
Beispiel #2
0
    def test_certificate_info_for_user_when_grade_changes(
            self, whitelisted, grade, output):
        """
        Verify that certificate_info_for_user works as expect in scenario when grading of problems
        changes after certificates already generated. In such scenario `Certificate delivered` should not depend
        on student's eligibility to get certificates since in above scenario eligibility can change over period
        of time.
        """
        student = UserFactory()

        certificate1 = GeneratedCertificateFactory.create(
            user=student,
            course_id=self.instructor_paced_course.id,
            status=CertificateStatuses.downloadable,
            mode='honor')

        certificate2 = GeneratedCertificateFactory.create(
            user=student,
            course_id=self.self_paced_course.id,
            status=CertificateStatuses.downloadable,
            mode='honor')

        # for instructor paced course
        certificate_info = certificate_info_for_user(
            student, self.instructor_paced_course.id, grade, whitelisted,
            certificate1)
        assert certificate_info == output

        # for self paced course
        certificate_info = certificate_info_for_user(student,
                                                     self.self_paced_course.id,
                                                     grade, whitelisted,
                                                     certificate2)
        assert certificate_info == output
Beispiel #3
0
    def test_certificate_info_for_user(self, allow_certificate, whitelisted,
                                       grade, output):
        """
        Verify that certificate_info_for_user works.
        """
        student = UserFactory()
        student.profile.allow_certificate = allow_certificate
        student.profile.save()  # pylint: disable=no-member

        # for instructor paced course
        certificate_info = certificate_info_for_user(
            student,
            self.instructor_paced_course.id,
            grade,
            whitelisted,
            user_certificate=None)
        assert certificate_info == output

        # for self paced course
        certificate_info = certificate_info_for_user(student,
                                                     self.self_paced_course.id,
                                                     grade,
                                                     whitelisted,
                                                     user_certificate=None)
        assert certificate_info == output
Beispiel #4
0
 def _user_certificate_info(self, user, context, course_grade, bulk_certs):
     """
     Returns the course certification information for the given user.
     """
     is_whitelisted = user.id in bulk_certs.whitelisted_user_ids
     certificate_info = certificate_info_for_user(
         user,
         context.course_id,
         course_grade.letter_grade,
         is_whitelisted,
         bulk_certs.certificates_by_user.get(user.id),
     )
     TASK_LOG.info(
         u'Student certificate eligibility: %s '
         u'(user=%s, course_id=%s, grade_percent=%s letter_grade=%s gradecutoffs=%s, allow_certificate=%s, '
         u'is_whitelisted=%s)',
         certificate_info[0],
         user,
         context.course_id,
         course_grade.percent,
         course_grade.letter_grade,
         context.course.grade_cutoffs,
         user.profile.allow_certificate,
         is_whitelisted,
     )
     return certificate_info
Beispiel #5
0
    def test_certificate_info_for_user(self, allow_certificate, whitelisted, grade, output):
        """
        Verify that certificate_info_for_user works.
        """
        student = UserFactory()
        _ = CourseFactory.create(org='edx', number='verified', display_name='Verified Course')
        student.profile.allow_certificate = allow_certificate
        student.profile.save()

        certificate_info = certificate_info_for_user(student, grade, whitelisted, user_certificate=None)
        self.assertEqual(certificate_info, output)
Beispiel #6
0
    def test_certificate_info_for_user(self, allow_certificate, whitelisted, grade, output):
        """
        Verify that certificate_info_for_user works.
        """
        student = UserFactory()
        student.profile.allow_certificate = allow_certificate
        student.profile.save()

        # for instructor paced course
        certificate_info = certificate_info_for_user(
            student, self.instructor_paced_course.id, grade,
            whitelisted, user_certificate=None
        )
        self.assertEqual(certificate_info, output)

        # for self paced course
        certificate_info = certificate_info_for_user(
            student, self.self_paced_course.id, grade,
            whitelisted, user_certificate=None
        )
        self.assertEqual(certificate_info, output)
Beispiel #7
0
 def test_certificate_info_for_user_with_course_modes(self, allow_certificate, whitelisted, grade, mode, output):
     """
     Verify that certificate_info_for_user works with course modes.
     """
     user = UserFactory.create()
     user.profile.allow_certificate = allow_certificate
     user.profile.save()
     _ = CourseEnrollment.enroll(user, self.instructor_paced_course.id, mode)
     certificate_info = certificate_info_for_user(
         user, self.instructor_paced_course.id, grade,
         whitelisted, user_certificate=None
     )
     self.assertEqual(certificate_info, output)
Beispiel #8
0
 def _user_certificate_info(self, user, context, course_grade, bulk_certs):
     """
     Returns the course certification information for the given user.
     """
     is_whitelisted = user.id in bulk_certs.whitelisted_user_ids
     certificate_info = certificate_info_for_user(
         user,
         context.course_id,
         course_grade.letter_grade,
         is_whitelisted,
         bulk_certs.certificates_by_user.get(user.id),
     )
     return certificate_info
Beispiel #9
0
    def test_certificate_info_for_user(self, whitelisted, grade, output):
        """
        Verify that certificate_info_for_user works.
        """
        student = UserFactory()

        # for instructor paced course
        certificate_info = certificate_info_for_user(
            student,
            self.instructor_paced_course.id,
            grade,
            whitelisted,
            user_certificate=None)
        assert certificate_info == output

        # for self paced course
        certificate_info = certificate_info_for_user(student,
                                                     self.self_paced_course.id,
                                                     grade,
                                                     whitelisted,
                                                     user_certificate=None)
        assert certificate_info == output
Beispiel #10
0
    def test_certificate_info_for_user_when_grade_changes(self, allow_certificate, whitelisted, grade, output):
        """
        Verify that certificate_info_for_user works as expect in scenario when grading of problems
        changes after certificates already generated. In such scenario `Certificate delivered` should not depend
        on student's eligibility to get certificates since in above scenario eligibility can change over period
        of time.
        """
        student = UserFactory()
        student.profile.allow_certificate = allow_certificate
        student.profile.save()

        certificate1 = GeneratedCertificateFactory.create(
            user=student,
            course_id=self.instructor_paced_course.id,
            status=CertificateStatuses.downloadable,
            mode='honor'
        )

        certificate2 = GeneratedCertificateFactory.create(
            user=student,
            course_id=self.self_paced_course.id,
            status=CertificateStatuses.downloadable,
            mode='honor'
        )

        # for instructor paced course
        certificate_info = certificate_info_for_user(
            student, self.instructor_paced_course.id, grade,
            whitelisted, certificate1
        )
        self.assertEqual(certificate_info, output)

        # for self paced course
        certificate_info = certificate_info_for_user(
            student, self.self_paced_course.id, grade,
            whitelisted, certificate2
        )
        self.assertEqual(certificate_info, output)
Beispiel #11
0
    def test_certificate_info_for_user_with_course_modes(
            self, whitelisted, grade, mode, output):
        """
        Verify that certificate_info_for_user works with course modes.
        """
        user = UserFactory.create()

        _ = CourseEnrollment.enroll(user, self.instructor_paced_course.id,
                                    mode)
        certificate_info = certificate_info_for_user(
            user,
            self.instructor_paced_course.id,
            grade,
            whitelisted,
            user_certificate=None)
        assert certificate_info == output
Beispiel #12
0
    def test_certificate_info_for_user(self, allow_certificate, whitelisted,
                                       grade, output):
        """
        Verify that certificate_info_for_user works.
        """
        student = UserFactory()
        _ = CourseFactory.create(org='edx',
                                 number='verified',
                                 display_name='Verified Course')
        student.profile.allow_certificate = allow_certificate
        student.profile.save()

        certificate_info = certificate_info_for_user(student,
                                                     grade,
                                                     whitelisted,
                                                     user_certificate=None)
        self.assertEqual(certificate_info, output)
Beispiel #13
0
    def test_certificate_info_for_user_when_grade_changes(self, allow_certificate, whitelisted, grade, output):
        """
        Verify that certificate_info_for_user works as expect in scenario when grading of problems
        changes after certificates already generated. In such scenario `Certificate delivered` should not depend
        on student's eligibility to get certificates since in above scenario eligibility can change over period
        of time.
        """
        student = UserFactory()
        course = CourseFactory.create(org='edx', number='verified', display_name='Verified Course')
        student.profile.allow_certificate = allow_certificate
        student.profile.save()

        certificate = GeneratedCertificateFactory.create(
            user=student,
            course_id=course.id,
            status=CertificateStatuses.downloadable,
            mode='honor'
        )
        certificate_info = certificate_info_for_user(student, grade, whitelisted, certificate)
        self.assertEqual(certificate_info, output)
Beispiel #14
0
    def test_certificate_info_for_user_when_grade_changes(
            self, allow_certificate, whitelisted, grade, output):
        """
        Verify that certificate_info_for_user works as expect in scenario when grading of problems
        changes after certificates already generated. In such scenario `Certificate delivered` should not depend
        on student's eligibility to get certificates since in above scenario eligibility can change over period
        of time.
        """
        student = UserFactory()
        course = CourseFactory.create(org='edx',
                                      number='verified',
                                      display_name='Verified Course')
        student.profile.allow_certificate = allow_certificate
        student.profile.save()

        certificate = GeneratedCertificateFactory.create(
            user=student,
            course_id=course.id,
            status=CertificateStatuses.downloadable,
            mode='honor')
        certificate_info = certificate_info_for_user(student, grade,
                                                     whitelisted, certificate)
        self.assertEqual(certificate_info, output)
Beispiel #15
0
def upload_user_grades_csv(_xmodule_instance_args, _entry_id, course_id, _task_input, action_name):  # pylint: disable=too-many-statements
    """
    For a given `course_id`, for given usernames generates a grades CSV file,
    and store using a `ReportStore`. Once created, the files can
    be accessed by instantiating another `ReportStore` (via
    `ReportStore.from_config()`) and calling `link_for()` on it.

    Unenrolled users and unknown usernames are stored in *_err_*.csv
    This task is very close to the .upload_grades_csv from instructor_tasks.task_helper
    The difference is that we filter enrolled students against requested usernames and
    we push info about this into PLP
    """
    start_time = time()
    start_date = datetime.now(UTC)
    status_interval = 100
    fmt = u'Task: {task_id}, InstructorTask ID: {entry_id}, Course: {course_id}, Input: {task_input}'
    task_info_string = fmt.format(
        task_id=_xmodule_instance_args.get('task_id') if _xmodule_instance_args is not None else None,
        entry_id=_entry_id,
        course_id=course_id,
        task_input=_task_input
    )
    TASK_LOG.info(u'%s, Task type: %s, Starting task execution', task_info_string, action_name)

    extended_kwargs_id = _task_input.get("extended_kwargs_id")
    extended_kwargs = InstructorTaskExtendedKwargs.get_kwargs_for_id(extended_kwargs_id)
    usernames = extended_kwargs.get("usernames", None)

    err_rows = [["id", "username", "error_msg"]]
    if usernames is None:
        message = "Error occured during edx task execution: no usersnames in InstructorTaskExtendedKwargs."
        TASK_LOG.error(u'%s, Task type: %s, ' + message, task_info_string)
        err_rows.append(["-1", "__", message])
        usernames = []

    enrolled_students = CourseEnrollment.objects.users_enrolled_in(course_id)
    enrolled_students = enrolled_students.filter(username__in=usernames)
    total_enrolled_students = enrolled_students.count()
    requester_id = _task_input.get("requester_id")
    task_progress = TaskProgress(action_name, total_enrolled_students, start_time)

    course = get_course_by_id(course_id)
    course_is_cohorted = is_course_cohorted(course.id)
    teams_enabled = course.teams_enabled
    cohorts_header = ['Cohort Name'] if course_is_cohorted else []
    teams_header = ['Team Name'] if teams_enabled else []

    experiment_partitions = get_split_user_partitions(course.user_partitions)
    group_configs_header = [u'Experiment Group ({})'.format(partition.name) for partition in experiment_partitions]

    certificate_info_header = ['Certificate Eligible', 'Certificate Delivered', 'Certificate Type']
    certificate_whitelist = CertificateWhitelist.objects.filter(course_id=course_id, whitelist=True)
    whitelisted_user_ids = [entry.user_id for entry in certificate_whitelist]

    # Loop over all our students and build our CSV lists in memory
    rows = []
    current_step = {'step': 'Calculating Grades'}

    TASK_LOG.info(
        u'%s, Task type: %s, Current step: %s, Starting grade calculation for total students: %s',
        task_info_string,
        action_name,
        current_step,
        total_enrolled_students,
    )
    found_students = User.objects.filter(username__in=usernames)
    # Check invalid usernames
    if len(found_students)!= len(usernames):
        found_students_usernames = [x.username for x in found_students]
        for u in usernames:
            if u not in found_students_usernames:
                err_rows.append([-1, u, "invalid_username"])
    # Check not enrolled requested students
    if found_students != enrolled_students:
        diff = found_students.exclude(id__in=enrolled_students)
        for u in diff:
            if u in diff:
                err_rows.append([u.id, u.username, "enrollment_for_username_not_found"])

    total_enrolled_students = enrolled_students.count()
    student_counter = 0
    TASK_LOG.info(
        u'%s, Task type: %s, Current step: %s, Starting grade calculation for total students: %s',
        task_info_string,
        action_name,
        current_step,

        total_enrolled_students
    )

    graded_assignments = course.grading.graded_assignments(course_id)
    grade_header = course.grading.grade_header(graded_assignments)

    rows.append(
        ["Student ID", "Email", "Username", "Last Name", "First Name", "Second Name", "Grade", "Grade Percent"] +
        grade_header +
        cohorts_header +
        group_configs_header +
        teams_header +
        ['Enrollment Track', 'Verification Status'] +
        certificate_info_header
    )
    for student, course_grade, err_msg in CourseGradeFactory().iter(course, enrolled_students):
        # Periodically update task status (this is a cache write)
        if task_progress.attempted % status_interval == 0:
            task_progress.update_task_state(extra_meta=current_step)
        task_progress.attempted += 1

        # Now add a log entry after each student is graded to get a sense
        # of the task's progress
        student_counter += 1
        TASK_LOG.info(
            u'%s, Task type: %s, Current step: %s, Grade calculation in-progress for students: %s/%s',
            task_info_string,
            action_name,
            current_step,
            student_counter,
            total_enrolled_students
        )

        if not course_grade:
            # An empty course_grade means we failed to grade a student.
            task_progress.failed += 1
            err_rows.append([student.id, student.username, err_msg])
            continue

        # We were able to successfully grade this student for this course.
        task_progress.succeeded += 1

        cohorts_group_name = []
        if course_is_cohorted:
            group = get_cohort(student, course_id, assign=False)
            cohorts_group_name.append(group.name if group else '')

        group_configs_group_names = []
        for partition in experiment_partitions:
            group = PartitionService(course_id).get_group(student, partition, assign=False)
            group_configs_group_names.append(group.name if group else '')

        team_name = []
        if teams_enabled:
            try:
                membership = CourseTeamMembership.objects.get(user=student, team__course_id=course_id)
                team_name.append(membership.team.name)
            except CourseTeamMembership.DoesNotExist:
                team_name.append('')

        enrollment_mode = CourseEnrollment.enrollment_mode_for_user(student, course_id)[0]
        verification_status = SoftwareSecurePhotoVerification.verification_status_for_user(
            student,
            course_id,
            enrollment_mode
        )
        certificate_info = certificate_info_for_user(
            student,
            course_id,
            course_grade.letter_grade,
            student.id in whitelisted_user_ids
        )
        second_name = ''
        try:
            up = UserProfile.objects.get(user=student)
            if up.goals:
                second_name = json.loads(up.goals).get('second_name', '')
        except ValueError:
            pass
        if certificate_info[0] == 'Y':
            TASK_LOG.info(
                u'Student is marked eligible_for_certificate'
                u'(user=%s, course_id=%s, grade_percent=%s gradecutoffs=%s, allow_certificate=%s, is_whitelisted=%s)',
                student,
                course_id,
                course_grade.percent,
                course.grade_cutoffs,
                student.profile.allow_certificate,
                student.id in whitelisted_user_ids
            )

        grade_results = course.grading.grade_results(graded_assignments, course_grade)

        grade_results = list(chain.from_iterable(grade_results))

        rows.append(
            [student.id, student.email, student.username, student.last_name, student.first_name,
             second_name, course_grade.percent, course_grade.percent*100] +
            grade_results + cohorts_group_name + group_configs_group_names + team_name +
            [enrollment_mode] + [verification_status] + certificate_info
        )
    TASK_LOG.info(
        u'%s, Task type: %s, Current step: %s, Grade calculation completed for students: %s/%s',
        task_info_string,
        action_name,
        current_step,
        student_counter,
        total_enrolled_students
    )

    # By this point, we've got the rows we're going to stuff into our CSV files.
    current_step = {'step': 'Uploading CSVs'}
    task_progress.update_task_state(extra_meta=current_step)
    TASK_LOG.info(u'%s, Task type: %s, Current step: %s', task_info_string, action_name, current_step)

    # Perform the actual upload
    custom_grades_download = get_custom_grade_config()

    report_hash_unique_hash = hex(random.getrandbits(32))[2:]
    report_name = 'plp_grade_users_report_{}_id_{}'.format(report_hash_unique_hash, requester_id)
    err_report_name = 'plp_grade_users_report_err_{}_id_{}'.format(report_hash_unique_hash, requester_id)
    upload_csv_to_report_store(rows, report_name, course_id, start_date, config_name=custom_grades_download)

    # If there are any error rows (don't count the header), write them out as well
    has_errors = len(err_rows) > 1
    if has_errors:
        upload_csv_to_report_store(err_rows, err_report_name, course_id, start_date, config_name=custom_grades_download)

    callback_url = _task_input.get("callback_url", None)

    if callback_url:
        report_store = ReportStore.from_config(config_name=custom_grades_download)
        files_urls_pairs = report_store.links_for(course_id)
        find_by_name = lambda name: [url for filename, url in files_urls_pairs if name in filename][0]
        try:
            csv_url = find_by_name(report_name)
            csv_err_url = find_by_name(err_report_name) if has_errors else None
            PlpApiClient().push_grade_api_result(callback_url, csv_url, csv_err_url)
        except Exception as e:
            TASK_LOG.error("Failed push to PLP:{}".format(str(e)))

    # One last update before we close out...
    TASK_LOG.info(u'%s, Task type: %s, Finalizing grade task', task_info_string, action_name)
    return task_progress.update_task_state(extra_meta=current_step)