Esempio n. 1
0
def container_post(request):
    '''
    Proxies the grading result from inside container to A+
    '''
    sid = request.POST.get("sid", None)
    if not sid:
        return HttpResponseForbidden("Missing sid")

    meta = read_and_remove_submission_meta(sid)
    if meta is None:
        return HttpResponseForbidden("Invalid sid")
    #clean_submission_dir(meta["dir"])


    data = {
        "points": int(request.POST.get("points", 0)),
        "max_points": int(request.POST.get("max_points", 1)),
    }
    for key in ["error", "grading_data"]:
        if key in request.POST:
            data[key] = request.POST[key]
    if "error" in data and data["error"].lower() in ("no", "false"):
        del data["error"]

    feedback = request.POST.get("feedback", "")
    # Fetch the corresponding exercise entry from the config.
    lang = meta["lang"]
    (course, exercise) = config.exercise_entry(meta["course_key"], meta["exercise_key"], lang=lang)
    if "feedback_template" in exercise:
        # replace the feedback with a rendered feedback template if the exercise is configured to do so
        # it is not feasible to support all of the old feedback template variables that runactions.py
        # used to have since the grading actions are not configured in the exercise YAML file anymore
        required_fields = { 'points', 'max_points', 'error', 'out' }
        result = MonitoredDict({
            "points": data["points"],
            "max_points": data["max_points"],
            "out": feedback,
            "error": data.get("error", False),
            "title": exercise.get("title", ""),
        })
        translation.activate(lang)
        feedback = template_to_str(course, exercise, None, exercise["feedback_template"], result=result)
        if result.accessed.isdisjoint(required_fields):
            alert = template_to_str(
                course, exercise, None,
                "access/feedback_template_did_not_use_result_alert.html")
            feedback = alert + feedback
        # Make unicode results ascii.
        feedback = feedback.encode("ascii", "xmlcharrefreplace")

    data["feedback"] = feedback

    if not post_data(meta["url"], data):
        write_submission_meta(sid, meta)
        return HttpResponse("Failed to deliver results", status=502)
    return HttpResponse("Ok")
Esempio n. 2
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
                           })
Esempio n. 3
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
        })
Esempio n. 4
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
                           })
Esempio n. 5
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
        })