Example #1
0
 def _verify_no_automatic_cohorting(self):
     """ Check that upgrading self.user to verified does not move them into a cohort. """
     self._enroll_in_course()
     self.assertIsNone(get_cohort(self.user, self.course.id, assign=False))
     self._upgrade_to_verified()
     self.assertIsNone(get_cohort(self.user, self.course.id, assign=False))
     self.assertEqual(0, self.mocked_celery_task.call_count)
    def test_cohorting_enabled_multiple_random_cohorts(self):
        """
        If the VerifiedTrackCohortedCourse feature is enabled for a course, and the course is cohorted
        with > 1 random cohorts, the learner is randomly assigned to one of the random
        cohorts when in the audit track.
        """
        # Enable cohorting, and create the verified cohort.
        self._enable_cohorting()
        self._create_verified_cohort()
        # Create two random cohorts.
        self._create_named_random_cohort("Random 1")
        self._create_named_random_cohort("Random 2")
        # Enable verified track cohorting feature
        self._enable_verified_track_cohorting()

        self._enroll_in_course()
        assert get_cohort(self.user, self.course.id,
                          assign=False).name in ['Random 1', 'Random 2']
        self._upgrade_enrollment()
        assert DEFAULT_VERIFIED_COHORT_NAME == get_cohort(self.user,
                                                          self.course.id,
                                                          assign=False).name

        self._unenroll()
        self._reenroll()
        assert get_cohort(self.user, self.course.id,
                          assign=False).name in ['Random 1', 'Random 2']
Example #3
0
 def _verify_no_automatic_cohorting(self):
     """ Check that upgrading self.user to verified does not move them into a cohort. """
     self._enroll_in_course()
     self.assertIsNone(get_cohort(self.user, self.course.id, assign=False))
     self._upgrade_enrollment()
     self.assertIsNone(get_cohort(self.user, self.course.id, assign=False))
     self.assertEqual(0, self.mocked_celery_task.call_count)
Example #4
0
    def test_custom_default_cohort_name(self):
        """
        Test that enrolling and un-enrolling works correctly when the single cohort
        of type random has a different name from "Default Group".
        """
        random_cohort_name = "custom random cohort"
        self._enable_cohorting()
        self._create_verified_cohort()
        default_cohort = self._create_named_random_cohort(random_cohort_name)
        self._enable_verified_track_cohorting()
        self._enroll_in_course()
        self.assertEqual(random_cohort_name, get_cohort(self.user, self.course.id, assign=False).name)
        self._upgrade_enrollment()
        self.assertEqual(DEFAULT_VERIFIED_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)

        # Un-enroll from the course. The learner stays in the verified cohort, but is no longer active.
        self._unenroll()
        self.assertEqual(DEFAULT_VERIFIED_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)

        # Change the name of the "default" cohort.
        modified_cohort_name = "renamed random cohort"
        default_cohort.name = modified_cohort_name
        default_cohort.save()

        # Re-enroll in the course, which will downgrade the learner to audit.
        self._reenroll()
        self.assertEqual(modified_cohort_name, get_cohort(self.user, self.course.id, assign=False).name)
        self._upgrade_enrollment()
        self.assertEqual(DEFAULT_VERIFIED_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)
Example #5
0
    def test_custom_default_cohort_name(self):
        """
        Test that enrolling and un-enrolling works correctly when the single cohort
        of type random has a different name from "Default Group".
        """
        random_cohort_name = "custom random cohort"
        self._enable_cohorting()
        self._create_verified_cohort()
        default_cohort = self._create_named_random_cohort(random_cohort_name)
        self._enable_verified_track_cohorting()
        self._enroll_in_course()
        assert random_cohort_name == get_cohort(self.user, self.course.id, assign=False).name
        self._upgrade_enrollment()
        assert DEFAULT_VERIFIED_COHORT_NAME == get_cohort(self.user, self.course.id, assign=False).name

        # Un-enroll from the course. The learner stays in the verified cohort, but is no longer active.
        self._unenroll()
        assert DEFAULT_VERIFIED_COHORT_NAME == get_cohort(self.user, self.course.id, assign=False).name

        # Change the name of the "default" cohort.
        modified_cohort_name = "renamed random cohort"
        default_cohort.name = modified_cohort_name
        default_cohort.save()

        # Re-enroll in the course, which will downgrade the learner to audit.
        self._reenroll()
        assert modified_cohort_name == get_cohort(self.user, self.course.id, assign=False).name
        self._upgrade_enrollment()
        assert DEFAULT_VERIFIED_COHORT_NAME == get_cohort(self.user, self.course.id, assign=False).name
    def test_automatic_cohorting_enabled(self, upgrade_mode):
        """
        If the VerifiedTrackCohortedCourse feature is enabled for a course (with course cohorting enabled
        with an existing verified cohort), enrollment in the verified track automatically moves learners
        into the verified cohort.
        """
        # Enable cohorting and create a verified cohort.
        self._enable_cohorting()
        self._create_verified_cohort()
        # Enable verified track cohorting feature
        self._enable_verified_track_cohorting()
        assert VerifiedTrackCohortedCourse.is_verified_track_cohort_enabled(
            self.course.id)
        self._enroll_in_course()

        assert 2 == self.mocked_celery_task.call_count
        assert DEFAULT_COHORT_NAME == get_cohort(self.user,
                                                 self.course.id,
                                                 assign=False).name

        self._upgrade_enrollment(upgrade_mode)
        assert 4 == self.mocked_celery_task.call_count
        assert DEFAULT_VERIFIED_COHORT_NAME == get_cohort(self.user,
                                                          self.course.id,
                                                          assign=False).name
 def _verify_no_automatic_cohorting(self):
     """ Check that upgrading self.user to verified does not move them into a cohort. """
     self._enroll_in_course()
     assert get_cohort(self.user, self.course.id, assign=False) is None
     self._upgrade_enrollment()
     assert get_cohort(self.user, self.course.id, assign=False) is None
     assert 0 == self.mocked_celery_task.call_count
    def test_unenrolled(self):
        """
        Test that un-enrolling and re-enrolling works correctly. This is important because usually
        learners maintain their previously assigned cohort on re-enrollment.
        """
        # Enable verified track cohorting feature and enroll in the verified track
        self._enable_cohorting()
        self._create_verified_cohort()
        self._enable_verified_track_cohorting()
        self._enroll_in_course()
        self._upgrade_enrollment()
        assert DEFAULT_VERIFIED_COHORT_NAME == get_cohort(self.user,
                                                          self.course.id,
                                                          assign=False).name

        # Un-enroll from the course and then re-enroll
        self._unenroll()
        assert DEFAULT_VERIFIED_COHORT_NAME == get_cohort(self.user,
                                                          self.course.id,
                                                          assign=False).name
        self._reenroll()
        assert DEFAULT_COHORT_NAME == get_cohort(self.user,
                                                 self.course.id,
                                                 assign=False).name
        self._upgrade_enrollment()
        assert DEFAULT_VERIFIED_COHORT_NAME == get_cohort(self.user,
                                                          self.course.id,
                                                          assign=False).name
