Exemplo n.º 1
0
 def validate_user_id(self, user_id):
     try:
         self.user = get_user_by_username_or_email(user_id)
         return user_id
     except DjangoUser.DoesNotExist:
         raise ValidationError(
             u"'{}' is not a valid student identifier".format(user_id))
Exemplo n.º 2
0
def remove_user_from_cohort(cohort, username_or_email):
    """
    Look up the given user, and if successful, remove them from the specified cohort.

    Arguments:
        cohort: CourseUserGroup
        username_or_email: string.  Treated as email if has '@'

    Raises:
        User.DoesNotExist if can't find user.
        ValueError if user not already present in this cohort.
    """
    user = get_user_by_username_or_email(username_or_email)

    try:
        membership = CohortMembership.objects.get(course_user_group=cohort,
                                                  user=user)
        course_key = membership.course_id
        membership.delete()
        COHORT_MEMBERSHIP_UPDATED.send(sender=None,
                                       user=user,
                                       course_key=course_key)
    except CohortMembership.DoesNotExist:
        raise ValueError(u"User {} was not present in cohort {}".format(
            username_or_email, cohort))
Exemplo n.º 3
0
 def validate_user_id(self, user_id):  # lint-amnesty, pylint: disable=missing-function-docstring
     try:
         self.user = get_user_by_username_or_email(user_id)
         return user_id
     except User.DoesNotExist:
         raise ValidationError(
             f"'{user_id}' is not a valid student identifier")  # lint-amnesty, pylint: disable=raise-missing-from
Exemplo n.º 4
0
def get_student_from_identifier(unique_student_identifier):
    """
    Gets a student object using either an email address or username.

    Returns the student object associated with `unique_student_identifier`

    Raises User.DoesNotExist if no user object can be found, the user was
    retired, or the user is in the process of being retired.

    DEPRECATED: use student.models.get_user_by_username_or_email instead.
    """
    return get_user_by_username_or_email(unique_student_identifier)
Exemplo n.º 5
0
    def delete_student_attempt(self, student_identifier, course_id, content_id,
                               requesting_user):
        """
        Deletes student state for a problem. requesting_user may be kept as an audit trail.

        Takes some of the following query parameters
            - student_identifier is an email or username
            - content_id is a url-name of a problem
            - course_id is the id for the course
        """
        course_id = CourseKey.from_string(course_id)

        try:
            student = get_user_by_username_or_email(student_identifier)
        except ObjectDoesNotExist:
            err_msg = (
                'Error occurred while attempting to reset student attempts for user '
                f'{student_identifier} for content_id {content_id}. '
                'User does not exist!')
            log.error(err_msg)
            return

        try:
            module_state_key = UsageKey.from_string(content_id)
        except InvalidKeyError:
            err_msg = (f'Invalid content_id {content_id}!')
            log.error(err_msg)
            return

        if student:
            try:
                enrollment.reset_student_attempts(
                    course_id,
                    student,
                    module_state_key,
                    requesting_user=requesting_user,
                    delete_module=True,
                )
            except (StudentModule.DoesNotExist,
                    enrollment.sub_api.SubmissionError):
                err_msg = (
                    'Error occurred while attempting to reset student attempts for user '
                    f'{student_identifier} for content_id {content_id}.')
                log.error(err_msg)

            # In some cases, reset_student_attempts does not clear the entire exam's completion state.
            # One example of this is an exam with multiple units (verticals) within it and the learner
            # never viewing one of the units. All of the content in that unit will still be marked complete,
            # but the reset code is unable to handle clearing the completion in that scenario.
            update_exam_completion_task.apply_async(
                (student_identifier, content_id, 0.0))
Exemplo n.º 6
0
    def validate_user_id(self, user_id):
        """
        Validate user id
        Args:
            user_id (str): username or email

        Returns:
            str: user id if valid
        """
        try:
            self.user = get_user_by_username_or_email(user_id)
            return user_id
        except User.DoesNotExist as err:
            raise ValidationError(
                f"'{user_id}' is not a valid student identifier") from err
