Example #1
0
    def compress(self, in_ext=None, sol_ext=None):
        """Compress all test cases in this dataset in a single zip file.
        The basename of the corresponding subtask subdirectory is prepended
        to each file.
        """
        in_ext = in_ext or self._in_ext
        sol_ext = sol_ext or self._sol_ext
        dst_file = FilePath(self._directory, 'data.zip')
        if dst_file.exists() and dst_file.mtime() >= self.mtime():
            return True

        tmpdir = Directory.tmpdir()
        try:
            copied = 0
            for subtask in self._subtasks:
                copied += subtask.copy_to(tmpdir)

            if not copied:
                # ui.show_message("Warning", "no files in dataset", ui.WARNING)
                return True

            cmd = 'cd %s && zip data.zip *%s *%s' % (tmpdir, in_ext, sol_ext)
            st = subprocess.call(cmd, stdout=subprocess.DEVNULL, shell=True)
            FilePath(tmpdir, 'data.zip').copy(dst_file)
        finally:
            tmpdir.rmtree()

        return st == 0
Example #2
0
 def run_subtask(self, stn, subtask):
     groups = subtask['groups']
     for (group, tests) in sorted(groups.items()):
         for (i, test) in enumerate(tests, 1):
             cmd = test['cmd']
             test_file = self.test_filepath(stn, group, i)
             if cmd == 'copy':
                 self.copy(test['file'], test_file)
             elif cmd == 'echo':
                 self.echo(test['args'], test_file)
             else:
                 test['args'].insert(0, '%s-%s-%s' % (stn, group, i))
                 source = FilePath(self._directory, test['source'])
                 if cmd == 'cpp':
                     self.run_cpp_generator(source, test['args'], test_file)
                 elif cmd in ['py', 'py2', 'py3']:
                     self.run_py_generator(source, test['args'], test_file,
                                           cmd)
                 elif cmd == 'java':
                     self.run_java_generator(source, test['args'],
                                             test_file)
                 elif cmd == 'run':
                     bin_path = FilePath(self._directory, test['bin'])
                     self.run_bin_generator(bin_path, test['args'],
                                            test_file)
                 else:
                     ui.fatal_error(
                         'unexpected command when running plan: %s ' % cmd)
Example #3
0
    def compress(self, in_ext=None, sol_ext=None, random_sort=False):
        """Compress all test cases in this dataset in a single zip file.
        The basename of the corresponding subtask subdirectory is prepended
        to each file.
        """
        in_ext = in_ext or self._in_ext
        sol_ext = sol_ext or self._sol_ext
        dst_file = FilePath(self._directory, 'data.zip')
        if dst_file.exists() and dst_file.mtime() >= self.mtime():
            return True

        tmpdir = Directory.tmpdir()
        try:
            copied = 0
            for subtask in self._subtasks:
                copied += subtask.copy_to(tmpdir, random_sort=random_sort)

            if not copied:
                # ui.show_message("Warning", "no files in dataset", ui.WARNING)
                return True

            cmd = 'cd %s && zip data.zip *%s *%s' % (tmpdir, in_ext, sol_ext)
            st = subprocess.call(cmd, stdout=subprocess.DEVNULL, shell=True)
            FilePath(tmpdir, 'data.zip').copy(dst_file)
        finally:
            tmpdir.rmtree()

        return st == 0
Example #4
0
 def __init__(self, source):
     """
     Args:
         source (FilePath)
     """
     self._source = source
     self._compiler = CppCompiler(['-I"%s"' % source.directory()])
     self._binary_path = FilePath(source.directory(), 'checker')
Example #5
0
 def copy(self, src, dst):
     fp = FilePath(self._task_directory, src)
     if not fp.exists():
         return (False, 'No such file')
     try:
         fp.copy(dst)
         (st, msg) = (True, 'OK')
         return (st, msg)
     except Exception:  # pylint: disable=broad-except
         return (False, 'Error when copying file')
Example #6
0
 def copy(self, src, dst):
     fp = FilePath(self._task_directory, src)
     if not fp.exists():
         return (False, 'No such file')
     try:
         fp.copy(dst)
         (st, msg) = (True, 'OK')
         return (st, msg)
     except Exception:  # pylint: disable=broad-except
         return (False, 'Error when copying file')
Example #7
0
    def run(self):
        (subtasks, cmds) = self.parse_file()

        for stn in range(1, subtasks + 1):
            dire = FilePath(self._dataset_directory, 'st%d' % stn).get_or_create_dir()
            dire.clear()

        if not cmds:
            ui.show_message("Warning", 'no commands were executed for the plan.', ui.WARNING)

        for (stn, subtask) in sorted(cmds.items()):
            self.run_subtask(stn, subtask)
Example #8
0
 def __init__(self, directory, num=None, codename=None):
     """
     Args:
         directory (Directory): Directory to search for statement source file.
         num (int): Number of the statement in the contest starting from 0
     """
     assert FilePath(directory, 'statement.tex').exists()
     self._source = FilePath(directory, 'statement.tex')
     self._pdf = self._source.chext('.pdf')
     self._compiler = LatexCompiler()
     self._directory = directory
     self._num = num
     self._codename = codename
Example #9
0
    def create_layout(contest_path, config):
        """Copies contest skeleton to contest_path and saves specified configurations

        Args:
            contest_path (Filepath)
        """
        ocimatic_dir = FilePath(__file__).directory()
        contest_skel = ocimatic_dir.chdir('resources', 'contest-skel')
        contest_skel.copy_tree(contest_path, ['auto'])
        contest_dir = contest_path.get_or_create_dir()
        with FilePath(contest_dir,
                      '.ocimatic_contest').open('w') as config_file:
            json.dump(config, config_file, indent=4)
