Newer
Older
},
allow_instant_flow_requests=False)
@retry_transaction_decorator(serializable=True)
def post_start_flow(pctx, fctx, flow_id):
now_datetime = get_now_or_fake_time(pctx.request)
past_sessions = (FlowSession.objects
.filter(
participation=pctx.participation,
flow_id=fctx.flow_id,
participation__isnull=False)
.order_by("start_time"))
if past_sessions:
latest_session = past_sessions.reverse()[0]
cooldown_seconds = getattr(
settings, "RELATE_SESSION_RESTART_COOLDOWN_SECONDS", 10)
from datetime import timedelta
if (
timedelta(seconds=0)
<= (now_datetime - latest_session.start_time)
return redirect("relate-view_flow_page",
pctx.course.identifier, latest_session.id, 0)
session_start_rule = get_session_start_rule(pctx.course, pctx.participation,
pctx.role, flow_id, fctx.flow_desc, now_datetime,
facilities=pctx.request.relate_facilities)
if not session_start_rule.may_start_new_session:
raise PermissionDenied(_("new session not allowed"))
flow_user = pctx.request.user
if not flow_user.is_authenticated():
flow_user = None
session = start_flow(
pctx.repo, pctx.course, pctx.participation,
user=flow_user,
flow_id=flow_id, flow_desc=fctx.flow_desc,
access_rules_tag=session_start_rule.tag_session,
now_datetime=now_datetime)
access_rule = get_session_access_rule(
session, pctx.role, fctx.flow_desc, now_datetime,
facilities=pctx.request.relate_facilities)
lock_down_if_needed(pctx.request, access_rule.permissions, session)
return redirect("relate-view_flow_page",
pctx.course.identifier, session.id, 0)
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
# {{{ view: resume flow
# The purpose of this interstitial redirection page is to set the exam
# lockdown flag upon resumption/review. Without this, the exam lockdown
# middleware will refuse access to flow pages in a locked-down facility.
@course_view
def view_resume_flow(pctx, flow_session_id):
now_datetime = get_now_or_fake_time(pctx.request)
flow_session = get_and_check_flow_session(pctx, int(flow_session_id))
fctx = FlowContext(pctx.repo, pctx.course, flow_session.flow_id,
participation=pctx.participation)
access_rule = get_session_access_rule(
flow_session, pctx.role, fctx.flow_desc, now_datetime,
facilities=pctx.request.relate_facilities)
lock_down_if_needed(pctx.request, access_rule.permissions,
flow_session)
return redirect("relate-view_flow_page",
pctx.course.identifier, flow_session.id, 0)
# }}}
# {{{ view: flow page
def get_and_check_flow_session(pctx, flow_session_id):
try:
flow_session = FlowSession.objects.get(id=flow_session_id)
except ObjectDoesNotExist:
raise http.Http404()
if pctx.role in [
participation_role.instructor,
participation_role.teaching_assistant]:
pass
elif pctx.role in [
participation_role.student,
participation_role.observer,
participation_role.auditor,
if (pctx.participation != flow_session.participation
and flow_session.participation is not None):
raise PermissionDenied(_("may not view other people's sessions"))
Andreas Klöckner
committed
if (flow_session.user is not None
and pctx.request.user != flow_session.user):
raise PermissionDenied(_("may not view other people's sessions"))
else:
raise PermissionDenied()
if flow_session.course.pk != pctx.course.pk:
raise SuspiciousOperation()
return flow_session
def will_receive_feedback(permissions):
return (
flow_permission.see_correctness in permissions
or flow_permission.see_answer_after_submission in permissions)
Andreas Klöckner
committed
def get_page_behavior(page, permissions, session_in_progress, answer_was_graded,
generates_grade, is_unenrolled_session, viewing_prior_version=False):
show_correctness = None
show_answer = None
if 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
elif 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)
not viewing_prior_version
and (not answer_was_graded
or (flow_permission.change_answer in permissions))
# can happen if no answer was ever saved
and session_in_progress
and (flow_permission.submit_answer in permissions)
and (generates_grade and not is_unenrolled_session
or (not generates_grade))
)
from course.page.base import PageBehavior
return PageBehavior(
show_correctness=show_correctness,
show_answer=show_answer,
def add_buttons_to_form(form, fpctx, flow_session, permissions):
from crispy_forms.layout import Submit
show_save_button = getattr(form, "show_save_button", True)
if show_save_button:
form.helper.add_input(
Submit("save", _("Save answer"),
css_class="relate-save-button"))
if will_receive_feedback(permissions):
if flow_permission.change_answer in permissions:
"submit", _("Submit answer for feedback"),
accesskey="g",
css_class="relate-save-button relate-submit-button"))
form.helper.add_input(
css_class="relate-save-button relate-submit-button"))
else:
# Only offer 'save and move on' if student will receive no feedback
if fpctx.page_data.ordinal + 1 < flow_session.page_count:
Submit("save_and_next",
mark_safe_lazy(
string_concat(
_("Save answer and move on"),
" »")),
else:
form.helper.add_input(
Submit("save_and_finish",
mark_safe_lazy(
string_concat(
_("Save answer and finish"),
" »")),
def create_flow_page_visit(request, flow_session, page_data):
FlowPageVisit(
flow_session=flow_session,
page_data=page_data,
remote_address=request.META['REMOTE_ADDR'],
is_submitted_answer=None).save()
@course_view
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)
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,
try:
fpctx = FlowPageContext(pctx.repo, pctx.course, flow_id, ordinal,
participation=pctx.participation,
flow_session=flow_session,
request=pctx.request)
except PageOrdinalOutOfRange:
return redirect("relate-view_flow_page",
pctx.course.identifier,
flow_session.id,
flow_session.page_count-1)
Andreas Klöckner
committed
now_datetime = get_now_or_fake_time(request)
access_rule = get_session_access_rule(
Andreas Klöckner
committed
flow_session, pctx.role, fpctx.flow_desc, now_datetime,
facilities=pctx.request.relate_facilities)
Andreas Klöckner
committed
grading_rule = get_session_grading_rule(
flow_session, pctx.role, fpctx.flow_desc, now_datetime)
generates_grade = (
grading_rule.grade_identifier is not None
and
grading_rule.generates_grade)
Andreas Klöckner
committed
del grading_rule
permissions = fpctx.page.get_modified_permissions_for_page(
access_rule.permissions)
if access_rule.message:
messages.add_message(request, messages.INFO, access_rule.message)
lock_down_if_needed(pctx.request, permissions, flow_session)
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"))
answer_visit = None
prev_visit_id = None
if request.method == "POST":
if "finish" in request.POST:
return redirect("relate-finish_flow_session_view",
pctx.course.identifier, flow_session_id)
post_result = post_flow_page(
flow_session, fpctx, request, permissions, generates_grade)
if not isinstance(post_result, tuple):
# ought to be an HTTP response
return post_result
(
page_behavior,
prev_answer_visits,
form,
feedback,
answer_data,
answer_was_graded) = post_result
# continue at common flow page generation below
create_flow_page_visit(request, flow_session, fpctx.page_data)
prev_answer_visits = list(
get_prev_answer_visits_qset(fpctx.page_data))
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
# {{{ fish out previous answer_visit
prev_visit_id = pctx.request.GET.get("visit_id")
if prev_visit_id is not None:
try:
prev_visit_id = int(prev_visit_id)
except ValueError:
raise SuspiciousOperation("non-integer passed for 'visit_id'")
viewing_prior_version = False
if prev_answer_visits and prev_visit_id is not None:
answer_visit = prev_answer_visits[0]
for ivisit, pvisit in enumerate(prev_answer_visits):
if pvisit.id == prev_visit_id:
answer_visit = pvisit
if ivisit > 0:
viewing_prior_version = True
break
if viewing_prior_version:
from django.template import defaultfilters
from relate.utils import as_local_time
messages.add_message(request, messages.INFO,
_("Viewing prior submission dated %(date)s.")
% {
"date": defaultfilters.date(
as_local_time(pvisit.visit_time),
"DATETIME_FORMAT"),
})
prev_visit_id = answer_visit.id
elif prev_answer_visits:
answer_visit = prev_answer_visits[0]
prev_visit_id = answer_visit.id
else:
answer_visit = None
# }}}
if answer_visit is not None:
answer_was_graded = answer_visit.is_submitted_answer
else:
answer_was_graded = False
page_behavior = get_page_behavior(
page=fpctx.page,
permissions=permissions,
session_in_progress=flow_session.in_progress,
Andreas Klöckner
committed
answer_was_graded=answer_was_graded,
generates_grade=generates_grade,
is_unenrolled_session=flow_session.participation is None,
viewing_prior_version=viewing_prior_version)
if answer_visit is not None:
answer_data = answer_visit.answer
most_recent_grade = 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
try:
form = fpctx.page.make_form(
page_context, page_data.data,
answer_data, page_behavior)
except InvalidPageData as e:
messages.add_message(request, messages.ERROR,
ugettext(
"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", {})
feedback = None
# start common flow page generation
# defined at this point:
# form, page_behavior, answer_was_graded, feedback
# answer_data, grade_data
if form is not None and page_behavior.may_change_answer:
form = add_buttons_to_form(form, fpctx, flow_session,
shown_feedback = None
if (fpctx.page.expects_answer() and answer_was_graded
and (
page_behavior.show_correctness
or page_behavior.show_answer)):
shown_feedback = feedback
title = fpctx.page.title(page_context, page_data.data)
body = fpctx.page.body(page_context, page_data.data)
if page_behavior.show_answer:
correct_answer = fpctx.page.correct_answer(
page_context, page_data.data,
answer_data, grade_data)
else:
correct_answer = None
and flow_session.participation is None
and flow_permission.submit_answer in permissions):
messages.add_message(request, messages.INFO,
_("Changes to this session are being prevented "
"because this session yields a permanent grade, but "
"you have not completed your enrollment process in "
"this course."))
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))
session_minutes = None
time_factor = 1
if flow_permission.see_session_time in permissions:
session_minutes = (
now_datetime - flow_session.start_time).total_seconds() / 60
if flow_session.participation is not None:
time_factor = flow_session.participation.time_factor
all_page_data = get_all_page_data(flow_session)
"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,
"feedback": shown_feedback,
"correct_answer": correct_answer,
"show_correctness": page_behavior.show_correctness,
"may_change_answer": page_behavior.may_change_answer,
"may_change_graded_answer": (
page_behavior.may_change_answer
and
(flow_permission.change_answer in permissions)),
"will_receive_feedback": will_receive_feedback(permissions),
"show_answer": page_behavior.show_answer,
"session_minutes": session_minutes,
"time_factor": time_factor,
"expiration_mode_choices": expiration_mode_choices,
"expiration_mode_choice_count": len(expiration_mode_choices),
"expiration_mode": flow_session.expiration_mode,
"flow_session_interaction_kind": flow_session_interaction_kind,
fpctx, flow_session, generates_grade, all_page_data),
"prev_answer_visits": prev_answer_visits,
"prev_visit_id": prev_visit_id,
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 get_pressed_button(form):
buttons = ["save", "save_and_next", "save_and_finish", "submit"]
for button in buttons:
if button in form.data:
return button
raise SuspiciousOperation(_("could not find which button was pressed"))
@retry_transaction_decorator()
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
def post_flow_page(flow_session, fpctx, request, permissions, generates_grade):
page_context = fpctx.page_context
page_data = fpctx.page_data
prev_answer_visits = list(
get_prev_answer_visits_qset(fpctx.page_data))
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 (prev_answer_visits
and prev_answer_visits[0].is_submitted_answer
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(
fpctx.page_context, fpctx.page_data.data,
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."))
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(
fpctx.page_context, fpctx.page_data.data,
form, request.FILES)
answer_visit.is_submitted_answer = pressed_button == "submit"
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,
grade_data=None)
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
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
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)
@course_view
def update_expiration_mode(pctx, flow_session_id):
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"))
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,
participation=pctx.participation,
flow_session=flow_session)
access_rule = get_session_access_rule(
flow_session, pctx.role, fctx.flow_desc,
get_now_or_fake_time(pctx.request),
facilities=pctx.request.relate_facilities)
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
@retry_transaction_decorator()
def finish_flow_session_view(pctx, flow_session_id):
Andreas Klöckner
committed
now_datetime = get_now_or_fake_time(pctx.request)
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,
participation=pctx.participation,
flow_session=flow_session)
access_rule = get_session_access_rule(
flow_session, pctx.role, fctx.flow_desc, now_datetime,
facilities=pctx.request.relate_facilities)
answer_visits = assemble_answer_visits(flow_session)
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", ""))
(answered_count, unanswered_count) = count_answered_gradable(
fctx, flow_session, answer_visits)
is_graded_flow = bool(answered_count + unanswered_count)
if flow_permission.view not in access_rule.permissions:
raise PermissionDenied()
def render_finish_response(template, **kwargs):
render_args = {
"flow_desc": fctx.flow_desc,
}
render_args.update(kwargs)
return render_course_page(
pctx, template, render_args,
allow_instant_flow_requests=False)
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"))
grading_rule = get_session_grading_rule(
flow_session, pctx.role, fctx.flow_desc, now_datetime)
Andreas Klöckner
committed
grade_info = finish_flow_session(
fctx, flow_session, grading_rule,
Andreas Klöckner
committed
now_datetime=now_datetime)
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
# {{{ 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).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,
fctx.course.from_email,
fctx.flow_desc.notify_on_submit)
msg.bcc = [fctx.course.notify_email]
msg.send()
# }}}
if is_graded_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)
(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, 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,
total_count=answered_count+unanswered_count)
# {{{ view: regrade flow
class RegradeFlowForm(StyledForm):
def __init__(self, flow_ids, *args, **kwargs):
super(RegradeFlowForm, self).__init__(*args, **kwargs)
self.fields["flow_id"] = forms.ChoiceField(
choices=[(fid, fid) for fid in flow_ids],
initial=participation_role.student,
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"))
if pctx.role != participation_role.instructor:
raise PermissionDenied(_("must be instructor to 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"),