Exemplo n.º 7
0
def get_user_group_ids(course_id, content, user=None):
    """
    Given a user, course ID, and the content of the thread or comment, returns the group ID for the current user
    and the user that posted the thread/comment.
    """
    content_user_group_id = None
    user_group_id = None
    if course_id is not None:
        if content.get('username'):
            try:
                content_user = get_user_by_username_or_email(content.get('username'))
                content_user_group_id = get_group_id_for_user_from_cache(content_user, course_id)
            except User.DoesNotExist:
                content_user_group_id = None

        user_group_id = get_group_id_for_user_from_cache(user, course_id) if user else None
    return user_group_id, content_user_group_id
Exemplo n.º 8
0
    def get(self, request, course_id=None, username=None):
        """ Return user-metadata for the given course and user """
        try:
            user = get_user_by_username_or_email(username)
        except User.DoesNotExist:
            # Note: this will only be seen by staff, for administrative de-bugging purposes
            message = "Provided user is not found"
            return JsonResponse({'message': message}, status=404)

        try:
            course = courses.get_course_by_id(CourseKey.from_string(course_id))
        except Http404:
            message = "Provided course is not found"
            return JsonResponse({'message': message}, status=404)

        context = get_experiment_user_metadata_context(course, user)
        user_metadata = context.get('user_metadata')
        return JsonResponse(user_metadata)
    def delete_student_attempt(self, student_identifier, course_id, content_id,
                               requesting_user):
        """
        Deletes student state for a problem. requesting_user may be kept as an audit trail.

        Takes some of the following query parameters
            - student_identifier is an email or username
            - content_id is a url-name of a problem
            - course_id is the id for the course
        """
        course_id = CourseKey.from_string(course_id)

        try:
            student = get_user_by_username_or_email(student_identifier)
        except ObjectDoesNotExist:
            err_msg = (
                'Error occurred while attempting to reset student attempts for user '
                f'{student_identifier} for content_id {content_id}. '
                'User does not exist!')
            log.error(err_msg)
            return

        try:
            module_state_key = UsageKey.from_string(content_id)
        except InvalidKeyError:
            err_msg = (f'Invalid content_id {content_id}!')
            log.error(err_msg)
            return

        if student:
            try:
                enrollment.reset_student_attempts(
                    course_id,
                    student,
                    module_state_key,
                    requesting_user=requesting_user,
                    delete_module=True,
                )
            except (StudentModule.DoesNotExist,
                    enrollment.sub_api.SubmissionError):
                err_msg = (
                    'Error occurred while attempting to reset student attempts for user '
                    f'{student_identifier} for content_id {content_id}.')
                log.error(err_msg)
    def get_anonymous_user_id(self, username, course_id):
        """
        Get the anonymous user id for a user.

        Args:
            username(str): username of a user.
            course_id(str): course id of particular course.

        Returns:
            A unique anonymous_user_id for (user, course) pair.
            None for Non-staff users.
        """
        if not self.get_current_user().opt_attrs.get(ATTR_KEY_USER_IS_STAFF):
            return None

        try:
            user = get_user_by_username_or_email(username_or_email=username)
        except User.DoesNotExist:
            return None

        course_id = CourseKey.from_string(course_id)
        return anonymous_id_for_user(user=user, course_id=course_id)
Exemplo n.º 11
0
    def get_state_as_dict(self, username_or_email, block_id):
        """
        Return dict containing user state for a given set of parameters.

        Arguments:
            username_or_email: username or email of the user for whom the data is being retrieved
            block_id: string/object representation of the block whose user state is required

        Returns:
            Returns a dict containing user state, if present, else empty.
        """
        try:
            user = get_user_by_username_or_email(
                username_or_email=username_or_email)
        except User.DoesNotExist:
            return {}
        try:
            student_module = StudentModule.objects.get(
                student=user, module_state_key=block_id)
            return json.loads(student_module.state)
        except StudentModule.DoesNotExist:
            return {}
