Skip to content
8 changes: 8 additions & 0 deletions acedit/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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'])
Expand Down
245 changes: 128 additions & 117 deletions acedit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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):
"""
Expand All @@ -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)

Expand All @@ -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)

Expand Down Expand Up @@ -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):
"""
Expand All @@ -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[
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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)

Expand Down