def test_transform_with_content_gating_partition(self):
        self.setup_partitions_and_course()
        CourseModeFactory.create(course_id=self.course.id, mode_slug='audit')
        CourseModeFactory.create(course_id=self.course.id, mode_slug='verified')
        ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
        partition = create_content_gating_partition(self.course)
        self.user_partitions.append(partition)
        cohort = self.partition_cohorts[0][1]
        add_user_to_cohort(cohort, self.user.username)

        with patch(
            'lms.djangoapps.course_blocks.transformers.user_partitions.get_partition_from_id',
            return_value=partition
        ), patch(
            'lms.djangoapps.course_blocks.transformers.user_partitions._MergedGroupAccess.get_allowed_groups',
            return_value={51: set([])}
        ):
            trans_block_structure = get_course_blocks(
                self.user,
                self.course.location,
                self.transformers,
            )
            xblocks_denial_reason = [trans_block_structure.get_xblock_field(b, 'authorization_denial_reason')
                                     for b in trans_block_structure.get_block_keys()]
            self.assertSetEqual(set(xblocks_denial_reason), set([u'Feature-based Enrollments']))
Exemplo n.º 2
0
    def test_with_cohorted_content(self, content_creator_method_name):
        self.login_and_enroll()
        self._setup_course_partitions(scheme_id="cohort", is_cohorted=True)

        cohorts = []
        for group_id in [0, 1]:
            getattr(self, content_creator_method_name)(group_id)

            cohorts.append(CohortFactory(course_id=self.course.id, name=u"Cohort " + unicode(group_id)))
            link = CourseUserGroupPartitionGroup(
                course_user_group=cohorts[group_id], partition_id=self.partition_id, group_id=group_id
            )
            link.save()

        for cohort_index in range(len(cohorts)):
            # add user to this cohort
            add_user_to_cohort(cohorts[cohort_index], self.user.username)

            # should only see video for this cohort
            video_outline = self.api_response().data
            self.assertEqual(len(video_outline), 1)
            self.assertEquals(u"video for group " + unicode(cohort_index), video_outline[0]["summary"]["name"])

            # remove user from this cohort
            remove_user_from_cohort(cohorts[cohort_index], self.user.username)

        # un-cohorted user should see no videos
        video_outline = self.api_response().data
        self.assertEqual(len(video_outline), 0)

        # staff user sees all videos
        self.user.is_staff = True
        self.user.save()
        video_outline = self.api_response().data
        self.assertEqual(len(video_outline), 2)
    def test_transform_with_content_gating_partition(self):
        self.setup_partitions_and_course()
        CourseModeFactory.create(course_id=self.course.id, mode_slug='audit')
        CourseModeFactory.create(course_id=self.course.id,
                                 mode_slug='verified')
        ContentTypeGatingConfig.objects.create(enabled=True,
                                               enabled_as_of=datetime(
                                                   2018, 1, 1))
        partition = create_content_gating_partition(self.course)
        self.user_partitions.append(partition)
        cohort = self.partition_cohorts[0][1]
        add_user_to_cohort(cohort, self.user.username)

        with patch(
                'lms.djangoapps.course_blocks.transformers.user_partitions.get_partition_from_id',
                return_value=partition
        ), patch(
                'lms.djangoapps.course_blocks.transformers.user_partitions._MergedGroupAccess.get_allowed_groups',
                return_value={51: set([])}):
            trans_block_structure = get_course_blocks(
                self.user,
                self.course.location,
                self.transformers,
            )
            xblocks_denial_reason = [
                trans_block_structure.get_xblock_field(
                    b, 'authorization_denial_reason')
                for b in trans_block_structure.get_block_keys()
            ]
            self.assertSetEqual(set(xblocks_denial_reason),
                                set([u'Feature-based Enrollments']))
Exemplo n.º 4
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)
Exemplo n.º 5
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
        )
    def add_user_to_cohort_group(self):
        """
        adds user to cohort and links cohort to content group
        """
        add_user_to_cohort(self.first_cohort, self.user.username)

        link_cohort_to_partition_group(self.first_cohort, self.user_partition.id, self.groups[0].id)

        self.courses[0].save()
        modulestore().update_item(self.courses[0], self.user.id)
Exemplo n.º 7
0
    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.cohort = form.cleaned_data['cohort']
        instance.can_edit = False
        instance.save()

        try:
            add_user_to_cohort(instance.cohort, instance.user.email)
        except ValueError:
            pass
        return super(UniversityIDUpdateView, self).form_valid(form)
Exemplo n.º 8
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)
    def test_transform(self, group_id, expected_blocks):
        if group_id:
            cohort = self.partition_cohorts[self.user_partition.id -
                                            1][group_id - 1]
            add_user_to_cohort(cohort, self.user.username)

        trans_block_structure = get_course_blocks(
            self.user, self.course.location, transformers={self.transformer})
        self.assertSetEqual(
            set(trans_block_structure.get_block_keys()),
            self.get_block_key_set(self.blocks, *expected_blocks))
Exemplo n.º 10
0
    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.cohort = form.cleaned_data['cohort']
        instance.can_edit = False
        instance.save()

        try:
            add_user_to_cohort(instance.cohort, instance.user.email)
        except ValueError:
           pass
        return super(UniversityIDUpdateView, self).form_valid(form)
Exemplo n.º 11
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)
    def test_transform(self, group_id, expected_blocks):
        if group_id:
            add_user_to_cohort(self.cohorts[group_id - 1], self.user.username)

        trans_block_structure = get_course_blocks(
            self.user,
            self.course.location,
            transformers={self.transformer}
        )
        self.assertSetEqual(
            set(trans_block_structure.get_block_keys()),
            self.get_block_key_set(self.blocks, *expected_blocks)
        )
Exemplo n.º 13
0
    def _enroll_user(self, data, errors, response={}):
        """Enroll user in a course and add him to a cohort."""
        user = data.get('user_object')
        email = user.email
        roles = data.get('roles', {})
        ignore_roles = data.get('ignore_roles', [])
        permissions = data.get('permissions', {})
        status = data.get('status', '').lower()
        course = data.get('course')
        course_key = data.get('course_key')

        # Enroll in course.
        try:
            CourseEnrollment.enroll(user, course_key)
        except IntegrityError:
            # If the enrollment already exists, it's possible we weren't able to add to the cohort yet,
            # so ignore the error and continue.
            pass

        cohort = get_cohort_by_name(course_key,
                                    CourseUserGroup.default_cohort_name)

        try:
            add_user_to_cohort(cohort, user.username)
        except ValueError:
            # This situation can occur if user is already present in the cohort for the course.
            pass

        # Assign role and permission in course.
        try:
            if status != "participant":
                # Add role.
                role = roles[status]
                if role not in ignore_roles:
                    _manage_role(course, user, role, 'allow')

                # Add permission for role.
                permission = permissions[role]
                permission_groups = Group.objects.get(
                    groupprofile__name=permission)
                user.groups.through.objects.get_or_create(
                    group=permission_groups, user=user)
        except IntegrityError as exc:
            AUDIT_LOG.exception(exc.message or exc)
        except Exception as exc:
            self._add_error(errors, str(exc.message),
                            _("Setting Participant's Status"), email)
        else:
            response['user_id'] = user.id
            AUDIT_LOG.info(u"API::User updated with user-id - {0}".format(
                user.id))
