Exemple #1
0
def render_configured_template(request, course, exercise, post_url, default=None, result=None):
    '''
    Renders a configured or optional default template.

    @type request: C{django.http.request.HttpRequest}
    @param request: a request to handle
    @type course: C{dict}
    @param course: a course configuration
    @type exercise: C{dict}
    @param exercise: an exercise configuration
    @type post_url: C{str}
    @param post_url: the post URL for the exercise
    @type default: C{str}
    @param default: a default template name to use if not configured
    @type result: C{dict}
    @param result: results from grading
    @rtype: C{django.http.response.HttpResponse}
    @return: a response
    '''
    template = None
    if "template" in exercise:
        template = exercise["template"]
    elif default is not None:
        template = default
    else:
        raise ConfigError("Missing \"template\" in exercise configuration.")

    return render_template(request, course, exercise, post_url, template, result)
Exemple #2
0
def host_path(mounts: Dict[str, str], path: str):
    path = os.path.realpath(path)
    for k,v in mounts.items():
        if path.startswith(k):
            return path.replace(k, v)

    raise ConfigError(f"Could not find where {path} is mounted")
Exemple #3
0
def gitlabquery(course, exercise, action, submission_dir):
    '''
    Queries gitlab API to check repository properties.
    '''
    if not "require_gitlab" in exercise:
        raise ConfigError("This action needs require_gitlab in exercise.")
    if not "token" in action:
        raise ConfigError(
            "Token missing from configuration for gitlab privacy check.")
    url = None
    err = ""
    try:
        with open(submission_dir + "/user/gitsource") as content:
            source = content.read()
        try:
            from urllib.parse import quote_plus
        except ImportError:
            from urllib import quote_plus
        rid = quote_plus(source[source.index(":") + 1:])
        url = "https://%s/api/v3/projects/%s?private_token=%s" % (
            exercise["require_gitlab"], rid, action["token"])
        data = get_json(url)
        if "private" in action and action["private"] and data["public"]:
            err = "%s has public access in settings! Remove it to grade exercises." % (
                data["web_url"])
        if "forks" in action:
            if not "forked_from_project" in data or \
                data["forked_from_project"]["path_with_namespace"] != action["forks"]:
                err = "%s is not forked from %s." % (data["web_url"],
                                                     action["forks"])
    except Exception:
        LOGGER.exception("Failed to check gitlab URL: %s", url)
    return {
        "points": 0,
        "max_points": 0,
        "out": "",
        "err": err,
        "stop": err != ""
    }
Exemple #4
0
def invoke_sandbox(course_key, action, dirarg=None, without_sandbox=False):
    '''
    Invokes a configured command in the sandbox environment.

    @type action: C{dict}
    @param action: action configuration
    @type dirarg: C{str}
    @param dirarg: a submission directory to grade
    @rtype: C{dict}
    @return: code = process return code, out = standard out, err = standard error
    '''
    if not "cmd" in action or not isinstance(action["cmd"], list):
        raise ConfigError("Missing list \"cmd\" from action configuration")

    if not without_sandbox and os.path.isfile(settings.SANDBOX_RUNNER):
        cmd = [settings.SANDBOX_RUNNER]
    else:
        cmd = [settings.SANDBOX_FALLBACK]

    if "net" in action and (action["net"] is True
                            or str(action["net"]).lower() in ('true', 'yes')):
        cmd.append("net")

    for key in ("time", "memory", "files", "disk"):
        if key in action:
            cmd.append(str(action[key]))
        else:
            cmd.append(str(settings.SANDBOX_LIMITS[key]))

    if dirarg:
        if "dir" in action:
            if action["dir"] == ".":
                cmd.append(dirarg)
            else:
                cmd.append(os.path.join(dirarg, action["dir"]))
        else:
            cmd.append(os.path.join(dirarg, "user"))
    else:
        cmd.append("-")
    cmd.append(course_key)

    if without_sandbox:
        cmd.append("without_sandbox")

    cmd.extend(action["cmd"])

    return invoke(cmd)
Exemple #5
0
def store_user_files(course,
                     exercise,
                     action,
                     submission_dir,
                     user_ids,
                     submission_number=1):
    '''
    Stores files from the submission directory to the personal directory of the user(s).
    '''
    if not (settings.ENABLE_PERSONAL_DIRECTORIES and \
            "personalized" in exercise and exercise["personalized"]):
        msg = 'Action "grader.actions.store_user_files" can only be used in personalized exercises. ' \
            'Check project settings.ENABLE_PERSONAL_DIRECTORIES value and exercise-specific personalization configuration.'
        LOGGER.error(msg)
        raise ConfigError(msg)
    args = {
        "target": user_personal_directory_path(course, exercise, user_ids),
    }

    return _boolean(
        invoke_script(settings.STORE_USER_FILES_SCRIPT,
                      _collect_args(("cp", ), action, args), submission_dir))
