Beispiel #1
0
    def POST_AUTH(self, courseid):  # pylint: disable=arguments-differ
        """ GET request """
        course, __ = self.get_course_and_check_rights(courseid,
                                                      allow_all_staff=False)

        # Tags
        tags = dict_from_prefix("tags", web.input())
        if tags is None:
            tags = {}

        tags_id = [tag["id"] for key, tag in tags.items() if tag["id"]]

        if len(tags_id) != len(set(tags_id)):
            return self.show_page(
                course, False,
                _("Some tags have the same id! The id of a tag must be unique."
                  ))

        tags = {tag["id"]: tag for key, tag in tags.items() if tag["id"]}

        # Repair tags
        for key, tag in tags.items():
            # Since unchecked checkboxes are not present here, we manually add them to avoid later errors
            tag["visible"] = "visible" in tag
            tag["type"] = int(tag["type"])

            if (tag["id"] == "" and tag["type"] != 2) or tag["name"] == "":
                return self.show_page(course, False,
                                      _("Some tag fields were missing."))

            if not id_checker(tag["id"]):
                return self.show_page(
                    course, False,
                    _("Invalid tag id: {}").format(tag["id"]))

            del tag["id"]

        course_content = self.course_factory.get_course_descriptor_content(
            courseid)
        course_content["tags"] = tags
        self.course_factory.update_course_descriptor_content(
            courseid, course_content)

        course, __ = self.get_course_and_check_rights(courseid,
                                                      allow_all_staff=False)

        return self.show_page(course, True, "")
