Beispiel #1
0
    def get_all_team_submissions_for_course_item(course_id, item_id):
        """
        Given a course_id and item_id, return all team submissions for the team assignment in the course

        Raises:
            - TeamSubmissionInternalError if there is some error looking up the team submissions.
        """
        try:
            return TeamSubmission.objects.prefetch_related(
                'submissions').filter(
                    course_id=course_id,
                    item_id=item_id,
                ).all()
        except Exception as exc:
            query_params_string = "course_id={course_id} item_id={item_id}".format(
                course_id=course_id,
                item_id=item_id,
            )
            err_msg = "Attempt to get team submissions for {params} caused error: {exc}".format(
                params=query_params_string, exc=exc)
            logger.error(err_msg)
            raise TeamSubmissionInternalError(err_msg)
Beispiel #2
0
    def get_team_submission_by_student_item(student_item):
        """
        Return the team submission that has an individual submission tied to the given StudentItem

        Raises:
            - TeamSubmissionNotFoundError if there is no matching team submission
            - TeamSubmissionInternalError if there is some other error looking up the team submission.

        """
        try:
            return TeamSubmission.objects.prefetch_related('submissions').get(
                submissions__student_item=student_item)
        except TeamSubmission.DoesNotExist:
            logger.error(
                "Team submission for {} not found.".format(student_item))
            raise TeamSubmissionNotFoundError(
                "No team submission matching {}".format(student_item))
        except Exception as exc:
            err_msg = "Attempt to get team submission for {student_item} caused error: {exc}".format(
                student_item=student_item, exc=exc)
            logger.error(err_msg)
            raise TeamSubmissionInternalError(err_msg)
Beispiel #3
0
    def get_team_submission_by_uuid(team_submission_uuid):
        """
        Given a uuid, return the matching team submission.

        Raises:
            - TeamSubmissionNotFoundError if there is no matching team submission
            - TeamSubmissionInternalError if there is some other error looking up the team submission.
        """
        try:
            return TeamSubmission.objects.prefetch_related('submissions').get(
                uuid=team_submission_uuid)
        except TeamSubmission.DoesNotExist:
            logger.error(
                "Team Submission {} not found.".format(team_submission_uuid))
            raise TeamSubmissionNotFoundError(
                "No team submission matching uuid {}".format(
                    team_submission_uuid))
        except Exception as exc:
            err_msg = "Attempt to get team submission for uuid {uuid} caused error: {exc}".format(
                uuid=team_submission_uuid, exc=exc)
            logger.error(err_msg)
            raise TeamSubmissionInternalError(err_msg)
Beispiel #4
0
def get_team_submission_student_ids(team_submission_uuid):
    """
    Returns a list of student_ids for a specific team submission.

    Raises:
        - TeamSubmissionNotFoundError when no matching student_ids are found, or if team_submission_uuid is falsy
        - TeamSubmissionInternalError if there is a database error
    """
    if not team_submission_uuid:
        raise TeamSubmissionNotFoundError()
    try:
        student_ids = StudentItem.objects.filter(
            submission__team_submission__uuid=team_submission_uuid).order_by(
                'student_id').distinct().values_list('student_id', flat=True)
    except DatabaseError as exc:
        err_msg = "Attempt to get student ids for team submission {team_submission_uuid} caused error: {exc}".format(
            team_submission_uuid=team_submission_uuid, exc=exc)
        logger.error(err_msg)
        raise TeamSubmissionInternalError(err_msg) from exc
    if not student_ids:
        raise TeamSubmissionNotFoundError()
    return list(student_ids)
