Exemple #1
0
    def get(self, course_uuid, assignment_uuid):
        """
        Get answers submitted to the assignment submitted by current user

        :param course_uuid:
        :param assignment_uuid:
        :return: answers
        """
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)

        require(
            READ,
            Answer(user_id=current_user.id),
            title="Answers Unavailable",
            message=
            "Sorry, your role in this course does not allow you to view answers for this assignment."
        )
        restrict_user = not allow(MANAGE, assignment)

        params = user_answer_list_parser.parse_args()

        query = Answer.query \
            .options(joinedload('comments')) \
            .options(joinedload('file')) \
            .options(joinedload('user')) \
            .options(joinedload('group')) \
            .options(joinedload('score')) \
            .filter_by(
                active=True,
                assignment_id=assignment.id,
                course_id=course.id,
                draft=params.get('draft')
            )

        # get group and individual answers for user if applicable
        group = current_user.get_course_group(course.id)
        if group:
            query = query.filter(
                or_(Answer.user_id == current_user.id,
                    Answer.group_id == group.id))
        # get just individual answers for user
        else:
            query = query.filter(Answer.user_id == current_user.id)

        answers = query.all()

        on_user_answer_get.send(self,
                                event_name=on_user_answer_get.name,
                                user=current_user,
                                course_id=course.id,
                                data={'assignment_id': assignment.id})

        return {
            "objects": marshal(answers, dataformat.get_answer(restrict_user))
        }
Exemple #2
0
    def get(self, course_uuid, assignment_uuid):
        """
        Get answers submitted to the assignment submitted by current user

        :param course_uuid:
        :param assignment_uuid:
        :return: answers
        """
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)

        require(READ, Answer(user_id=current_user.id),
            title="Answers Unavailable",
            message="Sorry, your role in this course does not allow you to view answers for this assignment.")
        restrict_user = not allow(MANAGE, assignment)

        params = user_answer_list_parser.parse_args()

        query = Answer.query \
            .options(joinedload('comments')) \
            .options(joinedload('file')) \
            .options(joinedload('user')) \
            .options(joinedload('group')) \
            .options(joinedload('score')) \
            .filter_by(
                active=True,
                assignment_id=assignment.id,
                course_id=course.id,
                draft=params.get('draft')
            )

        # get group and individual answers for user if applicable
        group = current_user.get_course_group(course.id)
        if group:
            query = query.filter(or_(
                Answer.user_id == current_user.id,
                Answer.group_id == group.id
            ))
        # get just individual answers for user
        else:
            query = query.filter(Answer.user_id == current_user.id)

        answers = query.all()

        on_user_answer_get.send(
            self,
            event_name=on_user_answer_get.name,
            user=current_user,
            course_id=course.id,
            data={'assignment_id': assignment.id})

        return {"objects": marshal(answers, dataformat.get_answer(restrict_user))}
