Beispiel #1
0
def createForm(request, course, exercise, post_url):
    '''
    Creates form by configuration and grades answers.
    '''
    if "max_points" not in exercise:
        raise ConfigError("Missing required \"max_points\" in exercise configuration")
    try:
        acceptNonce(request)
    except PermissionDenied:
        return render_template(request, course, exercise, post_url,
            'access/exercise_frame.html', { "error":True, "nonce_used":True })

    form = GradedForm(request.POST or None, exercise=exercise)
    result = { "form": form }

    # Grade valid form posts.
    if form.is_valid():
        (points, error_groups, error_fields) = form.grade()
        points = pointsInRange(points, exercise["max_points"])

        # If points are not granted by form fields.
        if points == 0 and not error_fields:
            points = exercise["max_points"]

        result = { "form": form, "accepted": True, "points": points,
            "error_groups": error_groups, "error_fields": error_fields }

    return render_configured_template(request, course, exercise, post_url,
        'access/create_form_default.html', result)
Beispiel #2
0
def _acceptSubmission(request, course, exercise, post_url, sdir):
    '''
    Queues the submission for grading.
    '''

    # Backup synchronous grading.
    if not settings.CELERY_BROKER:
        LOGGER.warning("No queue configured")
        from grader.runactions import runactions
        r = runactions(course, exercise, sdir, get_uid(request), int(request.GET.get("ordinal_number", 1)))
        html = template_to_str(course, exercise, "", r["template"], r["result"])
        return render_template(request, course, exercise, post_url,
            "access/async_accepted.html", {
                "synchronous": True,
                "accepted": True,
                "max_points": r["result"].get("max_points", 1),
                "points": r["result"].get("points", 0),
                "feedback": html,
            })

    if "submission_url" in request.GET:
        surl = request.GET["submission_url"]
        surl_missing = False
    else:
        LOGGER.warning("submission_url missing from a request")
        surl = request.build_absolute_uri(reverse('test-result'))
        surl_missing = True

    # Queue grader.
    tasks.grade.delay(course["key"], exercise["key"],
        translation.get_language(), surl, sdir, get_uid(request),
        int(request.GET.get("ordinal_number", 1)))

    _acceptSubmission.counter += 1
    qlen = queue_length()
    LOGGER.debug("Submission of %s/%s, queue counter %d, queue length %d",
        course["key"], exercise["key"], _acceptSubmission.counter, qlen)
    if qlen >= settings.QUEUE_ALERT_LENGTH:
        LOGGER.error("Queue alert, length: %d", qlen)

    return render_template(request, course, exercise, post_url,
        "access/async_accepted.html", {
            "accepted": True,
            "wait": True,
            "missing_url": surl_missing,
            "queue": qlen
        })
Beispiel #3
0
def createFormModel(request, course, exercise, parameter):
    form = GradedForm(None, exercise=exercise, show_correct=True)
    form.bind_initial()
    points,error_groups,error_fields = form.grade()
    result = { "form": form, "accepted": True, "points": points,
        "error_groups": error_groups, "error_fields": error_fields }
    return render_template(request, course, exercise, None,
        'access/graded_form.html', result)
Beispiel #4
0
def createForm(request, course, exercise, post_url):
    '''
    Creates form by configuration and grades answers.
    '''
    if "max_points" not in exercise:
        raise ConfigError("Missing required \"max_points\" in exercise configuration")
    try:
        acceptNonce(request)
    except PermissionDenied:
        return render_template(request, course, exercise, post_url,
            'access/exercise_frame.html', { "error":True, "nonce_used":True })

    last = False
    if request.method == 'POST':
        try:
            n = int(request.GET.get('ordinal_number', '0'))
            max_n = int(request.GET.get('max_submissions', '0'))
        except ValueError:
            pass
        last = max_n > 0 and n >= max_n

    form = GradedForm(request.POST or None, request.FILES or None,
        exercise=exercise, show_correct_once=last)

    # Support caching of non personalized forms.
    if not form.randomized and not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = { "form": form, "rejected": True }

    # Grade valid form posts.
    if form.is_valid():
        (points, error_groups, error_fields) = form.grade()
        points = pointsInRange(points, exercise["max_points"])

        # Allow passing to asynchronous grading.
        if "actions" in exercise or "container" in exercise:
            from .stdasync import _saveForm
            return _saveForm(request, course, exercise, post_url, form)

        # If points are not granted by form fields.
        if points == 0 and not error_fields:
            points = exercise["max_points"]

        result = { "form": form, "accepted": True, "points": points,
            "error_groups": error_groups, "error_fields": error_fields }

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            'access/create_form_default.html', result
        ),
        request,
        exercise,
        form.randomized,
    )
