-
Notifications
You must be signed in to change notification settings - Fork 0
/
rest.py
204 lines (164 loc) · 6.35 KB
/
rest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#! /usr/bin/env python
#-*- coding: utf8 -*-
from settings import SUPERVISOR, NODE
from six.moves.http_client import FORBIDDEN, NOT_FOUND, OK
from sys import exit
from requests import ConnectionError
import logging
import os
import requests
import json
class NotFoundException(Exception):
"""Raised when there is no problem or tests for the problem
with a given name, or the Supervisor return no submission to judge."""
pass
class UnauthorizedException(Exception):
"""Raised when the Node is unauthorised to judge submissions."""
pass
class UnknownErrorException(Exception):
"""Raised when an unknown error occured."""
pass
class RESTConnection(object):
"""Class gathering all the REST queries and requests."""
@classmethod
def __get(cls, url):
"""Perform a GET request on the given URL."""
try:
return requests.get(
SUPERVISOR['HOST'] + url + '?format=json',
verify=True,
headers={'X-token': NODE['TOKEN']}
)
except ConnectionError as e:
logging.critical("Connection failed: {}".format(e))
exit(1)
@classmethod
def __put(cls, url, *args, **kwargs):
"""Perform a POST request on the given URL."""
headers = {'X-token': NODE['TOKEN']}
headers.update(kwargs.get('headers', {}))
del kwargs['headers']
try:
return requests.put(
SUPERVISOR['HOST'] + url + '?format=json',
verify=True,
headers=headers,
*args, **kwargs
)
except ConnectionError as e:
logging.critical("Connection failed: {}".format(e))
exit(1)
@classmethod
def get_submission(cls):
"""Get a new submission for judging."""
# Prepare the URL
url = 'submission/for_judging/'
response = cls.__get(url)
if response.status_code == OK:
# If everything is okay then parse the data and return it
try:
data = json.loads(response.text)
except Exception as e:
logging.error(
"Error while parsing response: {}\n{}.".format(data, e)
)
raise
logging.info(
"Recived submission id {}.".format(data['id'])
)
return data
elif response.status_code == NOT_FOUND:
logging.info("No submission recived.")
raise NotFoundException()
elif response.status_code == FORBIDDEN:
logging.warning("Node unauthorized.")
raise UnauthorizedException()
else:
logging.error("Unknown error status code: {}".format(
response.status_code))
raise UnknownErrorException()
@classmethod
def post_submission(cls, submissionId, submission):
"""Post a judged submission to the Supervisor."""
url = 'submission/{}/'.format(submissionId)
# Prepare the submission
try:
data = json.dumps(submission)
except:
logging.critical("Data malformed {}.".format(submission))
raise
response = cls.__put(
url,
data=data,
headers={'Content-Type': 'application/json'}
)
if response.status_code == OK and not submission['error']:
logging.info("The results have been sent.")
elif response.status_code == OK and submission['error']:
logging.info("Supervisor has been notified about the failure.")
elif response.status_code == FORBIDDEN:
logging.warning("Node unauthorized.")
raise UnauthorizedException()
else:
logging.error("Unknown error status code: {}".format(
response.status_code))
raise UnknownErrorException()
@classmethod
def get_test_timestamps(cls, problem):
"""Get the timestamps for the tests for the problem with a given id."""
url = 'problem/{}/test_timestamps'.format(problem)
response = cls.__get(url)
if response.status_code == OK:
try:
data = json.loads(response.text)
except Exception as e:
logging.error(
"Error while parsing response: {}\n{}.".format(data, e)
)
raise
logging.info(
"Recived test timestamps for problem {}.".format(problem)
)
return data
elif response.status_code == NOT_FOUND:
logging.info(
"No test timestamps for problem {}.".format(problem)
)
raise NotFoundException()
elif response.status_code == FORBIDDEN:
logging.warning("Node unauthorized.")
raise UnauthorizedException()
else:
logging.error("Unknown error status code: {}".format(
response.status_code))
raise UnknownErrorException()
@classmethod
def get_tests(cls, problem, testType, path):
"""Get the tests for the problem with a given id as a file path."""
# There are only 3 types of test files
if testType not in ('input', 'output', 'config'):
raise TypeError("Type not recognized: {}.".format(testType))
url = 'problem/{}/test_{}/'.format(problem, testType)
response = cls.__get(url)
if response.status_code == OK:
cls.__write_to_file(response, path)
elif response.status_code == NOT_FOUND:
logging.info("No tests for problem {}.".format(problem))
raise NotFoundException()
elif response.status_code == FORBIDDEN:
logging.warning("Node unauthorized.")
raise UnauthorizedException()
else:
logging.error("Unknown error status code: {}".format(
response.status_code))
raise UnknownErrorException()
@classmethod
def __write_to_file(cls, response, path):
"""Writes the response body to a file given by the path."""
if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
if os.path.exists(path):
os.remove(path)
with open(path, 'wb') as output:
for chunk in response.iter_content(1024):
output.write(chunk)