Newer
Older
and flow_permission.change_answer
not in permissions):
messages.add_message(request, messages.ERROR,
_("Already have final answer."))
submission_allowed = False
page_behavior = get_page_behavior(
page=fpctx.page,
permissions=permissions,
session_in_progress=flow_session.in_progress,
answer_was_graded=False,
generates_grade=generates_grade,
is_unenrolled_session=flow_session.participation is None)
form = fpctx.page.process_form_post(
post_data=request.POST, files_data=request.FILES,
page_behavior=page_behavior)
pressed_button = get_pressed_button(form)
if submission_allowed and form.is_valid():
# {{{ form validated, process answer
messages.add_message(request, messages.SUCCESS,
_("Answer saved."))
Dong Zhuang
committed
answer_visit = FlowPageVisit()
answer_visit.flow_session = flow_session
answer_visit.page_data = fpctx.page_data
answer_visit.remote_address = request.META['REMOTE_ADDR']
answer_data = answer_visit.answer = fpctx.page.answer_data(
Dong Zhuang
committed
answer_visit.is_submitted_answer = pressed_button == "submit"
if hasattr(request, "relate_impersonate_original_user"):
answer_visit.impersonated_by = \
request.relate_impersonate_original_user
answer_visit.save()
prev_answer_visits.insert(0, answer_visit)
answer_was_graded = answer_visit.is_submitted_answer
page_behavior = get_page_behavior(
page=fpctx.page,
permissions=permissions,
session_in_progress=flow_session.in_progress,
answer_was_graded=answer_was_graded,
generates_grade=generates_grade,
is_unenrolled_session=flow_session.participation is None)
if fpctx.page.is_answer_gradable():
with translation.override(settings.RELATE_ADMIN_EMAIL_LOCALE):
feedback = fpctx.page.grade(
page_context, page_data.data, answer_visit.answer,
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
if answer_visit.is_submitted_answer:
grade = FlowPageVisitGrade()
grade.visit = answer_visit
grade.max_points = fpctx.page.max_points(page_data.data)
grade.graded_at_git_commit_sha = fpctx.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",
fpctx.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",
fpctx.course.identifier, flow_session.id)
else:
# The form needs to be recreated here, although there
# already is a form from the process_form_post above. This
# is because the value of 'answer_was_graded' may have
# changed between then and now (and page_behavior with
# it)--and that value depends on form validity, which we
# can only decide once we have a form.
form = fpctx.page.make_form(
page_context, page_data.data,
answer_data, page_behavior)
# }}}
else:
# form did not validate
Dong Zhuang
committed
create_flow_page_visit(request, flow_session, fpctx.page_data)
answer_data = None
answer_was_graded = False
if prev_answer_visits:
answer_data = prev_answer_visits[0].answer
feedback = None
messages.add_message(request, messages.ERROR,
_("Failed to submit answer."))
return (
page_behavior,
prev_answer_visits,
form,
feedback,
answer_data,
answer_was_graded)
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
# }}}
# {{{ view: update page bookmark state
@course_view
def update_page_bookmark_state(pctx, flow_session_id, ordinal):
if pctx.request.method != "POST":
raise SuspiciousOperation(_("only POST allowed"))
flow_session = get_object_or_404(FlowSession, id=flow_session_id)
if flow_session.participation != pctx.participation:
raise PermissionDenied(
_("may only change your own flow sessions"))
bookmark_state = pctx.request.POST.get("bookmark_state")
if bookmark_state not in ["0", "1"]:
raise SuspiciousOperation(_("invalid bookmark state"))
bookmark_state = bookmark_state == "1"
fpd = get_object_or_404(FlowPageData.objects,
flow_session=flow_session,
ordinal=ordinal)
fpd.bookmarked = bookmark_state
fpd.save()
return http.HttpResponse("OK")
# }}}
# {{{ view: update expiration mode
@course_view
def update_expiration_mode(pctx, flow_session_id):
Andreas Klöckner
committed
# type: (CoursePageContext, int) -> http.HttpRequest
if pctx.request.method != "POST":
raise SuspiciousOperation(_("only POST allowed"))
login_exam_ticket = get_login_exam_ticket(pctx.request)
flow_session = get_object_or_404(FlowSession, id=flow_session_id)
if flow_session.participation != pctx.participation:
raise PermissionDenied(
_("may only change your own flow sessions"))
if not flow_session.in_progress:
raise PermissionDenied(
_("may only change in-progress flow sessions"))
expmode = pctx.request.POST.get("expiration_mode")
if not any(expmode == em_key
for em_key, _ in FLOW_SESSION_EXPIRATION_MODE_CHOICES):
raise SuspiciousOperation(_("invalid expiration mode"))
fctx = FlowContext(pctx.repo, pctx.course, flow_session.flow_id,
Andreas Klöckner
committed
participation=pctx.participation)
access_rule = get_session_access_rule(
flow_session, fctx.flow_desc,
get_now_or_fake_time(pctx.request),
facilities=pctx.request.relate_facilities,
login_exam_ticket=login_exam_ticket)
if is_expiration_mode_allowed(expmode, access_rule.permissions):
flow_session.expiration_mode = expmode
flow_session.save()
return http.HttpResponse("OK")
else:
raise PermissionDenied()
# {{{ view: finish flow
def finish_flow_session_view(pctx, flow_session_id):
Andreas Klöckner
committed
# type: (CoursePageContext, int) -> http.HttpResponse
Andreas Klöckner
committed
# Does not need to be atomic: All writing to the db
# is done in 'finish_flow_session' below.
Andreas Klöckner
committed
now_datetime = get_now_or_fake_time(pctx.request)
login_exam_ticket = get_login_exam_ticket(pctx.request)
Andreas Klöckner
committed
request = pctx.request
flow_session_id = int(flow_session_id)
flow_session = get_and_check_flow_session(
pctx, flow_session_id)
fctx = FlowContext(pctx.repo, pctx.course, flow_id,
Andreas Klöckner
committed
participation=pctx.participation)
access_rule = get_session_access_rule(
flow_session, fctx.flow_desc, now_datetime,
facilities=pctx.request.relate_facilities,
login_exam_ticket=login_exam_ticket)
from course.content import markup_to_html
completion_text = markup_to_html(
fctx.course, fctx.repo, pctx.course_commit_sha,
getattr(fctx.flow_desc, "completion_text", ""))
Andreas Klöckner
committed
adjust_flow_session_page_data(pctx.repo, flow_session, pctx.course.identifier,
fctx.flow_desc, respect_preview=True)
Andreas Klöckner
committed
answer_visits = assemble_answer_visits(flow_session) # type: List[Optional[FlowPageVisit]] # noqa
(answered_page_data_list, unanswered_page_data_list) =\
get_session_answered_page_data(
fctx, flow_session, answer_visits)
answered_count = len(answered_page_data_list)
unanswered_count = len(unanswered_page_data_list)
is_interactive_flow = bool(answered_count + unanswered_count) # type: bool
if flow_permission.view not in access_rule.permissions:
raise PermissionDenied()
def render_finish_response(template, **kwargs):
Andreas Klöckner
committed
# type: (...) -> http.HttpResponse
"flow_desc": fctx.flow_desc,
}
render_args.update(kwargs)
return render_course_page(
pctx, template, render_args,
allow_instant_flow_requests=False)
grading_rule = get_session_grading_rule(
flow_session, fctx.flow_desc, now_datetime)
if request.method == "POST":
if "submit" not in request.POST:
raise SuspiciousOperation(_("odd POST parameters"))
if not flow_session.in_progress:
messages.add_message(request, messages.ERROR,
_("Cannot end a session that's already ended"))
if flow_permission.end_session not in access_rule.permissions:
raise PermissionDenied(
_("not permitted to end session"))
Andreas Klöckner
committed
grade_info = finish_flow_session(
fctx, flow_session, grading_rule,
Andreas Klöckner
committed
now_datetime=now_datetime)
# {{{ send notify email if requested
if (hasattr(fctx.flow_desc, "notify_on_submit")
and fctx.flow_desc.notify_on_submit):
if (grading_rule.grade_identifier
and flow_session.participation is not None):
from course.models import get_flow_grading_opportunity
review_uri = reverse("relate-view_single_grade",
args=(
pctx.course.identifier,
flow_session.participation.id,
get_flow_grading_opportunity(
pctx.course, flow_session.flow_id, fctx.flow_desc,
grading_rule.grade_identifier,
grading_rule.grade_aggregation_strategy).id))
else:
review_uri = reverse("relate-view_flow_page",
args=(
pctx.course.identifier,
flow_session.id,
0))
with translation.override(settings.RELATE_ADMIN_EMAIL_LOCALE):
from django.template.loader import render_to_string
message = render_to_string("course/submit-notify.txt", {
"course": fctx.course,
"flow_session": flow_session,
"review_uri": pctx.request.build_absolute_uri(review_uri)
})
from django.core.mail import EmailMessage
msg = EmailMessage(
string_concat("[%(identifier)s:%(flow_id)s] ",
_("Submission by %(participation)s"))
% {'participation': flow_session.participation,
'identifier': fctx.course.identifier,
'flow_id': flow_session.flow_id},
message,
getattr(settings, "NOTIFICATION_EMAIL_FROM",
settings.ROBOT_EMAIL_FROM),
fctx.flow_desc.notify_on_submit)
msg.bcc = [fctx.course.notify_email]
from relate.utils import get_outbound_mail_connection
get_outbound_mail_connection("notification")
if hasattr(settings, "NOTIFICATION_EMAIL_FROM")
else get_outbound_mail_connection("robot"))
if is_interactive_flow:
if flow_permission.cannot_see_flow_result in access_rule.permissions:
grade_info = None
return render_finish_response(
"course/flow-completion-grade.html",
completion_text=completion_text,
grade_info=grade_info)
else:
return render_finish_response(
"course/flow-completion.html",
last_page_nr=None,
flow_session=flow_session,
completion_text=completion_text)
if (not is_interactive_flow
(flow_session.in_progress
and flow_permission.end_session not in access_rule.permissions)):
# No ability to end--just show completion page.
return render_finish_response(
"course/flow-completion.html",
flow_session=flow_session,
completion_text=completion_text)
elif not flow_session.in_progress:
# Just reviewing: re-show grades.
grade_info = gather_grade_info(
fctx, flow_session, grading_rule, answer_visits)
if flow_permission.cannot_see_flow_result in access_rule.permissions:
grade_info = None
return render_finish_response(
"course/flow-completion-grade.html",
completion_text=completion_text,
grade_info=grade_info)
else:
# confirm ending flow
return render_finish_response(
"course/flow-confirm-completion.html",
flow_session=flow_session,
answered_count=answered_count,
unanswered_count=unanswered_count,
unanswered_page_data_list=unanswered_page_data_list,
total_count=answered_count+unanswered_count)
# {{{ view: regrade flow
class RegradeFlowForm(StyledForm):
def __init__(self, flow_ids, *args, **kwargs):
Andreas Klöckner
committed
# type: (List[Text], *Any, **Any) -> None
super(RegradeFlowForm, self).__init__(*args, **kwargs)
self.fields["flow_id"] = forms.ChoiceField(
choices=[(fid, fid) for fid in flow_ids],
label=_("Flow ID"),
widget=Select2Widget())
self.fields["access_rules_tag"] = forms.CharField(
help_text=_("If non-empty, limit the regrading to sessions "
"started under this access rules tag."),
self.fields["regraded_session_in_progress"] = forms.ChoiceField(
choices=(
_("Regrade in-progress and not-in-progress sessions")),
_("Regrade in-progress sessions only")),
_("Regrade not-in-progress sessions only")),
),
label=_("Regraded session in progress"))
Andreas Klöckner
committed
# type: (CoursePageContext) -> http.HttpResponse
if not pctx.has_permission(pperm.batch_regrade_flow_session):
Andreas Klöckner
committed
raise PermissionDenied(_("may not batch-regrade flows"))
from course.content import list_flow_ids
flow_ids = list_flow_ids(pctx.repo, pctx.course_commit_sha)
request = pctx.request
if request.method == "POST":
form = RegradeFlowForm(flow_ids, request.POST, request.FILES)
if form.is_valid():
inprog_value = {
"any": None,
"yes": True,
"no": False,
}[form.cleaned_data["regraded_session_in_progress"]]
from course.tasks import regrade_flow_sessions
async_res = regrade_flow_sessions.delay(
pctx.course.id,
form.cleaned_data["flow_id"],
form.cleaned_data["access_rules_tag"],
inprog_value)
return redirect("relate-monitor_task", async_res.id)
else:
form = RegradeFlowForm(flow_ids)
return render_course_page(pctx, "course/generic-course-form.html", {
"form": form,
"form_text": string_concat(
"<p>",
_("This regrading process is only intended for flows that do"
"not show up in the grade book. If you would like to regrade"
"for-credit flows, use the corresponding functionality in "
"form_description": _("Regrade not-for-credit Flow Sessions"),