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