Skip to content

Commit

Permalink
#208: add submit feature for HackerRank
Browse files Browse the repository at this point in the history
  • Loading branch information
kmyk committed Feb 4, 2019
1 parent daff0a5 commit b571275
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 5 deletions.
1 change: 1 addition & 0 deletions onlinejudge/implementation/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def get_parser() -> argparse.ArgumentParser:
Codeforces
TopCoder (Marathon Match)
Yukicoder
HackerRank
''')
subparser.add_argument('url', nargs='?', help='the URL of the problem to submit. if not given, guessed from history of download command.')
Expand Down
26 changes: 22 additions & 4 deletions onlinejudge/service/hackerrank.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,29 @@ def _get_model(self, session: Optional[requests.Session] = None) -> Dict[str, An
raise onlinejudge.type.SubmissionError
return it['model']

def _get_lang_display_mapping(self, session: Optional[requests.Session] = None) -> Dict[str, str]:
session = session or utils.new_default_session()
# get
url = 'https://hrcdn.net/hackerrank/assets/codeshell/dist/codeshell-cdffcdf1564c6416e1a2eb207a4521ce.js' # at "Mon Feb 4 14:51:27 JST 2019"
resp = utils.request('GET', url, session=session)
# parse
s = resp.content.decode()
l = s.index('lang_display_mapping:{c:"C",')
l = s.index('{', l)
r = s.index('}', l) + 1
s = s[l:r]
log.debug('lang_display_mapping (raw): %s', s) # this is not a json
lang_display_mapping = {}
for lang in s[1:-2].split('",'):
key, value = lang.split(':"')
lang_display_mapping[key] = value
log.debug('lang_display_mapping (parsed): %s', lang_display_mapping)
return lang_display_mapping

def get_language_dict(self, session: Optional[requests.Session] = None) -> Dict[str, Dict[str, str]]:
session = session or utils.new_default_session()
info = self._get_model(session=session)
# lang_display_mapping from https://hrcdn.net/hackerrank/assets/codeshell/dist/codeshell-449bb296b091277fedc42b23f7c9c447.js, Sun Feb 19 02:25:36 JST 2017
lang_display_mapping = {'c': 'C', 'cpp': 'C++', 'java': 'Java 7', 'csharp': 'C#', 'haskell': 'Haskell', 'php': 'PHP', 'python': 'Python 2', 'pypy': 'Pypy 2', 'pypy3': 'Pypy 3', 'ruby': 'Ruby', 'perl': 'Perl', 'bash': 'BASH', 'oracle': 'Oracle', 'mysql': 'MySQL', 'sql': 'SQL', 'clojure': 'Clojure', 'scala': 'Scala', 'code': 'Generic', 'text': 'Plain Text', 'brainfuck': 'Brainfuck', 'javascript': 'Javascript', 'typescript': 'Typescript', 'lua': 'Lua', 'sbcl': 'Common Lisp (SBCL)', 'erlang': 'Erlang', 'go': 'Go', 'd': 'D', 'ocaml': 'OCaml', 'pascal': 'Pascal', 'python3': 'Python 3', 'groovy': 'Groovy', 'objectivec': 'Objective-C', 'text_pseudo': 'Plain Text', 'fsharp': 'F#', 'visualbasic': 'VB. NET', 'cobol': 'COBOL', 'tsql': 'MS SQL Server', 'lolcode': 'LOLCODE', 'smalltalk': 'Smalltalk', 'tcl': 'Tcl', 'whitespace': 'Whitespace', 'css': 'CSS', 'html': 'HTML', 'java8': 'Java 8', 'db2': 'DB2', 'octave': 'Octave', 'r': 'R', 'xquery': 'XQuery', 'racket': 'Racket', 'xml': 'XML', 'rust': 'Rust', 'swift': 'Swift', 'elixir': 'Elixir', 'fortran': 'Fortran', 'ada': 'Ada', 'nim': 'Nim', 'julia': 'Julia', 'cpp14': 'C++14', 'coffeescript': 'Coffeescript'}
lang_display_mapping = self._get_lang_display_mapping()
result = {}
for lang in info['languages']:
descr = lang_display_mapping.get(lang)
Expand All @@ -170,7 +188,7 @@ def get_language_dict(self, session: Optional[requests.Session] = None) -> Dict[
result[lang] = {'description': descr}
return result

def submit(self, code: str, language: str, session: Optional[requests.Session] = None) -> onlinejudge.type.Submission:
def submit_code(self, code: bytes, language: str, session: Optional[requests.Session] = None) -> onlinejudge.type.Submission:
session = session or utils.new_default_session()
# get
resp = utils.request('GET', self.get_url(), session=session)
Expand All @@ -179,7 +197,7 @@ def submit(self, code: str, language: str, session: Optional[requests.Session] =
csrftoken = soup.find('meta', attrs={'name': 'csrf-token'}).attrs['content']
# post
url = 'https://www.hackerrank.com/rest/contests/{}/challenges/{}/submissions'.format(self.contest_slug, self.challenge_slug)
payload = {'code': code, 'language': language}
payload = {'code': code, 'language': language, 'contest_slug': self.contest_slug}
log.debug('payload: %s', payload)
resp = utils.request('POST', url, session=session, json=payload, headers={'X-CSRF-Token': csrftoken})
# parse
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Tools for online judge services. Downloading sample cases, Testing/Submitting yo
- AtCoder
- ~~Yukicoder~~ (removed)
- Codeforces
- ~~HackerRank~~ (removed)
- HackerRank
- TopCoder (Marathon Match)
- Generate scanner for input (experimental)
- AtCoder
Expand Down
27 changes: 27 additions & 0 deletions tests/command_submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,30 @@ def test_call_submit_beta_3_b(self):
ojtools = os.path.abspath('oj')
with tests.utils.sandbox(files):
subprocess.check_call([ojtools, 's', '-y', '--no-open', url, 'main.cpp'], stdout=sys.stdout, stderr=sys.stderr)


class SubmitHackerRankTest(unittest.TestCase):
@unittest.skipIf('CI' in os.environ, 'login is required')
def test_call_submit_worldcodesprint_mars_exploration(self):
url = 'https://www.hackerrank.com/contests/worldcodesprint/challenges/mars-exploration'
code = '''#!/usr/bin/env python3
s = input()
ans = 0
for i in range(len(s) // 3):
if s[3 * i] != 'S':
ans += 1
if s[3 * i + 1] != 'O':
ans += 1
if s[3 * i + 2] != 'S':
ans += 1
print(ans)
'''
files = [
{
'path': 'a.py',
'data': code
},
]
ojtools = os.path.abspath('oj')
with tests.utils.sandbox(files):
subprocess.check_call([ojtools, 's', '-y', '--no-open', url, 'a.py'], stdout=sys.stdout, stderr=sys.stderr)

0 comments on commit b571275

Please sign in to comment.