def grade(self, page_context, page_data, answer_data, grade_data): correctness = 0 if answer_data is None: return AnswerFeedback(correctness=0, feedback="No answer provided.") answer = answer_data["answer"] for correct_answer in self.page_desc.answers: if correct_answer == answer: correctness = 1 break return AnswerFeedback(correctness=correctness)
def get_most_recent_feedback(self): grade = self.get_most_recent_grade() from course.page import AnswerFeedback if grade is not None: return AnswerFeedback.from_json(grade.feedback) else: return None
def get_feedback_for_grade(grade): try: bulk_feedback_json = FlowPageBulkFeedback.objects.get( page_data=grade.visit.page_data, grade=grade).bulk_feedback except ObjectDoesNotExist: bulk_feedback_json = None from course.page import AnswerFeedback if grade is not None: return AnswerFeedback.from_json(grade.feedback, bulk_feedback_json) else: return None
def get_feedback_for_grade(grade): try: bulk_feedback_json = FlowPageBulkFeedback.objects.get( page_data=grade.visit.page_data, grade=grade).bulk_feedback except ObjectDoesNotExist: bulk_feedback_json = None from course.page import AnswerFeedback if grade is not None: return AnswerFeedback.from_json( grade.feedback, bulk_feedback_json) else: return None
def gather_grade_info(flow_session, answer_visits): all_page_data = (FlowPageData.objects .filter(flow_session=flow_session) .order_by("ordinal")) points = 0 max_points = 0 fully_correct_count = 0 partially_correct_count = 0 incorrect_count = 0 for i, page_data in enumerate(all_page_data): assert i == page_data.ordinal if answer_visits[i] is None: # page did not expect an answer continue grade = answer_visits[i].get_most_recent_grade() assert grade is not None from course.page import AnswerFeedback feedback = AnswerFeedback.from_json(grade.feedback) if feedback is None or feedback.correctness is None: return None max_points += grade.max_points points += grade.max_points*feedback.correctness if feedback.correctness == 1: fully_correct_count += 1 elif feedback.correctness == 0: incorrect_count += 1 else: partially_correct_count += 1 return GradeInfo( points=points, max_points=max_points, fully_correct_count=fully_correct_count, partially_correct_count=partially_correct_count, incorrect_count=incorrect_count)
def grade_flow_page(pctx, flow_session_id, page_ordinal): if pctx.role not in [ participation_role.instructor, participation_role.teaching_assistant]: raise PermissionDenied("must be instructor or TA to view grades") flow_session = get_object_or_404(FlowSession, id=int(flow_session_id)) if flow_session.course.pk != pctx.course.pk: raise SuspiciousOperation("Flow session not part of specified course") fpctx = FlowPageContext(pctx.repo, pctx.course, flow_session.flow_id, page_ordinal, participation=flow_session.participation, flow_session=flow_session) if fpctx.page_desc is None: raise http.Http404() # {{{ enable flow session zapping all_flow_sessions = list(FlowSession.objects .filter( course=pctx.course, flow_id=flow_session.flow_id, in_progress=flow_session.in_progress, for_credit=flow_session.for_credit) .order_by( "participation__user__last_name", "start_time")) next_flow_session_id = None prev_flow_session_id = None for i, other_flow_session in enumerate(all_flow_sessions): if other_flow_session.pk == flow_session.pk: if i > 0: prev_flow_session_id = all_flow_sessions[i-1].id if i + 1 < len(all_flow_sessions): next_flow_session_id = all_flow_sessions[i+1].id # }}} # {{{ reproduce student view form = None feedback = None answer_data = None if fpctx.page.expects_answer(): if fpctx.prev_answer_visit is not None: answer_data = fpctx.prev_answer_visit.answer most_recent_grade = fpctx.prev_answer_visit.get_most_recent_grade() if most_recent_grade is not None: if most_recent_grade.feedback is not None: from course.page import AnswerFeedback feedback = AnswerFeedback.from_json( most_recent_grade.feedback) else: feedback = None grade_data = most_recent_grade.grade_data else: feedback = None grade_data = None else: feedback = None form = fpctx.page.make_form( fpctx.page_context, fpctx.page_data.data, answer_data, answer_is_final=True) if form is not None: form_html = fpctx.page.form_to_html( pctx.request, fpctx.page_context, form, answer_data) else: form_html = None max_points = None points_awarded = None if fpctx.page.expects_answer(): max_points = fpctx.page.max_points(fpctx.page_data) if feedback is not None and feedback.correctness is not None: points_awarded = max_points * feedback.correctness # }}} # {{{ grading form if fpctx.page.expects_answer() and fpctx.prev_answer_visit is not None: request = pctx.request if pctx.request.method == "POST": grading_form = fpctx.page.post_grading_form( fpctx.page_context, fpctx.page_data, grade_data, request.POST, request.FILES) if grading_form.is_valid(): grade_data = fpctx.page.update_grade_data_from_grading_form( fpctx.page_context, fpctx.page_data, grade_data, grading_form, request.FILES) feedback = fpctx.page.grade( fpctx.page_context, fpctx.page_data, answer_data, grade_data) if feedback is not None: correctness = feedback.correctness else: correctness = None if feedback is not None: feedback_json = feedback.as_json() else: feedback_json = None grade = FlowPageVisitGrade( visit=fpctx.prev_answer_visit, grader=pctx.request.user, graded_at_git_commit_sha=fpctx.flow_commit_sha, grade_data=grade_data, max_points=fpctx.page.max_points(fpctx.page_data), correctness=correctness, feedback=feedback_json) grade.save() current_access_rule = fpctx.get_current_access_rule( flow_session, flow_session.participation.role, flow_session.participation, now()) from course.flow import grade_flow_session grade_flow_session(fpctx, flow_session, current_access_rule) else: grading_form = fpctx.page.make_grading_form( fpctx.page_context, fpctx.page_data, grade_data) else: grading_form = None if grading_form is not None: from crispy_forms.layout import Submit grading_form.helper.add_input( Submit( "submit", "Submit", accesskey="s", css_class="col-lg-offset-2 cf-grading-save-button")) grading_form_html = fpctx.page.grading_form_to_html( pctx.request, fpctx.page_context, grading_form, grade_data) else: grading_form_html = None # }}} grading_opportunity = get_flow_grading_opportunity( pctx.course, flow_session.flow_id, fpctx.flow_desc) return render_course_page( pctx, "course/grade-flow-page.html", { "flow_identifier": fpctx.flow_identifier, "flow_session": flow_session, "flow_desc": fpctx.flow_desc, "ordinal": fpctx.ordinal, "page_data": fpctx.page_data, "body": fpctx.page.body( fpctx.page_context, fpctx.page_data.data), "form": form, "form_html": form_html, "feedback": feedback, "max_points": max_points, "points_awarded": points_awarded, "grading_opportunity": grading_opportunity, "prev_flow_session_id": prev_flow_session_id, "next_flow_session_id": next_flow_session_id, "grading_form": grading_form, "grading_form_html": grading_form_html, })
def view_flow_page(pctx, flow_identifier, ordinal): request = pctx.request flow_session = find_current_flow_session( request, pctx.course, flow_identifier) if flow_session is None: messages.add_message(request, messages.WARNING, "No in-progress session record found for this flow. " "Redirected to flow start page.") return redirect("course.flow.start_flow", pctx.course.identifier, flow_identifier) fpctx = FlowPageContext(pctx.repo, pctx.course, flow_identifier, ordinal, participation=pctx.participation, flow_session=flow_session) if fpctx.page_desc is None: messages.add_message(request, messages.ERROR, "Your session does not match the course content and needs " "to be reset. Course staff have been notified about this issue. " "Please get in touch with them to get help.") from django.template.loader import render_to_string message = render_to_string("course/session-mismatch.txt", { "page_data": fpctx.page_data, "course": pctx.course, "user": pctx.request.user, }) from django.core.mail import send_mail from django.conf import settings send_mail("[%s] session mismatch with course content" % pctx.course.identifier, message, settings.ROBOT_EMAIL_FROM, recipient_list=[pctx.course.email]) return redirect("course.flow.start_flow", pctx.course.identifier, flow_identifier) current_access_rule = fpctx.get_current_access_rule( flow_session, pctx.role, pctx.participation, get_now_or_fake_time(request)) permissions = fpctx.page.get_modified_permissions_for_page( current_access_rule.permissions) page_context = fpctx.page_context page_data = fpctx.page_data answer_data = None grade_data = None if flow_permission.view not in permissions: raise PermissionDenied("not allowed to view flow") if request.method == "POST": if "finish" in request.POST: return redirect("course.flow.finish_flow_session_view", pctx.course.identifier, flow_identifier) else: # reject answer update if flow is not in-progress if not flow_session.in_progress: raise PermissionDenied("session is not in progress") # reject if previous answer was final if (fpctx.prev_answer_visit is not None and fpctx.prev_answer_visit.is_graded_answer and flow_permission.change_answer not in permissions): raise PermissionDenied("already have final answer") form = fpctx.page.post_form( fpctx.page_context, fpctx.page_data.data, post_data=request.POST, files_data=request.FILES) pressed_button = get_pressed_button(form) if form.is_valid(): # {{{ form validated, process answer messages.add_message(request, messages.INFO, "Answer saved.") page_visit = FlowPageVisit() page_visit.flow_session = flow_session page_visit.page_data = fpctx.page_data page_visit.remote_address = request.META['REMOTE_ADDR'] answer_data = page_visit.answer = fpctx.page.answer_data( fpctx.page_context, fpctx.page_data.data, form, request.FILES) page_visit.is_graded_answer = pressed_button == "submit" page_visit.save() answer_was_graded = page_visit.is_graded_answer may_change_answer = ( not answer_was_graded or flow_permission.change_answer in permissions) feedback = fpctx.page.grade( page_context, page_data.data, page_visit.answer, grade_data=None) if page_visit.is_graded_answer: grade = FlowPageVisitGrade() grade.visit = page_visit grade.max_points = fpctx.page.max_points(page_data.data) grade.graded_at_git_commit_sha = fpctx.flow_commit_sha if feedback is not None: grade.correctness = feedback.correctness grade.feedback = feedback.as_json() grade.save() del grade if (pressed_button == "save_and_next" and not will_receive_feedback(permissions)): return redirect("course.flow.view_flow_page", pctx.course.identifier, flow_identifier, fpctx.ordinal + 1) elif (pressed_button == "save_and_finish" and not will_receive_feedback(permissions)): return redirect("course.flow.finish_flow_session_view", pctx.course.identifier, flow_identifier) else: form = fpctx.page.make_form( page_context, page_data.data, page_visit.answer, not may_change_answer) # continue at common flow page generation below # }}} del page_visit else: # form did not validate create_flow_page_visit(request, flow_session, fpctx.page_data) answer_was_graded = False may_change_answer = True # because we were allowed this far in by the check above feedback = None # continue at common flow page generation below else: create_flow_page_visit(request, flow_session, fpctx.page_data) if fpctx.prev_answer_visit is not None: answer_was_graded = fpctx.prev_answer_visit.is_graded_answer else: answer_was_graded = False may_change_answer = ( (not answer_was_graded or (flow_permission.change_answer in permissions)) # can happen if no answer was ever saved and flow_session.in_progress) if fpctx.page.expects_answer(): if fpctx.prev_answer_visit is not None: answer_data = fpctx.prev_answer_visit.answer most_recent_grade = fpctx.prev_answer_visit.get_most_recent_grade() if most_recent_grade is not None: if most_recent_grade.feedback is not None: from course.page import AnswerFeedback feedback = AnswerFeedback.from_json( most_recent_grade.feedback) else: feedback = None grade_data = most_recent_grade.grade_data else: feedback = None grade_data = None else: feedback = None form = fpctx.page.make_form( page_context, page_data.data, answer_data, not may_change_answer) else: form = None feedback = None # start common flow page generation # defined at this point: # form, form_html, may_change_answer, answer_was_graded, feedback if form is not None and may_change_answer: form = add_buttons_to_form(form, fpctx, flow_session, permissions) show_correctness = None show_answer = None shown_feedback = None if fpctx.page.expects_answer() and answer_was_graded: show_correctness = ( flow_permission.see_correctness in permissions or ( (flow_permission.see_correctness_after_completion in permissions) and not flow_session.in_progress)) show_answer = flow_permission.see_answer in permissions if show_correctness or show_answer: shown_feedback = feedback elif fpctx.page.expects_answer() and not answer_was_graded: # Don't show answer yet pass else: show_answer = flow_permission.see_answer in permissions title = fpctx.page.title(page_context, page_data.data) body = fpctx.page.body(page_context, page_data.data) if show_answer: correct_answer = fpctx.page.correct_answer( page_context, page_data.data, answer_data, grade_data) else: correct_answer = None # {{{ render flow page if form is not None: form_html = fpctx.page.form_to_html( pctx.request, page_context, form, answer_data) else: form_html = None args = { "flow_identifier": fpctx.flow_identifier, "flow_desc": fpctx.flow_desc, "ordinal": fpctx.ordinal, "page_data": fpctx.page_data, "percentage": int(100*(fpctx.ordinal+1) / flow_session.page_count), "flow_session": flow_session, "title": title, "body": body, "form": form, "form_html": form_html, "feedback": shown_feedback, "correct_answer": correct_answer, "show_correctness": show_correctness, "may_change_answer": may_change_answer, "may_change_graded_answer": ( (flow_permission.change_answer in permissions) and flow_session.in_progress), "will_receive_feedback": will_receive_feedback(permissions), "show_answer": show_answer, } if fpctx.page.expects_answer(): args["max_points"] = fpctx.page.max_points(fpctx.page_data) return render_course_page( pctx, "course/flow-page.html", args, allow_instant_flow_requests=False)