rbx.cp 0.5.50__py3-none-any.whl → 0.5.52__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.
- rbx/box/cli.py +17 -4
- rbx/box/code.py +2 -1
- rbx/box/download.py +11 -1
- rbx/box/environment.py +11 -1
- rbx/box/header.py +73 -0
- rbx/box/main.py +4 -0
- rbx/box/naming.py +9 -0
- rbx/box/packaging/boca/packager.py +3 -1
- rbx/box/packaging/contest_main.py +6 -4
- rbx/box/packaging/main.py +15 -1
- rbx/box/packaging/moj/packager.py +6 -1
- rbx/box/packaging/polygon/packager.py +30 -7
- rbx/box/packaging/polygon/polygon_api.py +1327 -0
- rbx/box/packaging/polygon/upload.py +336 -0
- rbx/box/packaging/polygon/xml_schema.py +6 -0
- rbx/box/solutions.py +55 -25
- rbx/box/stresses.py +9 -6
- rbx/box/testcase_utils.py +15 -0
- rbx/box/ui/captured_log.py +9 -5
- rbx/box/ui/css/app.tcss +1 -1
- rbx/box/ui/run.py +19 -18
- rbx/box/unit.py +1 -1
- rbx/resources/packagers/boca/checker.sh +5 -0
- rbx/resources/templates/rbx.h +90 -0
- rbx/testing_utils.py +2 -2
- {rbx_cp-0.5.50.dist-info → rbx_cp-0.5.52.dist-info}/METADATA +3 -1
- {rbx_cp-0.5.50.dist-info → rbx_cp-0.5.52.dist-info}/RECORD +30 -26
- {rbx_cp-0.5.50.dist-info → rbx_cp-0.5.52.dist-info}/LICENSE +0 -0
- {rbx_cp-0.5.50.dist-info → rbx_cp-0.5.52.dist-info}/WHEEL +0 -0
- {rbx_cp-0.5.50.dist-info → rbx_cp-0.5.52.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1327 @@
|
|
1
|
+
"""
|
2
|
+
https://github.com/citxx/polygon-py
|
3
|
+
|
4
|
+
Manually copied because can't add direct dependency with Poetry.
|
5
|
+
"""
|
6
|
+
# flake8: noqa
|
7
|
+
|
8
|
+
import hashlib
|
9
|
+
import json
|
10
|
+
import random
|
11
|
+
import string
|
12
|
+
import time
|
13
|
+
from enum import Enum
|
14
|
+
|
15
|
+
import requests
|
16
|
+
|
17
|
+
|
18
|
+
class Polygon:
|
19
|
+
""" """
|
20
|
+
|
21
|
+
# API methods
|
22
|
+
_CONTEST_PROBLEMS = 'contest.problems'
|
23
|
+
_PROBLEM_INFO = 'problem.info'
|
24
|
+
_PROBLEM_UPDATE_INFO = 'problem.updateInfo'
|
25
|
+
_PROBLEM_UPDATE_WORKING_COPY = 'problem.updateWorkingCopy'
|
26
|
+
_PROBLEM_COMMIT_CHANGES = 'problem.commitChanges'
|
27
|
+
_PROBLEM_STATEMENTS = 'problem.statements'
|
28
|
+
_PROBLEM_SAVE_STATEMENT = 'problem.saveStatement'
|
29
|
+
_PROBLEM_STATEMENT_RESOURCES = 'problem.statementResources'
|
30
|
+
_PROBLEM_SAVE_STATEMENT_RESOURCE = 'problem.saveStatementResource'
|
31
|
+
_PROBLEMS_LIST = 'problems.list'
|
32
|
+
_PROBLEM_CREATE = 'problem.create'
|
33
|
+
_PROBLEM_CHECKER = 'problem.checker'
|
34
|
+
_PROBLEM_VALIDATOR = 'problem.validator'
|
35
|
+
_PROBLEM_INTERACTOR = 'problem.interactor'
|
36
|
+
_PROBLEM_FILES = 'problem.files'
|
37
|
+
_PROBLEM_SOLUTIONS = 'problem.solutions'
|
38
|
+
_PROBLEM_VIEW_FILE = 'problem.viewFile'
|
39
|
+
_PROBLEM_VIEW_SOLUTION = 'problem.viewSolution'
|
40
|
+
_PROBLEM_SCRIPT = 'problem.script'
|
41
|
+
_PROBLEM_TESTS = 'problem.tests'
|
42
|
+
_PROBLEM_TEST_INPUT = 'problem.testInput'
|
43
|
+
_PROBLEM_TEST_ANSWER = 'problem.testAnswer'
|
44
|
+
_PROBLEM_SET_VALIDATOR = 'problem.setValidator'
|
45
|
+
_PROBLEM_SET_CHECKER = 'problem.setChecker'
|
46
|
+
_PROBLEM_SET_INTERACTOR = 'problem.setInteractor'
|
47
|
+
_PROBLEM_SAVE_FILE = 'problem.saveFile'
|
48
|
+
_PROBLEM_SAVE_SOLUTION = 'problem.saveSolution'
|
49
|
+
_PROBLEM_EDIT_SOLUTION_EXTRA_TAGS = 'problem.editSolutionExtraTags'
|
50
|
+
_PROBLEM_SAVE_SCRIPT = 'problem.saveScript'
|
51
|
+
_PROBLEM_SAVE_TEST = 'problem.saveTest'
|
52
|
+
_PROBLEM_ENABLE_GROUPS = 'problem.enableGroups'
|
53
|
+
_PROBLEM_ENABLE_POINTS = 'problem.enablePoints'
|
54
|
+
_PROBLEM_VIEW_TEST_GROUP = 'problem.viewTestGroup'
|
55
|
+
_PROBLEM_SAVE_TEST_GROUP = 'problem.saveTestGroup'
|
56
|
+
_PROBLEM_VIEW_TAGS = 'problem.viewTags'
|
57
|
+
_PROBLEM_SAVE_TAGS = 'problem.saveTags'
|
58
|
+
_PROBLEM_VIEW_GENERAL_DESCRIPTION = 'problem.viewGeneralDescription'
|
59
|
+
_PROBLEM_SAVE_GENERAL_DESCRIPTION = 'problem.saveGeneralDescription'
|
60
|
+
_PROBLEM_VIEW_GENERAL_TUTORIAL = 'problem.viewGeneralTutorial'
|
61
|
+
_PROBLEM_SAVE_GENERAL_TUTORIAL = 'problem.saveGeneralTutorial'
|
62
|
+
|
63
|
+
def __init__(self, api_url, api_key, api_secret):
|
64
|
+
self.request_config = RequestConfig(api_url, api_key, api_secret)
|
65
|
+
|
66
|
+
def problems_list(self, show_deleted=None, id=None, name=None, owner=None):
|
67
|
+
""" """
|
68
|
+
response = self._request_ok_or_raise(
|
69
|
+
self._PROBLEMS_LIST,
|
70
|
+
args={
|
71
|
+
'showDeleted': show_deleted,
|
72
|
+
'id': id,
|
73
|
+
'name': name,
|
74
|
+
'owner': owner,
|
75
|
+
},
|
76
|
+
)
|
77
|
+
return [Problem.from_json(self, p_json) for p_json in response.result]
|
78
|
+
|
79
|
+
def problem_create(self, name):
|
80
|
+
""" """
|
81
|
+
response = self._request_ok_or_raise(
|
82
|
+
self._PROBLEM_CREATE,
|
83
|
+
args={
|
84
|
+
'name': name,
|
85
|
+
},
|
86
|
+
)
|
87
|
+
return Problem.from_json(self, response.result)
|
88
|
+
|
89
|
+
def problem_info(self, problem_id):
|
90
|
+
""" """
|
91
|
+
response = self._request_ok_or_raise(
|
92
|
+
self._PROBLEM_INFO,
|
93
|
+
args={'problemId': problem_id},
|
94
|
+
)
|
95
|
+
return ProblemInfo.from_json(response.result)
|
96
|
+
|
97
|
+
def problem_update_info(self, problem_id, problem_info):
|
98
|
+
""" """
|
99
|
+
response = self._request_ok_or_raise(
|
100
|
+
self._PROBLEM_UPDATE_INFO,
|
101
|
+
args={
|
102
|
+
'problemId': problem_id,
|
103
|
+
'inputFile': problem_info.input_file,
|
104
|
+
'outputFile': problem_info.output_file,
|
105
|
+
'interactive': problem_info.interactive, # TODO
|
106
|
+
'timeLimit': problem_info.time_limit,
|
107
|
+
'memoryLimit': problem_info.memory_limit,
|
108
|
+
},
|
109
|
+
)
|
110
|
+
return response.result
|
111
|
+
|
112
|
+
def problem_update_working_copy(self, problem_id):
|
113
|
+
""" """
|
114
|
+
response = self._request_ok_or_raise(
|
115
|
+
self._PROBLEM_UPDATE_WORKING_COPY,
|
116
|
+
args={'problemId': problem_id},
|
117
|
+
)
|
118
|
+
return response.result
|
119
|
+
|
120
|
+
def problem_commit_changes(self, problem_id, minor_changes=None, message=None):
|
121
|
+
""" """
|
122
|
+
response = self._request_ok_or_raise(
|
123
|
+
self._PROBLEM_COMMIT_CHANGES,
|
124
|
+
args={
|
125
|
+
'problemId': problem_id,
|
126
|
+
'minorChanges': minor_changes,
|
127
|
+
'message': message,
|
128
|
+
},
|
129
|
+
)
|
130
|
+
return response.result
|
131
|
+
|
132
|
+
def problem_view_tags(self, problem_id):
|
133
|
+
""" """
|
134
|
+
response = self._request_ok_or_raise(
|
135
|
+
self._PROBLEM_VIEW_TAGS,
|
136
|
+
args={'problemId': problem_id},
|
137
|
+
)
|
138
|
+
return response.result
|
139
|
+
|
140
|
+
def problem_save_tags(self, problem_id, tags):
|
141
|
+
""" """
|
142
|
+
tags_str = ','.join(tags)
|
143
|
+
response = self._request_ok_or_raise(
|
144
|
+
self._PROBLEM_SAVE_TAGS,
|
145
|
+
args={
|
146
|
+
'problemId': problem_id,
|
147
|
+
'tags': tags_str,
|
148
|
+
},
|
149
|
+
)
|
150
|
+
return response.result
|
151
|
+
|
152
|
+
def problem_view_general_description(self, problem_id):
|
153
|
+
""" """
|
154
|
+
response = self._request_ok_or_raise(
|
155
|
+
self._PROBLEM_VIEW_GENERAL_DESCRIPTION,
|
156
|
+
args={'problemId': problem_id},
|
157
|
+
)
|
158
|
+
return response.result
|
159
|
+
|
160
|
+
def problem_save_general_description(self, problem_id, description):
|
161
|
+
""" """
|
162
|
+
response = self._request_ok_or_raise(
|
163
|
+
self._PROBLEM_SAVE_GENERAL_DESCRIPTION,
|
164
|
+
args={
|
165
|
+
'problemId': problem_id,
|
166
|
+
'description': description,
|
167
|
+
},
|
168
|
+
)
|
169
|
+
return response.result
|
170
|
+
|
171
|
+
def problem_view_general_tutorial(self, problem_id):
|
172
|
+
""" """
|
173
|
+
response = self._request_ok_or_raise(
|
174
|
+
self._PROBLEM_VIEW_GENERAL_TUTORIAL,
|
175
|
+
args={'problemId': problem_id},
|
176
|
+
)
|
177
|
+
return response.result
|
178
|
+
|
179
|
+
def problem_save_general_tutorial(self, problem_id, tutorial):
|
180
|
+
""" """
|
181
|
+
response = self._request_ok_or_raise(
|
182
|
+
self._PROBLEM_SAVE_GENERAL_TUTORIAL,
|
183
|
+
args={
|
184
|
+
'problemId': problem_id,
|
185
|
+
'tutorial': tutorial,
|
186
|
+
},
|
187
|
+
)
|
188
|
+
return response.result
|
189
|
+
|
190
|
+
def problem_statements(self, problem_id):
|
191
|
+
""" """
|
192
|
+
response = self._request_ok_or_raise(
|
193
|
+
self._PROBLEM_STATEMENTS,
|
194
|
+
args={
|
195
|
+
'problemId': problem_id,
|
196
|
+
},
|
197
|
+
)
|
198
|
+
return {
|
199
|
+
lang: Statement.from_json(statement_json)
|
200
|
+
for lang, statement_json in response.result.items()
|
201
|
+
}
|
202
|
+
|
203
|
+
def problem_save_statement(self, problem_id, lang, problem_statement):
|
204
|
+
""" """
|
205
|
+
if not isinstance(problem_statement, Statement):
|
206
|
+
raise ValueError(
|
207
|
+
'Expected Statement instance for problem_statement argument, but %s found'
|
208
|
+
% type(problem_statement)
|
209
|
+
)
|
210
|
+
response = self._request_ok_or_raise(
|
211
|
+
self._PROBLEM_SAVE_STATEMENT,
|
212
|
+
args={
|
213
|
+
'problemId': problem_id,
|
214
|
+
'lang': lang,
|
215
|
+
'encoding': problem_statement.encoding,
|
216
|
+
'name': problem_statement.name,
|
217
|
+
'legend': problem_statement.legend,
|
218
|
+
'input': problem_statement.input,
|
219
|
+
'output': problem_statement.output,
|
220
|
+
'scoring': problem_statement.scoring,
|
221
|
+
'interaction': problem_statement.interaction,
|
222
|
+
'notes': problem_statement.notes,
|
223
|
+
'tutorial': problem_statement.tutorial,
|
224
|
+
},
|
225
|
+
)
|
226
|
+
return response.result
|
227
|
+
|
228
|
+
def problem_statement_resources(self, problem_id):
|
229
|
+
response = self._request_ok_or_raise(
|
230
|
+
self._PROBLEM_STATEMENT_RESOURCES,
|
231
|
+
args={
|
232
|
+
'problemId': problem_id,
|
233
|
+
},
|
234
|
+
)
|
235
|
+
return response.result
|
236
|
+
|
237
|
+
def problem_save_statement_resource(
|
238
|
+
self, problem_id, name, file, check_existing=None
|
239
|
+
):
|
240
|
+
response = self._request_ok_or_raise(
|
241
|
+
self._PROBLEM_SAVE_STATEMENT_RESOURCE,
|
242
|
+
args={
|
243
|
+
'problemId': problem_id,
|
244
|
+
'name': name,
|
245
|
+
'file': file,
|
246
|
+
'checkExisting': check_existing,
|
247
|
+
},
|
248
|
+
)
|
249
|
+
return response.result
|
250
|
+
|
251
|
+
def problem_enable_groups(self, problem_id, testset, enable):
|
252
|
+
""" """
|
253
|
+
response = self._request_ok_or_raise(
|
254
|
+
self._PROBLEM_ENABLE_GROUPS,
|
255
|
+
args={
|
256
|
+
'problemId': problem_id,
|
257
|
+
'testset': testset,
|
258
|
+
'enable': enable,
|
259
|
+
},
|
260
|
+
)
|
261
|
+
return response.result
|
262
|
+
|
263
|
+
def problem_enable_points(self, problem_id, enable):
|
264
|
+
""" """
|
265
|
+
response = self._request_ok_or_raise(
|
266
|
+
self._PROBLEM_ENABLE_POINTS,
|
267
|
+
args={
|
268
|
+
'problemId': problem_id,
|
269
|
+
'enable': enable,
|
270
|
+
},
|
271
|
+
)
|
272
|
+
return response.result
|
273
|
+
|
274
|
+
def problem_save_test(
|
275
|
+
self,
|
276
|
+
problem_id,
|
277
|
+
testset,
|
278
|
+
test_index,
|
279
|
+
test_input,
|
280
|
+
test_group=None,
|
281
|
+
test_points=None,
|
282
|
+
test_description=None,
|
283
|
+
test_use_in_statements=None,
|
284
|
+
test_input_for_statements=None,
|
285
|
+
test_output_for_statements=None,
|
286
|
+
verify_input_output_for_statements=None,
|
287
|
+
check_existing=None,
|
288
|
+
):
|
289
|
+
""" """
|
290
|
+
response = self._request_ok_or_raise(
|
291
|
+
self._PROBLEM_SAVE_TEST,
|
292
|
+
args={
|
293
|
+
'problemId': problem_id,
|
294
|
+
'testset': testset,
|
295
|
+
'testIndex': test_index,
|
296
|
+
'testInput': test_input,
|
297
|
+
'testGroup': test_group,
|
298
|
+
'testPoints': test_points,
|
299
|
+
'testDescription': test_description,
|
300
|
+
'testUseInStatements': test_use_in_statements,
|
301
|
+
'testInputForStatements': test_input_for_statements,
|
302
|
+
'testOutputForStatements': test_output_for_statements,
|
303
|
+
'verifyInputOutputForStatements': verify_input_output_for_statements,
|
304
|
+
'checkExisting': check_existing,
|
305
|
+
},
|
306
|
+
)
|
307
|
+
return response.result
|
308
|
+
|
309
|
+
def problem_solutions(self, problem_id):
|
310
|
+
response = self._request_ok_or_raise(
|
311
|
+
self._PROBLEM_SOLUTIONS,
|
312
|
+
args={
|
313
|
+
'problemId': problem_id,
|
314
|
+
},
|
315
|
+
)
|
316
|
+
return [Solution.from_json(js) for js in response.result]
|
317
|
+
|
318
|
+
def problem_files(self, problem_id):
|
319
|
+
response = self._request_ok_or_raise(
|
320
|
+
self._PROBLEM_FILES,
|
321
|
+
args={
|
322
|
+
'problemId': problem_id,
|
323
|
+
},
|
324
|
+
)
|
325
|
+
return {
|
326
|
+
type: [File.from_json(js) for js in response.result[str(type) + 'Files']]
|
327
|
+
for type in FileType
|
328
|
+
}
|
329
|
+
|
330
|
+
def problem_tests(self, problem_id, testset):
|
331
|
+
response = self._request_ok_or_raise(
|
332
|
+
self._PROBLEM_TESTS,
|
333
|
+
args={
|
334
|
+
'problemId': problem_id,
|
335
|
+
'testset': testset,
|
336
|
+
},
|
337
|
+
)
|
338
|
+
return [Test.from_json(self, problem_id, testset, js) for js in response.result]
|
339
|
+
|
340
|
+
def problem_save_test_group(
|
341
|
+
self,
|
342
|
+
problem_id,
|
343
|
+
testset,
|
344
|
+
group,
|
345
|
+
points_policy=None,
|
346
|
+
feedback_policy=None,
|
347
|
+
dependencies=None,
|
348
|
+
):
|
349
|
+
if isinstance(dependencies, list):
|
350
|
+
dependencies = ','.join(map(str, dependencies))
|
351
|
+
elif dependencies is not None:
|
352
|
+
dependencies = str(dependencies)
|
353
|
+
if points_policy is not None and not isinstance(points_policy, PointsPolicy):
|
354
|
+
raise ValueError(
|
355
|
+
'Expected PointsPolicy instance for points_policy argument, but %s found'
|
356
|
+
% type(points_policy)
|
357
|
+
)
|
358
|
+
if feedback_policy is not None and not isinstance(
|
359
|
+
feedback_policy, FeedbackPolicy
|
360
|
+
):
|
361
|
+
raise ValueError(
|
362
|
+
'Expected FeedbackPolicy instance for feedback_policy argument, but %s found'
|
363
|
+
% type(feedback_policy)
|
364
|
+
)
|
365
|
+
response = self._request_ok_or_raise(
|
366
|
+
self._PROBLEM_SAVE_TEST_GROUP,
|
367
|
+
args={
|
368
|
+
'problemId': problem_id,
|
369
|
+
'testset': testset,
|
370
|
+
'group': group,
|
371
|
+
'pointsPolicy': points_policy,
|
372
|
+
'feedbackPolicy': feedback_policy,
|
373
|
+
'dependencies': dependencies,
|
374
|
+
},
|
375
|
+
)
|
376
|
+
return response.result
|
377
|
+
|
378
|
+
def problem_view_test_group(self, testset, group):
|
379
|
+
response = self._request_ok_or_raise(
|
380
|
+
self._PROBLEM_VIEW_TEST_GROUP,
|
381
|
+
args={
|
382
|
+
'testset': testset,
|
383
|
+
'group': group,
|
384
|
+
},
|
385
|
+
)
|
386
|
+
return TestGroup.from_json(response.result)
|
387
|
+
|
388
|
+
def problem_view_file(self, problem_id, type, name):
|
389
|
+
response = self._request_raw(
|
390
|
+
self._PROBLEM_VIEW_FILE,
|
391
|
+
args={'problemId': problem_id, 'type': type, 'name': name},
|
392
|
+
)
|
393
|
+
return response
|
394
|
+
|
395
|
+
def problem_save_file(
|
396
|
+
self,
|
397
|
+
problem_id,
|
398
|
+
type,
|
399
|
+
name,
|
400
|
+
file,
|
401
|
+
source_type=None,
|
402
|
+
resource_advanced_properties=None,
|
403
|
+
):
|
404
|
+
stages = (
|
405
|
+
None
|
406
|
+
if resource_advanced_properties is None
|
407
|
+
or resource_advanced_properties.stages is None
|
408
|
+
else ';'.join(map(str, resource_advanced_properties.stages))
|
409
|
+
)
|
410
|
+
assets = (
|
411
|
+
None
|
412
|
+
if resource_advanced_properties is None
|
413
|
+
or resource_advanced_properties.assets is None
|
414
|
+
else ';'.join(map(str, resource_advanced_properties.assets))
|
415
|
+
)
|
416
|
+
response = self._request_ok_or_raise(
|
417
|
+
self._PROBLEM_SAVE_FILE,
|
418
|
+
args={
|
419
|
+
'problemId': problem_id,
|
420
|
+
'type': type,
|
421
|
+
'name': name,
|
422
|
+
'file': file,
|
423
|
+
'sourceType': source_type,
|
424
|
+
'forTypes': None
|
425
|
+
if resource_advanced_properties is None
|
426
|
+
else resource_advanced_properties.for_types,
|
427
|
+
'stages': stages,
|
428
|
+
'assets': assets,
|
429
|
+
},
|
430
|
+
)
|
431
|
+
return response.result
|
432
|
+
|
433
|
+
def problem_save_solution(
|
434
|
+
self, problem_id, name, file, source_type, tag, check_existing=None
|
435
|
+
):
|
436
|
+
response = self._request_ok_or_raise(
|
437
|
+
self._PROBLEM_SAVE_SOLUTION,
|
438
|
+
args={
|
439
|
+
'problemId': problem_id,
|
440
|
+
'name': name,
|
441
|
+
'file': file,
|
442
|
+
'sourceType': source_type,
|
443
|
+
'tag': tag,
|
444
|
+
'checkExisting': check_existing,
|
445
|
+
},
|
446
|
+
)
|
447
|
+
return response.result
|
448
|
+
|
449
|
+
def problem_checker(self, problem_id):
|
450
|
+
response = self._request_ok_or_raise(
|
451
|
+
self._PROBLEM_CHECKER,
|
452
|
+
args={
|
453
|
+
'problemId': problem_id,
|
454
|
+
},
|
455
|
+
)
|
456
|
+
return response.result
|
457
|
+
|
458
|
+
def problem_set_checker(self, problem_id, checker):
|
459
|
+
response = self._request_ok_or_raise(
|
460
|
+
self._PROBLEM_SET_CHECKER,
|
461
|
+
args={
|
462
|
+
'problemId': problem_id,
|
463
|
+
'checker': checker,
|
464
|
+
},
|
465
|
+
)
|
466
|
+
return response.result
|
467
|
+
|
468
|
+
def problem_validator(self, problem_id):
|
469
|
+
response = self._request_ok_or_raise(
|
470
|
+
self._PROBLEM_VALIDATOR,
|
471
|
+
args={
|
472
|
+
'problemId': problem_id,
|
473
|
+
},
|
474
|
+
)
|
475
|
+
return response.result
|
476
|
+
|
477
|
+
def problem_set_validator(self, problem_id, validator):
|
478
|
+
response = self._request_ok_or_raise(
|
479
|
+
self._PROBLEM_SET_VALIDATOR,
|
480
|
+
args={
|
481
|
+
'problemId': problem_id,
|
482
|
+
'validator': validator,
|
483
|
+
},
|
484
|
+
)
|
485
|
+
return response.result
|
486
|
+
|
487
|
+
def problem_interactor(self, problem_id):
|
488
|
+
response = self._request_ok_or_raise(
|
489
|
+
self._PROBLEM_INTERACTOR,
|
490
|
+
args={
|
491
|
+
'problemId': problem_id,
|
492
|
+
},
|
493
|
+
)
|
494
|
+
return response.result
|
495
|
+
|
496
|
+
def problem_set_interactor(self, problem_id, interactor):
|
497
|
+
response = self._request_ok_or_raise(
|
498
|
+
self._PROBLEM_SET_INTERACTOR,
|
499
|
+
args={
|
500
|
+
'problemId': problem_id,
|
501
|
+
'interactor': interactor,
|
502
|
+
},
|
503
|
+
)
|
504
|
+
return response.result
|
505
|
+
|
506
|
+
def contest_problems(self, contest_id):
|
507
|
+
""" """
|
508
|
+
response = self._request_ok_or_raise(
|
509
|
+
self._CONTEST_PROBLEMS,
|
510
|
+
args={'contestId': contest_id},
|
511
|
+
)
|
512
|
+
return {
|
513
|
+
name: Problem.from_json(self, p_json)
|
514
|
+
for name, p_json in response.result.items()
|
515
|
+
}
|
516
|
+
|
517
|
+
def _request_ok_or_raise(self, method_name, args=None):
|
518
|
+
response = self._request(method_name, args=args)
|
519
|
+
if response.status != Response.STATUS_OK:
|
520
|
+
raise PolygonRequestFailedException(response.comment)
|
521
|
+
return response
|
522
|
+
|
523
|
+
def _request(self, method_name, args=None):
|
524
|
+
request = Request(self.request_config, method_name, args)
|
525
|
+
return request.issue()
|
526
|
+
|
527
|
+
def _request_raw(self, method_name, args=None):
|
528
|
+
request = Request(self.request_config, method_name, args)
|
529
|
+
return request.issue_raw()
|
530
|
+
|
531
|
+
|
532
|
+
class Problem:
|
533
|
+
"""
|
534
|
+
Object representing Polygon problem
|
535
|
+
"""
|
536
|
+
|
537
|
+
# JSON field names
|
538
|
+
_ID_FIELD = 'id'
|
539
|
+
_OWNER_FIELD = 'owner'
|
540
|
+
_NAME_FIELD = 'name'
|
541
|
+
_DELETED_FIELD = 'deleted'
|
542
|
+
_FAVORITE_FIELD = 'favourite'
|
543
|
+
_ACCESS_TYPE_FIELD = 'accessType'
|
544
|
+
_REVISION_FIELD = 'revision'
|
545
|
+
_LATEST_PACKAGE_FIELD = 'latestPackage'
|
546
|
+
_MODIFIED_FIELD = 'modified'
|
547
|
+
|
548
|
+
@classmethod
|
549
|
+
def from_json(cls, polygon, problem_json):
|
550
|
+
return cls(
|
551
|
+
polygon=polygon,
|
552
|
+
problem_id=problem_json.get(Problem._ID_FIELD),
|
553
|
+
owner=problem_json.get(Problem._OWNER_FIELD),
|
554
|
+
name=problem_json.get(Problem._NAME_FIELD),
|
555
|
+
deleted=problem_json.get(Problem._DELETED_FIELD),
|
556
|
+
favorite=problem_json.get(Problem._FAVORITE_FIELD),
|
557
|
+
access_type=problem_json.get(Problem._ACCESS_TYPE_FIELD),
|
558
|
+
revision=problem_json.get(Problem._REVISION_FIELD),
|
559
|
+
latest_package=problem_json.get(Problem._LATEST_PACKAGE_FIELD),
|
560
|
+
modified=problem_json.get(Problem._MODIFIED_FIELD),
|
561
|
+
)
|
562
|
+
|
563
|
+
def __init__(
|
564
|
+
self,
|
565
|
+
polygon,
|
566
|
+
problem_id,
|
567
|
+
owner,
|
568
|
+
name,
|
569
|
+
deleted,
|
570
|
+
favorite,
|
571
|
+
access_type,
|
572
|
+
revision,
|
573
|
+
latest_package,
|
574
|
+
modified,
|
575
|
+
):
|
576
|
+
self._polygon = polygon
|
577
|
+
|
578
|
+
self.id = problem_id
|
579
|
+
self.owner = owner
|
580
|
+
self.name = name
|
581
|
+
self.deleted = deleted
|
582
|
+
self.favorite = favorite
|
583
|
+
self.access_type = access_type
|
584
|
+
self.revision = revision
|
585
|
+
self.latest_package = latest_package
|
586
|
+
self.modified = modified
|
587
|
+
|
588
|
+
def __str__(self):
|
589
|
+
return '{}:{}'.format(self.name, self.id)
|
590
|
+
|
591
|
+
def info(self):
|
592
|
+
return self._polygon.problem_info(self.id)
|
593
|
+
|
594
|
+
def update_info(self, problem_info):
|
595
|
+
return self._polygon.problem_update_info(self.id, problem_info)
|
596
|
+
|
597
|
+
def update_working_copy(self):
|
598
|
+
return self._polygon.problem_update_working_copy(self.id)
|
599
|
+
|
600
|
+
def commit_changes(self, minor_changes=None, message=None):
|
601
|
+
return self._polygon.problem_commit_changes(self.id, minor_changes, message)
|
602
|
+
|
603
|
+
def tags(self):
|
604
|
+
return self._polygon.problem_view_tags(self.id)
|
605
|
+
|
606
|
+
def save_tags(self, tags):
|
607
|
+
return self._polygon.problem_save_tags(self.id, tags)
|
608
|
+
|
609
|
+
def general_description(self):
|
610
|
+
return self._polygon.problem_view_general_description(self.id)
|
611
|
+
|
612
|
+
def save_general_description(self, description):
|
613
|
+
return self._polygon.problem_save_general_description(self.id, description)
|
614
|
+
|
615
|
+
def general_tutorial(self):
|
616
|
+
return self._polygon.problem_view_general_tutorial(self.id)
|
617
|
+
|
618
|
+
def save_general_tutorial(self, tutorial):
|
619
|
+
return self._polygon.problem_save_general_tutorial(self.id, tutorial)
|
620
|
+
|
621
|
+
def statements(self):
|
622
|
+
return self._polygon.problem_statements(self.id)
|
623
|
+
|
624
|
+
def save_statement(self, lang, problem_statement):
|
625
|
+
return self._polygon.problem_save_statement(self.id, lang, problem_statement)
|
626
|
+
|
627
|
+
def statement_resources(self):
|
628
|
+
return self._polygon.problem_statement_resources(self.id)
|
629
|
+
|
630
|
+
def save_statement_resource(self, name, file, check_existing=None):
|
631
|
+
return self._polygon.problem_save_statement_resource(
|
632
|
+
self.id, name, file, check_existing
|
633
|
+
)
|
634
|
+
|
635
|
+
def enable_groups(self, testset, enable):
|
636
|
+
return self._polygon.problem_enable_groups(self.id, testset, enable)
|
637
|
+
|
638
|
+
def enable_points(self, enable):
|
639
|
+
return self._polygon.problem_enable_points(self.id, enable)
|
640
|
+
|
641
|
+
def save_test(
|
642
|
+
self,
|
643
|
+
testset,
|
644
|
+
test_index,
|
645
|
+
test_input,
|
646
|
+
test_group=None,
|
647
|
+
test_points=None,
|
648
|
+
test_description=None,
|
649
|
+
test_use_in_statements=None,
|
650
|
+
test_input_for_statements=None,
|
651
|
+
test_output_for_statements=None,
|
652
|
+
verify_input_output_for_statements=None,
|
653
|
+
check_existing=None,
|
654
|
+
):
|
655
|
+
return self._polygon.problem_save_test(
|
656
|
+
self.id,
|
657
|
+
testset,
|
658
|
+
test_index,
|
659
|
+
test_input,
|
660
|
+
test_group,
|
661
|
+
test_points,
|
662
|
+
test_description,
|
663
|
+
test_use_in_statements,
|
664
|
+
test_input_for_statements,
|
665
|
+
test_output_for_statements,
|
666
|
+
verify_input_output_for_statements,
|
667
|
+
check_existing,
|
668
|
+
)
|
669
|
+
|
670
|
+
def tests(self, testset):
|
671
|
+
return self._polygon.problem_tests(self.id, testset)
|
672
|
+
|
673
|
+
def save_test_group(
|
674
|
+
self,
|
675
|
+
testset,
|
676
|
+
group,
|
677
|
+
points_policy=None,
|
678
|
+
feedback_policy=None,
|
679
|
+
dependencies=None,
|
680
|
+
):
|
681
|
+
return self._polygon.problem_save_test_group(
|
682
|
+
self.id, testset, group, points_policy, feedback_policy, dependencies
|
683
|
+
)
|
684
|
+
|
685
|
+
def view_test_group(self, testset, group):
|
686
|
+
return self._polygon.problem_view_test_group(testset, group)
|
687
|
+
|
688
|
+
def view_file(self, type, name):
|
689
|
+
return self._polygon.problem_view_file(self.id, type, name)
|
690
|
+
|
691
|
+
def save_file(
|
692
|
+
self, type, name, file, source_type=None, resource_advanced_properties=None
|
693
|
+
):
|
694
|
+
return self._polygon.problem_save_file(
|
695
|
+
self.id, type, name, file, source_type, resource_advanced_properties
|
696
|
+
)
|
697
|
+
|
698
|
+
def save_solution(self, name, file, source_type, tag, check_existing=None):
|
699
|
+
return self._polygon.problem_save_solution(
|
700
|
+
self.id, name, file, source_type, tag, check_existing
|
701
|
+
)
|
702
|
+
|
703
|
+
def checker(self):
|
704
|
+
return self._polygon.problem_checker(self.id)
|
705
|
+
|
706
|
+
def set_checker(self, checker):
|
707
|
+
return self._polygon.problem_set_checker(self.id, checker)
|
708
|
+
|
709
|
+
def validator(self):
|
710
|
+
return self._polygon.problem_validator(self.id)
|
711
|
+
|
712
|
+
def set_validator(self, validator):
|
713
|
+
return self._polygon.problem_set_validator(self.id, validator)
|
714
|
+
|
715
|
+
def interactor(self):
|
716
|
+
return self._polygon.problem_interactor(self.id)
|
717
|
+
|
718
|
+
def set_interactor(self, interactor):
|
719
|
+
return self._polygon.problem_set_interactor(self.id, interactor)
|
720
|
+
|
721
|
+
def solutions(self):
|
722
|
+
return self._polygon.problem_solutions(self.id)
|
723
|
+
|
724
|
+
def files(self):
|
725
|
+
return self._polygon.problem_files(self.id)
|
726
|
+
|
727
|
+
def files_resource(self):
|
728
|
+
return self.files()[FileType.RESOURCE]
|
729
|
+
|
730
|
+
def files_source(self):
|
731
|
+
return self.files()[FileType.SOURCE]
|
732
|
+
|
733
|
+
def files_aux(self):
|
734
|
+
return self.files()[FileType.AUX]
|
735
|
+
|
736
|
+
|
737
|
+
class ProblemInfo:
|
738
|
+
"""
|
739
|
+
Object representing Polygon problem info
|
740
|
+
"""
|
741
|
+
|
742
|
+
# JSON field names
|
743
|
+
_INPUT_FILE = 'inputFile'
|
744
|
+
_OUTPUT_FILE = 'outputFile'
|
745
|
+
_INTERACTIVE = 'interactive'
|
746
|
+
_TIME_LIMIT = 'timeLimit'
|
747
|
+
_MEMORY_LIMIT = 'memoryLimit'
|
748
|
+
|
749
|
+
@classmethod
|
750
|
+
def from_json(cls, problem_info_json):
|
751
|
+
return cls(
|
752
|
+
input_file=problem_info_json[ProblemInfo._INPUT_FILE],
|
753
|
+
output_file=problem_info_json[ProblemInfo._OUTPUT_FILE],
|
754
|
+
interactive=problem_info_json[ProblemInfo._INTERACTIVE],
|
755
|
+
time_limit=problem_info_json[ProblemInfo._TIME_LIMIT],
|
756
|
+
memory_limit=problem_info_json[ProblemInfo._MEMORY_LIMIT],
|
757
|
+
)
|
758
|
+
|
759
|
+
def __init__(
|
760
|
+
self,
|
761
|
+
input_file=None,
|
762
|
+
output_file=None,
|
763
|
+
interactive=None,
|
764
|
+
time_limit=None,
|
765
|
+
memory_limit=None,
|
766
|
+
):
|
767
|
+
self.input_file = input_file
|
768
|
+
self.output_file = output_file
|
769
|
+
self.interactive = interactive
|
770
|
+
self.time_limit = time_limit
|
771
|
+
self.memory_limit = memory_limit
|
772
|
+
|
773
|
+
|
774
|
+
class Test:
|
775
|
+
_INDEX = 'index'
|
776
|
+
_MANUAL = 'manual'
|
777
|
+
_INPUT = 'input'
|
778
|
+
_DESCRIPTION = 'description'
|
779
|
+
_USE_IN_STATEMENTS = 'useInStatements'
|
780
|
+
_SCRIPT_LINE = 'scriptLine'
|
781
|
+
_GROUP = 'group'
|
782
|
+
_POINTS = 'points'
|
783
|
+
_INPUT_FOR_STATEMENTS = 'inputForStatements'
|
784
|
+
_OUTPUT_FOR_STATEMENTS = 'outputForStatements'
|
785
|
+
_VERIFY_INPUT_OUTPUT_FOR_STATEMENTS = 'verifyInputOutputForStatements'
|
786
|
+
|
787
|
+
@classmethod
|
788
|
+
def from_json(cls, polygon, problem_id, testset, test_json):
|
789
|
+
if test_json['manual']:
|
790
|
+
return ManualTest.from_json(polygon, problem_id, testset, test_json)
|
791
|
+
else:
|
792
|
+
return GeneratedTest.from_json(polygon, problem_id, testset, test_json)
|
793
|
+
|
794
|
+
def __init__(
|
795
|
+
self,
|
796
|
+
polygon,
|
797
|
+
problem_id,
|
798
|
+
testset,
|
799
|
+
index,
|
800
|
+
group,
|
801
|
+
points,
|
802
|
+
description,
|
803
|
+
use_in_statements,
|
804
|
+
input_for_statements,
|
805
|
+
output_for_statements,
|
806
|
+
verify_input_output_for_statements,
|
807
|
+
):
|
808
|
+
self._polygon = polygon
|
809
|
+
self._problem_id = problem_id
|
810
|
+
self.testset = testset
|
811
|
+
self.index = index
|
812
|
+
self.group = group
|
813
|
+
self.points = points
|
814
|
+
self.description = description
|
815
|
+
self.use_in_statements = use_in_statements
|
816
|
+
self.input_for_statements = input_for_statements
|
817
|
+
self.output_for_statements = output_for_statements
|
818
|
+
self.verify_input_output_for_statements = verify_input_output_for_statements
|
819
|
+
|
820
|
+
|
821
|
+
class ManualTest(Test):
|
822
|
+
@classmethod
|
823
|
+
def from_json(cls, polygon, problem_id, testset, test_json):
|
824
|
+
verify = test_json.get(Test._VERIFY_INPUT_OUTPUT_FOR_STATEMENTS, None)
|
825
|
+
return cls(
|
826
|
+
polygon=polygon,
|
827
|
+
problem_id=problem_id,
|
828
|
+
testset=testset,
|
829
|
+
index=int(test_json[Test._INDEX]),
|
830
|
+
input=test_json[Test._INPUT],
|
831
|
+
group=test_json.get(Test._GROUP, ''),
|
832
|
+
points=int(test_json.get(Test._POINTS, '0')),
|
833
|
+
description=test_json.get(Test._DESCRIPTION, None),
|
834
|
+
use_in_statements=test_json[Test._USE_IN_STATEMENTS],
|
835
|
+
input_for_statements=test_json.get(Test._INPUT_FOR_STATEMENTS, None),
|
836
|
+
output_for_statements=test_json.get(Test._OUTPUT_FOR_STATEMENTS, None),
|
837
|
+
verify_input_output_for_statements=None
|
838
|
+
if verify is None
|
839
|
+
else (verify == 'true'),
|
840
|
+
)
|
841
|
+
|
842
|
+
def __init__(
|
843
|
+
self,
|
844
|
+
polygon,
|
845
|
+
problem_id,
|
846
|
+
testset,
|
847
|
+
index,
|
848
|
+
input,
|
849
|
+
group=None,
|
850
|
+
points=None,
|
851
|
+
description=None,
|
852
|
+
use_in_statements=None,
|
853
|
+
input_for_statements=None,
|
854
|
+
output_for_statements=None,
|
855
|
+
verify_input_output_for_statements=None,
|
856
|
+
):
|
857
|
+
super().__init__(
|
858
|
+
polygon,
|
859
|
+
problem_id,
|
860
|
+
testset,
|
861
|
+
index,
|
862
|
+
group,
|
863
|
+
points,
|
864
|
+
description,
|
865
|
+
use_in_statements,
|
866
|
+
input_for_statements,
|
867
|
+
output_for_statements,
|
868
|
+
verify_input_output_for_statements,
|
869
|
+
)
|
870
|
+
self.input = input
|
871
|
+
|
872
|
+
def save(self):
|
873
|
+
return self._polygon.problem_save_test(
|
874
|
+
self._problem_id,
|
875
|
+
self.testset,
|
876
|
+
self.index,
|
877
|
+
self.input,
|
878
|
+
self.group,
|
879
|
+
self.points,
|
880
|
+
self.description,
|
881
|
+
self.use_in_statements,
|
882
|
+
self.input_for_statements,
|
883
|
+
self.output_for_statements,
|
884
|
+
self.verify_input_output_for_statements,
|
885
|
+
)
|
886
|
+
|
887
|
+
|
888
|
+
class GeneratedTest(Test):
|
889
|
+
@classmethod
|
890
|
+
def from_json(cls, polygon, problem_id, testset, test_json):
|
891
|
+
verify = test_json.get(Test._VERIFY_INPUT_OUTPUT_FOR_STATEMENTS, None)
|
892
|
+
return cls(
|
893
|
+
polygon=polygon,
|
894
|
+
problem_id=problem_id,
|
895
|
+
testset=testset,
|
896
|
+
index=int(test_json[Test._INDEX]),
|
897
|
+
group=test_json.get(Test._GROUP, ''),
|
898
|
+
points=int(test_json.get(Test._POINTS, '0')),
|
899
|
+
description=test_json.get(Test._DESCRIPTION, None),
|
900
|
+
use_in_statements=test_json[Test._USE_IN_STATEMENTS],
|
901
|
+
script_line=test_json[Test._SCRIPT_LINE],
|
902
|
+
input_for_statements=test_json.get(Test._INPUT_FOR_STATEMENTS, None),
|
903
|
+
output_for_statements=test_json.get(Test._OUTPUT_FOR_STATEMENTS, None),
|
904
|
+
verify_input_output_for_statements=None
|
905
|
+
if verify is None
|
906
|
+
else (verify == 'true'),
|
907
|
+
)
|
908
|
+
|
909
|
+
def __init__(
|
910
|
+
self,
|
911
|
+
polygon,
|
912
|
+
problem_id,
|
913
|
+
testset,
|
914
|
+
index,
|
915
|
+
group,
|
916
|
+
points,
|
917
|
+
description,
|
918
|
+
use_in_statements,
|
919
|
+
script_line,
|
920
|
+
input_for_statements,
|
921
|
+
output_for_statements,
|
922
|
+
verify_input_output_for_statements,
|
923
|
+
):
|
924
|
+
super().__init__(
|
925
|
+
polygon,
|
926
|
+
problem_id,
|
927
|
+
testset,
|
928
|
+
index,
|
929
|
+
group,
|
930
|
+
points,
|
931
|
+
description,
|
932
|
+
use_in_statements,
|
933
|
+
input_for_statements,
|
934
|
+
output_for_statements,
|
935
|
+
verify_input_output_for_statements,
|
936
|
+
)
|
937
|
+
self.script_line = script_line
|
938
|
+
|
939
|
+
|
940
|
+
class TestGroup:
|
941
|
+
""" """
|
942
|
+
|
943
|
+
_NAME = 'name'
|
944
|
+
_POINTS_POLICY = 'pointsPolicy'
|
945
|
+
_FEEDBACK_POLICY = 'feedbackPolicy'
|
946
|
+
_DEPENDENCIES = 'dependencies'
|
947
|
+
|
948
|
+
@classmethod
|
949
|
+
def from_json(cls, test_group_json):
|
950
|
+
return cls(
|
951
|
+
name=test_group_json[TestGroup._NAME],
|
952
|
+
points_policy=PointsPolicy[test_group_json[TestGroup._POINTS_POLICY]],
|
953
|
+
feedback_policy=FeedbackPolicy[test_group_json[TestGroup._FEEDBACK_POLICY]],
|
954
|
+
dependencies=test_group_json[TestGroup._DEPENDENCIES],
|
955
|
+
)
|
956
|
+
|
957
|
+
def __init__(
|
958
|
+
self, name, points_policy=None, feedback_policy=None, dependencies=None
|
959
|
+
):
|
960
|
+
self.name = name
|
961
|
+
if points_policy is not None and not isinstance(points_policy, PointsPolicy):
|
962
|
+
raise TypeError(
|
963
|
+
'Expected PointsPolicy instance for points_policy argument, but %s found'
|
964
|
+
% type(points_policy)
|
965
|
+
)
|
966
|
+
if feedback_policy is not None and not isinstance(
|
967
|
+
feedback_policy, FeedbackPolicy
|
968
|
+
):
|
969
|
+
raise TypeError(
|
970
|
+
'Expected FeedbackPolicy instance for feedback_policy argument, but %s found'
|
971
|
+
% type(feedback_policy)
|
972
|
+
)
|
973
|
+
self.points_policy = points_policy
|
974
|
+
self.feedback_policy = feedback_policy
|
975
|
+
self.dependencies = dependencies
|
976
|
+
|
977
|
+
|
978
|
+
class Statement:
|
979
|
+
"""
|
980
|
+
Object: representing Polygon problem statement
|
981
|
+
"""
|
982
|
+
|
983
|
+
_ENCODING = 'encoding'
|
984
|
+
_NAME = 'name'
|
985
|
+
_LEGEND = 'legend'
|
986
|
+
_INPUT = 'input'
|
987
|
+
_OUTPUT = 'output'
|
988
|
+
_SCORING = 'scoring'
|
989
|
+
_INTERACTION = 'interaction'
|
990
|
+
_NOTES = 'notes'
|
991
|
+
_TUTORIAL = 'tutorial'
|
992
|
+
|
993
|
+
@classmethod
|
994
|
+
def from_json(cls, statement_json):
|
995
|
+
return cls(
|
996
|
+
encoding=statement_json[Statement._ENCODING],
|
997
|
+
name=statement_json[Statement._NAME],
|
998
|
+
legend=statement_json[Statement._LEGEND],
|
999
|
+
input=statement_json[Statement._INPUT],
|
1000
|
+
output=statement_json[Statement._OUTPUT],
|
1001
|
+
scoring=statement_json.get(Statement._SCORING, None),
|
1002
|
+
interaction=statement_json.get(Statement._INTERACTION, None),
|
1003
|
+
notes=statement_json[Statement._NOTES],
|
1004
|
+
tutorial=statement_json[Statement._TUTORIAL],
|
1005
|
+
)
|
1006
|
+
|
1007
|
+
def __init__(
|
1008
|
+
self,
|
1009
|
+
encoding=None,
|
1010
|
+
name=None,
|
1011
|
+
legend=None,
|
1012
|
+
input=None,
|
1013
|
+
output=None,
|
1014
|
+
scoring=None,
|
1015
|
+
interaction=None,
|
1016
|
+
notes=None,
|
1017
|
+
tutorial=None,
|
1018
|
+
):
|
1019
|
+
self.encoding = encoding
|
1020
|
+
self.name = name
|
1021
|
+
self.legend = legend
|
1022
|
+
self.input = input
|
1023
|
+
self.output = output
|
1024
|
+
self.scoring = scoring
|
1025
|
+
self.interaction = interaction
|
1026
|
+
self.notes = notes
|
1027
|
+
self.tutorial = tutorial
|
1028
|
+
|
1029
|
+
|
1030
|
+
class Solution:
|
1031
|
+
"""
|
1032
|
+
Object: representing Polygon problem solution
|
1033
|
+
"""
|
1034
|
+
|
1035
|
+
_NAME = 'name'
|
1036
|
+
_MODIFICATION_TIME_SECONDS = 'modificationTimeSeconds'
|
1037
|
+
_LENGTH = 'length'
|
1038
|
+
_SOURCE_TYPE = 'sourceType'
|
1039
|
+
_TAG = 'tag'
|
1040
|
+
|
1041
|
+
@classmethod
|
1042
|
+
def from_json(cls, solution_json):
|
1043
|
+
return cls(
|
1044
|
+
name=solution_json[Solution._NAME],
|
1045
|
+
modification_time_seconds=solution_json[
|
1046
|
+
Solution._MODIFICATION_TIME_SECONDS
|
1047
|
+
],
|
1048
|
+
length=solution_json[Solution._LENGTH],
|
1049
|
+
source_type=solution_json[Solution._SOURCE_TYPE],
|
1050
|
+
tag=SolutionTag[solution_json[Solution._TAG]],
|
1051
|
+
)
|
1052
|
+
|
1053
|
+
def __init__(self, name, modification_time_seconds, length, source_type, tag):
|
1054
|
+
self.name = name
|
1055
|
+
self.modification_time_seconds = modification_time_seconds
|
1056
|
+
self.length = length
|
1057
|
+
self.source_type = source_type
|
1058
|
+
self.tag = tag
|
1059
|
+
|
1060
|
+
|
1061
|
+
class File:
|
1062
|
+
"""
|
1063
|
+
Object: representing Polygon file (resource, source, or aux, not solutions)
|
1064
|
+
"""
|
1065
|
+
|
1066
|
+
_NAME = 'name'
|
1067
|
+
_MODIFICATION_TIME_SECONDS = 'modificationTimeSeconds'
|
1068
|
+
_LENGTH = 'length'
|
1069
|
+
_SOURCE_TYPE = 'sourceType'
|
1070
|
+
_RESOURCE_ADVANCED_PROPERTIES = 'resourceAdvancedProperties'
|
1071
|
+
|
1072
|
+
@classmethod
|
1073
|
+
def from_json(cls, file_json):
|
1074
|
+
return cls(
|
1075
|
+
name=file_json[File._NAME],
|
1076
|
+
modification_time_seconds=file_json[File._MODIFICATION_TIME_SECONDS],
|
1077
|
+
length=file_json[File._LENGTH],
|
1078
|
+
source_type=file_json.get(File._SOURCE_TYPE, None),
|
1079
|
+
resource_advanced_properties=ResourceAdvancedProperties.from_json(
|
1080
|
+
file_json[File._RESOURCE_ADVANCED_PROPERTIES]
|
1081
|
+
)
|
1082
|
+
if File._RESOURCE_ADVANCED_PROPERTIES in file_json.keys()
|
1083
|
+
else None,
|
1084
|
+
)
|
1085
|
+
|
1086
|
+
def __init__(
|
1087
|
+
self,
|
1088
|
+
name,
|
1089
|
+
modification_time_seconds,
|
1090
|
+
length,
|
1091
|
+
source_type,
|
1092
|
+
resource_advanced_properties,
|
1093
|
+
):
|
1094
|
+
self.name = name
|
1095
|
+
self.modification_time_seconds = modification_time_seconds
|
1096
|
+
self.length = length
|
1097
|
+
self.source_type = source_type
|
1098
|
+
self.resource_advanced_properties = resource_advanced_properties
|
1099
|
+
|
1100
|
+
|
1101
|
+
class ResourceAdvancedProperties:
|
1102
|
+
""" """
|
1103
|
+
|
1104
|
+
_FOR_TYPES = 'forTypes'
|
1105
|
+
_MAIN = 'main'
|
1106
|
+
_STAGES = 'stages'
|
1107
|
+
_ASSETS = 'assets'
|
1108
|
+
|
1109
|
+
@classmethod
|
1110
|
+
def from_json(cls, resource_advanced_properties):
|
1111
|
+
return cls(
|
1112
|
+
for_types=resource_advanced_properties[
|
1113
|
+
ResourceAdvancedProperties._FOR_TYPES
|
1114
|
+
],
|
1115
|
+
main=resource_advanced_properties[ResourceAdvancedProperties._MAIN],
|
1116
|
+
stages=[
|
1117
|
+
Stage[stage]
|
1118
|
+
for stage in resource_advanced_properties[
|
1119
|
+
ResourceAdvancedProperties._STAGES
|
1120
|
+
]
|
1121
|
+
],
|
1122
|
+
assets=[
|
1123
|
+
Asset[asset]
|
1124
|
+
for asset in resource_advanced_properties[
|
1125
|
+
ResourceAdvancedProperties._ASSETS
|
1126
|
+
]
|
1127
|
+
],
|
1128
|
+
)
|
1129
|
+
|
1130
|
+
def __init__(self, for_types=None, main=None, stages=None, assets=None):
|
1131
|
+
self.for_types = for_types
|
1132
|
+
self.main = main
|
1133
|
+
self.stages = stages
|
1134
|
+
self.assets = assets
|
1135
|
+
|
1136
|
+
|
1137
|
+
ResourceAdvancedProperties.DELETE = ResourceAdvancedProperties(for_types='')
|
1138
|
+
|
1139
|
+
|
1140
|
+
class Request:
|
1141
|
+
"""
|
1142
|
+
Request to Polygon API.
|
1143
|
+
TODO: update
|
1144
|
+
Usage example:
|
1145
|
+
>>> request = Request(Request.CONTEST_PROBLEMS,
|
1146
|
+
... args={'contestId': contest_id})
|
1147
|
+
>>> response = request.issue()
|
1148
|
+
>>> problems = response["result"]
|
1149
|
+
"""
|
1150
|
+
|
1151
|
+
@staticmethod
|
1152
|
+
def _encoded_args(args):
|
1153
|
+
return [
|
1154
|
+
(Request._value_to_utf8_bytes(key), Request._value_to_utf8_bytes(value))
|
1155
|
+
for key, value in args
|
1156
|
+
]
|
1157
|
+
|
1158
|
+
@staticmethod
|
1159
|
+
def _value_to_utf8_bytes(value):
|
1160
|
+
if isinstance(value, bytes):
|
1161
|
+
return value
|
1162
|
+
if isinstance(value, bytearray):
|
1163
|
+
return bytes(value)
|
1164
|
+
return str(value).encode('utf-8')
|
1165
|
+
|
1166
|
+
def __init__(self, config, method_name, args=None):
|
1167
|
+
if args is None:
|
1168
|
+
args = {}
|
1169
|
+
args = {key: value for key, value in args.items() if value is not None}
|
1170
|
+
for key in args:
|
1171
|
+
if isinstance(args[key], bool):
|
1172
|
+
args[key] = 'true' if args[key] else 'false'
|
1173
|
+
self.args = []
|
1174
|
+
for key, value in args.items():
|
1175
|
+
if isinstance(value, list):
|
1176
|
+
for it in value:
|
1177
|
+
self.args.append((key, it))
|
1178
|
+
else:
|
1179
|
+
self.args.append((key, value))
|
1180
|
+
self.config = config
|
1181
|
+
self.method_name = method_name
|
1182
|
+
|
1183
|
+
def issue(self):
|
1184
|
+
"""Issues request and returns parsed JSON response"""
|
1185
|
+
|
1186
|
+
return Response(json.loads(self.issue_raw()))
|
1187
|
+
|
1188
|
+
def issue_raw(self):
|
1189
|
+
"""Issues request and returns raw response (useful e.g. for files)"""
|
1190
|
+
|
1191
|
+
args = list(self.args)
|
1192
|
+
args.append(('apiKey', self.config.api_key))
|
1193
|
+
args.append(('time', str(int(time.time()))))
|
1194
|
+
args = Request._encoded_args(args)
|
1195
|
+
args.append(
|
1196
|
+
(
|
1197
|
+
b'apiSig',
|
1198
|
+
self.get_api_signature(
|
1199
|
+
args, Request._value_to_utf8_bytes(self.config.api_secret)
|
1200
|
+
),
|
1201
|
+
)
|
1202
|
+
)
|
1203
|
+
response = requests.post(self.config.api_url + self.method_name, files=args)
|
1204
|
+
return response.text
|
1205
|
+
|
1206
|
+
def get_api_signature(self, args, api_secret):
|
1207
|
+
rand_bit = Request._value_to_utf8_bytes(
|
1208
|
+
''.join(
|
1209
|
+
random.choice(string.ascii_lowercase + string.digits) for _ in range(6)
|
1210
|
+
)
|
1211
|
+
)
|
1212
|
+
|
1213
|
+
arg_tuples = list(sorted(args))
|
1214
|
+
args_bit = b'&'.join(key + b'=' + value for key, value in arg_tuples)
|
1215
|
+
api_signature_string = (
|
1216
|
+
rand_bit
|
1217
|
+
+ b'/'
|
1218
|
+
+ self.method_name.encode('utf-8')
|
1219
|
+
+ b'?'
|
1220
|
+
+ args_bit
|
1221
|
+
+ b'#'
|
1222
|
+
+ api_secret
|
1223
|
+
)
|
1224
|
+
api_signature = rand_bit + Request._value_to_utf8_bytes(
|
1225
|
+
hashlib.sha512(api_signature_string).hexdigest()
|
1226
|
+
)
|
1227
|
+
return api_signature
|
1228
|
+
|
1229
|
+
|
1230
|
+
class Response:
|
1231
|
+
""" """
|
1232
|
+
|
1233
|
+
# JSON field names
|
1234
|
+
FIELD_STATUS = 'status'
|
1235
|
+
FIELD_COMMENT = 'comment'
|
1236
|
+
FIELD_RESULT = 'result'
|
1237
|
+
|
1238
|
+
# Status values
|
1239
|
+
STATUS_OK = 'OK'
|
1240
|
+
STATUS_FAILED = 'FAILED'
|
1241
|
+
STATUS_UNKNOWN = 'UNKNOWN'
|
1242
|
+
|
1243
|
+
def __init__(self, response_json):
|
1244
|
+
self.status = response_json.get(Response.FIELD_STATUS)
|
1245
|
+
if self.status not in [Response.STATUS_OK, Response.STATUS_FAILED]:
|
1246
|
+
self.status = Response.STATUS_UNKNOWN
|
1247
|
+
|
1248
|
+
self.comment = response_json.get(Response.FIELD_COMMENT, '')
|
1249
|
+
self.result = response_json.get(Response.FIELD_RESULT)
|
1250
|
+
|
1251
|
+
|
1252
|
+
class RequestConfig:
|
1253
|
+
def __init__(self, api_url, api_key, api_secret):
|
1254
|
+
if not api_url.endswith('/'):
|
1255
|
+
api_url += '/'
|
1256
|
+
self.api_url = api_url
|
1257
|
+
self.api_key = api_key
|
1258
|
+
self.api_secret = api_secret
|
1259
|
+
|
1260
|
+
|
1261
|
+
class PolygonRequestFailedException(Exception):
|
1262
|
+
"""Exception raised when Polygon returns FAILED as request status"""
|
1263
|
+
|
1264
|
+
def __init__(self, comment):
|
1265
|
+
self.comment = comment
|
1266
|
+
|
1267
|
+
|
1268
|
+
class PointsPolicy(Enum):
|
1269
|
+
COMPLETE_GROUP = 0
|
1270
|
+
EACH_TEST = 1
|
1271
|
+
|
1272
|
+
def __str__(self):
|
1273
|
+
return self.name
|
1274
|
+
|
1275
|
+
|
1276
|
+
class FeedbackPolicy(Enum):
|
1277
|
+
NONE = 0
|
1278
|
+
POINTS = 1
|
1279
|
+
ICPC = 2
|
1280
|
+
COMPLETE = 3
|
1281
|
+
|
1282
|
+
def __str__(self):
|
1283
|
+
return self.name
|
1284
|
+
|
1285
|
+
|
1286
|
+
class FileType(Enum):
|
1287
|
+
RESOURCE = 0
|
1288
|
+
SOURCE = 1
|
1289
|
+
AUX = 2
|
1290
|
+
|
1291
|
+
def __str__(self):
|
1292
|
+
return self.name.lower()
|
1293
|
+
|
1294
|
+
|
1295
|
+
class SolutionTag(Enum):
|
1296
|
+
MA = 0 # Main correct solution
|
1297
|
+
OK = 1 # Accepted
|
1298
|
+
RJ = 2 # Rejected, Incorrect
|
1299
|
+
TL = 3 # Time limit exceeded
|
1300
|
+
TO = 4 # Time limit exceeded or accepted
|
1301
|
+
WA = 5 # Wrong answer
|
1302
|
+
PE = 6 # Presentation error
|
1303
|
+
ML = 7 # Memory limit exceeded
|
1304
|
+
RE = 8 # Runtime error
|
1305
|
+
TM = 9 # Time limit exceeded or memory limit exceeded
|
1306
|
+
NR = 10 # Do not run
|
1307
|
+
|
1308
|
+
def __str__(self):
|
1309
|
+
return self.name
|
1310
|
+
|
1311
|
+
|
1312
|
+
class Asset(Enum):
|
1313
|
+
VALIDATOR = 0
|
1314
|
+
INTERACTOR = 1
|
1315
|
+
CHECKER = 2
|
1316
|
+
SOLUTION = 3
|
1317
|
+
|
1318
|
+
def __str__(self):
|
1319
|
+
return self.name
|
1320
|
+
|
1321
|
+
|
1322
|
+
class Stage(Enum):
|
1323
|
+
COMPILE = 0
|
1324
|
+
RUN = 1
|
1325
|
+
|
1326
|
+
def __str__(self):
|
1327
|
+
return self.name
|