Beispiel #1
0
def get_readable_tasks(courseid):
    """ Returns the list of all available tasks in a course """
    tasks = [
        task for task in os.listdir(os.path.join(get_tasks_directory(), courseid))
        if os.path.isdir(os.path.join(get_tasks_directory(), courseid, task))
        and _task_file_exists(os.path.join(get_tasks_directory(), courseid, task))]
    return tasks
    def _synchronize_task_dir_p2(self, agent, local_td, generated_files, async_value_remote_td):
        """ Synchronizes the task directory with the remote agent, part 2 """
        try:
            remote_td = copy.deepcopy(async_value_remote_td.value)
        except:
            print "An error occured while retrieving list of files in the task dir from remote agent"
            return

        if remote_td is None:  # sync disabled for this Agent
            return

        to_update, to_delete = directory_compare_from_hash(local_td, remote_td)
        tmpfile = tempfile.TemporaryFile()
        tar = tarfile.open(fileobj=tmpfile, mode='w:gz')
        for path in to_update:
            # be a little safe about what the agent returns...
            if os.path.relpath(os.path.join(get_tasks_directory(), path), get_tasks_directory()) == path and ".." not in path:
                if path in generated_files:  # the file do not really exists on disk, it was generated
                    info = tarfile.TarInfo(name=path)
                    info.size = generated_files[path].len
                    info.mode = 0o777
                    tar.addfile(tarinfo=info, fileobj=generated_files[path])
                else:  # the file really exists on disk
                    tar.add(arcname=path, name=os.path.join(get_tasks_directory(), path))
            else:
                print "Agent returned non-safe file path: " + path
        tar.close()
        tmpfile.flush()
        tmpfile.seek(0)

        # sync the agent
        async_update = rpyc.async(agent.root.update_task_directory)
        # do not forget to close the file
        async_update(tmpfile, to_delete).add_callback(lambda r: tmpfile.close())
Beispiel #3
0
 def get_all_courses(cls):
     """Returns a table containing courseid=>Course pairs."""
     files = [os.path.splitext(f)[0] for f in os.listdir(get_tasks_directory()) if
              os.path.isfile(os.path.join(get_tasks_directory(), f, "course.yaml")) or
              os.path.isfile(os.path.join(get_tasks_directory(), f, "course.json"))]
     output = {}
     for course in files:
         try:
             output[course] = cls(course)
         except Exception as e:  # todo log the error
             print e
     return output
Beispiel #4
0
    def GET(self, courseid, taskid, path):
        """ GET request """
        if User.is_logged_in():
            try:
                course = FrontendCourse(courseid)
                if not course.is_open_to_user(User.get_username(), course.is_group_course()):
                    return renderer.course_unavailable()

                task = course.get_task(taskid)
                if not task.is_visible_by_user(User.get_username()):
                    return renderer.task_unavailable()

                path_norm = posixpath.normpath(urllib.unquote(path))
                public_folder_path = os.path.normpath(os.path.realpath(os.path.join(get_tasks_directory(), courseid, taskid, "public")))
                file_path = os.path.normpath(os.path.realpath(os.path.join(public_folder_path, path_norm)))

                # Verify that we are still inside the public directory
                if os.path.normpath(os.path.commonprefix([public_folder_path, file_path])) != public_folder_path:
                    raise web.notfound()

                if os.path.isfile(file_path):
                    mimetypes.init()
                    mime_type = mimetypes.guess_type(file_path)
                    web.header('Content-Type', mime_type[0])
                    with open(file_path) as static_file:
                        return static_file.read()
                else:
                    raise web.notfound()
            except:
                if web.config.debug:
                    raise
                else:
                    raise web.notfound()
        else:
            return renderer.index(False)
    def start(self):
        # init the synchronization of task directories
        self._last_content_in_task_directory = directory_content_with_hash(get_tasks_directory())
        threading.Timer((30 if not self._is_testing else 2), self._try_synchronize_task_dir).start()

        # connect to agents
        self._try_agent_connection()
Beispiel #6
0
def delete_all_possible_task_files(courseid, taskid):
    """ Deletes all possibles task files in directory, to allow to change the format """
    for ext in get_available_task_file_managers().keys():
        try:
            os.remove(os.path.join(get_tasks_directory(), courseid, taskid, "task.{}".format(ext)))
        except:
            pass
Beispiel #7
0
 def test_init(self):
     """ Test the initialisation of the common lib """
     init_common_lib(os.path.join(os.path.dirname(__file__), 'tasks'),
                     [".c"],
                     1024 * 1024)
     assert os.path.abspath(get_tasks_directory()) == os.path.abspath(os.path.join(os.path.dirname(__file__), 'tasks'))
     assert get_allowed_file_extensions() == [".c"]
     assert get_max_file_size() == 1024 * 1024
