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())
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())
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())