Exemplo n.º 14
0
    def test_transform(self, group_id, expected_blocks):
        if group_id:
            cohort = self.partition_cohorts[self.user_partition.id - 1][group_id - 1]
            add_user_to_cohort(cohort, self.user.username)

        trans_block_structure = get_course_blocks(
            self.user,
            self.course.location,
            self.transformers,
        )
        self.assertSetEqual(
            set(trans_block_structure.get_block_keys()),
            self.get_block_key_set(self.blocks, *expected_blocks)
        )
Exemplo n.º 15
0
    def add_user_to_cohort_group(self):
        """
        adds user to cohort and links cohort to content group
        """
        add_user_to_cohort(self.first_cohort, self.user.username)

        link_cohort_to_partition_group(
            self.first_cohort,
            self.user_partition.id,
            self.groups[0].id,
        )

        self.courses[0].save()
        modulestore().update_item(self.courses[0], self.user.id)
Exemplo n.º 16
0
    def test_send_to_cohort(self):
        """
        Make sure email sent to a cohort goes there.
        """
        cohort = CourseCohort.create(cohort_name='test cohort', course_id=self.course.id)
        for student in self.students:
            add_user_to_cohort(cohort.course_user_group, student.username)
        test_email = {
            'action': 'Send email',
            'send_to': f'["cohort:{cohort.course_user_group.name}"]',
            'subject': 'test subject for cohort',
            'message': 'test message for cohort'
        }
        response = self.client.post(self.send_mail_url, test_email)
        assert json.loads(response.content.decode('utf-8')) == self.success_content

        assert len([e.to[0] for e in mail.outbox]) == len([s.email for s in self.students])
Exemplo n.º 17
0
def register_user_verified_cohort(user, order):
    """
    Once a user has been verified, add it to a cohort.

    The cohort to which the user should be added is given by the VERIFIED_COHORTS setting.
    settings.VERIFIED_COHORTS is a list of (r"<course id regex>", "<cohort name>") tuples. Regular expressions
    are matched agains the course ID. If at least one regular expression matches, registered users from this
    course are added to the cohort with the highest priority. Cohorts are sorted by decreasing priority.

    If a course is associated to a cohort that doesn't exist or an empty cohort name, then the user is not
    added to any cohort. Thus, to disable this feature for a specific course, set:

        VERIFIED_COHORTS = [
            ...
            (r"<course id>", None),
            ...
        ]

    To enable this feature for a particular course, but not for all others, write:

        VERIFIED_COHORTS = [
            (r"<course id>", "cohort name"),
            (r".*", None),
        ]

    To disable this feature globally, set:

        VERIFIED_COHORTS = []
    """
    course = get_course(order)
    verified_cohort_name = None
    for course_id_regex, cohort_name in getattr(settings, "VERIFIED_COHORTS",
                                                []):
        if re.match(course_id_regex, course.key):
            verified_cohort_name = cohort_name
            break
    cohort = None
    if verified_cohort_name:
        course_key = CourseKey.from_string(course.key)
        try:
            cohort = get_cohort_by_name(course_key, verified_cohort_name)
        except CourseUserGroup.DoesNotExist:
            pass
    if cohort:
        # This will also trigger the edx.cohort.user_add_requested event
        add_user_to_cohort(cohort, user.email)
Exemplo n.º 18
0
    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.user = self.request.user
        instance.course_key = self.get_course_key()
        instance.cohort = form.cleaned_data['cohort']
        instance.save()

        update_account_settings(self.request.user, {
            'name': form.cleaned_data['full_name'],
        })

        try:
            add_user_to_cohort(instance.cohort, instance.user.email)
        except ValueError:
            # User already present in the cohort
            pass

        return super(UniversityIDView, self).form_valid(form)
Exemplo n.º 19
0
    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.user = self.request.user
        instance.course_key = self.get_course_key()
        instance.cohort = form.cleaned_data['cohort']
        instance.save()

        update_account_settings(self.request.user, {
            'name': form.cleaned_data['full_name'],
        })

        try:
            add_user_to_cohort(instance.cohort, instance.user.email)
        except ValueError:
           # User already present in the cohort
           pass

        return super(UniversityIDView, self).form_valid(form)
