Init the external grader plugin. This simple grader allows only anonymous requests, and submissions are not stored in database.

        Available configuration:
        ::

            plugins:
                - plugin_module: inginious.frontend.plugins.simple_grader
                  courseid : "external"
                  page_pattern: "/external"
                  return_fields: "^(result|text|problems)$"

        The grader will only return fields that are in the job return dict if their key match return_fields.

        Different types of request are available : see documentation

def init(plugin_manager, course_factory, client, config):
    """
        Init the external grader plugin. This simple grader allows only anonymous requests, and submissions are not stored in database.

        Available configuration:
        ::

            plugins:
                - plugin_module: inginious.frontend.plugins.simple_grader
                  courseid : "external"
                  page_pattern: "/external"
                  return_fields: "^(result|text|problems)$"

        The grader will only return fields that are in the job return dict if their key match return_fields.

        Different types of request are available : see documentation
    """
    courseid = config.get('courseid', 'external')
    course = course_factory.get_course(courseid)
    page_pattern = config.get('page_pattern', '/external')
    return_fields = re.compile(config.get('return_fields', '^(result|text|problems)$'))

    client_buffer = ClientBuffer(client)
    client_sync = ClientSync(client)

    class ExternalGrader(INGIniousPage):

        """ Manages job from outside, using the default input """

        def GET(self):
            """ GET request """
            return """
            <!DOCTYPE html>
            <html>
                <head>
                    <title>External grade POST test</title>
                </head>
                <body>
                    <form method="post">
                        <textarea style="width:100%; height:400px;" name="input">{"question1":"print 'Hello World!'"}</textarea><br/>
                        <input type="text" name="taskid" value="helloworld"/> (taskid)<br/>
                        <input type="checkbox" name="async"/> async?<br/>
                        <input type="submit"/>
                    </form>
                </body>
            </html>"""

        def keep_only_config_return_values(self, job_return):
            """ Keep only some useful return values """
            return {key: value for key, value in job_return.items() if return_fields.match(key)}

        def POST(self):
            """ POST request """
            web.header('Access-Control-Allow-Origin', '*')
            web.header('Content-Type', 'application/json')
            post_input = web.input()

            if "input" in post_input and "taskid" in post_input:
                # New job
                try:
                    task_input = json.loads(post_input.input)
                except:
                    return json.dumps({"status": "error", "status_message": "Cannot decode input"})

                try:
                    task = course.get_task(post_input.taskid)
                except:
                    return json.dumps({"status": "error", "status_message": "Cannot open task"})

                if not task.input_is_consistent(task_input, self.default_allowed_file_extensions, self.default_max_file_size):
                    return json.dumps({"status": "error", "status_message": "Input is not consistent with the task"})

                if post_input.get("async") is None:
                    # New sync job
                    try:
                        result, grade, problems, tests, custom, state, archive, stdout, stderr = client_sync.new_job(task, task_input, "Plugin - Simple Grader")
                        job_return = {"result":result, "grade": grade, "problems": problems, "tests": tests, "custom": custom, "state": state, "archive": archive, "stdout": stdout, "stderr": stderr}
                    except:
                        return json.dumps({"status": "error", "status_message": "An internal error occurred"})

                    return json.dumps(dict(list({"status": "done"}.items()) + list(self.keep_only_config_return_values(job_return).items())))
                else:
                    # New async job
                    jobid = client_buffer.new_job(task, task_input, "Plugin - Simple Grader")
                    return json.dumps({"status": "done", "jobid": str(jobid)})
            elif "jobid" in post_input:
                # Get status of async job
                if client_buffer.is_waiting(post_input["jobid"]):
                    return json.dumps({"status": "waiting"})
                elif client_buffer.is_done(post_input["jobid"]):
                    result, grade, problems, tests, custom, state, archive, stdout, stderr = client_buffer.get_result(post_input["jobid"])
                    job_return = {"result": result, "grade": grade, "problems": problems, "tests": tests,
                                  "custom": custom, "archive": archive, "stdout": stdout, "stderr": stderr}
                    return json.dumps(dict(list({"status": "done"}.items()) + list(self.keep_only_config_return_values(job_return).items())))
                else:
                    return json.dumps({"status": "error", "status_message": "There is no job with jobid {}".format(post_input["jobid"])})
            else:
                return json.dumps({"status": "error", "status_message": "Unknown request type"})

    plugin_manager.add_page(page_pattern, ExternalGrader)