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) require( CREATE, ComparisonExample(assignment=Assignment(course_id=course.id)), title="Comparison Example Not Saved", message= "Sorry, your role in this course does not allow you to save practice answers." ) new_comparison_example = ComparisonExample(assignment_id=assignment.id) params = new_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True new_comparison_example.answer1 = answer1 else: abort( 400, title="Comparison Example Not Saved", message= "Please add two answers with content to the practice answers and try again." ) if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True new_comparison_example.answer2 = answer2 else: abort( 400, title="Comparison Example Not Saved", message= "Please add two answers with content to the practice answers and try again." ) on_comparison_example_create.send( self, event_name=on_comparison_example_create.name, user=current_user, course_id=course.id, data=marshal( new_comparison_example, dataformat.get_comparison_example(with_answers=False))) db.session.add(new_comparison_example) db.session.commit() return marshal(new_comparison_example, dataformat.get_comparison_example())
def post(self, course_uuid, assignment_uuid, answer_uuid): """ Mark an answer as being a top answer :param course_uuid: :param assignment_uuid: :param answer_uuid: :return: marked answer """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(MANAGE, answer, title="Answer Not Added", message="Your role in this course does not allow you to add to the list of instructor-picked answers.") params = top_answer_parser.parse_args() answer.top_answer = params.get('top_answer') db.session.add(answer) on_set_top_answer.send( self, event_name=on_set_top_answer.name, user=current_user, course_id=course.id, assignment_id=assignment.id, data={'answer_id': answer.id, 'top_answer': answer.top_answer}) db.session.commit() return marshal(answer, dataformat.get_answer(restrict_user=False))
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) require(CREATE, AssignmentComment(course_id=course.id)) new_assignment_comment = AssignmentComment(assignment_id=assignment.id) params = new_assignment_comment_parser.parse_args() new_assignment_comment.content = params.get("content") if not new_assignment_comment.content: return {"error": "The comment content is empty!"}, 400 new_assignment_comment.user_id = current_user.id db.session.add(new_assignment_comment) db.session.commit() on_assignment_comment_create.send( self, event_name=on_assignment_comment_create.name, user=current_user, course_id=course.id, assignment_comment=new_assignment_comment, data=marshal(new_assignment_comment, dataformat.get_assignment_comment(False))) return marshal(new_assignment_comment, dataformat.get_assignment_comment())
def delete(self, course_uuid, assignment_uuid, answer_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(DELETE, answer, title="Answer Not Deleted", message="Sorry, your role in this course does not allow you to delete this answer.") if current_app.config.get('DEMO_INSTALLATION', False): from data.fixtures import DemoDataFixture if assignment.id in DemoDataFixture.DEFAULT_ASSIGNMENT_IDS and answer.user_id in DemoDataFixture.DEFAULT_STUDENT_IDS: abort(400, title="Answer Not Deleted", message="Sorry, you cannot delete the default student demo answers.") answer.active = False db.session.commit() # update course & assignment grade for user if answer was 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) on_answer_delete.send( self, event_name=on_answer_delete.name, user=current_user, course_id=course.id, answer=answer, data={'assignment_id': assignment.id, 'answer_id': answer.id}) return {'id': answer.uuid}
def post(self, course_uuid, assignment_uuid, answer_uuid): """ Mark an answer as being a top answer :param course_uuid: :param assignment_uuid: :param answer_uuid: :return: marked answer """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(MANAGE, answer) params = top_answer_parser.parse_args() answer.top_answer = params.get('top_answer') db.session.add(answer) on_set_top_answer.send( self, event_name=on_set_top_answer.name, user=current_user, course_id=course.id, assignment_id=assignment.id, data={'answer_id': answer.id, 'top_answer': answer.top_answer}) db.session.commit() return marshal(answer, dataformat.get_answer(restrict_user=False))
def delete(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) require(DELETE, assignment, title="Assignment Not Deleted", message="Sorry, your role in this course does not allow you to delete 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 Deleted", message="Sorry, you cannot remove the default demo assignments.") formatted_assignment = marshal(assignment, dataformat.get_assignment(False)) # delete file when assignment is deleted assignment.active = False assignment.clear_lti_links() db.session.commit() # update course grades course.calculate_grades() on_assignment_delete.send( self, event_name=on_assignment_delete.name, user=current_user, course_id=course.id, assignment=assignment, data=formatted_assignment) return {'id': assignment.uuid}
def delete(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Delete an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404(answer_comment_uuid) require(DELETE, answer_comment) data = marshal(answer_comment, dataformat.get_answer_comment(False)) answer_comment.active = False db.session.commit() # update course & assignment grade for user if self-evaluation is completed if not answer_comment.draft and answer_comment.comment_type == AnswerCommentType.self_evaluation: assignment.calculate_grade(answer_comment.user) course.calculate_grade(answer_comment.user) on_answer_comment_delete.send( self, event_name=on_answer_comment_delete.name, user=current_user, course_id=course.id, answer_comment=answer_comment, data=data, ) return {"id": answer_comment.uuid}
def post(self, course_uuid, assignment_uuid, assignment_comment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) assignment_comment = AssignmentComment.get_active_by_uuid_or_404(assignment_comment_uuid) require(EDIT, assignment_comment) params = existing_assignment_comment_parser.parse_args() # make sure the comment id in the rul and the id matches if params['id'] != assignment_comment_uuid: return {"error": "Comment id does not match URL."}, 400 # modify comment according to new values, preserve original values if values not passed if not params.get("content"): return {"error": "The comment content is empty!"}, 400 assignment_comment.content = params.get("content") db.session.add(assignment_comment) on_assignment_comment_modified.send( self, event_name=on_assignment_comment_modified.name, user=current_user, course_id=course.id, assignment_comment=assignment_comment, data=get_model_changes(assignment_comment)) db.session.commit() return marshal(assignment_comment, dataformat.get_assignment_comment())
def get(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Get an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404( answer_comment_uuid) require( READ, answer_comment, title="Reply Unavailable", message= "Sorry, your role in this course does not allow you to view this reply." ) on_answer_comment_get.send(self, event_name=on_answer_comment_get.name, user=current_user, course_id=course.id, data={ 'assignment_id': assignment.id, 'answer_id': answer.id, 'answer_comment_id': answer_comment.id }) return marshal(answer_comment, dataformat.get_answer_comment())
def delete(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Delete an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404( answer_comment_uuid) require( DELETE, answer_comment, title="Reply Not Deleted", message= "Sorry, your role in this course does not allow you to delete replies for this answer." ) data = marshal(answer_comment, dataformat.get_answer_comment(False)) answer_comment.active = False db.session.commit() # update course & assignment grade for user if self-evaluation is completed if not answer_comment.draft and answer_comment.comment_type == AnswerCommentType.self_evaluation: assignment.calculate_grade(answer_comment.user) course.calculate_grade(answer_comment.user) on_answer_comment_delete.send(self, event_name=on_answer_comment_delete.name, user=current_user, course_id=course.id, answer_comment=answer_comment, data=data) return {'id': answer_comment.uuid}
def get(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) require( READ, assignment, title="Help Comments Unavailable", message= "Help comments can be seen only by those enrolled in the course. Please double-check your enrollment in this course." ) restrict_user = not allow(MANAGE, assignment) assignment_comments = AssignmentComment.query \ .filter_by( course_id=course.id, assignment_id=assignment.id, active=True ) \ .order_by(AssignmentComment.created.asc()).all() on_assignment_comment_list_get.send( self, event_name=on_assignment_comment_list_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return { "objects": marshal(assignment_comments, dataformat.get_assignment_comment(restrict_user)) }
def delete(self, course_uuid, assignment_uuid, assignment_comment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) assignment_comment = AssignmentComment.get_active_by_uuid_or_404( assignment_comment_uuid) require( DELETE, assignment_comment, title="Help Comment Not Deleted", message= "Sorry, your role in this course does not allow you to delete help comments." ) data = marshal(assignment_comment, dataformat.get_assignment_comment(False)) assignment_comment.active = False db.session.commit() on_assignment_comment_delete.send( self, event_name=on_assignment_comment_delete.name, user=current_user, course_id=course.id, assignment_comment=assignment_comment, data=data) return {'id': assignment_comment.uuid}
def get(self, course_uuid, assignment_uuid, answer_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404( answer_uuid, joinedloads=['file', 'user', 'group', 'score']) require( READ, answer, title="Answer Unavailable", message= "Sorry, your role in this course does not allow you to view this answer." ) restrict_user = not allow(MANAGE, assignment) on_answer_get.send(self, event_name=on_answer_get.name, user=current_user, course_id=course.id, data={ 'assignment_id': assignment.id, 'answer_id': answer.id }) # don't include score/rank unless the user is non-restricted include_score = not restrict_user return marshal( answer, dataformat.get_answer(restrict_user, include_score=include_score))
def get(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) require( READ, ComparisonExample(course_id=course.id), title="Comparison Example Unavailable", message= "Sorry, your role in this course does not allow you to view practice answers." ) # Get all comparison examples for this assignment comparison_examples = ComparisonExample.query \ .filter_by( active=True, assignment_id=assignment.id ) \ .all() on_comparison_example_list_get.send( self, event_name=on_comparison_example_list_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return { "objects": marshal(comparison_examples, dataformat.get_comparison_example()) }
def delete(self, course_uuid, assignment_uuid, answer_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(DELETE, answer) answer.active = False if answer.file: answer.file.active = False db.session.commit() # update course & assignment grade for user if answer was fully submitted if not answer.draft: assignment.calculate_grade(answer.user) course.calculate_grade(answer.user) on_answer_delete.send( self, event_name=on_answer_delete.name, user=current_user, course_id=course.id, answer=answer, data={'assignment_id': assignment.id, 'answer_id': answer.id}) return {'id': answer.uuid}
def delete(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Delete an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404(answer_comment_uuid) require(DELETE, answer_comment, title="Feedback Not Deleted", message="Sorry, your role in this course does not allow you to delete feedback for this answer.") data = marshal(answer_comment, dataformat.get_answer_comment(False)) answer_comment.active = False db.session.commit() # update course & assignment grade for user if self-evaluation is completed if not answer_comment.draft and answer_comment.comment_type == AnswerCommentType.self_evaluation: assignment.calculate_grade(answer_comment.user) course.calculate_grade(answer_comment.user) on_answer_comment_delete.send( self, event_name=on_answer_comment_delete.name, user=current_user, course_id=course.id, answer_comment=answer_comment, data=data) return {'id': answer_comment.uuid}
def delete(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404( comparison_example_uuid) require( DELETE, comparison_example, title="Comparison Example Not Deleted", message= "Sorry, your role in this course does not allow you to delete practice answers." ) formatted_comparison_example = marshal( comparison_example, dataformat.get_comparison_example(with_answers=False)) comparison_example.active = False db.session.add(comparison_example) db.session.commit() on_comparison_example_delete.send( self, event_name=on_comparison_example_delete.name, user=current_user, course_id=course.id, data=formatted_comparison_example) return {'id': comparison_example.uuid}
def post(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404(comparison_example_uuid) require(EDIT, comparison_example) params = existing_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True comparison_example.answer1 = answer1 else: return {"error": "Comparison examples must have 2 answers"}, 400 if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True comparison_example.answer2 = answer2 else: return {"error": "Comparison examples must have 2 answers"}, 400 on_comparison_example_modified.send( self, event_name=on_comparison_example_modified.name, user=current_user, course_id=course.id, data=get_model_changes(comparison_example)) db.session.add(comparison_example) db.session.commit() return marshal(comparison_example, dataformat.get_comparison_example())
def get(self, course_uuid, assignment_uuid): # course unused, but we need to call it to check if it's a valid course course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) # permission checks require( MANAGE, assignment, title="Unable to download attachments", message= "Sorry, your system role does not allow downloading all attachments" ) # grab answers so we can see how many has files answers = self.getStudentAnswersByAssignment(assignment) fileIds = [] fileAuthors = {} for answer in answers: if not answer.file_id: continue # answer has an attachment fileIds.append(answer.file_id) # the user who uploaded the file can be different from the answer # author (e.g. instructor can upload on behalf of student), so # we need to use the answer author instead of file uploader author = answer.user_fullname if answer.user_student_number: author += self.DELIM + answer.user_student_number fileAuthors[answer.file_id] = author if not fileIds: return {'msg': 'Assignment has no attachments'} # grab files so we can get the real file location files = self.getFilesByIds(fileIds) # zip up the tmp dir and save it to the report dir zipName = '{} [attachments-{}].zip'.format(assignment.name, assignment.uuid) zipPath = '{}/{}'.format(current_app.config['REPORT_FOLDER'], zipName) # we're using compression level 6 as a compromise between speed & # compression (this is Z_DEFAULT_COMPRESSION in zlib) with zipfile.ZipFile(zipPath, 'w', zipfile.ZIP_DEFLATED, True, 6) as zipFile: for srcFile in files: srcFilePath = '{}/{}'.format( current_app.config['ATTACHMENT_UPLOAD_FOLDER'], srcFile.name) # filename should be 'full name - student number - uuid.ext' # student number is omitted if user doesn't have one srcFileName = fileAuthors[ srcFile.id] + self.DELIM + srcFile.name #current_app.logger.debug("writing " + srcFileName) zipFile.write(srcFilePath, srcFileName) #current_app.logger.debug("Writing zip file") return {'file': 'report/' + zipName, 'filename': zipName}
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, assignment_uuid, answer_uuid): """ Create comment for an answer """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(CREATE, AnswerComment(course_id=course.id)) answer_comment = AnswerComment(answer_id=answer.id) params = new_answer_comment_parser.parse_args() answer_comment.draft = params.get("draft") answer_comment.content = params.get("content") # require content not empty if not a draft if not answer_comment.content and not answer_comment.draft: return {"error": "The comment content is empty!"}, 400 if params.get("user_id") and current_user.system_role == SystemRole.sys_admin: user = User.get_by_uuid_or_404(params.get("user_id")) answer_comment.user_id = user.id else: answer_comment.user_id = current_user.id comment_types = [ AnswerCommentType.public.value, AnswerCommentType.private.value, AnswerCommentType.evaluation.value, AnswerCommentType.self_evaluation.value, ] comment_type = params.get("comment_type") if comment_type not in comment_types: abort(400) answer_comment.comment_type = AnswerCommentType(comment_type) db.session.add(answer_comment) db.session.commit() # update course & assignment grade for user if self-evaluation is completed if not answer_comment.draft and answer_comment.comment_type == AnswerCommentType.self_evaluation: assignment.calculate_grade(answer_comment.user) course.calculate_grade(answer_comment.user) on_answer_comment_create.send( self, event_name=on_answer_comment_create.name, user=current_user, course_id=course.id, answer_comment=answer_comment, data=marshal(answer_comment, dataformat.get_answer_comment(False)), ) return marshal(answer_comment, dataformat.get_answer_comment())
def get(self, course_uuid, assignment_uuid): # course unused, but we need to call it to check if it's a valid course course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) # permission checks require( MANAGE, assignment, title="Unable to download attachments", message= "Sorry, your system role does not allow downloading all attachments" ) # grab answers so we can see how many has files answers = self.getAnswersByAssignment(assignment) fileIds = [] for answer in answers: if not answer.file_id: continue # answer has an attachment fileIds.append(answer.file_id) if not fileIds: return {'msg': 'Assignment has no attachments'} # grab files so we can get the real file location files = self.getFilesByIds(fileIds) # zip up the tmp dir and save it to the report dir zipName = '{} [attachments-{}].zip'.format(assignment.name, assignment.uuid) zipPath = '{}/{}'.format(current_app.config['REPORT_FOLDER'], zipName) # we're using compression level 6 as a compromise between speed & # compression (this is Z_DEFAULT_COMPRESSION in zlib) with zipfile.ZipFile(zipPath, 'w', zipfile.ZIP_DEFLATED, True, 6) as zipFile: for srcFile in files: srcFilePath = '{}/{}'.format( current_app.config['ATTACHMENT_UPLOAD_FOLDER'], srcFile.name) # set filename to 'full name - student number - uuid.ext' # omit student number or extension if not exist delim = ' - ' srcFileName = srcFile.user.fullname if srcFile.user.student_number: srcFileName += delim + srcFile.user.student_number srcFileName += delim + srcFile.name #current_app.logger.debug("writing " + srcFileName) zipFile.write(srcFilePath, srcFileName) #current_app.logger.debug("Writing zip file") return {'file': 'report/' + zipName, 'filename': zipName}
def post(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404( comparison_example_uuid) require( EDIT, comparison_example, title="Comparison Example Not Saved", message= "Sorry, your role in this course does not allow you to save practice answers." ) params = existing_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True comparison_example.answer1 = answer1 else: abort( 400, title="Comparison Example Not Saved", message= "Please add two answers with content to the practice answers and try again." ) if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True comparison_example.answer2 = answer2 else: abort( 400, title="Comparison Example Not Saved", message= "Please add two answers with content to the practice answers and try again." ) model_changes = get_model_changes(comparison_example) db.session.add(comparison_example) db.session.commit() on_comparison_example_modified.send( self, event_name=on_comparison_example_modified.name, user=current_user, course_id=course.id, data=model_changes) return marshal(comparison_example, dataformat.get_comparison_example())
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 get(self, course_uuid, assignment_uuid, assignment_comment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) assignment_comment = AssignmentComment.get_active_by_uuid_or_404(assignment_comment_uuid) require(READ, assignment_comment) on_assignment_comment_get.send( self, event_name=on_assignment_comment_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id, 'assignment_comment_id': assignment_comment.id}) return marshal(assignment_comment, dataformat.get_assignment_comment())
def get(self): if allow(MANAGE, Course()): courses = Course.query.filter_by(active=True).all() else: courses = [] for user_course in current_user.user_courses: if user_course.course.active and allow( MANAGE, Assignment(course_id=user_course.course_id)): courses.append(user_course.course) course_list = [{'id': c.uuid, 'name': c.name} for c in courses] on_teaching_course_get.send(self, event_name=on_teaching_course_get.name, user=current_user) return {'courses': course_list}
def post(self, course_uuid, assignment_uuid, assignment_comment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) assignment_comment = AssignmentComment.get_active_by_uuid_or_404( assignment_comment_uuid) require( EDIT, assignment_comment, title="Help Comment Not Saved", message= "Sorry, your role in this course does not allow you to save help comments." ) params = existing_assignment_comment_parser.parse_args() # make sure the comment id in the rul and the id matches if params['id'] != assignment_comment_uuid: abort( 400, title="Help Comment Not Saved", message= "The comment's ID does not match the URL, which is required in order to save the comment." ) # modify comment according to new values, preserve original values if values not passed if not params.get("content"): abort( 400, title="Help Comment Not Saved", message= "Please provide content in the text editor and try saving again." ) assignment_comment.content = params.get("content") db.session.add(assignment_comment) on_assignment_comment_modified.send( self, event_name=on_assignment_comment_modified.name, user=current_user, course_id=course.id, assignment_comment=assignment_comment, data=get_model_changes(assignment_comment)) db.session.commit() return marshal(assignment_comment, dataformat.get_assignment_comment())
def get(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) require(READ, assignment) now = datetime.datetime.utcnow() if assignment.answer_start and not allow(MANAGE, assignment) and not (assignment.answer_start <= now): return {"error": "The assignment is unavailable!"}, 403 restrict_user = not allow(MANAGE, assignment) on_assignment_get.send( self, event_name=on_assignment_get.name, user=current_user, course_id=course.id, data={'id': assignment.id}) return marshal(assignment, dataformat.get_assignment(restrict_user))
def get(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Get an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404(answer_comment_uuid) require(READ, answer_comment) on_answer_comment_get.send( self, event_name=on_answer_comment_get.name, user=current_user, course_id=course.id, data={"assignment_id": assignment.id, "answer_id": answer.id, "answer_comment_id": answer_comment.id}, ) return marshal(answer_comment, dataformat.get_answer_comment())
def get(self, course_uuid, assignment_uuid, answer_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404( answer_uuid, joinedloads=['file', 'user', 'scores'] ) require(READ, answer) restrict_user = not allow(MANAGE, assignment) on_answer_get.send( self, event_name=on_answer_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id, 'answer_id': answer.id}) return marshal(answer, dataformat.get_answer(restrict_user))
def delete(self, course_uuid, assignment_uuid, assignment_comment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) assignment_comment = AssignmentComment.get_active_by_uuid_or_404(assignment_comment_uuid) require(DELETE, assignment_comment) data = marshal(assignment_comment, dataformat.get_assignment_comment(False)) assignment_comment.active = False db.session.commit() on_assignment_comment_delete.send( self, event_name=on_assignment_comment_delete.name, user=current_user, course_id=course.id, assignment_comment=assignment_comment, data=data) return {'id': assignment_comment.uuid}
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)) 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('scores')) \ .filter_by( active=True, assignment_id=assignment.id, course_id=course.id, user_id=current_user.id, draft=params.get('draft') ) if params.get('unsaved'): query = query.filter(Answer.modified == Answer.created) 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 get(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) require(READ, assignment, title="Assignment Unavailable", message="Assignments can be saved only by those enrolled in the course. Please double-check your enrollment in this course.") now = datetime.datetime.utcnow() if assignment.answer_start and not allow(MANAGE, assignment) and not (assignment.answer_start <= now): abort(403, title="Assignment Unavailable", message="This assignment is not yet open to students. Please check back after the start date the instructor has set.") restrict_user = not allow(MANAGE, assignment) on_assignment_get.send( self, event_name=on_assignment_get.name, user=current_user, course_id=course.id, data={'id': assignment.id}) return marshal(assignment, dataformat.get_assignment(restrict_user))
def get(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) require(READ, ComparisonExample(course_id=course.id)) # Get all comparison examples for this assignment comparison_examples = ComparisonExample.query \ .filter_by( active=True, assignment_id=assignment.id ) \ .all() on_comparison_example_list_get.send( self, event_name=on_comparison_example_list_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return { "objects": marshal(comparison_examples, dataformat.get_comparison_example()) }
def delete(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404(comparison_example_uuid) require(DELETE, comparison_example) formatted_comparison_example = marshal(comparison_example, dataformat.get_comparison_example(with_answers=False)) comparison_example.active = False db.session.add(comparison_example) db.session.commit() on_comparison_example_delete.send( self, event_name=on_comparison_example_delete.name, user=current_user, course_id=course.id, data=formatted_comparison_example) return {'id': comparison_example.uuid}
def get(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) require(READ, course) criteria = Criterion.query \ .join(AssignmentCriterion) \ .filter(and_( AssignmentCriterion.assignment_id == assignment.id, AssignmentCriterion.active == True, Criterion.active == True) ) \ .all() on_assignment_criterion_get.send( self, event_name=on_assignment_criterion_get.name, user=current_user ) return {'objects': marshal(criteria, dataformat.get_criterion()) }
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) require(CREATE, ComparisonExample(assignment=Assignment(course_id=course.id)), title="Comparison Example Not Saved", message="Sorry, your role in this course does not allow you to save practice answers.") new_comparison_example = ComparisonExample(assignment_id=assignment.id) params = new_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True new_comparison_example.answer1 = answer1 else: abort(400, title="Comparison Example Not Saved", message="Please add two answers with content to the practice answers and try again.") if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True new_comparison_example.answer2 = answer2 else: abort(400, title="Comparison Example Not Saved", message="Please add two answers with content to the practice answers and try again.") on_comparison_example_create.send( self, event_name=on_comparison_example_create.name, user=current_user, course_id=course.id, data=marshal(new_comparison_example, dataformat.get_comparison_example(with_answers=False))) db.session.add(new_comparison_example) db.session.commit() return marshal(new_comparison_example, dataformat.get_comparison_example())
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) require( CREATE, AssignmentComment(course_id=course.id), title="Help Comment Not Saved", message= "Help comments can be saved only by those enrolled in the course. Please double-check your enrollment in this course." ) new_assignment_comment = AssignmentComment(assignment_id=assignment.id) params = new_assignment_comment_parser.parse_args() new_assignment_comment.content = params.get("content") if not new_assignment_comment.content: abort( 400, title="Help Comment Not Saved", message= "Please provide content in the text editor and try saving again." ) new_assignment_comment.user_id = current_user.id db.session.add(new_assignment_comment) db.session.commit() on_assignment_comment_create.send( self, event_name=on_assignment_comment_create.name, user=current_user, course_id=course.id, assignment_comment=new_assignment_comment, data=marshal(new_assignment_comment, dataformat.get_assignment_comment(False))) return marshal(new_assignment_comment, dataformat.get_assignment_comment())
def post(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404(comparison_example_uuid) require(EDIT, comparison_example, title="Comparison Example Not Saved", message="Sorry, your role in this course does not allow you to save practice answers.") params = existing_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True comparison_example.answer1 = answer1 else: abort(400, title="Comparison Example Not Saved", message="Please add two answers with content to the practice answers and try again.") if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True comparison_example.answer2 = answer2 else: abort(400, title="Comparison Example Not Saved", message="Please add two answers with content to the practice answers and try again.") model_changes = get_model_changes(comparison_example) db.session.add(comparison_example) db.session.commit() on_comparison_example_modified.send( self, event_name=on_comparison_example_modified.name, user=current_user, course_id=course.id, data=model_changes) return marshal(comparison_example, dataformat.get_comparison_example())
def get(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require(READ, course, title="Assignments Unavailable", message="Assignments can be seen only by those enrolled in the course. Please double-check your enrollment in this course.") assignment = Assignment(course_id=course.id) restrict_user = not allow(MANAGE, assignment) # Get all assignments for this course, order by answer_start date, created date base_query = Assignment.query \ .options(joinedload("assignment_criteria").joinedload("criterion")) \ .options(undefer_group('counts')) \ .filter( Assignment.course_id == course.id, Assignment.active == True ) \ .order_by(desc(Assignment.answer_start), desc(Assignment.created)) if restrict_user: now = datetime.datetime.utcnow() assignments = base_query \ .filter(or_( Assignment.answer_start.is_(None), now >= Assignment.answer_start ))\ .all() else: assignments = base_query.all() on_assignment_list_get.send( self, event_name=on_assignment_list_get.name, user=current_user, course_id=course.id) return { "objects": marshal(assignments, dataformat.get_assignment(restrict_user)) }
def get(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) require(READ, assignment) restrict_user = not allow(MANAGE, assignment) assignment_comments = AssignmentComment.query \ .filter_by( course_id=course.id, assignment_id=assignment.id, active=True ) \ .order_by(AssignmentComment.created.asc()).all() on_assignment_comment_list_get.send( self, event_name=on_assignment_comment_list_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return {"objects": marshal(assignment_comments, dataformat.get_assignment_comment(restrict_user))}
def post(self, course_uuid, assignment_uuid, answer_uuid): """ Mark an answer as inappropriate or incomplete to instructors :param course_uuid: :param assignment_uuid: :param answer_uuid: :return: marked answer """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(READ, answer, title="Answer Not Flagged", message="Sorry, your role in this course does not allow you to flag answers.") restrict_user = not allow(MANAGE, answer) # anyone can flag an answer, but only the original flagger or someone who can manage # the answer can unflag it if answer.flagged and answer.flagger_user_id != current_user.id and \ not allow(MANAGE, answer): abort(400, title="Answer Not Updated", message="Sorry, your role in this course does not allow you to unflag answers.") params = flag_parser.parse_args() answer.flagged = params['flagged'] answer.flagger_user_id = current_user.id db.session.add(answer) db.session.commit() on_answer_flag.send( self, event_name=on_answer_flag.name, user=current_user, course_id=course.id, assignment_id=assignment.id, answer=answer, data={'answer_id': answer.id, 'flag': answer.flagged}) return marshal(answer, dataformat.get_answer(restrict_user))
def delete(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404(comparison_example_uuid) require(DELETE, comparison_example, title="Comparison Example Not Deleted", message="Sorry, your role in this course does not allow you to delete practice answers.") formatted_comparison_example = marshal(comparison_example, dataformat.get_comparison_example(with_answers=False)) comparison_example.active = False db.session.add(comparison_example) db.session.commit() on_comparison_example_delete.send( self, event_name=on_comparison_example_delete.name, user=current_user, course_id=course.id, data=formatted_comparison_example) return {'id': comparison_example.uuid}
def get(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) require(READ, ComparisonExample(course_id=course.id), title="Comparison Example Unavailable", message="Sorry, your role in this course does not allow you to view practice answers.") # Get all comparison examples for this assignment comparison_examples = ComparisonExample.query \ .filter_by( active=True, assignment_id=assignment.id ) \ .all() on_comparison_example_list_get.send( self, event_name=on_comparison_example_list_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return { "objects": marshal(comparison_examples, dataformat.get_comparison_example()) }
def post(self, course_uuid, assignment_uuid, answer_uuid): """ Mark an answer as inappropriate or incomplete to instructors :param course_uuid: :param assignment_uuid: :param answer_uuid: :return: marked answer """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(READ, answer) restrict_user = not allow(MANAGE, answer) # anyone can flag an answer, but only the original flagger or someone who can manage # the answer can unflag it if answer.flagged and answer.flagger_user_id != current_user.id and \ not allow(MANAGE, answer): return {"error": "You do not have permission to unflag this answer."}, 400 params = flag_parser.parse_args() answer.flagged = params['flagged'] answer.flagger_user_id = current_user.id db.session.add(answer) db.session.commit() on_answer_flag.send( self, event_name=on_answer_flag.name, user=current_user, course_id=course.id, assignment_id=assignment.id, answer=answer, data={'answer_id': answer.id, 'flag': answer.flagged}) return marshal(answer, dataformat.get_answer(restrict_user))
def get(self, course_uuid, assignment_uuid, assignment_comment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) assignment_comment = AssignmentComment.get_active_by_uuid_or_404( assignment_comment_uuid) require( READ, assignment_comment, title="Help Comment Unavailable", message= "Sorry, your role in this course does not allow you to view help comments." ) on_assignment_comment_get.send( self, event_name=on_assignment_comment_get.name, user=current_user, course_id=course.id, data={ 'assignment_id': assignment.id, 'assignment_comment_id': assignment_comment.id }) return marshal(assignment_comment, dataformat.get_assignment_comment())
def get(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Get an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404(answer_comment_uuid) require(READ, answer_comment, title="Feedback Unavailable", message="Sorry, your role in this course does not allow you to view this feedback.") restrict_user = not allow(MANAGE, assignment) restrict_user = not allow(MANAGE, assignment) on_answer_comment_get.send( self, event_name=on_answer_comment_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id, 'answer_id': answer.id, 'answer_comment_id': answer_comment.id}) return marshal(answer_comment, dataformat.get_answer_comment(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) require(CREATE, ComparisonExample(assignment=Assignment(course_id=course.id))) new_comparison_example = ComparisonExample(assignment_id=assignment.id) params = new_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True new_comparison_example.answer1 = answer1 else: return {"error": "Comparison examples must have 2 answers"}, 400 if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True new_comparison_example.answer2 = answer2 else: return {"error": "Comparison examples must have 2 answers"}, 400 on_comparison_example_create.send( self, event_name=on_comparison_example_create.name, user=current_user, course_id=course.id, data=marshal(new_comparison_example, dataformat.get_comparison_example(with_answers=False))) db.session.add(new_comparison_example) db.session.commit() return marshal(new_comparison_example, dataformat.get_comparison_example())
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 get(self, course_uuid, assignment_uuid): """ Return a list of answers for a assignment based on search criteria. The list of the answers are paginated. If there is any answers from instructor or TA, their answers will be on top of the list (unless they are comparable). :param course_uuid: course uuid :param assignment_uuid: assignment uuid :return: list of answers """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require( READ, assignment, title="Answers Unavailable", message= "Answers are visible only to those enrolled in the course. Please double-check your enrollment in this course." ) restrict_user = not allow(MANAGE, assignment) params = answer_list_parser.parse_args() # if assingment has no rank display limit set, restricted users can't force to retreive by rank if params[ 'orderBy'] == 'score' and restrict_user and not assignment.rank_display_limit: abort( 400, title="Answers Unavailable", message= "Sorry, you cannot cannot see answers by rank for this assignment." ) if restrict_user and not assignment.after_comparing: # only the answer from student himself/herself should be returned params['author'] = current_user.uuid # this query could be further optimized by reduction the selected columns query = Answer.query \ .options(joinedload('file')) \ .options(joinedload('user')) \ .options(joinedload('group')) \ .options(joinedload('score')) \ .options(undefer_group('counts')) \ .outerjoin(UserCourse, and_( Answer.user_id == UserCourse.user_id, UserCourse.course_id == course.id )) \ .add_columns( and_( UserCourse != None, UserCourse.course_role.__eq__(CourseRole.instructor), not Answer.comparable ).label("instructor_role"), and_( UserCourse != None, UserCourse.course_role.__eq__(CourseRole.teaching_assistant), not Answer.comparable ).label("ta_role") ) \ .filter(and_( Answer.assignment_id == assignment.id, Answer.active == True, Answer.practice == False, Answer.draft == False, or_( and_(UserCourse.course_role != CourseRole.dropped, Answer.user_id != None), Answer.group_id != None ) )) \ .order_by(desc('instructor_role'), desc('ta_role')) if params['author']: user = User.get_by_uuid_or_404(params['author']) group = user.get_course_group(course.id) if group: query = query.filter( or_(Answer.user_id == user.id, Answer.group_id == group.id)) else: query = query.filter(Answer.user_id == user.id) elif params['group']: group = Group.get_active_by_uuid_or_404(params['group']) query = query.filter( or_(UserCourse.group_id == group.id, Answer.group_id == group.id)) if params['ids']: query = query.filter(Answer.uuid.in_(params['ids'].split(','))) if params['top']: query = query.filter(Answer.top_answer == True) if params['orderBy'] == 'score': # use outer join to include comparable answers that are not yet compared (for non-restricted users) query = query.outerjoin(AnswerScore) \ .filter(Answer.comparable == True) \ .order_by(AnswerScore.score.desc(), Answer.submission_date.desc(), Answer.created.desc()) if restrict_user: # when orderd by rank, students won't see answers that are not compared (i.e. no score/rank) query = query.filter(AnswerScore.score.isnot(None)) # limit answers up to rank if rank_display_limit is set and current_user is restricted (student) if assignment.rank_display_limit and restrict_user: score_for_rank = AnswerScore.get_score_for_rank( assignment.id, assignment.rank_display_limit) # display answers with score >= score_for_rank if score_for_rank != None: # will get all answers with a score greater than or equal to the score for a given rank # the '- 0.00001' fixes floating point precision problems query = query.filter( AnswerScore.score >= score_for_rank - 0.00001) else: # when ordered by date, non-comparable answers should be on top of the list query = query.order_by(Answer.comparable, Answer.submission_date.desc(), Answer.created.desc()) page = query.paginate(params['page'], params['perPage'], error_out=False) # remove label entities from results page.items = [ answer for (answer, instructor_role, ta_role) in page.items ] on_answer_list_get.send(self, event_name=on_answer_list_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) # only include score/rank info if: # - requesters are non-restricted users (i.e. instructors / TAs); or, # - retrieving answers ordered by score/rank include_score = (not restrict_user) or \ (params['orderBy'] == 'score' and assignment.rank_display_limit) return { "objects": marshal( page.items, dataformat.get_answer(restrict_user, include_score=include_score)), "page": page.page, "pages": page.pages, "total": page.total, "per_page": page.per_page }
def post(self, course_uuid, assignment_uuid, answer_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." ) answer = Answer.get_active_by_uuid_or_404(answer_uuid) old_file = answer.file require( EDIT, answer, title="Answer Not Saved", message= "Sorry, your role in this course does not allow you to save this answer." ) restrict_user = not allow(MANAGE, assignment) if current_app.config.get('DEMO_INSTALLATION', False): from data.fixtures import DemoDataFixture if assignment.id in DemoDataFixture.DEFAULT_ASSIGNMENT_IDS and answer.user_id in DemoDataFixture.DEFAULT_STUDENT_IDS: abort( 400, title="Answer Not Saved", message= "Sorry, you cannot edit the default student demo answers.") params = existing_answer_parser.parse_args() # make sure the answer id in the url and the id matches if params['id'] != answer_uuid: abort( 400, title="Answer Not Submitted", message= "The answer's ID does not match the URL, which is required in order to save the answer." ) # modify answer according to new values, preserve original values if values not passed answer.content = params.get("content") 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 user_uuid != answer.user_uuid and not allow( MANAGE, answer): 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 group_uuid != answer.group_uuid and not allow( MANAGE, answer): 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 answer.user group = Group.get_active_by_uuid_or_404( group_uuid) if group_uuid else answer.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 ) \ .filter(Answer.id != answer.id) \ .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 # can only change draft status while a draft if answer.draft: 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." ) # 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)) model_changes = get_model_changes(answer) db.session.add(answer) db.session.commit() on_answer_modified.send(self, event_name=on_answer_modified.name, user=current_user, course_id=course.id, answer=answer, data=model_changes) if old_file and (not attachment or old_file.id != attachment.id): on_detach_file.send(self, event_name=on_detach_file.name, user=current_user, course_id=course.id, file=old_file, answer=answer, data={ 'answer_id': answer.id, 'file_id': old_file.id }) if attachment and (not old_file or old_file.id != attachment.id): 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) 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, 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 get(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) require(READ, Comparison(course_id=course.id), title="Comparisons Unavailable", message="Sorry, your role in this course does not allow you to view all comparisons for this assignment.") restrict_user = is_user_access_restricted(current_user) params = assignment_users_comparison_list_parser.parse_args() # only get users who have at least made one comparison # each paginated item is a user (with a set of comparisons and self-evaluations) user_query = User.query \ .join(UserCourse, and_( User.id == UserCourse.user_id, UserCourse.course_id == course.id )) \ .join(Comparison, and_( Comparison.user_id == User.id, Comparison.assignment_id == assignment.id )) \ .filter(and_( UserCourse.course_role != CourseRole.dropped, Comparison.completed == True )) \ .group_by(User) \ .order_by(User.lastname, User.firstname) self_evaluation_total = AnswerComment.query \ .join("answer") \ .with_entities( func.count(Answer.assignment_id).label('self_evaluation_count') ) \ .filter(and_( AnswerComment.active == True, AnswerComment.comment_type == AnswerCommentType.self_evaluation, AnswerComment.draft == False, Answer.active == True, Answer.practice == False, Answer.draft == False, Answer.assignment_id == assignment.id )) comparison_total = Comparison.query \ .with_entities( func.count(Comparison.assignment_id).label('comparison_count') ) \ .filter(and_( Comparison.completed == True, Comparison.assignment_id == assignment.id )) if params['author']: user = User.get_by_uuid_or_404(params['author']) user_query = user_query.filter(User.id == user.id) self_evaluation_total = self_evaluation_total.filter(AnswerComment.user_id == user.id) comparison_total = comparison_total.filter(Comparison.user_id == user.id) elif params['group']: user_query = user_query.filter(UserCourse.group_name == params['group']) self_evaluation_total = self_evaluation_total \ .join(UserCourse, and_( AnswerComment.user_id == UserCourse.user_id, UserCourse.course_id == course.id )) \ .filter(UserCourse.group_name == params['group']) comparison_total = comparison_total \ .join(UserCourse, and_( Comparison.user_id == UserCourse.user_id, UserCourse.course_id == course.id )) \ .filter(UserCourse.group_name == params['group']) page = user_query.paginate(params['page'], params['perPage']) self_evaluation_total = self_evaluation_total.scalar() comparison_total = comparison_total.scalar() comparison_sets = [] if page.total: user_ids = [user.id for user in page.items] # get all comparisons that group of users created comparisons = Comparison.query \ .filter(and_( Comparison.completed == True, Comparison.assignment_id == assignment.id, Comparison.user_id.in_(user_ids) )) \ .all() # retrieve the answer comments user_comparison_answers = {} for comparison in comparisons: user_answers = user_comparison_answers.setdefault(comparison.user_id, set()) user_answers.add(comparison.answer1_id) user_answers.add(comparison.answer2_id) conditions = [] for user_id, answer_set in user_comparison_answers.items(): conditions.append(and_( AnswerComment.comment_type == AnswerCommentType.evaluation, AnswerComment.user_id == user_id, AnswerComment.answer_id.in_(list(answer_set)), AnswerComment.assignment_id == assignment.id )) conditions.append(and_( AnswerComment.comment_type == AnswerCommentType.self_evaluation, AnswerComment.user_id == user_id, AnswerComment.assignment_id == assignment.id )) answer_comments = AnswerComment.query \ .filter(or_(*conditions)) \ .filter_by(draft=False) \ .all() # add comparison answer evaluation comments to comparison object for comparison in comparisons: comparison.answer1_feedback = [comment for comment in answer_comments if comment.user_id == comparison.user_id and comment.answer_id == comparison.answer1_id and comment.comment_type == AnswerCommentType.evaluation ] comparison.answer2_feedback = [comment for comment in answer_comments if comment.user_id == comparison.user_id and comment.answer_id == comparison.answer2_id and comment.comment_type == AnswerCommentType.evaluation ] for user in page.items: comparison_sets.append({ 'user': user, 'comparisons': [comparison for comparison in comparisons if comparison.user_id == user.id ], 'self_evaluations': [comment for comment in answer_comments if comment.user_id == user.id and comment.comment_type == AnswerCommentType.self_evaluation ] }) on_assignment_users_comparisons_get.send( self, event_name=on_assignment_users_comparisons_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id} ) return {"objects": marshal(comparison_sets, dataformat.get_comparison_set(restrict_user, with_user=True)), "comparison_total": comparison_total, "self_evaluation_total": self_evaluation_total, "page": page.page, "pages": page.pages, "total": page.total, "per_page": page.per_page}
def get(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) require(READ, assignment, title="Comparisons Unavailable", message="Sorry, your role in this course does not allow you to view comparisons for this assignment.") restrict_user = is_user_access_restricted(current_user) # get comparisons for current user comparisons = Comparison.query \ .filter(and_( Comparison.completed == True, Comparison.assignment_id == assignment.id, Comparison.user_id == current_user.id )) \ .all() # get all self-evaluations and evaluation comments for current user answer_comments = AnswerComment.query \ .join("answer") \ .filter(and_( AnswerComment.active == True, AnswerComment.comment_type.in_([AnswerCommentType.self_evaluation, AnswerCommentType.evaluation]), AnswerComment.draft == False, Answer.active == True, Answer.draft == False, Answer.assignment_id == assignment.id, AnswerComment.user_id == current_user.id )) \ .all() # add comparison answer evaluation comments to comparison object for comparison in comparisons: comparison.answer1_feedback = [comment for comment in answer_comments if comment.user_id == comparison.user_id and comment.answer_id == comparison.answer1_id and comment.comment_type == AnswerCommentType.evaluation ] comparison.answer2_feedback = [comment for comment in answer_comments if comment.user_id == comparison.user_id and comment.answer_id == comparison.answer2_id and comment.comment_type == AnswerCommentType.evaluation ] on_assignment_user_comparisons_get.send( self, event_name=on_assignment_user_comparisons_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id} ) comparison_set = { 'comparisons': [comparison for comparison in comparisons if comparison.user_id == current_user.id ], 'self_evaluations': [comment for comment in answer_comments if comment.user_id == current_user.id and comment.comment_type == AnswerCommentType.self_evaluation ] } return marshal(comparison_set, dataformat.get_comparison_set(restrict_user, with_user=False))
def get(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) require(READ, assignment, 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.") answer_count = Answer.query \ .filter_by( user_id=current_user.id, assignment_id=assignment.id, active=True, practice=False, draft=False ) \ .count() feedback_count = AnswerComment.query \ .join("answer") \ .filter(and_( AnswerComment.active == True, AnswerComment.draft == False, Answer.user_id == current_user.id, Answer.assignment_id == assignment.id, Answer.active == True, Answer.practice == False, Answer.draft == False )) \ .count() drafts = Answer.query \ .options(load_only('id')) \ .filter_by( user_id=current_user.id, assignment_id=assignment.id, active=True, practice=False, draft=True, saved=True ) \ .all() comparison_count = assignment.completed_comparison_count_for_user(current_user.id) other_student_answers = assignment.student_answer_count - answer_count comparison_available = comparison_count < other_student_answers * (other_student_answers - 1) / 2 status = { 'answers': { 'answered': answer_count > 0, 'feedback': feedback_count, 'count': answer_count, 'has_draft': len(drafts) > 0, 'draft_ids': [draft.uuid for draft in drafts] }, 'comparisons': { 'available': comparison_available, 'count': comparison_count, 'left': max(0, assignment.total_comparisons_required - comparison_count) } } if assignment.enable_self_evaluation: self_evaluations = AnswerComment.query \ .join("answer") \ .filter(and_( AnswerComment.user_id == current_user.id, AnswerComment.active == True, AnswerComment.comment_type == AnswerCommentType.self_evaluation, AnswerComment.draft == False, Answer.assignment_id == assignment.id, Answer.active == True, Answer.practice == False, Answer.draft == False )) \ .count() status['comparisons']['self_evaluation_completed'] = self_evaluations > 0 on_assignment_get_status.send( self, event_name=on_assignment_get_status.name, user=current_user, course_id=course.id, data=status) return {"status": status}