def API_POST(self, courseid, taskid):  # pylint: disable=arguments-differ
        """
            Creates a new submissions. Takes as (POST) input the key of the subproblems, with the value assigned each time.

            Returns

            - an error 400 Bad Request if all the input is not (correctly) given,
            - an error 403 Forbidden if you are not allowed to create a new submission for this task
            - an error 404 Not found if the course/task id not found
            - an error 500 Internal server error if the grader is not available,
            - 200 Ok, with {"submissionid": "the submission id"} as output.
        """

        try:
            course = self.course_factory.get_course(courseid)
        except:
            raise APINotFound("Course not found")

        username = self.user_manager.session_username()

        if not self.user_manager.course_is_open_to_user(course, username, False):
            raise APIForbidden("You are not registered to this course")

        try:
            task = course.get_task(taskid)
        except:
            raise APINotFound("Task not found")

        self.user_manager.user_saw_task(username, courseid, taskid)

        # Verify rights
        if not self.user_manager.task_can_user_submit(task, username, False):
            raise APIForbidden("You are not allowed to submit for this task")

        user_input = flask.request.form.copy()
        for problem in task.get_problems():
            pid = problem.get_id()
            if problem.input_type() == list:
                user_input[pid] = flask.request.form.getlist(pid)
            elif problem.input_type() == dict:
                user_input[pid] = flask.request.files.get(pid)
            else:
                user_input[pid] = flask.request.form.get(pid)

        user_input = task.adapt_input_for_backend(user_input)

        if not task.input_is_consistent(user_input, self.default_allowed_file_extensions, self.default_max_file_size):
            raise APIInvalidArguments()

        # Get debug info if the current user is an admin
        debug = self.user_manager.has_admin_rights_on_course(course, username)


        # Start the submission
        try:
            submissionid, _ = self.submission_manager.add_job(task, user_input, debug)
            return 200, {"submissionid": str(submissionid)}
        except Exception as ex:
            raise APIError(500, str(ex))
def _get_submissions(course_factory, submission_manager, user_manager, translations, courseid, taskid, with_input, submissionid=None):
    """
        Helper for the GET methods of the two following classes
    """

    try:
        course = course_factory.get_course(courseid)
    except:
        raise APINotFound("Course not found")

    if not user_manager.course_is_open_to_user(course, lti=False):
        raise APIForbidden("You are not registered to this course")

    try:
        task = course.get_task(taskid)
    except:
        raise APINotFound("Task not found")

    if submissionid is None:
        submissions = submission_manager.get_user_submissions(task)
    else:
        try:
            submissions = [submission_manager.get_submission(submissionid)]
        except:
            raise APINotFound("Submission not found")
        if submissions[0]["taskid"] != task.get_id() or submissions[0]["courseid"] != course.get_id():
            raise APINotFound("Submission not found")

    output = []

    for submission in submissions:
        submission = submission_manager.get_feedback_from_submission(
            submission,
            show_everything=user_manager.has_staff_rights_on_course(course, user_manager.session_username()),
            translation=translations.get(user_manager.session_language(), gettext.NullTranslations())
        )
        data = {
            "id": str(submission["_id"]),
            "submitted_on": str(submission["submitted_on"]),
            "status": submission["status"]
        }

        if with_input:
            data["input"] = submission_manager.get_input_from_submission(submission, True)

            # base64 encode file to allow JSON encoding
            for d in data["input"]:
                if isinstance(d, dict) and d.keys() == {"filename", "value"}:
                    d["value"] = base64.b64encode(d["value"]).decode("utf8")

        if submission["status"] == "done":
            data["grade"] = submission.get("grade", 0)
            data["result"] = submission.get("result", "crash")
            data["feedback"] = submission.get("text", "")
            data["problems_feedback"] = submission.get("problems", {})

        output.append(data)

    return 200, output