Example #10
0
 def __init__(self,
              directory,
              task_directory,
              dataset_directory,
              filename='testplan.txt'):
     self._directory = directory
     self._testplan_path = FilePath(directory, filename)
     if not self._testplan_path.exists():
         ui.fatal_error('No such file plan for creating dataset: "%s"' %
                        self._testplan_path)
     self._task_directory = task_directory
     self._dataset_directory = dataset_directory
     self._cpp_compiler = CppCompiler()
     self._java_compiler = JavaCompiler()
Example #11
0
 def build_validator(self, source):
     fp = FilePath(self._directory, source)
     if not fp.exists():
         return (None, 'File does not exists.')
     if fp.ext == '.cpp':
         binary = fp.chext('.bin')
         if binary.mtime() < fp.mtime() and not self._cpp_compiler(fp, binary):
             return (None, 'Failed to build validator.')
         return (Runnable(binary), 'OK')
     if fp.ext in ['.py', '.py3']:
         return (Runnable('python3', [str(source)]), 'OK')
     if fp.ext == '.py2':
         return (Runnable('python2', [str(source)]), 'OK')
     return (None, 'Not supported source file.')
Example #12
0
    def run(self):
        (subtasks, cmds) = self.parse_file()

        for stn in range(1, subtasks + 1):
            dire = FilePath(self._dataset_directory,
                            'st%d' % stn).get_or_create_dir()
            dire.clear()

        if not cmds:
            ui.show_message("Warning",
                            'no commands were executed for the plan.',
                            ui.WARNING)

        for (stn, subtask) in sorted(cmds.items()):
            self.run_subtask(stn, subtask)
Example #13
0
 def build_validator(self, source):
     fp = FilePath(self._directory, source)
     if not fp.exists():
         return (None, 'File does not exists.')
     if fp.ext == '.cpp':
         binary = fp.chext('.bin')
         if binary.mtime() < fp.mtime() and not self._cpp_compiler(
                 fp, binary):
             return (None, 'Failed to build validator.')
         return (Runnable(binary), 'OK')
     if fp.ext in ['.py', '.py3']:
         return (Runnable('python3', [str(source)]), 'OK')
     if fp.ext == '.py2':
         return (Runnable('python2', [str(source)]), 'OK')
     return (None, 'Not supported source file.')
Example #14
0
    def __init__(self, directory):
        """
        Args:
            directory (Directory): Directory where the contest reside.
        """
        self._directory = directory
        self._config = pjson.load(FilePath(directory, '.ocimatic_contest'))

        self._init_tasks()

        if 'phase' in self._config:
            os.environ['OCIMATIC_PHASE'] = self._config['phase']

        self._titlepage = FilePath(directory, 'titlepage.tex')
        self._compiler = LatexCompiler()
