Exemple #1
0
    def post(self):

        submission_id = self.get_argument("submission_id", "")

        # Decrypt submission_id.
        try:
            submission_id = decrypt_number(submission_id)
        except ValueError:
            # We reply with Forbidden if the given ID cannot be
            # decrypted.
            logger.warning(
                "User %s tried to play a token " "on an undecryptable submission_id." % self.current_user.username
            )
            raise tornado.web.HTTPError(403)

        # Find submission and check it is of the current user.
        submission = Submission.get_from_id(submission_id, self.sql_session)
        if submission is None or submission.user != self.current_user:
            logger.warning(
                "User %s tried to play a token " "on an unexisting submission_id." % self.current_user.username
            )
            raise tornado.web.HTTPError(404)

        # Don't trust the user, check again if (s)he can really play
        # the token.
        timestamp = int(time.time())
        if self.contest.tokens_available(self.current_user.username, submission.task.name, timestamp)[0] <= 0:
            logger.warning("User %s tried to play a token " "when it shouldn't." % self.current_user.username)
            # Add "no luck" notification
            self.application.service.add_notification(
                self.current_user.username,
                timestamp,
                self._("Token request discarded"),
                self._("Your request has been discarded because you have no " "tokens available."),
            )
            self.redirect("/tasks/%s" % encrypt_number(submission.task.id))
            return

        token = Token(timestamp, submission)
        self.sql_session.add(token)
        self.sql_session.commit()

        # Inform ScoringService and eventually the ranking that the
        # token has been played.
        self.application.service.scoring_service.submission_tokened(submission_id=submission_id, timestamp=timestamp)

        logger.info("Token played by user %s on task %s." % (self.current_user.username, submission.task.name))

        # Add "All ok" notification
        self.application.service.add_notification(
            self.current_user.username,
            timestamp,
            self._("Token request received"),
            self._("Your request has been received " "and applied to the submission."),
        )

        self.redirect("/tasks/%s" % encrypt_number(submission.task.id))
Exemple #2
0
 def __init__(self, browser, task, filename, base_url=None):
     GenericRequest.__init__(self, browser, base_url)
     self.url = "%ssubmit/%s" % (self.base_url,
                                 encrypt_number(task[0]))
     self.task = task
     self.filename = filename
     self.data = {}
Exemple #3
0
 def __init__(self, browser, task, base_url=None,
              submissions_path=None):
     GenericRequest.__init__(self, browser, base_url)
     self.url = "%ssubmit/%s" % (self.base_url,
                                 encrypt_number(task[0]))
     self.task = task
     self.submissions_path = submissions_path
     self.data = {}