Example #9
0
def sync_cohort_with_mode(self, course_id, user_id, verified_cohort_name, default_cohort_name):
    """
    If the learner's mode does not match their assigned cohort, move the learner into the correct cohort.
    It is assumed that this task is only initiated for courses that are using the
    Automatic Verified Track Cohorting MVP feature. It is also assumed that before
    initiating this task, verification has been done to ensure that the course is
    cohorted and has an appropriately named "verified" cohort.
    """
    course_key = CourseKey.from_string(course_id)
    user = User.objects.get(id=user_id)
    try:
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        # Note that this will enroll the user in the default cohort on initial enrollment.
        # That's good because it will force creation of the default cohort if necessary.
        try:
            current_cohort = get_cohort(user, course_key)
        except IntegrityError as integrity_error:
            # It is quite common that get_cohort will throw an IntegrityError. This happens
            # when 2 celery workers are both handling enrollment change events for the same user
            # (for example, if the enrollment mode goes from None -> Audit -> Honor); if the user
            # was not previously in a cohort, calling get_cohort will result in a cohort assignment.
            LOGGER.info(
                "HANDLING_INTEGRITY_ERROR: IntegrityError encountered for course '%s' and user '%s': %s",
                course_id, user.id, unicode(integrity_error)
            )
            current_cohort = get_cohort(user, course_key)

        verified_cohort = get_cohort_by_name(course_key, verified_cohort_name)

        if enrollment.mode == CourseMode.VERIFIED and (current_cohort.id != verified_cohort.id):
            LOGGER.info(
                "MOVING_TO_VERIFIED: Moving user '%s' to the verified cohort '%s' for course '%s'",
                user.id, verified_cohort.name, course_id
            )
            add_user_to_cohort(verified_cohort, user.username)
        elif enrollment.mode != CourseMode.VERIFIED and current_cohort.id == verified_cohort.id:
            default_cohort = get_cohort_by_name(course_key, default_cohort_name)
            LOGGER.info(
                "MOVING_TO_DEFAULT: Moving user '%s' to the default cohort '%s' for course '%s'",
                user.id, default_cohort.name, course_id
            )
            add_user_to_cohort(default_cohort, user.username)
        else:
            LOGGER.info(
                "NO_ACTION_NECESSARY: No action necessary for user '%s' in course '%s' and enrollment mode '%s'. "
                "The user is already in cohort '%s'.",
                user.id, course_id, enrollment.mode, current_cohort.name
            )
    except Exception as exc:
        LOGGER.warning(
            "SYNC_COHORT_WITH_MODE_RETRY: Exception encountered for course '%s' and user '%s': %s",
            course_id, user.id, unicode(exc)
        )
        raise self.retry(exc=exc)
Example #10
0
def sync_cohort_with_mode(self, course_id, user_id, verified_cohort_name,
                          default_cohort_name):
    """
    If the learner's mode does not match their assigned cohort, move the learner into the correct cohort.
    It is assumed that this task is only initiated for courses that are using the
    Automatic Verified Track Cohorting MVP feature. It is also assumed that before
    initiating this task, verification has been done to ensure that the course is
    cohorted and has an appropriately named "verified" cohort.
    """
    course_key = CourseKey.from_string(course_id)
    user = User.objects.get(id=user_id)
    try:
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        # Note that this will enroll the user in the default cohort on initial enrollment.
        # That's good because it will force creation of the default cohort if necessary.
        try:
            current_cohort = get_cohort(user, course_key)
        except IntegrityError as integrity_error:
            # It is quite common that get_cohort will throw an IntegrityError. This happens
            # when 2 celery workers are both handling enrollment change events for the same user
            # (for example, if the enrollment mode goes from None -> Audit -> Honor); if the user
            # was not previously in a cohort, calling get_cohort will result in a cohort assignment.
            LOGGER.info(
                "HANDLING_INTEGRITY_ERROR: IntegrityError encountered for course '%s' and user '%s': %s",
                course_id, user.id, unicode(integrity_error))
            current_cohort = get_cohort(user, course_key)

        verified_cohort = get_cohort_by_name(course_key, verified_cohort_name)

        if enrollment.mode == CourseMode.VERIFIED and (current_cohort.id !=
                                                       verified_cohort.id):
            LOGGER.info(
                "MOVING_TO_VERIFIED: Moving user '%s' to the verified cohort '%s' for course '%s'",
                user.id, verified_cohort.name, course_id)
            add_user_to_cohort(verified_cohort, user.username)
        elif enrollment.mode != CourseMode.VERIFIED and current_cohort.id == verified_cohort.id:
            default_cohort = get_cohort_by_name(course_key,
                                                default_cohort_name)
            LOGGER.info(
                "MOVING_TO_DEFAULT: Moving user '%s' to the default cohort '%s' for course '%s'",
                user.id, default_cohort.name, course_id)
            add_user_to_cohort(default_cohort, user.username)
        else:
            LOGGER.info(
                "NO_ACTION_NECESSARY: No action necessary for user '%s' in course '%s' and enrollment mode '%s'. "
                "The user is already in cohort '%s'.", user.id, course_id,
                enrollment.mode, current_cohort.name)
    except Exception as exc:
        LOGGER.warning(
            "SYNC_COHORT_WITH_MODE_RETRY: Exception encountered for course '%s' and user '%s': %s",
            course_id, user.id, unicode(exc))
        raise self.retry(exc=exc)
Example #11
0
def has_valid_timer(user, course_id):
    #check if course has global timer
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course_overview = CourseOverview.objects.get(id=course_key)
    course_extra = json.loads(course_overview.course_extra)['is_cut_off']

    #Check use cohort CohortMembership
    if get_cohort(user, course_key):
        user_cohort_timer = get_cohort(user, course_key).tma_timer
    else:
        user_cohort_timer = None

    #check user course enrollement
    if CourseEnrollment.is_enrolled(user, course_key):
        user_register_date = CourseEnrollment.get_enrollment(
            user, course_key).created
        if user_register_date > course_overview.start:
            user_enter_date = user_register_date
        else:
            user_enter_date = course_overview.start
    else:
        user_enter_date = None

    user_access = True
    #Timer is activated
    user_timer_date = None
    if course_extra['_is']:
        #Check for cohort timer
        if user_cohort_timer is not None:
            user_timer_date = user_cohort_timer
        #If no cohort_timer check for course timer
        else:
            if 'timer_type' in course_extra and course_extra[
                    'timer_type'] == 'days_timer' and user_enter_date is not None:
                user_timer_date = user_enter_date + timedelta(
                    days=course_extra['timer_days_value'])

            elif 'timer_type' in course_extra and course_extra[
                    'timer_type'] == 'date_timer' and user_enter_date is not None:
                user_timer_date = datetime.strptime(
                    course_extra['timer_date_value'],
                    '%d-%m-%Y').replace(hour=11, minute=59)

    if user_timer_date is not None and datetime.now(
    ) > user_timer_date.replace(tzinfo=None):
        user_access = False

    return user_access
