Exemple #1
0
def iter_all_for_block(block_key, scope=Scope.user_state):
    """
    Return an iterator over the data stored in the block (e.g. a problem block).

    You get no ordering guarantees.If you're using this method, you should be running in an
    async task.

    Arguments:
        block_key: an XBlock's locator (e.g. :class:`~BlockUsageLocator`)
        scope (Scope): must be `Scope.user_state`

    Returns:
        an iterator over all data. Each invocation returns the next :class:`~XBlockUserState`
            object, which includes the block's contents.
    """
    if scope != Scope.user_state:
        raise ValueError("Only Scope.user_state is supported")

    results = StudentModule.objects.order_by('id').filter(
        module_state_key=block_key)
    p = Paginator(results, settings.USER_STATE_BATCH_SIZE)

    for page_number in p.page_range:
        page = p.page(page_number)

        for sm in page.object_list:
            state = json.loads(sm.state)

            if state == {}:
                continue
            try:
                yield XBlockUserState(sm.student.username, sm.module_state_key,
                                      state, sm.modified, scope)
            except User.DoesNotExist:
                pass
    def get_many(self,
                 username,
                 block_keys,
                 scope=Scope.user_state,
                 fields=None):
        """
        Retrieve the stored XBlock state for the specified XBlock usages.

        Arguments:
            username: The name of the user whose state should be retrieved
            block_keys ([UsageKey]): A list of UsageKeys identifying which xblock states to load.
            scope (Scope): The scope to load data from
            fields: A list of field values to retrieve. If None, retrieve all stored fields.

        Yields:
            XBlockUserState tuples for each specified UsageKey in block_keys.
            field_state is a dict mapping field names to values.
        """
        if scope != Scope.user_state:
            raise ValueError(
                "Only Scope.user_state is supported, not {}".format(scope))

        block_count = state_length = 0
        evt_time = time()

        self._ddog_histogram(evt_time, 'get_many.blks_requested',
                             len(block_keys))

        modules = self._get_student_modules(username, block_keys)
        for module, usage_key in modules:
            if module.state is None:
                self._ddog_increment(evt_time, 'get_many.empty_state')
                continue

            state = json.loads(module.state)
            state_length += len(module.state)

            self._ddog_histogram(evt_time, 'get_many.block_size',
                                 len(module.state))

            # If the state is the empty dict, then it has been deleted, and so
            # conformant UserStateClients should treat it as if it doesn't exist.
            if state == {}:
                continue

            if fields is not None:
                state = {
                    field: state[field]
                    for field in fields if field in state
                }
            block_count += 1
            yield XBlockUserState(username, usage_key, state, module.modified,
                                  scope)

        # The rest of this method exists only to submit DataDog events.
        # Remove it once we're no longer interested in the data.
        finish_time = time()
        self._ddog_histogram(evt_time, 'get_many.blks_out', block_count)
        self._ddog_histogram(evt_time, 'get_many.response_time',
                             (finish_time - evt_time) * 1000)