Beispiel #5
0
def createFormModel(request, course, exercise, parameter):
    form = GradedForm(None, exercise=exercise, show_correct=True)
    form.bind_initial()
    points, error_groups, error_fields = form.grade()
    result = {
        "form": form,
        "accepted": True,
        "points": points,
        "error_groups": error_groups,
        "error_fields": error_fields
    }
    return render_template(request, course, exercise, None,
                           'access/graded_form.html', result)
Beispiel #6
0
def createForm(request, course, exercise, post_url):
    '''
    Creates form by configuration and grades answers.
    '''
    if "max_points" not in exercise:
        raise ConfigError(
            "Missing required \"max_points\" in exercise configuration")
    try:
        acceptNonce(request)
    except PermissionDenied:
        return render_template(request, course, exercise, post_url,
                               'access/exercise_frame.html', {
                                   "error": True,
                                   "nonce_used": True
                               })

    last = False
    if request.method == 'POST':
        try:
            n = int(request.GET.get('ordinal_number', '0'))
            max_n = int(request.GET.get('max_submissions', '0'))
        except ValueError:
            pass
        last = max_n > 0 and n >= max_n

    form = GradedForm(request.POST or None,
                      request.FILES or None,
                      exercise=exercise,
                      show_correct_once=last)

    # Support caching of non personalized forms.
    if not form.randomized and not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = {"form": form, "rejected": True}

    # Grade valid form posts.
    if form.is_valid():
        (points, error_groups, error_fields) = form.grade()
        points = pointsInRange(points, exercise["max_points"])

        # Allow passing to asynchronous grading.
        if "actions" in exercise or "container" in exercise:
            from .stdasync import _saveForm
            return _saveForm(request, course, exercise, post_url, form)

        # If points are not granted by form fields.
        if points == 0 and not error_fields:
            points = exercise["max_points"]

        result = {
            "form": form,
            "accepted": True,
            "points": points,
            "error_groups": error_groups,
            "error_fields": error_fields
        }

    return cache_headers(
        render_configured_template(request, course, exercise, post_url,
                                   'access/create_form_default.html', result),
        request,
        exercise,
        form.randomized,
    )
