Esempio n. 1
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)
        assignment_criteria = assignment.assignment_criteria
        require(EDIT, assignment,
            title="Assignment Not Saved",
            message="Sorry, your role in this course does not allow you to save assignments.")

        if current_app.config.get('DEMO_INSTALLATION', False):
            from data.fixtures import DemoDataFixture
            if assignment.id in DemoDataFixture.DEFAULT_ASSIGNMENT_IDS:
                abort(400, title="Assignment Not Saved", message="Sorry, you cannot edit the default demo assignments.")

        params = existing_assignment_parser.parse_args()

        # make sure the assignment id in the url and the id matches
        if params['id'] != assignment_uuid:
            abort(400, title="Assignment Not Saved", message="The assignment's ID does not match the URL, which is required in order to update the assignment.")

        # make sure that file attachment exists
        file_uuid = params.get('file_id')
        if file_uuid:
            attachment = File.get_by_uuid_or_404(file_uuid)
            assignment.file_id = attachment.id
        else:
            assignment.file_id = None

        # modify assignment according to new values, preserve original values if values not passed
        assignment.name = params.get("name", assignment.name)
        assignment.description = params.get("description", assignment.description)
        assignment.peer_feedback_prompt = params.get("peer_feedback_prompt", assignment.peer_feedback_prompt)
        assignment.answer_start = datetime.datetime.strptime(
            params.get('answer_start', assignment.answer_start),
            '%Y-%m-%dT%H:%M:%S.%fZ')
        assignment.answer_end = datetime.datetime.strptime(
            params.get('answer_end', assignment.answer_end),
            '%Y-%m-%dT%H:%M:%S.%fZ')
        # if nothing in request, assume user don't want comparison date
        assignment.compare_start = params.get('compare_start', None)
        if assignment.compare_start is not None:
            assignment.compare_start = datetime.datetime.strptime(
                assignment.compare_start,
                '%Y-%m-%dT%H:%M:%S.%fZ')
        assignment.compare_end = params.get('compare_end', None)
        if assignment.compare_end is not None:
            assignment.compare_end = datetime.datetime.strptime(
                params.get('compare_end', assignment.compare_end),
                '%Y-%m-%dT%H:%M:%S.%fZ')

        # validate answer + comparison period start & end times
        valid, error_message = Assignment.validate_periods(course.start_date, course.end_date,
             assignment.answer_start, assignment.answer_end, assignment.compare_start, assignment.compare_end)
        if not valid:
            abort(400, title="Assignment Not Saved", message=error_message)

        assignment.students_can_reply = params.get('students_can_reply', False)
        assignment.number_of_comparisons = params.get(
            'number_of_comparisons', assignment.number_of_comparisons)
        assignment.enable_self_evaluation = params.get(
            'enable_self_evaluation', assignment.enable_self_evaluation)

        assignment.answer_grade_weight = params.get(
            'answer_grade_weight', assignment.answer_grade_weight)
        assignment.comparison_grade_weight = params.get(
            'comparison_grade_weight', assignment.comparison_grade_weight)
        assignment.self_evaluation_grade_weight = params.get(
            'self_evaluation_grade_weight', assignment.self_evaluation_grade_weight)

        pairing_algorithm = params.get("pairing_algorithm")
        check_valid_pairing_algorithm(pairing_algorithm)
        if not assignment.compared:
            assignment.pairing_algorithm = PairingAlgorithm(pairing_algorithm)
        elif assignment.pairing_algorithm != PairingAlgorithm(pairing_algorithm):
            msg = 'The answer pair selection algorithm cannot be changed for this assignment ' + \
                    'because it has already been used in one or more comparisons.'
            abort(403, title="Assignment Not Saved", message=msg)

        assignment.educators_can_compare = params.get("educators_can_compare")

        assignment.rank_display_limit = params.get("rank_display_limit", None)
        if assignment.rank_display_limit != None and assignment.rank_display_limit <= 0:
            assignment.rank_display_limit = None

        criterion_uuids = [c.get('id') for c in params.criteria]
        criterion_data = {c.get('id'): c.get('weight', 1) for c in params.criteria}
        if assignment.compared:
            active_uuids = [c.uuid for c in assignment.criteria]
            active_data = {c.uuid: c.weight for c in assignment.criteria}
            if set(criterion_uuids) != set(active_uuids):
                msg = 'The criteria cannot be changed for this assignment ' + \
                      'because they have already been used in one or more comparisons.'
                abort(403, title="Assignment Not Saved", message=msg)

            for criterion in assignment.criteria:
                if criterion_data.get(criterion.uuid) != criterion.weight:
                    msg = 'The criteria weights cannot be changed for this assignment ' + \
                        'because they have already been used in one or more comparisons.'
                    abort(403, title="Assignment Not Saved", message=msg)
        else:
            # assignment not compared yet, can change criteria
            if len(criterion_uuids) == 0:
                msg = "Please add at least one criterion to the assignment and save again."
                abort(403, title="Assignment Not Saved", message=msg)

            existing_uuids = [c.criterion_uuid for c in assignment_criteria]
            # disable old ones
            for c in assignment_criteria:
                c.active = c.criterion_uuid in criterion_uuids
                if c.active:
                    c.weight = criterion_data.get(c.criterion_uuid)

            # add the new ones
            new_uuids = []
            for criterion_uuid in criterion_uuids:
                if criterion_uuid not in existing_uuids:
                    new_uuids.append(criterion_uuid)

            if len(new_uuids) > 0:
                new_criteria = Criterion.query.filter(Criterion.uuid.in_(new_uuids)).all()
                for criterion in new_criteria:
                    assignment_criteria.append(AssignmentCriterion(
                        criterion=criterion,
                        weight=criterion_data.get(criterion.uuid)
                    ))

        # ensure criteria are in order
        for index, criterion_uuid in enumerate(criterion_uuids):
            assignment_criterion = next(assignment_criterion \
                for assignment_criterion in assignment_criteria \
                if assignment_criterion.criterion_uuid == criterion_uuid)

            assignment_criteria.remove(assignment_criterion)
            assignment_criteria.insert(index, assignment_criterion)

        model_changes = get_model_changes(assignment)

        on_assignment_modified.send(
            self,
            event_name=on_assignment_modified.name,
            user=current_user,
            course_id=course.id,
            assignment=assignment,
            data=model_changes)

        db.session.commit()

        # need to reorder after update
        assignment_criteria.reorder()

        # update assignment and course grades if needed
        if model_changes and (model_changes.get('answer_grade_weight') or
                 model_changes.get('comparison_grade_weight') or
                 model_changes.get('self_evaluation_grade_weight') or
                 model_changes.get('enable_self_evaluation')):
            assignment.calculate_grades()
            course.calculate_grades()

        return marshal(assignment, dataformat.get_assignment())
