def add_job(task, inputdata, debug=False): """ Add a job in the queue and returns a submission id. task is a Task instance and inputdata is the input as a dictionary If debug is true, more debug data will be saved """ if not User.is_logged_in(): raise Exception("A user must be logged in to submit an object") username = User.get_username() course = FrontendCourse(task.get_course_id()) obj = { "courseid": task.get_course_id(), "taskid": task.get_id(), "input": get_gridfs().put( json.dumps(inputdata)), "status": "waiting", "submitted_on": datetime.now()} if course.is_group_course() and username not in course.get_staff(True): group = get_database().groups.find_one({"course_id": task.get_course_id(), "users": username}) obj.update({"username": group["users"]}) else: obj.update({"username": [username]}) submissionid = get_database().submissions.insert(obj) PluginManager.get_instance().call_hook("new_submission", submissionid=submissionid, submission=obj, inputdata=inputdata) get_job_manager().new_job(task, inputdata, (lambda job: _job_done_callback(submissionid, task, job)), "Frontend - {}".format(username), debug) return submissionid
def get_app(config_file): """ Get the application. config_file is the path to the JSON configuration file """ appli = web.application(urls, globals(), autoreload=False) common.base.INGIniousConfiguration.load(config_file) frontend.base.init_database() update_database() frontend.session.init(appli) def not_found(): """ Display the error 404 page """ return web.notfound(frontend.base.renderer.notfound('Page not found')) appli.notfound = not_found plugin_manager = PluginManager(appli, common.base.INGIniousConfiguration.get("plugins", [])) # Plugin Manager is also a Hook Manager submission_manager.init_backend_interface(plugin_manager) # Loads template_helper TemplateHelper() # Loads plugins plugin_manager.load() return appli
def _job_done_callback(submissionid, task, job): """ Callback called by JobManager when a job is done. Updates the submission in the database with the data returned after the completion of the job """ submission = get_submission(submissionid, False) submission = get_input_from_submission(submission) job = _parse_text(task, job) data = { "status": ("done" if job["result"] == "success" or job["result"] == "failed" else "error"), # error only if error was made by INGInious "result": job["result"], "grade": job["grade"], "text": job.get("text", None), "tests": job.get("tests", None), "problems": (job["problems"] if "problems" in job else {}), "archive": (get_gridfs().put(base64.b64decode(job["archive"])) if "archive" in job else None) } # Store additional data dont_dump = ["task", "course", "input"] for index in job: if index not in data and index not in dont_dump: data[index] = job[index] # Save submission to database get_database().submissions.update( {"_id": submission["_id"]}, {"$set": data} ) for username in submission["username"]: UserData(username).update_stats(submission, job) PluginManager.get_instance().call_hook("submission_done", submission=submission, job=job)
def add_job(task, inputdata, debug=False): """ Add a job in the queue and returns a submission id. task is a Task instance and inputdata is the input as a dictionary If debug is true, more debug data will be saved """ if not User.is_logged_in(): raise Exception("A user must be logged in to submit an object") username = User.get_username() jobid = get_job_manager().new_job_id() obj = { "username": username, "courseid": task.get_course_id(), "taskid": task.get_id(), "input": get_gridfs().put( json.dumps(inputdata)), "status": "waiting", "jobid": jobid, "submitted_on": datetime.now()} submissionid = get_database().submissions.insert(obj) PluginManager.get_instance().call_hook("new_submission", submissionid=submissionid, submission=obj, jobid=jobid, inputdata=inputdata) get_job_manager().new_job(task, inputdata, job_done_callback, "Frontend - {}".format(username), jobid, debug) return submissionid
def _javascript_helper(cls, position): """ Add javascript links for the current page and for the plugins """ if position not in ["header", "footer"]: position = "footer" # Load javascript files from plugins if position == "header": entries = [entry for entry in PluginManager.get_instance().call_hook("javascript_header") if entry is not None] else: entries = [entry for entry in PluginManager.get_instance().call_hook("javascript_footer") if entry is not None] # Load javascript for the current page entries += cls.get_instance()._get_ctx()["javascript"][position] entries = ["<script src='" + entry + "' type='text/javascript' charset='utf-8'></script>" for entry in entries] return "\n".join(entries)
def _css_helper(cls): """ Add CSS links for the current page and for the plugins """ entries = [entry for entry in PluginManager.get_instance().call_hook("css") if entry is not None] # Load javascript for the current page entries += cls.get_instance()._get_ctx()["css"] entries = ["<link href='" + entry + "' rel='stylesheet'>" for entry in entries] return "\n".join(entries)
def get_menu(course, current): """ Returns the HTML of the menu used in the administration. ```current``` is the current page of section """ custom_renderer = get_template_renderer('templates/') default_entries = [("settings", "<span class='glyphicon glyphicon-cog'></span> Course settings"), ("students", "<span class='glyphicon glyphicon-user'></span> Students"), ("tasks", "<span class='glyphicon glyphicon-tasks'></span> Tasks")] # Hook should return a tuple (link,name) where link is the relative link from the index of the course administration. additionnal_entries = [entry for entry in PluginManager.get_instance().call_hook('course_admin_menu', course=course) if entry is not None] return custom_renderer.course_admin.menu(course, default_entries + additionnal_entries, current)
def __init__(self, course, taskid, init_data=None): # We load the descriptor of the task here to allow plugins to modify settings of the task before it is read by the Task constructor if not id_checker(taskid): raise Exception("Task with invalid id: " + course.get_id() + "/" + taskid) if init_data is None: try: init_data = get_task_file_manager(course.get_id(), taskid).read() except Exception as inst: raise Exception("Error while reading task file: " + self._course.get_id() + "/" + self._taskid + " :\n" + str(inst)) PluginManager.get_instance().call_hook('modify_task_data', course=course, taskid=taskid, data=init_data) # Now init the task common.tasks.Task.__init__(self, course, taskid, init_data) self._name = self._data.get('name', 'Task {}'.format(taskid)) self._context = ParsableText(self._data.get('context', ""), "HTML" if self._data.get("contextIsHTML", False) else "rst") # Authors if isinstance(self._data.get('author'), basestring): # verify if author is a string self._author = [self._data['author']] elif isinstance(self._data.get('author'), list): # verify if author is a list for author in self._data['author']: if not isinstance(author, basestring): # authors must be strings raise Exception("This task has an invalid author") self._author = self._data['author'] else: self._author = [] # Grade weight self._weight = float(self._data.get("weight", 1.0)) # _accessible self._accessible = AccessibleTime(self._data.get("accessible", None)) # Order self._order = int(self._data.get('order', -1))
def get_menu(course, current): """ Returns the HTML of the menu used in the administration. ```current``` is the current page of section """ custom_renderer = get_template_renderer('templates/') default_entries = [] if User.get_username() in course.get_admins(): default_entries += [("settings", "<i class='fa fa-cog fa-fw'></i> Course settings"), ("batch", "<i class='fa fa-rocket fa-fw'></i> Batch operations")] default_entries += [("students", "<i class='fa " + ("fa-group" if course.is_group_course() else "fa-user") + " fa-fw'></i> " + ("Groups" if course.is_group_course() else "Students"))] default_entries += [("tasks", "<i class='fa fa-tasks fa-fw'></i> Tasks")] # Hook should return a tuple (link,name) where link is the relative link from the index of the course administration. additionnal_entries = [entry for entry in PluginManager.get_instance().call_hook('course_admin_menu', course=course) if entry is not None] return custom_renderer.course_admin.menu(course, default_entries + additionnal_entries, current)
def call(self, name, **kwargs): helpers = dict(self._base_helpers.items() + PluginManager.get_instance().call_hook("template_helper")) if helpers.get(name, None) is None: return "" else: return helpers.get(name, None)(**kwargs)
def generic_hook(name, **kwargs): """ A generic hook that links the TemplateHelper with PluginManager """ entries = [entry for entry in PluginManager.get_instance().call_hook(name, **kwargs) if entry is not None] return "\n".join(entries)
def connect(auth_method_id, login_data): """ Connect throught plugins """ return PluginManager.get_instance().get_auth_method_callback(auth_method_id)(login_data)