Example #12
0
def sync_cohort_with_mode(course_id, user_id, verified_cohort_name):
    """
    If the learner's mode does not match their assigned cohort, move the learner into the correct cohort.
    It is assumed that this task is only initiated for courses that are using the
    Automatic Verified Track Cohorting MVP feature. It is also assumed that before
    initiating this task, verification has been done to ensure that the course is
    cohorted and has an appropriately named "verified" cohort.
    """
    course_key = CourseKey.from_string(course_id)
    user = User.objects.get(id=user_id)
    enrollment = CourseEnrollment.get_enrollment(user, course_key)
    # Note that this will enroll the user in the default cohort on initial enrollment.
    # That's good because it will force creation of the default cohort if necessary.
    current_cohort = get_cohort(user, course_key)
    verified_cohort = get_cohort_by_name(course_key, verified_cohort_name)

    if enrollment.mode == CourseMode.VERIFIED and (current_cohort.id !=
                                                   verified_cohort.id):
        LOGGER.info(
            "MOVING_TO_VERIFIED: Moving user '%s' to the verified cohort for course '%s'",
            user.username, course_id)
        add_user_to_cohort(verified_cohort, user.username)
    elif enrollment.mode != CourseMode.VERIFIED and current_cohort.id == verified_cohort.id:
        LOGGER.info(
            "MOVING_TO_DEFAULT: Moving user '%s' to the default cohort for course '%s'",
            user.username, course_id)
        default_cohort = get_cohort_by_name(course_key, DEFAULT_COHORT_NAME)
        add_user_to_cohort(default_cohort, user.username)
    else:
        LOGGER.info(
            "NO_ACTION_NECESSARY: No action necessary for user '%s' in course '%s' and enrollment mode '%s'",
            user.username, course_id, enrollment.mode)
Example #13
0
    def load_assignment_data(self, request, suffix=''):
        enrollments = CourseEnrollment.objects.filter(course_id=self.course_id)
        names = {}
        turmas_professores = self.turmas_professores
        needs_save = False
        for enr in enrollments:
            cohort = get_cohort(enr.user,
                                self.course_id,
                                assign=False,
                                use_cached=True)
            if cohort and cohort.name == PROFS_COHORT:  # add missing profs
                user_id = str(enr.user.id)
                if user_id not in turmas_professores:
                    turmas_professores[user_id] = []
                    needs_save = True
                names[user_id] = enr.user.username
                if enr.user.profile.name:
                    names[user_id] = self.format_name(enr.user.profile.name)

        # clean up "old" teachers
        for prof in list(turmas_professores.keys()):
            if prof not in names:
                del turmas_professores[prof]
                needs_save = True
        if needs_save:
            self.save_turmas_professores(turmas_professores)
        return Response(
            json.dumps({
                'turmas_profs': turmas_professores,
                'turmas': super().get_cohorts(),
                'nomes': names
            }))
Example #14
0
 def is_staff(self):
     cohort = get_cohort(user_by_anonymous_id(
         self.xmodule_runtime.anonymous_student_id),
                         self.course_id,
                         assign=False,
                         use_cached=True)
     return super().is_staff or (cohort and cohort.name == PROFS_COHORT)
Example #15
0
def sync_cohort_with_mode(course_id, user_id, verified_cohort_name):
    """
    If the learner's mode does not match their assigned cohort, move the learner into the correct cohort.
    It is assumed that this task is only initiated for courses that are using the
    Automatic Verified Track Cohorting MVP feature. It is also assumed that before
    initiating this task, verification has been done to ensure that the course is
    cohorted and has an appropriately named "verified" cohort.
    """
    course_key = CourseKey.from_string(course_id)
    user = User.objects.get(id=user_id)
    enrollment = CourseEnrollment.get_enrollment(user, course_key)
    # Note that this will enroll the user in the default cohort on initial enrollment.
    # That's good because it will force creation of the default cohort if necessary.
    current_cohort = get_cohort(user, course_key)
    verified_cohort = get_cohort_by_name(course_key, verified_cohort_name)

    if enrollment.mode == CourseMode.VERIFIED and (current_cohort.id != verified_cohort.id):
        LOGGER.info(
            "MOVING_TO_VERIFIED: Moving user '%s' to the verified cohort for course '%s'", user.username, course_id
        )
        add_user_to_cohort(verified_cohort, user.username)
    elif enrollment.mode != CourseMode.VERIFIED and current_cohort.id == verified_cohort.id:
        LOGGER.info(
            "MOVING_TO_DEFAULT: Moving user '%s' to the default cohort for course '%s'", user.username, course_id
        )
        default_cohort = get_cohort_by_name(course_key, DEFAULT_COHORT_NAME)
        add_user_to_cohort(default_cohort, user.username)
    else:
        LOGGER.info(
            "NO_ACTION_NECESSARY: No action necessary for user '%s' in course '%s' and enrollment mode '%s'",
            user.username, course_id, enrollment.mode
        )
Example #16
0
 def test_custom_verified_cohort_name(self):
     CUSTOM_COHORT_NAME = 'special verified cohort'
     self._enable_cohorting()
     self._create_verified_cohort(name=CUSTOM_COHORT_NAME)
     self._enable_verified_track_cohorting(cohort_name=CUSTOM_COHORT_NAME)
     self._enroll_in_course()
     self._upgrade_to_verified()
     self.assertEqual(CUSTOM_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)
Example #17
0
 def _user_cohort_group_names(self, user, context):
     """
     Returns a list of names of cohort groups in which the given user
     belongs.
     """
     cohort_group_names = []
     if context.cohorts_enabled:
         group = get_cohort(user, context.course_id, assign=False, use_cached=True)
         cohort_group_names.append(group.name if group else '')
     return cohort_group_names
Example #18
0
 def test_custom_verified_cohort_name(self):
     """
     Test that enrolling in verified works correctly when the "verified cohort" has a custom name.
     """
     custom_cohort_name = 'special verified cohort'
     self._enable_cohorting()
     self._create_verified_cohort(name=custom_cohort_name)
     self._enable_verified_track_cohorting(cohort_name=custom_cohort_name)
     self._enroll_in_course()
     self._upgrade_enrollment()
     self.assertEqual(custom_cohort_name, get_cohort(self.user, self.course.id, assign=False).name)