Beispiel #7
0
def _acceptSubmission(request, course, exercise, post_url, sdir):
    '''
    Queues the submission for grading.
    '''
    uids = get_uid(request)
    attempt = int(request.GET.get("ordinal_number", 1))
    container_flag = settings.CONTAINER_MODE and "container" in exercise

    # Backup synchronous grading.
    if not container_flag and not settings.CELERY_BROKER:
        LOGGER.warning("No queue configured")
        from grader.runactions import runactions
        r = runactions(course, exercise, sdir, uids, attempt)
        html = template_to_str(course, exercise, "", r["template"],
                               r["result"])
        return render_template(
            request, course, exercise, post_url, "access/async_accepted.html",
            {
                "synchronous": True,
                "accepted": True,
                "max_points": r["result"].get("max_points", 1),
                "points": r["result"].get("points", 0),
                "feedback": html,
            })

    if "submission_url" in request.GET:
        surl = request.GET["submission_url"]
        surl_missing = False
    else:
        LOGGER.warning("submission_url missing from a request")
        surl = request.build_absolute_uri(reverse('test-result'))
        surl_missing = True

    _acceptSubmission.counter += 1

    # Order container for grading.
    if container_flag:
        c = _requireContainer(exercise)

        course_extra = {
            "key": course["key"],
            "name": course["name"],
        }
        exercise_extra = {
            "key": exercise["key"],
            "title": exercise.get("title", None),
        }
        if exercise.get("personalized", False):
            exercise_extra["personalized_exercise"] \
                = select_generated_exercise_instance(course, exercise, uids, attempt)
            if settings.ENABLE_PERSONAL_DIRECTORIES:
                exercise_extra["personal_directory"] \
                    = user_personal_directory_path(course, exercise, uids)

        sid = os.path.basename(sdir)
        write_submission_meta(sid, {
            "url": surl,
            "dir": sdir,
        })
        r = invoke([
            settings.CONTAINER_SCRIPT,
            sid,
            request.scheme + "://" + request.get_host(),
            c["image"],
            os.path.join(DIR, course["key"], c["mount"]),
            sdir,
            c["cmd"],
            json.dumps(course_extra),
            json.dumps(exercise_extra),
        ])
        LOGGER.debug("Container order exit=%d out=%s err=%s", r["code"],
                     r["out"], r["err"])
        qlen = 1

    # Queue in celery & rabbitmq for chroot sandbox actions.
    else:
        tasks.grade.delay(course["key"], exercise["key"],
                          translation.get_language(), surl, sdir, uids,
                          attempt)
        qlen = queue_length()
        LOGGER.debug("Submission of %s/%s, queue counter %d, queue length %d",
                     course["key"], exercise["key"], _acceptSubmission.counter,
                     qlen)
        if qlen >= settings.QUEUE_ALERT_LENGTH:
            LOGGER.error("Queue alert, length: %d", qlen)

    return render_template(request, course, exercise, post_url,
                           "access/async_accepted.html", {
                               "accepted": True,
                               "wait": True,
                               "missing_url": surl_missing,
                               "queue": qlen
                           })
Beispiel #8
0
def createForm(request, course, exercise, post_url):
    '''
    Creates form by configuration and grades answers.
    '''
    if "max_points" not in exercise:
        raise ConfigError("Missing required \"max_points\" in exercise configuration")

    last = False
    if request.method == 'POST':
        try:
            n = int(request.GET.get('ordinal_number', '0'))
            max_n = int(request.GET.get('max_submissions', '0'))
        except ValueError:
            pass
        last = max_n > 0 and n >= max_n

    try:
        form = GradedForm(request.POST or None, request.FILES or None,
            exercise=exercise, reveal_correct=last, request=request)
    except PermissionDenied:
        # Randomized forms raise PermissionDenied when the POST data contains
        # forged checksums or samples. It could be cleaner to check those
        # in the form validation, but the old code raises an exception like this.
        return render_template(request, course, exercise, post_url,
            'access/exercise_frame.html', { "rejected": True, "invalid_checksum": True })

    # Support caching of non personalized forms.
    if not form.randomized and not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = { "form": form, "rejected": True }

    # Grade valid form posts.
    if form.is_valid():
        (points, error_groups, error_fields) = form.grade()
        points = pointsInRange(points, exercise["max_points"])

        # Allow passing to asynchronous grading.
        if "container" in exercise:
            from .stdasync import _saveForm
            return _saveForm(request, course, exercise, post_url, form)

        # If points are not granted by form fields.
        if points == 0 and not error_fields:
            points = exercise["max_points"]

        result = { "form": form, "accepted": True, "points": points,
            "error_groups": error_groups, "error_fields": error_fields }
    else:
        # Don't reveal the correct answers if the form was rejected, and a
        # submission was not consumed.
        form.reveal_correct = False

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            'access/create_form_default.html', result
        ),
        request,
        exercise,
        form.randomized,
    )
