kattis-cli 1.0.7__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kattis_cli/client.py +466 -0
- kattis_cli/download.py +196 -145
- kattis_cli/fireworks.py +128 -0
- kattis_cli/kattis.py +68 -412
- kattis_cli/kattis_setup.py +124 -87
- kattis_cli/main.py +3 -3
- kattis_cli/solution_tester.py +221 -0
- kattis_cli/ui.py +112 -71
- {kattis_cli-1.0.7.dist-info → kattis_cli-1.1.0.dist-info}/METADATA +28 -20
- kattis_cli-1.1.0.dist-info/RECORD +21 -0
- {kattis_cli-1.0.7.dist-info → kattis_cli-1.1.0.dist-info}/WHEEL +1 -1
- kattis_cli/test_solution.py +0 -179
- kattis_cli-1.0.7.dist-info/RECORD +0 -19
- {kattis_cli-1.0.7.dist-info → kattis_cli-1.1.0.dist-info}/entry_points.txt +0 -0
- {kattis_cli-1.0.7.dist-info → kattis_cli-1.1.0.dist-info/licenses}/LICENSE +0 -0
kattis_cli/kattis.py
CHANGED
|
@@ -1,433 +1,89 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"""Compatibility thin wrapper exposing a KattisClient instance.
|
|
2
|
+
|
|
3
|
+
Refactored: main functionality moved to `kattis_cli.client.KattisClient`.
|
|
4
|
+
This module keeps the original functional API by delegating to a
|
|
5
|
+
module-level client instance so existing call-sites do not need to change.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
|
-
from typing import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import json
|
|
13
|
-
import configparser
|
|
14
|
-
from bs4 import BeautifulSoup
|
|
15
|
-
import requests
|
|
16
|
-
import requests.exceptions
|
|
17
|
-
import requests.cookies
|
|
18
|
-
from rich.console import Console
|
|
19
|
-
from rich.align import Align
|
|
20
|
-
from rich.live import Live
|
|
21
|
-
from rich.prompt import Confirm
|
|
22
|
-
|
|
23
|
-
from kattis_cli.utils import languages
|
|
24
|
-
from kattis_cli.utils import config
|
|
25
|
-
from kattis_cli import ui
|
|
26
|
-
|
|
27
|
-
_HEADERS = {'User-Agent': 'kattis-cli-submit'}
|
|
28
|
-
|
|
29
|
-
_RUNNING_STATUS = 5
|
|
30
|
-
_COMPILE_ERROR_STATUS = 8
|
|
31
|
-
_ACCEPTED_STATUS = 16
|
|
32
|
-
_DEBUG = False
|
|
33
|
-
|
|
34
|
-
_STATUS_MAP = {
|
|
35
|
-
0: 'New', # <invalid value>
|
|
36
|
-
1: 'New',
|
|
37
|
-
2: 'Waiting for compile',
|
|
38
|
-
3: 'Compiling',
|
|
39
|
-
4: 'Waiting for run',
|
|
40
|
-
_RUNNING_STATUS: 'Running',
|
|
41
|
-
6: 'Judge Error',
|
|
42
|
-
7: 'Submission Error',
|
|
43
|
-
_COMPILE_ERROR_STATUS: 'Compile Error',
|
|
44
|
-
9: 'Run Time Error',
|
|
45
|
-
10: 'Memory Limit Exceeded',
|
|
46
|
-
11: 'Output Limit Exceeded',
|
|
47
|
-
12: 'Time Limit Exceeded',
|
|
48
|
-
13: 'Illegal Function',
|
|
49
|
-
14: 'Wrong Answer',
|
|
50
|
-
# 15: '<invalid value>',
|
|
51
|
-
_ACCEPTED_STATUS: 'Accepted',
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def get_url(cfg: configparser.ConfigParser, option: str, default: str) -> str:
|
|
56
|
-
"""Get a URL from the config file
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
cfg (configparser.ConfigParser): _description_
|
|
60
|
-
option (str): option name
|
|
61
|
-
default (str): default value
|
|
62
|
-
|
|
63
|
-
Returns:
|
|
64
|
-
str: _description_
|
|
65
|
-
"""
|
|
66
|
-
if cfg.has_option('kattis', option):
|
|
67
|
-
return cfg.get('kattis', option)
|
|
68
|
-
else:
|
|
69
|
-
return f"https://{cfg.get('kattis', 'hostname')}/{default}"
|
|
8
|
+
from typing import Any, List
|
|
9
|
+
from kattis_cli.client import KattisClient
|
|
10
|
+
|
|
11
|
+
# Single client used by module-level functions for backward compatibility
|
|
12
|
+
_client = KattisClient()
|
|
70
13
|
|
|
71
14
|
|
|
72
15
|
def login(
|
|
73
16
|
login_url: str,
|
|
74
17
|
username: str,
|
|
75
18
|
password: str = '',
|
|
76
|
-
token: str = '') ->
|
|
77
|
-
"""
|
|
19
|
+
token: str = '') -> Any:
|
|
20
|
+
"""Convenience wrapper that delegates to the module-level client.
|
|
78
21
|
|
|
79
|
-
|
|
22
|
+
Kept for backward compatibility with the original procedural API.
|
|
23
|
+
"""
|
|
80
24
|
|
|
81
|
-
|
|
82
|
-
login_url (str): URL to login page
|
|
83
|
-
username (str): username
|
|
84
|
-
password (str, optional): password. Defaults to ''
|
|
85
|
-
token (str, optional): token. Defaults to ''
|
|
25
|
+
return _client.login(login_url, username, password, token)
|
|
86
26
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
timeout=10)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def login_from_config(cfg: configparser.ConfigParser) -> requests.Response:
|
|
103
|
-
"""Log in to Kattis using the access information in a kattisrc file
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
cfg (configparser.ConfigParser)
|
|
107
|
-
- ConfigParser object for the kattisrc file
|
|
108
|
-
Returns:
|
|
109
|
-
requests (requests.Response)
|
|
110
|
-
- Response with cookies needed to be able to submit
|
|
111
|
-
"""
|
|
112
|
-
username = cfg.get('user', 'username')
|
|
113
|
-
password = token = ''
|
|
114
|
-
try:
|
|
115
|
-
password = cfg.get('user', 'password')
|
|
116
|
-
except configparser.NoOptionError:
|
|
117
|
-
pass
|
|
118
|
-
try:
|
|
119
|
-
token = cfg.get('user', 'token')
|
|
120
|
-
except configparser.NoOptionError:
|
|
121
|
-
pass
|
|
122
|
-
if not password and not token:
|
|
123
|
-
raise config.ConfigError('''\
|
|
124
|
-
Your .kattisrc file appears corrupted. It must provide a token (or a
|
|
125
|
-
KATTIS password).
|
|
126
|
-
|
|
127
|
-
Please download a new .kattisrc file''')
|
|
128
|
-
|
|
129
|
-
loginurl = get_url(cfg, 'loginurl', 'login')
|
|
130
|
-
return login(loginurl, username, password, token)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def submit(
|
|
134
|
-
submit_url: str,
|
|
135
|
-
cookies: requests.cookies.RequestsCookieJar,
|
|
136
|
-
problem: str,
|
|
137
|
-
language: str,
|
|
138
|
-
files: List[str],
|
|
139
|
-
mainclass: str,
|
|
140
|
-
tag: str) -> requests.Response:
|
|
141
|
-
"""Make a submission.
|
|
142
|
-
|
|
143
|
-
The url_opener argument is an OpenerDirector object to use (as
|
|
144
|
-
returned by the login() function)
|
|
145
|
-
|
|
146
|
-
Returns the requests.Result from the submission
|
|
147
|
-
"""
|
|
148
|
-
# mainfile = utility.guess_mainfile(language, files, problem)
|
|
149
|
-
# mainclass = mainfile
|
|
150
|
-
|
|
151
|
-
data = {'submit': 'true',
|
|
152
|
-
'submit_ctr': 2,
|
|
153
|
-
'language': language,
|
|
154
|
-
'mainclass': mainclass,
|
|
155
|
-
'problem': problem,
|
|
156
|
-
'tag': tag,
|
|
157
|
-
'script': 'true'}
|
|
158
|
-
|
|
159
|
-
sub_files = []
|
|
160
|
-
for f in files:
|
|
161
|
-
with open(f, 'rb') as sub_file:
|
|
162
|
-
sub_files.append(('sub_file[]',
|
|
163
|
-
(os.path.basename(f),
|
|
164
|
-
sub_file.read(),
|
|
165
|
-
'application/octet-stream')))
|
|
166
|
-
|
|
167
|
-
return requests.post(
|
|
27
|
+
|
|
28
|
+
def login_from_config(cfg: Any) -> Any:
|
|
29
|
+
"""Delegate login using a parsed .kattisrc ConfigParser."""
|
|
30
|
+
|
|
31
|
+
return _client.login_from_config(cfg)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def submit(submit_url: str, cookies: Any, problem: str,
|
|
35
|
+
language: str, files: List[str], mainclass: str, tag: str) -> Any:
|
|
36
|
+
"""Delegate a submission to the configured KattisClient instance."""
|
|
37
|
+
|
|
38
|
+
return _client.submit(
|
|
168
39
|
submit_url,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def confirm_or_die(problem: str, language: str,
|
|
176
|
-
files: List[str], mainclass: str,
|
|
177
|
-
tag: str) -> None:
|
|
178
|
-
"""Confirm submission"""
|
|
179
|
-
console = Console()
|
|
180
|
-
console.clear()
|
|
181
|
-
console.print('Problem:', problem)
|
|
182
|
-
console.print('Language:', language)
|
|
183
|
-
console.print('Files:', ', '.join(files))
|
|
184
|
-
if mainclass:
|
|
185
|
-
if language in languages.GUESS_MAINFILE:
|
|
186
|
-
console.print('Main file:', mainclass)
|
|
187
|
-
else:
|
|
188
|
-
console.print('Mainclass:', mainclass)
|
|
189
|
-
if tag:
|
|
190
|
-
console.print('Tag:', tag)
|
|
191
|
-
if not Confirm.ask('Submit to Kattis', default=True):
|
|
192
|
-
console.print('Cancelling...')
|
|
193
|
-
sys.exit(1)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def get_submission_url(submit_response: str,
|
|
197
|
-
cfg: configparser.ConfigParser
|
|
198
|
-
) -> str:
|
|
199
|
-
"""Get the URL of the submission from the HTML response
|
|
200
|
-
"""
|
|
201
|
-
m = re.search(r'Submission ID: (\d+)', submit_response)
|
|
202
|
-
if m:
|
|
203
|
-
submissions_url = get_url(cfg, 'submissionsurl', 'submissions')
|
|
204
|
-
submission_id = m.group(1)
|
|
205
|
-
return f'{submissions_url}/{submission_id}'
|
|
206
|
-
else:
|
|
207
|
-
raise config.ConfigError('Could not find submission ID in response')
|
|
40
|
+
cookies,
|
|
41
|
+
problem,
|
|
42
|
+
language,
|
|
43
|
+
files,
|
|
44
|
+
mainclass,
|
|
45
|
+
tag)
|
|
208
46
|
|
|
209
47
|
|
|
210
|
-
def get_submission_status(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
"""Get judge status for a submission"""
|
|
215
|
-
reply = requests.get(
|
|
216
|
-
submission_url + '?json',
|
|
217
|
-
cookies=cookies,
|
|
218
|
-
headers=_HEADERS, timeout=10)
|
|
219
|
-
return reply.json()
|
|
48
|
+
def get_submission_status(submission_url: str, cookies: Any) -> Any:
|
|
49
|
+
"""Return parsed JSON status for a submission by delegating to client."""
|
|
50
|
+
|
|
51
|
+
return _client.get_submission_status(submission_url, cookies)
|
|
220
52
|
|
|
221
|
-
|
|
53
|
+
|
|
54
|
+
def get_submission_url(submit_response: str, cfg: Any) -> str:
|
|
55
|
+
"""Extract the submission URL from a submission response string."""
|
|
56
|
+
|
|
57
|
+
return _client.get_submission_url(submit_response, cfg)
|
|
222
58
|
|
|
223
59
|
|
|
224
60
|
def parse_row_html(html: str) -> Any:
|
|
225
|
-
"""Parse
|
|
61
|
+
"""Parse a submission HTML row and return a structured tuple."""
|
|
226
62
|
|
|
227
|
-
|
|
228
|
-
html (str): html string
|
|
63
|
+
return _client.parse_row_html(html)
|
|
229
64
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
soup = BeautifulSoup(html, 'html.parser')
|
|
239
|
-
tr_submission: Any = soup.find("tr", {"data-submission-id": True})
|
|
240
|
-
# print(tr_submission)
|
|
241
|
-
if tr_submission:
|
|
242
|
-
td_cputime = tr_submission.findChild("td", {"data-type": "cpu"})
|
|
243
|
-
if td_cputime:
|
|
244
|
-
runtime = td_cputime.text.strip().replace(' ', ' ')
|
|
245
|
-
if not runtime:
|
|
246
|
-
runtime = '❓ s'
|
|
247
|
-
div_status = tr_submission.findChild(
|
|
248
|
-
"div", {"class": "status"}, recursive=True)
|
|
249
|
-
if div_status:
|
|
250
|
-
status = div_status.text.strip()
|
|
251
|
-
else:
|
|
252
|
-
status = '❓'
|
|
253
|
-
td_lang = tr_submission.findChild(
|
|
254
|
-
"td", {"data-type": "lang"}, recursive=False)
|
|
255
|
-
if td_lang:
|
|
256
|
-
language = td_lang.text.strip()
|
|
257
|
-
else:
|
|
258
|
-
language = '❓'
|
|
259
|
-
td_test_cases = tr_submission.findChild(
|
|
260
|
-
"td", {"data-type": "testcases"})
|
|
261
|
-
if td_test_cases:
|
|
262
|
-
test_status = td_test_cases.text.strip()
|
|
263
|
-
|
|
264
|
-
i_tag = soup.findAll("i", {"class": True})
|
|
265
|
-
test_result = []
|
|
266
|
-
for num, i in enumerate(i_tag):
|
|
267
|
-
if 'title' not in i.attrs:
|
|
268
|
-
continue
|
|
269
|
-
title = i['title'].split(':')
|
|
270
|
-
# print(title[-1])
|
|
271
|
-
if 'Accepted' in title[-1]: # Accepted
|
|
272
|
-
test_result.append(f'{num}✅')
|
|
273
|
-
elif 'not checked' in title[-1]: # not checked
|
|
274
|
-
test_result.append(f'{num}❓')
|
|
275
|
-
else:
|
|
276
|
-
test_result.append(f'{num}❌')
|
|
277
|
-
return runtime, status, language, test_status, test_result
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
def show_kattis_judgement(problemid: str, submission_url: str,
|
|
281
|
-
cfg: configparser.ConfigParser) -> None:
|
|
282
|
-
"""Show judgement from Kattis.
|
|
283
|
-
"""
|
|
284
|
-
config_data = ui.show_problem_metadata(problemid)
|
|
285
|
-
config_data['submissions'] += 1
|
|
286
|
-
console = Console()
|
|
287
|
-
login_reply = login_from_config(cfg)
|
|
288
|
-
title = '\n[bold blue] '
|
|
289
|
-
title += ':cat: Kattis Judgement Results :cat:[/]\n'
|
|
290
|
-
status_id = 0
|
|
291
|
-
with Live(console=console, screen=False,
|
|
292
|
-
refresh_per_second=10) as kattis_live:
|
|
293
|
-
counter = 1
|
|
294
|
-
while True:
|
|
295
|
-
time.sleep(0.1)
|
|
296
|
-
result = get_submission_status(submission_url,
|
|
297
|
-
login_reply.cookies)
|
|
298
|
-
if _DEBUG:
|
|
299
|
-
with open(f'{counter}.json', 'w', encoding='utf-8') as f:
|
|
300
|
-
json.dump(result, f, indent=4)
|
|
301
|
-
rt, status, lang, t_status, t_results = parse_row_html(
|
|
302
|
-
result['row_html'])
|
|
303
|
-
# console.print(f'{runtime=} {status=} {language=}')
|
|
304
|
-
status_id = int(result['status_id'])
|
|
305
|
-
# cases_done = int(result['testcase_index'])
|
|
306
|
-
if status_id > 4 and status_id < 16:
|
|
307
|
-
judgement = f'[bold red] {status}[/]'
|
|
308
|
-
else:
|
|
309
|
-
judgement = f'[bold yellow] {status}[/]'
|
|
310
|
-
if status_id == 12:
|
|
311
|
-
runtime = f'[bold red] {rt}[/]'
|
|
312
|
-
else:
|
|
313
|
-
runtime = f'[bold yellow] {rt}[/]'
|
|
314
|
-
|
|
315
|
-
text = title + f'\n[bold blue]JUDGEMENT:[/] {judgement}'
|
|
316
|
-
text += f'\t[bold blue]LANGUAGE:[/] [bold yellow]{lang}[/]'
|
|
317
|
-
text += f'\t[bold blue]RUNTIME:[/] {runtime}'
|
|
318
|
-
text += f'\t[bold deep_pink3][link={submission_url}]'
|
|
319
|
-
text += 'VIEW DETAILS ON KATTIS[/link][/]\n\n'
|
|
320
|
-
text += f'[bold blue]TESTCASES:[/] [bold green]{t_status}[/]\n\n'
|
|
321
|
-
if status_id < 5:
|
|
322
|
-
test_cases = '🤞🏻🤞🏻🤞🏻🤞🏻🤞🏻 [bold yellow]\
|
|
323
|
-
WAITING...[/] 🤞🏻🤞🏻🤞🏻🤞🏻🤞🏻'
|
|
324
|
-
else:
|
|
325
|
-
test_cases = ' '.join(t_results)
|
|
326
|
-
test_cases += '\n'
|
|
327
|
-
text += test_cases
|
|
328
|
-
kattis_live.update(Align.center(text))
|
|
329
|
-
if status_id > 5:
|
|
330
|
-
kattis_live.stop()
|
|
331
|
-
break
|
|
332
|
-
if status_id == _ACCEPTED_STATUS:
|
|
333
|
-
verdict = '👍🎆🔥🎈🎈 [bold yellow]YAY!! \
|
|
334
|
-
KEEP GOING...[/] 🎈🎈👍🎆🔥'
|
|
335
|
-
console.print()
|
|
336
|
-
config_data['accepted'] += 1
|
|
337
|
-
else:
|
|
338
|
-
verdict = '💪🧐💪 [bold green]SORRY![/] 🧐💪🧐'
|
|
339
|
-
console.print(Align.center(verdict))
|
|
340
|
-
config.update_problem_metadata(problemid, config_data)
|
|
341
|
-
config_data = ui.show_problem_metadata(problemid)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
def get_login_reply(cfg: configparser.ConfigParser) -> requests.Response:
|
|
345
|
-
"""Log in to Kattis.
|
|
346
|
-
"""
|
|
347
|
-
console = Console()
|
|
348
|
-
|
|
349
|
-
try:
|
|
350
|
-
login_reply = login_from_config(cfg)
|
|
351
|
-
except config.ConfigError as exc:
|
|
352
|
-
console.print(exc)
|
|
353
|
-
sys.exit(1)
|
|
354
|
-
except requests.exceptions.RequestException as err:
|
|
355
|
-
console.print('Login connection failed:', err)
|
|
356
|
-
sys.exit(1)
|
|
357
|
-
|
|
358
|
-
if not login_reply.status_code == 200:
|
|
359
|
-
console.print('Login failed.')
|
|
360
|
-
if login_reply.status_code == 403:
|
|
361
|
-
console.print('Incorrect username or password/token (403)')
|
|
362
|
-
elif login_reply.status_code == 404:
|
|
363
|
-
console.print('Incorrect login URL (404)')
|
|
364
|
-
else:
|
|
365
|
-
console.print('Status code:', login_reply.status_code)
|
|
366
|
-
sys.exit(1)
|
|
367
|
-
return login_reply
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
def submit_solution(files: List[str], problemid: str,
|
|
371
|
-
language: str, mainclass: str,
|
|
372
|
-
tag: str, force: bool) -> None:
|
|
373
|
-
"""Submit a solution to Kattis.
|
|
374
|
-
|
|
375
|
-
Args:
|
|
376
|
-
files (Tuple[str]): Tuple of files to submit
|
|
377
|
-
problem (str, optional): problemid. Defaults to ''.
|
|
378
|
-
language (str, optional): language. Defaults to ''.
|
|
379
|
-
mainclass (str, optional): main class/file. Defaults to ''.
|
|
380
|
-
tag (str, optional): Tag. Defaults to ''.
|
|
381
|
-
force (bool, optional): Force bumission without confirmation.
|
|
65
|
+
|
|
66
|
+
def show_kattis_judgement(
|
|
67
|
+
problemid: str,
|
|
68
|
+
submission_url: str,
|
|
69
|
+
cfg: Any) -> None:
|
|
70
|
+
"""Display live judgement output for a submission.
|
|
71
|
+
|
|
72
|
+
This is a thin wrapper around the client's method.
|
|
382
73
|
"""
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
confirm_or_die(problemid, language, files, mainclass, tag)
|
|
400
|
-
|
|
401
|
-
try:
|
|
402
|
-
result = submit(submit_url,
|
|
403
|
-
login_reply.cookies,
|
|
404
|
-
problemid,
|
|
405
|
-
language,
|
|
406
|
-
files,
|
|
407
|
-
mainclass,
|
|
408
|
-
tag)
|
|
409
|
-
except requests.exceptions.RequestException as err:
|
|
410
|
-
console.print('Submit connection failed:', err, style='bold red')
|
|
411
|
-
sys.exit(1)
|
|
412
|
-
|
|
413
|
-
if result.status_code != 200:
|
|
414
|
-
console.print('Submission failed.', style='bold red')
|
|
415
|
-
if result.status_code == 403:
|
|
416
|
-
console.print('Access denied (403)', style='bold red')
|
|
417
|
-
elif result.status_code == 404:
|
|
418
|
-
console.print('Incorrect submit URL (404)')
|
|
419
|
-
else:
|
|
420
|
-
console.print('Status code:', result.status_code, style='bold red')
|
|
421
|
-
sys.exit(1)
|
|
422
|
-
|
|
423
|
-
plain_result = result.content.decode('utf-8').replace('<br />', '\n')
|
|
424
|
-
console.print(f'[bold blue]{plain_result}[/]')
|
|
425
|
-
|
|
426
|
-
submission_url = None
|
|
427
|
-
try:
|
|
428
|
-
submission_url = get_submission_url(plain_result, cfg)
|
|
429
|
-
except configparser.NoOptionError:
|
|
430
|
-
pass
|
|
431
|
-
|
|
432
|
-
if submission_url:
|
|
433
|
-
show_kattis_judgement(problemid, submission_url, cfg)
|
|
74
|
+
|
|
75
|
+
return _client.show_kattis_judgement(problemid, submission_url, cfg)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_login_reply(cfg: Any) -> Any:
|
|
79
|
+
"""Obtain a logged-in requests.Response using config credentials."""
|
|
80
|
+
|
|
81
|
+
return _client.get_login_reply(cfg)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def submit_solution(files: List[str], problemid: str, language: str,
|
|
85
|
+
mainclass: str, tag: str, force: bool) -> None:
|
|
86
|
+
"""High-level helper to submit solution files for a problem id."""
|
|
87
|
+
|
|
88
|
+
return _client.submit_solution(
|
|
89
|
+
files, problemid, language, mainclass, tag, force)
|