Example #19
0
    def test_automatic_cohorting_enabled(self, upgrade_mode):
        """
        If the VerifiedTrackCohortedCourse feature is enabled for a course (with course cohorting enabled
        with an existing verified cohort), enrollment in the verified track automatically moves learners
        into the verified cohort.
        """
        # Enable cohorting and create a verified cohort.
        self._enable_cohorting()
        self._create_verified_cohort()
        # Enable verified track cohorting feature
        self._enable_verified_track_cohorting()
        self.assertTrue(VerifiedTrackCohortedCourse.is_verified_track_cohort_enabled(self.course.id))
        self._enroll_in_course()

        self.assertEqual(2, self.mocked_celery_task.call_count)
        self.assertEqual(DEFAULT_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)

        self._upgrade_enrollment(upgrade_mode)
        self.assertEqual(4, self.mocked_celery_task.call_count)
        self.assertEqual(DEFAULT_VERIFIED_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)
Example #20
0
 def in_cohort(user):
     if cohorted:
         cohort = get_cohort(user,
                             course_key,
                             assign=False,
                             use_cached=True)
         if not cohort or cohort.name not in valid_cohorts or (
                 self.cohort and cohort.name != self.cohort):
             # skip this one if not on the requested cohort or has no cohort (instructor)
             return False
     return True
Example #21
0
    def test_unenrolled(self):
        """
        Test that un-enrolling and re-enrolling works correctly. This is important because usually
        learners maintain their previously assigned cohort on re-enrollment.
        """
        # Enable verified track cohorting feature and enroll in the verified track
        self._enable_cohorting()
        self._create_verified_cohort()
        self._enable_verified_track_cohorting()
        self._enroll_in_course()
        self._upgrade_enrollment()
        self.assertEqual(DEFAULT_VERIFIED_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)

        # Un-enroll from the course and then re-enroll
        self._unenroll()
        self.assertEqual(DEFAULT_VERIFIED_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)
        self._reenroll()
        self.assertEqual(DEFAULT_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)
        self._upgrade_enrollment()
        self.assertEqual(DEFAULT_VERIFIED_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)
Example #22
0
 def test_custom_verified_cohort_name(self):
     """
     Test that enrolling in verified works correctly when the "verified cohort" has a custom name.
     """
     custom_cohort_name = 'special verified cohort'
     self._enable_cohorting()
     self._create_verified_cohort(name=custom_cohort_name)
     self._enable_verified_track_cohorting(cohort_name=custom_cohort_name)
     self._enroll_in_course()
     self._upgrade_enrollment()
     assert custom_cohort_name == get_cohort(self.user, self.course.id, assign=False).name
Example #23
0
    def setUp(self):
        super(UniversityIDFormTest, self).setUp()
        self.course = CourseFactory.create()
        set_course_cohorted(course_key=self.course.id, cohorted=True)

        # Initialize the default group!
        default_cohort = get_cohort(user=self.user, course_key=self.course.id)
        self.assertEquals(default_cohort.name, DEFAULT_COHORT_NAME)  # Sanity-check

        self.cohort, _created = CourseUserGroup.create(
            name='Cohort_A',
            course_id=self.course.id,
        )
Example #24
0
    def delete(self, request, *args, **kwargs):
        obj = self.get_object() # University ID object
        obj.can_edit = True
        obj.save()

        cohort = get_cohort(obj.user, obj.course_key, assign=False)
        try:
            remove_user_from_cohort(cohort, obj.user.username)
        except ValueError:
            # If user not already present in this cohort.
            pass

        return super(UniversityIDDeleteView, self).delete(request, *args, **kwargs)
Example #25
0
    def delete(self, request, *args, **kwargs):
        obj = self.get_object()  # University ID object
        obj.can_edit = True
        obj.save()

        cohort = get_cohort(obj.user, obj.course_key, assign=False)
        try:
            remove_user_from_cohort(cohort, obj.user.username)
        except ValueError:
            # If user not already present in this cohort.
            pass

        return super(UniversityIDDeleteView,
                     self).delete(request, *args, **kwargs)
Example #26
0
    def test_cohorting_enabled_multiple_random_cohorts(self):
        """
        If the VerifiedTrackCohortedCourse feature is enabled for a course, and the course is cohorted
        with > 1 random cohorts, the learner is randomly assigned to one of the random
        cohorts when in the audit track.
        """
        # Enable cohorting, and create the verified cohort.
        self._enable_cohorting()
        self._create_verified_cohort()
        # Create two random cohorts.
        self._create_named_random_cohort("Random 1")
        self._create_named_random_cohort("Random 2")
        # Enable verified track cohorting feature
        self._enable_verified_track_cohorting()

        self._enroll_in_course()
        self.assertIn(get_cohort(self.user, self.course.id, assign=False).name, ["Random 1", "Random 2"])
        self._upgrade_enrollment()
        self.assertEqual(DEFAULT_VERIFIED_COHORT_NAME, get_cohort(self.user, self.course.id, assign=False).name)

        self._unenroll()
        self._reenroll()
        self.assertIn(get_cohort(self.user, self.course.id, assign=False).name, ["Random 1", "Random 2"])
Example #27
0
def sync_cohort_with_mode(self, course_id, user_id, verified_cohort_name, default_cohort_name):
    """
    If the learner's mode does not match their assigned cohort, move the learner into the correct cohort.
    It is assumed that this task is only initiated for courses that are using the
    Automatic Verified Track Cohorting MVP feature. It is also assumed that before
    initiating this task, verification has been done to ensure that the course is
    cohorted and has an appropriately named "verified" cohort.
    """
    course_key = CourseKey.from_string(course_id)
    user = User.objects.get(id=user_id)
    try:
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        # Note that this will enroll the user in the default cohort on initial enrollment.
        # That's good because it will force creation of the default cohort if necessary.
        current_cohort = get_cohort(user, course_key)

        verified_cohort = get_cohort_by_name(course_key, verified_cohort_name)

        acceptable_modes = {CourseMode.VERIFIED, CourseMode.CREDIT_MODE}
        if enrollment.mode in acceptable_modes and (current_cohort.id != verified_cohort.id):
            LOGGER.info(
                u"MOVING_TO_VERIFIED: Moving user '%s' to the verified cohort '%s' for course '%s'",
                user.id, verified_cohort.name, course_id
            )
            add_user_to_cohort(verified_cohort, user.username)
        elif enrollment.mode not in acceptable_modes and current_cohort.id == verified_cohort.id:
            default_cohort = get_cohort_by_name(course_key, default_cohort_name)
            LOGGER.info(
                u"MOVING_TO_DEFAULT: Moving user '%s' to the default cohort '%s' for course '%s'",
                user.id, default_cohort.name, course_id
            )
            add_user_to_cohort(default_cohort, user.username)
        else:
            LOGGER.info(
                u"NO_ACTION_NECESSARY: No action necessary for user '%s' in course '%s' and enrollment mode '%s'. "
                u"The user is already in cohort '%s'.",
                user.id, course_id, enrollment.mode, current_cohort.name
            )
    except Exception as exc:
        LOGGER.warning(
            u"SYNC_COHORT_WITH_MODE_RETRY: Exception encountered for course '%s' and user '%s': %s",
            course_id, user.id, six.text_type(exc)
        )
        raise self.retry(exc=exc)
