Example #1
0
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), pctx.remote_address
    )

    if is_expiration_mode_allowed(expmode, access_rule.permissions):
        flow_session.expiration_mode = expmode
        flow_session.save()

        return http.HttpResponse("OK")
    else:
        raise PermissionDenied()
Example #2
0
def expire_flow_session(fctx, flow_session, grading_rule, now_datetime, past_due_only=False):
    if not flow_session.in_progress:
        raise RuntimeError(_("Can't expire a session that's not in progress"))
    if flow_session.participation is None:
        raise RuntimeError(_("Can't expire an anonymous flow session"))

    assert isinstance(grading_rule, FlowSessionGradingRule)

    if past_due_only and grading_rule.due is not None and now_datetime < grading_rule.due:
        return False

    if flow_session.expiration_mode == flow_session_expiration_mode.roll_over:
        session_start_rule = get_session_start_rule(
            flow_session.course,
            flow_session.participation,
            flow_session.participation.role,
            flow_session.flow_id,
            fctx.flow_desc,
            now_datetime,
            for_rollover=True,
        )

        if not session_start_rule.may_start_new_session:
            # No new session allowed: finish.
            return finish_flow_session(fctx, flow_session, grading_rule, now_datetime=now_datetime)

        flow_session.access_rules_tag = session_start_rule.tag_session

        access_rule = get_session_access_rule(
            flow_session, flow_session.participation.role, fctx.flow_desc, now_datetime
        )

        if not is_expiration_mode_allowed(flow_session.expiration_mode, access_rule.permissions):
            flow_session.expiration_mode = flow_session_expiration_mode.end
        flow_session.save()

        return True

    elif flow_session.expiration_mode == flow_session_expiration_mode.end:
        return finish_flow_session(fctx, flow_session, grading_rule, now_datetime=now_datetime)
    else:
        raise ValueError(
            _("invalid expiration mode '%(mode)s' on flow session ID " "%(session_id)d")
            % {"mode": flow_session.expiration_mode, "session_id": flow_session.id}
        )
Example #3
0
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)
            },
        })
Example #4
0
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)},
    })
Example #5
0
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)
    flow_id = flow_session.flow_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, flow_id)

    try:
        fpctx = FlowPageContext(
            pctx.repo, pctx.course, flow_id, ordinal, participation=pctx.participation, flow_session=flow_session
        )
    except PageOrdinalOutOfRange:
        return redirect("relate-view_flow_page", pctx.course.identifier, flow_session.id, flow_session.page_count - 1)

    access_rule = get_session_access_rule(
        flow_session, pctx.role, fpctx.flow_desc, get_now_or_fake_time(request), pctx.remote_address
    )
    permissions = fpctx.page.get_modified_permissions_for_page(access_rule.permissions)

    if access_rule.message:
        messages.add_message(request, messages.INFO, access_rule.message)

    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"))

    if request.method == "POST":
        if "finish" in request.POST:
            return redirect("relate-finish_flow_session_view", pctx.course.identifier, flow_session_id)
        else:
            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 (
                fpctx.prev_answer_visit is not None
                and fpctx.prev_answer_visit.is_submitted_answer
                and flow_permission.change_answer not in permissions
            ):
                messages.add_message(request, messages.ERROR, _("Already have final answer."))
                submission_allowed = False

            form = fpctx.page.post_form(
                fpctx.page_context, fpctx.page_data.data, post_data=request.POST, files_data=request.FILES
            )

            pressed_button = get_pressed_button(form)

            if submission_allowed and 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"]

                answer_data = page_visit.answer = fpctx.page.answer_data(
                    fpctx.page_context, fpctx.page_data.data, form, request.FILES
                )
                page_visit.is_submitted_answer = pressed_button == "submit"
                page_visit.save()

                answer_was_graded = page_visit.is_submitted_answer
                may_change_answer = not answer_was_graded or flow_permission.change_answer in permissions

                if fpctx.page.is_answer_gradable():
                    feedback = fpctx.page.grade(page_context, page_data.data, page_visit.answer, grade_data=None)

                    if page_visit.is_submitted_answer:
                        grade = FlowPageVisitGrade()
                        grade.visit = page_visit
                        grade.max_points = fpctx.page.max_points(page_data.data)
                        grade.graded_at_git_commit_sha = pctx.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", pctx.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", pctx.course.identifier, flow_session_id)
                else:
                    form = 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_submitted_answer
        else:
            answer_was_graded = False

        may_change_answer = (
            (not answer_was_graded or (flow_permission.change_answer in permissions))
            # can happen if no answer was ever saved
            and flow_session.in_progress
            and (flow_permission.submit_answer in permissions)
        )

        if fpctx.page.expects_answer():
            if fpctx.prev_answer_visit is not None:
                answer_data = fpctx.prev_answer_visit.answer

                most_recent_grade = fpctx.prev_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

            form = fpctx.page.make_form(page_context, page_data.data, answer_data, not may_change_answer)
        else:
            form = 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, 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 permissions

        show_answer = flow_permission.see_answer_after_submission in permissions

        if show_correctness or show_answer:
            shown_feedback = feedback

    elif fpctx.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
        )

    title = fpctx.page.title(page_context, page_data.data)
    body = fpctx.page.body(page_context, page_data.data)

    if show_answer:
        correct_answer = fpctx.page.correct_answer(page_context, page_data.data, answer_data, grade_data)
    else:
        correct_answer = None

    # {{{ render flow page

    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))

    args = {
        "flow_identifier": fpctx.flow_id,
        "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,
        "page_numbers": zip(range(flow_session.page_count), range(1, flow_session.page_count + 1)),
        "title": title,
        "body": body,
        "form": form,
        "form_html": form_html,
        "feedback": shown_feedback,
        "correct_answer": correct_answer,
        "show_correctness": show_correctness,
        "may_change_answer": may_change_answer,
        "may_change_graded_answer": ((flow_permission.change_answer in permissions) and flow_session.in_progress),
        "will_receive_feedback": will_receive_feedback(permissions),
        "show_answer": show_answer,
        "expiration_mode_choices": expiration_mode_choices,
        "expiration_mode_choice_count": len(expiration_mode_choices),
        "expiration_mode": flow_session.expiration_mode,
    }

    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)