Exemplo n.º 12
0
    def get_quiz_data(self):
        pr_class = ProblemResponses().__class__
        user_id = user_by_anonymous_id(
            self.xmodule_runtime.anonymous_student_id).id
        course_key = self.course_id

        valid_cohorts = self.get_cohorts()

        usage_key = self.get_quiz_unit()
        if not usage_key:
            raise InvalidKeyError

        user = get_user_model().objects.get(pk=user_id)

        student_data = []

        store = modulestore()

        with store.bulk_operations(course_key):
            try:
                course_blocks = get_course_blocks(user, usage_key)
            except:
                raise QuizNotFound
            usernames = set()
            for title, path, block_key in pr_class._build_problem_list(
                    course_blocks, usage_key):
                # Chapter and sequential blocks are filtered out since they include state
                # which isn't useful for this report.
                if block_key.block_type != "problem":
                    continue

                block = store.get_item(block_key)
                generated_report_data = defaultdict(list)

                # Blocks can implement the generate_report_data method to provide their own
                # human-readable formatting for user state.
                try:
                    user_state_iterator = iter_all_for_block(block_key)
                    for username, state in self.generate_report_data(
                            block, user_state_iterator):
                        generated_report_data[username].append(state)
                except NotImplementedError:
                    pass
                cohorted = is_course_cohorted(self.course_id)

                def in_cohort(user):
                    if cohorted:
                        cohort = get_cohort(user,
                                            course_key,
                                            assign=False,
                                            use_cached=True)
                        if not cohort or cohort.name not in valid_cohorts or (
                                self.cohort and cohort.name != self.cohort):
                            # skip this one if not on the requested cohort or has no cohort (instructor)
                            return False
                    return True

                responses = []
                for response in list_problem_responses(course_key, block_key):
                    # A block that has a single state per user can contain multiple responses
                    # within the same state.
                    try:
                        user = get_user_by_username_or_email(
                            response['username'])
                    except User.DoesNotExist:
                        continue
                    usernames.add(user.username)
                    if not in_cohort(user):
                        continue
                    response['name'] = self.format_name(user.profile.name)
                    user_states = generated_report_data.get(
                        response['username'])
                    response['state'] = json.loads(response['state'])
                    response['state'].pop('input_state', None)
                    response['state'].pop('student_answers', None)

                    if user_states:
                        response['user_states'] = user_states
                    responses.append(response)
                enrollments = CourseEnrollment.objects.filter(
                    course_id=self.course_id)
                for enr in enrollments:
                    if enr.user.username in usernames:
                        continue
                    if in_cohort(enr.user):  # add missing students
                        student_data.append({
                            'username':
                            enr.user.username,
                            'name':
                            self.format_name(enr.user.profile.name)
                        })
                        usernames.add(enr.user.username)
                student_data += responses
        return student_data
Exemplo n.º 13
0
def complete_student_attempt_task(user_identifier: str, content_id: str) -> None:
    """
    Marks all completable children of content_id as complete for the user

    Submits all completable xblocks inside of the content_id block to the
    Completion Service to mark them as complete. One use case of this function is
    for special exams (timed/proctored) where regardless of submission status on
    individual problems, we want to mark the entire exam as complete when the exam
    is finished.

    params:
        user_identifier (str): username or email of a user
        content_id (str): the block key for a piece of content
    """
    err_msg_prefix = (
        'Error occurred while attempting to complete student attempt for user '
        f'{user_identifier} for content_id {content_id}. '
    )
    err_msg = None
    try:
        user = get_user_by_username_or_email(user_identifier)
        block_key = UsageKey.from_string(content_id)
        root_descriptor = modulestore().get_item(block_key)
    except ObjectDoesNotExist:
        err_msg = err_msg_prefix + 'User does not exist!'
    except InvalidKeyError:
        err_msg = err_msg_prefix + 'Invalid content_id!'
    except ItemNotFoundError:
        err_msg = err_msg_prefix + 'Block not found in the modulestore!'
    if err_msg:
        log.error(err_msg)
        return

    # This logic has been copied over from openedx/core/djangoapps/schedules/content_highlights.py
    # in the _get_course_module function.
    # I'm not sure if this is an anti-pattern or not, so if you can avoid re-copying this, please do.
    # We are using it here because we ran into issues with the User service being undefined when we
    # encountered a split_test xblock.

    # Fake a request to fool parts of the courseware that want to inspect it.
    request = get_request_or_stub()
    request.user = user

    # Now evil modulestore magic to inflate our descriptor with user state and
    # permissions checks.
    field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
        root_descriptor.course_id, user, root_descriptor, read_only=True,
    )
    root_module = get_module_for_descriptor(
        user, request, root_descriptor, field_data_cache, root_descriptor.course_id,
    )
    if not root_module:
        err_msg = err_msg_prefix + 'Module unable to be created from descriptor!'
        log.error(err_msg)
        return

    def _submit_completions(block, user):
        """
        Recursively submits the children for completion to the Completion Service
        """
        mode = XBlockCompletionMode.get_mode(block)
        if mode == XBlockCompletionMode.COMPLETABLE:
            block.runtime.publish(block, 'completion', {'completion': 1.0, 'user_id': user.id})
        elif mode == XBlockCompletionMode.AGGREGATOR:
            # I know this looks weird, but at the time of writing at least, there isn't a good
            # single way to get the children assigned for a partcular user. Some blocks define the
            # child descriptors method, but others don't and with blocks like Randomized Content
            # (Library Content), the get_children method returns all children and not just assigned
            # children. So this is our way around situations like that. See also Split Test Module
            # for another use case where user state has to be taken into account via get_child_descriptors
            block_children = ((hasattr(block, 'get_child_descriptors') and block.get_child_descriptors())
                              or (hasattr(block, 'get_children') and block.get_children())
                              or [])
            for child in block_children:
                _submit_completions(child, user)

    _submit_completions(root_module, user)
