def grade_page_visits(fctx, flow_session, answer_visits, force_regrade=False): for i in range(len(answer_visits)): answer_visit = answer_visits[i] if answer_visit is not None: answer_visit.is_graded_answer = True answer_visit.save() else: page_data = flow_session.page_data.get(ordinal=i) page = instantiate_flow_page_with_ctx(fctx, page_data) if not page.expects_answer(): continue # Create a synthetic visit to attach a grade answer_visit = FlowPageVisit() answer_visit.flow_session = flow_session answer_visit.page_data = page_data answer_visit.is_synthetic = True answer_visit.answer = None answer_visit.is_graded_answer = True answer_visit.save() answer_visits[i] = answer_visit if (answer_visit is not None and (not answer_visit.grades.count() or force_regrade)): grade_page_visit(answer_visit, graded_at_git_commit_sha=fctx.flow_commit_sha)
def grade_page_visits(fctx, flow_session, answer_visits): for i in range(len(answer_visits)): answer_visit = answer_visits[i] if answer_visit is not None: answer_visit.is_graded_answer = True answer_visit.save() else: page_data = flow_session.page_data.get(ordinal=i) page = instantiate_flow_page_with_ctx(fctx, page_data) if not page.expects_answer(): continue # Create a synthetic visit to attach a grade answer_visit = FlowPageVisit() answer_visit.flow_session = flow_session answer_visit.page_data = page_data answer_visit.is_synthetic = True answer_visit.answer = None answer_visit.is_graded_answer = True answer_visit.save() answer_visits[i] = answer_visit if answer_visit is not None: grade_page_visit(answer_visit)
def view_flow_page(pctx, flow_identifier, ordinal): request = pctx.request flow_session = find_current_flow_session(request, 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)) page_context = fpctx.page_context page_data = fpctx.page_data if flow_permission.view not in current_access_rule.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 current_access_rule.permissions): raise PermissionDenied("already have final answer") form, form_html = fpctx.page.post_form( fpctx.page_context, fpctx.page_data.data, post_data=request.POST, files_data=request.POST) 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'] page_visit.answer = fpctx.page.answer_data( fpctx.page_context, fpctx.page_data.data, form) 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 current_access_rule.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( current_access_rule.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( current_access_rule.permissions)): return redirect("course.flow.finish_flow_session_view", pctx.course.identifier, flow_identifier) else: form, form_html = 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 current_access_rule.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 feedback = fpctx.prev_answer_visit.get_most_recent_feedback() else: answer_data = None feedback = None form, form_html = fpctx.page.make_form( page_context, page_data.data, answer_data, not may_change_answer) else: form = None form_html = 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, current_access_rule.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 current_access_rule.permissions or ( (flow_permission.see_correctness_after_completion in current_access_rule.permissions) and not flow_session.in_progress)) show_answer = flow_permission.see_answer in current_access_rule.permissions if show_correctness or show_answer: shown_feedback = feedback title = fpctx.page.title(page_context, page_data.data) body = fpctx.page.body(page_context, page_data.data) # {{{ render flow page if form is not None and form_html is None: from crispy_forms.utils import render_crispy_form from django.template import RequestContext context = RequestContext(request, {}) form_html = render_crispy_form(form, context=context) del context 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, "show_correctness": show_correctness, "may_change_answer": may_change_answer, "will_receive_feedback": will_receive_feedback(current_access_rule.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)
def view_flow_page(request, course_identifier, flow_identifier, ordinal): flow_session = find_current_flow_session(request, 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", course_identifier, flow_identifier) fpctx = FlowPageContext(request, course_identifier, flow_identifier, ordinal, flow_session) page_context = fpctx.page_context page_data = fpctx.page_data if flow_permission.view not in fpctx.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", 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 fpctx.permissions): raise PermissionDenied("already have final answer") form, form_html = fpctx.page.post_form( fpctx.page_context, fpctx.page_data.data, post_data=request.POST, files_data=request.POST) 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 = fpctx.flow_session page_visit.page_data = fpctx.page_data page_visit.remote_address = request.META['REMOTE_ADDR'] page_visit.answer = fpctx.page.answer_data( fpctx.page_context, fpctx.page_data.data, form) 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 fpctx.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) 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 fpctx.will_receive_feedback()): return redirect("course.flow.view_flow_page", course_identifier, flow_identifier, fpctx.ordinal + 1) elif (pressed_button == "save_and_finish" and not fpctx.will_receive_feedback()): return redirect("course.flow.finish_flow_session_view", course_identifier, flow_identifier) else: form, form_html = 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 fpctx.create_visit(request) 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: fpctx.create_visit(request) 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 fpctx.permissions) # can happen if no answer was ever saved and fpctx.flow_session.in_progress) if fpctx.page.expects_answer(): if fpctx.prev_answer_visit is not None: answer_data = fpctx.prev_answer_visit.answer feedback = fpctx.prev_answer_visit.get_most_recent_feedback() else: answer_data = None feedback = None form, form_html = fpctx.page.make_form( page_context, page_data.data, answer_data, not may_change_answer) else: form = None form_html = 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(fpctx, form) 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 fpctx.permissions show_answer = flow_permission.see_answer in fpctx.permissions if show_correctness or show_answer: shown_feedback = feedback title = fpctx.page.title(page_context, page_data.data) body = fpctx.page.body(page_context, page_data.data) # {{{ render flow page if form is not None and form_html is None: from crispy_forms.utils import render_crispy_form from django.template import RequestContext context = RequestContext(request, {}) form_html = render_crispy_form(form, context=context) del context args = { "course": fpctx.course, "course_desc": fpctx.course_desc, "flow_identifier": fpctx.flow_identifier, "flow_desc": fpctx.flow_desc, "ordinal": fpctx.ordinal, "page_data": fpctx.page_data, "percentage": fpctx.percentage_done, "flow_session": fpctx.flow_session, "participation": fpctx.participation, "role": fpctx.role, "participation_role": participation_role, "title": title, "body": body, "form": form, "form_html": form_html, "feedback": shown_feedback, "show_correctness": show_correctness, "may_change_answer": may_change_answer, "will_receive_feedback": fpctx.will_receive_feedback(), "show_answer": show_answer, } if fpctx.page.expects_answer(): args["max_points"] = fpctx.page.max_points(fpctx.page_data) return render(request, "course/flow-page.html", args)