Beispiel #3
0
    def API_GET(self, courseid):  # pylint: disable=arguments-differ
        """
            List courses available to the connected client. Returns a dict in the form

            ::

                {
                    "courseid1":
                    {
                        "name": "Name of the course",     #the name of the course
                        "require_password": False,        #indicates if this course requires a password or not
                        "is_registered": False,           #indicates if the user is registered to this course or not
                        "tasks":                          #only appears if is_registered is True
                        {
                            "taskid1": "name of task1",
                            "taskid2": "name of task2"
                            #...
                        },
                        "grade": 0.0                      #the current grade in the course. Only appears if is_registered is True
                    }
                    #...
                }

            If you use the endpoint /api/v0/courses/the_course_id, this dict will contain one entry or the page will return 404 Not Found.
        """
        output = []

        if courseid is None:
            courses = self.course_factory.get_all_courses()
        else:
            try:
                courses = {courseid: self.course_factory.get_course(courseid)}
            except:
                raise APINotFound("Course not found")

        username = self.user_manager.session_username()
        user_info = self.user_manager.get_user_info(username)

        for courseid, course in courses.items():
            if self.user_manager.course_is_open_to_user(course, username, False) or course.is_registration_possible(user_info):
                data = {
                    "id": courseid,
                    "name": course.get_name(self.user_manager.session_language()),
                    "require_password": course.is_password_needed_for_registration(),
                    "is_registered": self.user_manager.course_is_open_to_user(course, username, False)
                }
                if self.user_manager.course_is_open_to_user(course, username, False):
                    data["tasks"] = {taskid: task.get_name(self.user_manager.session_language()) for taskid, task in course.get_tasks().items()}
                    data["grade"] = self.user_manager.get_course_cache(username, course)["grade"]
                output.append(data)

        return 200, output
Beispiel #4
0
    def API_GET(self, courseid, taskid=None):  # pylint: disable=arguments-differ
        """
            List tasks available to the connected client. Returns a dict in the form

            ::

                {
                    "taskid1":
                    {
                        "name": "Name of the course",     #the name of the course
                        "authors": [],
                        "deadline": "",
                        "status": "success"               # can be "succeeded", "failed" or "notattempted"
                        "grade": 0.0,
                        "grade_weight": 0.0,
                        "context": ""                     # context of the task, in RST
                        "problems":                       # dict of the subproblems
                        {
                                                          # see the format of task.yaml for the content of the dict. Contains everything but
                                                          # responses of multiple-choice and match problems.
                        }
                    }
                    #...
                }

            If you use the endpoint /api/v0/courses/the_course_id/tasks/the_task_id, this dict will contain one entry or the page will return 404 Not
            Found.
        """

        try:
            course = self.course_factory.get_course(courseid)
        except:
            raise APINotFound("Course not found")

        if not self.user_manager.course_is_open_to_user(course, lti=False):
            raise APIForbidden("You are not registered to this course")

        if taskid is None:
            tasks = course.get_tasks()
        else:
            try:
                tasks = {taskid: course.get_task(taskid)}
            except:
                raise APINotFound("Task not found")

        output = []
        for taskid, task in tasks.items():
            task_cache = self.user_manager.get_task_cache(
                self.user_manager.session_username(), task.get_course_id(),
                task.get_id())

            data = {
                "id":
                taskid,
                "name":
                task.get_name(self.user_manager.session_language()),
                "authors":
                task.get_authors(self.user_manager.session_language()),
                "deadline":
                task.get_deadline(),
                "status":
                "notviewed" if task_cache is None else
                "notattempted" if task_cache["tried"] == 0 else
                "succeeded" if task_cache["succeeded"] else "failed",
                "grade":
                task_cache.get("grade", 0.0)
                if task_cache is not None else 0.0,
                "grade_weight":
                task.get_grading_weight(),
                "context":
                task.get_context(
                    self.user_manager.session_language()).original_content(),
                "problems": []
            }

            for problem in task.get_problems():
                pcontent = problem.get_original_content()
                pcontent["id"] = problem.get_id()
                if pcontent["type"] == "match":
                    del pcontent["answer"]
                if pcontent["type"] == "multiple-choice":
                    pcontent["choices"] = {
                        key: val["text"]
                        for key, val in enumerate(pcontent["choices"])
                    }
                pcontent = self._check_for_parsable_text(pcontent)
                data["problems"].append(pcontent)

            output.append(data)

        return 200, output