diff --git a/course/grades.py b/course/grades.py index 012a474cb0f65f808f9412dd3659b994c3bd270e..fa126741de86bddb21393a0d564b482d1401f6b4 100644 --- a/course/grades.py +++ b/course/grades.py @@ -52,7 +52,7 @@ from course.models import ( Participation, participation_status, GradingOpportunity, GradeChange, GradeStateMachine, grade_state_change_types, - FlowSession, FlowPageVisit) + FlowSession, FlowPageVisit, FlowRuleException) from course.flow import adjust_flow_session_page_data from course.views import get_now_or_fake_time from course.constants import ( @@ -64,6 +64,7 @@ from course.constants import ( if False: from typing import Tuple, Text, Optional, Any, Iterable, List # noqa from course.utils import CoursePageContext # noqa + from course.utils import FlowSessionAccessRule, FlowSessionGradingRule # noqa from course.content import FlowDesc # noqa from course.models import Course, FlowPageVisitGrade # noqa @@ -792,6 +793,47 @@ def average_grade(opportunity): return None, 0 +def parse_exception( + access_exc, # type: Optional[FlowRuleException] + grading_exc # type: Optional[FlowRuleException] + ): + # type: (...) -> Tuple[Optional[FlowRuleException], Optional[str]] + + # if grading exception is the same as the default exception, ignore it + if grading_exc: + def has_grading_exc(rule): + # type: (Dict[str, float]) -> bool + + if 'bonus_points' in rule and rule['bonus_points'] != 0.0: + return True + elif 'credit_percent' in rule and rule['credit_percent'] != 100.0: + return True + elif 'max_points' in rule or 'max_points_enforced_cap' in rule: + return True + else: + return False + if has_grading_exc(grading_exc.rule): + grading_exc = None + + last_exception = None # type: Optional[FlowRuleException] + # if both exceptions exist + if access_exc and grading_exc: + last_exception = (access_exc # noqa + if access_exc.creation_time > grading_exc.creation_time # noqa + else grading_exc) + kind = str(_("access and grading")) + elif access_exc or grading_exc: + last_exception = (access_exc + if access_exc is not None + else grading_exc) + kind = str(_(str(last_exception.kind))) + # no exception exists + else: + kind = None + + return last_exception, kind + + @course_view def view_single_grade(pctx, participation_id, opportunity_id): # type: (CoursePageContext, Text, Text) -> http.HttpResponse @@ -833,6 +875,7 @@ def view_single_grade(pctx, participation_id, opportunity_id): request = pctx.request if pctx.request.method == "POST": + action_re = re.compile("^([a-z]+)_([0-9]+)$") for key in request.POST.keys(): action_match = action_re.match(key) @@ -932,7 +975,7 @@ def view_single_grade(pctx, participation_id, opportunity_id): from collections import namedtuple SessionProperties = namedtuple( # noqa "SessionProperties", - ["due", "grade_description"]) + ["due", "grade_description", "exception"]) from course.utils import get_session_grading_rule from course.content import get_flow_desc @@ -952,9 +995,40 @@ def view_single_grade(pctx, participation_id, opportunity_id): grading_rule = get_session_grading_rule( session, flow_desc, now_datetime) + ExcInfo = namedtuple( # noqa + "ExcInfo", + ["kind", "creation_time", "creator"]) + + from course.constants import flow_rule_kind + + # get the latest access and grading exceptions + access_exc = FlowRuleException.objects.filter( + participation=session.participation, + kind=flow_rule_kind.access, + active=True, + flow_id=session.flow_id).order_by("creation_time").last() + grading_exc = FlowRuleException.objects.filter( + participation=session.participation, + active=True, + kind=flow_rule_kind.grading, + flow_id=session.flow_id).order_by("creation_time").last() + # kind: whether the exception is both access and grading exceptions + # or only one of them + last_exception, kind = parse_exception(access_exc, grading_exc) + + exc_info = None + if last_exception is not None: + exc_info = ExcInfo( + kind=kind, + creation_time=last_exception.creation_time, + creator=last_exception.creator + ) + session_properties = SessionProperties( due=grading_rule.due, - grade_description=grading_rule.description) + grade_description=grading_rule.description, + exception=exc_info) + flow_sessions_and_session_properties.append( (session, session_properties)) @@ -995,7 +1069,7 @@ def view_single_grade(pctx, participation_id, opportunity_id): pperm.impose_flow_session_deadline or pperm.end_flow_session or pperm.regrade_flow_session - or pperm.recalculate_flow_session_grade), + or pperm.recalculate_flow_session_grade) }) # }}} diff --git a/course/templates/course/gradebook-single.html b/course/templates/course/gradebook-single.html index 757010c3d6f9920003373b87995ca60a6327e612..a2fe8e407d9debd4c8f3c0b3334a3e4232bb13a8 100644 --- a/course/templates/course/gradebook-single.html +++ b/course/templates/course/gradebook-single.html @@ -184,6 +184,22 @@ {% trans "Last activity" %}: {{ flow_session.last_activity }}
{% endif %} +{% trans "Exsiting exception:" %}
+{% trans "Created: " %}{{session_properties.exception.creation_time}}
+{% trans "Creator: " %}{{session_properties.exception.creator}}
+{% trans "Kind: " %}{{session_properties.exception.kind}}
+ + {% trans "Edit exception" %} + + {% else %} +{% trans "Exsiting Exception: " %}None
+ {% endif %} + {% endif %}