Exemple #1
0
def _get_or_create_student_item(student_item_dict):
    """Gets or creates a Student Item that matches the values specified.

    Attempts to get the specified Student Item. If it does not exist, the
    specified parameters are validated, and a new Student Item is created.

    Args:
        student_item_dict (dict): The dict containing the student_id, item_id,
            course_id, and item_type that uniquely defines a student item.

    Returns:
        StudentItem: The student item that was retrieved or created.

    Raises:
        SubmissionInternalError: Thrown if there was an internal error while
            attempting to create or retrieve the specified student item.
        SubmissionRequestError: Thrown if the given student item parameters fail
            validation.

    Examples:
        >>> student_item_dict = dict(
        >>>    student_id="Tim",
        >>>    item_id="item_1",
        >>>    course_id="course_1",
        >>>    item_type="type_one"
        >>> )
        >>> _get_or_create_student_item(student_item_dict)
        {'item_id': 'item_1', 'item_type': 'type_one', 'course_id': 'course_1', 'student_id': 'Tim'}

    """
    try:
        try:
            return StudentItem.objects.get(**student_item_dict)
        except StudentItem.DoesNotExist:
            student_item_serializer = StudentItemSerializer(
                data=student_item_dict
            )
            if not student_item_serializer.is_valid():
                logger.error(
                    u"Invalid StudentItemSerializer: errors:{} data:{}".format(
                        student_item_serializer.errors,
                        student_item_dict
                    )
                )
                raise SubmissionRequestError(field_errors=student_item_serializer.errors)
            return student_item_serializer.save()
    except DatabaseError:
        error_message = u"An error occurred creating student item: {}".format(
            student_item_dict
        )
        logger.exception(error_message)
        raise SubmissionInternalError(error_message)
Exemple #2
0
def _get_or_create_student_item(student_item_dict):
    """Gets or creates a Student Item that matches the values specified.

    Attempts to get the specified Student Item. If it does not exist, the
    specified parameters are validated, and a new Student Item is created.

    Args:
        student_item_dict (dict): The dict containing the student_id, item_id,
            course_id, and item_type that uniquely defines a student item.

    Returns:
        StudentItem: The student item that was retrieved or created.

    Raises:
        SubmissionInternalError: Thrown if there was an internal error while
            attempting to create or retrieve the specified student item.
        SubmissionRequestError: Thrown if the given student item parameters fail
            validation.

    Examples:
        >>> student_item_dict = dict(
        >>>    student_id="Tim",
        >>>    item_id="item_1",
        >>>    course_id="course_1",
        >>>    item_type="type_one"
        >>> )
        >>> _get_or_create_student_item(student_item_dict)
        {'item_id': 'item_1', 'item_type': 'type_one', 'course_id': 'course_1', 'student_id': 'Tim'}

    """
    try:
        try:
            return StudentItem.objects.get(**student_item_dict)
        except StudentItem.DoesNotExist as student_error:
            student_item_serializer = StudentItemSerializer(
                data=student_item_dict
            )
            if not student_item_serializer.is_valid():
                logger.error(
                    "Invalid StudentItemSerializer: errors:%(errors)s data:%(data)s",
                    {
                        'errors': student_item_serializer.errors,
                        'data': student_item_dict,
                    }
                )
                raise SubmissionRequestError(field_errors=student_item_serializer.errors) from student_error
            return student_item_serializer.save()
    except DatabaseError as error:
        error_message = f"An error occurred creating student item: {student_item_dict}"
        logger.exception(error_message)
        raise SubmissionInternalError(error_message) from error
Exemple #3
0
def get_course_item_submissions(course_id,
                                item_id,
                                item_type,
                                read_replica=True):
    submission_qs = Submission.objects
    if read_replica:
        submission_qs = _use_read_replica(submission_qs)

    query = submission_qs.select_related(
        'student_item__scoresummary__latest__submission').filter(
            student_item__course_id=course_id,
            student_item__item_type=item_type,
            student_item__item_id=item_id,
        ).iterator()

    for submission in query:
        student_item = submission.student_item
        serialized_score = {}
        if hasattr(student_item, 'scoresummary'):
            latest_score = student_item.scoresummary.latest
            if (not latest_score.is_hidden()
                ) and latest_score.submission.uuid == submission.uuid:
                serialized_score = ScoreSerializer(latest_score).data
        yield (StudentItemSerializer(student_item).data,
               SubmissionSerializer(submission).data, serialized_score)
