def test_grade_is_none(self): self.assertIsNone(models.get_feedback_for_grade(None))
def grade_flow_page(pctx, flow_session_id, page_ordinal): # type: (CoursePageContext, int, int) -> http.HttpResponse now_datetime = get_now_or_fake_time(pctx.request) page_ordinal = int(page_ordinal) viewing_prev_grade = False prev_grade_id = pctx.request.GET.get("grade_id") if prev_grade_id is not None: try: prev_grade_id = int(prev_grade_id) viewing_prev_grade = True except ValueError: raise SuspiciousOperation("non-integer passed for 'grade_id'") if not pctx.has_permission(pperm.view_gradebook): raise PermissionDenied(_("may not view grade book")) 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")) if flow_session.participation is None: raise SuspiciousOperation(_("Cannot grade anonymous session")) from course.flow import adjust_flow_session_page_data adjust_flow_session_page_data(pctx.repo, flow_session, pctx.course.identifier, respect_preview=False) fpctx = FlowPageContext(pctx.repo, pctx.course, flow_session.flow_id, page_ordinal, participation=flow_session.participation, flow_session=flow_session, request=pctx.request) if fpctx.page_desc is None: raise http.Http404() assert fpctx.page is not None assert fpctx.page_context is not None # {{{ enable flow session zapping all_flow_sessions = list( FlowSession.objects.filter(course=pctx.course, flow_id=flow_session.flow_id, participation__isnull=False, in_progress=flow_session.in_progress). order_by( # Datatables will default to sorting the user list # by the first column, which happens to be the username. # Match that sorting. "participation__user__username", "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 # }}} prev_grades = get_prev_visit_grades(pctx.course_identifier, flow_session_id, page_ordinal) # {{{ reproduce student view form = None feedback = None answer_data = None grade_data = None shown_grade = None page_expects_answer = fpctx.page.expects_answer() if page_expects_answer: if fpctx.prev_answer_visit is not None and prev_grade_id is None: answer_data = fpctx.prev_answer_visit.answer shown_grade = fpctx.prev_answer_visit.get_most_recent_grade() if shown_grade is not None: feedback = get_feedback_for_grade(shown_grade) grade_data = shown_grade.grade_data else: feedback = None grade_data = None if shown_grade is not None: prev_grade_id = shown_grade.id elif prev_grade_id is not None: try: shown_grade = prev_grades.filter(id=prev_grade_id).get() except ObjectDoesNotExist: raise http.Http404() feedback = get_feedback_for_grade(shown_grade) grade_data = shown_grade.grade_data answer_data = shown_grade.visit.answer else: feedback = None from course.page.base import PageBehavior page_behavior = PageBehavior(show_correctness=True, show_answer=False, may_change_answer=False) try: form = fpctx.page.make_form(fpctx.page_context, fpctx.page_data.data, answer_data, page_behavior) except InvalidPageData as e: messages.add_message( pctx.request, messages.ERROR, _("The page data stored in the database was found " "to be invalid for the page as given in the " "course content. Likely the course content was " "changed in an incompatible way (say, by adding " "an option to a choice question) without changing " "the question ID. The precise error encountered " "was the following: ") + str(e)) return render_course_page(pctx, "course/course-base.html", {}) if form is not None: form_html = fpctx.page.form_to_html(pctx.request, fpctx.page_context, form, answer_data) else: form_html = None # }}} # {{{ grading form if (page_expects_answer and fpctx.page.is_answer_gradable() and fpctx.prev_answer_visit is not None and not flow_session.in_progress and not viewing_prev_grade): request = pctx.request if pctx.request.method == "POST": if not pctx.has_permission(pperm.assign_grade): raise PermissionDenied(_("may not assign grades")) 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_v2( request, fpctx.page_context, fpctx.page_data, grade_data, grading_form, request.FILES) from course.utils import LanguageOverride with LanguageOverride(pctx.course): feedback = fpctx.page.grade(fpctx.page_context, fpctx.page_data.data, answer_data, grade_data) if feedback is not None: correctness = feedback.correctness else: correctness = None feedback_json = None # type: Optional[Dict[Text, Any]] bulk_feedback_json = None # type: Optional[Dict[Text, Any]] if feedback is not None: feedback_json, bulk_feedback_json = feedback.as_json() else: feedback_json = bulk_feedback_json = None most_recent_grade = FlowPageVisitGrade( visit=fpctx.prev_answer_visit, grader=pctx.request.user, graded_at_git_commit_sha=pctx.course_commit_sha, grade_data=grade_data, max_points=fpctx.page.max_points(fpctx.page_data), correctness=correctness, feedback=feedback_json) prev_grade_id = _save_grade(fpctx, flow_session, most_recent_grade, bulk_feedback_json, now_datetime) else: grading_form = fpctx.page.make_grading_form( fpctx.page_context, fpctx.page_data, grade_data) else: grading_form = None grading_form_html = None # type: Optional[Text] if grading_form is not None: from crispy_forms.layout import Submit grading_form.helper.form_class += " relate-grading-form" grading_form.helper.add_input( Submit("submit", _("Submit"), accesskey="s", css_class="relate-grading-save-button")) grading_form_html = fpctx.page.grading_form_to_html( pctx.request, fpctx.page_context, grading_form, grade_data) # }}} # {{{ compute points_awarded max_points = None points_awarded = None if (page_expects_answer and fpctx.page.is_answer_gradable()): 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_rule = get_session_grading_rule(flow_session, fpctx.flow_desc, get_now_or_fake_time(pctx.request)) if grading_rule.grade_identifier is not None: grading_opportunity = get_flow_grading_opportunity( pctx.course, flow_session.flow_id, fpctx.flow_desc, grading_rule.grade_identifier, grading_rule. grade_aggregation_strategy) # type: Optional[GradingOpportunity] else: grading_opportunity = None return render_course_page( pctx, "course/grade-flow-page.html", { "flow_identifier": fpctx.flow_id, "flow_session": flow_session, "flow_desc": fpctx.flow_desc, "page_ordinal": fpctx.page_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, "shown_grade": shown_grade, "prev_grade_id": prev_grade_id, "expects_answer": page_expects_answer, "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, "correct_answer": fpctx.page.correct_answer(fpctx.page_context, fpctx.page_data.data, answer_data, grade_data), # Wrappers used by JavaScript template (tmpl) so as not to # conflict with Django template's tag wrapper "JQ_OPEN": '{%', 'JQ_CLOSE': '%}', })
def grade_flow_page(pctx, flow_session_id, page_ordinal): now_datetime = get_now_or_fake_time(pctx.request) page_ordinal = int(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")) if flow_session.participation is None: raise SuspiciousOperation( _("Cannot grade anonymous session")) fpctx = FlowPageContext(pctx.repo, pctx.course, flow_session.flow_id, page_ordinal, participation=flow_session.participation, flow_session=flow_session, request=pctx.request) if fpctx.page_desc is None: raise http.Http404() from course.flow import adjust_flow_session_page_data adjust_flow_session_page_data(pctx.repo, flow_session, pctx.course.identifier, fpctx.flow_desc) # {{{ enable flow session zapping all_flow_sessions = list(FlowSession.objects .filter( course=pctx.course, flow_id=flow_session.flow_id, participation__isnull=False, in_progress=flow_session.in_progress) .order_by( # Datatables will default to sorting the user list # by the first column, which happens to be the username. # Match that sorting. "participation__user__username", "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 grade_data = None most_recent_grade = 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: feedback = get_feedback_for_grade(most_recent_grade) grade_data = most_recent_grade.grade_data else: feedback = None grade_data = None else: feedback = None from course.page.base import PageBehavior page_behavior = PageBehavior( show_correctness=True, show_answer=False, may_change_answer=False) form = fpctx.page.make_form( fpctx.page_context, fpctx.page_data.data, answer_data, page_behavior) if form is not None: form_html = fpctx.page.form_to_html( pctx.request, fpctx.page_context, form, answer_data) else: form_html = None # }}} # {{{ grading form if (fpctx.page.expects_answer() and fpctx.page.is_answer_gradable() and fpctx.prev_answer_visit is not None and not flow_session.in_progress): 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) with translation.override(settings.RELATE_ADMIN_EMAIL_LOCALE): 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, bulk_feedback_json = feedback.as_json() else: feedback_json = bulk_feedback_json = None most_recent_grade = FlowPageVisitGrade( visit=fpctx.prev_answer_visit, grader=pctx.request.user, graded_at_git_commit_sha=pctx.course_commit_sha, grade_data=grade_data, max_points=fpctx.page.max_points(fpctx.page_data), correctness=correctness, feedback=feedback_json) _save_grade(fpctx, flow_session, most_recent_grade, bulk_feedback_json, now_datetime) 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.form_class += " relate-grading-form" grading_form.helper.add_input( Submit( "submit", _("Submit"), accesskey="s", css_class="relate-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 # }}} # {{{ compute points_awarded max_points = None points_awarded = None if (fpctx.page.expects_answer() and fpctx.page.is_answer_gradable()): 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_rule = get_session_grading_rule( flow_session, flow_session.participation.role, fpctx.flow_desc, get_now_or_fake_time(pctx.request)) if grading_rule.grade_identifier is not None: grading_opportunity = get_flow_grading_opportunity( pctx.course, flow_session.flow_id, fpctx.flow_desc, grading_rule) else: grading_opportunity = None return render_course_page( pctx, "course/grade-flow-page.html", { "flow_identifier": fpctx.flow_id, "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, "most_recent_grade": most_recent_grade, "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, "correct_answer": fpctx.page.correct_answer( fpctx.page_context, fpctx.page_data.data, answer_data, grade_data), })
def view_flow_page(pctx, flow_session_id, ordinal): request = pctx.request ordinal = int(ordinal) flow_session_id = int(flow_session_id) flow_session = get_and_check_flow_session(pctx, flow_session_id) flow_id = flow_session.flow_id 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("relate-view_start_flow", pctx.course.identifier, flow_id) try: fpctx = FlowPageContext( pctx.repo, pctx.course, flow_id, ordinal, participation=pctx.participation, flow_session=flow_session ) except PageOrdinalOutOfRange: return redirect("relate-view_flow_page", pctx.course.identifier, flow_session.id, flow_session.page_count - 1) access_rule = get_session_access_rule( flow_session, pctx.role, fpctx.flow_desc, get_now_or_fake_time(request), pctx.remote_address ) permissions = fpctx.page.get_modified_permissions_for_page(access_rule.permissions) if access_rule.message: messages.add_message(request, messages.INFO, access_rule.message) 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("relate-finish_flow_session_view", pctx.course.identifier, flow_session_id) else: submission_allowed = True # reject answer update if permission not present if flow_permission.submit_answer not in permissions: messages.add_message(request, messages.ERROR, _("Answer submission not allowed.")) submission_allowed = False # reject if previous answer was final if ( fpctx.prev_answer_visit is not None and fpctx.prev_answer_visit.is_submitted_answer and flow_permission.change_answer not in permissions ): messages.add_message(request, messages.ERROR, _("Already have final answer.")) submission_allowed = False 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 submission_allowed and 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_submitted_answer = pressed_button == "submit" page_visit.save() answer_was_graded = page_visit.is_submitted_answer may_change_answer = not answer_was_graded or flow_permission.change_answer in permissions if fpctx.page.is_answer_gradable(): feedback = fpctx.page.grade(page_context, page_data.data, page_visit.answer, grade_data=None) if page_visit.is_submitted_answer: grade = FlowPageVisitGrade() grade.visit = page_visit grade.max_points = fpctx.page.max_points(page_data.data) grade.graded_at_git_commit_sha = pctx.course_commit_sha bulk_feedback_json = None if feedback is not None: grade.correctness = feedback.correctness grade.feedback, bulk_feedback_json = feedback.as_json() grade.save() update_bulk_feedback(page_data, grade, bulk_feedback_json) del grade else: feedback = None if pressed_button == "save_and_next" and not will_receive_feedback(permissions): return redirect("relate-view_flow_page", pctx.course.identifier, flow_session_id, fpctx.ordinal + 1) elif pressed_button == "save_and_finish" and not will_receive_feedback(permissions): return redirect("relate-finish_flow_session_view", pctx.course.identifier, flow_session_id) 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_submitted_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 and (flow_permission.submit_answer in permissions) ) 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: feedback = get_feedback_for_grade(most_recent_grade) 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 show_answer = flow_permission.see_answer_after_submission 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 show_answer = flow_permission.see_answer_before_submission in permissions else: show_answer = ( flow_permission.see_answer_before_submission in permissions or flow_permission.see_answer_after_submission 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 expiration_mode_choices = [] for key, descr in FLOW_SESSION_EXPIRATION_MODE_CHOICES: if is_expiration_mode_allowed(key, permissions): expiration_mode_choices.append((key, descr)) args = { "flow_identifier": fpctx.flow_id, "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, "page_numbers": zip(range(flow_session.page_count), range(1, flow_session.page_count + 1)), "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, "expiration_mode_choices": expiration_mode_choices, "expiration_mode_choice_count": len(expiration_mode_choices), "expiration_mode": flow_session.expiration_mode, } if fpctx.page.expects_answer() and fpctx.page.is_answer_gradable(): 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)
def gather_grade_info(fctx, flow_session, answer_visits): """ :returns: a :class:`GradeInfo` """ all_page_data = FlowPageData.objects.filter(flow_session=flow_session, ordinal__isnull=False).order_by("ordinal") points = 0 provisional_points = 0 max_points = 0 max_reachable_points = 0 fully_correct_count = 0 partially_correct_count = 0 incorrect_count = 0 unknown_count = 0 for i, page_data in enumerate(all_page_data): page = instantiate_flow_page_with_ctx(fctx, page_data) assert i == page_data.ordinal if answer_visits[i] is None: # This is true in principle, but early code to deal with survey questions # didn't generate synthetic answer visits for survey questions, so this # can't actually be enforced. # assert not page.expects_answer() continue if not page.is_answer_gradable(): continue grade = answer_visits[i].get_most_recent_grade() assert grade is not None feedback = get_feedback_for_grade(grade) max_points += grade.max_points if feedback is None or feedback.correctness is None: unknown_count += 1 points = None continue max_reachable_points += grade.max_points page_points = grade.max_points * feedback.correctness if points is not None: points += page_points provisional_points += page_points if grade.max_points > 0: if feedback.correctness == 1: fully_correct_count += 1 elif feedback.correctness == 0: incorrect_count += 1 else: partially_correct_count += 1 return GradeInfo( points=points, provisional_points=provisional_points, max_points=max_points, max_reachable_points=max_reachable_points, fully_correct_count=fully_correct_count, partially_correct_count=partially_correct_count, incorrect_count=incorrect_count, unknown_count=unknown_count, )
def grade_flow_page(pctx, flow_session_id, page_ordinal): page_ordinal = int(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")) if flow_session.participation is None: raise SuspiciousOperation( _("Cannot grade anonymous session")) 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, participation__isnull=False, in_progress=flow_session.in_progress) .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 grade_data = None most_recent_grade = 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: feedback = get_feedback_for_grade(most_recent_grade) grade_data = most_recent_grade.grade_data else: feedback = None grade_data = None else: feedback = None from course.page.base import PageBehavior page_behavior = PageBehavior( show_correctness=True, show_answer=False, may_change_answer=False) form = fpctx.page.make_form( fpctx.page_context, fpctx.page_data.data, answer_data, page_behavior) if form is not None: form_html = fpctx.page.form_to_html( pctx.request, fpctx.page_context, form, answer_data) else: form_html = None # }}} # {{{ grading form if (fpctx.page.expects_answer() and fpctx.page.is_answer_gradable() and fpctx.prev_answer_visit is not None and not flow_session.in_progress): 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, bulk_feedback_json = feedback.as_json() else: feedback_json = bulk_feedback_json = None most_recent_grade = FlowPageVisitGrade( visit=fpctx.prev_answer_visit, grader=pctx.request.user, graded_at_git_commit_sha=pctx.course_commit_sha, grade_data=grade_data, max_points=fpctx.page.max_points(fpctx.page_data), correctness=correctness, feedback=feedback_json) most_recent_grade.save() update_bulk_feedback( fpctx.prev_answer_visit.page_data, most_recent_grade, bulk_feedback_json) grading_rule = get_session_grading_rule( flow_session, flow_session.participation.role, fpctx.flow_desc, get_now_or_fake_time(request)) from course.flow import grade_flow_session grade_flow_session(fpctx, flow_session, grading_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 relate-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 # }}} # {{{ compute points_awarded max_points = None points_awarded = None if (fpctx.page.expects_answer() and fpctx.page.is_answer_gradable()): 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_rule = get_session_grading_rule( flow_session, flow_session.participation.role, fpctx.flow_desc, get_now_or_fake_time(pctx.request)) if grading_rule.grade_identifier is not None: grading_opportunity = get_flow_grading_opportunity( pctx.course, flow_session.flow_id, fpctx.flow_desc, grading_rule) else: grading_opportunity = None return render_course_page( pctx, "course/grade-flow-page.html", { "flow_identifier": fpctx.flow_id, "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, "most_recent_grade": most_recent_grade, "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, })