Exemple #4
0
    def post(self, task_id):

        self.timestamp = self.r_params["timestamp"]

        self.task_id = task_id
        self.task = Task.get_from_id(task_id, self.sql_session)

        if self.current_user is None or self.task is None or self.task.contest != self.contest:
            raise tornado.web.HTTPError(404)

        # Enforce minimum time between submissions for the same task.
        last_submission = (
            self.sql_session.query(Submission)
            .filter_by(task_id=self.task.id)
            .filter_by(user_id=self.current_user.id)
            .order_by(Submission.timestamp.desc())
            .first()
        )
        if last_submission is not None and self.timestamp - last_submission.timestamp < config.min_submission_interval:
            self.application.service.add_notification(
                self.current_user.username,
                int(time.time()),
                self._("Submissions too frequent!"),
                self._("For each task, you can submit " "again after %s seconds from last submission.")
                % config.min_submission_interval,
            )
            self.redirect("/tasks/%s" % encrypt_number(self.task.id))
            return

        # Ensure that the user did not submit multiple files with the
        # same name.
        if any(len(x) != 1 for x in self.request.files.values()):
            self.application.service.add_notification(
                self.current_user.username,
                int(time.time()),
                self._("Invalid submission format!"),
                self._("Please select the correct files."),
            )
            self.redirect("/tasks/%s" % encrypt_number(self.task.id))
            return

        # If the user submitted an archive, extract it and use content
        # as request.files.
        if len(self.request.files) == 1 and self.request.files.keys()[0] == "submission":
            archive_data = self.request.files["submission"][0]
            del self.request.files["submission"]

            # Extract the files from the archive.
            temp_archive_file, temp_archive_filename = tempfile.mkstemp(config.temp_dir)
            with os.fdopen(temp_archive_file, "w") as temp_archive_file:
                temp_archive_file.write(archive_data["body"])

            archive_contents = extract_archive(temp_archive_filename, archive_data["filename"])

            if archive_contents is None:
                self.application.service.add_notification(
                    self.current_user.username,
                    int(time.time()),
                    self._("Invalid archive format!"),
                    self._("The submitted archive could not be opened."),
                )
                self.redirect("/tasks/%s" % encrypt_number(self.task.id))
                return

            for item in archive_contents:
                self.request.files[item["filename"]] = [item]

        # This ensure that the user sent one file for every name in
        # submission format and no more. Less is acceptable if task
        # type says so.
        task_type = get_task_type(task=self.task)
        required = set([x.filename for x in self.task.submission_format])
        provided = set(self.request.files.keys())
        if not (required == provided or (task_type.ALLOW_PARTIAL_SUBMISSION and required.issuperset(provided))):
            self.application.service.add_notification(
                self.current_user.username,
                int(time.time()),
                self._("Invalid submission format!"),
                self._("Please select the correct files."),
            )
            self.redirect("/tasks/%s" % encrypt_number(self.task.id))
            return

        # Add submitted files. After this, self.files is a dictionary
        # indexed by *our* filenames (something like "output01.txt" or
        # "taskname.%l", and whose value is a couple
        # (user_assigned_filename, content).
        self.files = {}
        for uploaded, data in self.request.files.iteritems():
            self.files[uploaded] = (data[0]["filename"], data[0]["body"])

        # If we allow partial submissions, implicitly we recover the
        # non-submitted files from the previous submission. And put
        # them in self.file_digests (i.e., like they have already been
        # sent to FS).
        self.submission_lang = None
        self.file_digests = {}
        self.retrieved = 0
        if task_type.ALLOW_PARTIAL_SUBMISSION and last_submission is not None:
            for filename in required.difference(provided):
                if filename in last_submission.files:
                    # If we retrieve a language-dependent file from
                    # last submission, we take not that language must
                    # be the same.
                    if "%l" in filename:
                        self.submission_lang = last_submission.language
                    self.file_digests[filename] = last_submission.files[filename].digest
                    self.retrieved += 1

        # We need to ensure that everytime we have a .%l in our
        # filenames, the user has one amongst ".cpp", ".c", or ".pas,
        # and that all these are the same (i.e., no mixed-language
        # submissions).
        def which_language(user_filename):
            """Determine the language of user_filename from its
            extension.

            user_filename (string): the file to test.
            return (string): the extension of user_filename, or None
                             if it is not a recognized language.

            """
            extension = os.path.splitext(user_filename)[1]
            try:
                return Submission.LANGUAGES_MAP[extension]
            except KeyError:
                return None

        error = None
        for our_filename in self.files:
            user_filename = self.files[our_filename][0]
            if our_filename.find(".%l") != -1:
                lang = which_language(user_filename)
                if lang is None:
                    error = self._("Cannot recognize submission's language.")
                    break
                elif self.submission_lang is not None and self.submission_lang != lang:
                    error = self._("All sources must be in the same language.")
                    break
                else:
                    self.submission_lang = lang
        if error is not None:
            self.application.service.add_notification(
                self.current_user.username, int(time.time()), self._("Invalid submission!"), error
            )
            self.redirect("/tasks/%s" % encrypt_number(self.task.id))
            return

        # Check if submitted files are small enough.
        if any([len(f[1]) > config.max_submission_length for f in self.files.values()]):
            self.application.service.add_notification(
                self.current_user.username,
                int(time.time()),
                self._("Submission too big!"),
                self._("Each files must be at most %d bytes long.") % config.max_submission_length,
            )
            self.redirect("/tasks/%s" % encrypt_number(self.task.id))
            return

        # All checks done, submission accepted.

        # Attempt to store the submission locally to be able to
        # recover a failure.
        self.local_copy_saved = False

        if config.submit_local_copy:
            try:
                path = os.path.join(
                    config.submit_local_copy_path.replace("%s", config.data_dir), self.current_user.username
                )
                if not os.path.exists(path):
                    os.makedirs(path)
                with codecs.open(os.path.join(path, str(self.timestamp)), "w", "utf-8") as file_:
                    pickle.dump((self.contest.id, self.current_user.id, self.task, self.files), file_)
                self.local_copy_saved = True
            except Exception as error:
                logger.error("Submission local copy failed - %s" % traceback.format_exc())
        self.username = self.current_user.username
        self.sql_session.close()

        # We now have to send all the files to the destination...
        try:
            for filename in self.files:
                digest = self.application.service.file_cacher.put_file(
                    description="Submission file %s sent by %s at %d." % (filename, self.username, self.timestamp),
                    binary_data=self.files[filename][1],
                )
                self.file_digests[filename] = digest

        # In case of error, the server aborts the submission
        except Exception as error:
            logger.error("Storage failed! %s" % error)
            if self.local_copy_saved:
                message = "In case of emergency, this server has a local copy."
            else:
                message = "No local copy stored! Your submission was ignored."
            self.application.service.add_notification(
                self.username, int(time.time()), self._("Submission storage failed!"), self._(message)
            )
            self.redirect("/tasks/%s" % encrypt_number(self.task_id))

        # All the files are stored, ready to submit!
        self.sql_session = Session()
        current_user = self.get_current_user()
        self.task = Task.get_from_id(self.task_id, self.sql_session)
        logger.info("All files stored for submission sent by %s" % self.username)
        submission = Submission(
            user=current_user, task=self.task, timestamp=self.timestamp, files={}, language=self.submission_lang
        )

        for filename, digest in self.file_digests.items():
            self.sql_session.add(File(digest, filename, submission))
        self.sql_session.add(submission)
        self.sql_session.commit()
        self.r_params["submission"] = submission
        self.r_params["warned"] = False
        self.application.service.evaluation_service.new_submission(submission_id=submission.id)
        self.application.service.add_notification(
            self.username,
            int(time.time()),
            self._("Submission received"),
            self._("Your submission has been received " "and is currently being evaluated."),
        )
        # The argument (encripted submission id) is not used by CWS
        # (nor it discloses information to the user), but it is useful
        # for automatic testing to obtain the submission id).
        self.redirect("/tasks/%s?%s" % (encrypt_number(self.task.id), encrypt_number(submission.id)))
Exemple #5
0
 def __init__(self, browser, task_id, base_url=None):
     GenericRequest.__init__(self, browser, base_url)
     self.url = "%stasks/%s" % (self.base_url, encrypt_number(task_id))
     self.task_id = task_id
Exemple #6
0
 def __init__(self, browser, task_id, base_url=None):
     GenericRequest.__init__(self, browser, base_url)
     self.url = "%stasks/%s" % (self.base_url, encrypt_number(task_id))
     self.task_id = task_id
Exemple #7
0
 def __init__(self, browser, task, base_url=None, submissions_path=None):
     GenericRequest.__init__(self, browser, base_url)
     self.url = "%ssubmit/%s" % (self.base_url, encrypt_number(task[0]))
     self.task = task
     self.submissions_path = submissions_path
     self.data = {}