Exemple #6
0
def expaca(course, exercise, action, submission_dir):
    '''
    Executes third party expaca testing application.
    '''
    r = invoke_script(
        settings.EXPACA_SCRIPT,
        _collect_args(("rule_file", "model_dir", "user_dir"), action),
        submission_dir)

    # Expaca should always return 0.
    if r["code"] != 0:
        raise ConfigError(
            "Expaca return code not zero!\nMore information: %s" % (str(r)))
    out = r["out"]
    points = 0
    max_points = 0

    # Find points in the XML.
    b = out.find("<TotalPoints>")
    if b >= 0:
        e = out.find("</TotalPoints>", b + 13)
        points = int(out[b + 13:e])
    b = out.find("<TotalMaxpoints>")
    if b >= 0:
        e = out.find("</TotalMaxpoints>", b + 16)
        max_points = int(out[b + 16:e])

    # Transform the feedback if configured.
    if "xslt_transform" in action:
        out = transform(
            out, "%s/%s" % (settings.BASE_DIR, action["xslt_transform"]))

    return {
        "points": points,
        "max_points": max_points,
        "out": out,
        "err": r["err"],
        "stop": False
    }
Exemple #7
0
def runactions(course,
               exercise,
               submission_dir,
               user_ids="",
               submission_number=1):
    '''
    Runs configured grading actions for an exercise submission.

    @type course: C{dict}
    @param course: a course configuration
    @type exercise: C{dict}
    @param exercise: an exercise configuration
    @type submission_dir: C{str}
    @param submission_dir: a submission directory where submitted files are stored
    @type user_ids: C{str}
    @param user_ids: user id(s) of the submitter(s) for personalized exercises
    @type submission_number: C{int}
    @param submission_number: ordinal number of the submission (parameter in the grader protocol)
    @rtype: C{dict}
    @return: template = template name, result = points, max_points, tests
    '''
    total_points = 0
    max_points = 0
    total_result = []
    error = False
    has_appendixes = False

    # Try to run the grading actions.
    try:
        for action in exercise["actions"]:
            exgrader = None
            try:
                exgrader = import_named(course, action["type"])
            except ImproperlyConfigured as e:
                raise ConfigError(
                    "Invalid action \"type\" in exercise configuration.", e)

            # Run the exercise grader action
            LOGGER.debug("Running action \"%s\"", action["type"])
            if action["type"] == "grader.actions.prepare" or \
                    action["type"] == "grader.actions.store_user_files":
                r = exgrader(course, exercise, action, submission_dir,
                             user_ids, submission_number)
            else:
                r = exgrader(course, exercise, action, submission_dir)
            has_appendixes = has_appendixes or \
                ("appendix" in r and r["appendix"])

            # Configured template values.
            if "title" in action:
                r["title"] = action["title"]
            if "html" in action and action["html"]:
                r["html"] = True

            # Override with configured points.
            if "points" in action:
                r["max_points"] = action["points"]
                if r["stop"]:
                    r["points"] = 0
                else:
                    r["points"] = action["points"]
            elif "max_points" in action:
                r["max_points"] = action["max_points"]
                if r["points"] > action["max_points"]:
                    r["points"] = action["max_points"]

            # Sum total numbers.
            total_result.append(r)
            total_points += r["points"]
            if "max_points" in r:
                max_points += r["max_points"]
            if r["stop"]:
                if "expect_success" in action:
                    error = action["expect_success"]
                if not ("continue_after_error" in action
                        and action["continue_after_error"]):
                    break  # skip the subsequent actions

        # Override with configured max points.
        if "max_points" in exercise:
            max_points = exercise["max_points"]

        # Check the points are in range.
        if total_points > max_points:
            total_points = max_points
        elif total_points < 0:
            total_points = 0

        # Determine template.
        template = None
        if "feedback_template" in exercise:
            template = exercise["feedback_template"]
        else:
            template = "access/task_success.html"

        return {
            "template": template,
            "result": {
                "points": total_points,
                "max_points": max_points,
                "tests": total_result,
                "error": error,
                "has_appendixes": has_appendixes,
            }
        }

    finally:
        clean_submission_dir(submission_dir)