Exemplo n.º 20
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)
Exemplo n.º 21
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)
Exemplo n.º 22
0
    def post(self, request):
        serializer = BulkEnrollmentSerializer(data=request.data)
        if serializer.is_valid():
            # Setting the content type to be form data makes Django Rest Framework v3.6.3 treat all passed JSON data as
            # POST parameters. This is necessary because this request is forwarded on to the student_update_enrollment
            # view, which requires all of the parameters to be passed in via POST parameters.
            metadata = request._request.META  # pylint: disable=protected-access
            metadata['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'

            response_dict = {
                'auto_enroll': serializer.data.get('auto_enroll'),
                'email_students': serializer.data.get('email_students'),
                'action': serializer.data.get('action'),
                'courses': {}
            }
            for course_id, cohort_name in itertools.izip_longest(
                    serializer.data.get('courses'),
                    serializer.data.get('cohorts', [])):
                response = students_update_enrollment(self.request,
                                                      course_id=course_id)
                response_content = json.loads(response.content)

                if cohort_name:
                    try:
                        course_key = CourseKey.from_string(course_id)
                        cohort = get_cohort_by_name(course_key=course_key,
                                                    name=cohort_name)
                    except (CourseUserGroup.DoesNotExist,
                            InvalidKeyError) as exc:
                        return Response(exc.message,
                                        status=status.HTTP_400_BAD_REQUEST)

                    for user_data in response_content['results']:
                        if "after" in user_data and (
                                user_data["after"].get("enrollment", False) is
                                True or user_data["after"].get(
                                    "allowed", False) is True):
                            user_id = user_data['identifier']
                            try:
                                _user_obj, previous_cohort, _pre_assigned = add_user_to_cohort(
                                    cohort, user_id)
                            except ValueError:
                                # User already present in cohort
                                previous_cohort = cohort_name

                            if previous_cohort:
                                user_data['before']['cohort'] = previous_cohort
                            else:
                                user_data['before']['cohort'] = None
                            user_data['after']['cohort'] = cohort_name

                response_dict['courses'][course_id] = response_content
            return Response(data=response_dict, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)
Exemplo n.º 23
0
    def test_send_to_cohort_unenrolled(self):
        """
        Make sure email sent to a cohort does not go to unenrolled members of the cohort.
        """
        self.students.append(UserFactory())  # user will be added to cohort, but not enrolled in course
        cohort = CourseCohort.create(cohort_name='test cohort', course_id=self.course.id)
        for student in self.students:
            add_user_to_cohort(cohort.course_user_group, student.username)
        test_email = {
            'action': 'Send email',
            'send_to': '["cohort:{}"]'.format(cohort.course_user_group.name),
            'subject': 'test subject for cohort',
            'message': 'test message for cohort'
        }
        response = self.client.post(self.send_mail_url, test_email)
        self.assertEquals(json.loads(response.content.decode('utf-8')), self.success_content)

        self.assertEquals(len(mail.outbox), len(self.students) - 1)
        self.assertNotIn(self.students[-1].email, [e.to[0] for e in mail.outbox])
Exemplo n.º 24
0
    def test_send_to_cohort(self):
        """
        Make sure email sent to a cohort goes there.
        """
        cohort = CourseCohort.create(cohort_name='test cohort',
                                     course_id=self.course.id)
        for student in self.students:
            add_user_to_cohort(cohort.course_user_group, student.username)
        test_email = {
            'action': 'Send email',
            'send_to': '["cohort:{}"]'.format(cohort.course_user_group.name),
            'subject': 'test subject for cohort',
            'message': 'test message for cohort'
        }
        response = self.client.post(self.send_mail_url, test_email)
        self.assertEquals(json.loads(response.content), self.success_content)

        self.assertItemsEqual([e.to[0] for e in mail.outbox],
                              [s.email for s in self.students])
Exemplo n.º 25
0
    def test_send_to_cohort_unenrolled(self):
        """
        Make sure email sent to a cohort does not go to unenrolled members of the cohort.
        """
        self.students.append(UserFactory())  # user will be added to cohort, but not enrolled in course
        cohort = CourseCohort.create(cohort_name='test cohort', course_id=self.course.id)
        for student in self.students:
            add_user_to_cohort(cohort.course_user_group, student.username)
        test_email = {
            'action': 'Send email',
            'send_to': '["cohort:{}"]'.format(cohort.course_user_group.name),
            'subject': 'test subject for cohort',
            'message': 'test message for cohort'
        }
        response = self.client.post(self.send_mail_url, test_email)
        self.assertEquals(json.loads(response.content), self.success_content)

        self.assertEquals(len(mail.outbox), len(self.students) - 1)
        self.assertNotIn(self.students[-1].email, [e.to[0] for e in mail.outbox])
Exemplo n.º 26
0
    def test_send_to_cohort(self):
        """
        Make sure email sent to a cohort goes there.
        """
        cohort = CourseCohort.create(cohort_name='test cohort', course_id=self.course.id)
        for student in self.students:
            add_user_to_cohort(cohort.course_user_group, student.username)
        test_email = {
            'action': 'Send email',
            'send_to': '["cohort:{}"]'.format(cohort.course_user_group.name),
            'subject': 'test subject for cohort',
            'message': 'test message for cohort'
        }
        response = self.client.post(self.send_mail_url, test_email)
        self.assertEquals(json.loads(response.content), self.success_content)

        self.assertItemsEqual(
            [e.to[0] for e in mail.outbox],
            [s.email for s in self.students]
        )
Exemplo n.º 27
0
    def test_with_cohorted_content(self, content_creator_method_name,
                                   api_version):
        self.login_and_enroll()
        self._setup_course_partitions(scheme_id='cohort', is_cohorted=True)

        cohorts = []
        for group_id in [0, 1]:
            getattr(self, content_creator_method_name)(group_id)

            cohorts.append(
                CohortFactory(course_id=self.course.id,
                              name=u"Cohort " + unicode(group_id)))
            link = CourseUserGroupPartitionGroup(
                course_user_group=cohorts[group_id],
                partition_id=self.partition_id,
                group_id=group_id,
            )
            link.save()

        for cohort_index in range(len(cohorts)):
            # add user to this cohort
            add_user_to_cohort(cohorts[cohort_index], self.user.username)

            # should only see video for this cohort
            video_outline = self.api_response(api_version=api_version).data
            self.assertEqual(len(video_outline), 1)
            self.assertEquals(u"video for group " + unicode(cohort_index),
                              video_outline[0]["summary"]["name"])

            # remove user from this cohort
            remove_user_from_cohort(cohorts[cohort_index], self.user.username)

        # un-cohorted user should see no videos
        video_outline = self.api_response(api_version=api_version).data
        self.assertEqual(len(video_outline), 0)

        # staff user sees all videos
        self.user.is_staff = True
        self.user.save()
        video_outline = self.api_response(api_version=api_version).data
        self.assertEqual(len(video_outline), 2)
Exemplo n.º 28
0
    def post(self, request):
        serializer = BulkEnrollmentSerializer(data=request.data)
        if serializer.is_valid():
            # Setting the content type to be form data makes Django Rest Framework v3.6.3 treat all passed JSON data as
            # POST parameters. This is necessary because this request is forwarded on to the student_update_enrollment
            # view, which requires all of the parameters to be passed in via POST parameters.
            metadata = request._request.META  # pylint: disable=protected-access
            metadata['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'

            response_dict = {
                'auto_enroll': serializer.data.get('auto_enroll'),
                'email_students': serializer.data.get('email_students'),
                'action': serializer.data.get('action'),
                'courses': {}
            }
            for course_id, cohort_name in itertools.izip_longest(serializer.data.get('courses'),
                                                                 serializer.data.get('cohorts', [])):
                response = students_update_enrollment(self.request, course_id=course_id)
                response_content = json.loads(response.content)

                if cohort_name:
                    try:
                        course_key = CourseKey.from_string(course_id)
                        cohort = get_cohort_by_name(course_key=course_key, name=cohort_name)
                    except (CourseUserGroup.DoesNotExist, InvalidKeyError) as exc:
                        return Response(exc.message, status=status.HTTP_400_BAD_REQUEST)

                    for user_data in response_content['results']:
                        if "after" in user_data and (
                            user_data["after"].get("enrollment", False) is True or
                            user_data["after"].get("allowed", False) is True
                        ):
                            user_id = user_data['identifier']
                            try:
                                _user_obj, previous_cohort, _pre_assigned = add_user_to_cohort(cohort, user_id)
                            except ValueError:
                                # User already present in cohort
                                previous_cohort = cohort_name

                            if previous_cohort:
                                user_data['before']['cohort'] = previous_cohort
                            else:
                                user_data['before']['cohort'] = None
                            user_data['after']['cohort'] = cohort_name

                response_dict['courses'][course_id] = response_content
            return Response(data=response_dict, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Exemplo n.º 29
0
    def handle(self, *args, **options):

        self.stdout.write('### Checking CourseUserGroup group types\n')
        error = False
        for course_group in CourseUserGroup.objects.all():
            if course_group.group_type != CourseUserGroup.COHORT:
                if options['fix']:
                    self.stdout.write(
                        'Fixed: CourseUserGroup with an invalid group_type found: {} (type: {})\n'.format(
                            course_group.name, course_group.group_type)
                    )
                    course_group.group_type = CourseUserGroup.COHORT
                    course_group.save()
                else:
                    error = True
                    self.stdout.write(
                        'CourseUserGroup with an invalid group_type found: {} (type: {})\n'.format(
                            course_group.name, course_group.group_type)
                    )

        if not error:
            self.stdout.write('Ok.\n')

        self.stdout.write('\n### Checking user cohorts\n')
        error = False
        users = User.objects.all()
        _courses = modulestore().get_courses()
        courses = [c for c in _courses if isinstance(c, CourseDescriptor)]
        # for each course, check if users are in atleast and only 1 cohort
        for course in courses:
            for user in users:
                if not CourseEnrollment.is_enrolled(user, course.id):
                    continue
                try:
                    CourseUserGroup.objects.get(course_id=course.id,
                                                users__id=user.id)
                except CourseUserGroup.DoesNotExist:
                    if options['fix']:
                        # create a "default_cohort" is it doesn't already exist
                        try:
                            default_cohort = get_cohort_by_name(course.id, CourseUserGroup.default_cohort_name)
                        except CourseUserGroup.DoesNotExist:
                            default_cohort = add_cohort(
                                course.id, CourseUserGroup.default_cohort_name, CourseCohort.RANDOM
                            )
                            self.stdout.write(
                                'Default cohort "{}" created for course "{}"'.format(
                                    default_cohort.name, course.display_name
                                )
                            )
                        add_user_to_cohort(default_cohort, user.username)
                        self.stdout.write(
                            'Fixed: User "{}" is not in a cohort in course "{}". Added in "{}" cohort\n'.format(
                                user.username, course.display_name, default_cohort.name)
                        )
                    else:
                        error = True
                        self.stdout.write(
                            'User "{}" is not in a cohort in course "{}".\n'.format(
                                user.username, course.display_name)
                        )
                except MultipleObjectsReturned:
                    self.stdout.write(
                        'User "{}" is in multiple cohorts in course "{}".\n'.format(
                            user.username, course.display_name)
                    )
                    if options['fix']:
                        user_cohorts = CourseUserGroup.objects.filter(course_id=course.id,
                                                                      users__id=user.id).all()
                        for cohort in user_cohorts[1:]:
                            remove_user_from_cohort(cohort, user.username)
                            self.stdout.write(
                                "User '{}' has been removed from cohort '{}' in course '{}'.\n".format(
                                    user.username, cohort.name, course.display_name
                                )
                            )
                        self.stdout.write(
                            "User '{}' is now only in cohort '{}' in course '{}'.\n".format(
                                # pylint: disable=undefined-loop-variable
                                user.username, cohort.name, course.display_name
                            )
                        )
                    else:
                        error = True

        if not error:
            self.stdout.write('Ok.\n')

        self.stdout.write('\nTo fix issues, run the script with the "--fix" option.\n')
Exemplo n.º 30
0
def cohort_students_and_upload(_xmodule_instance_args, _entry_id, course_id, task_input, action_name):
    """
    Within a given course, cohort students in bulk, then upload the results
    using a `ReportStore`.
    """
    start_time = time()
    start_date = datetime.now(UTC)

    # Iterate through rows to get total assignments for task progress
    with DefaultStorage().open(task_input['file_name']) as f:
        total_assignments = 0
        for _line in unicodecsv.DictReader(UniversalNewlineIterator(f)):
            total_assignments += 1

    task_progress = TaskProgress(action_name, total_assignments, start_time)
    current_step = {'step': 'Cohorting Students'}
    task_progress.update_task_state(extra_meta=current_step)

    # cohorts_status is a mapping from cohort_name to metadata about
    # that cohort.  The metadata will include information about users
    # successfully added to the cohort, users not found, and a cached
    # reference to the corresponding cohort object to prevent
    # redundant cohort queries.
    cohorts_status = {}

    with DefaultStorage().open(task_input['file_name']) as f:
        for row in unicodecsv.DictReader(UniversalNewlineIterator(f), encoding='utf-8'):
            # Try to use the 'email' field to identify the user.  If it's not present, use 'username'.
            username_or_email = row.get('email') or row.get('username')
            cohort_name = row.get('cohort') or ''
            task_progress.attempted += 1

            if not cohorts_status.get(cohort_name):
                cohorts_status[cohort_name] = {
                    'Cohort Name': cohort_name,
                    'Students Added': 0,
                    'Students Not Found': set()
                }
                try:
                    cohorts_status[cohort_name]['cohort'] = CourseUserGroup.objects.get(
                        course_id=course_id,
                        group_type=CourseUserGroup.COHORT,
                        name=cohort_name
                    )
                    cohorts_status[cohort_name]["Exists"] = True
                except CourseUserGroup.DoesNotExist:
                    cohorts_status[cohort_name]["Exists"] = False

            if not cohorts_status[cohort_name]['Exists']:
                task_progress.failed += 1
                continue

            try:
                with transaction.commit_on_success():
                    add_user_to_cohort(cohorts_status[cohort_name]['cohort'], username_or_email)
                cohorts_status[cohort_name]['Students Added'] += 1
                task_progress.succeeded += 1
            except User.DoesNotExist:
                cohorts_status[cohort_name]['Students Not Found'].add(username_or_email)
                task_progress.failed += 1
            except ValueError:
                # Raised when the user is already in the given cohort
                task_progress.skipped += 1

            task_progress.update_task_state(extra_meta=current_step)

    current_step['step'] = 'Uploading CSV'
    task_progress.update_task_state(extra_meta=current_step)

    # Filter the output of `add_users_to_cohorts` in order to upload the result.
    output_header = ['Cohort Name', 'Exists', 'Students Added', 'Students Not Found']
    output_rows = [
        [
            ','.join(status_dict.get(column_name, '')) if column_name == 'Students Not Found'
            else status_dict[column_name]
            for column_name in output_header
        ]
        for _cohort_name, status_dict in cohorts_status.iteritems()
    ]
    output_rows.insert(0, output_header)
    upload_csv_to_report_store(output_rows, 'cohort_results', course_id, start_date)

    return task_progress.update_task_state(extra_meta=current_step)
Exemplo n.º 31
0
 def _set_user_cohort():
     cohort = _get_verified_cohort()
     try:
         add_user_to_cohort(cohort, user.username)
     except:
         pass
Exemplo n.º 32
0
def cohort_students_and_upload(_xmodule_instance_args, _entry_id, course_id, task_input, action_name):
    """
    Within a given course, cohort students in bulk, then upload the results
    using a `ReportStore`.
    """
    start_time = time()
    start_date = datetime.now(UTC)

    # Iterate through rows to get total assignments for task progress
    with DefaultStorage().open(task_input['file_name']) as f:
        total_assignments = 0
        for _line in unicodecsv.DictReader(UniversalNewlineIterator(f)):
            total_assignments += 1

    task_progress = TaskProgress(action_name, total_assignments, start_time)
    current_step = {'step': 'Cohorting Students'}
    task_progress.update_task_state(extra_meta=current_step)

    # cohorts_status is a mapping from cohort_name to metadata about
    # that cohort.  The metadata will include information about users
    # successfully added to the cohort, users not found, Preassigned
    # users, and a cached reference to the corresponding cohort object
    # to prevent redundant cohort queries.
    cohorts_status = {}

    with DefaultStorage().open(task_input['file_name']) as f:
        for row in unicodecsv.DictReader(UniversalNewlineIterator(f), encoding='utf-8'):
            # Try to use the 'email' field to identify the user.  If it's not present, use 'username'.
            username_or_email = row.get('email') or row.get('username')
            cohort_name = row.get('cohort') or ''
            task_progress.attempted += 1

            if not cohorts_status.get(cohort_name):
                cohorts_status[cohort_name] = {
                    'Cohort Name': cohort_name,
                    'Learners Added': 0,
                    'Learners Not Found': set(),
                    'Invalid Email Addresses': set(),
                    'Preassigned Learners': set()
                }
                try:
                    cohorts_status[cohort_name]['cohort'] = CourseUserGroup.objects.get(
                        course_id=course_id,
                        group_type=CourseUserGroup.COHORT,
                        name=cohort_name
                    )
                    cohorts_status[cohort_name]["Exists"] = True
                except CourseUserGroup.DoesNotExist:
                    cohorts_status[cohort_name]["Exists"] = False

            if not cohorts_status[cohort_name]['Exists']:
                task_progress.failed += 1
                continue

            try:
                # If add_user_to_cohort successfully adds a user, a user object is returned.
                # If a user is preassigned to a cohort, no user object is returned (we already have the email address).
                (user, previous_cohort, preassigned) = add_user_to_cohort(cohorts_status[cohort_name]['cohort'], username_or_email)
                if preassigned:
                    cohorts_status[cohort_name]['Preassigned Learners'].add(username_or_email)
                    task_progress.preassigned += 1
                else:
                    cohorts_status[cohort_name]['Learners Added'] += 1
                    task_progress.succeeded += 1
            except User.DoesNotExist:
                # Raised when a user with the username could not be found, and the email is not valid
                cohorts_status[cohort_name]['Learners Not Found'].add(username_or_email)
                task_progress.failed += 1
            except ValidationError:
                # Raised when a user with the username could not be found, and the email is not valid,
                # but the entered string contains an "@"
                # Since there is no way to know if the entered string is an invalid username or an invalid email,
                # assume that a string with the "@" symbol in it is an attempt at entering an email
                cohorts_status[cohort_name]['Invalid Email Addresses'].add(username_or_email)
                task_progress.failed += 1
            except ValueError:
                # Raised when the user is already in the given cohort
                task_progress.skipped += 1

            task_progress.update_task_state(extra_meta=current_step)

    current_step['step'] = 'Uploading CSV'
    task_progress.update_task_state(extra_meta=current_step)

    # Filter the output of `add_users_to_cohorts` in order to upload the result.
    output_header = ['Cohort Name', 'Exists', 'Learners Added', 'Learners Not Found', 'Invalid Email Addresses', 'Preassigned Learners']
    output_rows = [
        [
            ','.join(status_dict.get(column_name, '')) if (column_name == 'Learners Not Found'
                                                           or column_name == 'Invalid Email Addresses'
                                                           or column_name == 'Preassigned Learners')
            else status_dict[column_name]
            for column_name in output_header
        ]
        for _cohort_name, status_dict in cohorts_status.iteritems()
    ]
    output_rows.insert(0, output_header)
    upload_csv_to_report_store(output_rows, 'cohort_results', course_id, start_date)

    return task_progress.update_task_state(extra_meta=current_step)
Exemplo n.º 33
0
    def post(self, request):
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.

        username = request.data.get('user', request.user.username)
        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        mode = request.data.get('mode')

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode not in (CourseMode.AUDIT, CourseMode.HONOR, None) and not has_api_key_permissions:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": u"User does not have permission to create enrollment with mode [{mode}].".format(
                        mode=mode
                    )
                }
            )

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(
                status=status.HTTP_406_NOT_ACCEPTABLE,
                data={
                    'message': u'The user {} does not exist.'.format(username)
                }
            )

        embargo_response = embargo_api.get_embargo_response(request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message': (u"'{value}' is an invalid enrollment activation status.").format(value=is_active)
                    }
                )

            explicit_linked_enterprise = request.data.get('linked_enterprise_customer')
            if explicit_linked_enterprise and has_api_key_permissions and enterprise_enabled():
                enterprise_api_client = EnterpriseApiServiceClient()
                consent_client = ConsentApiServiceClient()
                try:
                    enterprise_api_client.post_enterprise_course_enrollment(username, unicode(course_id), None)
                except EnterpriseApiException as error:
                    log.exception("An unexpected error occurred while creating the new EnterpriseCourseEnrollment "
                                  "for user [%s] in course run [%s]", username, course_id)
                    raise CourseEnrollmentError(text_type(error))
                kwargs = {
                    'username': username,
                    'course_id': unicode(course_id),
                    'enterprise_customer_uuid': explicit_linked_enterprise,
                }
                consent_client.provide_consent(**kwargs)

            enrollment_attributes = request.data.get('enrollment_attributes')
            enrollment = api.get_enrollment(username, unicode(course_id))
            mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
            missing_attrs = []
            if enrollment_attributes:
                actual_attrs = [
                    u"{namespace}:{name}".format(**attr)
                    for attr in enrollment_attributes
                ]
                missing_attrs = set(REQUIRED_ATTRIBUTES.get(mode, [])) - set(actual_attrs)
            if has_api_key_permissions and (mode_changed or active_changed):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                if len(missing_attrs) > 0:
                    msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
                        mode, REQUIRED_ATTRIBUTES.get(mode)
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                response = api.update_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    # If we are updating enrollment by authorized api caller, we should allow expired modes
                    include_expired=has_api_key_permissions
                )
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes
                )

            cohort_name = request.data.get('cohort')
            if cohort_name is not None:
                cohort = get_cohort_by_name(course_id, cohort_name)
                try:
                    add_user_to_cohort(cohort, user)
                except ValueError:
                    # user already in cohort, probably because they were un-enrolled and re-enrolled
                    log.exception('Cohort re-addition')
            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            log.info('The user [%s] has already been enrolled in course run [%s].', username, course_id)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The [{mode}] course mode is expired or otherwise unavailable for course run [{course_id}]."
                    ).format(mode=mode, course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            log.warning('An enrollment already exists for user [%s] in course run [%s].', username, course_id)
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            log.exception("An error occurred while creating the new course enrollment for user "
                          "[%s] in course run [%s]", username, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
        except CourseUserGroup.DoesNotExist:
            log.exception('Missing cohort [%s] in course run [%s]', cohort_name, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": "An error occured while adding to cohort [%s]" % cohort_name
                })
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            if has_api_key_permissions:
                current_enrollment = api.get_enrollment(username, unicode(course_id))
                audit_log(
                    'enrollment_change_requested',
                    course_id=unicode(course_id),
                    requested_mode=mode,
                    actual_mode=current_enrollment['mode'] if current_enrollment else None,
                    requested_activation=is_active,
                    actual_activation=current_enrollment['is_active'] if current_enrollment else None,
                    user_id=user.id
                )
Exemplo n.º 34
0
    def post(self, request):
        log.info(request.data)
        username = request.data.get('username')
        try:
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            log.error(u"User {username} does not exist".format(username=username))
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"User {username} does not exist".format(username=username)}
            )

        course_id = request.data.get('course_id')
        if not course_id:
            log.error(u"Course ID must be specified to create a new enrollment.")
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_key = CourseKey.from_string(course_id)
        except InvalidKeyError:
            log.error(u"No course '{course_id}' found for enrollment".format(course_id=course_id))
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        course_is_cohorted = is_course_cohorted(course_key)
        if not course_is_cohorted:
            log.info(u"Course {course_id} is not cohorted.".format(course_id=course_id))
            return Response(
                status=status.HTTP_200_OK,
                data={"message": u"Course {course_id} is not cohorted.".format(course_id=course_id)}
            )

        action = request.data.get('action')
        if action not in [u'add', u'delete']:
            log.error(u"Available actions are 'add' and 'delete'.")
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Available actions are 'add' and 'delete'."}
            )

        cohort_exists = is_cohort_exists(course_key, VERIFIED)
        if not cohort_exists:
            if action == u'add':
                log.info(u"Cohort VERIFIED doesn't exist for course {} so let's create it!".format(course_id))
                cohort = add_cohort(course_key, VERIFIED, 'manual')
                log.info(u"Cohort VEIFIED created for the course {}".format(course_id))
            else:
                log.info(u"There aren't cohort verified for {course_id}".format(course_id=course_id))
                return Response(
                    status=status.HTTP_200_OK,
                    data={"message": u"There aren't cohort verified for {course_id}".format(course_id=course_id)}
                )
        else:
            cohort = get_cohort_by_name(course_key, VERIFIED)

        enrollment = CourseEnrollment.objects.get(
            user__username=username, course_id=course_key
        )
        if not enrollment or not enrollment.is_active:
            if action == u'add':
                log.error(u"Failed to add user into verified cohort. User {username} not enrolled or unenrolled in course {course_id}.".format(username=username, course_id=course_id))
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={"message": u"User {username} not enrolled or unenrolled in course {course_id}.".format(
                        username=username,
                        course_id=course_id
                    )}
                )
            if action == u'delete':
                if not enrollment:
                    log.info(u"User {username} is not enrolled in course {course_id}. (!!!)".format(username=username, course_id=course_id))
                    return Response(
                        status=status.HTTP_200_OK,
                        data={"message": u"User {username} is not enrolled in course {course_id}. (!!!)".format(
                            username=username,
                            course_id=course_id
                        )}
                    )
                else:
                    log.info(u"User {username} was unenrolled from course {course_id}.".format(username=username, course_id=course_id))
                    return Response(
                        status=status.HTTP_200_OK,
                        data={"message": u"User {username} was unenrolled from course {course_id}.".format(
                            username=username,
                            course_id=course_id
                        )}
                    )

        course_cohorts = CourseUserGroup.objects.filter(
            course_id=course_key,
            users__id=user.id,
            group_type=CourseUserGroup.COHORT
        )

        default_group = None
        for group in CourseUserGroup.objects.filter(course_id=course_key, group_type=CourseUserGroup.COHORT):
            if group.name.lower() == "default" or group.name.lower() == "default group":
                default_group = group
        if not default_group:
            log.info(u"Cohort DEFAULT doesn't exist for course {} so let's create it!".format(course_id))
            default_group = add_cohort(course_key, "Default Group", 'random')
            log.info(u"Cohort 'Default Group' succesfully created for the course {}".format(course_id))

        # remove user from verified cohort and add to default
        if action == u'delete':
            # let's check, that user not already presented into other cohort
            if course_cohorts.exists():
                if course_cohorts.first().name == default_group.name:
                    log.warning(
                        u"User {username} already present into default cohort {cohort_name} in course {course_id}".format(
                            username=username, cohort_name=default_group.name, course_id=course_id))
                    return Response(
                        status=status.HTTP_200_OK,
                        data={
                            "message": u"User {username} already present into default cohort {cohort_name} in course {course_id}".format(
                                username=username,
                                cohort_name=default_group.name,
                                course_id=course_id
                            )}
                    )
                elif course_cohorts.first().name == VERIFIED:
                    try:
                        add_user_to_cohort(default_group, username)
                        log.info(
                            u"User {username} succesfully moved into default cohort {cohort_name} in course {course_id}".format(
                                username=username, cohort_name=default_group.name, course_id=course_id))
                    except ValueError:
                        log.warning(
                            u"User {username} already present into default cohort {cohort_name} in course {course_id}".format(
                                username=username, cohort_name=default_group.name, course_id=course_id))
                    return Response(
                        status=status.HTTP_200_OK,
                        data={
                            "message": u"User {username} moved into default cohort {cohort_name} in course {course_id}".format(
                                username=username,
                                cohort_name=default_group.name,
                                course_id=course_id
                            )}
                    )
                else:
                    log.warning(u"Moving user {username} into default cohort {cohort_name} from verified in course {course_id}".format(username=username, cohort_name=default_group.name, course_id=course_id))
                    try:
                        add_user_to_cohort(default_group, username)
                        log.info(u"User {username} succesfully moved into default cohort {cohort_name} in course {course_id}".format(username=username, cohort_name=default_group.name, course_id=course_id))
                    except ValueError:
                        log.warning(u"User {username} already present into default cohort {cohort_name} in course {course_id}".format(username=username, cohort_name=default_group.name, course_id=course_id))
                    return Response(
                        status=status.HTTP_200_OK,
                        data={"message": u"User {username} already present in non-verified cohort {cohort_name} in course {course_id}".format(
                                username=username, cohort_name=course_cohorts.first().name, course_id=course_id
                        )}
                    )

        if action == u"add":
            message = add_user_into_verified_cohort(course_cohorts, cohort, user)
            if not message:
                message = u"User {username} added to cohort {cohort_name} into course {course_id}".format(username=user.username, cohort_name=cohort.name, course_id=course_id)
            log.info(message)
            return Response(
                status=status.HTTP_200_OK,
                data={"message":message}
            )
