Пример #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))
        }
Пример #2
0
    def post(self, course_uuid):
        """
        Duplicate a course
        """
        course = Course.get_active_by_uuid_or_404(course_uuid)
        require(
            EDIT,
            course,
            title="Course Not Duplicated",
            message=
            "Sorry, your role in this course does not allow you to duplicate it."
        )

        params = duplicate_course_parser.parse_args()

        start_date = datetime.datetime.strptime(
            params.get("start_date"),
            '%Y-%m-%dT%H:%M:%S.%fZ') if params.get("start_date") else None

        end_date = datetime.datetime.strptime(
            params.get("end_date"),
            '%Y-%m-%dT%H:%M:%S.%fZ') if params.get("end_date") else None

        if start_date is None:
            abort(400,
                  title="Course Not Saved",
                  message="Course start time is required.")
        elif start_date and end_date and start_date > end_date:
            abort(400,
                  title="Course Not Saved",
                  message="Course end time must be after course start time.")

        assignments = [
            assignment for assignment in course.assignments
            if assignment.active
        ]
        assignments_copy_data = params.get("assignments")

        if len(assignments) != len(assignments_copy_data):
            abort(
                400,
                title="Course Not Saved",
                message=
                "The course is missing assignments. Please reload the page and try duplicating again."
            )

        for assignment_copy_data in assignments_copy_data:
            if assignment_copy_data.get('answer_start'):
                assignment_copy_data[
                    'answer_start'] = datetime.datetime.strptime(
                        assignment_copy_data.get('answer_start'),
                        '%Y-%m-%dT%H:%M:%S.%fZ')

            if assignment_copy_data.get('answer_end'):
                assignment_copy_data[
                    'answer_end'] = datetime.datetime.strptime(
                        assignment_copy_data.get('answer_end'),
                        '%Y-%m-%dT%H:%M:%S.%fZ')

            if assignment_copy_data.get('compare_start'):
                assignment_copy_data[
                    'compare_start'] = datetime.datetime.strptime(
                        assignment_copy_data.get('compare_start'),
                        '%Y-%m-%dT%H:%M:%S.%fZ')

            if assignment_copy_data.get('compare_end'):
                assignment_copy_data[
                    'compare_end'] = datetime.datetime.strptime(
                        assignment_copy_data.get('compare_end'),
                        '%Y-%m-%dT%H:%M:%S.%fZ')

            if 'enable_self_evaluation' not in assignment_copy_data:
                assignment_copy_data['enable_self_evaluation'] = False

            if assignment_copy_data.get('self_eval_start'):
                assignment_copy_data[
                    'self_eval_start'] = datetime.datetime.strptime(
                        assignment_copy_data.get('self_eval_start'),
                        '%Y-%m-%dT%H:%M:%S.%fZ')

            if assignment_copy_data.get('self_eval_end'):
                assignment_copy_data[
                    'self_eval_end'] = datetime.datetime.strptime(
                        assignment_copy_data.get('self_eval_end'),
                        '%Y-%m-%dT%H:%M:%S.%fZ')

            valid, error_message = Assignment.validate_periods(
                start_date, end_date, assignment_copy_data.get('answer_start'),
                assignment_copy_data.get('answer_end'),
                assignment_copy_data.get('compare_start'),
                assignment_copy_data.get('compare_end'),
                assignment_copy_data.get('self_eval_start'),
                assignment_copy_data.get('self_eval_end'))
            if not valid:
                error_message = error_message.replace(
                    ".", "") + " for assignment " + text_type(
                        assignment_copy_data.get('name', '')) + "."
                abort(400, title="Course Not Saved", message=error_message)

        # duplicate course
        duplicate_course = Course(name=params.get("name"),
                                  year=params.get("year"),
                                  term=params.get("term"),
                                  sandbox=params.get("sandbox"),
                                  start_date=start_date,
                                  end_date=end_date)
        db.session.add(duplicate_course)

        # also need to enrol the user as an instructor
        new_user_course = UserCourse(course=duplicate_course,
                                     user_id=current_user.id,
                                     course_role=CourseRole.instructor)
        db.session.add(new_user_course)

        # duplicate assignments
        for assignment in assignments:
            # this should never be null due
            assignment_copy_data = next(
                (assignment_copy_data
                 for assignment_copy_data in assignments_copy_data
                 if assignment_copy_data.get('id') == assignment.uuid), None)

            if not assignment_copy_data:
                abort(400,
                      title="Course Not Saved",
                      message="Missing information for assignment " +
                      assignment.name + ". Please try duplicating again.")

            duplicate_assignment = Assignment(
                course=duplicate_course,
                user_id=current_user.id,
                file=assignment.file,
                name=assignment.name,
                description=assignment.description,
                answer_start=assignment_copy_data.get('answer_start'),
                answer_end=assignment_copy_data.get('answer_end'),
                compare_start=assignment_copy_data.get('compare_start'),
                compare_end=assignment_copy_data.get('compare_end'),
                self_eval_start=assignment_copy_data.get('self_eval_start')
                if assignment_copy_data.get('enable_self_evaluation',
                                            False) else None,
                self_eval_end=assignment_copy_data.get('self_eval_end')
                if assignment_copy_data.get('enable_self_evaluation',
                                            False) else None,
                self_eval_instructions=assignment.self_eval_instructions
                if assignment_copy_data.get('enable_self_evaluation',
                                            False) else None,
                answer_grade_weight=assignment.answer_grade_weight,
                comparison_grade_weight=assignment.comparison_grade_weight,
                self_evaluation_grade_weight=assignment.
                self_evaluation_grade_weight,
                number_of_comparisons=assignment.number_of_comparisons,
                students_can_reply=assignment.students_can_reply,
                enable_self_evaluation=assignment_copy_data.get(
                    'enable_self_evaluation', False),
                enable_group_answers=assignment.enable_group_answers,
                pairing_algorithm=assignment.pairing_algorithm,
                scoring_algorithm=assignment.scoring_algorithm,
                peer_feedback_prompt=assignment.peer_feedback_prompt,
                educators_can_compare=assignment.educators_can_compare,
                rank_display_limit=assignment.rank_display_limit,
            )
            db.session.add(duplicate_assignment)

            # duplicate assignment criteria
            for assignment_criterion in assignment.assignment_criteria:
                if not assignment_criterion.active:
                    continue

                duplicate_assignment_criterion = AssignmentCriterion(
                    assignment=duplicate_assignment,
                    criterion_id=assignment_criterion.criterion_id)
                db.session.add(duplicate_assignment_criterion)

            # duplicate assignment comparisons examples
            for comparison_example in assignment.comparison_examples:
                answer1 = comparison_example.answer1
                answer2 = comparison_example.answer2

                # duplicate assignment comparisons example answers
                duplicate_answer1 = Answer(assignment=duplicate_assignment,
                                           user_id=current_user.id,
                                           file=answer1.file,
                                           content=answer1.content,
                                           practice=answer1.practice,
                                           active=answer1.active,
                                           draft=answer1.draft)
                db.session.add(duplicate_answer1)

                # duplicate assignment comparisons example answers
                duplicate_answer2 = Answer(assignment=duplicate_assignment,
                                           user_id=current_user.id,
                                           file=answer2.file,
                                           content=answer2.content,
                                           practice=answer2.practice,
                                           active=answer2.active,
                                           draft=answer2.draft)
                db.session.add(duplicate_answer2)

                duplicate_comparison_example = ComparisonExample(
                    assignment=duplicate_assignment,
                    answer1=duplicate_answer1,
                    answer2=duplicate_answer2)
                db.session.add(duplicate_comparison_example)

        db.session.commit()

        on_course_duplicate.send(self,
                                 event_name=on_course_duplicate.name,
                                 user=current_user,
                                 course=duplicate_course,
                                 data=marshal(course, dataformat.get_course()))

        return marshal(duplicate_course, dataformat.get_course())
Пример #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))
Пример #4
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')
        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")
        # 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 user_uuid:
            user = User.get_by_uuid_or_404(user_uuid)
            answer.user_id = user.id
        else:
            answer.user_id = current_user.id

        user_course = UserCourse.query \
            .filter_by(
                course_id=course.id,
                user_id=answer.user_id
            ) \
            .one_or_none()

        # we allow instructor and TA to submit multiple answers for their own,
        # but not for student. Each student can only have one answer.
        instructors_and_tas = [CourseRole.instructor.value, CourseRole.teaching_assistant.value]
        if user_course == None:
            # 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 current_user.id != answer.user_id or 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.")

        elif user_course.course_role.value not in instructors_and_tas:
            # check if there is a previous answer submitted for the student
            prev_answer = Answer.query. \
                filter_by(
                    assignment_id=assignment.id,
                    user_id=answer.user_id,
                    active=True
                ). \
                first()
            if prev_answer:
                abort(400, title="Answer Not Submitted", message="An answer has already been submitted for this assignment by you or on your behalf.")

        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,
            data=marshal(answer, dataformat.get_answer(restrict_user)))

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

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