Ejemplo n.º 1
0
    def test_migrated(self):
        source = self._create_user(enrolled=self.course)
        target = self._create_user()

        self._create_user_progress(source)

        with patch('openedx.core.djangoapps.user_api.completion.tasks.update_user_gradebook') as update_user_gradebook:
            outcome = _migrate_progress(self.course_id, source.email, target.email)

        course_key = str(self.course.id)
        update_user_gradebook.assert_has_calls([
            call(course_key, source.id), call(course_key, target.id)
        ])
        self.assertEqual(outcome, OUTCOME_MIGRATED)

        # Check that all user's progress transferred to another user
        assert CourseEnrollment.objects.filter(user=target, course=self.course.id).exists()
        assert BlockCompletion.user_learning_context_completion_queryset(user=target, context_key=self.course.id).exists()
        assert StudentItem.objects.filter(
            course_id=self.course.id, student_id=anonymous_id_for_user(target, self.course.id)
        ).exists()
        assert StudentModule.objects.filter(student=target, course_id=self.course.id).exists()
Ejemplo n.º 2
0
def _migrate_progress(course, source, target):
    """
    Task that migrates progress from one user to another
    """
    log.info('Started progress migration from "%s" to "%s" for "%s" course', source, target, course)

    try:
        course_key = CourseKey.from_string(course)
    except InvalidKeyError:
        log.warning('Migration failed. Invalid course key: %s', course)
        return OUTCOME_COURSE_KEY_INVALID

    try:
        get_course(course_key)
    except ValueError:
        log.warning('Migration failed. Course not found:: %s', course_key)
        return OUTCOME_COURSE_NOT_FOUND

    try:
        source = get_user_model().objects.get(email=source)
    except ObjectDoesNotExist:
        log.warning('Migration failed. Source user with such email not found: %s', source)
        return OUTCOME_SOURCE_NOT_FOUND

    try:
        enrollment = CourseEnrollment.objects.select_for_update().get(user=source, course=course_key)
    except ObjectDoesNotExist:
        log.warning(
            'Migration failed. Source user with email "%s" not enrolled in "%s" course', source.email, course_key
        )
        return OUTCOME_SOURCE_NOT_ENROLLED

    try:
        target = get_user_model().objects.get(email=target)
    except ObjectDoesNotExist:
        log.warning('Migration failed. Target user with such email not found: %s', target)
        return OUTCOME_TARGET_NOT_FOUND

    try:
        assert not BlockCompletion.user_learning_context_completion_queryset(
            user=target, context_key=course_key
        ).exists()
        anonymous_ids = AnonymousUserId.objects.filter(user=target, course_id=course_key).values('anonymous_user_id')
        assert not StudentItem.objects.filter(course_id=course_key, student_id__in=anonymous_ids).exists()
    except AssertionError:
        log.warning(
            'Migration failed. Target user with email "%s" already enrolled in "%s" course and progress is present.', target.email, course_key
        )
        return OUTCOME_TARGET_ALREADY_ENROLLED

    # Fetch completions for source user
    completions = BlockCompletion.user_learning_context_completion_queryset(
        user=source, context_key=course_key
    ).select_for_update()

    # Fetch edx-submissions data for source user
    anonymous_ids = AnonymousUserId.objects.filter(user=source, course_id=course_key).values('anonymous_user_id')
    submissions = StudentItem.objects.select_for_update().filter(course_id=course_key, student_id__in=anonymous_ids)

    # Fetch StudentModule table data for source user
    student_states = StudentModule.objects.select_for_update().filter(student=source, course_id=course_key)

    # Actually migrate completions and progress
    try:
        # Modify enrollment
        try:
            target_enrollment = CourseEnrollment.objects.select_for_update().get(user=target, course=course_key)
        except ObjectDoesNotExist:
            enrollment.user = target
        else:
            enrollment.is_active = False
            target_enrollment.is_active = True
            target_enrollment.save()
        finally:
            enrollment.save()

        # Migrate completions for user
        for completion in completions:
            completion.user = target
            completion.save()

        # Migrate edx-submissions
        for submission in submissions:
            submission.student_id = anonymous_id_for_user(target, course_key)
            submission.save()

        # Migrate StudentModule
        for state in student_states:
            state.student = target
            state.save()

        log.info('Removing stale aggregators for source user.')
        Aggregator.objects.filter(user=source, course_key=course_key).delete()

    except Exception:
        log.exception("Unexpected error while migrating user progress.")
        return OUTCOME_FAILED_MIGRATION

    log.info('Updating gradebook for %s user.', source.email)
    update_user_gradebook(course, source.id)
    log.info('Updating gradebook for %s user.', target.email)
    update_user_gradebook(course, target.id)

    log.info(
        'User progress in "%s" course successfully migrated from "%s" to "%s"', course_key, source.email, target.email
    )
    return OUTCOME_MIGRATED