Beispiel #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))
Beispiel #2
0
    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]))
Beispiel #3
0
    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))
Beispiel #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"})