Exemple #4
0
def get_all_course_submission_information(course_id,
                                          item_type,
                                          read_replica=True):
    """ For the given course, get all student items of the given item type, all the submissions for those itemes,
    and the latest scores for each item. If a submission was given a score that is not the latest score for the
    relevant student item, it will still be included but without score.

    Args:
        course_id (str): The course that we are getting submissions from.
        item_type (str): The type of items that we are getting submissions for.
        read_replica (bool): Try to use the database's read replica if it's available.

    Yields:
        A tuple of three dictionaries representing:
        (1) a student item with the following fields:
            student_id
            course_id
            student_item
            item_type
        (2) a submission with the following fields:
            student_item
            attempt_number
            submitted_at
            created_at
            answer
        (3) a score with the following fields, if one exists and it is the latest score:
            (if both conditions are not met, an empty dict is returned here)
            student_item
            submission
            points_earned
            points_possible
            created_at
            submission_uuid
    """

    submission_qs = Submission.objects
    if read_replica:
        submission_qs = _use_read_replica(submission_qs)

    query = submission_qs.select_related(
        'student_item__scoresummary__latest__submission').filter(
            student_item__course_id=course_id,
            student_item__item_type=item_type,
        ).iterator()

    for submission in query:
        student_item = submission.student_item
        serialized_score = {}
        if hasattr(student_item, 'scoresummary'):
            latest_score = student_item.scoresummary.latest

            # Only include the score if it is not a reset score (is_hidden), and if the current submission is the same
            # as the student_item's latest score's submission. This matches the behavior of the API's get_score method.
            if (not latest_score.is_hidden()
                ) and latest_score.submission.uuid == submission.uuid:
                serialized_score = ScoreSerializer(latest_score).data
        yield (StudentItemSerializer(student_item).data,
               SubmissionSerializer(submission).data, serialized_score)
Exemple #5
0
def get_submission_and_student(uuid, read_replica=False):
    """
    Retrieve a submission by its unique identifier, including the associated student item.

    Args:
        uuid (str): the unique identifier of the submission.

    Kwargs:
        read_replica (bool): If true, attempt to use the read replica database.
            If no read replica is available, use the default database.

    Returns:
        Serialized Submission model (dict) containing a serialized StudentItem model

    Raises:
        SubmissionNotFoundError: Raised if the submission does not exist.
        SubmissionRequestError: Raised if the search parameter is not a string.
        SubmissionInternalError: Raised for unknown errors.

    """
    # This may raise API exceptions
    submission = get_submission(uuid, read_replica=read_replica)

    # Retrieve the student item from the cache
    cache_key = "submissions.student_item.{}".format(
        submission['student_item'])
    try:
        cached_student_item = cache.get(cache_key)
    except Exception:  # pylint: disable=broad-except
        # The cache backend could raise an exception
        # (for example, memcache keys that contain spaces)
        logger.exception(
            "Error occurred while retrieving student item from the cache")
        cached_student_item = None

    if cached_student_item is not None:
        submission['student_item'] = cached_student_item
    else:
        # There is probably a more idiomatic way to do this using the Django REST framework
        try:
            student_item_qs = StudentItem.objects
            if read_replica:
                student_item_qs = _use_read_replica(student_item_qs)

            student_item = student_item_qs.get(id=submission['student_item'])
            submission['student_item'] = StudentItemSerializer(
                student_item).data
            cache.set(cache_key, submission['student_item'])
        except Exception as ex:
            err_msg = f"Could not get submission due to error: {ex}"
            logger.exception(err_msg)
            raise SubmissionInternalError(err_msg) from ex

    return submission
Exemple #6
0
def get_submission_and_student(uuid):
    """
    Retrieve a submission by its unique identifier, including the associated student item.

    Args:
        uuid (str): the unique identifier of the submission.

    Returns:
        Serialized Submission model (dict) containing a serialized StudentItem model

    Raises:
        SubmissionNotFoundError: Raised if the submission does not exist.
        SubmissionRequestError: Raised if the search parameter is not a string.
        SubmissionInternalError: Raised for unknown errors.

    """
    # This may raise API exceptions
    submission = get_submission(uuid)

    # Retrieve the student item from the cache
    cache_key = "submissions.student_item.{}".format(
        submission['student_item'])
    try:
        cached_student_item = cache.get(cache_key)
    except:
        # The cache backend could raise an exception
        # (for example, memcache keys that contain spaces)
        logger.exception(
            "Error occurred while retrieving student item from the cache")
        cached_student_item = None

    if cached_student_item is not None:
        submission['student_item'] = cached_student_item
    else:
        # There is probably a more idiomatic way to do this using the Django REST framework
        try:
            student_item = StudentItem.objects.get(
                id=submission['student_item'])
            submission['student_item'] = StudentItemSerializer(
                student_item).data
            cache.set(cache_key, submission['student_item'])
        except Exception as ex:
            err_msg = "Could not get submission due to error: {}".format(ex)
            logger.exception(err_msg)
            raise SubmissionInternalError(err_msg)

    return submission