Exemple #3
0
    def iter_all_for_course(self, course_key, block_type=None, scope=Scope.user_state):
        """
        Return an iterator over all data stored in a course's blocks.

        You get no ordering guarantees. If you're using this method, you should be running in an
        async task.

        Arguments:
            course_key: a course locator
            scope (Scope): must be `Scope.user_state`

        Returns:
            an iterator over all data. Each invocation returns the next :class:`~XBlockUserState`
                object, which includes the block's contents.
        """
        if scope != Scope.user_state:
            raise ValueError("Only Scope.user_state is supported")

        results = StudentModule.objects.order_by('id').filter(course_id=course_key)
        if block_type:
            results = results.filter(module_type=block_type)

        p = Paginator(results, settings.USER_STATE_BATCH_SIZE)

        for page_number in p.page_range:
            page = p.page(page_number)

            for sm in page.object_list:
                state = json.loads(sm.state)

                if state == {}:
                    continue

                yield XBlockUserState(sm.student.username, sm.module_state_key, state, sm.modified, scope)
    def get_many(self, username, block_keys, scope=Scope.user_state, fields=None):
        """
        Retrieve the stored XBlock state for the specified XBlock usages.

        Arguments:
            username: The name of the user whose state should be retrieved
            block_keys ([UsageKey]): A list of UsageKeys identifying which xblock states to load.
            scope (Scope): The scope to load data from
            fields: A list of field values to retrieve. If None, retrieve all stored fields.

        Yields:
            XBlockUserState tuples for each specified UsageKey in block_keys.
            field_state is a dict mapping field names to values.
        """
        if scope != Scope.user_state:
            raise ValueError(u"Only Scope.user_state is supported, not {}".format(scope))

        total_block_count = 0
        evt_time = time()

        # count how many times this function gets called
        self._nr_stat_increment('get_many', 'calls')

        # keep track of blocks requested
        self._nr_stat_accumulate('get_many', 'blocks_requested', len(block_keys))

        modules = self._get_student_modules(username, block_keys)
        for module, usage_key in modules:
            if module.state is None:
                continue

            state = json.loads(module.state)
            state_length = len(module.state)

            # If the state is the empty dict, then it has been deleted, and so
            # conformant UserStateClients should treat it as if it doesn't exist.
            if state == {}:
                continue

            # collect statistics for metric reporting
            self._nr_block_stat_increment('get_many', usage_key.block_type, 'blocks_out')
            self._nr_block_stat_accumulate('get_many', usage_key.block_type, 'size', state_length)
            total_block_count += 1

            # filter state on fields
            if fields is not None:
                state = {
                    field: state[field]
                    for field in fields
                    if field in state
                }
            yield XBlockUserState(username, usage_key, state, module.modified, scope)

        # The rest of this method exists only to report metrics.
        finish_time = time()
        duration = (finish_time - evt_time) * 1000  # milliseconds
        self._nr_stat_accumulate('get_many', 'duration', duration)
 def _add_state(self, username, block_key, scope, state):
     """
     Add the specified state to the state history of this block.
     """
     history_list = self._history.setdefault((username, block_key, scope),
                                             [])
     history_list.insert(
         0,
         XBlockUserState(username, block_key, state, datetime.now(pytz.utc),
                         scope))
    def get_history(self, username, block_key, scope=Scope.user_state):
        """
        Retrieve history of state changes for a given block for a given
        student.  We don't guarantee that history for many blocks will be fast.

        If the specified block doesn't exist, raise :class:`~DoesNotExist`.

        Arguments:
            username: The name of the user whose history should be retrieved.
            block_key: The key identifying which xblock history to retrieve.
            scope (Scope): The scope to load data from.

        Yields:
            XBlockUserState entries for each modification to the specified XBlock, from latest
            to earliest.
        """

        if scope != Scope.user_state:
            raise ValueError("Only Scope.user_state is supported")
        student_modules = list(
            student_module
            for student_module, usage_id
            in self._get_student_modules(username, [block_key])
        )
        if len(student_modules) == 0:
            raise self.DoesNotExist()

        history_entries = StudentModuleHistory.objects.prefetch_related('student_module').filter(
            student_module__in=student_modules
        ).order_by('-id')

        # If no history records exist, raise an error
        if not history_entries:
            raise self.DoesNotExist()

        for history_entry in history_entries:
            state = history_entry.state

            # If the state is serialized json, then load it
            if state is not None:
                state = json.loads(state)

            # If the state is empty, then for the purposes of `get_history`, it has been
            # deleted, and so we list that entry as `None`.
            if state == {}:
                state = None

            block_key = history_entry.student_module.module_state_key
            block_key = block_key.map_into_course(
                history_entry.student_module.course_id
            )

            yield XBlockUserState(username, block_key, state, history_entry.created, scope)