Example #28
0
def sync_cohort_with_mode(self, course_id, user_id, verified_cohort_name, default_cohort_name):
    """
    If the learner's mode does not match their assigned cohort, move the learner into the correct cohort.
    It is assumed that this task is only initiated for courses that are using the
    Automatic Verified Track Cohorting MVP feature. It is also assumed that before
    initiating this task, verification has been done to ensure that the course is
    cohorted and has an appropriately named "verified" cohort.
    """
    course_key = CourseKey.from_string(course_id)
    user = User.objects.get(id=user_id)
    try:
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        # Note that this will enroll the user in the default cohort on initial enrollment.
        # That's good because it will force creation of the default cohort if necessary.
        current_cohort = get_cohort(user, course_key)

        verified_cohort = get_cohort_by_name(course_key, verified_cohort_name)

        acceptable_modes = {CourseMode.VERIFIED, CourseMode.CREDIT_MODE}
        if enrollment.mode in acceptable_modes and (current_cohort.id != verified_cohort.id):
            LOGGER.info(
                u"MOVING_TO_VERIFIED: Moving user '%s' to the verified cohort '%s' for course '%s'",
                user.id, verified_cohort.name, course_id
            )
            add_user_to_cohort(verified_cohort, user.username)
        elif enrollment.mode not in acceptable_modes and current_cohort.id == verified_cohort.id:
            default_cohort = get_cohort_by_name(course_key, default_cohort_name)
            LOGGER.info(
                u"MOVING_TO_DEFAULT: Moving user '%s' to the default cohort '%s' for course '%s'",
                user.id, default_cohort.name, course_id
            )
            add_user_to_cohort(default_cohort, user.username)
        else:
            LOGGER.info(
                u"NO_ACTION_NECESSARY: No action necessary for user '%s' in course '%s' and enrollment mode '%s'. "
                u"The user is already in cohort '%s'.",
                user.id, course_id, enrollment.mode, current_cohort.name
            )
    except Exception as exc:
        LOGGER.warning(
            u"SYNC_COHORT_WITH_MODE_RETRY: Exception encountered for course '%s' and user '%s': %s",
            course_id, user.id, six.text_type(exc)
        )
        raise self.retry(exc=exc)
Example #29
0
    def test_users_with_multiple_cohorts_cleanup(self):
        """
        Test that user which have been added in multiple cohorts of a course,
        can get cohorts without error after running cohorts cleanup command
        """
        # set two auto_cohort_groups for both courses
        config_course_cohorts(
            self.course1, [], cohorted=True, auto_cohort_groups=["Course1AutoGroup1", "Course1AutoGroup2"]
        )
        config_course_cohorts(
            self.course2, [], cohorted=True, auto_cohort_groups=["Course2AutoGroup1", "Course2AutoGroup2"]
        )

        # get the cohorts from the courses, which will cause auto cohorts to be created
        cohort_handler(self.request, unicode(self.course1.id))
        cohort_handler(self.request, unicode(self.course2.id))
        course_1_auto_cohort_1 = get_cohort_by_name(self.course1.id, "Course1AutoGroup1")
        course_1_auto_cohort_2 = get_cohort_by_name(self.course1.id, "Course1AutoGroup2")
        course_2_auto_cohort_1 = get_cohort_by_name(self.course2.id, "Course2AutoGroup1")

        # forcefully add user1 in two auto cohorts
        course_1_auto_cohort_1.users.add(self.user1)
        course_1_auto_cohort_2.users.add(self.user1)
        # forcefully add user2 in auto cohorts of both courses
        course_1_auto_cohort_1.users.add(self.user2)
        course_2_auto_cohort_1.users.add(self.user2)

        # now check that when user1 goes on discussion page and tries to get
        # cohorts 'MultipleObjectsReturned' exception is returned
        with self.assertRaises(MultipleObjectsReturned):
            get_cohort(self.user1, self.course1.id)
        # also check that user 2 can go on discussion page of both courses
        # without any exception
        get_cohort(self.user2, self.course1.id)
        get_cohort(self.user2, self.course2.id)

        # call command to remove users added in multiple cohorts of a course
        # are removed from all cohort groups
        call_command('remove_users_from_multiple_cohorts')

        # check that only user1 (with multiple cohorts) is removed from cohorts
        # and user2 is still in auto cohorts of both course after running
        # 'remove_users_from_multiple_cohorts' management command
        self.assertEqual(self.user1.course_groups.count(), 0)
        self.assertEqual(self.user2.course_groups.count(), 2)
        user2_cohorts = list(self.user2.course_groups.values_list('name', flat=True))
        self.assertEqual(user2_cohorts, ['Course1AutoGroup1', 'Course2AutoGroup1'])

        # now check that user1 can get cohorts in which he is added
        response = cohort_handler(self.request, unicode(self.course1.id))
        self.assertEqual(response.status_code, 200)
Example #30
0
def cohort_info(xblock):
    """
    Provide the course cohort information for the current user.
    """
    try:
        from openedx.core.djangoapps.course_groups import cohorts
    except ImportError:
        return

    user = get_xblock_user(xblock)
    if not user:
        return

    cohort = cohorts.get_cohort(course_key=xblock.course.id, user=user)
    if cohort and cohort.name:
        return {
            'custom_cohort_name': cohort.name,
            'custom_cohort_id': unicode(cohort.pk),  # noqa: F821
        }
    def get_user_cohort(self, data, suffix=''):
        """
        Handler used to retrieve information about the students cohort, it uses the course that
        includes this component.
        """
        is_studio = hasattr(self.xmodule_runtime, "is_author_mode")  # pylint: disable=no-member

        if is_studio or self.xmodule_runtime.__class__.__name__ == "StudioEditModuleRuntime":
            return {}

        current_anonymous_student_id = self.runtime.anonymous_student_id
        user = self.runtime.get_real_user(current_anonymous_student_id)
        course_id_str = str(self.runtime.course_id)

        course_key = CourseKey.from_string(course_id_str)
        cohort = get_cohort(user, course_key, assign=False, use_cached=True)

        if not cohort:
            return {}

        return {"cohort_name": cohort.name}
