-
Notifications
You must be signed in to change notification settings - Fork 1
/
script_download_server.py
257 lines (206 loc) · 8.07 KB
/
script_download_server.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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
import cgi
import io
import os
import subprocess
from http.server import BaseHTTPRequestHandler, HTTPServer
import const
""" ###########################################
Server for student to access and download handin.py file
"""
# TODO: config module code and name here...
MODULE_CODE = "cs4115"
MODULE_NAME = "Default"
HOST = "127.0.0.1"
PORT = 8000
ADDR = (HOST, PORT)
DIR_ROOT = os.path.dirname(__file__)
# directory to save handin.py file temperately
DIR_TEMP = "/temp/"
DIR_DATA = f"/module/{MODULE_CODE}/data/"
DIR_MODULE = f"/module/{MODULE_CODE}/"
class BaseCase(object):
@staticmethod
def handle_file(handler, full_path):
try:
with open(full_path, 'rb') as f:
content = f.read()
handler.send_content(content)
except IOError as msg:
msg = "'{0}' cannot be read: {1}".format(full_path, msg)
handler.handle_error(msg)
@staticmethod
def index_path(handler):
return os.path.join(handler.full_path, 'handin.html')
def test(self, handler):
raise NotImplementedError("Not implemented")
def act(self, handler):
raise NotImplementedError("Not implemented")
class CaseDirectoryIndexFile(BaseCase):
def test(self, handler):
return os.path.isdir(handler.full_path) \
and os.path.isfile(self.index_path(handler))
def act(self, handler):
self.handle_file(handler, self.index_path(handler))
class CaseNoFile(BaseCase):
def test(self, handler):
return not os.path.exists(handler.full_path)
def act(self, handler):
raise ServerException("'{0}' not found".format(handler.path))
class CaseCgiFile(BaseCase):
@staticmethod
def run_cgi(handler):
content = subprocess.check_output(["python", handler.full_path])
handler.send_content(content)
def test(self, handler):
return os.path.isfile(handler.full_path) \
and handler.full_path.endswith('.py')
def act(self, handler):
self.run_cgi(handler)
class CaseExistingFile(BaseCase):
def test(self, handler):
return os.path.isfile(handler.full_path)
def act(self, handler):
self.handle_file(handler, handler.full_path)
class CaseDefault(BaseCase):
def test(self, handler):
return True
def act(self, handler):
raise ServerException("Unknown object '{0}'".format(handler.path))
def check_if_student_id_in_file(student_id):
# TODO: dynamically identify module code
with open(DIR_ROOT + DIR_MODULE + 'class-list', 'r') as f:
for line in f:
if student_id in line:
return True
return False
class RequestHandler(BaseHTTPRequestHandler):
"""Handle request and return page"""
error_page = """\
<html>
<body>
<h1>Error accessing {path}</h1>
<p>{msg}</p>
</body>
</html>
"""
cases = [
CaseNoFile(),
CaseCgiFile(),
CaseExistingFile(),
CaseDirectoryIndexFile(),
CaseDefault(),
]
base_path = DIR_ROOT
handin_file_path = DIR_ROOT + DIR_TEMP
student_data_path = DIR_ROOT + DIR_DATA
def do_GET(self):
try:
self.full_path = os.getcwd() + self.path
for case in self.cases:
if case.test(self):
case.act(self)
break
except Exception as e:
self.handle_error(e)
def do_POST(self):
try:
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={
'REQUEST_METHOD': 'POST',
'CONTENT_TYPE': self.headers['Content-Type'],
},
)
self.send_response(200)
self.send_header('Content-Type', 'text/html;charset=utf-8')
self.end_headers()
out = io.TextIOWrapper(
self.wfile,
encoding='utf-8',
line_buffering=False,
write_through=True,
)
for field in form.keys():
out.write('<p>{}={}</p>'.format(
field, form[field].value))
student_id = form['studentID'].value
student_name = form['studentName'].value
out.write('<a href="/temp/handin_{}.txt" download="handin.py">Download handin.py</a>'.format(student_id))
out.detach()
self.create_handin_file(student_id)
self.update_handin_file(student_id, student_name)
self.create_student_directory(student_id)
self.add_student_to_class_list(student_id)
except Exception as e:
self.handle_error(e)
def create_handin_file(self, student_id):
# create /temp/ file directory if not exists
if not os.path.exists(self.handin_file_path):
os.mkdir(self.handin_file_path)
# the /temp/handin_xxx.txt must be in .txt format. It is downloaded as handin.py file
filename = "handin_" + student_id + ".txt"
if not os.path.exists(self.handin_file_path + filename):
with open(self.handin_file_path + filename, 'w'):
pass
def update_handin_file(self, student_id, student_name):
"""write content to handin.py file"""
# check if handin_xxx.txt file exists
filename = "handin_" + student_id + ".txt"
if not os.path.exists(self.handin_file_path + filename):
self.create_handin_file(student_id)
# write content of handin_student_template.py to handin_xxx.txt
with open(self.handin_file_path + filename, 'wb') as f:
content_bytes: bytes = open('handin_student_template.py', 'rb').read()
content = content_bytes.decode('utf-8').format(
str(const.HANDIN_HOST), # host
str(const.HANDIN_PORT), # port
str(student_name), # student name
str(student_id), # student id
str(MODULE_CODE), # module code
str(MODULE_NAME), # module name
).encode('utf-8')
f.write(content)
def create_student_directory(self, student_id):
"""create /data/**student_id**/ directory AND all weekNumbers directory"""
if not os.path.exists(self.student_data_path):
os.mkdir(self.student_data_path)
subdir = str(student_id)
if not os.path.exists(self.student_data_path + subdir):
os.mkdir(self.student_data_path + subdir)
folders = ["w01", "w02", "w03", "w04", "w05", "w06",
"w07", "w08", "w09", "w10", "w11", "w12", "w13"]
for folder in folders:
week_dir = self.student_data_path + subdir + "/" + folder
if not os.path.exists(week_dir):
os.mkdir(week_dir)
def add_student_to_class_list(self, student_id):
"""add student id to class list file"""
# create class-list.txt file if not exists
filename = DIR_ROOT + DIR_MODULE + "class-list"
if os.path.exists(filename):
append_write = 'a'
else:
append_write = 'w'
if not check_if_student_id_in_file(student_id):
class_list = open(filename, append_write)
class_list.write(str(student_id) + '\n')
class_list.close()
def handle_error(self, msg):
content = self.error_page.format(path=self.path, msg=msg)
self.send_content(bytes(content.encode('utf-8')), status=404)
def send_content(self, content: bytes, status=200):
self.send_response(status)
self.send_header("Content-Type", "text/html")
self.send_header("Content-Length", str(len(content)))
self.end_headers()
self.wfile.write(content)
class ServerException(Exception):
"""Server Exception"""
pass
if __name__ == '__main__':
serverAddr = ADDR
server = HTTPServer(server_address=serverAddr, RequestHandlerClass=RequestHandler)
print('Starting server ...')
print('Open http://{}:{}'.format(HOST, PORT))
server.serve_forever()