Commit 28f6df6a authored by Marcel Rieger's avatar Marcel Rieger
Browse files

Refactor CodeEditor.

parent 8b2323ef
......@@ -87,35 +87,40 @@ class AbstractController(object):
setattr(self, url, StaticController(path))
# a little helper
def get(self, key, *args):
def get(self, key, *args, **kwargs):
key = key.lower()
try:
session = cherrypy.session
request = cherrypy.request
if key == 'session_id':
return session.id # @UndefinedVariable
elif key == 'user_id':
if key == "session_id":
return session.id
elif key == "user_id":
return request.user.id
elif key == 'user_name':
elif key == "user_name":
return request.user.name
elif key == 'db':
elif key == "db":
return request.db
elif key == 'workspace_ids':
return session.get('workspace_ids', None) # @UndefinedVariable
elif key == 'workspace':
elif key == "workspace_ids":
return session.get("workspace_ids", None)
elif key == "workspace":
if len(args) == 0:
return request.workspace
else:
return Workspace.get_by_id(request.db, int(args[0]))
elif key == 'profile_id':
return session.get('profile_id', None) # @UndefinedVariable
elif key == 'profile':
elif key == "profile_id":
return session.get("profile_id", None)
elif key == "profile":
return request.profile
elif key == 'fs':
elif key == "fs":
workspace = request.workspace if len(args) == 0 else args[0]
return vispa.workspace.get_instance('vispa.remote.filesystem.FileSystem', workspace=workspace)
return vispa.workspace.get_instance(
"vispa.remote.filesystem.FileSystem", workspace=workspace)
elif key == "view_id":
return request.private_params.get("_viewId", None)
elif key == "proxy":
if not hasattr(self, "extension") or not len(args):
return None
return self.extension.get_workspace_instance(*args, **kwargs)
except:
vispa.log_exception()
return None
......
# -*- coding: utf-8 -*-
from vispa.server import AbstractExtension
from controller import EditorController
......@@ -11,17 +10,22 @@ class CodeEditorExtension(AbstractExtension):
return "codeeditor"
def dependencies(self):
return ['jobmanagement_bootstrap']
return []
def setup(self):
self.add_controller(EditorController())
self.add_js('js/extension.js')
self.add_js('js/editor.js')
self.add_js('js/parameterwindow.js')
self.add_js('js/plotpreview.js')
self.add_js('js/vendor/ace/ace.js')
self.add_js('js/actions.js')
self.add_js('js/executionlog.js')
self.add_js('js/prefs.js')
self.add_css('css/styles.css')
self.add_workspace_directoy()
\ No newline at end of file
self.add_js("js/extension.js")
self.add_js("js/editor.js")
self.add_js("js/ui.js")
self.add_js("js/output.js")
self.add_js("js/preview.js")
# self.add_js("js/actions.js")
# self.add_js("js/executionlog.js")
self.add_js("js/prefs.js")
self.add_css("css/styles.less")
self.add_js("js/vendor/ace/ace.js")
self.add_workspace_directoy()
......@@ -7,63 +7,62 @@ import os
import json
import vispa.workspace
from vispa.controller import AbstractController
from vispa import MessageException
class EditorController(AbstractController):
workspace_class_name = "vispa.extensions.codeeditor.workspace.CodeEditorRpc"
class EditorController(AbstractController):
@cherrypy.expose
@cherrypy.tools.ajax(encoded=True)
def getcontent(self, path):
Rpc = self.extension.get_workspace_instance("EditorRpc", cherrypy.request.private_params['_viewId'])
content = Rpc.getcontent(path)
return content
try:
rpc = self.get("proxy", "CodeEditorRpc")
return rpc.getcontent(path)
except Exception as e:
raise MessageException(e.message)
@cherrypy.expose
@cherrypy.tools.ajax()
@cherrypy.tools.ajax(encoded=True)
def savecontent(self, path, content):
Rpc = self.extension.get_workspace_instance("EditorRpc", cherrypy.request.private_params['_viewId'])
Rpc.savecontent(path,content)
try:
rpc = self.get("proxy", "CodeEditorRpc")
return rpc.savecontent(path, content)
except Exception as e:
raise MessageException(e.message)
@cherrypy.expose
@cherrypy.tools.ajax()
def execute(self, path, params, jobID):
Rpc = self.extension.get_workspace_instance("EditorRpc", cherrypy.request.private_params['_viewId'])
def execute(self, path):
# atm, only python scripts are supported
if not path.endswith(".py"):
return {"success": False}
try:
maxtime = int(vispa.config.get('extensions', 'maxexe'))
except:
maxtime = 3600 #(seconds)
Rpc.execute(path, params, jobID, maxtime)
@cherrypy.expose
@cherrypy.tools.ajax(encoded=True)
def jobpolling(self, jobID):
Rpc = self.extension.get_workspace_instance("EditorRpc", cherrypy.request.private_params['_viewId'])
return Rpc.jobpolling(jobID)
rpc = self.get("proxy", "CodeEditorRpc")
return {"success": rpc.execute(path)}
except Exception as e:
raise MessageException(e.message)
@cherrypy.expose
@cherrypy.tools.ajax(encoded=True)
def killjob(self, jobID):
Rpc = self.extension.get_workspace_instance("EditorRpc", cherrypy.request.private_params['_viewId'])
return Rpc.killjob(jobID)
def poll(self):
try:
rpc = self.get("proxy", "CodeEditorRpc")
return rpc.poll()
except Exception as e:
raise MessageException(e.message)
@cherrypy.expose
@cherrypy.tools.ajax(encoded=True)
def get_file_list(self, path=None):
def getpreviews(self, path):
rfs = self.get("fs")
filelist = rfs.get_file_list(path);
filelist = rfs.get_file_list(path)
return filelist
@cherrypy.expose
@cherrypy.tools.ajax(encoded=True)
def getinientry(self, category, entry):
tbr = {
"inientry": vispa.config.get(category, entry)
}
return json.dumps(tbr)
@cherrypy.expose
@cherrypy.tools.ajax()
def getmtime(self, path):
fs = self.get("fs")
return {"mtime": fs.get_mtime(path)}
def abort(self):
try:
rpc = self.get("proxy", "CodeEditorRpc")
return {"success": rpc.abort()}
except Exception as e:
raise MessageException(e.message)
@border-color: #b8b8b8;
.absolute (@top: 0px, @right: 0px, @bottom: 0px, @left: 0px) {
position: absolute;
top: @top;
right: @right;
bottom: @bottom;
left: @left;
}
.codeeditor {
.absolute();
left: 0px;
margin: 0px;
padding: 0px;
}
.codeeditor-left {
.absolute(@right: none);
width: 100%;
overflow: auto;
box-shadow: 1px 0px 0px @border-color;
}
.codeeditor-right {
.absolute(@left: none);
width: 0%;
overflow: auto;
padding-left: 1px;
}
.codeeditor-right-top {
.absolute(@bottom: none);
height: 100%;
overflow: hidden;
box-shadow: 0px 1px 0px @border-color;
}
.codeeditor-right-bottom {
.absolute(@top: none);
height: 0%;
overflow: auto;
padding: 11px 10px 10px 10px;
}
.codeeditor-ace {
.absolute() !important;
}
.codeeditor-output {
.absolute() !important;
border-radius: 0px;
margin: 0px 0px 0px 1px;
border: 0px;
}
.codeeditor-output-buttons {
position: absolute;
top: 10px;
right: 10px;
}
.codeeditor-right-top pre {
overflow: auto;
}
.codeeditor-right-bottom .well {
.absolute(@top: 55px, @right: 10px, @bottom: 10px, @left: 10px);
padding: 10px;
margin: 0px;
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
}
.codeeditor-right-bottom .codeeditor-preview {
position: relative;
display: inline-block;
height: 100%;
margin: 0px 10px 10px 0px;
padding: 6px;
border-radius: 3px;
border: 1px solid @border-color;
background-color: white;
}
.codeeditor-right-bottom .codeeditor-preview:hover {
border-color: #428bca;
}
.codeeditor-right-bottom .codeeditor-preview-label {
position: absolute;
top: 0px;
right: 0px;
left: 0px;
height: 24px;
text-align: center;
}
.codeeditor-right-bottom .codeeditor-preview-img {
position: absolute;
top: 24px;
right: 0px;
bottom: 0px;
left: 0px;
text-align: center;
}
.codeeditor-right-bottom .codeeditor-preview img {
padding-top: 24px;
height: 100%;
width: auto;
}
<style type="text/css">
</style>
<div class="codeeditor">
<div class="input">
<div class="inputtext">
</div>
</div>
<div class="codeeditor-left">
<div class="codeeditor-ace"></div>
</div>
<div class="output">
<div class="outputtext">
</div>
<div class="canvaspreview">
<input type="button" class="btn btn-default picturepathbutton" value="Picturedirectory"/>
<div class="picturediv">
<p align="center"><b>Canvas preview</b></p>
</div>
</div>
</div>
<div class="codeeditor-right">
<div class="codeeditor-right-top">
<pre class="codeeditor-output">Execution Output</pre>
<div class="codeeditor-output-buttons">
<div class="btn-group">
<button class="btn btn-sm btn-default gototop" type="button">
<i class="glyphicon glyphicon-arrow-up"></i>
<span>Top</span>
</button>
<button class="btn btn-sm btn-default gotobottom" type="button">
<i class="glyphicon glyphicon-arrow-down"></i>
<span>Bottom</span>
</button>
</div>
<button class="btn btn-sm btn-default clear" type="button">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Clear</span>
</button>
</div>
</div>
<div class="codeeditor-right-bottom">
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default select-button" type="button">
<i class="glyphicon glyphicon-folder-close"></i>
</button>
</span>
<input type="text" class="form-control" id="path-input" placeholder="Directory to inspect" />
</div>
<div class="well"></div>
</div>
</div>
</div>
\ No newline at end of file
<div class="codeeditor-preview">
<a href="#">
<div class="codeeditor-preview-label">
<div class="vispa-table-outer">
<div class="vispa-table-inner">
<span></span>
</div>
</div>
</div>
<img />
</a>
</div>
var actions = Class.extend({
init: function(instance, div) {
this.instance = instance;
this.maindiv = div;
this.currentjob = {
executing: false,
poll: null
}
this.executebutton = instance._menuEntries[2];
this.savebuttons = instance._menuEntries[3];
},
openfile: function() {
var self = this;
var instance = this.instance;
var path = "";
var ignoresavestatus = true; //check if previous file has been saved, default: file has been saved
if (instance.inputeditor.modified)
ignoresavestatus = confirm("File has not been saved. Proceed?");
if (!ignoresavestatus) return;
//get the path to open ('DefaultPath' if workpath is empty)
if (instance.workpath == "")
path = instance.getPreference('DefaultPath');
else
path = instance.workpath;
var args = {
"path": path, //when navigation works, use this!
callback: function(path, args) {
var instance = args.instance;
var type = path.split(".").pop();
if (path == null || instance.allowedtypes.indexOf(type) == -1) {
instance.alert('<p align="center">Not an allowed type of file!</p>');
return;
}
//visualize loading status
instance.setLoading(true);
instance.setIcon(instance.static("img/fileopen.png"));
//get the new content and handle it
var dfd = instance.GET("getcontent", {
path: path
}, function(res) {
if (res.status) { //file could be opened
instance.inputeditor.setcontent(res.content);
instance.mtime = res.mtime
instance.setpathnames(path);
instance.setLabel(instance.path, true);
instance.inputeditor.setmode(instance.type);
instance.haspath = true;
instance.inputeditor.gototop();
instance.inputeditor.checkmodification(res.content);
instance.outputeditor.dump();
instance.outputeditor.add("Output of " + instance.filename);
instance.plotpreview.sethtml('<p align="center"><b>Canvas preview</b></p>');
instance.plotpreview.setdirectory(instance.workpath);
} else {
instance.alert(res.content);
}
instance.setLoading(false);
instance.setIcon(instance.icon);
});
},
cb_args: {
instance: instance
},
view: "symbol",
sort: ["time", "ext"],
reverse: [true, false]
};
instance.selector.createInstance(instance.getWorkspaceId(), FileSelectorView, args);
},
executescript: function() {
var self = this;
var instance = this.instance;
if (self.currentjob.executing) {
instance.alert('<p align="center">Previous execution is still running!</p>');
return;
}
if (!instance.haspath) {
instance.alert("Please save file before execution");
return;
}
if (!instance.inputeditor.CheckBadChars())
return;
if (instance.type != 'py') {
instance.alert('<p align="center">File is no python script!</p>');
return;
}
//job is good to go
self.currentjob.executing = true;
self.jobID = (new Date()).getTime();
var jobParameters = instance.parameterwindow.getParameterString();
instance.plotpreview.sethtml('<p align="center"> Analysis running </p>');
var dfd = instance.inputeditor.save(); //save the content before execution
$.when(dfd).then(function() { //when save command is done, fire the execute()
instance.GET("execute", {
path: instance.path,
params: jobParameters,
jobID: self.jobID
},
function() {
instance.setIcon(instance.static('img/loading.gif'));
self.jobpolling();
});
});
},
jobpolling: function() {
var self = this;
var instance = this.instance;
var polltime = instance.getPreference('JobPollingDelay');
self.currentjob.poll = setInterval(function() {
var dfd = instance.GET("jobpolling", {
jobID: self.jobID
});
$.when(dfd).then(function(data) {
if (data.finished) {
clearInterval(self.currentjob.poll);
self.exetime = data.exetime;
self.displayOutput(data, aborted = false);
self.currentjob.executing = false; //at this point the execution on the server is finished
}
})
}, polltime);
},
killjob: function() {
var self = this;
var instance = this.instance;
console.log(self.currentjob.executing);
if (!self.currentjob.executing) {
instance.alert("No job to abort!");
return;
}
clearInterval(self.currentjob.poll);
var dfd = instance.GET("killjob", {
jobID: self.jobID
});
$.when(dfd).then(function(data) {
self.displayOutput(data, aborted = true)
self.currentjob.executing = false;
});
},
displayOutput: function(data, aborted) {
var self = this;
var instance = this.instance;
var d = new Date(data.exetime * 1000); //*1000 because time needs be in ms
var exedate = d.getDate() + '.' + d.getMonth() + 1 + '. ' + d.getHours() + ':' + d.getMinutes() +
':' + d.getSeconds();
var logentry = {
file: instance.filename,
submission: exedate,
runtime: data.runtime,
status: data.exitcode
};
if (data.exitcode == 0 && !aborted) { //finishd without errors
var content = 'Job (submitted:' + exedate + ') finished with status: ' + data.exitcode +
' runtime: ' + data.runtime + 's\n' + data.output;
instance.outputeditor.add(content);
logentry.output = content;
instance.plotpreview.makepreview(self.exetime);
/*instance.GET("get_file_list", {
path: instance.workpath
},
function(data) {
instance.plotpreview.makepreview(data.filelist, self.exetime, instance.workpath);
});*/
instance.setIcon(instance.icon);
} else if (data.exitcode == -15 && !aborted) { //runtime on workspace exceeded
var content = 'Job (submitted:' + exedate + ') runtime: ' + data.runtime +
's\n Maximum runtime on workspace exceeded!:\n';
instance.outputeditor.add(content);
logentry.output = content;
instance.plotpreview.sethtml('<p style="text-align: center"> Runtime exceeded ! </p>');
instance.setIcon(instance.icon);
} else if (!aborted) { //finished with errors
var content = 'Job (submitted:' + exedate + '): Following error has occured:\n' + data.error;
instance.outputeditor.add(content);
logentry.output = content;
instance.plotpreview.sethtml('<p style="text-align: center"> Error in script ! </p>');
instance.setIcon(instance.icon);
} else {
var content = 'Job (submitted:' + exedate + ') runtime: ' + data.runtime +
's\n ABORTED BY USER';
instance.outputeditor.add(content);
logentry.output =