-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patheditor_view.py
310 lines (268 loc) · 11 KB
/
editor_view.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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
import os, subprocess, sys, time, uuid
from flask import Blueprint, request, render_template, jsonify, make_response, send_file, abort
from werkzeug.utils import secure_filename
from ..config import const
from pathlib import Path
# = = = = = = = = = = = = = = = = = =
### Native private methods
def _response_(status, msg, code='', data=''):
"""
Basic web interface format
---
description:
1.Bool status : 0 -> success / 1 -> failure
2.String msg : error message
3.Data : fetching data when success
4.String code : error code when fail (start with 'CE')
example:
{"status" : 0, "msg" : "request success", "data":[]}
{"status" : 1, "msg" : "password invalid", "code":"CE00001"}
"""
if status:
return jsonify({"status" : 0, "msg" : msg, "data" : data})
else :
return jsonify({"status" : 1, "msg" : msg, "code" : code})
def _allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1] in const.ALLOWED_EXTENSIONS
def _get_user_id():
# Production
return request.cookies.get(const.FIELD_USER_ID)
# Test
# return request.form.get(const.FIELD_USER_ID)
def _check_path():
parent = const.PATH_TMP_STORAGE + os.sep + _get_user_id()
path = parent + os.sep + const.DEFAULT_FILE_NAME
parent_trace = const.PATH_TRACE
parent_user_trace = const.PATH_TRACE + os.sep + _get_user_id()
if not Path(parent).exists(): Path(parent).mkdir()
if not Path(parent_trace).exists(): Path(parent_trace).mkdir()
if not Path(parent_user_trace).exists(): Path(parent_user_trace).mkdir()
if not Path(path).exists(): Path(path).touch()
return path
# = = = = = = = = = = = = = = = = = =
### Web interface
editor = Blueprint("editor", __name__, url_prefix="/editor")
@editor.route("/", methods = ['GET'])
def initial():
"""
Main page for users
---
description: show users an online Cyclone IDE
parameters:
- name: id
type: string
required: false
default: null
description: every new user will have an unique id or retrieve the previous id stored in the cookie
responses:
200:
description: successfully show the webpage 'index.html'
"""
if _get_user_id():
parent = const.PATH_TMP_STORAGE + os.sep + _get_user_id()
if Path(parent).exists():
code = Path(parent + os.sep + const.DEFAULT_FILE_NAME).read_text()
return render_template(const.HTML_INDEX, code=code)
code = Path(const.PATH_TMP_STORAGE + os.sep + const.DEFAULT_FILE_NAME).read_text()
res = make_response(render_template(const.HTML_INDEX, code=code))
res.set_cookie(const.FIELD_USER_ID, const.BRAND_PREFIX + str(uuid.uuid4()))
return res
@editor.route("/run", methods = ['POST'])
def runCode():
"""
Run Cyclone code
---
description: runing the code sent by users and return the result
parameters:
- name: id
type: string
required: true
description: every new user will have an unique id or retrieve the previous id stored in the cookie
- name: code
type: string
required: true
description: the code that user wants to run
responses:
200:
description: successfully run the Cyclone code or not
"""
if not _get_user_id(): return _response_(False, const.ERROR_USER_ID, code=const.CODE_USER_ID)
code = request.form.get(const.FIELD_USER_CODE)
if code and id:
path = _check_path()
Path(path).write_text(code)
result = subprocess.Popen([const.SPT_EX, const.PATH_PROJECT, path, _get_user_id()], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = result.communicate()
symbol = "Trace Generated"
res = stdout.decode(sys.getdefaultencoding())
if symbol not in res:
return _response_(True, const.SUCCESS_REQ, data=res);
else:
# translate the local path into an <a> html tage and return
for line in res.splitlines():
if symbol in line:
kv = line.split(":")
if len(kv) < 2:
return _response_(False, const.ERROR_CYC_TRACE_RETURN, code=const.CODE_CYC_TRACE_RETURN)
else:
return _response_(True, const.SUCCESS_REQ, data=res.replace(kv[1], "<a href='/editor/file?path=" + kv[1] + "'>Trace file download</a>"))
else: return _response_(False, const.ERROR_CODE_ETY, code=const.CODE_CODE_ETY)
@editor.route("/file", methods = ['GET'])
def send_trace_file():
"""
Send trace file to users
---
description: send trace file to users through the unique file path
parameters:
- name: id
type: string
required: true
description: every new user will have an unique id or retrieve the previous id stored in the cookie
- name: file
type: string
required: true
description: the absolute trace file path produced by Cyclone
responses:
200:
description: successfully send the trace file to the user
"""
if not _get_user_id(): return _response_(False, const.ERROR_USER_ID, code=const.CODE_USER_ID)
path = request.args.get(const.FIELD_FILE_PATH)
if not path or const.PATH_TRACE not in path or not Path(path).exists(): abort(404)
try:
format = path.rsplit('.', 1)[1]
if format and format == const.ALLOWED_EXTENSIONS_DOT:
path_dot = path.rsplit(os.sep, 1)
parent = path_dot[0]
filename = path_dot[1]
result = subprocess.Popen([const.SPT_DOT, const.PATH_PROJECT, filename, _get_user_id()], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = result.communicate()
path_png = parent + os.sep + const.PNG_CYCLONE
if Path(path_png).exists():
return send_file(path_png, as_attachment=True)
return send_file(path, as_attachment=False)
except Exception as e:
abort(404)
@editor.route('/upload', methods = ['POST'])
def upload():
"""
User the code from the file to cover with the local code
---
description: User the code from the file uploaded by user to replace the local code
parameters:
- name: id
type: string
required: true
description: every new user will have an unique id or retrieve the previous id stored in the cookie
- name: file
type: file
required: true
description: the file provided by user's device
responses:
200:
description: successfully replace the local code
"""
if not _get_user_id(): return _response_(False, const.ERROR_USER_ID, code=const.CODE_USER_ID)
# check if the post request has the file part
if const.FIELD_FILE in request.files: file = request.files[const.FIELD_FILE]
else: return _response_(False, const.ERROR_FILE_NOT_EXIST, code=const.CODE_FILE_NOT_EXIST)
# If the user does not select a file, the browser submits an empty file without a filename.
if file.filename == '':
return _response_(False, const.ERROR_FILE_NAME_ETY, code=const.CODE_FILE_NAME_ETY)
if file and _allowed_file(file.filename):
path_org = _check_path()
path = os.path.join(const.PATH_TMP_STORAGE + os.sep + _get_user_id(), secure_filename(file.filename))
file.save(path)
code = Path(path).read_text()
os.remove(path)
Path(path_org).write_text(code)
return _response_(True, const.SUCCESS_REQ_UPDATE, data=code)
else:
return _response_(False, const.ERROR_FILE_UPDATE, code=const.CODE_FILE_UPDATE)
@editor.route('/save2LocalFile', methods = ['POST'])
def downLoadFile():
"""
Download the local code
---
description: Download the local code
parameters:
- name: id
type: string
required: true
description: every new user will have an unique id or retrieve the previous id stored in the cookie
- name: code
type: string
required: true
description: the code provided by user's online IDE
responses:
200:
description: successfully Download the local code
"""
if not _get_user_id(): return _response_(False, const.ERROR_USER_ID, code=const.CODE_USER_ID)
code = request.form.get(const.FIELD_USER_CODE)
if code:
path = _check_path()
Path(path).write_text(code)
return send_file(path, download_name=const.DEFAULT_FILE_NAME, as_attachment=True)
else: return _response_(False, const.ERROR_CODE_ETY, code=const.CODE_CODE_ETY)
@editor.route("/examples", methods = ['POST'])
def getExamplesList():
"""
Show examples from the Cyclone folder
---
description: Show every example from the Cyclone folder in light of its structure
parameters:
- name: id
type: string
required: true
description: every new user will have an unique id or retrieve the previous id stored in the cookie
responses:
200:
description: successfully return the structure of examples
"""
if not _get_user_id(): return _response_(False, const.ERROR_USER_ID, code=const.CODE_USER_ID)
structure = {}
if Path(const.PATH_EXAMPLE).exists():
for path, dirs, files in os.walk(const.PATH_EXAMPLE):
if path != const.PATH_EXAMPLE:
s_folder = path.split(os.sep)[-1]
if s_folder and len(files) > 0:
structure[s_folder] = files
structure[s_folder].sort()
return _response_(True, const.SUCCESS_REQ, data=structure)
@editor.route("/example", methods = ['POST'])
def getExample():
"""
Get the code from a example in the Cyclone folder
---
description: Read the code from a specific example
parameters:
- name: id
type: string
required: true
description: every new user will have an unique id or retrieve the previous id stored in the cookie
- name: file
type: string
required: true
description: the file name
- name: folder
type: string
required: true
description: the folder name
responses:
200:
description: successfully return the code from a example in the Cyclone folder
"""
if not _get_user_id(): return _response_(False, const.ERROR_USER_ID, code=const.CODE_USER_ID)
file = request.form.get(const.FIELD_FILE)
folder = request.form.get(const.FIELD_FOLDER)
# TODO verify the data
if file and folder:
try:
code = Path(const.PATH_EXAMPLE + os.sep + folder + os.sep + file).read_text()
return _response_(True, const.SUCCESS_REQ, data=code)
except Exception as e:
# TODO deal with the error when the file cannot be reached.
code = Path(parent + os.sep + const.DEFAULT_FILE_NAME).read_text()
return _response_(True, const.SUCCESS_REQ, data=code)
else: return _response_(False, const.ERROR_FILE_NOT_EXIST, code=CODE_FILE_NOT_EXIST)