Example #6
0
def view_start_flow(pctx, flow_id):
    request = pctx.request

    now_datetime = get_now_or_fake_time(request)
    fctx = FlowContext(pctx.repo, pctx.course, flow_id, participation=pctx.participation)

    session_start_rule = get_session_start_rule(
        pctx.course,
        pctx.participation,
        pctx.role,
        flow_id,
        fctx.flow_desc,
        now_datetime,
        remote_address=pctx.remote_address,
    )

    if request.method == "POST":
        if "start" in request.POST:

            if not session_start_rule.may_start_new_session:
                raise PermissionDenied(_("new session not allowed"))

            session = start_flow(
                pctx.repo,
                pctx.course,
                pctx.participation,
                flow_id,
                fctx.flow_desc,
                access_rules_tag=session_start_rule.tag_session,
                now_datetime=now_datetime,
            )

            return redirect("relate-view_flow_page", pctx.course.identifier, session.id, 0)

        else:
            raise SuspiciousOperation(_("unrecognized POST action"))

    else:
        if session_start_rule.may_list_existing_sessions:
            past_sessions = FlowSession.objects.filter(
                participation=pctx.participation, flow_id=fctx.flow_id, participation__isnull=False
            ).order_by("start_time")

            from collections import namedtuple

            SessionProperties = namedtuple(
                "SessionProperties", ["may_view", "may_modify", "due", "grade_description"]  # noqa
            )

            past_sessions_and_properties = []
            for session in past_sessions:
                access_rule = get_session_access_rule(
                    session, pctx.role, fctx.flow_desc, now_datetime, remote_address=pctx.remote_address
                )
                grading_rule = get_session_grading_rule(session, pctx.role, fctx.flow_desc, now_datetime)

                session_properties = SessionProperties(
                    may_view=flow_permission.view in access_rule.permissions,
                    may_modify=(
                        flow_permission.submit_answer in access_rule.permissions
                        or flow_permission.end_session in access_rule.permissions
                    ),
                    due=grading_rule.due,
                    grade_description=grading_rule.description,
                )
                past_sessions_and_properties.append((session, session_properties))
        else:
            past_sessions_and_properties = []

        may_start = session_start_rule.may_start_new_session
        potential_session = FlowSession(
            course=pctx.course,
            participation=pctx.participation,
            flow_id=flow_id,
            in_progress=True,
            expiration_mode=flow_session_expiration_mode.end,
            access_rules_tag=session_start_rule.tag_session,
        )

        new_session_grading_rule = get_session_grading_rule(potential_session, pctx.role, fctx.flow_desc, now_datetime)

        start_may_decrease_grade = bool(
            past_sessions_and_properties
        ) and new_session_grading_rule.grade_aggregation_strategy not in [
            None,
            grade_aggregation_strategy.max_grade,
            grade_aggregation_strategy.use_earliest,
        ]

        return render_course_page(
            pctx,
            "course/flow-start.html",
            {
                "flow_desc": fctx.flow_desc,
                "flow_identifier": flow_id,
                "now": now_datetime,
                "may_start": may_start,
                "new_session_grading_rule": new_session_grading_rule,
                "grade_aggregation_strategy_descr": (
                    dict(GRADE_AGGREGATION_STRATEGY_CHOICES).get(new_session_grading_rule.grade_aggregation_strategy)
                ),
                "start_may_decrease_grade": start_may_decrease_grade,
                "past_sessions_and_properties": past_sessions_and_properties,
            },
            allow_instant_flow_requests=False,
        )
Example #7
0
def finish_flow_session_view(pctx, flow_session_id):
    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)
    flow_id = flow_session.flow_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, pctx.remote_address)

    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, 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_identifier": fctx.flow_id, "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:
            raise PermissionDenied(_("Can't 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)
        grade_info = finish_flow_session(fctx, flow_session, grading_rule, now_datetime=now_datetime)

        if is_graded_flow:
            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_graded_flow or (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",
            last_page_nr=flow_session.page_count - 1,
            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)

        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",
            last_page_nr=flow_session.page_count - 1,
            flow_session=flow_session,
            answered_count=answered_count,
            unanswered_count=unanswered_count,
            total_count=answered_count + unanswered_count,
        )
Example #8
0
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),
    })