Exemplo 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):
        raise IOError("Failed to deliver results")
    return HttpResponse("Ok")
Exemplo n.º 2
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):
        raise IOError("Failed to deliver results")
    return HttpResponse("Ok")
Exemplo n.º 3
0
def post_result(submission_url, course, exercise, template, result):
    '''
    Posts grading result to the submission URL.

    @type submission_url: C{str}
    @param submission_url: a submission URL where grader should POST result
    @type course: C{dict}
    @param course: a course configuration
    @type exercise: C{dict}
    @param exercise: an exercise configuration
    @type template: C{str}
    @param template: a template name to use
    @type result: C{dict}
    @param result: additional results
    '''
    html = template_to_str(course, exercise, None, template, result)

    # Make unicode results ascii.
    html = html.encode("ascii", "xmlcharrefreplace")

    data = {
        "max_points": result.get("max_points", 1),
        "points": result.get("points", 0),
        "feedback": html
    }

    if "error" in result and result["error"]:
        data["error"] = True

    if "grading_data" in result:
        data["grading_data"] = result["grading_data"]

    post_data(submission_url, data)
Exemplo n.º 4
0
def resubmit_form(course, exercise, action, submission_dir):
    data = json.loads(read_submission_file(submission_dir, "data.json"))
    form = GradedForm(data, exercise=exercise)
    out = template_to_str(course, exercise, data.get("__aplus_post_url"), "access/task_resubmit.html", {
        "form": form,
        "instructions": action.get("instructions", False),
    })
    return { "points": 0, "max_points": 0, "out": out, "err": "", "stop": False }
Exemplo n.º 5
0
 def create_more(self, configuration):
     '''
     Creates more instructions by configuration.
     '''
     more = ""
     if "more" in configuration:
         more += configuration["more"]
     if "include" in configuration:
         more += template_to_str(None, None, configuration["include"])
     return more or None
Exemplo n.º 6
0
 def create_more(self, configuration):
     '''
     Creates more instructions by configuration.
     '''
     more = ""
     if "more" in configuration:
         more += configuration["more"]
     if "include" in configuration:
         more += template_to_str(None, None, configuration["include"])
     return more or None
Exemplo n.º 7
0
def resubmit_form(course, exercise, action, submission_dir):
    data = json.loads(read_submission_file(submission_dir, "data.json"))
    form = GradedForm(data, exercise=exercise)
    out = template_to_str(
        course, exercise, data.get("__aplus_post_url"),
        "access/task_resubmit.html", {
            "form": form,
            "instructions": action.get("instructions", False),
        })
    return {"points": 0, "max_points": 0, "out": out, "err": "", "stop": False}
Exemplo n.º 8
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
        })
Exemplo n.º 9
0
    def handle(self, *args, **options):

        config = ConfigParser()

        # Check arguments.
        if len(args) < 1 or "/" not in args[0]:
            raise CommandError("Required arguments missing: course_key/exercise_key")
        course_key, exercise_key = args[0].split("/", 1)

        # Get exercise configuration.
        (course, exercise) = config.exercise_entry(course_key, exercise_key)
        if course is None:
            raise CommandError("Course not found for key: %s" % (course_key))
        if exercise is None:
            raise CommandError("Exercise not found for key: %s/%s" % (course_key, exercise_key))
        self.stdout.write('Exercise configuration retrieved.')

        # Check exercise type.
        if not "actions" in exercise:
            raise CommandError("Cannot grade: exercise does not configure asynchronous actions")

        # Create submission.
        sdir = create_submission_dir(course, exercise)
        if len(args) == 1:
            os.makedirs(sdir + "/user")
        for n in range(1, len(args)):
            name = args[n]

            # Copy individual files.
            if os.path.isfile(name):
                submit_path = submission_file_path(sdir, os.path.basename(name))
                shutil.copy2(name, submit_path)

            # Copy a directory.
            elif os.path.isdir(name):
                if len(args) != 2:
                    raise CommandError("Can only submit one directory or multiple files.")
                shutil.copytree(name, sdir + "/user", True)

            else:
                raise CommandError("Submit file not found: %s" % (name))

        # Run actions.
        r = runactions(course, exercise, sdir)
        self.stdout.write("Response body:")
        self.stdout.write(template_to_str(course, exercise, "", r["template"], r["result"]))
Exemplo n.º 10
0
def post_result(submission_url, course, exercise, template, result):
    '''
    Posts grading result to the submission URL.

    @type submission_url: C{str}
    @param submission_url: a submission URL where grader should POST result
    @type course: C{dict}
    @param course: a course configuration
    @type exercise: C{dict}
    @param exercise: an exercise configuration
    @type template: C{str}
    @param template: a template name to use
    @type result: C{dict}
    @param result: additional results
    '''
    html = template_to_str(course, exercise, None, template, result)

    # Make unicode results ascii.
    html = html.encode("ascii", "xmlcharrefreplace")

    data = {
        "max_points": result.get("max_points", 1),
        "points": result.get("points", 0),
        "feedback": html
    }

    if "error" in result and result["error"]:
        data["error"] = True

    if "grading_data" in result:
        data["grading_data"] = result["grading_data"]

    # Try to send send the result.
    try:
        r = requests.post(submission_url, data=data)
        if r.status_code != 200:
            r.raise_for_status()
        rsp = r.json()
        if not "success" in rsp or not rsp["success"]:
            LOGGER.error("Result POST to \"%s\" got unexpected response: %s",
                submission_url, rsp.body)
    except Exception:
        LOGGER.error("Failed to submit \"%s\"", submission_url)
Exemplo n.º 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),
        }
        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
                           })
Exemplo n.º 12
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
        })