Пример #1
0
    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))
Пример #2
0
    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))
Пример #3
0
    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
Пример #4
0
    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
Пример #5
0
    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"})