def test_score_annotations(self, annotation_types):
     """
     Ensure that annotation types are returned with serialized scores.
     """
     annotation_kwargs = {
         'creator': 'test_annotator',
         'reason': 'tests for the test god'
     }
     for test_type in annotation_types:
         ScoreAnnotation.objects.create(
             score=self.score,
             annotation_type=test_type,
             **annotation_kwargs
         )
     score_dict = ScoreSerializer(self.score).data
     self.assertEqual(
         score_dict['annotations'],
         [
             {
                 'reason': annotation_kwargs['reason'],
                 'annotation_type': annotation_type,
                 'creator': annotation_kwargs['creator'],
             }
             for annotation_type in annotation_types
         ]
     )
Exemplo n.º 2
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)
Exemplo n.º 3
0
def get_latest_score_for_submission(submission_uuid, read_replica=False):
    """
    Retrieve the latest score for a particular submission.

    Args:
        submission_uuid (str): The UUID of the submission to retrieve.

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

    Returns:
        dict: The serialized score model, or None if no score is available.

    """
    try:
        # Ensure that submission_uuid is valid before fetching score
        submission_model = _get_submission_model(submission_uuid, read_replica)
        score_qs = Score.objects.filter(
            submission__uuid=submission_model.uuid).order_by(
                "-id").select_related("submission")

        if read_replica:
            score_qs = _use_read_replica(score_qs)

        score = score_qs[0]
        if score.is_hidden():
            return None
    except (IndexError, Submission.DoesNotExist):
        return None

    return ScoreSerializer(score).data
Exemplo n.º 4
0
def get_score_override(student_item):
    """
    Get latest override score for peer assessment question.

    Args:
        student_item (dict): The dictionary representation of a student item.

    Returns:
        The latest override score if there is one, otherwise None.
    """

    try:
        student_item_model = StudentItem.objects.get(**student_item)
    except (TypeError, StudentItem.DoesNotExist):
        return None

    score = Score.objects.filter(
        student_item=student_item_model,
        submission=None,
        reset=False,
    )

    if score:
        return ScoreSerializer(score.latest('created_at')).data
    else:
        return None
Exemplo n.º 5
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)
Exemplo n.º 6
0
 def test_score_with_null_submission(self):
     # Create a score with a null submission
     null_sub_score = Score.objects.create(
         student_item=self.item,
         submission=None,
         points_earned=3,
         points_possible=8,
     )
     null_sub_score_dict = ScoreSerializer(null_sub_score).data
     self.assertIs(null_sub_score_dict['submission_uuid'], None)
     self.assertEqual(null_sub_score_dict['points_earned'], 3)
     self.assertEqual(null_sub_score_dict['points_possible'], 8)
Exemplo n.º 7
0
def get_score(student_item):
    """Get the score for a particular student item

    Each student item should have a unique score. This function will return the
    score if it is available. A score is only calculated for a student item if
    it has completed the workflow for a particular assessment module.

    Args:
        student_item (dict): The dictionary representation of a student item.
            Function returns the score related to this student item.

    Returns:
        score (dict): The score associated with this student item. None if there
            is no score found.

    Raises:
        SubmissionInternalError: Raised if a score cannot be retrieved because
            of an internal server error.

    Examples:
        >>> student_item = {
        >>>     "student_id":"Tim",
        >>>     "course_id":"TestCourse",
        >>>     "item_id":"u_67",
        >>>     "item_type":"openassessment"
        >>> }
        >>>
        >>> get_score(student_item)
        [{
            'student_item': 2,
            'submission': 2,
            'points_earned': 8,
            'points_possible': 20,
            'created_at': datetime.datetime(2014, 2, 7, 18, 30, 1, 807911, tzinfo=<UTC>)
        }]

    """
    try:
        student_item_model = StudentItem.objects.get(**student_item)
        score = ScoreSummary.objects.get(
            student_item=student_item_model).latest
    except (ScoreSummary.DoesNotExist, StudentItem.DoesNotExist):
        return None

    # By convention, scores are hidden if "points possible" is set to 0.
    # This can occur when an instructor has reset scores for a student.
    if score.is_hidden():
        return None
    else:
        return ScoreSerializer(score).data
Exemplo n.º 8
0
    def test_score_with_null_submission(self):
        item = StudentItem.objects.create(
            student_id="score_test_student",
            course_id="score_test_course",
            item_id="i4x://mycourse/special_presentation")

        # Create a score with a null submission
        score = Score.objects.create(student_item=item,
                                     submission=None,
                                     points_earned=2,
                                     points_possible=6)
        score_dict = ScoreSerializer(score).data

        self.assertIs(score_dict['submission_uuid'], None)
        self.assertEqual(score_dict['points_earned'], 2)
        self.assertEqual(score_dict['points_possible'], 6)
Exemplo n.º 9
0
def get_latest_score_for_submission(submission_uuid):
    """
    Retrieve the latest score for a particular submission.

    Args:
        submission_uuid (str): The UUID of the submission to retrieve.

    Returns:
        dict: The serialized score model, or None if no score is available.

    """
    try:
        score = Score.objects.filter(
            submission__uuid=submission_uuid).order_by("-id").select_related(
                "submission")[0]
        if score.is_hidden():
            return None
    except IndexError:
        return None

    return ScoreSerializer(score).data
