diff --git a/acedit/main.py b/acedit/main.py index f3c533a..d13583a 100644 --- a/acedit/main.py +++ b/acedit/main.py @@ -16,6 +16,10 @@ def validate_args(args): if args['clear_cache']: return + if args['add_test'] and (not args['contest'] and args['site'] != 'spoj' or not args['problem']): + print 'Please specify contest and problem code' + sys.exit(0) + if not args['site'] == 'spoj' and args['contest'] is None: print 'Please specify contest code or set a default contest.' sys.exit(0) @@ -37,6 +41,10 @@ def main(): # set default site util.Utilities.set_constants('default_site', args['default_site']) + elif args['add_test']: + # adding test + util.Utilities.add_test(args) + elif args['default_contest']: # set default contest util.Utilities.set_constants('default_contest', args['default_contest']) diff --git a/acedit/util.py b/acedit/util.py index 28beb23..24b93f4 100644 --- a/acedit/util.py +++ b/acedit/util.py @@ -26,6 +26,12 @@ class Utilities: 'ENDC': '\033[0m', 'BOLD': '\033[1m', } + verdicts = { + 'AC': colors['BOLD'] + colors['GREEN'] + 'AC' + colors['ENDC'], + 'WA': colors['BOLD'] + colors['RED'] + 'WA' + colors['ENDC'], + 'RTE': colors['BOLD'] + colors['RED'] + 'RTE' + colors['ENDC'], + 'TLE': colors['BOLD'] + colors['YELLOW'] + 'TLE' + colors['ENDC'] + } @staticmethod def parse_flags(supported_sites): @@ -53,6 +59,11 @@ def parse_flags(supported_sites): action='store_true', help='Force download the test cases, even if they are cached') + parser.add_argument('--add-test', + dest='add_test', + action='store_true', + help='Add test to specific problem of contest') + parser.add_argument('--run', dest='source_file', help='Name of source file to be run') @@ -103,6 +114,7 @@ def parse_flags(supported_sites): flags['source'] = args.source_file flags['default_site'] = args.default_site flags['default_contest'] = args.default_contest + flags['add_test'] = args.add_test return flags @@ -162,6 +174,30 @@ def clear_cache(site): os.makedirs(os.path.join(Utilities.cache_dir, site)) print 'Done.' + @staticmethod + def get_long_input(message): + print message + lines = [] + while True: + try: + line = raw_input() + if line == '' and len(lines) > 0 and lines[-1] == '': + break + except EOFError: + lines.append('') + break + lines.append(line) + return '\n'.join(lines) + + @staticmethod + def add_test(args): + print 'Adding new test to %s (contest: %s, problem: %s)' % (args['site'], args['contest'], args['problem']) + inputs = [Utilities.get_long_input('Specify input (^D or two consecutive empty lines to stop):')] + outputs = [Utilities.get_long_input('Specify output (^D or two consecutive empty lines to stop):')] + is_in_cache = Utilities.check_cache(args['site'], args['contest'], args['problem']) + Utilities.store_files(args['site'], args['contest'], args['problem'], inputs, outputs) + print 'Test is successfully added' + @staticmethod def store_files(site, contest, problem, inputs, outputs): """ @@ -170,33 +206,36 @@ def store_files(site, contest, problem, inputs, outputs): # Handle case for SPOJ specially as it does not have contests contest = '' if site == 'spoj' else contest + testcases_path = os.path.join(Utilities.cache_dir, site, contest, problem) + num_cases = len(os.listdir(testcases_path)) / 2 for i, inp in enumerate(inputs): - filename = os.path.join( - Utilities.cache_dir, site, contest, problem, 'Input' + str(i)) + filename = os.path.join(testcases_path, 'Input' + str(i + num_cases)) with open(filename, 'w') as handler: handler.write(inp) for i, out in enumerate(outputs): - filename = os.path.join( - Utilities.cache_dir, site, contest, problem, 'Output' + str(i)) + filename = os.path.join(testcases_path, 'Output' + str(i + num_cases)) with open(filename, 'w') as handler: handler.write(out) @staticmethod - def download_problem_testcases(args): - """ - Download test cases for a given problem - """ + def get_platform(args): if args['site'] == 'codeforces': - platform = Codeforces(args) + return Codeforces(args) elif args['site'] == 'codechef': - platform = Codechef(args) + return Codechef(args) elif args['site'] == 'spoj': - platform = Spoj(args) + return Spoj(args) else: - platform = Hackerrank(args) + return Hackerrank(args) + @staticmethod + def download_problem_testcases(args): + """ + Download test cases for a given problem + """ + platform = Utilities.get_platform(args) is_in_cache = Utilities.check_cache( platform.site, platform.contest, platform.problem) @@ -211,13 +250,7 @@ def download_contest_testcases(args): """ Download test cases for all problems in a given contest """ - if args['site'] == 'codeforces': - platform = Codeforces(args) - elif args['site'] == 'codechef': - platform = Codechef(args) - elif args['site'] == 'hackerrank': - platform = Hackerrank(args) - + platform = Utilities.get_platform(args) Utilities.check_cache( platform.site, platform.contest, platform.problem) @@ -259,17 +292,47 @@ def handle_kbd_interrupt(site, contest, problem): # Handle case for SPOJ specially as it does not have contests contest = '' if site == 'spoj' else contest - if problem is not None: - path = os.path.join(Utilities.cache_dir, site, contest, problem) - if os.path.isdir(path): - rmtree(path) - else: - path = os.path.join(Utilities.cache_dir, site, contest) - if os.path.isdir(path): - rmtree(path) + # if problem is not None: + # path = os.path.join(Utilities.cache_dir, site, contest, problem) + # if os.path.isdir(path): + # rmtree(path) + # else: + # path = os.path.join(Utilities.cache_dir, site, contest) + # if os.path.isdir(path): + # rmtree(path) print 'Done. Exiting gracefully.' + @staticmethod + def run_command_on_one_test(testcases_path, testcase_number, execute_command): + status = os.system('timeout 2s ' + execute_command + ' < ' + os.path.join( + testcases_path, 'Input' + str(testcase_number)) + ' > temp_output' + str(testcase_number)) + user_output = '' + with open(os.path.join(testcases_path, 'Output' + str(testcase_number)), 'r') as out_handler: + expected_output = out_handler.read().strip().split('\n') + expected_output = '\n'.join([line.strip() for line in expected_output]) + if status == 124: + # Time Limit Exceeded + results = Utilities.verdicts['TLE'] + + elif status == 0: + # Ran successfully + with open('temp_output' + str(testcase_number), 'r') as temp_handler: + user_output = temp_handler.read().strip().split('\n') + user_output = '\n'.join([line.strip() for line in user_output]) + + if expected_output == user_output: + # All Correct + results = Utilities.verdicts['AC'] + else: + # Wrong Answer + results = Utilities.verdicts['WA'] + + else: + # Runtime Error + results = Utilities.verdicts['RTE'] + return (expected_output, user_output, results) + @staticmethod def run_solution(args): """ @@ -294,91 +357,38 @@ def run_solution(args): if os.path.isdir(testcases_path): num_cases = len(os.listdir(testcases_path)) / 2 - results, expected_outputs, user_outputs = [], [], [] - - if extension == 'py': - - for i in xrange(num_cases): - status = os.system('cat ' + os.path.join(testcases_path, 'Input' + str( - i)) + ' | timeout 3s python \'' + problem_path + '.py\' > temp_output' + str(i)) - if status == 124: - # Time Limit Exceeded - results += [Utilities.colors['BOLD'] + - Utilities.colors['YELLOW'] + 'TLE' + Utilities.colors['ENDC']] - - elif status == 0: - # Ran successfully - with open('temp_output' + str(i), 'r') as temp_handler, open(os.path.join(testcases_path, 'Output' + str(i)), 'r') as out_handler: - expected_output = out_handler.read().strip().split('\n') - user_output = temp_handler.read().strip().split('\n') - - expected_output = '\n'.join( - [line.strip() for line in expected_output]) - user_output = '\n'.join( - [line.strip() for line in user_output]) - - expected_outputs += [expected_output] - user_outputs += [user_output] - - if expected_output == user_output: - # All Correct - results += [Utilities.colors['BOLD'] + Utilities.colors[ - 'GREEN'] + 'AC' + Utilities.colors['ENDC']] - else: - # Wrong Answer - results += [Utilities.colors['BOLD'] + - Utilities.colors['RED'] + 'WA' + Utilities.colors['ENDC']] - - else: - # Runtime Error - results += [Utilities.colors['BOLD'] + - Utilities.colors['RED'] + 'RTE' + Utilities.colors['ENDC']] - - elif extension in ['c', 'cpp', 'java']: - - compiler = {'c': 'gcc', 'cpp': 'g++', 'java': 'javac -d .'}[extension] - execute_command = {'c': './a.out', 'cpp': './a.out', 'java': 'java ' + basename}[extension] - compile_status = os.system( - compiler + ' \'' + problem_path + '.' + extension + '\'') + results, expected_outputs, user_outputs = [''] * num_cases, [''] * num_cases, [''] * num_cases + + if extension in ['c', 'cpp', 'java', 'py', 'hs', 'rb', 'kt']: + + compiler = { + 'hs': 'ghc --make -O -dynamic -o ' + basename, + 'py': None, + 'rb': None, + 'c': 'gcc -static -DONLINE_JUDGE -fno-asm -lm -s -O2 -o ' + basename, + 'cpp': 'clang++ -DONLINE_JUDGE -lm -s -x c++ -include /home/igorjan/206round/bits.h -O2 -std=c++17 -o ' + basename, + 'kt': 'kotlinc -d .', + 'java': 'javac -d .' + }[extension] + execute_command = { + 'py': 'python ' + basename + '.' + extension, + 'rb': 'ruby ' + basename + '.' + extension, + 'hs': './' + basename, + 'c': './' + basename, + 'cpp': './' + basename, + 'kt': 'kotlin -DONLINE_JUDGE=true -Duser.language=en -Duser.region=US -Duser.variant=US ' + basename + 'Kt', + 'java': 'java -DONLINE_JUDGE=true -Duser.language=en -Duser.region=US -Duser.variant=US ' + basename + }[extension] + if compiler is None: + compile_status = 0 + else: + compile_status = os.system(compiler + ' \'' + problem_path + '.' + extension + '\'') if compile_status == 0: # Compiled successfully for i in xrange(num_cases): - status = os.system('timeout 2s ' + execute_command + ' < ' + os.path.join( - testcases_path, 'Input' + str(i)) + ' > temp_output' + str(i)) - if status == 124: - # Time Limit Exceeded - results += [Utilities.colors['BOLD'] + Utilities.colors[ - 'YELLOW'] + 'TLE' + Utilities.colors['ENDC']] - - elif status == 0: - # Ran successfully - with open('temp_output' + str(i), 'r') as temp_handler, open(os.path.join(testcases_path, 'Output' + str(i)), 'r') as out_handler: - expected_output = out_handler.read().strip().split('\n') - user_output = temp_handler.read().strip().split('\n') - - expected_output = '\n'.join( - [line.strip() for line in expected_output]) - user_output = '\n'.join( - [line.strip() for line in user_output]) - - expected_outputs += [expected_output] - user_outputs += [user_output] - - if expected_output == user_output: - # All Correct - results += [Utilities.colors['BOLD'] + Utilities.colors[ - 'GREEN'] + 'AC' + Utilities.colors['ENDC']] - else: - # Wrong Answer - results += [Utilities.colors['BOLD'] + Utilities.colors[ - 'RED'] + 'WA' + Utilities.colors['ENDC']] - - else: - # Runtime Error - results += [Utilities.colors['BOLD'] + - Utilities.colors['RED'] + 'RTE' + Utilities.colors['ENDC']] + expected_outputs[i], user_outputs[i], results[i] = Utilities.run_command_on_one_test(testcases_path, i, execute_command) else: # Compilation error occurred message = Utilities.colors['BOLD'] + Utilities.colors[ @@ -387,7 +397,7 @@ def run_solution(args): sys.exit(0) else: - print 'Supports only C, C++, Python and Java as of now.' + print 'Supports only C, C++, Python, Kotlin and Java as of now.' sys.exit(0) from terminaltables import AsciiTable @@ -479,17 +489,17 @@ def parse_html(self, req): formatted_inputs, formatted_outputs = [], [] - for inp in inputs: + def getContent(inp): pre = inp.find('pre').decode_contents() pre = reduce(lambda a, kv: a.replace(*kv), repls, pre) pre = re.sub('<[^<]+?>', '', pre) - formatted_inputs += [pre] + return re.sub(r'^\s*', '', pre) + + for inp in inputs: + formatted_inputs += [getContent(inp)] for out in outputs: - pre = out.find('pre').decode_contents() - pre = reduce(lambda a, kv: a.replace(*kv), repls, pre) - pre = re.sub('<[^<]+?>', '', pre) - formatted_outputs += [pre] + formatted_outputs += [getContent(out)] # print 'Inputs', formatted_inputs # print 'Outputs', formatted_outputs @@ -543,8 +553,8 @@ def scrape_problem(self): Method to scrape a single problem from codeforces """ print 'Fetching problem ' + self.contest + '-' + self.problem + ' from Codeforces...' - url = 'http://codeforces.com/contest/' + \ - self.contest + '/problem/' + self.problem + type = 'contest' if int(self.contest) <= 100000 else 'gym' + url = 'http://codeforces.com/%s/%s/problem/%s' % (type, self.contest, self.problem) req = Utilities.get_html(url) inputs, outputs = self.parse_html(req) Utilities.store_files(self.site, self.contest, @@ -556,7 +566,8 @@ def scrape_contest(self): Method to scrape all problems from a given codeforces contest """ print 'Checking problems available for contest ' + self.contest + '...' - url = 'http://codeforces.com/contest/' + self.contest + type = 'contest' if int(self.contest) <= 100000 else 'gym' + url = 'http://codeforces.com/%s/%s' % (type, self.contest) req = Utilities.get_html(url) links = self.get_problem_links(req)