Example #32
0
    def get_sorted_submissions(self):
        """returns student recent assignments sorted on date"""
        assignments = []
        submissions = submissions_api.get_all_submissions(
            self.block_course_id, self.block_id, ITEM_TYPE)

        for submission in submissions:
            student = user_by_anonymous_id(submission['student_id'])
            sub = {
                'submission_id':
                submission['uuid'],
                'username':
                student.username,
                'student_id':
                submission['student_id'],
                'fullname':
                student.profile.name,
                'timestamp':
                submission['submitted_at'] or submission['created_at'],
                'filename':
                submission['answer']["filename"],
                'score':
                json.loads(submission['answer']['score'])
                if 'score' in submission['answer'] else 0,
                'result':
                json.loads(submission['answer']['result'])
            }
            if is_course_cohorted(self.course_id):
                group = get_cohort(student,
                                   self.course_id,
                                   assign=False,
                                   use_cached=True)
                sub['cohort'] = group.name if group else '(não atribuído)'
            assignments.append(sub)

        assignments.sort(key=lambda assignment: assignment['timestamp'],
                         reverse=True)
        return assignments
Example #33
0
    def get_rows_to_export(self):
        """
        Return iterator of rows for file export.
        """
        location = UsageKey.from_string(self.block_id)
        my_name = self.display_name

        students = get_scores(location)
        course_key = location.course_key
        enrollments = _get_enrollments(course_key,
                                       track=self.track,
                                       cohort=self.cohort)
        for enrollment in enrollments:
            cohort = get_cohort(enrollment['user'], course_key, assign=False)
            row = {
                'block_id': location,
                'title': my_name,
                'New Points': None,
                'Previous Points': None,
                'date_last_graded': None,
                'who_last_graded': None,
                'user_id': enrollment['user_id'],
                'username': enrollment['username'],
                'full_name': enrollment['full_name'],
                'student_uid': enrollment['student_uid'],
                'enrolled': enrollment['enrolled'],
                'track': enrollment['track'],
                'cohort': cohort.name if cohort else None,
            }
            score = students.get(enrollment['user_id'], None)

            if score:
                row['Previous Points'] = float(score['score'])
                row['date_last_graded'] = score['modified'].strftime(
                    '%Y-%m-%d %H:%M')
                row['who_last_graded'] = score['who_last_graded']
            yield row