Example #15
0
class CppChecker(Checker):
    def __init__(self, source):
        """
        Args:
            source (FilePath)
        """
        self._source = source
        self._compiler = CppCompiler(['-I"%s"' % source.directory()])
        self._binary_path = FilePath(source.directory(), 'checker')

    def __call__(self, in_path, expected_path, out_path):
        """Run checker to evaluate outcome. Parameters correspond to convention
        for checker in cms.
        Args:
            in_path (FilePath)
            expected_path (FilePath)
            out_path (FilePath)
        """
        assert in_path.exists()
        assert expected_path.exists()
        assert out_path.exists()
        if self._binary_path.mtime() < self._source.mtime():
            if not self.build():
                return (False, 0.0, "Failed to build checker")
        complete = subprocess.run([
            str(self._binary_path),
            str(in_path),
            str(expected_path),
            str(out_path)
        ],
                                  universal_newlines=True,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
        ret = complete.returncode
        st = ret == 0
        if st:
            outcome = float(complete.stdout)
            msg = complete.stderr
        else:
            stderr = complete.stderr.strip('\n')
            outcome = 0.0
            if stderr and len(stderr) < 75:
                msg = stderr
            else:
                if ret < 0:
                    sig = -ret
                    msg = 'Execution killed with signal %d' % sig
                    if sig in SIGNALS:
                        msg += ': %s' % SIGNALS[sig]
                else:
                    msg = 'Execution ended with error (return code %d)' % ret

        return (st, outcome, msg)

    def build(self):
        """Build source of the checker
        Returns:
            bool: True if compilation is successful. False otherwise
        """
        return self._compiler(self._source, self._binary_path)
Example #16
0
 def __init__(self, source):
     """
     Args:
         source (FilePath)
     """
     self._source = source
     self._compiler = CppCompiler(['-I"%s"' % source.directory()])
     self._binary_path = FilePath(source.directory(), 'checker')
Example #17
0
class CppChecker(Checker):
    def __init__(self, source):
        """
        Args:
            source (FilePath)
        """
        self._source = source
        self._compiler = CppCompiler(['-I"%s"' % source.directory()])
        self._binary_path = FilePath(source.directory(), 'checker')

    def __call__(self, in_path, expected_path, out_path):
        """Run checker to evaluate outcome. Parameters correspond to convention
        for checker in cms.
        Args:
            in_path (FilePath)
            expected_path (FilePath)
            out_path (FilePath)
        """
        assert in_path.exists()
        assert expected_path.exists()
        assert out_path.exists()
        if self._binary_path.mtime() < self._source.mtime():
            if not self.build():
                return (False, 0.0, "Failed to build checker")
        complete = subprocess.run(
            [str(self._binary_path),
             str(in_path), str(expected_path),
             str(out_path)],
            universal_newlines=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)
        ret = complete.returncode
        st = ret == 0
        if st:
            outcome = float(complete.stdout)
            msg = complete.stderr
        else:
            stderr = complete.stderr.strip('\n')
            outcome = 0.0
            if stderr and len(stderr) < 75:
                msg = stderr
            else:
                if ret < 0:
                    sig = -ret
                    msg = 'Execution killed with signal %d' % sig
                    if sig in SIGNALS:
                        msg += ': %s' % SIGNALS[sig]
                else:
                    msg = 'Execution ended with error (return code %d)' % ret

        return (st, outcome, msg)

    def build(self):
        """Build source of the checker
        Returns:
            bool: True if compilation is successful. False otherwise
        """
        return self._compiler(self._source, self._binary_path)
Example #18
0
 def __init__(self, directory, task_directory, dataset_directory, filename='testplan.txt'):
     self._directory = directory
     self._testplan_path = FilePath(directory, filename)
     if not self._testplan_path.exists():
         ui.fatal_error('No such file plan for creating dataset: "%s"' % self._testplan_path)
     self._task_directory = task_directory
     self._dataset_directory = dataset_directory
     self._cpp_compiler = CppCompiler()
     self._java_compiler = JavaCompiler()
Example #19
0
 def copy_to(self, directory, random_sort=False):
     copied = 0
     for test in self._tests:
         if test.expected_path.exists():
             # Sort testcases withing a subtask randomly
             if random_sort:
                 choices = string.ascii_lowercase
                 rnd_str = ''.join(random.choice(choices) for _ in range(3))
                 in_name = "%s-%s-%s" % (self._name, rnd_str,
                                         test.in_path.name)
                 sol_name = "%s-%s-%s" % (self._name, rnd_str,
                                          test.expected_path.name)
             else:
                 in_name = "%s-%s-%s" % (self._name, rnd_str,
                                         test.in_path.name)
                 sol_name = "%s-%s-%s" % (self._name, rnd_str,
                                          test.expected_path.name)
             test.in_path.copy(FilePath(directory, in_name))
             test.expected_path.copy(FilePath(directory, sol_name))
             copied += 1
     return copied
Example #20
0
    def merge_pdfs(self, filename):
        """Merges statements and title page in a single file """
        if not shutil.which('gs'):
            return (False, 'Cannot find gs')

        pdfs = ' '.join('"%s"' % t.statement.pdf for t in self._tasks
                        if t.statement.pdf)
        titlepage = FilePath(self._directory, 'titlepage.pdf')
        if titlepage.exists():
            pdfs = '"%s" %s' % (titlepage, pdfs)

        cmd = ('gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite'
               ' -dPDFSETTINGS=/prepress -sOutputFile=%s %s') % (FilePath(
                   self._directory, filename), pdfs)
        complete = subprocess.run(cmd,
                                  shell=True,
                                  timeout=20,
                                  stdin=subprocess.DEVNULL,
                                  stdout=subprocess.DEVNULL,
                                  stderr=subprocess.DEVNULL)
        st = complete.returncode == 0
        return (st, 'OK' if st else 'FAILED')
Example #21
0
 def io_samples(self):
     """Find sample input data in the satement
     Returns:
         List[FilePath]: list of paths
     """
     latex_file = self._source.open('r')
     samples = set()
     for line in latex_file:
         m = re.match(r'[^%]*\\sampleIO(\[[^\]]*\]){0,2}{([^}]+)}', line)
         if m:
             samples.add(m.group(2))
     latex_file.close()
     return [FilePath(self._directory, s) for s in samples]
Example #22
0
    def copy_to(self, directory):
        new_dir = directory.mkdir(str(self))

        (st, _) = self.compress_dataset()
        if st:
            dataset = FilePath(self._directory.chdir('dataset'), 'data.zip')
            dataset_dst = FilePath(new_dir, 'data.zip')
            if dataset.exists():
                dataset.copy(dataset_dst)

        (st, _) = self.build_statement()
        if st:
            statement = FilePath(new_dir, 'statement.pdf')
            FilePath(self._directory.chdir('statement'),
                     'statement.pdf').copy(statement)
Example #23
0
    def run(self, in_path, out_path, args=None, timeout=None):  # pylint: disable=too-many-locals
        """Run binary redirecting standard input and output.

        Args:
            in_path (Optional[FilePath]): Path to redirect stdin from. If None
                input is redirected from /dev/null.
            out_path (Optional[FilePath]): File to redirec stdout to. If None
                output is redirected to /dev/null.
            args (List[str]): Additional parameters

        Returns:
            (bool, str, float): Returns a tuple (status, time, errmsg).
                status is True if the execution terminates with exit code zero
                or False otherwise.
                time corresponds to execution time.
                if status is False errmsg contains an explanatory error
                message, otherwise it contains a success message.
        """
        args = args or []
        assert in_path is None or in_path.exists()
        with contextlib.ExitStack() as stack:
            if in_path is None:
                in_path = FilePath('/dev/null')
            in_file = stack.enter_context(in_path.open('r'))
            if not out_path:
                out_path = FilePath('/dev/null')
            out_file = stack.enter_context(out_path.open('w'))

            start = pytime.monotonic()
            self._cmd.extend(args)
            try:
                complete = subprocess.run(
                    self._cmd,
                    timeout=timeout,
                    stdin=in_file,
                    stdout=out_file,
                    universal_newlines=True,
                    stderr=subprocess.PIPE)
            except subprocess.TimeoutExpired:
                return (False, pytime.monotonic() - start, 'Execution timed out')
            time = pytime.monotonic() - start
            ret = complete.returncode
            status = ret == 0
            msg = 'OK'
            if not status:
                stderr = complete.stderr.strip('\n')
                if stderr and len(stderr) < 100:
                    msg = stderr
                else:
                    if ret < 0:
                        sig = -ret
                        msg = 'Execution killed with signal %d' % sig
                        if sig in SIGNALS:
                            msg += ': %s' % SIGNALS[sig]
                    else:
                        msg = 'Execution ended with error (return code %d)' % ret
            return (status, time, msg)
Example #24
0
def new_contest(args, optlist):
    if not args:
        ui.fatal_error('You have to specify a name for the contest.')
    name = args[0]

    contest_config = {}
    if '--phase' in optlist:
        contest_config['phase'] = optlist['--phase']

    try:
        cwd = Directory.getcwd()
        if cwd.find(name):
            ui.fatal_error("Couldn't create contest. Path already exists")
        contest_path = FilePath(cwd, name)
        core.Contest.create_layout(contest_path, contest_config)
        ui.show_message('Info', 'Contest [%s] created' % name)
    except Exception as exc:  # pylint: disable=broad-except
        ui.fatal_error("Couldn't create contest: %s." % exc)
Example #25
0
    def run(self, runnable, checker, check=False):
        """Run runnable whit this test as input and check output correctness
        Args:
            runnable (Runnable)
            checker (Checker): Checker to check outcome
            check  (bool): If true this only report if expected output
                correspond to binary execution output.
        """
        out_path = FilePath.tmpfile()
        if not self.expected_path.exists():
            out_path.remove()
            return (False, 'No expected output file')

        (st, time, errmsg) = runnable.run(self.in_path,
                                          out_path,
                                          timeout=ocimatic.config['timeout'])

        # Execution failed
        if not st:
            if check:
                return (st, errmsg)
            return (st, '%s' % errmsg)

        (st, outcome, checkmsg) = checker(self.in_path, self.expected_path,
                                          out_path)
        # Checker failed
        if not st:
            msg = 'Failed to run checker: %s' % checkmsg
            return (st, msg)

        st = outcome == 1.0
        if check:
            msg = 'OK' if st else 'FAILED'
            return (st, msg)

        msg = '%s [%.2fs]' % (outcome, time)
        if checkmsg:
            msg += ' - %s' % checkmsg
        return (st, msg)
Example #26
0
    def run(self, runnable, checker, check=False):
        """Run runnable whit this test as input and check output correctness
        Args:
            runnable (Runnable)
            checker (Checker): Checker to check outcome
            check  (bool): If true this only report if expected output
                correspond to binary execution output.
        """
        out_path = FilePath.tmpfile()
        if not self.expected_path.exists():
            out_path.remove()
            return (False, 'No expected output file')

        (st, time, errmsg) = runnable.run(
            self.in_path, out_path, timeout=ocimatic.config['timeout'])

        # Execution failed
        if not st:
            if check:
                return (st, errmsg)
            return (st, '%s' % errmsg)

        (st, outcome, checkmsg) = checker(self.in_path, self.expected_path, out_path)
        # Checker failed
        if not st:
            msg = 'Failed to run checker: %s' % checkmsg
            return (st, msg)

        st = outcome == 1.0
        if check:
            msg = 'OK' if st else 'FAILED'
            return (st, msg)

        msg = '%s [%.2fs]' % (outcome, time)
        if checkmsg:
            msg += ' - %s' % checkmsg
        return (st, msg)
Example #27
0
 def new_task(self, name):
     task_dir = FilePath(self._directory, name)
     Task.create_layout(task_dir)
     self._config.setdefault('tasks', []).append(name)
Example #28
0
class Statement:
    """Represents a statement. A statement is formed by a latex source and a pdf
    file.
    """
    def __init__(self, directory, num=None, codename=None):
        """
        Args:
            directory (Directory): Directory to search for statement source file.
            num (int): Number of the statement in the contest starting from 0
        """
        assert FilePath(directory, 'statement.tex').exists()
        self._source = FilePath(directory, 'statement.tex')
        self._pdf = self._source.chext('.pdf')
        self._compiler = LatexCompiler()
        self._directory = directory
        self._num = num
        self._codename = codename

    @property
    def pdf(self):
        """Returns path to pdf file and compiles it if necessary.
        Returns:
            Optional[FilePath]: The file path if the binary is present or None
                if the pdf file cannot be generated.
        """
        if self._pdf.mtime() < self._source.mtime():
            (st, _msg) = self.build()
            if not st:
                return None
        return self._pdf

    def __str__(self):
        return str(self._source)

    @ui.work('PDF')
    def build(self, blank_page=False):
        """Compile statement latex source
        Args:
           blank_page (Optional[bool]) if true adds a blank page at the end of the
               problem.
        Returns:
           (bool, msg) a tuple containing status code and result message.

        """
        if self._num is not None:
            os.environ['OCIMATIC_PROBLEM_NUMBER'] = chr(ord('A') + self._num)
        if self._codename:
            os.environ['OCIMATIC_CODENAME'] = self._codename
        if blank_page:
            os.environ['OCIMATIC_BLANK_PAGE'] = 'True'
        st = self._compiler(self._source)
        return (st, 'OK' if st else 'FAILED')

    def io_samples(self):
        """Find sample input data in the satement
        Returns:
            List[FilePath]: list of paths
        """
        latex_file = self._source.open('r')
        samples = set()
        for line in latex_file:
            m = re.match(r'[^%]*\\sampleIO(\[[^\]]*\]){0,2}{([^}]+)}', line)
            if m:
                samples.add(m.group(2))
        latex_file.close()
        return [FilePath(self._directory, s) for s in samples]
Example #29
0
class DatasetPlan:
    """Functionality to read and run a plan for generating dataset."""
    def __init__(self,
                 directory,
                 task_directory,
                 dataset_directory,
                 filename='testplan.txt'):
        self._directory = directory
        self._testplan_path = FilePath(directory, filename)
        if not self._testplan_path.exists():
            ui.fatal_error('No such file plan for creating dataset: "%s"' %
                           self._testplan_path)
        self._task_directory = task_directory
        self._dataset_directory = dataset_directory
        self._cpp_compiler = CppCompiler()
        self._java_compiler = JavaCompiler()

    def test_filepath(self, stn, group, i):
        st_dir = FilePath(self._dataset_directory,
                          'st%d' % stn).get_or_create_dir()
        return FilePath(st_dir, '%s-%d.in' % (group, i))

    def validate_input(self):
        (_, cmds) = self.parse_file()
        for (st, subtask) in sorted(cmds.items()):
            self.validate_subtask(st, subtask)

    @ui.workgroup('Subtask {1}')
    def validate_subtask(self, stn, subtask):
        validator = None
        if subtask['validator']:
            (validator, msg) = self.build_validator(subtask['validator'])
            if validator is None:
                ui.show_message('Warning',
                                'Failed to build validator: %s' % msg,
                                ui.WARNING)
        else:
            ui.show_message('Info', 'No validator specified', ui.INFO)
        if validator:
            for (group, tests) in sorted(subtask['groups'].items()):
                for (i, _) in enumerate(tests, 1):
                    test_file = self.test_filepath(stn, group, i)
                    self.validate_test_input(test_file, validator)

    @ui.work('Validating', '{1}')
    def validate_test_input(self, test_file, validator):
        if not test_file.exists():
            return False, 'Test file does not exist'
        (st, _time, msg) = validator.run(test_file, None)
        return st, msg

    def build_validator(self, source):
        fp = FilePath(self._directory, source)
        if not fp.exists():
            return (None, 'File does not exists.')
        if fp.ext == '.cpp':
            binary = fp.chext('.bin')
            if binary.mtime() < fp.mtime() and not self._cpp_compiler(
                    fp, binary):
                return (None, 'Failed to build validator.')
            return (Runnable(binary), 'OK')
        if fp.ext in ['.py', '.py3']:
            return (Runnable('python3', [str(source)]), 'OK')
        if fp.ext == '.py2':
            return (Runnable('python2', [str(source)]), 'OK')
        return (None, 'Not supported source file.')

    def run(self):
        (subtasks, cmds) = self.parse_file()

        for stn in range(1, subtasks + 1):
            dire = FilePath(self._dataset_directory,
                            'st%d' % stn).get_or_create_dir()
            dire.clear()

        if not cmds:
            ui.show_message("Warning",
                            'no commands were executed for the plan.',
                            ui.WARNING)

        for (stn, subtask) in sorted(cmds.items()):
            self.run_subtask(stn, subtask)

    @ui.workgroup('Subtask {1}')
    def run_subtask(self, stn, subtask):
        groups = subtask['groups']
        for (group, tests) in sorted(groups.items()):
            for (i, test) in enumerate(tests, 1):
                cmd = test['cmd']
                test_file = self.test_filepath(stn, group, i)
                if cmd == 'copy':
                    self.copy(test['file'], test_file)
                elif cmd == 'echo':
                    self.echo(test['args'], test_file)
                else:
                    test['args'].insert(0, '%s-%s-%s' % (stn, group, i))
                    source = FilePath(self._directory, test['source'])
                    if cmd == 'cpp':
                        self.run_cpp_generator(source, test['args'], test_file)
                    elif cmd in ['py', 'py2', 'py3']:
                        self.run_py_generator(source, test['args'], test_file,
                                              cmd)
                    elif cmd == 'java':
                        self.run_java_generator(source, test['args'],
                                                test_file)
                    elif cmd == 'run':
                        bin_path = FilePath(self._directory, test['bin'])
                        self.run_bin_generator(bin_path, test['args'],
                                               test_file)
                    else:
                        ui.fatal_error(
                            'unexpected command when running plan: %s ' % cmd)

    @ui.work('Copy', '{1}')
    def copy(self, src, dst):
        fp = FilePath(self._task_directory, src)
        if not fp.exists():
            return (False, 'No such file')
        try:
            fp.copy(dst)
            (st, msg) = (True, 'OK')
            return (st, msg)
        except Exception:  # pylint: disable=broad-except
            return (False, 'Error when copying file')

    @ui.work('Echo', '{1}')
    def echo(self, args, dst):
        with dst.open('w') as test_file:
            test_file.write(' '.join(args) + '\n')
            (st, msg) = (True, 'Ok')
            return (st, msg)

    @ui.work('Gen', '{1}')
    def run_cpp_generator(self, source, args, dst):
        if not source.exists():
            return (False, 'No such file')
        binary = source.chext('.bin')
        if binary.mtime() < source.mtime():
            st = self._cpp_compiler(source, binary)
            if not st:
                return (st, 'Failed to build generator')

        (st, _time, msg) = Runnable(binary).run(None, dst, args)
        return (st, msg)

    @ui.work('Gen', '{1}')
    def run_py_generator(self, source, args, dst, cmd):
        if not source.exists():
            return (False, 'No such file')
        python = 'python2' if cmd == 'py2' else 'python3'
        (st, _time, msg) = Runnable(python, [str(source)]).run(None, dst, args)
        return (st, msg)

    @ui.work('Gen', '{1}')
    def run_java_generator(self, source, args, dst):
        if not source.exists():
            return (False, 'No such file')
        bytecode = source.chext('.class')
        if bytecode.mtime() < source.mtime():
            st = self._java_compiler(source)
            if not st:
                return (st, 'Failed to build generator')

        classname = bytecode.rootname()
        classpath = str(bytecode.directory().path())
        (st, _time,
         msg) = Runnable('java',
                         ['-cp', classpath, classname]).run(None, dst, args)
        return (st, msg)

    @ui.work('Gen', '{1}')
    def run_bin_generator(self, bin_path, args, dst):
        if not bin_path.exists():
            return (False, 'No such file')
        if not Runnable.is_callable(bin_path):
            return (False,
                    'Cannot run file, it may not have correct permissions')
        (st, _time, msg) = Runnable(bin_path).run(None, dst, args)
        return (st, msg)

    def parse_file(self):  # pylint: disable=too-many-locals,too-many-branches
        """
        Args:
            path (FilePath)
        """
        cmds = {}
        st = 0
        for (lineno,
             line) in enumerate(self._testplan_path.open('r').readlines(), 1):
            line = line.strip()
            subtask_header = re.compile(
                r'\s*\[\s*Subtask\s*(\d+)\s*(?:-\s*([^\]\s]+))?\s*\]\s*')
            cmd_line = re.compile(r'\s*([^;\s]+)\s*;\s*(\S+)(:?\s+(.*))?')
            comment = re.compile(r'\s*#.*')

            if not line:
                continue
            if not comment.fullmatch(line):
                header_match = subtask_header.fullmatch(line)
                cmd_match = cmd_line.fullmatch(line)
                if header_match:
                    found_st = int(header_match.group(1))
                    validator = header_match.group(2)
                    if st + 1 != found_st:
                        ui.fatal_error(
                            'line %d: found subtask %d, but subtask %d was expected'
                            % (lineno, found_st, st + 1))
                    st += 1
                    cmds[st] = {'validator': validator, 'groups': {}}
                elif cmd_match:
                    if st == 0:
                        ui.fatal_error(
                            'line %d: found command before declaring a subtask.'
                            % lineno)
                    group = cmd_match.group(1)
                    cmd = cmd_match.group(2)
                    args = _parse_args(cmd_match.group(3) or '')
                    if group not in cmds[st]['groups']:
                        cmds[st]['groups'][group] = []

                    if cmd == 'copy':
                        if len(args) > 2:
                            ui.fatal_error(
                                'line %d: command copy expects exactly one argument.'
                                % lineno)
                        cmds[st]['groups'][group].append({
                            'cmd': 'copy',
                            'file': args[0],
                        })
                    elif cmd == 'echo':
                        cmds[st]['groups'][group].append({
                            'cmd': 'echo',
                            'args': args
                        })
                    else:
                        f = FilePath(self._directory, cmd)
                        if f.ext in ['.cpp', '.java', '.py', '.py2', '.py3']:
                            cmds[st]['groups'][group].append({
                                'cmd': f.ext[1:],
                                'source': cmd,
                                'args': args
                            })
                        else:
                            cmds[st]['groups'][group].append({
                                'cmd': 'run',
                                'bin': cmd,
                                'args': args
                            })
                else:
                    ui.fatal_error('line %d: error while parsing line `%s`\n' %
                                   (lineno, line))
        return (st, cmds)
Example #30
0
 def create_layout(task_path):
     ocimatic_dir = FilePath(__file__).directory()
     skel = ocimatic_dir.chdir('resources', 'task-skel')
     skel.copy_tree(task_path)
Example #31
0
class DatasetPlan:
    """Functionality to read and run a plan for generating dataset."""

    def __init__(self, directory, task_directory, dataset_directory, filename='testplan.txt'):
        self._directory = directory
        self._testplan_path = FilePath(directory, filename)
        if not self._testplan_path.exists():
            ui.fatal_error('No such file plan for creating dataset: "%s"' % self._testplan_path)
        self._task_directory = task_directory
        self._dataset_directory = dataset_directory
        self._cpp_compiler = CppCompiler()
        self._java_compiler = JavaCompiler()

    def test_filepath(self, stn, group, i):
        st_dir = FilePath(self._dataset_directory, 'st%d' % stn).get_or_create_dir()
        return FilePath(st_dir, '%s-%d.in' % (group, i))

    def validate_input(self):
        (_, cmds) = self.parse_file()
        for (st, subtask) in sorted(cmds.items()):
            self.validate_subtask(st, subtask)

    @ui.workgroup('Subtask {1}')
    def validate_subtask(self, stn, subtask):
        validator = None
        if subtask['validator']:
            (validator, msg) = self.build_validator(subtask['validator'])
            if validator is None:
                ui.show_message('Warning', 'Failed to build validator: %s' % msg, ui.WARNING)
        else:
            ui.show_message('Info', 'No validator specified', ui.INFO)
        if validator:
            for (group, tests) in sorted(subtask['groups'].items()):
                for (i, _) in enumerate(tests, 1):
                    test_file = self.test_filepath(stn, group, i)
                    self.validate_test_input(test_file, validator)

    @ui.work('Validating', '{1}')
    def validate_test_input(self, test_file, validator):
        if not test_file.exists():
            return False, 'Test file does not exist'
        (st, _time, msg) = validator.run(test_file, None)
        return st, msg

    def build_validator(self, source):
        fp = FilePath(self._directory, source)
        if not fp.exists():
            return (None, 'File does not exists.')
        if fp.ext == '.cpp':
            binary = fp.chext('.bin')
            if binary.mtime() < fp.mtime() and not self._cpp_compiler(fp, binary):
                return (None, 'Failed to build validator.')
            return (Runnable(binary), 'OK')
        if fp.ext in ['.py', '.py3']:
            return (Runnable('python3', [str(source)]), 'OK')
        if fp.ext == '.py2':
            return (Runnable('python2', [str(source)]), 'OK')
        return (None, 'Not supported source file.')

    def run(self):
        (subtasks, cmds) = self.parse_file()

        for stn in range(1, subtasks + 1):
            dire = FilePath(self._dataset_directory, 'st%d' % stn).get_or_create_dir()
            dire.clear()

        if not cmds:
            ui.show_message("Warning", 'no commands were executed for the plan.', ui.WARNING)

        for (stn, subtask) in sorted(cmds.items()):
            self.run_subtask(stn, subtask)

    @ui.workgroup('Subtask {1}')
    def run_subtask(self, stn, subtask):
        groups = subtask['groups']
        for (group, tests) in sorted(groups.items()):
            for (i, test) in enumerate(tests, 1):
                cmd = test['cmd']
                test_file = self.test_filepath(stn, group, i)
                if cmd == 'copy':
                    self.copy(test['file'], test_file)
                elif cmd == 'echo':
                    self.echo(test['args'], test_file)
                else:
                    test['args'].insert(0, '%s-%s-%s' % (stn, group, i))
                    source = FilePath(self._directory, test['source'])
                    if cmd == 'cpp':
                        self.run_cpp_generator(source, test['args'], test_file)
                    elif cmd in ['py', 'py2', 'py3']:
                        self.run_py_generator(source, test['args'], test_file, cmd)
                    elif cmd == 'java':
                        self.run_java_generator(source, test['args'], test_file)
                    elif cmd == 'run':
                        bin_path = FilePath(self._directory, test['bin'])
                        self.run_bin_generator(bin_path, test['args'], test_file)
                    else:
                        ui.fatal_error('unexpected command when running plan: %s ' % cmd)

    @ui.work('Copy', '{1}')
    def copy(self, src, dst):
        fp = FilePath(self._task_directory, src)
        if not fp.exists():
            return (False, 'No such file')
        try:
            fp.copy(dst)
            (st, msg) = (True, 'OK')
            return (st, msg)
        except Exception:  # pylint: disable=broad-except
            return (False, 'Error when copying file')

    @ui.work('Echo', '{1}')
    def echo(self, args, dst):
        with dst.open('w') as test_file:
            test_file.write(' '.join(args) + '\n')
            (st, msg) = (True, 'Ok')
            return (st, msg)

    @ui.work('Gen', '{1}')
    def run_cpp_generator(self, source, args, dst):
        if not source.exists():
            return (False, 'No such file')
        binary = source.chext('.bin')
        if binary.mtime() < source.mtime():
            st = self._cpp_compiler(source, binary)
            if not st:
                return (st, 'Failed to build generator')

        (st, _time, msg) = Runnable(binary).run(None, dst, args)
        return (st, msg)

    @ui.work('Gen', '{1}')
    def run_py_generator(self, source, args, dst, cmd):
        if not source.exists():
            return (False, 'No such file')
        python = 'python2' if cmd == 'py2' else 'python3'
        (st, _time, msg) = Runnable(python, [str(source)]).run(None, dst, args)
        return (st, msg)

    @ui.work('Gen', '{1}')
    def run_java_generator(self, source, args, dst):
        if not source.exists():
            return (False, 'No such file')
        bytecode = source.chext('.class')
        if bytecode.mtime() < source.mtime():
            st = self._java_compiler(source)
            if not st:
                return (st, 'Failed to build generator')

        classname = bytecode.rootname()
        classpath = str(bytecode.directory().path())
        (st, _time, msg) = Runnable('java', ['-cp', classpath, classname]).run(None, dst, args)
        return (st, msg)

    @ui.work('Gen', '{1}')
    def run_bin_generator(self, bin_path, args, dst):
        if not bin_path.exists():
            return (False, 'No such file')
        if not Runnable.is_callable(bin_path):
            return (False, 'Cannot run file, it may not have correct permissions')
        (st, _time, msg) = Runnable(bin_path).run(None, dst, args)
        return (st, msg)

    def parse_file(self):  # pylint: disable=too-many-locals,too-many-branches
        """
        Args:
            path (FilePath)
        """
        cmds = {}
        st = 0
        for (lineno, line) in enumerate(self._testplan_path.open('r').readlines(), 1):
            line = line.strip()
            subtask_header = re.compile(r'\s*\[\s*Subtask\s*(\d+)\s*(?:-\s*([^\]\s]+))?\s*\]\s*')
            cmd_line = re.compile(r'\s*([^;\s]+)\s*;\s*(\S+)(:?\s+(.*))?')
            comment = re.compile(r'\s*#.*')

            if not line:
                continue
            if not comment.fullmatch(line):
                header_match = subtask_header.fullmatch(line)
                cmd_match = cmd_line.fullmatch(line)
                if header_match:
                    found_st = int(header_match.group(1))
                    validator = header_match.group(2)
                    if st + 1 != found_st:
                        ui.fatal_error('line %d: found subtask %d, but subtask %d was expected' %
                                       (lineno, found_st, st + 1))
                    st += 1
                    cmds[st] = {'validator': validator, 'groups': {}}
                elif cmd_match:
                    if st == 0:
                        ui.fatal_error(
                            'line %d: found command before declaring a subtask.' % lineno)
                    group = cmd_match.group(1)
                    cmd = cmd_match.group(2)
                    args = (cmd_match.group(3) or '').split()
                    if group not in cmds[st]['groups']:
                        cmds[st]['groups'][group] = []

                    if cmd == 'copy':
                        if len(args) > 2:
                            ui.fatal_error(
                                'line %d: command copy expects exactly one argument.' % lineno)
                        cmds[st]['groups'][group].append({
                            'cmd': 'copy',
                            'file': args[0],
                        })
                    elif cmd == 'echo':
                        cmds[st]['groups'][group].append({'cmd': 'echo', 'args': args})
                    else:
                        f = FilePath(self._directory, cmd)
                        if f.ext in ['.cpp', '.java', '.py', '.py2', '.py3']:
                            cmds[st]['groups'][group].append({
                                'cmd': f.ext[1:],
                                'source': cmd,
                                'args': args
                            })
                        else:
                            cmds[st]['groups'][group].append({
                                'cmd': 'run',
                                'bin': cmd,
                                'args': args
                            })
                else:
                    ui.fatal_error('line %d: error while parsing line `%s`\n' % (lineno, line))
        return (st, cmds)
Example #32
0
    def package(self):
        """Compress statement and dataset of all tasks in a single file"""
        tmpdir = Directory.tmpdir()
        try:
            for task in self._tasks:
                task.copy_to(tmpdir)

            self.build_problemset_twoside()
            self.build_problemset_oneside()
            oneside = FilePath(self._directory, 'oneside.pdf')
            if oneside.exists():
                oneside.copy(FilePath(tmpdir, 'oneside.pdf'))
            twoside = FilePath(self._directory, 'twoside.pdf')
            if twoside.exists():
                twoside.copy(FilePath(tmpdir, 'twoside.pdf'))

            cmd = 'cd %s && zip -r contest.zip .' % tmpdir
            st = subprocess.call(cmd, stdout=subprocess.DEVNULL, shell=True)
            contest = FilePath(self._directory, '%s.zip' % self.name)
            FilePath(tmpdir, 'contest.zip').copy(contest)
        finally:
            tmpdir.rmtree()

        return st == 0
Example #33
0
 def test_filepath(self, stn, group, i):
     st_dir = FilePath(self._dataset_directory,
                       'st%d' % stn).get_or_create_dir()
     return FilePath(st_dir, '%s-%d.in' % (group, i))
Example #34
0
    def parse_file(self):  # pylint: disable=too-many-locals,too-many-branches
        """
        Args:
            path (FilePath)
        """
        cmds = {}
        st = 0
        for (lineno,
             line) in enumerate(self._testplan_path.open('r').readlines(), 1):
            line = line.strip()
            subtask_header = re.compile(
                r'\s*\[\s*Subtask\s*(\d+)\s*(?:-\s*([^\]\s]+))?\s*\]\s*')
            cmd_line = re.compile(r'\s*([^;\s]+)\s*;\s*(\S+)(:?\s+(.*))?')
            comment = re.compile(r'\s*#.*')

            if not line:
                continue
            if not comment.fullmatch(line):
                header_match = subtask_header.fullmatch(line)
                cmd_match = cmd_line.fullmatch(line)
                if header_match:
                    found_st = int(header_match.group(1))
                    validator = header_match.group(2)
                    if st + 1 != found_st:
                        ui.fatal_error(
                            'line %d: found subtask %d, but subtask %d was expected'
                            % (lineno, found_st, st + 1))
                    st += 1
                    cmds[st] = {'validator': validator, 'groups': {}}
                elif cmd_match:
                    if st == 0:
                        ui.fatal_error(
                            'line %d: found command before declaring a subtask.'
                            % lineno)
                    group = cmd_match.group(1)
                    cmd = cmd_match.group(2)
                    args = _parse_args(cmd_match.group(3) or '')
                    if group not in cmds[st]['groups']:
                        cmds[st]['groups'][group] = []

                    if cmd == 'copy':
                        if len(args) > 2:
                            ui.fatal_error(
                                'line %d: command copy expects exactly one argument.'
                                % lineno)
                        cmds[st]['groups'][group].append({
                            'cmd': 'copy',
                            'file': args[0],
                        })
                    elif cmd == 'echo':
                        cmds[st]['groups'][group].append({
                            'cmd': 'echo',
                            'args': args
                        })
                    else:
                        f = FilePath(self._directory, cmd)
                        if f.ext in ['.cpp', '.java', '.py', '.py2', '.py3']:
                            cmds[st]['groups'][group].append({
                                'cmd': f.ext[1:],
                                'source': cmd,
                                'args': args
                            })
                        else:
                            cmds[st]['groups'][group].append({
                                'cmd': 'run',
                                'bin': cmd,
                                'args': args
                            })
                else:
                    ui.fatal_error('line %d: error while parsing line `%s`\n' %
                                   (lineno, line))
        return (st, cmds)