Exemplo n.º 35
0
    def set_cohort(self, cohort):
        current_cohort = self.get_cohort()

        if cohort.id != current_cohort.id:
            add_user_to_cohort(cohort, self.user.email)
Exemplo n.º 36
0
def add_user_into_verified_cohort(course_cohorts, cohort, user):
    try:
        add_user_to_cohort(cohort, user.username)
    except ValueError as e:
        log.warning("User {} already present in the cohort {}".format(user.username, cohort.name))
        return str(e)
Exemplo n.º 37
0
    def handle(self, *args, **options):

        self.stdout.write('### Checking CourseUserGroup group types\n')
        error = False
        for course_group in CourseUserGroup.objects.all():
            if course_group.group_type != CourseUserGroup.COHORT:
                if options['fix']:
                    self.stdout.write(
                        'Fixed: CourseUserGroup with an invalid group_type found: {} (type: {})\n'
                        .format(course_group.name, course_group.group_type))
                    course_group.group_type = CourseUserGroup.COHORT
                    course_group.save()
                else:
                    error = True
                    self.stdout.write(
                        'CourseUserGroup with an invalid group_type found: {} (type: {})\n'
                        .format(course_group.name, course_group.group_type))

        if not error:
            self.stdout.write('Ok.\n')

        self.stdout.write('\n### Checking user cohorts\n')
        error = False
        users = User.objects.all()
        _courses = modulestore().get_courses()
        courses = [c for c in _courses if isinstance(c, CourseDescriptor)]
        # for each course, check if users are in atleast and only 1 cohort
        for course in courses:
            for user in users:
                if not CourseEnrollment.is_enrolled(user, course.id):
                    continue
                try:
                    CourseUserGroup.objects.get(course_id=course.id,
                                                users__id=user.id)
                except CourseUserGroup.DoesNotExist:
                    if options['fix']:
                        # create a "default_cohort" is it doesn't already exist
                        try:
                            default_cohort = get_cohort_by_name(
                                course.id, CourseUserGroup.default_cohort_name)
                        except CourseUserGroup.DoesNotExist:
                            default_cohort = add_cohort(
                                course.id, CourseUserGroup.default_cohort_name,
                                CourseCohort.RANDOM)
                            self.stdout.write(
                                'Default cohort "{}" created for course "{}"'.
                                format(default_cohort.name,
                                       course.display_name))
                        add_user_to_cohort(default_cohort, user.username)
                        self.stdout.write(
                            'Fixed: User "{}" is not in a cohort in course "{}". Added in "{}" cohort\n'
                            .format(user.username, course.display_name,
                                    default_cohort.name))
                    else:
                        error = True
                        self.stdout.write(
                            'User "{}" is not in a cohort in course "{}".\n'.
                            format(user.username, course.display_name))
                except MultipleObjectsReturned:
                    self.stdout.write(
                        'User "{}" is in multiple cohorts in course "{}".\n'.
                        format(user.username, course.display_name))
                    if options['fix']:
                        user_cohorts = CourseUserGroup.objects.filter(
                            course_id=course.id, users__id=user.id).all()
                        for cohort in user_cohorts[1:]:
                            remove_user_from_cohort(cohort, user.username)
                            self.stdout.write(
                                "User '{}' has been removed from cohort '{}' in course '{}'.\n"
                                .format(user.username, cohort.name,
                                        course.display_name))
                        self.stdout.write(
                            "User '{}' is now only in cohort '{}' in course '{}'.\n"
                            .format(
                                # pylint: disable=undefined-loop-variable
                                user.username,
                                cohort.name,
                                course.display_name))
                    else:
                        error = True

        if not error:
            self.stdout.write('Ok.\n')

        self.stdout.write(
            '\nTo fix issues, run the script with the "--fix" option.\n')