Beispiel #2
0
    def POST_AUTH(self, courseid, taskid):  # pylint: disable=arguments-differ
        """ Edit a task """
        if not id_checker(taskid) or not id_checker(courseid):
            raise NotFound(description=_("Invalid course/task id"))

        course, __ = self.get_course_and_check_rights(courseid,
                                                      allow_all_staff=False)
        data = flask.request.form.copy()
        data["task_file"] = flask.request.files.get("task_file")

        # Delete task ?
        if "delete" in data:
            toc = course.get_task_dispenser().get_dispenser_data()
            toc.remove_task(taskid)
            self.course_factory.update_course_descriptor_element(
                courseid, 'toc', toc.to_structure())
            self.task_factory.delete_task(courseid, taskid)
            if data.get("wipe", False):
                self.wipe_task(courseid, taskid)
            return redirect(self.app.get_homepath() + "/admin/" + courseid +
                            "/tasks")

        # Else, parse content
        try:
            try:
                task_zip = data.get("task_file").read()
            except:
                task_zip = None
            del data["task_file"]

            problems = dict_from_prefix("problem", data)
            environment_type = data.get("environment_type", "")
            environment_parameters = dict_from_prefix("envparams", data).get(
                environment_type, {})
            environment_id = dict_from_prefix("environment_id",
                                              data).get(environment_type, "")

            data = {
                key: val
                for key, val in data.items()
                if not key.startswith("problem") and not key.startswith(
                    "envparams") and not key.startswith("environment_id")
                and not key.startswith("/") and not key == "@action"
            }

            data[
                "environment_id"] = environment_id  # we do this after having removed all the environment_id[something] entries

            # Determines the task filetype
            if data["@filetype"] not in self.task_factory.get_available_task_file_extensions(
            ):
                return json.dumps({
                    "status":
                    "error",
                    "message":
                    _("Invalid file type: {}").format(str(data["@filetype"]))
                })
            file_ext = data["@filetype"]
            del data["@filetype"]

            # Parse and order the problems (also deletes @order from the result)
            if problems is None:
                data["problems"] = OrderedDict([])
            else:
                data["problems"] = OrderedDict([
                    (key, self.parse_problem(val))
                    for key, val in sorted(iter(problems.items()),
                                           key=lambda x: int(x[1]['@order']))
                ])

            # Categories
            course_tags = course.get_tags()
            data['categories'] = [
                cat for cat in map(str.strip, data['categories'].split(','))
                if cat
            ]
            for category in data['categories']:
                if category not in course_tags:
                    return json.dumps({
                        "status": "error",
                        "message": _("Unknown category tag.")
                    })

            # Task environment parameters
            data["environment_parameters"] = environment_parameters

            # Weight
            try:
                data["weight"] = float(data["weight"])
            except:
                return json.dumps({
                    "status":
                    "error",
                    "message":
                    _("Grade weight must be a floating-point number")
                })

            # Groups
            if "groups" in data:
                data["groups"] = True if data["groups"] == "true" else False

            # Submission storage
            if "store_all" in data:
                try:
                    stored_submissions = data["stored_submissions"]
                    data["stored_submissions"] = 0 if data[
                        "store_all"] == "true" else int(stored_submissions)
                except:
                    return json.dumps({
                        "status":
                        "error",
                        "message":
                        _("The number of stored submission must be positive!")
                    })

                if data["store_all"] == "false" and data[
                        "stored_submissions"] <= 0:
                    return json.dumps({
                        "status":
                        "error",
                        "message":
                        _("The number of stored submission must be positive!")
                    })
                del data['store_all']

            # Submission limits
            if "submission_limit" in data:
                if data["submission_limit"] == "none":
                    result = {"amount": -1, "period": -1}
                elif data["submission_limit"] == "hard":
                    try:
                        result = {
                            "amount": int(data["submission_limit_hard"]),
                            "period": -1
                        }
                    except:
                        return json.dumps({
                            "status":
                            "error",
                            "message":
                            _("Invalid submission limit!")
                        })

                else:
                    try:
                        result = {
                            "amount": int(data["submission_limit_soft_0"]),
                            "period": int(data["submission_limit_soft_1"])
                        }
                    except:
                        return json.dumps({
                            "status":
                            "error",
                            "message":
                            _("Invalid submission limit!")
                        })

                del data["submission_limit_hard"]
                del data["submission_limit_soft_0"]
                del data["submission_limit_soft_1"]
                data["submission_limit"] = result

            # Accessible
            if data["accessible"] == "custom":
                data["accessible"] = "{}/{}/{}".format(
                    data["accessible_start"], data["accessible_soft_end"],
                    data["accessible_end"])
            elif data["accessible"] == "true":
                data["accessible"] = True
            else:
                data["accessible"] = False
            del data["accessible_start"]
            del data["accessible_end"]
            del data["accessible_soft_end"]
            try:
                AccessibleTime(data["accessible"])
            except Exception as message:
                return json.dumps({
                    "status":
                    "error",
                    "message":
                    _("Invalid task accessibility ({})").format(message)
                })

            # Checkboxes
            if data.get("responseIsHTML"):
                data["responseIsHTML"] = True

            # Network grading
            data["network_grading"] = "network_grading" in data
        except Exception as message:
            return json.dumps({
                "status":
                "error",
                "message":
                _("Your browser returned an invalid form ({})").format(message)
            })

        # Get the course
        try:
            course = self.course_factory.get_course(courseid)
        except:
            return json.dumps({
                "status":
                "error",
                "message":
                _("Error while reading course's informations")
            })

        # Get original data
        try:
            orig_data = self.task_factory.get_task_descriptor_content(
                courseid, taskid)
            data["order"] = orig_data["order"]
        except:
            pass

        task_fs = self.task_factory.get_task_fs(courseid, taskid)
        task_fs.ensure_exists()

        # Call plugins and return the first error
        plugin_results = self.plugin_manager.call_hook('task_editor_submit',
                                                       course=course,
                                                       taskid=taskid,
                                                       task_data=data,
                                                       task_fs=task_fs)

        # Retrieve the first non-null element
        error = next(filter(None, plugin_results), None)
        if error is not None:
            return error

        try:
            Task(course, taskid, data, self.course_factory.get_fs(),
                 self.plugin_manager, self.task_factory.get_problem_types())
        except Exception as message:
            return json.dumps({
                "status":
                "error",
                "message":
                _("Invalid data: {}").format(str(message))
            })

        if task_zip:
            try:
                zipfile = ZipFile(task_zip)
            except Exception:
                return json.dumps({
                    "status":
                    "error",
                    "message":
                    _("Cannot read zip file. Files were not modified")
                })

            with tempfile.TemporaryDirectory() as tmpdirname:
                try:
                    zipfile.extractall(tmpdirname)
                except Exception:
                    return json.dumps({
                        "status":
                        "error",
                        "message":
                        _("There was a problem while extracting the zip archive. Some files may have been modified"
                          )
                    })
                task_fs.copy_to(tmpdirname)

        self.task_factory.delete_all_possible_task_files(courseid, taskid)
        self.task_factory.update_task_descriptor_content(
            courseid, taskid, data, force_extension=file_ext)

        return json.dumps({"status": "ok"})