def serialize(obj): return { "id": obj.id, "visit_time": ( format_datetime_local(as_local_time(obj.visit.visit_time))), "grade_time": format_datetime_local(as_local_time(obj.grade_time)), "value": obj.value(), }
def view_reopen_session(pctx, flow_session_id, opportunity_id): if pctx.role not in [ participation_role.instructor, participation_role.teaching_assistant ]: raise PermissionDenied() request = pctx.request session = get_object_or_404(FlowSession, id=int(flow_session_id)) from course.content import get_flow_desc try: flow_desc = get_flow_desc(pctx.repo, pctx.course, session.flow_id, pctx.course_commit_sha) except ObjectDoesNotExist: raise http.Http404() if request.method == "POST": form = ReopenSessionForm(flow_desc, session.access_rules_tag, request.POST, request.FILES) if form.is_valid(): new_access_rules_tag = form.cleaned_data["set_access_rules_tag"] if new_access_rules_tag == NONE_SESSION_TAG: new_access_rules_tag = None session.access_rules_tag = new_access_rules_tag from relate.utils import (local_now, as_local_time, format_datetime_local) session.append_comment( ugettext("Session reopened at %(now)s by %(user)s, " "previous completion time was '%(completion_time)s': " "%(comment)s.") % { "now": format_datetime_local(local_now()), "user": pctx.request.user, "completion_time": format_datetime_local( as_local_time(session.completion_time)), "comment": form.cleaned_data["comment"] }) session.save() from course.flow import reopen_session reopen_session(session, suppress_log=True) return redirect("relate-view_single_grade", pctx.course.identifier, session.participation.id, opportunity_id) else: form = ReopenSessionForm(flow_desc, session.access_rules_tag) return render(request, "generic-form.html", { "form": form, "form_description": _("Reopen session") })
def view_reopen_session(pctx, flow_session_id, opportunity_id): if pctx.role not in [ participation_role.instructor, participation_role.teaching_assistant]: raise PermissionDenied() request = pctx.request session = get_object_or_404(FlowSession, id=int(flow_session_id)) from course.content import get_flow_desc try: flow_desc = get_flow_desc(pctx.repo, pctx.course, session.flow_id, pctx.course_commit_sha) except ObjectDoesNotExist: raise http.Http404() if request.method == "POST": form = ReopenSessionForm(flow_desc, session.access_rules_tag, request.POST, request.FILES) if form.is_valid(): new_access_rules_tag = form.cleaned_data["set_access_rules_tag"] if new_access_rules_tag == NONE_SESSION_TAG: new_access_rules_tag = None session.access_rules_tag = new_access_rules_tag from relate.utils import ( local_now, as_local_time, format_datetime_local) session.append_comment( ugettext("Session reopened at %(now)s by %(user)s, " "previous completion time was '%(completion_time)s': " "%(comment)s.") % { "now": format_datetime_local(local_now()), "user": pctx.request.user, "completion_time": format_datetime_local( as_local_time(session.completion_time)), "comment": form.cleaned_data["comment"] }) session.save() from course.flow import reopen_session reopen_session(session, suppress_log=True) return redirect("relate-view_single_grade", pctx.course.identifier, session.participation.id, opportunity_id) else: form = ReopenSessionForm(flow_desc, session.access_rules_tag) return render(request, "generic-form.html", { "form": form, "form_description": _("Reopen session") })
def strify_session_for_exception(session): from relate.utils import as_local_time result = ("started at %s" % as_local_time(session.start_time) .strftime('%b %d %Y - %I:%M %p')) if session.access_rules_tag: result += " tagged '%s'" % session.access_rules_tag return result
def strify_session_for_exception(session): from relate.utils import as_local_time, format_datetime_local # Translators: %s is the string of the start time of a session. result = (_("started at %s") % format_datetime_local(as_local_time(session.start_time))) if session.access_rules_tag: result += " tagged '%s'" % session.access_rules_tag return result
def strify_session_for_exception(session): from relate.utils import as_local_time, format_datetime_local # Translators: %s is the string of the start time of a session. result = (_("started at %s") % format_datetime_local( as_local_time(session.start_time))) if session.access_rules_tag: result += " tagged '%s'" % session.access_rules_tag return result
def reopen_session(session, force=False, suppress_log=False): if session.in_progress: raise RuntimeError(_("Can't reopen a session that's already in progress")) if session.participation is None: raise RuntimeError(_("Can't reopen anonymous sessions")) session.in_progress = True session.points = None session.max_points = None if not suppress_log: session.append_comment( _("Session reopened at %(now)s, previous completion time " "was '%(complete_time)s'.") % { "now": format_datetime_local(local_now()), "complete_time": format_datetime_local(as_local_time(session.completion_time)), } ) session.completion_time = None session.save()
def test_all_day_event(self): self.switch_to_fake_commit_sha() # lecture 2, no end_time lecture2_start_time = datetime.datetime(2019, 1, 1, tzinfo=pytz.UTC) self.mock_get_now_or_fake_time.return_value = ( lecture2_start_time + timedelta(minutes=5)) lecture2_evt = factories.EventFactory( kind=self.default_event_kind, course=self.course, all_day=True, time=lecture2_start_time, ordinal=2) # lecture 3 lecture3_start_time = lecture2_start_time + timedelta(weeks=1) factories.EventFactory( kind=self.default_event_kind, course=self.course, time=lecture3_start_time, ordinal=3) resp = self.get_course_calendar_view() events_json = json.loads(resp.context["events_json"]) self.assertEqual(len(events_json), 2) self.assertDictEqual( events_json[1], {'id': lecture2_evt.pk, 'start': lecture2_start_time.isoformat(), 'allDay': True, 'title': 'Lecture 2', 'url': '#event-%i' % lecture2_evt.pk}) # now we add end_time of lecture 2 evt to a time which is not midnight lecture2_end_time = lecture2_start_time + timedelta(hours=18) lecture2_evt.end_time = lecture2_end_time lecture2_evt.save() resp = self.get_course_calendar_view() events_json = json.loads(resp.context["events_json"]) self.assertEqual(len(events_json), 2) self.assertDictEqual( events_json[1], {'id': lecture2_evt.pk, 'start': lecture2_start_time.isoformat(), 'allDay': True, 'title': 'Lecture 2', 'url': '#event-%i' % lecture2_evt.pk, 'end': lecture2_end_time.isoformat() }) # now we update end_time of lecture 2 evt to midnight while True: local_t = as_local_time(lecture2_end_time) end_midnight = datetime.time(tzinfo=local_t.tzinfo) if local_t.time() == end_midnight: lecture2_evt.end_time = lecture2_end_time lecture2_evt.save() break lecture2_end_time += timedelta(hours=1) resp = self.get_course_calendar_view() events_json = json.loads(resp.context["events_json"]) self.assertEqual(len(events_json), 2) self.assertDictEqual( events_json[1], {'id': lecture2_evt.pk, 'start': lecture2_start_time.isoformat(), 'allDay': True, 'title': 'Lecture 2', 'url': '#event-%i' % lecture2_evt.pk, 'end': lecture2_end_time.isoformat() })
def grant_exception_stage_3(pctx, participation_id, flow_id, session_id): # type: (CoursePageContext, int, Text, int) -> http.HttpResponse if not pctx.has_permission(pperm.grant_exception): raise PermissionDenied(_("may not grant exceptions")) participation = get_object_or_404(Participation, id=participation_id) from course.content import get_flow_desc try: flow_desc = get_flow_desc(pctx.repo, pctx.course, flow_id, pctx.course_commit_sha) except ObjectDoesNotExist: raise http.Http404() session = FlowSession.objects.get(id=int(session_id)) now_datetime = get_now_or_fake_time(pctx.request) from course.utils import (get_session_access_rule, get_session_grading_rule) access_rule = get_session_access_rule(session, flow_desc, now_datetime) grading_rule = get_session_grading_rule(session, flow_desc, now_datetime) request = pctx.request if request.method == "POST": form = ExceptionStage3Form({}, flow_desc, session.access_rules_tag, request.POST) from course.constants import flow_rule_kind if form.is_valid(): permissions = [ key for key, _ in FLOW_PERMISSION_CHOICES if form.cleaned_data[key] ] from course.validation import (validate_session_access_rule, validate_session_grading_rule, ValidationContext) from relate.utils import dict_to_struct vctx = ValidationContext(repo=pctx.repo, commit_sha=pctx.course_commit_sha) flow_desc = get_flow_desc(pctx.repo, pctx.course, flow_id, pctx.course_commit_sha) tags = [] # type: List[Text] if hasattr(flow_desc, "rules"): try: from typing import Text # noqa except ImportError: Text = None # noqa tags = cast(List[Text], getattr(flow_desc.rules, "tags", [])) # type: ignore # noqa # {{{ put together access rule if form.cleaned_data["create_access_exception"]: new_access_rule = {"permissions": permissions} if (form.cleaned_data.get("restrict_to_same_tag") and session.access_rules_tag is not None): new_access_rule["if_has_tag"] = session.access_rules_tag validate_session_access_rule( vctx, ugettext("newly created exception"), dict_to_struct(new_access_rule), tags) fre_access = FlowRuleException( flow_id=flow_id, participation=participation, expiration=form.cleaned_data["access_expires"], creator=pctx.request.user, comment=form.cleaned_data["comment"], kind=flow_rule_kind.access, rule=new_access_rule) fre_access.save() # }}} new_access_rules_tag = form.cleaned_data.get( "set_access_rules_tag") if new_access_rules_tag == NONE_SESSION_TAG: new_access_rules_tag = None if session.access_rules_tag != new_access_rules_tag: session.access_rules_tag = new_access_rules_tag session.save() # {{{ put together grading rule if form.cleaned_data["create_grading_exception"]: due = form.cleaned_data["due"] if form.cleaned_data["due_same_as_access_expiration"]: due = form.cleaned_data["access_expires"] descr = ugettext("Granted excecption") if form.cleaned_data["credit_percent"] is not None: descr += string_concat(" (%.1f%% ", ugettext('credit'), ")") \ % form.cleaned_data["credit_percent"] due_local_naive = due if due_local_naive is not None: from relate.utils import as_local_time due_local_naive = (as_local_time(due_local_naive).replace( tzinfo=None)) new_grading_rule = { "description": descr, } if due_local_naive is not None: new_grading_rule["due"] = due_local_naive new_grading_rule["if_completed_before"] = due_local_naive for attr_name in [ "credit_percent", "bonus_points", "max_points", "max_points_enforced_cap", "generates_grade" ]: if form.cleaned_data[attr_name] is not None: new_grading_rule[attr_name] = form.cleaned_data[ attr_name] if (form.cleaned_data.get("restrict_to_same_tag") and session.access_rules_tag is not None): new_grading_rule["if_has_tag"] = session.access_rules_tag validate_session_grading_rule( vctx, ugettext("newly created exception"), dict_to_struct(new_grading_rule), tags, grading_rule.grade_identifier) fre_grading = FlowRuleException( flow_id=flow_id, participation=participation, creator=pctx.request.user, comment=form.cleaned_data["comment"], kind=flow_rule_kind.grading, rule=new_grading_rule) fre_grading.save() # }}} messages.add_message( pctx.request, messages.SUCCESS, ugettext("Exception granted to '%(participation)s' " "for '%(flow_id)s'.") % { 'participation': participation, 'flow_id': flow_id }) return redirect("relate-grant_exception", pctx.course.identifier) else: data = { "restrict_to_same_tag": session.access_rules_tag is not None, #"due_same_as_access_expiration": True, "due": grading_rule.due, "generates_grade": grading_rule.generates_grade, "credit_percent": grading_rule.credit_percent, "bonus_points": grading_rule.bonus_points, "max_points": grading_rule.max_points, "max_points_enforced_cap": grading_rule.max_points_enforced_cap, } for perm in access_rule.permissions: data[perm] = True form = ExceptionStage3Form(data, flow_desc, session.access_rules_tag) return render_course_page( pctx, "course/generic-course-form.html", { "form": form, "form_description": ugettext("Grant Exception"), "form_text": string_concat( "<div class='well'>", ugettext("Granting exception to '%(participation)s' " "for '%(flow_id)s' (session %(session)s)."), "</div>") % { 'participation': participation, 'flow_id': flow_id, 'session': strify_session_for_exception(session) }, })
def view_calendar(pctx): from course.content import markup_to_html, parse_date_spec from course.views import get_now_or_fake_time now = get_now_or_fake_time(pctx.request) if not pctx.has_permission(pperm.view_calendar): raise PermissionDenied(_("may not view calendar")) events_json = [] from course.content import get_raw_yaml_from_repo try: event_descr = get_raw_yaml_from_repo(pctx.repo, pctx.course.events_file, pctx.course_commit_sha) except ObjectDoesNotExist: event_descr = {} event_kinds_desc = event_descr.get("event_kinds", {}) event_info_desc = event_descr.get("events", {}) event_info_list = [] events = sorted( Event.objects .filter( course=pctx.course, shown_in_calendar=True), key=lambda evt: ( -evt.time.year, -evt.time.month, -evt.time.day, evt.time.hour, evt.time.minute, evt.time.second)) for event in events: kind_desc = event_kinds_desc.get(event.kind) human_title = six.text_type(event) event_json = { "id": event.id, "start": event.time.isoformat(), "allDay": event.all_day, } if event.end_time is not None: event_json["end"] = event.end_time.isoformat() if kind_desc is not None: if "color" in kind_desc: event_json["color"] = kind_desc["color"] if "title" in kind_desc: if event.ordinal is not None: human_title = kind_desc["title"].format(nr=event.ordinal) else: human_title = kind_desc["title"] description = None show_description = True event_desc = event_info_desc.get(six.text_type(event)) if event_desc is not None: if "description" in event_desc: description = markup_to_html( pctx.course, pctx.repo, pctx.course_commit_sha, event_desc["description"]) if "title" in event_desc: human_title = event_desc["title"] if "color" in event_desc: event_json["color"] = event_desc["color"] if "show_description_from" in event_desc: ds = parse_date_spec( pctx.course, event_desc["show_description_from"]) if now < ds: show_description = False if "show_description_until" in event_desc: ds = parse_date_spec( pctx.course, event_desc["show_description_until"]) if now > ds: show_description = False event_json["title"] = human_title if show_description and description: event_json["url"] = "#event-%d" % event.id start_time = event.time end_time = event.end_time if event.all_day: start_time = start_time.date() if end_time is not None: local_end_time = as_local_time(end_time) end_midnight = datetime.time(tzinfo=local_end_time.tzinfo) if local_end_time.time() == end_midnight: end_time = (end_time - datetime.timedelta(days=1)).date() else: end_time = end_time.date() event_info_list.append( EventInfo( id=event.id, human_title=human_title, start_time=start_time, end_time=end_time, description=description )) events_json.append(event_json) default_date = now.date() if pctx.course.end_date is not None and default_date > pctx.course.end_date: default_date = pctx.course.end_date from json import dumps return render_course_page(pctx, "course/calendar.html", { "events_json": dumps(events_json), "event_info_list": event_info_list, "default_date": default_date.isoformat(), })
def view_calendar(pctx): from course.content import markup_to_html, parse_date_spec from course.views import get_now_or_fake_time now = get_now_or_fake_time(pctx.request) if not pctx.has_permission(pperm.view_calendar): raise PermissionDenied(_("may not view calendar")) events_json = [] from course.content import get_raw_yaml_from_repo try: event_descr = get_raw_yaml_from_repo(pctx.repo, pctx.course.events_file, pctx.course_commit_sha) except ObjectDoesNotExist: event_descr = {} event_kinds_desc = event_descr.get("event_kinds", {}) event_info_desc = event_descr.get("events", {}) event_info_list = [] for event in Event.objects.filter(course=pctx.course, shown_in_calendar=True).order_by("-time"): kind_desc = event_kinds_desc.get(event.kind) human_title = six.text_type(event) event_json = {"id": event.id, "start": event.time.isoformat(), "allDay": event.all_day} if event.end_time is not None: event_json["end"] = event.end_time.isoformat() if kind_desc is not None: if "color" in kind_desc: event_json["color"] = kind_desc["color"] if "title" in kind_desc: if event.ordinal is not None: human_title = kind_desc["title"].format(nr=event.ordinal) else: human_title = kind_desc["title"] description = None show_description = True event_desc = event_info_desc.get(six.text_type(event)) if event_desc is not None: if "description" in event_desc: description = markup_to_html(pctx.course, pctx.repo, pctx.course_commit_sha, event_desc["description"]) if "title" in event_desc: human_title = event_desc["title"] if "color" in event_desc: event_json["color"] = event_desc["color"] if "show_description_from" in event_desc: ds = parse_date_spec(pctx.course, event_desc["show_description_from"]) if now < ds: show_description = False if "show_description_until" in event_desc: ds = parse_date_spec(pctx.course, event_desc["show_description_until"]) if now > ds: show_description = False event_json["title"] = human_title if show_description and description: event_json["url"] = "#event-%d" % event.id start_time = event.time end_time = event.end_time if event.all_day: start_time = start_time.date() local_end_time = as_local_time(end_time) end_midnight = datetime.time(tzinfo=local_end_time.tzinfo) if local_end_time.time() == end_midnight: end_time = (end_time - datetime.timedelta(days=1)).date() else: end_time = end_time.date() event_info_list.append( EventInfo( id=event.id, human_title=human_title, start_time=start_time, end_time=end_time, description=description, ) ) events_json.append(event_json) default_date = now.date() if pctx.course.end_date is not None and default_date > pctx.course.end_date: default_date = pctx.course.end_date from json import dumps return render_course_page( pctx, "course/calendar.html", { "events_json": dumps(events_json), "event_info_list": event_info_list, "default_date": default_date.isoformat(), }, )
def grant_exception_stage_3(pctx, participation_id, flow_id, session_id): # type: (CoursePageContext, int, Text, int) -> http.HttpResponse if not pctx.has_permission(pperm.grant_exception): raise PermissionDenied(_("may not grant exceptions")) participation = get_object_or_404(Participation, id=participation_id) from course.content import get_flow_desc try: flow_desc = get_flow_desc(pctx.repo, pctx.course, flow_id, pctx.course_commit_sha) except ObjectDoesNotExist: raise http.Http404() session = FlowSession.objects.get(id=int(session_id)) now_datetime = get_now_or_fake_time(pctx.request) from course.utils import ( get_session_access_rule, get_session_grading_rule) access_rule = get_session_access_rule(session, flow_desc, now_datetime) grading_rule = get_session_grading_rule(session, flow_desc, now_datetime) request = pctx.request if request.method == "POST": form = ExceptionStage3Form( {}, flow_desc, session.access_rules_tag, request.POST) from course.constants import flow_rule_kind if form.is_valid(): permissions = [ key for key, _ in FLOW_PERMISSION_CHOICES if form.cleaned_data[key]] from course.validation import ( validate_session_access_rule, validate_session_grading_rule, ValidationContext) from relate.utils import dict_to_struct vctx = ValidationContext( repo=pctx.repo, commit_sha=pctx.course_commit_sha) flow_desc = get_flow_desc(pctx.repo, pctx.course, flow_id, pctx.course_commit_sha) tags = None if hasattr(flow_desc, "rules"): tags = getattr(flow_desc.rules, "tags", None) # {{{ put together access rule if form.cleaned_data["create_access_exception"]: new_access_rule = {"permissions": permissions} if (form.cleaned_data.get("restrict_to_same_tag") and session.access_rules_tag is not None): new_access_rule["if_has_tag"] = session.access_rules_tag validate_session_access_rule( vctx, ugettext("newly created exception"), dict_to_struct(new_access_rule), tags) fre_access = FlowRuleException( flow_id=flow_id, participation=participation, expiration=form.cleaned_data["access_expires"], creator=pctx.request.user, comment=form.cleaned_data["comment"], kind=flow_rule_kind.access, rule=new_access_rule) fre_access.save() # }}} new_access_rules_tag = form.cleaned_data.get("set_access_rules_tag") if new_access_rules_tag == NONE_SESSION_TAG: new_access_rules_tag = None if session.access_rules_tag != new_access_rules_tag: session.access_rules_tag = new_access_rules_tag session.save() # {{{ put together grading rule if form.cleaned_data["create_grading_exception"]: due = form.cleaned_data["due"] if form.cleaned_data["due_same_as_access_expiration"]: due = form.cleaned_data["access_expires"] descr = ugettext("Granted excecption") if form.cleaned_data["credit_percent"] is not None: descr += string_concat(" (%.1f%% ", ugettext('credit'), ")") \ % form.cleaned_data["credit_percent"] due_local_naive = due if due_local_naive is not None: from relate.utils import as_local_time due_local_naive = ( as_local_time(due_local_naive) .replace(tzinfo=None)) new_grading_rule = { "description": descr, } if due_local_naive is not None: new_grading_rule["due"] = due_local_naive new_grading_rule["if_completed_before"] = due_local_naive for attr_name in ["credit_percent", "bonus_points", "max_points", "max_points_enforced_cap"]: if form.cleaned_data[attr_name] is not None: new_grading_rule[attr_name] = form.cleaned_data[attr_name] if (form.cleaned_data.get("restrict_to_same_tag") and session.access_rules_tag is not None): new_grading_rule["if_has_tag"] = session.access_rules_tag if hasattr(grading_rule, "generates_grade"): new_grading_rule["generates_grade"] = \ grading_rule.generates_grade validate_session_grading_rule( vctx, ugettext("newly created exception"), dict_to_struct(new_grading_rule), tags, grading_rule.grade_identifier) fre_grading = FlowRuleException( flow_id=flow_id, participation=participation, creator=pctx.request.user, comment=form.cleaned_data["comment"], kind=flow_rule_kind.grading, rule=new_grading_rule) fre_grading.save() # }}} messages.add_message(pctx.request, messages.SUCCESS, ugettext( "Exception granted to '%(participation)s' " "for '%(flow_id)s'.") % { 'participation': participation, 'flow_id': flow_id}) return redirect( "relate-grant_exception", pctx.course.identifier) else: data = { "restrict_to_same_tag": session.access_rules_tag is not None, "credit_percent": grading_rule.credit_percent, #"due_same_as_access_expiration": True, "due": grading_rule.due, } for perm in access_rule.permissions: data[perm] = True form = ExceptionStage3Form(data, flow_desc, session.access_rules_tag) return render_course_page(pctx, "course/generic-course-form.html", { "form": form, "form_description": ugettext("Grant Exception"), "form_text": string_concat( "<div class='well'>", ugettext("Granting exception to '%(participation)s' " "for '%(flow_id)s' (session %(session)s)."), "</div>") % { 'participation': participation, 'flow_id': flow_id, 'session': strify_session_for_exception(session)}, })
def test_all_day_event(self): self.switch_to_fake_commit_sha() # lecture 2, no end_time lecture2_start_time = datetime.datetime(2019, 1, 1, tzinfo=pytz.UTC) self.mock_get_now_or_fake_time.return_value = ( lecture2_start_time + timedelta(minutes=5)) lecture2_evt = factories.EventFactory( kind=self.default_event_kind, course=self.course, all_day=True, time=lecture2_start_time, ordinal=2) # lecture 3 lecture3_start_time = lecture2_start_time + timedelta(weeks=1) factories.EventFactory( kind=self.default_event_kind, course=self.course, time=lecture3_start_time, ordinal=3) resp = self.get_course_calendar_view() events_json = json.loads(resp.context["events_json"]) self.assertEqual(len(events_json), 2) self.assertDictEqual( events_json[1], {'id': 1, 'start': lecture2_start_time.isoformat(), 'allDay': True, 'title': 'Lecture 2', 'url': '#event-1'}) # now we add end_time of lecture 2 evt to a time which is not midnight lecture2_end_time = lecture2_start_time + timedelta(hours=18) lecture2_evt.end_time = lecture2_end_time lecture2_evt.save() resp = self.get_course_calendar_view() events_json = json.loads(resp.context["events_json"]) self.assertEqual(len(events_json), 2) self.assertDictEqual( events_json[1], {'id': 1, 'start': lecture2_start_time.isoformat(), 'allDay': True, 'title': 'Lecture 2', 'url': '#event-1', 'end': lecture2_end_time.isoformat() }) # now we update end_time of lecture 2 evt to midnight while True: local_t = as_local_time(lecture2_end_time) end_midnight = datetime.time(tzinfo=local_t.tzinfo) if local_t.time() == end_midnight: lecture2_evt.end_time = lecture2_end_time lecture2_evt.save() break lecture2_end_time += timedelta(hours=1) resp = self.get_course_calendar_view() events_json = json.loads(resp.context["events_json"]) self.assertEqual(len(events_json), 2) self.assertDictEqual( events_json[1], {'id': 1, 'start': lecture2_start_time.isoformat(), 'allDay': True, 'title': 'Lecture 2', 'url': '#event-1', 'end': lecture2_end_time.isoformat() })
def grant_exception_stage_3(pctx, participation_id, flow_id, session_id): if pctx.role not in [ participation_role.instructor, participation_role.teaching_assistant]: raise PermissionDenied("must be instructor or TA to grant exceptions") participation = get_object_or_404(Participation, id=participation_id) from course.content import get_flow_desc try: flow_desc = get_flow_desc(pctx.repo, pctx.course, flow_id, pctx.course_commit_sha) except ObjectDoesNotExist: raise http.Http404() session = FlowSession.objects.get(id=int(session_id)) now_datetime = get_now_or_fake_time(pctx.request) from course.utils import ( get_session_access_rule, get_session_grading_rule) access_rule = get_session_access_rule( session, pctx.role, flow_desc, now_datetime) grading_rule = get_session_grading_rule( session, pctx.role, flow_desc, now_datetime) request = pctx.request if request.method == "POST": form = ExceptionStage3Form({}, session.access_rules_tag, request.POST) from course.constants import flow_rule_kind if form.is_valid(): permissions = [ key for key, _ in FLOW_PERMISSION_CHOICES if form.cleaned_data[key]] from course.validation import ( validate_session_access_rule, validate_session_grading_rule, ValidationContext) from relate.utils import dict_to_struct vctx = ValidationContext( repo=pctx.repo, commit_sha=pctx.course_commit_sha) from course.content import get_flow_desc flow_desc = get_flow_desc(pctx.repo, pctx.course, flow_id, pctx.course_commit_sha) tags = None if hasattr(flow_desc, "rules"): tags = getattr(flow_desc.rules, "tags", None) # {{{ put together access rule new_access_rule = {"permissions": permissions} if (form.cleaned_data["restrict_to_same_tag"] and session.access_rules_tag is not None): new_access_rule["if_has_tag"] = session.access_rules_tag validate_session_access_rule(vctx, "newly created exception", dict_to_struct(new_access_rule), tags) fre_access = FlowRuleException( flow_id=flow_id, participation=participation, expiration=form.cleaned_data["access_expires"], creator=pctx.request.user, comment=form.cleaned_data["comment"], kind=flow_rule_kind.access, rule=new_access_rule) fre_access.save() # }}} # {{{ put together grading rule due = form.cleaned_data["due"] if form.cleaned_data["due_same_as_access_expiration"]: due = form.cleaned_data["access_expires"] descr = "Granted excecption" if form.cleaned_data["credit_percent"] is not None: descr += " (%.1f%% credit)" % form.cleaned_data["credit_percent"] from relate.utils import as_local_time new_grading_rule = { "credit_percent": form.cleaned_data["credit_percent"], "due": as_local_time(due).replace(tzinfo=None), "if_completed_before": as_local_time(due).replace(tzinfo=None), "description": descr, } if (form.cleaned_data["restrict_to_same_tag"] and session.access_rules_tag is not None): new_grading_rule["if_has_tag"] = session.access_rules_tag if hasattr(grading_rule, "grade_identifier"): new_grading_rule["grade_identifier"] = \ grading_rule.grade_identifier if hasattr(grading_rule, "grade_aggregation_strategy"): new_grading_rule["grade_aggregation_strategy"] = \ grading_rule.grade_aggregation_strategy validate_session_grading_rule(vctx, "newly created exception", dict_to_struct(new_grading_rule), tags) fre_grading = FlowRuleException( flow_id=flow_id, participation=participation, creator=pctx.request.user, comment=form.cleaned_data["comment"], kind=flow_rule_kind.grading, rule=new_grading_rule) fre_grading.save() # }}} messages.add_message(pctx.request, messages.SUCCESS, "Exception granted to '%s' for '%s'." % (participation, flow_id)) return redirect( "course.views.grant_exception", pctx.course.identifier) else: data = { "restrict_to_same_tag": session.access_rules_tag is not None, "credit_percent": grading_rule.credit_percent, #"due_same_as_access_expiration": True, "due": grading_rule.due, } for perm in access_rule.permissions: data[perm] = True form = ExceptionStage3Form(data, session.access_rules_tag) return render_course_page(pctx, "course/generic-course-form.html", { "form": form, "form_description": "Grant Exception", "form_text": "<div class='well'>Granting exception to '%s' for '%s'.</div>" % (participation, flow_id), })