-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmodels.py
437 lines (348 loc) · 15.1 KB
/
models.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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
"""
Module: monitor.py
Description:
This module manages the monitoring functionality for machine learning projects using TensorBoard.
It provides routes to start, stop, and check the status of TensorBoard processes for specific user
projects in a Flask application.
Features:
- Start TensorBoard for a specified project.
- Stop TensorBoard if it's running.
- Check the current status of TensorBoard for a project.
- Render a monitoring page.
Dependencies:
- Flask: For route handling and HTTP request/response management.
- Subprocess: For managing TensorBoard processes.
- Threading: To ensure thread-safe access to shared resources.
Author: Junyong Park
"""
import os
import json
import shutil
from flask import Blueprint, jsonify, request, session, render_template
from werkzeug.utils import secure_filename
from auth import session_required
models = Blueprint('models', __name__, url_prefix='/models')
# -------------------- HELPER FUNCTIONS --------------------
def get_temp_path(user, project_name):
"""Get the temporary folder path for model creation."""
return os.path.abspath(os.path.join('./workspace', user, project_name, 'temp_models'))
def get_workspace_path(user, project_name):
"""Get the workspace path for models."""
return os.path.abspath(os.path.join('./workspace', user, project_name, 'models'))
def get_project_json_path(user, project_name):
"""Get the project.json file path."""
return os.path.abspath(os.path.join('./workspace', user, project_name, 'project.json'))
def build_tree(path):
"""Recursively build directory tree structure."""
tree = {}
try:
for item in sorted(os.listdir(path)):
full_path = os.path.join(path, item)
if os.path.isdir(full_path):
tree[item] = {"type": "directory", "children": build_tree(full_path)}
else:
tree[item] = {"type": "file"}
except Exception as e:
print(f"Error building tree at {path}: {e}")
return tree
def ensure_path_safety(base_path, requested_path):
"""Ensure the requested path is within the allowed base path."""
requested_full_path = os.path.abspath(os.path.join(base_path, requested_path))
if not requested_full_path.startswith(base_path):
raise ValueError("Invalid path: Attempted to access outside allowed directory")
return requested_full_path
# -------------------- ROUTES --------------------
@models.route('/')
@session_required
def root():
return render_template('models.html')
@models.route('/get_temp_file', methods=['POST'])
@session_required
def get_temp_file():
"""Get content of a file from temp directory."""
try:
data = request.json
project_name = data.get('project_name')
path = data.get('path')
if not all([project_name, path]):
raise ValueError("Missing required parameters")
temp_path = get_temp_path(session['user'], project_name)
full_path = ensure_path_safety(temp_path, path)
if not os.path.exists(full_path):
raise FileNotFoundError(f"File not found: {path}")
with open(full_path, 'r', encoding='utf-8') as f:
content = f.read()
return jsonify({"content": content, "error": None})
except Exception as e:
return jsonify({"error": str(e)})
@models.route('/save_from_temp', methods=['POST'])
@session_required
def save_from_temp():
"""Save model from temp directory to workspace."""
try:
data = request.json
meta = data.get('meta')
project_name = data.get('project_name')
if not all([meta, project_name]):
raise ValueError("Missing required parameters")
model_name = meta.get('model_name')
if not model_name:
raise ValueError("Model name is required")
# Setup paths
temp_path = get_temp_path(session['user'], project_name)
workspace_path = get_workspace_path(session['user'], project_name)
model_path = os.path.join(workspace_path, model_name)
# Check if model already exists
if os.path.exists(model_path):
raise ValueError(f"Model '{model_name}' already exists")
# Copy from temp to workspace
shutil.copytree(temp_path, model_path)
# Update project.json
project_json_path = get_project_json_path(session['user'], project_name)
project_data = {"models": []}
if os.path.exists(project_json_path):
with open(project_json_path, 'r') as f:
project_data = json.load(f)
# Add new model metadata
project_data["models"].append(meta)
# Save updated project.json
with open(project_json_path, 'w') as f:
json.dump(project_data, f, indent=4)
return jsonify({
"message": f"Model '{model_name}' created successfully",
"error": None
})
except Exception as e:
return jsonify({"error": str(e)})
@models.route('/get_model_structure', methods=['POST'])
@session_required
def get_model_structure():
"""Get file structure of a saved model."""
try:
data = request.json
model_name = data.get('model_name')
project_name = data.get('project_name')
if not all([model_name, project_name]):
raise ValueError("Missing required parameters")
# Get model path
workspace_path = get_workspace_path(session['user'], project_name)
model_path = os.path.join(workspace_path, model_name)
if not os.path.exists(model_path):
raise FileNotFoundError(f"Model not found: {model_name}")
# Build tree structure
tree_data = {model_name: {"type": "directory", "children": build_tree(model_path)}}
# Find initial file (prefer model.py)
initial_file = None
model_file = os.path.join(model_name, 'model.py')
if os.path.exists(os.path.join(workspace_path, model_file)):
initial_file = model_file
return jsonify({
"tree_data": tree_data,
"initial_file": initial_file,
"error": None
})
except Exception as e:
return jsonify({"error": str(e)})
@models.route('/save_model_file', methods=['POST'])
@session_required
def save_model_file():
"""Save changes to a model file."""
try:
data = request.json
project_name = data.get('project_name')
file_path = data.get('path')
content = data.get('content')
if not all([project_name, file_path, content is not None]):
raise ValueError("Missing required parameters")
# Get workspace path and ensure file path is valid
workspace_path = get_workspace_path(session['user'], project_name)
full_path = ensure_path_safety(workspace_path, file_path)
# Create directories if they don't exist
os.makedirs(os.path.dirname(full_path), exist_ok=True)
# Save the file
with open(full_path, 'w', encoding='utf-8') as f:
f.write(content)
return jsonify({
"message": "File saved successfully",
"error": None
})
except Exception as e:
return jsonify({"error": str(e)})
@models.route('/reorder', methods=['POST'])
@session_required
def reorder_models():
"""Reorder models in project.json."""
try:
data = request.json
project_name = data.get('project_name')
new_order = data.get('order')
if not all([project_name, new_order]):
raise ValueError("Missing required parameters")
project_json_path = get_project_json_path(session['user'], project_name)
if not os.path.exists(project_json_path):
raise FileNotFoundError("Project configuration not found")
# Read current project data
with open(project_json_path, 'r') as f:
project_data = json.load(f)
# Create a map of model names to their data
model_map = {model['model_name']: model for model in project_data.get('models', [])}
# Reorder models according to new order
project_data['models'] = [model_map[name] for name in new_order if name in model_map]
# Save updated project.json
with open(project_json_path, 'w') as f:
json.dump(project_data, f, indent=4)
return jsonify({"message": "Models reordered successfully", "error": None})
except Exception as e:
return jsonify({"error": str(e)})
@models.route('/list', methods=['GET', 'POST'])
@session_required
def list_models():
"""List all models for a specific project."""
try:
project_name = request.get_json().get("project_name") if request.is_json else request.form.get("project_name")
if not project_name:
raise ValueError("Project name is missing")
project_json_path = os.path.join('./workspace', session['user'], project_name, 'project.json')
if os.path.exists(project_json_path):
with open(project_json_path, 'r') as f:
project_data = json.load(f)
models = project_data.get("models", [])
else:
models = []
return jsonify({"models": models})
except Exception as e:
return jsonify({"error": f"Failed to list models: {str(e)}"})
@models.route('/upload_temp_files', methods=['POST'])
@session_required
def upload_temp_files():
"""Handle file uploads to temp directory."""
try:
if 'files[]' not in request.files:
raise ValueError("No files provided")
project_name = request.form.get('project_name')
if not project_name:
raise ValueError("Project name is required")
temp_path = get_temp_path(session['user'], project_name)
# Create temp directory if it doesn't exist
os.makedirs(temp_path, exist_ok=True)
files = request.files.getlist('files[]')
for file in files:
if file.filename:
# Get the relative path from the file
relative_path = file.filename.replace('\\', '/')
# Create full path
full_path = os.path.join(temp_path, relative_path)
# Create directories if needed
os.makedirs(os.path.dirname(full_path), exist_ok=True)
# Save the file
file.save(full_path)
# Build and return updated tree structure
tree_data = build_tree(temp_path)
return jsonify({
"message": "Files uploaded successfully",
"tree_data": tree_data,
"error": None
})
except Exception as e:
return jsonify({"error": str(e)})
@models.route('/get_model_file', methods=['GET', 'POST'])
@session_required
def get_model_file():
"""Get content of a model file."""
try:
data = request.json
project_name = data.get('project_name')
path = data.get('path')
if not all([project_name, path]):
raise ValueError("Missing required parameters")
# Resolve the full path securely
base_path = os.path.abspath(os.path.join('./workspace', session['user'], project_name, 'models'))
full_path = os.path.normpath(os.path.join(base_path, path))
# Security check: ensure the path is within the workspace
if not full_path.startswith(base_path):
raise ValueError("Invalid file path.")
if not os.path.exists(full_path):
raise FileNotFoundError(f"File not found: {path}")
with open(full_path, 'r', encoding='utf-8') as f:
content = f.read()
return jsonify({"content": content, "error": None})
except Exception as e:
return jsonify({"error": str(e)})
@models.route('/delete', methods=['GET', 'POST'])
@session_required
def delete_model():
"""Delete a specific model."""
try:
data = request.json
model_name = data.get('name')
project_name = data.get('project_name')
if not model_name or not project_name:
raise ValueError("Model name or project name is missing.")
model_path = os.path.join('./workspace', session['user'], project_name, 'models', model_name)
# Delete model directory if it exists
if os.path.exists(model_path):
shutil.rmtree(model_path)
# Update project.json
project_json_path = os.path.join('./workspace', session['user'], project_name, 'project.json')
if os.path.exists(project_json_path):
with open(project_json_path, 'r') as f:
project_data = json.load(f)
# Remove model from the list
project_data["models"] = [m for m in project_data.get("models", []) if m["model_name"] != model_name]
# Save updated project.json
with open(project_json_path, 'w') as f:
json.dump(project_data, f, indent=4)
return jsonify({
"message": f"Model '{model_name}' deleted successfully.",
"error": None
})
except Exception as e:
return jsonify({"error": f"Failed to delete model: {str(e)}"})
@models.route('/clear_temp', methods=['POST'])
@session_required
def clear_temp():
"""Completely remove temp_models directory."""
try:
project_name = request.json.get('project_name')
if not project_name:
raise ValueError("Project name is required")
temp_path = os.path.join('./workspace', session['user'], project_name, 'temp_models')
# Force remove the directory and all contents
if os.path.exists(temp_path):
# Use rmtree with onerror handler to handle permission issues
def handle_remove_readonly(func, path, exc):
# os.chmod(path, stat.S_IWRITE)
func(path)
shutil.rmtree(temp_path, onerror=handle_remove_readonly)
return jsonify({"message": "Temp folder removed successfully", "error": None})
except Exception as e:
print(f"Error clearing temp folder: {str(e)}")
return jsonify({"error": str(e)})
@models.route('/init_temp_folder', methods=['POST'])
@session_required
def init_temp_folder():
"""Initialize temp folder with template files."""
try:
project_name = request.json.get('project_name')
if not project_name:
raise ValueError("Project name is required")
# Setup paths
temp_path = os.path.join('./workspace', session['user'], project_name, 'temp_models')
template_path = './edgeai/template/project/models/src'
# Create fresh temp directory
os.makedirs(temp_path, exist_ok=True)
# Copy template files
for filename in ['config.yaml', 'model.py']:
src_path = os.path.join(template_path, filename)
dst_path = os.path.join(temp_path, filename)
if os.path.exists(src_path):
shutil.copy2(src_path, dst_path)
# Build tree structure
tree_data = build_tree(temp_path)
return jsonify({
"tree_data": tree_data,
"initial_file": "model.py",
"error": None
})
except Exception as e:
return jsonify({"error": str(e)})