Exemplo n.º 10
0
def set_score(submission_uuid,
              points_earned,
              points_possible,
              annotation_creator=None,
              annotation_type=None,
              annotation_reason=None):
    """Set a score for a particular submission.

    Sets the score for a particular submission. This score is calculated
    externally to the API.

    Args:
        submission_uuid (str): UUID for the submission (must exist).
        points_earned (int): The earned points for this submission.
        points_possible (int): The total points possible for this particular student item.

        annotation_creator (str): An optional field for recording who gave this particular score
        annotation_type (str): An optional field for recording what type of annotation should be created,
                                e.g. "staff_override".
        annotation_reason (str): An optional field for recording why this score was set to its value.

    Returns:
        None

    Raises:
        SubmissionInternalError: Thrown if there was an internal error while
            attempting to save the score.
        SubmissionRequestError: Thrown if the given student item or submission
            are not found.

    Examples:
        >>> set_score("a778b933-9fb3-11e3-9c0f-040ccee02800", 11, 12)
        {
            'student_item': 2,
            'submission': 1,
            'points_earned': 11,
            'points_possible': 12,
            'created_at': datetime.datetime(2014, 2, 7, 20, 6, 42, 331156, tzinfo=<UTC>)
        }

    """
    try:
        submission_model = _get_submission_model(submission_uuid)
    except Submission.DoesNotExist as error:
        raise SubmissionNotFoundError(
            f"No submission matching uuid {submission_uuid}") from error
    except DatabaseError as error:
        error_msg = "Could not retrieve submission {}.".format(submission_uuid)
        logger.exception(error_msg)
        raise SubmissionRequestError(msg=error_msg) from error

    score = ScoreSerializer(
        data={
            "student_item": submission_model.student_item.pk,
            "submission": submission_model.pk,
            "points_earned": points_earned,
            "points_possible": points_possible,
        })
    if not score.is_valid():
        logger.exception(score.errors)
        raise SubmissionInternalError(score.errors)

    # When we save the score, a score summary will be created if
    # it does not already exist.
    # When the database's isolation level is set to repeatable-read,
    # it's possible for a score summary to exist for this student item,
    # even though we cannot retrieve it.
    # In this case, we assume that someone else has already created
    # a score summary and ignore the error.
    try:
        with transaction.atomic():
            score_model = score.save()
            _log_score(score_model)
            if annotation_creator is not None:
                score_annotation = ScoreAnnotation(
                    score=score_model,
                    creator=annotation_creator,
                    annotation_type=annotation_type,
                    reason=annotation_reason)
                score_annotation.save()
        # Send a signal out to any listeners who are waiting for scoring events.
        score_set.send(
            sender=None,
            points_possible=points_possible,
            points_earned=points_earned,
            anonymous_user_id=submission_model.student_item.student_id,
            course_id=submission_model.student_item.course_id,
            item_id=submission_model.student_item.item_id,
            created_at=score_model.created_at,
        )
    except IntegrityError:
        pass
Exemplo n.º 11
0
def set_score(submission_uuid, points_earned, points_possible):
    """Set a score for a particular submission.

    Sets the score for a particular submission. This score is calculated
    externally to the API.

    Args:
        submission_uuid (str): UUID for the submission (must exist).
        points_earned (int): The earned points for this submission.
        points_possible (int): The total points possible for this particular student item.

    Returns:
        None

    Raises:
        SubmissionInternalError: Thrown if there was an internal error while
            attempting to save the score.
        SubmissionRequestError: Thrown if the given student item or submission
            are not found.

    Examples:
        >>> set_score("a778b933-9fb3-11e3-9c0f-040ccee02800", 11, 12)
        {
            'student_item': 2,
            'submission': 1,
            'points_earned': 11,
            'points_possible': 12,
            'created_at': datetime.datetime(2014, 2, 7, 20, 6, 42, 331156, tzinfo=<UTC>)
        }

    """
    try:
        submission_model = Submission.objects.get(uuid=submission_uuid)
    except Submission.DoesNotExist:
        raise SubmissionNotFoundError(
            u"No submission matching uuid {}".format(submission_uuid)
        )
    except DatabaseError:
        error_msg = u"Could not retrieve student item: {} or submission {}.".format(
            submission_uuid
        )
        logger.exception(error_msg)
        raise SubmissionRequestError(msg=error_msg)

    score = ScoreSerializer(
        data={
            "student_item": submission_model.student_item.pk,
            "submission": submission_model.pk,
            "points_earned": points_earned,
            "points_possible": points_possible,
        }
    )
    if not score.is_valid():
        logger.exception(score.errors)
        raise SubmissionInternalError(score.errors)

    # When we save the score, a score summary will be created if
    # it does not already exist.
    # When the database's isolation level is set to repeatable-read,
    # it's possible for a score summary to exist for this student item,
    # even though we cannot retrieve it.
    # In this case, we assume that someone else has already created
    # a score summary and ignore the error.
    try:
        score_model = score.save()
        _log_score(score_model)
        # Send a signal out to any listeners who are waiting for scoring events.
        score_set.send(
            sender=None,
            points_possible=points_possible,
            points_earned=points_earned,
            anonymous_user_id=submission_model.student_item.student_id,
            course_id=submission_model.student_item.course_id,
            item_id=submission_model.student_item.item_id,
        )
    except IntegrityError:
        pass