Beispiel #9
0
def _acceptSubmission(request, course, exercise, post_url,
                      sdir: SubmissionDir):
    '''
    Queues the submission for grading.
    '''
    uids = get_uid(request)
    attempt = int(request.GET.get("ordinal_number", 1))

    if "submission_url" in request.GET:
        surl = request.GET["submission_url"]
        surl_missing = False
    else:
        LOGGER.warning("submission_url missing from a request")
        surl = f'http://{request.META["HOSTNAME"]}:{request.META["SERVER_PORT"]}{reverse("test-result")}'
        surl_missing = True

    # Order container for grading.
    c = _requireContainer(exercise)

    ro_mounts = c.get("mounts", {}).copy()
    if not isinstance(ro_mounts, dict):
        raise ConfigError(
            "'container'->'mounts' must be a <path on course>-<mount path> dictionary or omitted altogether"
        )
    for k, v in ro_mounts.items():
        if not os.path.isabs(v):
            raise ConfigError(f"Mount path must be absolute: {v}")
        if any(v == p or v.startswith(p + "/")
               for p in ("/exercise", "/submission",
                         "/personalized_exercise")):
            raise ConfigError(
                "/exercise, /submission and /personalized_exercise are reserved mounts, you cannot mount to them in 'mounts'"
            )
        if os.path.isabs(k) or os.path.normpath(k).startswith(".."):
            raise ConfigError(
                f"Mounted path on course must be a relative subpath: {k}")

    ro_mounts[c["mount"]] = "/exercise"

    ro_mounts = {
        os.path.join(settings.COURSES_PATH, course["key"], k): v
        for k, v in ro_mounts.items()
    }

    if len(set(ro_mounts.values())) != len(ro_mounts):
        raise ConfigError("Mount paths must be distinct")

    if exercise.get("personalized", False):
        personalized_dir = select_generated_exercise_instance(
            course, exercise, uids, attempt)
        ro_mounts[personalized_dir] = "/personalized_exercise"

    write_submission_meta(
        sdir.sid, {
            "url": surl,
            "dir": str(sdir.dir()),
            "course_key": course["key"],
            "exercise_key": exercise["key"],
            "lang": translation.get_language(),
        })
    return_code, out, err = runner_func(
        course=course,
        exercise=exercise,
        container_config=c,
        submission_id=sdir.sid,
        host_url=request.scheme + "://" + request.get_host(),
        readwrite_mounts={str(sdir.dir()): "/submission"},
        readonly_mounts=ro_mounts,
        image=c["image"],
        cmd=c["cmd"],
        settings=settings.RUNNER_MODULE_SETTINGS,
    )
    LOGGER.debug(f"Container order exit={return_code} out={out} err={err}")
    qlen = 1

    return render_template(
        request, course, exercise, post_url, "access/async_accepted.html", {
            "error": return_code != 0,
            "accepted": True,
            "wait": True,
            "missing_url": surl_missing,
            "queue": qlen
        })
Beispiel #10
0
def _acceptSubmission(request, course, exercise, post_url, sdir):
    '''
    Queues the submission for grading.
    '''
    uids = get_uid(request)
    attempt = int(request.GET.get("ordinal_number", 1))

    if "submission_url" in request.GET:
        surl = request.GET["submission_url"]
        surl_missing = False
    else:
        LOGGER.warning("submission_url missing from a request")
        surl = request.build_absolute_uri(reverse('test-result'))
        surl_missing = True

    _acceptSubmission.counter += 1

    # Order container for grading.
    c = _requireContainer(exercise)

    course_extra = {
        "key": course["key"],
        "name": course["name"],
    }
    exercise_extra = {
        "key":
        exercise["key"],
        "title":
        exercise.get("title", None),
        "resources":
        c.get("resources",
              {}),  # Unofficial param, implemented differently later
        "require_constant_environment":
        c.get("require_constant_environment",
              False)  # Unofficial param, implemented differently later
    }
    if exercise.get("personalized", False):
        exercise_extra["personalized_exercise"] \
            = select_generated_exercise_instance(course, exercise, uids, attempt)

    sid = os.path.basename(sdir)
    write_submission_meta(
        sid, {
            "url": surl,
            "dir": sdir,
            "course_key": course["key"],
            "exercise_key": exercise["key"],
            "lang": translation.get_language(),
        })
    r = invoke([
        settings.CONTAINER_SCRIPT,
        sid,
        request.scheme + "://" + request.get_host(),
        c["image"],
        os.path.join(DIR, course["key"], c["mount"]),
        sdir,
        c["cmd"],
        json.dumps(course_extra),
        json.dumps(exercise_extra),
    ])
    LOGGER.debug("Container order exit=%d out=%s err=%s", r["code"], r["out"],
                 r["err"])
    qlen = 1

    return render_template(request, course, exercise, post_url,
                           "access/async_accepted.html", {
                               "accepted": True,
                               "wait": True,
                               "missing_url": surl_missing,
                               "queue": qlen
                           })