Exemplo n.º 38
0
 def test_with_custom_cohort(self):
     add_user_to_cohort(self.cohort, self.model.user.email)
     self.assertEquals(1, self.cohort.users.count())
     self.assertEquals(self.model.get_cohort().id, self.cohort.id)
Exemplo n.º 39
0
    def users(self, request, pk):
        """
        Add a User to a Workgroup
        """
        if request.method == 'GET':
            users = User.objects.filter(workgroups=pk)
            response_data = []
            if users:
                for user in users:
                    serializer = UserSerializer(user,
                                                context={'request': request})
                    response_data.append(serializer.data)  # pylint: disable=E1101
            return Response(response_data, status=status.HTTP_200_OK)
        elif request.method == 'POST':
            user_id = request.data.get('id')
            try:
                user = User.objects.get(id=user_id)
            except ObjectDoesNotExist:
                message = 'User {} does not exist'.format(user_id)
                return Response({"detail": message},
                                status.HTTP_400_BAD_REQUEST)

            workgroup = self.get_object()

            # Ensure the user is not already assigned to a project for this course
            existing_projects = Project.objects.filter(
                course_id=workgroup.project.course_id).filter(
                    workgroups__users__id=user.id)
            if len(existing_projects):
                message = 'User {} already assigned to a project for this course'.format(
                    user_id)
                return Response({"detail": message},
                                status.HTTP_400_BAD_REQUEST)

            try:
                workgroup.add_user(user)
            except ValidationError as e:
                return Response({"detail": unicode(e)},
                                status.HTTP_400_BAD_REQUEST)

            workgroup.save()

            # add user to the workgroup cohort, create it if it doesn't exist (for cases where there is a legacy
            # workgroup)
            course_key = get_course_key(workgroup.project.course_id)
            try:
                cohort = get_cohort_by_name(course_key, workgroup.cohort_name)
                add_user_to_cohort(cohort, user.username)
            except ObjectDoesNotExist:
                # This use case handles cases where a workgroup might have been created before
                # the notion of a cohorted discussion. So we need to backfill in the data
                assignment_type = request.data.get('assignment_type',
                                                   CourseCohort.RANDOM)
                if assignment_type not in dict(
                        CourseCohort.ASSIGNMENT_TYPE_CHOICES).keys():
                    message = "Not a valid assignment type, '{}'".format(
                        assignment_type)
                    return Response({"detail": message},
                                    status.HTTP_400_BAD_REQUEST)
                workgroup = self.get_object()
                cohort = add_cohort(course_key, workgroup.cohort_name,
                                    assignment_type)
                for workgroup_user in workgroup.users.all():
                    add_user_to_cohort(cohort, workgroup_user.username)
            return Response({}, status=status.HTTP_201_CREATED)
        else:
            user_id = request.data.get('id')
            try:
                user = User.objects.get(id=user_id)
            except ObjectDoesNotExist:
                message = 'User {} does not exist'.format(user_id)
                return Response({"detail": message},
                                status.HTTP_400_BAD_REQUEST)
            workgroup = self.get_object()
            course_key = get_course_key(workgroup.project.course_id)
            cohort = get_cohort_by_name(course_key, workgroup.cohort_name)
            workgroup.remove_user(user)
            remove_user_from_cohort(cohort, user.username)
            return Response({}, status=status.HTTP_204_NO_CONTENT)
