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.
@@ -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