Beispiel #11
0
def _acceptSubmission(request, course, exercise, post_url, sdir):
    '''
    Queues the submission for grading.
    '''
    uids = get_uid(request)
    attempt = int(request.GET.get("ordinal_number", 1))
    container_flag = settings.CONTAINER_MODE and "container" in exercise

    # Backup synchronous grading.
    if not container_flag and not settings.CELERY_BROKER:
        LOGGER.warning("No queue configured")
        from grader.runactions import runactions
        r = runactions(course, exercise, sdir, uids, attempt)
        html = template_to_str(course, exercise, "", r["template"], r["result"])
        return render_template(request, course, exercise, post_url,
            "access/async_accepted.html", {
                "synchronous": True,
                "accepted": True,
                "max_points": r["result"].get("max_points", 1),
                "points": r["result"].get("points", 0),
                "feedback": html,
            })

    if "submission_url" in request.GET:
        surl = request.GET["submission_url"]
        surl_missing = False
    else:
        LOGGER.warning("submission_url missing from a request")
        surl = request.build_absolute_uri(reverse('test-result'))
        surl_missing = True

    _acceptSubmission.counter += 1

    # Order container for grading.
    if container_flag:
        c = _requireContainer(exercise)

        course_extra = {
            "key": course["key"],
            "name": course["name"],
        }
        exercise_extra = {
            "key": exercise["key"],
            "title": exercise.get("title", None),
            "resources": c.get("resources", {}), # Unofficial param, implemented differently later
            "require_constant_environment": c.get("require_constant_environment", False) # Unofficial param, implemented differently later
        }
        if exercise.get("personalized", False):
            exercise_extra["personalized_exercise"] \
                = select_generated_exercise_instance(course, exercise, uids, attempt)
            if settings.ENABLE_PERSONAL_DIRECTORIES:
                exercise_extra["personal_directory"] \
                    = user_personal_directory_path(course, exercise, uids)

        sid = os.path.basename(sdir)
        write_submission_meta(sid, {
            "url": surl,
            "dir": sdir,
            "course_key": course["key"],
            "exercise_key": exercise["key"],
            "lang": translation.get_language(),
        })
        r = invoke([
            settings.CONTAINER_SCRIPT,
            sid,
            request.scheme + "://" + request.get_host(),
            c["image"],
            os.path.join(DIR, course["key"], c["mount"]),
            sdir,
            c["cmd"],
            json.dumps(course_extra),
            json.dumps(exercise_extra),
        ])
        LOGGER.debug("Container order exit=%d out=%s err=%s",
            r["code"], r["out"], r["err"])
        qlen = 1

    # Queue in celery & rabbitmq for chroot sandbox actions.
    else:
        tasks.grade.delay(course["key"], exercise["key"],
            translation.get_language(), surl, sdir, uids, attempt)
        qlen = queue_length()
        LOGGER.debug("Submission of %s/%s, queue counter %d, queue length %d",
            course["key"], exercise["key"], _acceptSubmission.counter, qlen)
        if qlen >= settings.QUEUE_ALERT_LENGTH:
            LOGGER.error("Queue alert, length: %d", qlen)

    return render_template(request, course, exercise, post_url,
        "access/async_accepted.html", {
            "accepted": True,
            "wait": True,
            "missing_url": surl_missing,
            "queue": qlen
        })