Exemple #3
0
    def post(self, course_uuid, assignment_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)

        if not assignment.answer_grace and not allow(MANAGE, assignment):
            abort(
                403,
                title="Answer Not Submitted",
                message=
                "Sorry, the answer deadline has passed. No answers can be submitted after the deadline unless the instructor submits the answer for you."
            )

        require(
            CREATE,
            Answer(course_id=course.id),
            title="Answer Not Submitted",
            message=
            "Answers can be submitted only by those enrolled in the course. Please double-check your enrollment in this course."
        )
        restrict_user = not allow(MANAGE, assignment)

        answer = Answer(assignment_id=assignment.id)

        params = new_answer_parser.parse_args()
        answer.content = params.get("content")
        answer.draft = params.get("draft")

        file_uuid = params.get('file_id')
        attachment = None
        if file_uuid:
            attachment = File.get_by_uuid_or_404(file_uuid)
            answer.file_id = attachment.id
        else:
            answer.file_id = None

        # non-drafts must have content
        if not answer.draft and not answer.content and not file_uuid:
            abort(
                400,
                title="Answer Not Submitted",
                message=
                "Please provide content in the text editor or upload a file and try submitting again."
            )

        user_uuid = params.get("user_id")
        group_uuid = params.get("group_id")
        # we allow instructor and TA to submit multiple answers for other users in the class
        if user_uuid and not allow(MANAGE, Answer(course_id=course.id)):
            abort(
                400,
                title="Answer Not Submitted",
                message=
                "Only instructors and teaching assistants can submit an answer on behalf of another."
            )
        if group_uuid and not assignment.enable_group_answers:
            abort(400,
                  title="Answer Not Submitted",
                  message="Group answers are not allowed for this assignment.")
        if group_uuid and not allow(MANAGE, Answer(course_id=course.id)):
            abort(
                400,
                title="Answer Not Submitted",
                message=
                "Only instructors and teaching assistants can submit an answer on behalf of a group."
            )
        if group_uuid and user_uuid:
            abort(
                400,
                title="Answer Not Submitted",
                message=
                "You cannot submit an answer for a user and a group at the same time."
            )

        user = User.get_by_uuid_or_404(
            user_uuid) if user_uuid else current_user
        group = Group.get_active_by_uuid_or_404(
            group_uuid) if group_uuid else None

        if restrict_user and assignment.enable_group_answers and not group:
            group = current_user.get_course_group(course.id)
            if group == None:
                abort(
                    400,
                    title="Answer Not Submitted",
                    message=
                    "You are currently not in any group for this course. Please contact your instructor to be added to a group."
                )

        check_for_existing_answers = False
        if group and assignment.enable_group_answers:
            if group.course_id != course.id:
                abort(
                    400,
                    title="Answer Not Submitted",
                    message=
                    "Group answers can be submitted to courses they belong in."
                )

            answer.user_id = None
            answer.group_id = group.id
            answer.comparable = True
            check_for_existing_answers = True
        else:
            answer.user_id = user.id
            answer.group_id = None

            course_role = User.get_user_course_role(answer.user_id, course.id)

            # only system admin can add answers for themselves to a class without being enrolled in it
            # required for managing comparison examples as system admin
            if (not course_role or course_role == CourseRole.dropped
                ) and current_user.system_role != SystemRole.sys_admin:
                abort(
                    400,
                    title="Answer Not Submitted",
                    message=
                    "Answers can be submitted only by those enrolled in the course. Please double-check your enrollment in this course."
                )

            if course_role == CourseRole.student and assignment.enable_group_answers:
                abort(
                    400,
                    title="Answer Not Submitted",
                    message=
                    "Students can only submit group answers for this assignment."
                )

            # we allow instructor and TA to submit multiple answers for their own,
            # but not for student. Each student can only have one answer.
            if course_role and course_role == CourseRole.student:
                check_for_existing_answers = True
                answer.comparable = True
            else:
                # instructor / TA / Sys Admin can mark the answer as non-comparable, unless the answer is for a student
                answer.comparable = params.get("comparable")

        if check_for_existing_answers:
            # check for answers with user_id or group_id
            prev_answers = Answer.query \
                .filter_by(
                    assignment_id=assignment.id,
                    user_id=answer.user_id,
                    group_id=answer.group_id,
                    active=True
                ) \
                .all()

            # check if there is a previous answer submitted for the student
            non_draft_answers = [
                prev_answer for prev_answer in prev_answers
                if not prev_answer.draft
            ]
            if len(non_draft_answers) > 0:
                abort(
                    400,
                    title="Answer Not Submitted",
                    message=
                    "An answer has already been submitted for this assignment by you or on your behalf."
                )

            # check if there is a previous draft answer submitted for the student (soft-delete if present)
            draft_answers = [
                prev_answer for prev_answer in prev_answers
                if prev_answer.draft
            ]
            for draft_answer in draft_answers:
                draft_answer.active = False

        # set submission date if answer is being submitted for the first time
        if not answer.draft and not answer.submission_date:
            answer.submission_date = datetime.datetime.utcnow()

        answer.update_attempt(params.get('attempt_uuid'),
                              params.get('attempt_started', None),
                              params.get('attempt_ended', None))

        db.session.add(answer)
        db.session.commit()

        on_answer_create.send(self,
                              event_name=on_answer_create.name,
                              user=current_user,
                              course_id=course.id,
                              answer=answer,
                              data=marshal(
                                  answer,
                                  dataformat.get_answer(restrict_user)))

        if attachment:
            on_attach_file.send(self,
                                event_name=on_attach_file.name,
                                user=current_user,
                                course_id=course.id,
                                file=attachment,
                                data={
                                    'answer_id': answer.id,
                                    'file_id': attachment.id
                                })

        # update course & assignment grade for user if answer is fully submitted
        if not answer.draft:
            if answer.user:
                assignment.calculate_grade(answer.user)
                course.calculate_grade(answer.user)
            elif answer.group:
                assignment.calculate_group_grade(answer.group)
                course.calculate_group_grade(answer.group)

        return marshal(answer, dataformat.get_answer(restrict_user))