Exemplo n.º 14
0
def add_user_to_cohort(cohort, username_or_email_or_user):
    """
    Look up the given user, and if successful, add them to the specified cohort.

    Arguments:
        cohort: CourseUserGroup
        username_or_email_or_user: user or string.  Treated as email if has '@'

    Returns:
        User object (or None if the email address is preassigned),
        string (or None) indicating previous cohort,
        and whether the user is a preassigned user or not

    Raises:
        User.DoesNotExist if can't find user. However, if a valid email is provided for the user, it is stored
        in a database so that the user can be added to the cohort if they eventually enroll in the course.
        ValueError if user already present in this cohort.
        ValidationError if an invalid email address is entered.
        User.DoesNotExist if a user could not be found.
    """
    try:
        if hasattr(username_or_email_or_user, 'email'):
            user = username_or_email_or_user
        else:
            user = get_user_by_username_or_email(username_or_email_or_user)

        membership, previous_cohort = CohortMembership.assign(cohort, user)
        tracker.emit(
            "edx.cohort.user_add_requested",
            {
                "user_id": user.id,
                "cohort_id": cohort.id,
                "cohort_name": cohort.name,
                "previous_cohort_id": getattr(previous_cohort, 'id', None),
                "previous_cohort_name": getattr(previous_cohort, 'name', None),
            }
        )
        cache = RequestCache(COHORT_CACHE_NAMESPACE).data
        cache_key = _cohort_cache_key(user.id, membership.course_id)
        cache[cache_key] = membership.course_user_group
        COHORT_MEMBERSHIP_UPDATED.send(sender=None, user=user, course_key=membership.course_id)
        return user, getattr(previous_cohort, 'name', None), False
    except User.DoesNotExist as ex:
        # If username_or_email is an email address, store in database.
        try:
            validate_email(username_or_email_or_user)

            try:
                assignment = UnregisteredLearnerCohortAssignments.objects.get(
                    email=username_or_email_or_user, course_id=cohort.course_id
                )
                assignment.course_user_group = cohort
                assignment.save()
            except UnregisteredLearnerCohortAssignments.DoesNotExist:
                assignment = UnregisteredLearnerCohortAssignments.objects.create(
                    course_user_group=cohort, email=username_or_email_or_user, course_id=cohort.course_id
                )

            tracker.emit(
                "edx.cohort.email_address_preassigned",
                {
                    "user_email": assignment.email,
                    "cohort_id": cohort.id,
                    "cohort_name": cohort.name,
                }
            )

            return (None, None, True)
        except ValidationError as invalid:
            if "@" in username_or_email_or_user:  # lint-amnesty, pylint: disable=no-else-raise
                raise invalid
            else:
                raise ex  # lint-amnesty, pylint: disable=raise-missing-from
 def get_student():
     """ Fetches student instance if an identifier is provided, else return None """
     return None if not student_identifier else get_user_by_username_or_email(
         student_identifier)