Example #34
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)
Example #35
0
def upload_grades_csv(_xmodule_instance_args, _entry_id, course_id, _task_input, action_name):
    """
    For a given `course_id`, generate a grades CSV file for all students that
    are enrolled, 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. Writes are
    buffered, so we'll never write part of a CSV file to S3 -- i.e. any files
    that are visible in ReportStore will be complete ones.

    As we start to add more CSV downloads, it will probably be worthwhile to
    make a more general CSVDoc class instead of building out the rows like we
    do here.
    """
    start_time = time()
    start_date = datetime.now(UTC)
    status_interval = 100
    enrolled_students = CourseEnrollment.users_enrolled_in(course_id)
    task_progress = TaskProgress(action_name, enrolled_students.count(), start_time)

    course = get_course_by_id(course_id)
    cohorts_header = ["Cohort Name"] if course.is_cohorted else []

    partition_service = LmsPartitionService(user=None, course_id=course_id)
    partitions = partition_service.course_partitions
    group_configs_header = ["Group Configuration Group Name ({})".format(partition.name) for partition in partitions]

    # Loop over all our students and build our CSV lists in memory
    header = None
    rows = []
    err_rows = [["id", "username", "error_msg"]]
    current_step = {"step": "Calculating Grades"}
    for student, gradeset, err_msg in iterate_grades_for(course_id, 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

        if gradeset:
            # We were able to successfully grade this student for this course.
            task_progress.succeeded += 1
            if not header:
                header = [section["label"] for section in gradeset[u"section_breakdown"]]
                rows.append(["id", "email", "username", "grade"] + header + cohorts_header + group_configs_header)

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

            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 partitions:
                group = LmsPartitionService(student, course_id).get_group(partition, assign=False)
                group_configs_group_names.append(group.name if group else "")

            # Not everybody has the same gradable items. If the item is not
            # found in the user's gradeset, just assume it's a 0. The aggregated
            # grades for their sections and overall course will be calculated
            # without regard for the item they didn't have access to, so it's
            # possible for a student to have a 0.0 show up in their row but
            # still have 100% for the course.
            row_percents = [percents.get(label, 0.0) for label in header]
            rows.append(
                [student.id, student.email, student.username, gradeset["percent"]]
                + row_percents
                + cohorts_group_name
                + group_configs_group_names
            )
        else:
            # An empty gradeset means we failed to grade a student.
            task_progress.failed += 1
            err_rows.append([student.id, student.username, err_msg])

    # 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)

    # Perform the actual upload
    upload_csv_to_report_store(rows, "grade_report", course_id, start_date)

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

    # One last update before we close out...
    return task_progress.update_task_state(extra_meta=current_step)
Example #36
0
def upload_grades_csv(_xmodule_instance_args, _entry_id, course_id,
                      _task_input, action_name):
    """
    For a given `course_id`, generate a grades CSV file for all students that
    are enrolled, 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. Writes are
    buffered, so we'll never write part of a CSV file to S3 -- i.e. any files
    that are visible in ReportStore will be complete ones.

    As we start to add more CSV downloads, it will probably be worthwhile to
    make a more general CSVDoc class instead of building out the rows like we
    do here.
    """
    start_time = time()
    start_date = datetime.now(UTC)
    status_interval = 100
    enrolled_students = CourseEnrollment.users_enrolled_in(course_id)
    task_progress = TaskProgress(action_name, enrolled_students.count(),
                                 start_time)

    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)

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

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

    # Loop over all our students and build our CSV lists in memory
    header = None
    rows = []
    err_rows = [["id", "username", "error_msg"]]
    current_step = {'step': 'Calculating Grades'}

    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)
    for student, gradeset, err_msg in iterate_grades_for(
            course_id, 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 certain intervals to get a hint that task is in progress
        student_counter += 1
        if student_counter % 1000 == 0:
            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 gradeset:
            # We were able to successfully grade this student for this course.
            task_progress.succeeded += 1
            if not header:
                header = [
                    section['label']
                    for section in gradeset[u'section_breakdown']
                ]
                rows.append(["id", "email", "username", "grade"] + header +
                            cohorts_header + group_configs_header)

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

            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 = LmsPartitionService(student,
                                            course_id).get_group(partition,
                                                                 assign=False)
                group_configs_group_names.append(group.name if group else '')

            # Not everybody has the same gradable items. If the item is not
            # found in the user's gradeset, just assume it's a 0. The aggregated
            # grades for their sections and overall course will be calculated
            # without regard for the item they didn't have access to, so it's
            # possible for a student to have a 0.0 show up in their row but
            # still have 100% for the course.
            row_percents = [percents.get(label, 0.0) for label in header]
            rows.append([
                student.id, student.email, student.username,
                gradeset['percent']
            ] + row_percents + cohorts_group_name + group_configs_group_names)
        else:
            # An empty gradeset means we failed to grade a student.
            task_progress.failed += 1
            err_rows.append([student.id, student.username, err_msg])

    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
    upload_csv_to_report_store(rows, 'grade_report', course_id, start_date)

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

    # 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)
Example #37
0
def upload_grades_csv(_xmodule_instance_args, _entry_id, course_id, _task_input, action_name):
    """
    For a given `course_id`, generate a grades CSV file for all students that
    are enrolled, 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. Writes are
    buffered, so we'll never write part of a CSV file to S3 -- i.e. any files
    that are visible in ReportStore will be complete ones.

    As we start to add more CSV downloads, it will probably be worthwhile to
    make a more general CSVDoc class instead of building out the rows like we
    do here.
    """
    start_time = time()
    start_date = datetime.now(UTC)
    status_interval = 100
    enrolled_students = CourseEnrollment.users_enrolled_in(course_id)
    task_progress = TaskProgress(action_name, enrolled_students.count(), start_time)

    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)

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

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

    # Loop over all our students and build our CSV lists in memory
    header = None
    rows = []
    err_rows = [["id", "username", "error_msg"]]
    current_step = {'step': 'Calculating Grades'}

    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
    )
    for student, gradeset, err_msg in iterate_grades_for(course_id, 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 certain intervals to get a hint that task is in progress
        student_counter += 1
        if student_counter % 1000 == 0:
            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 gradeset:
            # We were able to successfully grade this student for this course.
            task_progress.succeeded += 1
            if not header:
                header = [section['label'] for section in gradeset[u'section_breakdown']]
                rows.append(
                    ["id", "email", "username", "grade"] + header + cohorts_header + group_configs_header
                )

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

            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 = LmsPartitionService(student, course_id).get_group(partition, assign=False)
                group_configs_group_names.append(group.name if group else '')

            # Not everybody has the same gradable items. If the item is not
            # found in the user's gradeset, just assume it's a 0. The aggregated
            # grades for their sections and overall course will be calculated
            # without regard for the item they didn't have access to, so it's
            # possible for a student to have a 0.0 show up in their row but
            # still have 100% for the course.
            row_percents = [percents.get(label, 0.0) for label in header]
            rows.append(
                [student.id, student.email, student.username, gradeset['percent']] +
                row_percents + cohorts_group_name + group_configs_group_names
            )
        else:
            # An empty gradeset means we failed to grade a student.
            task_progress.failed += 1
            err_rows.append([student.id, student.username, err_msg])

    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
    upload_csv_to_report_store(rows, 'grade_report', course_id, start_date)

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

    # 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)
Example #38
0
 def _verify_no_automatic_cohorting(self):
     self._enroll_in_course()
     self.assertIsNone(get_cohort(self.user, self.course.id, assign=False))
     self._upgrade_to_verified()
     self.assertIsNone(get_cohort(self.user, self.course.id, assign=False))
     self.assertEqual(0, self.mocked_celery_task.call_count)
Example #39
0
def to_paid_track(userlike_str,
                  course_id,
                  verified_cohort_name="verified",
                  default_cohort_name="default",
                  mode_slug="verified"):
    course_key = CourseKey.from_string(course_id)
    user = User.objects.get(email=userlike_str)
    course = get_course_by_id(course_key)
    acceptable_modes = ('verified', 'professional')

    def _check_user():
        """
        Различные проверки пользовательского аккаунта
        :return: bool
        """
        return user.is_active

    def _check_verified_course_mode():
        course_modes = CourseMode.objects.filter(
            course_id=course_key, mode_slug__in=acceptable_modes)
        return course_modes

    def _set_user_mode():
        available_verified_modes = _check_verified_course_mode()
        for mode in available_verified_modes:
            if mode.mode_slug == mode_slug:
                update_enrollment(user.username, course_id, mode_slug)
                return True
        return False

        #
        # if not mode:
        #     mode = available_verified_modes[0]
        #
        # if mode in available_verified_modes:
        #     update_enrollment(user.username, course_id, mode)
        # elif mode not in available_verified_modes:
        #     raise CourseModeNotFoundError

    def _get_verified_cohort():
        if not is_course_cohorted(course_key):
            logger.error(u"COURSE_NOT_COHORTED: Course '%s' is not cohorted",
                         course_id)
            set_course_cohorted(course_key, cohorted)

        existing_manual_cohorts = get_course_cohorts(
            course, assignment_type=CourseCohort.MANUAL)
        logger.info(u"Cohorts on course '%s' '%s'", course_id,
                    existing_manual_cohorts)

        return existing_manual_cohorts[0]
        # if verified_cohort_name:  # алгоритм с одной платной когортой
        #     if not verified_cohort_name in existing_manual_cohorts:
        #         # Создадём когорту и группу контента
        #         cohort = CourseCohort.create(cohort_name=verified_cohort_name, course_id=course_key,
        #                                      assignment_type=CourseCohort.MANUAL)
        #         return cohort
        # else:
        #     """
        #     Место для алгоритма с привязкой когорты к треку и многими треками
        #     """
        #     pass

    def _set_user_cohort():
        cohort = _get_verified_cohort()
        try:
            add_user_to_cohort(cohort, user.username)
        except:
            pass

    def _verify_user():
        if not SoftwareSecurePhotoVerification.user_is_verified(user):
            obj = SoftwareSecurePhotoVerification(
                user=user, photo_id_key="dummy_photo_id_key")
            obj.status = 'approved'
            obj.submitted_at = datetime.datetime.now()
            obj.reviewing_user = User.objects.get(username='******')
            obj.save()

    enrollment = None
    try:
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
    except CourseEnrollmentError:
        return "user is not enrolled"

    if enrollment:
        return str(_set_user_mode()), str(_set_user_cohort()), str(
            _verify_user())

        # def _check_enrollment(self, user, course_key):
        #     enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(user, course_key)
        #
        #     if enrollment_mode is not None and is_active:
        #         all_modes = CourseMode.modes_for_course_dict(course_key, include_expired=True)
        #         course_mode = all_modes.get(enrollment_mode)
        #         has_paid = (course_mode and course_mode.min_price > 0)
        #
        #     return (has_paid, bool(is_active))

        # try:
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        # Note that this will enroll the user in the default cohort on initial enrollment.
        # That's good because it will force creation of the default cohort if necessary.
        current_cohort = get_cohort(user, course_key)
        verified_cohort = get_cohort_by_name(course_key, verified_cohort_name)
Example #40
0
 def _verify_no_automatic_cohorting(self):
     self._enroll_in_course()
     self.assertIsNone(get_cohort(self.user, self.course.id, assign=False))
     self._upgrade_to_verified()
     self.assertIsNone(get_cohort(self.user, self.course.id, assign=False))
     self.assertEqual(0, self.mocked_celery_task.call_count)
Example #41
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 generate 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. Writes are
    buffered, so we'll never write part of a CSV file to S3 -- i.e. any files
    that are visible in ReportStore will be complete ones.

    Unenrolled users and unknown usernames are store in *_err_*.csv
    """
    start_time = time()
    start_date = datetime.now(UTC)
    status_interval = 100
    enrolled_students = CourseEnrollment.objects.users_enrolled_in(course_id)
    usernames = _task_input.get("usernames")
    found_students = User.objects.filter(username__in=usernames)
    enrolled_students = enrolled_students.filter(username__in=usernames)
    requester_id = _task_input.get("requester_id")

    task_progress = TaskProgress(action_name, enrolled_students.count(),
                                 start_time)

    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)

    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
    header = None
    rows = []
    err_rows = [["id", "username", "error_msg"]]
    current_step = {'step': 'Calculating Grades'}

    #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)
    for student, gradeset, err_msg in iterate_grades_for(
            course_id, 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 gradeset:
            # We were able to successfully grade this student for this course.
            task_progress.succeeded += 1
            if not header:
                header = [
                    section['label']
                    for section in gradeset[u'section_breakdown']
                ]
                rows.append(["id", "email", "username", "grade"] + header +
                            cohorts_header + group_configs_header +
                            teams_header +
                            ['Enrollment Track', 'Verification Status'] +
                            certificate_info_header)

            percents = {
                section['label']: section.get('percent', 0.1)
                for section in gradeset[u'section_breakdown']
                if 'label' in section
            }
            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 = LmsPartitionService(student,
                                            course_id).get_group(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, gradeset['grade'], student.id
                in whitelisted_user_ids)

            # Not everybody has the same gradable items. If the item is not
            # found in the user's gradeset, just assume it's a 0. The aggregated
            # grades for their sections and overall course will be calculated
            # without regard for the item they didn't have access to, so it's
            # possible for a student to have a 0.0 show up in their row but
            # still have 100% for the course.
            row_percents = [percents.get(label, 0.0) for label in header]
            rows.append([
                student.id, student.email, student.username,
                gradeset['percent']
            ] + row_percents + cohorts_group_name + group_configs_group_names +
                        team_name + [enrollment_mode] + [verification_status] +
                        certificate_info)
        else:
            # An empty gradeset means we failed to grade a student.
            task_progress.failed += 1
            err_rows.append([student.id, student.username, err_msg])

    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()

    upload_csv_to_report_store(rows,
                               'grade_users_report_id_{}'.format(requester_id),
                               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
    if len(err_rows) > 1:
        upload_csv_to_report_store(
            err_rows,
            'grade_users_report_err_id_{}'.format(requester_id),
            course_id,
            start_date,
            config_name=custom_grades_download)

    # 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)
    def _base_view(self, context=None):
        # This must be imported within this function, otherwise the build breaks.
        from openedx.core.djangoapps.course_groups.cohorts import get_cohort

        annoto_auth = self.get_annoto_settings()
        horizontal, vertical = self.get_position()
        translator = self.runtime.service(self, 'i18n').translator
        lang = getattr(
            translator,
            'get_language',
            lambda: translator.info().get('language', settings.LANGUAGE_CODE)
        )()
        rtl = getattr(translator, 'get_language_bidi', lambda: lang in settings.LANGUAGES_BIDI)()

        course = self.get_course_obj()
        course_overview = CourseOverview.objects.get(id=self.course_id)

        course_id = str(self.course_id)
        course_display_name = course.display_name
        user = self._get_user()
        if not context['is_author_view'] and user and self.discussions_scope == 'cohort':
            from openedx.core.djangoapps.course_groups.cohorts import get_cohort
            cohort = get_cohort(user, self.course_id)
            if cohort:
                course_id = u'{}_{}'.format(course_id, cohort.id)
                course_display_name = u'{} [{}]'.format(course_display_name, cohort.name)

        js_params = {
            'clientId': annoto_auth.get('client_id'),
            'horizontal': horizontal,
            'vertical': vertical,
            'tabs': self.tabs,
            'overlayVideo': self.overlay_video,
            'initialState': self.initial_state,
            'privateThread': self.discussions_scope != 'site',
            'mediaTitle': self.get_parent().display_name,
            'language': lang,
            'rtl': rtl,
            'courseId': course_id,
            'courseDisplayName': course_display_name,
            'courseDescription': course_overview.short_description,
            'courseImage': course_image_url(course),
            'demoMode': not bool(annoto_auth.get('client_id')),
            'isLive': self.video_type == 'stream',
            'comments': 'comments' in self.features,
            'privateNotes': 'notes' in self.features,
        }

        context['error'] = {}
        if not annoto_auth.get('client_id'):
            context['error']['type'] = 'warning'
            context['error']['messages'] = [
                self.i18n_service.gettext('You did not provide annoto credentials. And you view it in demo mode.'),
                self.i18n_service.gettext('Please add "annoto-auth:<CLIENT_ID>:<CLIENT_SECRET>" to "Advanced Settings" > "LTI Passports"'),
            ]
        else:
            try:
                jwt.PyJWS().decode(annoto_auth.get('client_id'), verify=False)
            except:
                context['error']['type'] = 'error'
                context['error']['messages'] = [
                    self.i18n_service.gettext('"CLIENT_ID" is not a valid JWT token.'),
                    self.i18n_service.gettext('Please provide valid "CLIENT_ID" in '
                      '"Advanced Settings" > "LTI Passports" > "annoto-auth:<CLIENT_ID>:<CLIENT_SECRET>"'),
                ]
            else:
                if not annoto_auth.get('client_secret'):
                    context['error']['type'] = 'error'
                    context['error']['messages'] = [
                        self.i18n_service.gettext('"CLIENT_SECRET" is required when "CLIENT_ID" provided.'),
                        self.i18n_service.gettext('Please add "CLIENT_SECRET" to '
                          '"Advanced Settings" > "LTI Passports" > "annoto-auth:<CLIENT_ID>:<CLIENT_SECRET>"'),
                    ]

        template = Template(self.resource_string("static/html/annoto.html"))
        html = template.render(Context(context))
        frag = Fragment(html)
        frag.add_css(self.resource_string("static/css/annoto.css"))
        frag.add_javascript(self.resource_string("static/js/src/annoto.js"))
        frag.initialize_js('AnnotoXBlock', json_args=js_params)
        return frag
Example #43
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)
Example #44
0
 def get_cohort(self):
     return get_cohort(self.user, self.course_key)