Exemple #4
0
    def get(self, course_uuid, assignment_uuid, **kwargs):
        """
        :query string ids: a comma separated comment uuids to query
        :query string answer_ids: a comma separated answer uuids for answer filter
        :query string assignment_id: filter the answer comments with a assignment uuid
        :query string user_ids: a comma separated user uuids that own the comments
        :query string self_evaluation: indicate whether the result should include self-evaluation comments or self-evaluation only.
                Possible values: true, false or only. Default true.
        :query string evaluation: indicate whether the result should include evaluation comments or evaluation only.
                Possible values: true, false or only. Default true.
        :query string draft: indicate whether the result should include drafts for current user or not.
                Possible values: true, false or only. Default false.
        :reqheader Accept: the response content type depends on :mailheader:`Accept` header
        :resheader Content-Type: this depends on :mailheader:`Accept` header of request
        :statuscode 200: no error
        :statuscode 404: answers don't exist

        """
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)

        restrict_user = not allow(MANAGE, assignment)

        params = answer_comment_list_parser.parse_args()
        answer_uuids = []
        if 'answer_uuid' in kwargs:
            answer_uuids.append(kwargs['answer_uuid'])
        elif 'answer_ids' in params and params['answer_ids']:
            answer_uuids.extend(params['answer_ids'].split(','))

        if not answer_uuids and not params['ids'] and not params['assignment_id'] and not params['user_ids']:
            abort(404, title="Feedback Unavailable", message="There was a problem getting the feedback for this answer. Please try again.")

        conditions = []

        answers = Answer.query \
            .filter(
                Answer.assignment_id == assignment.id,
                Answer.active == True,
                Answer.draft == False,
                Answer.uuid.in_(answer_uuids)
            ) \
            .all() if answer_uuids else []
        if answer_uuids and not answers:
            # non-existing answer ids.
            abort(404, title="Feedback Unavailable", message="There was a problem getting the feedback for this answer. Please try again.")

        group = current_user.get_course_group(course.id)
        course_role = current_user.get_course_role(course.id)

        # build query condition for each answer
        for answer in answers:
            clauses = [AnswerComment.answer_id == answer.id]

            # student can only see the comments for themselves or public ones.
            # since the owner of the answer can access all comments. We only filter
            # on non-owners
            answer_owner = answer.user_id == current_user.id or (group and group.id == answer.group_id)
            if course_role == CourseRole.student and not answer_owner:
                # public comments or comments owned by current user
                clauses.append(or_(
                    AnswerComment.comment_type == AnswerCommentType.public,
                    AnswerComment.user_id == current_user.id
                ))

            conditions.append(and_(*clauses))

        query = AnswerComment.query \
            .filter(
                AnswerComment.assignment_id == assignment.id,
                AnswerComment.active==True,
                or_(*conditions)
            )

        if params['ids']:
            query = query.filter(AnswerComment.uuid.in_(params['ids'].split(',')))

        if params['self_evaluation'] == 'false':
            # do not include self-evaluation
            query = query.filter(AnswerComment.comment_type != AnswerCommentType.self_evaluation)
        elif params['self_evaluation'] == 'only':
            # only self_evaluation
            query = query.filter(AnswerComment.comment_type == AnswerCommentType.self_evaluation)

        if params['evaluation'] == 'false':
            # do not include evalulation comments
            query = query.filter(AnswerComment.comment_type != AnswerCommentType.evaluation)
        elif params['evaluation'] == 'only':
            # only evaluation
            query = query.filter(AnswerComment.comment_type == AnswerCommentType.evaluation)

        if params['draft'] == 'true':
            # with draft (current_user)
            query = query.filter(or_(
                AnswerComment.draft == False,
                and_(
                    AnswerComment.draft == True,
                    AnswerComment.user_id == current_user.id
                )
            ))
        elif params['draft'] == 'only':
            # only draft (current_user)
            query = query.filter(and_(
                AnswerComment.draft == True,
                AnswerComment.user_id == current_user.id
            ))
        else:
            # do not include draft. Default
            query = query.filter(AnswerComment.draft == False)

        if params['user_ids']:
            user_ids = params['user_ids'].split(',')
            query = query \
                .join(User, AnswerComment.user_id == User.id) \
                .filter(User.uuid.in_(user_ids))

        answer_comments = query.order_by(AnswerComment.created.desc()).all()

        # checking the permission
        for answer_comment in answer_comments:
            require(READ, answer_comment.answer,
                title="Feedback Unavailable",
                message="Sorry, your role in this course does not allow you to view feedback for this answer.")

        on_answer_comment_list_get.send(
            self,
            event_name=on_answer_comment_list_get.name,
            user=current_user,
            data={'answer_ids': ','.join([str(answer.id) for answer in answers])})

        return marshal(answer_comments, dataformat.get_answer_comment(restrict_user))