Exemplo n.º 40
0
def cohort_students_and_upload(_xmodule_instance_args, _entry_id, course_id,
                               task_input, action_name):
    """
    Within a given course, cohort students in bulk, then upload the results
    using a `ReportStore`.
    """
    start_time = time()
    start_date = datetime.now(UTC)

    # Iterate through rows to get total assignments for task progress
    with DefaultStorage().open(task_input['file_name']) as f:
        total_assignments = 0
        for _line in unicodecsv.DictReader(UniversalNewlineIterator(f)):
            total_assignments += 1

    task_progress = TaskProgress(action_name, total_assignments, start_time)
    current_step = {'step': 'Cohorting Students'}
    task_progress.update_task_state(extra_meta=current_step)

    # cohorts_status is a mapping from cohort_name to metadata about
    # that cohort.  The metadata will include information about users
    # successfully added to the cohort, users not found, and a cached
    # reference to the corresponding cohort object to prevent
    # redundant cohort queries.
    cohorts_status = {}

    with DefaultStorage().open(task_input['file_name']) as f:
        for row in unicodecsv.DictReader(UniversalNewlineIterator(f),
                                         encoding='utf-8'):
            # Try to use the 'email' field to identify the user.  If it's not present, use 'username'.
            username_or_email = row.get('email') or row.get('username')
            cohort_name = row.get('cohort') or ''
            task_progress.attempted += 1

            if not cohorts_status.get(cohort_name):
                cohorts_status[cohort_name] = {
                    'Cohort Name': cohort_name,
                    'Students Added': 0,
                    'Students Not Found': set()
                }
                try:
                    cohorts_status[cohort_name][
                        'cohort'] = CourseUserGroup.objects.get(
                            course_id=course_id,
                            group_type=CourseUserGroup.COHORT,
                            name=cohort_name)
                    cohorts_status[cohort_name]["Exists"] = True
                except CourseUserGroup.DoesNotExist:
                    cohorts_status[cohort_name]["Exists"] = False

            if not cohorts_status[cohort_name]['Exists']:
                task_progress.failed += 1
                continue

            try:
                with transaction.commit_on_success():
                    add_user_to_cohort(cohorts_status[cohort_name]['cohort'],
                                       username_or_email)
                cohorts_status[cohort_name]['Students Added'] += 1
                task_progress.succeeded += 1
            except User.DoesNotExist:
                cohorts_status[cohort_name]['Students Not Found'].add(
                    username_or_email)
                task_progress.failed += 1
            except ValueError:
                # Raised when the user is already in the given cohort
                task_progress.skipped += 1

            task_progress.update_task_state(extra_meta=current_step)

    current_step['step'] = 'Uploading CSV'
    task_progress.update_task_state(extra_meta=current_step)

    # Filter the output of `add_users_to_cohorts` in order to upload the result.
    output_header = [
        'Cohort Name', 'Exists', 'Students Added', 'Students Not Found'
    ]
    output_rows = [[
        ','.join(status_dict.get(column_name, ''))
        if column_name == 'Students Not Found' else status_dict[column_name]
        for column_name in output_header
    ] for _cohort_name, status_dict in cohorts_status.iteritems()]
    output_rows.insert(0, output_header)
    upload_csv_to_report_store(output_rows, 'cohort_results', course_id,
                               start_date)

    return task_progress.update_task_state(extra_meta=current_step)