Beispiel #5
0
def create_submission_for_team(
    course_id,
    item_id,
    team_id,
    submitting_user_id,
    team_member_ids,
    answer,
    submitted_at=None,
    attempt_number=1,
    item_type='openassessment',
):
    """
    This api function:
      1. Creates a `TeamSubmission` record, and
      2. Creates `Submission` records for every member of the team by calling api.create_submission()

    This means that the ORA `SubmissionMixin` must first collect all of the files of the submitting user
    and the team into the `answer` dict.

    Parameters:
        - course_id (str): the course id for this team submission
        - item_id (str): the item id for this team submission
        - team_id (str): the team_id for the team for which we are making the submission
        - submitting_user_id (User): the user who has hit the "submit" button
        - team_member_ids (list of str): a list of the anonymous user ids associated with all members of the team
        - answer (json serializable object): string, dict, or other json-serializable object that represents the team's
                                             answer to the problem
        - submitted_at (datetime): (optional [default = now]) the datetime at which the team submission was submitted
        - attempt number (int): (optional [default = 1]) the attempt number for this submission
        - item_type (str): (optional [default = 'openassessment']) the type of item for which this submission is being
                                                                   submitted

    Returns:
        dict: A representation of the created TeamSubmission, with the following keys:
          'team_submission_uuid' Is the `uuid` field of the created `TeamSubmission`.
          'course_id' Is the ID of the course.
          'item_id' Is the ID of the item (e.g. the block).
          'team_id' Is the ID of the team.
          'submitted_by' Is the ID of the submitting user (same as `submitting_user_id`)
          'attempt_number' is the attempt number this submission represents for this question.
          'submitted_at' represents the time this submission was submitted, which can be configured, versus the...
          'created_at' date, which is when the submission is first created.
          'submission_uuids' Is a list of the UUIDs of each of the individual `Submission` records that is created.

    Raises:
        TeamSubmissionRequestError: Raised when there are validation errors for the
            student item or team submission. This can be caused by the student item
            missing required values, the submission being too long, the
            attempt_number is negative, or the given submitted_at time is invalid.
        SubmissionRequestError: Raised when there are validation errors for the underlying
            student item or submission. This can be caused by the same reason as
            the TeamSubmissionRequestError
        TeamSubmissionInternalError: Raised when submission access causes an
            internal error.
        TeamSubmissionInternalError: Raised when submission access causes an
            internal error when creating the underlying submissions.

    Examples:
        >>>course_id = "course_1"
        >>>item_id = "item_1"
        >>>team_id = "A Team"
        >>>submitting_user_id = "Tim"
        >>>team_member_ids = ["Alice", "Bob", "Carol", "Tim"]
        >>>answer = "The answer is 42."
        >>> )
        >>> create_submission_for_team(
                course_id, item_id, team_id, submitting_user_id, team_member_ids, answer, datetime.utcnow, 1
            )
        {
            'team_submission_uuid': 'blah',
            'course_id': "course_1",
            'item_id': "item_1",
            'team_id': "A Team",
            'submitted_by': "Tim",
            'attempt_number': 1,
            'submitted_at': datetime.datetime(2014, 1, 29, 17, 14, 52, 649284 tzinfo=<UTC>),
            'created_at': datetime.datetime(2014, 1, 29, 17, 14, 52, 668850, tzinfo=<UTC>),
            'answer': u'The answer is 42.',
            'submission_uuids': ['alice-uuid', 'bob-uuid', 'carol-uuid', 'tim-uuid'],
        }
    """

    # I have no clue what to do with attempt_number. The normal API checks if there are any duplicate submissions and
    # incrememnts attempt_number, but we prevent duplicate team submissions, so I can't copy that logic flow.
    # I thought maybe we should look for deleted submissions and incrememnt based of of that, but looking at
    # production data I can't find a single case where that happens.
    # For now ... I'm just gonna default it to 1?

    model_kwargs = {
        'course_id': course_id,
        'item_id': item_id,
        'team_id': team_id,
        'submitted_by': submitting_user_id,
        'attempt_number': attempt_number,
    }
    if submitted_at:
        model_kwargs["submitted_at"] = submitted_at

    try:
        team_submission_serializer = TeamSubmissionSerializer(
            data=model_kwargs, context={'answer': answer})
        if not team_submission_serializer.is_valid():
            raise TeamSubmissionRequestError(
                field_errors=team_submission_serializer.errors)
        team_submission = team_submission_serializer.save()
        _log_team_submission(team_submission_serializer.data)
    except DatabaseError as exc:
        error_message = "An error occurred while creating team submission {}: {}".format(
            model_kwargs, exc)
        logger.exception(error_message)
        raise TeamSubmissionInternalError(error_message)

    base_student_item_dict = {
        'course_id': course_id,
        'item_id': item_id,
        'item_type': item_type
    }
    for team_member_id in team_member_ids:
        team_member_student_item_dict = dict(base_student_item_dict)
        team_member_student_item_dict['student_id'] = team_member_id
        _api.create_submission(team_member_student_item_dict,
                               answer,
                               submitted_at=submitted_at,
                               attempt_number=attempt_number,
                               team_submission=team_submission)

    model_kwargs = {
        "answer": answer,
    }
    # We must serialize the model, since the existing serializer doesn't have info about the individual submissions
    model_serializer = TeamSubmissionSerializer(team_submission,
                                                context={"answer": answer})
    return model_serializer.data