def _submit_all_comparisons_for_assignment(self, assignment, user_id): submit_count = 0 for comparison_example in assignment.comparison_examples: comparisons = Comparison.create_new_comparison_set(assignment.id, user_id, False) self.assertEqual(comparisons[0].answer1_id, comparison_example.answer1_id) self.assertEqual(comparisons[0].answer2_id, comparison_example.answer2_id) for comparison in comparisons: comparison.completed = True comparison.winner_id = min([comparisons[0].answer1_id, comparisons[0].answer2_id]) db.session.add(comparison) submit_count += 1 db.session.commit() for i in range(assignment.number_of_comparisons): comparisons = Comparison.create_new_comparison_set(assignment.id, user_id, False) for comparison in comparisons: comparison.completed = True comparison.winner_id = min([comparisons[0].answer1_id, comparisons[0].answer2_id]) db.session.add(comparison) submit_count += 1 db.session.commit() Comparison.calculate_scores(assignment.id) return submit_count
def _submit_all_possible_comparisons_for_user(self, user_id): example_winner_ids = [] example_loser_ids = [] for comparison_example in self.data.comparisons_examples: if comparison_example.assignment_id == self.assignment.id: comparisons = Comparison.create_new_comparison_set(self.assignment.id, user_id, False) self.assertEqual(comparisons[0].answer1_id, comparison_example.answer1_id) self.assertEqual(comparisons[0].answer2_id, comparison_example.answer2_id) min_id = min([comparisons[0].answer1_id, comparisons[0].answer2_id]) max_id = max([comparisons[0].answer1_id, comparisons[0].answer2_id]) example_winner_ids.append(min_id) example_loser_ids.append(max_id) for comparison in comparisons: comparison.completed = True comparison.winner_id = min_id db.session.add(comparison) db.session.commit() # self.login(username) # calculate number of comparisons to do before user has compared all the pairs it can num_eligible_answers = 0 # need to minus one to exclude the logged in user's own answer for answer in self.data.get_student_answers(): if answer.assignment_id == self.assignment.id and answer.user_id != user_id: num_eligible_answers += 1 # n - 1 possible pairs before all answers have been compared num_possible_comparisons = num_eligible_answers - 1 winner_ids = [] loser_ids = [] for i in range(num_possible_comparisons): comparisons = Comparison.create_new_comparison_set(self.assignment.id, user_id, False) answer1_id = comparisons[0].answer1_id answer2_id = comparisons[0].answer2_id min_id = min([answer1_id, answer2_id]) max_id = max([answer1_id, answer2_id]) winner_ids.append(min_id) loser_ids.append(max_id) for comparison in comparisons: comparison.completed = True comparison.winner_id = min_id db.session.add(comparison) db.session.commit() Comparison.calculate_scores(self.assignment.id) return { 'comparisons': { 'winners': winner_ids, 'losers': loser_ids }, 'comparison_examples': { 'winners': example_winner_ids, 'losers': example_loser_ids } }
def recalculate(assignment_id): if not assignment_id: raise RuntimeError("Assignment with ID {} is not found.".format(assignment_id)) assignment = Assignment.query.filter_by(id=assignment_id).first() if not assignment: raise RuntimeError("Assignment with ID {} is not found.".format(assignment_id)) if prompt_bool("""All current answer scores and answer criterion scores will be overwritten. Final scores may differ slightly due to floating point rounding (especially if recalculating on different systems). Are you sure?"""): print ('Recalculating scores...') Comparison.calculate_scores(assignment.id) print ('Recalculate scores successful.')
def add_comparisons(self): for assignment in self.assignments: for student in self.students: for i in range(assignment.total_comparisons_required): comparisons = Comparison.create_new_comparison_set(assignment.id, student.id, False) for comparison in comparisons: comparison.completed = True comparison.winner_id = min([comparisons[0].answer1_id, comparisons[0].answer2_id]) db.session.add(comparison) db.session.commit() return self
def add_comparisons_for_user(self, assignment, student, with_comments=False, with_self_eval=False): answers = set() for i in range(assignment.total_comparisons_required): comparison = Comparison.create_new_comparison( assignment.id, student.id, False) comparison.completed = True comparison.winner = WinningAnswer.answer1 if comparison.answer1_id < comparison.answer2_id else WinningAnswer.answer2 for comparison_criterion in comparison.comparison_criteria: comparison_criterion.winner = comparison.winner db.session.add(comparison) db.session.commit() self.comparisons.append(comparison) if with_comments: for answer in answers: answer_comment = AnswerCommentFactory( user=student, answer=answer, comment_type=AnswerCommentType.evaluation) self.answer_comments.append(answer_comment) db.session.commit() if with_self_eval: student_answer = next(answer for answer in self.answers if answer.user_id == student.id and answer.assignment_id == assignment.id) if student_answer: self_evaluation = AnswerCommentFactory( user=student, answer=student_answer, comment_type=AnswerCommentType.self_evaluation) self.self_evaluations.append(self_evaluation) db.session.commit() return self
def add_comparisons_for_user(self, assignment, student, with_comments=False, with_self_eval=False): answers = set() for i in range(assignment.total_comparisons_required): comparison = Comparison.create_new_comparison(assignment.id, student.id, False) comparison.completed = True comparison.winner = WinningAnswer.answer1 if comparison.answer1_id < comparison.answer2_id else WinningAnswer.answer2 for comparison_criterion in comparison.comparison_criteria: comparison_criterion.winner = comparison.winner db.session.add(comparison) db.session.commit() self.comparisons.append(comparison) if with_comments: for answer in answers: answer_comment = AnswerCommentFactory( user=student, answer=answer, comment_type=AnswerCommentType.evaluation ) self.answer_comments.append(answer_comment) db.session.commit() if with_self_eval: student_answer = next( answer for answer in self.answers if answer.user_id == student.id and answer.assignment_id == assignment.id ) if student_answer: self_evaluation = AnswerCommentFactory( user=student, answer=student_answer, comment_type=AnswerCommentType.self_evaluation ) self.self_evaluations.append(self_evaluation) db.session.commit() return self
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): """ Get (or create if needed) a comparison set for assignment. """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require(READ, assignment) require(CREATE, Comparison) restrict_user = not allow(MANAGE, assignment) if not assignment.compare_grace: return {'error': comparison_deadline_message}, 403 elif not restrict_user and not assignment.educators_can_compare: return {'error': educators_can_not_compare_message}, 403 # check if user has comparisons they have not completed yet comparisons = Comparison.query \ .filter_by( assignment_id=assignment.id, user_id=current_user.id, completed=False ) \ .all() new_pair = False if len(comparisons) > 0: on_comparison_get.send( self, event_name=on_comparison_get.name, user=current_user, course_id=course.id, data=marshal(comparisons, dataformat.get_comparison(restrict_user))) else: # if there aren't incomplete comparisons, assign a new one try: comparisons = Comparison.create_new_comparison_set(assignment.id, current_user.id, skip_comparison_examples=allow(MANAGE, assignment)) new_pair = True on_comparison_create.send( self, event_name=on_comparison_create.name, user=current_user, course_id=course.id, data=marshal(comparisons, dataformat.get_comparison(restrict_user))) except InsufficientObjectsForPairException: return {"error": "Not enough answers are available for a comparison."}, 400 except UserComparedAllObjectsException: return {"error": "You have compared all the currently available answers."}, 400 except UnknownPairGeneratorException: return {"error": "Generating scored pairs failed, this really shouldn't happen."}, 500 comparison_count = assignment.completed_comparison_count_for_user(current_user.id) return { 'objects': marshal(comparisons, dataformat.get_comparison(restrict_user)), 'new_pair': new_pair, 'current': comparison_count+1 }
def post(self, course_uuid, assignment_uuid): """ Stores comparison set into the database. """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require(READ, assignment) require(CREATE, Comparison) restrict_user = not allow(MANAGE, assignment) if not assignment.compare_grace: return {'error': comparison_deadline_message}, 403 elif not restrict_user and not assignment.educators_can_compare: return {'error': educators_can_not_compare_message}, 403 comparisons = Comparison.query \ .filter_by( assignment_id=assignment.id, user_id=current_user.id, completed=False ) \ .all() params = update_comparison_parser.parse_args() completed = True # check if there are any comparisons to update if len(comparisons) == 0: return {"error": "There are no comparisons open for evaluation."}, 400 is_comparison_example = comparisons[0].comparison_example_id != None # check if number of comparisons submitted matches number of comparisons needed if len(comparisons) != len(params['comparisons']): return {"error": "Not all criteria were evaluated."}, 400 # check if each comparison has a criterion Id and a winner id for comparison_to_update in params['comparisons']: # check if saving a draft if 'draft' in comparison_to_update and comparison_to_update['draft']: completed = False # ensure criterion param is present if 'criterion_id' not in comparison_to_update: return {"error": "Missing criterion_id in evaluation."}, 400 # set default values for cotnent and winner comparison_to_update.setdefault('content', None) winner_uuid = comparison_to_update.setdefault('winner_id', None) # if winner isn't set for any comparisons, then the comparison set isn't complete yet if winner_uuid == None: completed = False # check that we're using criteria that were assigned to the course and that we didn't # get duplicate criteria in comparisons known_criterion = False for comparison in comparisons: if comparison_to_update['criterion_id'] == comparison.criterion_uuid: known_criterion = True # check that the winner id matches one of the answer pairs if winner_uuid not in [comparison.answer1_uuid, comparison.answer2_uuid, None]: return {"error": "Selected answer does not match the available answers in comparison."}, 400 break if not known_criterion: return {"error": "Unknown criterion submitted!"}, 400 # update comparisons for comparison in comparisons: comparison.completed = completed for comparison_to_update in params['comparisons']: if comparison_to_update['criterion_id'] != comparison.criterion_uuid: continue if comparison_to_update['winner_id'] == comparison.answer1_uuid: comparison.winner_id = comparison.answer1_id elif comparison_to_update['winner_id'] == comparison.answer2_uuid: comparison.winner_id = comparison.answer2_id else: comparison.winner_id = None comparison.content = comparison_to_update['content'] db.session.commit() # update answer scores if completed and not is_comparison_example: current_app.logger.debug("Doing scoring") Comparison.update_scores_1vs1(comparisons) #Comparison.calculate_scores(assignment.id) # update course & assignment grade for user if comparison is completed if completed: assignment.calculate_grade(current_user) course.calculate_grade(current_user) on_comparison_update.send( self, event_name=on_comparison_update.name, user=current_user, course_id=course.id, assignment=assignment, comparisons=comparisons, is_comparison_example=is_comparison_example, data=marshal(comparisons, dataformat.get_comparison(restrict_user))) return {'objects': marshal(comparisons, dataformat.get_comparison(restrict_user))}
def get(self, course_uuid, assignment_uuid): """ Get (or create if needed) a comparison set for assignment. """ 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="Assignments and their comparisons can only be seen here by those enrolled in the course. Please double-check your enrollment in this course.") require(CREATE, Comparison, title="Comparisons Unavailable", message="Comparisons can only be seen here by those enrolled in the course. Please double-check your enrollment in this course.") restrict_user = not allow(MANAGE, assignment) comparison_count = assignment.completed_comparison_count_for_user(current_user.id) if not assignment.compare_grace: abort(403, title="Comparisons Unavailable", message="Sorry, the comparison deadline has passed. No comparisons can be done after the deadline.") elif not restrict_user and not assignment.educators_can_compare: abort(403, title="Comparisons Unavailable", message="Only students can currently compare answers for this assignment. To change these settings to include instructors and teaching assistants, edit the assignment.") elif restrict_user and comparison_count >= assignment.total_comparisons_required: abort(400, title="Comparisons Completed", message="More comparisons aren't available, since you've finished your comparisons for this assignment. Good job!") # check if user has a comparison they have not completed yet comparison = Comparison.query \ .options(joinedload('comparison_criteria')) \ .filter_by( assignment_id=assignment.id, user_id=current_user.id, completed=False ) \ .first() if comparison: on_comparison_get.send( self, event_name=on_comparison_get.name, user=current_user, course_id=course.id, data=marshal(comparison, dataformat.get_comparison(restrict_user))) else: # if there isn't an incomplete comparison, assign a new one try: comparison = Comparison.create_new_comparison(assignment.id, current_user.id, skip_comparison_examples=allow(MANAGE, assignment)) on_comparison_create.send( self, event_name=on_comparison_create.name, user=current_user, course_id=course.id, data=marshal(comparison, dataformat.get_comparison(restrict_user))) except InsufficientObjectsForPairException: abort(400, title="Comparisons Unavailable", message="Not enough answers are available for you to do comparisons right now. Please check back later for more answers.") except UserComparedAllObjectsException: abort(400, title="Comparisons Unavailable", message="You have compared all the currently available answer pairs. Please check back later for more answers.") except UnknownPairGeneratorException: abort(500, title="Comparisons Unavailable", message="Generating scored pairs failed, this really shouldn't happen.") # get evaluation comments for answers by current user answer_comments = AnswerComment.query \ .join("answer") \ .filter(and_( # both draft and completed comments are allowed AnswerComment.active == True, AnswerComment.comment_type == AnswerCommentType.evaluation, Answer.id.in_([comparison.answer1_id, comparison.answer2_id]), AnswerComment.user_id == current_user.id )) \ .order_by(AnswerComment.draft, AnswerComment.created) \ .all() comparison.answer1_feedback = [c for c in answer_comments if c.answer_id == comparison.answer1_id] comparison.answer2_feedback = [c for c in answer_comments if c.answer_id == comparison.answer2_id] return { 'comparison': marshal(comparison, dataformat.get_comparison(restrict_user, include_answer_author=False, include_score=False, with_feedback=True)), 'current': comparison_count+1 }
def post(self, course_uuid, assignment_uuid): """ Stores comparison set into the database. """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require(READ, assignment, title="Comparison Not Saved", message="Comparisons can only be saved by those enrolled in the course. Please double-check your enrollment in this course.") require(EDIT, Comparison, title="Comparison Not Saved", message="Comparisons can only be saved by those enrolled in the course. Please double-check your enrollment in this course.") restrict_user = not allow(MANAGE, assignment) if not assignment.compare_grace: abort(403, title="Comparison Not Saved", message="Sorry, the comparison deadline has passed. No comparisons can be done after the deadline.") elif not restrict_user and not assignment.educators_can_compare: abort(403, title="Comparison Not Saved", message="Only students can save answer comparisons for this assignment. To change these settings to include instructors and teaching assistants, edit the assignment.") comparison = Comparison.query \ .options(joinedload('comparison_criteria')) \ .filter_by( assignment_id=assignment.id, user_id=current_user.id, completed=False ) \ .first() params = update_comparison_parser.parse_args() completed = True # check if there are any comparisons to update if not comparison: abort(400, title="Comparison Not Saved", message="There are no comparisons open for evaluation.") is_comparison_example = comparison.comparison_example_id != None # check if number of comparison criteria submitted matches number of comparison criteria needed if len(comparison.comparison_criteria) != len(params['comparison_criteria']): abort(400, title="Comparison Not Saved", message="Please double-check that all criteria were evaluated and try saving again.") if params.get('draft'): completed = False # check if each comparison has a criterion Id and a winner id for comparison_criterion_update in params['comparison_criteria']: # ensure criterion param is present if 'criterion_id' not in comparison_criterion_update: abort(400, title="Comparison Not Saved", message="Sorry, the assignment is missing criteria. Please reload the page and try again.") # set default values for cotnent and winner comparison_criterion_update.setdefault('content', None) winner = comparison_criterion_update.setdefault('winner', None) # if winner isn't set for any comparison criterion, then the comparison isn't complete yet if winner == None: completed = False # check that we're using criteria that were assigned to the course and that we didn't # get duplicate criteria in comparison criteria known_criterion = False for comparison_criterion in comparison.comparison_criteria: if comparison_criterion_update['criterion_id'] == comparison_criterion.criterion_uuid: known_criterion = True # check that the winner id matches one of the answer pairs if winner not in [None, WinningAnswer.answer1.value, WinningAnswer.answer2.value]: abort(400, title="Comparison Not Saved", message="Please select an answer from the two answers provided for each criterion and try saving again.") break if not known_criterion: abort(400, title="Comparison Not Saved", message="You are attempting to submit a comparison of an unknown criterion. Please remove the unknown criterion and try again.") # update comparison criterion comparison.completed = completed comparison.winner = None assignment_criteria = assignment.assignment_criteria answer1_weight = 0 answer2_weight = 0 for comparison_criterion in comparison.comparison_criteria: for comparison_criterion_update in params['comparison_criteria']: if comparison_criterion_update['criterion_id'] != comparison_criterion.criterion_uuid: continue winner = WinningAnswer(comparison_criterion_update['winner']) if comparison_criterion_update['winner'] != None else None comparison_criterion.winner = winner comparison_criterion.content = comparison_criterion_update['content'] if completed: weight = next(( assignment_criterion.weight for assignment_criterion in assignment_criteria \ if assignment_criterion.criterion_uuid == comparison_criterion.criterion_uuid ), 0) if winner == WinningAnswer.answer1: answer1_weight += weight elif winner == WinningAnswer.answer2: answer2_weight += weight if completed: if answer1_weight > answer2_weight: comparison.winner = WinningAnswer.answer1 elif answer1_weight < answer2_weight: comparison.winner = WinningAnswer.answer2 else: comparison.winner = WinningAnswer.draw else: # ensure that the comparison is 'touched' when saving a draft comparison.modified = datetime.datetime.utcnow() comparison.update_attempt( params.get('attempt_uuid'), params.get('attempt_started', None), params.get('attempt_ended', None) ) db.session.commit() # update answer scores if completed and not is_comparison_example: current_app.logger.debug("Doing scoring") Comparison.update_scores_1vs1(comparison) #Comparison.calculate_scores(assignment.id) # update course & assignment grade for user if comparison is completed if completed: assignment.calculate_grade(current_user) course.calculate_grade(current_user) on_comparison_update.send( self, event_name=on_comparison_update.name, user=current_user, course_id=course.id, assignment=assignment, comparison=comparison, is_comparison_example=is_comparison_example, data=marshal(comparison, dataformat.get_comparison(restrict_user))) return {'comparison': marshal(comparison, dataformat.get_comparison(restrict_user, include_answer_author=False, include_score=False))}
def get(self, course_uuid, assignment_uuid): """ Get (or create if needed) a comparison set for assignment. """ 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= "Assignments and their comparisons can only be seen here by those enrolled in the course. Please double-check your enrollment in this course." ) require( CREATE, Comparison, title="Comparisons Unavailable", message= "Comparisons can only be seen here by those enrolled in the course. Please double-check your enrollment in this course." ) restrict_user = not allow(MANAGE, assignment) comparison_count = assignment.completed_comparison_count_for_user( current_user.id) if not assignment.compare_grace: abort( 403, title="Comparisons Unavailable", message= "Sorry, the comparison deadline has passed. No comparisons can be done after the deadline." ) elif not restrict_user and not assignment.educators_can_compare: abort( 403, title="Comparisons Unavailable", message= "Only students can currently compare answers for this assignment. To change these settings to include instructors and teaching assistants, edit the assignment." ) elif restrict_user and comparison_count >= assignment.total_comparisons_required: abort( 400, title="Comparisons Completed", message= "More comparisons aren't available, since you've finished your comparisons for this assignment. Good job!" ) # check if user has a comparison they have not completed yet new_pair = False comparison = Comparison.query \ .options(joinedload('comparison_criteria')) \ .filter_by( assignment_id=assignment.id, user_id=current_user.id, completed=False ) \ .first() if comparison: on_comparison_get.send( self, event_name=on_comparison_get.name, user=current_user, course_id=course.id, data=marshal(comparison, dataformat.get_comparison(restrict_user))) else: # if there isn't an incomplete comparison, assign a new one try: comparison = Comparison.create_new_comparison( assignment.id, current_user.id, skip_comparison_examples=allow(MANAGE, assignment)) new_pair = True on_comparison_create.send( self, event_name=on_comparison_create.name, user=current_user, course_id=course.id, data=marshal(comparison, dataformat.get_comparison(restrict_user))) except InsufficientObjectsForPairException: abort( 400, title="Comparisons Unavailable", message= "Not enough answers are available for you to do comparisons right now. Please check back later for more answers." ) except UserComparedAllObjectsException: abort( 400, title="Comparisons Unavailable", message= "You have compared all the currently available answer pairs. Please check back later for more answers." ) except UnknownPairGeneratorException: abort( 500, title="Comparisons Unavailable", message= "Generating scored pairs failed, this really shouldn't happen." ) # get evaluation comments for answers by current user answer_comments = AnswerComment.query \ .join("answer") \ .filter(and_( # both draft and completed comments are allowed AnswerComment.active == True, AnswerComment.comment_type == AnswerCommentType.evaluation, Answer.id.in_([comparison.answer1_id, comparison.answer2_id]), AnswerComment.user_id == current_user.id )) \ .order_by(AnswerComment.draft, AnswerComment.created) \ .all() comparison.answer1_feedback = [ c for c in answer_comments if c.answer_id == comparison.answer1_id ] comparison.answer2_feedback = [ c for c in answer_comments if c.answer_id == comparison.answer2_id ] return { 'comparison': marshal( comparison, dataformat.get_comparison(restrict_user, include_answer_author=False, include_score=False, with_feedback=True)), 'new_pair': new_pair, 'current': comparison_count + 1 }
def post(self, course_uuid, assignment_uuid): """ Stores comparison set into the database. """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require( READ, assignment, title="Comparison Not Saved", message= "Comparisons can only be saved by those enrolled in the course. Please double-check your enrollment in this course." ) require( EDIT, Comparison, title="Comparison Not Saved", message= "Comparisons can only be saved by those enrolled in the course. Please double-check your enrollment in this course." ) restrict_user = not allow(MANAGE, assignment) if not assignment.compare_grace: abort( 403, title="Comparison Not Saved", message= "Sorry, the comparison deadline has passed. No comparisons can be done after the deadline." ) elif not restrict_user and not assignment.educators_can_compare: abort( 403, title="Comparison Not Saved", message= "Only students can save answer comparisons for this assignment. To change these settings to include instructors and teaching assistants, edit the assignment." ) comparison = Comparison.query \ .options(joinedload('comparison_criteria')) \ .filter_by( assignment_id=assignment.id, user_id=current_user.id, completed=False ) \ .first() params = update_comparison_parser.parse_args() completed = True # check if there are any comparisons to update if not comparison: abort(400, title="Comparison Not Saved", message="There are no comparisons open for evaluation.") is_comparison_example = comparison.comparison_example_id != None # check if number of comparison criteria submitted matches number of comparison criteria needed if len(comparison.comparison_criteria) != len( params['comparison_criteria']): abort( 400, title="Comparison Not Saved", message= "Please double-check that all criteria were evaluated and try saving again." ) if params.get('draft'): completed = False # check if each comparison has a criterion Id and a winner id for comparison_criterion_update in params['comparison_criteria']: # ensure criterion param is present if 'criterion_id' not in comparison_criterion_update: abort( 400, title="Comparison Not Saved", message= "Sorry, the assignment is missing criteria. Please reload the page and try again." ) # set default values for cotnent and winner comparison_criterion_update.setdefault('content', None) winner = comparison_criterion_update.setdefault('winner', None) # if winner isn't set for any comparison criterion, then the comparison isn't complete yet if winner == None: completed = False # check that we're using criteria that were assigned to the course and that we didn't # get duplicate criteria in comparison criteria known_criterion = False for comparison_criterion in comparison.comparison_criteria: if comparison_criterion_update[ 'criterion_id'] == comparison_criterion.criterion_uuid: known_criterion = True # check that the winner id matches one of the answer pairs if winner not in [ None, WinningAnswer.answer1.value, WinningAnswer.answer2.value ]: abort( 400, title="Comparison Not Saved", message= "Please select an answer from the two answers provided for each criterion and try saving again." ) break if not known_criterion: abort( 400, title="Comparison Not Saved", message= "You are attempting to submit a comparison of an unknown criterion. Please remove the unknown criterion and try again." ) # update comparison criterion comparison.completed = completed comparison.winner = None assignment_criteria = assignment.assignment_criteria answer1_weight = 0 answer2_weight = 0 for comparison_criterion in comparison.comparison_criteria: for comparison_criterion_update in params['comparison_criteria']: if comparison_criterion_update[ 'criterion_id'] != comparison_criterion.criterion_uuid: continue winner = WinningAnswer( comparison_criterion_update['winner'] ) if comparison_criterion_update['winner'] != None else None comparison_criterion.winner = winner comparison_criterion.content = comparison_criterion_update[ 'content'] if completed: weight = next(( assignment_criterion.weight for assignment_criterion in assignment_criteria \ if assignment_criterion.criterion_uuid == comparison_criterion.criterion_uuid ), 0) if winner == WinningAnswer.answer1: answer1_weight += weight elif winner == WinningAnswer.answer2: answer2_weight += weight if completed: if answer1_weight > answer2_weight: comparison.winner = WinningAnswer.answer1 elif answer1_weight < answer2_weight: comparison.winner = WinningAnswer.answer2 else: comparison.winner = WinningAnswer.draw else: # ensure that the comparison is 'touched' when saving a draft comparison.modified = datetime.datetime.utcnow() db.session.commit() # update answer scores if completed and not is_comparison_example: current_app.logger.debug("Doing scoring") Comparison.update_scores_1vs1(comparison) #Comparison.calculate_scores(assignment.id) # update course & assignment grade for user if comparison is completed if completed: assignment.calculate_grade(current_user) course.calculate_grade(current_user) on_comparison_update.send( self, event_name=on_comparison_update.name, user=current_user, course_id=course.id, assignment=assignment, comparison=comparison, is_comparison_example=is_comparison_example, data=marshal(comparison, dataformat.get_comparison(restrict_user))) return { 'comparison': marshal( comparison, dataformat.get_comparison(restrict_user, include_answer_author=False, include_score=False)) }
def get(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require(READ, course) assignments = course.assignments \ .filter_by(active=True) \ .all() assignment_ids = [assignment.id for assignment in assignments] answer_counts = Answer.query \ .with_entities( Answer.assignment_id, func.count(Answer.assignment_id).label('answer_count') ) \ .filter_by( user_id=current_user.id, active=True, practice=False, draft=False ) \ .filter(Answer.assignment_id.in_(assignment_ids)) \ .group_by(Answer.assignment_id) \ .all() # get self evaluation status for assignments with self evaluations enabled self_evaluations = AnswerComment.query \ .join("answer") \ .with_entities( Answer.assignment_id, func.count(Answer.assignment_id).label('self_evaluation_count') ) \ .filter(and_( AnswerComment.user_id == current_user.id, AnswerComment.active == True, AnswerComment.comment_type == AnswerCommentType.self_evaluation, AnswerComment.draft == False, Answer.active == True, Answer.practice == False, Answer.draft == False, Answer.assignment_id.in_(assignment_ids) )) \ .group_by(Answer.assignment_id) \ .all() drafts = Answer.query \ .options(load_only('id', 'assignment_id')) \ .filter_by( user_id=current_user.id, active=True, practice=False, draft=True, saved=True ) \ .all() statuses = {} for assignment in assignments: answer_count = next( (result.answer_count for result in answer_counts if result.assignment_id == assignment.id), 0 ) assignment_drafts = [draft for draft in drafts if draft.assignment_id == assignment.id] comparison_count = assignment.completed_comparison_count_for_user(current_user.id) comparison_availble = Comparison.comparison_avialble_for_user(course.id, assignment.id, current_user.id) statuses[assignment.uuid] = { 'answers': { 'answered': answer_count > 0, 'count': answer_count, 'has_draft': len(assignment_drafts) > 0, 'draft_ids': [draft.uuid for draft in assignment_drafts] }, 'comparisons': { 'available': comparison_availble, 'count': comparison_count, 'left': max(0, assignment.total_comparisons_required - comparison_count) } } if assignment.enable_self_evaluation: self_evaluation_count = next( (result.self_evaluation_count for result in self_evaluations if result.assignment_id == assignment.id), 0 ) statuses[assignment.uuid]['comparisons']['self_evaluation_completed'] = self_evaluation_count > 0 on_assignment_list_get_status.send( self, event_name=on_assignment_list_get_status.name, user=current_user, course_id=course.id, data=statuses) return {"statuses": statuses}
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) answer_count = Answer.query \ .filter_by( user_id=current_user.id, assignment_id=assignment.id, active=True, practice=False, 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) comparison_availble = Comparison.comparison_avialble_for_user(course.id, assignment.id, current_user.id) status = { 'answers': { 'answered': answer_count > 0, 'count': answer_count, 'has_draft': len(drafts) > 0, 'draft_ids': [draft.uuid for draft in drafts] }, 'comparisons': { 'available': comparison_availble, '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}
def _submit_all_possible_comparisons_for_user(self, user_id): example_winner_ids = [] example_loser_ids = [] for comparison_example in self.data.comparisons_examples: if comparison_example.assignment_id == self.assignment.id: comparison = Comparison.create_new_comparison( self.assignment.id, user_id, False) self.assertEqual(comparison.answer1_id, comparison_example.answer1_id) self.assertEqual(comparison.answer2_id, comparison_example.answer2_id) min_id = min([comparison.answer1_id, comparison.answer2_id]) max_id = max([comparison.answer1_id, comparison.answer2_id]) example_winner_ids.append(min_id) example_loser_ids.append(max_id) comparison.completed = True comparison.winner = WinningAnswer.answer1 if comparison.answer1_id < comparison.answer2_id else WinningAnswer.answer2 for comparison_criterion in comparison.comparison_criteria: comparison_criterion.winner = comparison.winner db.session.add(comparison) db.session.commit() # self.login(username) # calculate number of comparisons to do before user has compared all the pairs it can num_eligible_answers = 0 # need to minus one to exclude the logged in user's own answer for answer in self.data.get_student_answers(): if answer.assignment_id == self.assignment.id and answer.user_id != user_id: num_eligible_answers += 1 # n(n-1)/2 possible pairs before all answers have been compared num_possible_comparisons = int(num_eligible_answers * (num_eligible_answers - 1) / 2) winner_ids = [] loser_ids = [] for i in range(num_possible_comparisons): comparison = Comparison.create_new_comparison( self.assignment.id, user_id, False) min_id = min([comparison.answer1_id, comparison.answer2_id]) max_id = max([comparison.answer1_id, comparison.answer2_id]) winner_ids.append(min_id) loser_ids.append(max_id) comparison.completed = True comparison.winner = WinningAnswer.answer1 if comparison.answer1_id < comparison.answer2_id else WinningAnswer.answer2 for comparison_criterion in comparison.comparison_criteria: comparison_criterion.winner = comparison.winner db.session.add(comparison) db.session.commit() Comparison.calculate_scores(self.assignment.id) return { 'comparisons': { 'winners': winner_ids, 'losers': loser_ids }, 'comparison_examples': { 'winners': example_winner_ids, 'losers': example_loser_ids } }