Esempio n. 2
0
    def post(self, course_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        # check permission first before reading parser arguments
        new_assignment = Assignment(course_id=course.id)
        require(CREATE, new_assignment,
            title="Assignment Not Saved",
            message="Sorry, your role in this course does not allow you to save assignments.")

        params = new_assignment_parser.parse_args()

        new_assignment.user_id = current_user.id
        new_assignment.name = params.get("name")
        new_assignment.description = params.get("description")
        new_assignment.peer_feedback_prompt = params.get("peer_feedback_prompt")
        new_assignment.answer_start = dateutil.parser.parse(params.get('answer_start'))
        new_assignment.answer_end = dateutil.parser.parse(params.get('answer_end'))
        new_assignment.educators_can_compare = params.get("educators_can_compare")
        new_assignment.rank_display_limit = params.get("rank_display_limit", None)
        if new_assignment.rank_display_limit != None and new_assignment.rank_display_limit <= 0:
            new_assignment.rank_display_limit = None

        # make sure that file attachment exists
        file_uuid = params.get('file_id')
        if file_uuid:
            attachment = File.get_by_uuid_or_404(file_uuid)
            new_assignment.file_id = attachment.id
        else:
            new_assignment.file_id = None

        new_assignment.compare_start = params.get('compare_start', None)
        if new_assignment.compare_start is not None:
            new_assignment.compare_start = dateutil.parser.parse(params.get('compare_start', None))

        new_assignment.compare_end = params.get('compare_end', None)
        if new_assignment.compare_end is not None:
            new_assignment.compare_end = dateutil.parser.parse(params.get('compare_end', None))

        # validate answer + comparison period start & end times
        valid, error_message = Assignment.validate_periods(course.start_date, course.end_date,
             new_assignment.answer_start, new_assignment.answer_end,
             new_assignment.compare_start, new_assignment.compare_end)
        if not valid:
            abort(400, title="Assignment Not Saved", message=error_message)

        new_assignment.students_can_reply = params.get('students_can_reply', False)
        new_assignment.number_of_comparisons = params.get('number_of_comparisons')
        new_assignment.enable_self_evaluation = params.get('enable_self_evaluation')

        new_assignment.answer_grade_weight = params.get('answer_grade_weight')
        new_assignment.comparison_grade_weight = params.get('comparison_grade_weight')
        new_assignment.self_evaluation_grade_weight = params.get('self_evaluation_grade_weight')

        pairing_algorithm = params.get("pairing_algorithm", PairingAlgorithm.random)
        check_valid_pairing_algorithm(pairing_algorithm)
        new_assignment.pairing_algorithm = PairingAlgorithm(pairing_algorithm)

        criterion_uuids = [c.get('id') for c in params.criteria]
        criterion_data = {c.get('id'): c.get('weight', 1) for c in params.criteria}
        if len(criterion_data) == 0:
            msg = "Please add at least one criterion to the assignment and save again."
            abort(400, title="Assignment Not Saved", message=msg)

        criteria = Criterion.query \
            .filter(Criterion.uuid.in_(criterion_uuids)) \
            .all()

        if len(criterion_uuids) != len(criteria):
            abort(400, title="Assignment Not Saved", message="Please double-check the criteria you selected and save agaiin.")

        # add criteria to assignment in order
        for criterion_uuid in criterion_uuids:
            criterion = next(criterion for criterion in criteria if criterion.uuid == criterion_uuid)
            new_assignment.assignment_criteria.append(AssignmentCriterion(
                criterion=criterion,
                weight=criterion_data.get(criterion.uuid)
            ))

        db.session.add(new_assignment)
        db.session.commit()

        # need to reorder after insert
        new_assignment.assignment_criteria.reorder()

        # update course grades
        course.calculate_grades()

        on_assignment_create.send(
            self,
            event_name=on_assignment_create.name,
            user=current_user,
            course_id=course.id,
            assignment=new_assignment,
            data=marshal(new_assignment, dataformat.get_assignment(False)))

        return marshal(new_assignment, dataformat.get_assignment())
Esempio n. 3
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())