Exemple #5
0
    def get(self, course_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        require(READ, course,
            title="Assignment Status Unavailable",
            message="Assignment status can be seen only by those enrolled in the course. Please double-check your enrollment in this course.")

        group = current_user.get_course_group(course.id)
        group_id = group.id if group else None

        assignments = course.assignments \
            .filter_by(active=True) \
            .all()
        assignment_ids = [assignment.id for assignment in assignments]

        answer_counts = Answer.query \
            .with_entities(
                Answer.assignment_id,
                func.count(Answer.assignment_id).label('answer_count')
            ) \
            .filter_by(
                comparable=True,
                active=True,
                practice=False,
                draft=False
            ) \
            .filter(or_(
                Answer.user_id == current_user.id,
                and_(Answer.group_id == group_id, Answer.group_answer == True)
            )) \
            .filter(Answer.assignment_id.in_(assignment_ids)) \
            .group_by(Answer.assignment_id) \
            .all()


        feedback_counts = AnswerComment.query \
            .join("answer") \
            .with_entities(
                Answer.assignment_id,
                func.count(Answer.assignment_id).label('feedback_count')
            ) \
            .filter(and_(
                AnswerComment.active == True,
                AnswerComment.draft == False,
                Answer.active == True,
                Answer.practice == False,
                Answer.draft == False,
                Answer.assignment_id.in_(assignment_ids)
            )) \
            .filter(or_(
                Answer.user_id == current_user.id,
                and_(Answer.group_id == group_id, Answer.group_id != None)
            )) \
            .group_by(Answer.assignment_id) \
            .all()

        # get self evaluation status for assignments with self evaluations enabled
        self_evaluations = AnswerComment.query \
            .join("answer") \
            .with_entities(
                Answer.assignment_id,
                func.count(Answer.assignment_id).label('self_evaluation_count')
            ) \
            .filter(and_(
                AnswerComment.user_id == current_user.id,
                AnswerComment.active == True,
                AnswerComment.comment_type == AnswerCommentType.self_evaluation,
                AnswerComment.draft == False,
                Answer.active == True,
                Answer.practice == False,
                Answer.draft == False,
                Answer.assignment_id.in_(assignment_ids)
            )) \
            .group_by(Answer.assignment_id) \
            .all()

        self_evaluation_drafts = AnswerComment.query \
            .join("answer") \
            .with_entities(
                Answer.assignment_id,
                func.count(Answer.assignment_id).label('self_evaluation_count')
            ) \
            .filter(and_(
                AnswerComment.user_id == current_user.id,
                AnswerComment.active == True,
                AnswerComment.comment_type == AnswerCommentType.self_evaluation,
                AnswerComment.draft == True,
                Answer.active == True,
                Answer.practice == False,
                Answer.draft == False,
                Answer.assignment_id.in_(assignment_ids)
            )) \
            .group_by(Answer.assignment_id) \
            .all()

        query = Answer.query \
            .options(load_only('id', 'assignment_id', 'uuid')) \
            .filter_by(
                active=True,
                practice=False,
                draft=True
            ) \
            .filter(or_(
                and_(Answer.group_id == group_id, Answer.group_id != None),
                Answer.user_id == current_user.id
            )) \
            .filter(Answer.assignment_id.in_(assignment_ids))
        drafts = query.all()

        statuses = {}
        for assignment in assignments:
            answer_count = next(
                (result.answer_count for result in answer_counts if result.assignment_id == assignment.id),
                0
            )
            feedback_count = next(
                (result.feedback_count for result in feedback_counts if result.assignment_id == assignment.id),
                0
            )
            assignment_drafts = [draft for draft in drafts if draft.assignment_id == assignment.id]
            comparison_count = assignment.completed_comparison_count_for_user(current_user.id)
            comparison_draft_count = assignment.draft_comparison_count_for_user(current_user.id)
            other_comparable_answers = assignment.comparable_answer_count - answer_count

            # students can only begin comparing when there there are enough answers submitted that they can do
            # comparisons without seeing the same answer more than once
            comparison_available = other_comparable_answers >= assignment.number_of_comparisons * 2
            # instructors and tas can compare as long as there are new possible comparisons
            if allow(EDIT, assignment):
                comparison_available = comparison_count < other_comparable_answers * (other_comparable_answers - 1) / 2

            statuses[assignment.uuid] = {
                'answers': {
                    'answered': answer_count > 0,
                    'feedback': feedback_count,
                    'count': answer_count,
                    'has_draft': len(assignment_drafts) > 0,
                    'draft_ids': [draft.uuid for draft in assignment_drafts]
                },
                'comparisons': {
                    'available': comparison_available,
                    'count': comparison_count,
                    'left': max(0, assignment.total_comparisons_required - comparison_count),
                    'has_draft': comparison_draft_count > 0
                }
            }

            if assignment.enable_self_evaluation:
                self_evaluation_count = next(
                    (result.self_evaluation_count for result in self_evaluations if result.assignment_id == assignment.id),
                    0
                )
                self_evaluation_draft_count = next(
                    (result.self_evaluation_count for result in self_evaluation_drafts if result.assignment_id == assignment.id),
                    0
                )
                statuses[assignment.uuid]['comparisons']['self_evaluation_completed'] = self_evaluation_count > 0
                statuses[assignment.uuid]['comparisons']['self_evaluation_draft'] = self_evaluation_draft_count > 0

        on_assignment_list_get_status.send(
            self,
            event_name=on_assignment_list_get_status.name,
            user=current_user,
            course_id=course.id,
            data=statuses)

        return {"statuses": statuses}
Exemple #6
0
    def get(self, course_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        require(READ, course,
            title="Assignment Status Unavailable",
            message="Assignment status can be seen only by those enrolled in the course. Please double-check your enrollment in this course.")

        group = current_user.get_course_group(course.id)
        group_id = group.id if group else None

        assignments = course.assignments \
            .filter_by(active=True) \
            .all()
        assignment_ids = [assignment.id for assignment in assignments]

        answer_counts = Answer.query \
            .with_entities(
                Answer.assignment_id,
                func.count(Answer.assignment_id).label('answer_count')
            ) \
            .filter_by(
                comparable=True,
                active=True,
                practice=False,
                draft=False
            ) \
            .filter(or_(
                Answer.user_id == current_user.id,
                and_(Answer.group_id == group_id, Answer.group_answer == True)
            )) \
            .filter(Answer.assignment_id.in_(assignment_ids)) \
            .group_by(Answer.assignment_id) \
            .all()


        feedback_counts = AnswerComment.query \
            .join(Answer) \
            .with_entities(
                Answer.assignment_id,
                func.count(Answer.assignment_id).label('feedback_count')
            ) \
            .filter(and_(
                AnswerComment.active == True,
                AnswerComment.draft == False,
                Answer.active == True,
                Answer.practice == False,
                Answer.draft == False,
                Answer.assignment_id.in_(assignment_ids)
            )) \
            .filter(or_(
                Answer.user_id == current_user.id,
                and_(Answer.group_id == group_id, Answer.group_id != None)
            )) \
            .group_by(Answer.assignment_id) \
            .all()

        # get self evaluation status for assignments with self evaluations enabled
        self_evaluations = AnswerComment.query \
            .join(Answer) \
            .with_entities(
                Answer.assignment_id,
                func.count(Answer.assignment_id).label('self_evaluation_count')
            ) \
            .filter(and_(
                AnswerComment.user_id == current_user.id,
                AnswerComment.active == True,
                AnswerComment.comment_type == AnswerCommentType.self_evaluation,
                AnswerComment.draft == False,
                Answer.active == True,
                Answer.practice == False,
                Answer.draft == False,
                Answer.assignment_id.in_(assignment_ids)
            )) \
            .group_by(Answer.assignment_id) \
            .all()

        self_evaluation_drafts = AnswerComment.query \
            .join(Answer) \
            .with_entities(
                Answer.assignment_id,
                func.count(Answer.assignment_id).label('self_evaluation_count')
            ) \
            .filter(and_(
                AnswerComment.user_id == current_user.id,
                AnswerComment.active == True,
                AnswerComment.comment_type == AnswerCommentType.self_evaluation,
                AnswerComment.draft == True,
                Answer.active == True,
                Answer.practice == False,
                Answer.draft == False,
                Answer.assignment_id.in_(assignment_ids)
            )) \
            .group_by(Answer.assignment_id) \
            .all()

        query = Answer.query \
            .options(load_only('id', 'assignment_id', 'uuid')) \
            .filter_by(
                active=True,
                practice=False,
                draft=True
            ) \
            .filter(or_(
                and_(Answer.group_id == group_id, Answer.group_id != None),
                Answer.user_id == current_user.id
            )) \
            .filter(Answer.assignment_id.in_(assignment_ids))
        drafts = query.all()

        statuses = {}
        for assignment in assignments:
            answer_count = next(
                (result.answer_count for result in answer_counts if result.assignment_id == assignment.id),
                0
            )
            feedback_count = next(
                (result.feedback_count for result in feedback_counts if result.assignment_id == assignment.id),
                0
            )
            assignment_drafts = [draft for draft in drafts if draft.assignment_id == assignment.id]
            comparison_count = assignment.completed_comparison_count_for_user(current_user.id)
            comparison_draft_count = assignment.draft_comparison_count_for_user(current_user.id)
            other_comparable_answers = assignment.comparable_answer_count - answer_count

            # students can only begin comparing when there there are enough answers submitted that they can do
            # comparisons without seeing the same answer more than once
            comparison_available = other_comparable_answers >= assignment.number_of_comparisons * 2
            # instructors and tas can compare as long as there are new possible comparisons
            if can(EDIT, assignment):
                comparison_available = comparison_count < other_comparable_answers * (other_comparable_answers - 1) / 2

            statuses[assignment.uuid] = {
                'answers': {
                    'answered': answer_count > 0,
                    'feedback': feedback_count,
                    'count': answer_count,
                    'has_draft': len(assignment_drafts) > 0,
                    'draft_ids': [draft.uuid for draft in assignment_drafts]
                },
                'comparisons': {
                    'available': comparison_available,
                    'count': comparison_count,
                    'left': max(0, assignment.total_comparisons_required - comparison_count),
                    'has_draft': comparison_draft_count > 0
                }
            }

            if assignment.enable_self_evaluation:
                self_evaluation_count = next(
                    (result.self_evaluation_count for result in self_evaluations if result.assignment_id == assignment.id),
                    0
                )
                self_evaluation_draft_count = next(
                    (result.self_evaluation_count for result in self_evaluation_drafts if result.assignment_id == assignment.id),
                    0
                )
                statuses[assignment.uuid]['comparisons']['self_evaluation_completed'] = self_evaluation_count > 0
                statuses[assignment.uuid]['comparisons']['self_evaluation_draft'] = self_evaluation_draft_count > 0

        on_assignment_list_get_status.send(
            self,
            event_name=on_assignment_list_get_status.name,
            user=current_user,
            course_id=course.id,
            data=statuses)

        return {"statuses": statuses}
Exemple #7
0
    def post(self, course_uuid, assignment_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)

        if not assignment.answer_grace and not allow(MANAGE, assignment):
            abort(403, title="Answer Not Submitted", message="Sorry, the answer deadline has passed. No answers can be submitted after the deadline unless the instructor submits the answer for you.")

        require(CREATE, Answer(course_id=course.id),
            title="Answer Not Submitted",
            message="Answers can be submitted only by those enrolled in the course. Please double-check your enrollment in this course.")
        restrict_user = not allow(MANAGE, assignment)

        answer = Answer(assignment_id=assignment.id)

        params = new_answer_parser.parse_args()
        answer.content = params.get("content")
        answer.draft = params.get("draft")

        file_uuid = params.get('file_id')
        attachment = None
        if file_uuid:
            attachment = File.get_by_uuid_or_404(file_uuid)
            answer.file_id = attachment.id
        else:
            answer.file_id = None

        # non-drafts must have content
        if not answer.draft and not answer.content and not file_uuid:
            abort(400, title="Answer Not Submitted", message="Please provide content in the text editor or upload a file and try submitting again.")

        user_uuid = params.get("user_id")
        group_uuid = params.get("group_id")
        # we allow instructor and TA to submit multiple answers for other users in the class
        if user_uuid and not allow(MANAGE, Answer(course_id=course.id)):
            abort(400, title="Answer Not Submitted", message="Only instructors and teaching assistants can submit an answer on behalf of another.")
        if group_uuid and not assignment.enable_group_answers:
            abort(400, title="Answer Not Submitted", message="Group answers are not allowed for this assignment.")
        if group_uuid and not allow(MANAGE, Answer(course_id=course.id)):
            abort(400, title="Answer Not Submitted", message="Only instructors and teaching assistants can submit an answer on behalf of a group.")
        if group_uuid and user_uuid:
            abort(400, title="Answer Not Submitted", message="You cannot submit an answer for a user and a group at the same time.")

        user = User.get_by_uuid_or_404(user_uuid) if user_uuid else current_user
        group = Group.get_active_by_uuid_or_404(group_uuid) if group_uuid else None

        if restrict_user and assignment.enable_group_answers and not group:
            group = current_user.get_course_group(course.id)
            if group == None:
                abort(400, title="Answer Not Submitted",
                    message="You are currently not in any group for this course. Please contact your instructor to be added to a group.")

        check_for_existing_answers = False
        if group and assignment.enable_group_answers:
            if group.course_id != course.id:
                abort(400, title="Answer Not Submitted",
                    message="Group answers can be submitted to courses they belong in.")

            answer.user_id = None
            answer.group_id = group.id
            answer.comparable = True
            check_for_existing_answers = True
        else:
            answer.user_id = user.id
            answer.group_id = None

            course_role = User.get_user_course_role(answer.user_id, course.id)

            # only system admin can add answers for themselves to a class without being enrolled in it
            # required for managing comparison examples as system admin
            if (not course_role or course_role == CourseRole.dropped) and current_user.system_role != SystemRole.sys_admin:
                abort(400, title="Answer Not Submitted", message="Answers can be submitted only by those enrolled in the course. Please double-check your enrollment in this course.")

            if course_role == CourseRole.student and assignment.enable_group_answers:
                abort(400, title="Answer Not Submitted", message="Students can only submit group answers for this assignment.")

            # we allow instructor and TA to submit multiple answers for their own,
            # but not for student. Each student can only have one answer.
            if course_role and course_role == CourseRole.student:
                check_for_existing_answers = True
                answer.comparable = True
            else:
                # instructor / TA / Sys Admin can mark the answer as non-comparable, unless the answer is for a student
                answer.comparable = params.get("comparable")

        if check_for_existing_answers:
            # check for answers with user_id or group_id
            prev_answers = Answer.query \
                .filter_by(
                    assignment_id=assignment.id,
                    user_id=answer.user_id,
                    group_id=answer.group_id,
                    active=True
                ) \
                .all()

            # check if there is a previous answer submitted for the student
            non_draft_answers = [prev_answer for prev_answer in prev_answers if not prev_answer.draft]
            if len(non_draft_answers) > 0:
                abort(400, title="Answer Not Submitted", message="An answer has already been submitted for this assignment by you or on your behalf.")

            # check if there is a previous draft answer submitted for the student (soft-delete if present)
            draft_answers = [prev_answer for prev_answer in prev_answers if prev_answer.draft]
            for draft_answer in draft_answers:
                draft_answer.active = False

        # set submission date if answer is being submitted for the first time
        if not answer.draft and not answer.submission_date:
            answer.submission_date = datetime.datetime.utcnow()

        answer.update_attempt(
            params.get('attempt_uuid'),
            params.get('attempt_started', None),
            params.get('attempt_ended', None)
        )

        db.session.add(answer)
        db.session.commit()

        on_answer_create.send(
            self,
            event_name=on_answer_create.name,
            user=current_user,
            course_id=course.id,
            answer=answer,
            data=marshal(answer, dataformat.get_answer(restrict_user)))

        if attachment:
            on_attach_file.send(
                self,
                event_name=on_attach_file.name,
                user=current_user,
                course_id=course.id,
                file=attachment,
                data={'answer_id': answer.id, 'file_id': attachment.id})

        # update course & assignment grade for user if answer is fully submitted
        if not answer.draft:
            if answer.user:
                assignment.calculate_grade(answer.user)
                course.calculate_grade(answer.user)
            elif answer.group:
                assignment.calculate_group_grade(answer.group)
                course.calculate_group_grade(answer.group)

        return marshal(answer, dataformat.get_answer(restrict_user))