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 _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 get_task_filelist(cls, courseid, taskid): """ Returns a flattened version of all the files inside the task directory, excluding the files task.* and hidden files. It returns a list of tuples, of the type (Integer Level, Boolean IsDirectory, String Name, String CompleteName) """ path = os.path.join(get_tasks_directory(), courseid, taskid) if not os.path.exists(path): return [] result_dict = {} for root, _, files in os.walk(path): rel_root = os.path.normpath(os.path.relpath(root, path)) insert_dict = result_dict if rel_root != ".": hidden_dir = False for i in rel_root.split(os.path.sep): if i.startswith("."): hidden_dir = True break if i not in insert_dict: insert_dict[i] = {} insert_dict = insert_dict[i] if hidden_dir: continue for f in files: # Do not follow symlinks and do not take into account task describers if not os.path.islink( os.path.join( root, f)) and not ( root == path and os.path.splitext(f)[0] == "task" and os.path.splitext(f)[1][ 1:] in get_available_task_file_managers().keys()) and not f.startswith( "."): insert_dict[f] = None def recur_print(current, level, current_name): iteritems = sorted(current.iteritems()) # First, the files recur_print.flattened += [(level, False, f, os.path.join(current_name, f)) for f, t in iteritems if t is None] # Then, the dirs for name, sub in iteritems: if sub is not None: recur_print.flattened.append((level, True, name, os.path.join(current_name, name))) recur_print(sub, level + 1, os.path.join(current_name, name)) recur_print.flattened = [] recur_print(result_dict, 0, '') return recur_print.flattened
def verify_path(self, courseid, taskid, path, new_path=False): """ Return the real wanted path (relative to the INGInious root) or None if the path is not valid/allowed """ task_dir_path = os.path.join(get_tasks_directory(), courseid, taskid) # verify that the dir exists if not os.path.exists(task_dir_path): return None wanted_path = os.path.normpath(os.path.join(task_dir_path, path)) rel_wanted_path = os.path.relpath(wanted_path, task_dir_path) # normalized # verify that the path we want exists and is withing the directory we want if (new_path == os.path.exists(wanted_path)) or os.path.islink(wanted_path) or rel_wanted_path.startswith('..'): return None # do not allow touching the task.* file if os.path.splitext(rel_wanted_path)[0] == "task" and os.path.splitext(rel_wanted_path)[1][1:] in get_available_task_file_managers().keys(): return None # do not allow hidden dir/files if rel_wanted_path != ".": for i in rel_wanted_path.split(os.path.sep): if i.startswith("."): return None return wanted_path
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"})