Exemplo n.º 41
0
    def post(self, request):
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.

        username = request.data.get('user', request.user.username)
        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        mode = request.data.get('mode')

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(
                status=status.HTTP_406_NOT_ACCEPTABLE,
                data={
                    'message': u'The user {} does not exist.'.format(username)
                }
            )

        can_vip_enroll = False
        if settings.FEATURES.get('ENABLE_MEMBERSHIP_INTEGRATION'):
            from membership.models import VIPCourseEnrollment
            can_vip_enroll = VIPCourseEnrollment.can_vip_enroll(user, course_id)
        
        is_ecommerce_request = mode not in (CourseMode.AUDIT, CourseMode.HONOR, None)
        if is_ecommerce_request and not has_api_key_permissions and not can_vip_enroll:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": u"User does not have permission to create enrollment with mode [{mode}].".format(
                        mode=mode
                    )
                }
            )

        embargo_response = embargo_api.get_embargo_response(request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message': (u"'{value}' is an invalid enrollment activation status.").format(value=is_active)
                    }
                )

            explicit_linked_enterprise = request.data.get('linked_enterprise_customer')
            if explicit_linked_enterprise and has_api_key_permissions and enterprise_enabled():
                enterprise_api_client = EnterpriseApiServiceClient()
                consent_client = ConsentApiServiceClient()
                try:
                    enterprise_api_client.post_enterprise_course_enrollment(username, unicode(course_id), None)
                except EnterpriseApiException as error:
                    log.exception("An unexpected error occurred while creating the new EnterpriseCourseEnrollment "
                                  "for user [%s] in course run [%s]", username, course_id)
                    raise CourseEnrollmentError(text_type(error))
                kwargs = {
                    'username': username,
                    'course_id': unicode(course_id),
                    'enterprise_customer_uuid': explicit_linked_enterprise,
                }
                consent_client.provide_consent(**kwargs)

            enrollment_attributes = request.data.get('enrollment_attributes')
            enrollment = api.get_enrollment(username, unicode(course_id))
            mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
            missing_attrs = []
            audit_with_order = False
            if enrollment_attributes:
                actual_attrs = [
                    u"{namespace}:{name}".format(**attr)
                    for attr in enrollment_attributes
                ]
                missing_attrs = set(REQUIRED_ATTRIBUTES.get(mode, [])) - set(actual_attrs)
                audit_with_order = mode == 'audit' and 'order:order_number' in actual_attrs
            # Remove audit_with_order when no longer needed - implemented for REV-141
            if has_api_key_permissions and (mode_changed or active_changed or audit_with_order):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                if len(missing_attrs) > 0:
                    msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
                        mode, REQUIRED_ATTRIBUTES.get(mode)
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                response = api.update_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    # If we are updating enrollment by authorized api caller, we should allow expired modes
                    include_expired=has_api_key_permissions
                )
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    user=user,
                    is_ecommerce_request=is_ecommerce_request
                )

            cohort_name = request.data.get('cohort')
            if cohort_name is not None:
                cohort = get_cohort_by_name(course_id, cohort_name)
                add_user_to_cohort(cohort, user)
            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            log.info('The user [%s] has already been enrolled in course run [%s].', username, course_id)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The [{mode}] course mode is expired or otherwise unavailable for course run [{course_id}]."
                    ).format(mode=mode, course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            log.warning('An enrollment already exists for user [%s] in course run [%s].', username, course_id)
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            log.exception("An error occurred while creating the new course enrollment for user "
                          "[%s] in course run [%s]", username, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
        except CourseUserGroup.DoesNotExist:
            log.exception('Missing cohort [%s] in course run [%s]', cohort_name, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": "An error occured while adding to cohort [%s]" % cohort_name
                })
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            if has_api_key_permissions:
                current_enrollment = api.get_enrollment(username, unicode(course_id))
                audit_log(
                    'enrollment_change_requested',
                    course_id=unicode(course_id),
                    requested_mode=mode,
                    actual_mode=current_enrollment['mode'] if current_enrollment else None,
                    requested_activation=is_active,
                    actual_activation=current_enrollment['is_active'] if current_enrollment else None,
                    user_id=user.id
                )
Exemplo n.º 42
0
    def users(self, request, pk):
        """
        Add a User to a Workgroup
        """
        if request.method == "GET":
            users = User.objects.filter(workgroups=pk)
            response_data = []
            if users:
                for user in users:
                    serializer = UserSerializer(user)
                    response_data.append(serializer.data)  # pylint: disable=E1101
            return Response(response_data, status=status.HTTP_200_OK)
        elif request.method == "POST":
            user_id = request.DATA.get("id")
            try:
                user = User.objects.get(id=user_id)
            except ObjectDoesNotExist:
                message = "User {} does not exist".format(user_id)
                return Response({"detail": message}, status.HTTP_400_BAD_REQUEST)

            workgroup = self.get_object()

            # Ensure the user is not already assigned to a project for this course
            existing_projects = Project.objects.filter(course_id=workgroup.project.course_id).filter(
                workgroups__users__id=user.id
            )
            if len(existing_projects):
                message = "User {} already assigned to a project for this course".format(user_id)
                return Response({"detail": message}, status.HTTP_400_BAD_REQUEST)

            try:
                workgroup.add_user(user)
            except ValidationError as e:
                return Response({"detail": unicode(e)}, status.HTTP_400_BAD_REQUEST)

            workgroup.save()

            # add user to the workgroup cohort, create it if it doesn't exist (for cases where there is a legacy
            # workgroup)
            course_descriptor, course_key, course_content = _get_course(
                self.request, user, workgroup.project.course_id
            )  # pylint: disable=W0612
            try:
                cohort = get_cohort_by_name(course_key, workgroup.cohort_name)
                add_user_to_cohort(cohort, user.username)
            except ObjectDoesNotExist:
                # This use case handles cases where a workgroup might have been created before
                # the notion of a cohorted discussion. So we need to backfill in the data
                assignment_type = request.DATA.get("assignment_type", CourseCohort.RANDOM)
                if assignment_type not in dict(CourseCohort.ASSIGNMENT_TYPE_CHOICES).keys():
                    message = "Not a valid assignment type, '{}'".format(assignment_type)
                    return Response({"detail": message}, status.HTTP_400_BAD_REQUEST)
                cohort = add_cohort(course_key, workgroup.cohort_name, assignment_type)
                for workgroup_user in workgroup.users.all():
                    add_user_to_cohort(cohort, workgroup_user.username)
            return Response({}, status=status.HTTP_201_CREATED)
        else:
            user_id = request.DATA.get("id")
            try:
                user = User.objects.get(id=user_id)
            except ObjectDoesNotExist:
                message = "User {} does not exist".format(user_id)
                return Response({"detail": message}, status.HTTP_400_BAD_REQUEST)
            workgroup = self.get_object()
            course_descriptor, course_key, course_content = _get_course(
                self.request, user, workgroup.project.course_id
            )  # pylint: disable=W0612
            cohort = get_cohort_by_name(course_key, workgroup.cohort_name)
            workgroup.remove_user(user)
            remove_user_from_cohort(cohort, user.username)
            return Response({}, status=status.HTTP_204_NO_CONTENT)