def GET(self, courseid, taskid): """ Edit a task """ if not id_checker(taskid): raise Exception("Invalid task id") course, _ = get_course_and_check_rights(courseid, allow_all_staff=False) try: task_data = get_task_file_manager(courseid, taskid).read() except: task_data = None if task_data is None: task_data = {} environments = INGIniousConfiguration["containers"].keys() current_filetype = None try: current_filetype = get_task_file_manager(courseid, taskid).get_ext() except: pass available_filetypes = get_available_task_file_managers().keys() # custom problem-type: for pid in task_data.get("problems", {}): problem = task_data["problems"][pid] if (problem["type"] == "code" and "boxes" in problem) or problem["type"] not in ( "code", "code-single-line", "code-file", "match", "multiple-choice"): problem_copy = copy.deepcopy(problem) for i in ["name", "header", "headerIsHTML"]: if i in problem_copy: del problem_copy[i] problem["custom"] = common.custom_yaml.dump(problem_copy) return renderer.course_admin.edit_task( course, taskid, task_data, environments, json.dumps( task_data.get( 'problems', {})), self.contains_is_html(task_data), current_filetype, available_filetypes, AccessibleTime, CourseTaskFiles.get_task_filelist(courseid, taskid))
def __init__(self, course, taskid, init_data=None): """ Init the task. course is a Course object, taskid the task id, and init_data is a dictionnary containing the data needed to initialize the Task object. If init_data is None, the data will be taken from the course tasks' directory. """ if not id_checker(taskid): raise Exception("Task with invalid id: " + course.get_id() + "/" + taskid) self._course = course self._taskid = taskid if init_data is None: try: self._data = get_task_file_manager(self.get_course_id(), self.get_id()).read() except Exception as inst: raise Exception("Error while reading task file: " + self._course.get_id() + "/" + self._taskid + " :\n" + str(inst)) else: self._data = init_data self._environment = self._data.get('environment', None) #Response is HTML self._response_is_html = self._data.get("responseIsHTML", False) # Limits self._limits = {"time": 20, "memory": 1024, "disk": 1024} if "limits" in self._data: try: self._limits['time'] = int(self._data["limits"].get("time", 20)) self._limits['memory'] = int(self._data["limits"].get("memory", 1024)) self._limits['disk'] = int(self._data["limits"].get("disk", 1024)) except: raise Exception("Invalid limit") if "problems" not in self._data: raise Exception("Tasks must have some problems descriptions") # Check all problems self._problems = [] for problemid in self._data['problems']: self._problems.append(self._create_task_problem(self, problemid, self._data['problems'][problemid]))
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 _synchronize_task_dir(self, agent): """ Synchronizes the task directory with the remote agent. Steps are: - Get list of (path, file hash) for the main task directory (p1) - Ask agent for a list of all files in their task directory (p1) - Find differences for each agents (p2) - Create an archive with differences (p2) - Send it to each agent (p2) - Agents updates their directory """ local_td = self._last_content_in_task_directory # As agent only supports task.yaml files as descriptors (and not exotic things like task.rst...), we have to ensure that we convert and send # task.yaml files to it. task_files_to_convert = ["task." + ext for ext in get_available_task_file_managers()] task_files_to_convert.remove("task.yaml") new_local_td = {} generated_yaml_content = {} for file_path, data in local_td.iteritems(): match = re.match(r'^([a-zA-Z0-9_\-]+)/([a-zA-Z0-9_\-]+)/(task.[a-z0-9]+)$', file_path) if match is not None and match.group(3) in task_files_to_convert: try: path_to_file, _ = os.path.split(file_path) courseid, taskid = match.group(1), match.group(2) content = get_task_file_manager(courseid, taskid).read() yaml_content = StringIO(common.custom_yaml.dump(content).encode('utf-8')) new_local_td[os.path.join(path_to_file, "task.yaml")] = (hash_file(yaml_content), 0o777) yaml_content.seek(0) generated_yaml_content[os.path.join(path_to_file, "task.yaml")] = yaml_content except: print "Cannot convert {} to a yaml file for the agent!".format(file_path) new_local_td[file_path] = data else: new_local_td[file_path] = data async_get_file_list = rpyc.async(agent.root.get_task_directory_hashes) async_get_file_list().add_callback(lambda r: self._synchronize_task_dir_p2(agent, new_local_td, generated_yaml_content, r))
def POST(self, courseid, taskid): """ Edit a task """ if not id_checker(taskid) or not id_checker(courseid): raise Exception("Invalid course/task id") course, _ = get_course_and_check_rights(courseid, allow_all_staff=False) # Parse content try: data = web.input(task_file={}) try: task_zip = data.get("task_file").file except: task_zip = None del data["task_file"] problems = self.dict_from_prefix("problem", data) limits = self.dict_from_prefix("limits", data) data = {key: val for key, val in data.iteritems() if not key.startswith("problem") and not key.startswith("limits")} del data["@action"] try: file_manager = get_available_task_file_managers()[data["@filetype"]](courseid, taskid) except Exception as inst: return json.dumps({"status": "error", "message": "Invalid file type: {}".format(str(inst))}) del data["@filetype"] if problems is None: return json.dumps({"status": "error", "message": "You cannot create a task without subproblems"}) # Order the problems (this line also deletes @order from the result) data["problems"] = OrderedDict([(key, self.parse_problem(val)) for key, val in sorted(problems.iteritems(), key=lambda x: int(x[1]['@order']))]) data["limits"] = limits if "hard_time" in data["limits"] and data["limits"]["hard_time"] == "": del data["limits"]["hard_time"] # Weight try: data["weight"] = float(data["weight"]) except: return json.dumps({"status": "error", "message": "Grade weight must be a floating-point number"}) # Accessible if data["accessible"] == "custom": data["accessible"] = "{}/{}".format(data["accessible_start"], data["accessible_end"]) elif data["accessible"] == "true": data["accessible"] = True else: data["accessible"] = False del data["accessible_start"] del data["accessible_end"] # Checkboxes if data.get("responseIsHTML"): data["responseIsHTML"] = True if data.get("contextIsHTML"): data["contextIsHTML"] = True except Exception as message: return json.dumps({"status": "error", "message": "Your browser returned an invalid form ({})".format(str(message))}) # Get the course try: course = FrontendCourse(courseid) except: return json.dumps({"status": "error", "message": "Error while reading course's informations"}) # Get original data try: orig_data = get_task_file_manager(courseid, taskid).read() data["order"] = orig_data["order"] except: pass try: FrontendTask(course, taskid, data) except Exception as message: return json.dumps({"status": "error", "message": "Invalid data: {}".format(str(message))}) if not os.path.exists(os.path.join(get_tasks_directory(), courseid, taskid)): os.mkdir(os.path.join(get_tasks_directory(), courseid, taskid)) if task_zip: try: zipfile = ZipFile(task_zip) except Exception as message: return json.dumps({"status": "error", "message": "Cannot read zip file. Files were not modified"}) try: zipfile.extractall(os.path.join(get_tasks_directory(), courseid, taskid)) except Exception as message: return json.dumps( {"status": "error", "message": "There was a problem while extracting the zip archive. Some files may have been modified"}) delete_all_possible_task_files(courseid, taskid) file_manager.write(data) return json.dumps({"status": "ok"})