Exemplo n.º 12
0
def set_score(submission_uuid, points_earned, points_possible):
    """Set a score for a particular submission.

    Sets the score for a particular submission. This score is calculated
    externally to the API.

    Args:
        submission_uuid (str): UUID for the submission (must exist).
        points_earned (int): The earned points for this submission.
        points_possible (int): The total points possible for this particular
            student item.

    Returns:
        None

    Raises:
        SubmissionInternalError: Thrown if there was an internal error while
            attempting to save the score.
        SubmissionRequestError: Thrown if the given student item or submission
            are not found.

    Examples:
        >>> set_score("a778b933-9fb3-11e3-9c0f-040ccee02800", 11, 12)
        {
            'student_item': 2,
            'submission': 1,
            'points_earned': 11,
            'points_possible': 12,
            'created_at': datetime.datetime(2014, 2, 7, 20, 6, 42, 331156, tzinfo=<UTC>)
        }

    """
    try:
        submission_model = Submission.objects.get(uuid=submission_uuid)
    except Submission.DoesNotExist:
        raise SubmissionNotFoundError(
            u"No submission matching uuid {}".format(submission_uuid))
    except DatabaseError:
        error_msg = u"Could not retrieve student item: {} or submission {}.".format(
            submission_uuid)
        logger.exception(error_msg)
        raise SubmissionRequestError(error_msg)

    score = ScoreSerializer(
        data={
            "student_item": submission_model.student_item.pk,
            "submission": submission_model.pk,
            "points_earned": points_earned,
            "points_possible": points_possible,
        })
    if not score.is_valid():
        logger.exception(score.errors)
        raise SubmissionInternalError(score.errors)

    # When we save the score, a score summary will be created if
    # it does not already exist.
    # When the database's isolation level is set to repeatable-read,
    # it's possible for a score summary to exist for this student item,
    # even though we cannot retrieve it.
    # In this case, we assume that someone else has already created
    # a score summary and ignore the error.
    try:
        score_model = score.save()
        _log_score(score_model)
    except IntegrityError:
        pass
Exemplo n.º 13
0
Arquivo: api.py Projeto: ctpad/edx-tim
def set_score(student_item, submission, score, points_possible):
    """Set a score for a particular student item, submission pair.

    Sets the score for a particular student item and submission pair. This score
    is calculated externally to the API.

    Args:
        student_item (dict): The student item associated with this score. This
            dictionary must contain a course_id, student_id, and item_id.
        submission (dict): The submission associated with this score. This
            dictionary must contain all submission fields to properly get a
            unique submission item.
        score (int): The score to associate with the given submission and
            student item.
        points_possible (int): The total points possible for this particular
            student item.

    Returns:
        (dict): The dictionary representation of the saved score.

    Raises:
        SubmissionInternalError: Thrown if there was an internal error while
            attempting to save the score.
        SubmissionRequestError: Thrown if the given student item or submission
            are not found.

    Examples:
        >>> student_item_dict = dict(
        >>>    student_id="Tim",
        >>>    item_id="item_1",
        >>>    course_id="course_1",
        >>>    item_type="type_one"
        >>> )
        >>>
        >>> submission_dict = dict(
        >>>    student_item=2,
        >>>    attempt_number=1,
        >>>    submitted_at=datetime.datetime(2014, 1, 29, 23, 14, 52, 649284, tzinfo=<UTC>),
        >>>    created_at=datetime.datetime(2014, 1, 29, 17, 14, 52, 668850, tzinfo=<UTC>),
        >>>    answer=u'The answer is 42.'
        >>> )
        >>> set_score(student_item_dict, submission_dict, 11, 12)
        {
            'student_item': 2,
            'submission': 1,
            'points_earned': 11,
            'points_possible': 12,
            'created_at': datetime.datetime(2014, 2, 7, 20, 6, 42, 331156, tzinfo=<UTC>)
        }

    """
    try:
        student_item_model = StudentItem.objects.get(**student_item)
        submission_model = Submission.objects.get(**submission)
    except DatabaseError:
        error_msg = u"Could not retrieve student item: {} or submission {}.".format(
            student_item, submission
        )
        logger.exception(error_msg)
        raise SubmissionRequestError(error_msg)

    score = ScoreSerializer(
        data={
            "student_item": student_item_model.pk,
            "submission": submission_model.pk,
            "points_earned": score,
            "points_possible": points_possible,
        }
    )
    if not score.is_valid():
        logger.exception(score.errors)
        raise SubmissionInternalError(score.errors)
    score.save()
    return score.data