Beispiel #8
0
 def _get_course_descriptor_path(cls, courseid):
     """Returns the path to the file that describes the course 'courseid'"""
     if not id_checker(courseid):
         raise Exception("Course with invalid name: " + courseid)
     base_file = os.path.join(get_tasks_directory(), courseid, "course")
     if os.path.isfile(base_file + ".yaml"):
         return base_file + ".yaml"
     else:
         return base_file + ".json"
    def _try_synchronize_task_dir(self):
        """ Check if the remote tasks dirs (on the remote agents) should be updated """
        if self._closed:
            return

        current_content_in_task_directory = directory_content_with_hash(get_tasks_directory())
        changed, deleted = directory_compare_from_hash(current_content_in_task_directory, self._last_content_in_task_directory)
        if len(changed) != 0 or len(deleted) != 0:
            self._last_content_in_task_directory = current_content_in_task_directory
            for agent in self._agents:
                if agent is not None:
                    self._synchronize_task_dir(agent)

        if not self._is_testing:
            threading.Timer(30, self._try_synchronize_task_dir).start()
Beispiel #10
0
    def action_delete(self, courseid, taskid, path):
        """ Delete a file or a directory """

        wanted_path = self.verify_path(courseid, taskid, path)
        if wanted_path is None:
            return self.show_tab_file(courseid, taskid, "Internal error")

        # special case: cannot delete current directory of the task
        if "." == os.path.relpath(wanted_path, os.path.join(get_tasks_directory(), courseid, taskid)):
            return self.show_tab_file(courseid, taskid, "Internal error")

        if os.path.isdir(wanted_path):
            shutil.rmtree(wanted_path)
        else:
            os.unlink(wanted_path)
        return self.show_tab_file(courseid, taskid)
Beispiel #11
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
Beispiel #12
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
Beispiel #13
0
    def action_upload(self, courseid, taskid, path, fileobj):
        """ Upload a file """

        wanted_path = self.verify_path(courseid, taskid, path, True)
        if wanted_path is None:
            return self.show_tab_file(courseid, taskid, "Invalid new path")
        curpath = os.path.join(get_tasks_directory(), courseid, taskid)
        rel_path = os.path.relpath(wanted_path, curpath)

        for i in rel_path.split(os.path.sep)[:-1]:
            curpath = os.path.join(curpath, i)
            if not os.path.exists(curpath):
                os.mkdir(curpath)
            if not os.path.isdir(curpath):
                return self.show_tab_file(courseid, taskid, i + " is not a directory!")

        try:
            open(wanted_path, "w").write(fileobj.file.read())
            return self.show_tab_file(courseid, taskid)
        except:
            return self.show_tab_file(courseid, taskid, "An error occurred while writing the file")
Beispiel #14
0
    def action_create(self, courseid, taskid, path):
        """ Delete a file or a directory """

        want_directory = path.strip().endswith("/")

        wanted_path = self.verify_path(courseid, taskid, path, True)
        if wanted_path is None:
            return self.show_tab_file(courseid, taskid, "Invalid new path")
        curpath = os.path.join(get_tasks_directory(), courseid, taskid)
        rel_path = os.path.relpath(wanted_path, curpath)

        for i in rel_path.split(os.path.sep)[:-1]:
            curpath = os.path.join(curpath, i)
            if not os.path.exists(curpath):
                os.mkdir(curpath)
            if not os.path.isdir(curpath):
                return self.show_tab_file(courseid, taskid, i + " is not a directory!")
        if rel_path.split(os.path.sep)[-1] != "":
            if want_directory:
                os.mkdir(os.path.join(curpath, rel_path.split(os.path.sep)[-1]))
            else:
                open(os.path.join(curpath, rel_path.split(os.path.sep)[-1]), 'a')
        return self.show_tab_file(courseid, taskid)
Beispiel #15
0
def get_task_file_manager(courseid, taskid):
    """ Returns the appropriate task file manager for this task """
    for ext, subclass in get_available_task_file_managers().iteritems():
        if os.path.isfile(os.path.join(get_tasks_directory(), courseid, taskid, "task.{}".format(ext))):
            return subclass(courseid, taskid)
    return None
Beispiel #16
0
 def read(self):
     """ Read the file describing the task and returns a dict """
     return self._get_content(codecs.open(os.path.join(get_tasks_directory(), self._courseid, self._taskid, "task." + self.get_ext()), "r",
                                          'utf-8').read())
Beispiel #17
0
 def get_course_tasks_directory(self):
     """Return the complete path to the tasks directory of the course"""
     return os.path.join(get_tasks_directory(), self._id)
Beispiel #18
0
 def write(self, data):
     """ Write data to the task file """
     with codecs.open(os.path.join(get_tasks_directory(), self._courseid, self._taskid, "task." + self.get_ext()), "w", 'utf-8') as task_desc_file:
         task_desc_file.write(self._generate_content(data))
Beispiel #19
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"})