Skip to content

Commit 9de5184

Browse files
authored
Feature/uploader (#112)
* smaller arrow * More separation node-link * Remove colon from logger message sender * Upload Manager added * minio added to server * File Manager added to client. File upload working * UploadMgr not needed * Fill tree with data coming from s3 * File Manager selects file and filePath is written in output * Move hardcoded stuff to data/Fake * Update server to list objects in bucket * retrieve_url_for_file works * get objects list works * Progress bar added to file upload * S3 configuration variables moved to source/config.py
1 parent 65e959b commit 9de5184

File tree

11 files changed

+331
-22
lines changed

11 files changed

+331
-22
lines changed

services/web/client/source/class/qxapp/components/workbench/SvgWidget.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ qx.Class.define("qxapp.components.workbench.SvgWidget", {
3737
__linksCanvas: null,
3838

3939
__getControls: function(x1, y1, x2, y2) {
40-
const offset = 50;
40+
const offset = 60;
4141
return [{
4242
x: x1,
4343
y: y1

services/web/client/source/class/qxapp/components/workbench/Workbench.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,35 @@ qx.Class.define("qxapp.components.workbench.Workbench", {
327327
}, this);
328328

329329
node.addListener("dblclick", function(e) {
330-
this.fireDataEvent("NodeDoubleClicked", node);
330+
if (node.getMetadata().key === "FileManager") {
331+
let win = new qx.ui.window.Window(node.getMetadata().name).set({
332+
width: 800,
333+
height: 600,
334+
minWidth: 400,
335+
minHeight: 400,
336+
modal: true,
337+
showMinimize: false,
338+
layout: new qx.ui.layout.Canvas()
339+
});
340+
win.moveTo(50, 50);
341+
342+
let fileManager = new qxapp.components.workbench.widgets.FileManager();
343+
win.add(fileManager, {
344+
left: 0,
345+
top: 0,
346+
right: 0,
347+
bottom: 0
348+
});
349+
fileManager.addListener("FileSelected", function(data) {
350+
node.getMetadata().outputs[0].value = data.getData().filePath;
351+
node.setProgress(100);
352+
win.close();
353+
}, this);
354+
355+
this.addWindowToDesktop(win);
356+
} else {
357+
this.fireDataEvent("NodeDoubleClicked", node);
358+
}
331359
e.stopPropagation();
332360
}, this);
333361

services/web/client/source/class/qxapp/components/workbench/logger/LoggerView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ qx.Class.define("qxapp.components.workbench.logger.LoggerView", {
185185
this.__messengerColors.add([who, whoColor]);
186186
}
187187

188-
return ("<font color=" + whoColor +">" + who + ": </font>");
188+
return ("<font color=" + whoColor +">" + who + "</font>");
189189
},
190190

191191
__addLevelColorTag: function(what, logLevel) {
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/* global XMLHttpRequest */
2+
qx.Class.define("qxapp.components.workbench.widgets.FileManager", {
3+
extend: qx.ui.container.Composite,
4+
5+
construct: function() {
6+
this.base(arguments, new qx.ui.layout.VBox(10));
7+
8+
9+
// Create a button
10+
let input = new qx.html.Input("file", {
11+
display: "none"
12+
}, {
13+
multiple: true,
14+
accept: "image/*"
15+
});
16+
17+
this.getContentElement().add(input);
18+
19+
let pick = new qx.ui.form.Button(this.tr("Add file"));
20+
this.add(pick);
21+
22+
// Add an event listener
23+
pick.addListener("execute", function(e) {
24+
input.getDomElement().click();
25+
});
26+
27+
input.addListener("change", function(e) {
28+
let files = input.getDomElement().files;
29+
for (let i=0; i<files.length; i++) {
30+
this.__retrieveURLAndUpload(files[i]);
31+
}
32+
}, this);
33+
34+
let tree = this.__mainTree = new qx.ui.tree.Tree();
35+
this.add(tree, {
36+
flex: 1
37+
});
38+
tree.addListener("changeSelection", this.__selectionChanged, this);
39+
40+
this.__selectFileBtn = new qx.ui.form.Button(this.tr("Select File"));
41+
this.__selectFileBtn.setEnabled(false);
42+
this.add(this.__selectFileBtn);
43+
this.__selectFileBtn.addListener("execute", function() {
44+
this.__fileSelected();
45+
}, this);
46+
47+
this.__reloadTree();
48+
},
49+
50+
events: {
51+
"FileSelected": "qx.event.type.Data"
52+
},
53+
54+
members: {
55+
__mainTree: null,
56+
__publicTree: null,
57+
__userTree: null,
58+
__selectFileBtn: null,
59+
60+
__reloadTree: function() {
61+
this.__mainTree.resetRoot();
62+
63+
let root = this.__configureTreeItem(new qx.ui.tree.TreeFolder(), this.tr("Available Files"));
64+
root.setOpen(true);
65+
this.__mainTree.setRoot(root);
66+
67+
let tree1 = this.__publicTree = this.__configureTreeItem(new qx.ui.tree.TreeFolder(), this.tr("Public Files"));
68+
tree1.setOpen(true);
69+
root.add(tree1);
70+
71+
let tree2 = this.__userTree = this.__configureTreeItem(new qx.ui.tree.TreeFolder(), this.tr("User Files"));
72+
tree2.setOpen(true);
73+
root.add(tree2);
74+
75+
const username = qxapp.data.Fake.getUsername();
76+
this.__getObjLists(username);
77+
},
78+
79+
__getObjLists: function(bucketName) {
80+
let socket = qxapp.wrappers.WebSocket.getInstance();
81+
82+
socket.removeSlot("listObjectsPub");
83+
socket.on("listObjectsPub", function(data) {
84+
let treeItem = this.__addTreeItem(this.__publicTree, data);
85+
const publicBucket = qxapp.data.Fake.getS3PublicBucketName();
86+
treeItem.path = publicBucket + "/" + data.name;
87+
}, this);
88+
89+
socket.removeSlot("listObjectsUser");
90+
socket.on("listObjectsUser", function(data) {
91+
let treeItem = this.__addTreeItem(this.__userTree, data);
92+
const username = qxapp.data.Fake.getUsername();
93+
treeItem.path = username + "/" + data.name;
94+
}, this);
95+
96+
socket.emit("listObjects", bucketName);
97+
},
98+
99+
// Request to the server an upload URL.
100+
__retrieveURLAndUpload: function(file) {
101+
let socket = qxapp.wrappers.WebSocket.getInstance();
102+
103+
socket.removeSlot("presignedUrl");
104+
socket.on("presignedUrl", function(data) {
105+
const url = data["url"];
106+
this.__uploadFile(file, url);
107+
}, this);
108+
const data = {
109+
bucketName: qxapp.data.Fake.getUsername(),
110+
fileName: file.name
111+
};
112+
socket.emit("presignedUrl", data);
113+
},
114+
115+
// Use XMLHttpRequest to upload the file to S3.
116+
__uploadFile: function(file, url) {
117+
let hBox = new qx.ui.container.Composite(new qx.ui.layout.HBox());
118+
let label = new qx.ui.basic.Label(file.name);
119+
let progressBar = new qx.ui.indicator.ProgressBar();
120+
hBox.add(label, {
121+
width: "15%"
122+
});
123+
hBox.add(progressBar, {
124+
width: "85%"
125+
});
126+
this.addAt(hBox, 1);
127+
let xhr = new XMLHttpRequest();
128+
xhr.upload.addEventListener("progress", function(e) {
129+
if (e.lengthComputable) {
130+
const percentComplete = e.loaded / e.total * 100;
131+
progressBar.setValue(percentComplete);
132+
} else {
133+
console.log("Unable to compute progress information since the total size is unknown");
134+
}
135+
}, false);
136+
xhr.open("PUT", url, true);
137+
xhr.send(file);
138+
xhr.onload = () => {
139+
if (xhr.status == 200) {
140+
console.log("Uploaded", file.name);
141+
this.remove(hBox);
142+
this.__reloadTree();
143+
}
144+
};
145+
},
146+
147+
__selectionChanged: function() {
148+
let selectedItem = this.__mainTree.getSelection();
149+
this.__selectFileBtn.setEnabled("path" in selectedItem[0]);
150+
},
151+
152+
__fileSelected: function() {
153+
let selectedItem = this.__mainTree.getSelection();
154+
if ("path" in selectedItem[0]) {
155+
const data = {
156+
filePath: selectedItem[0].path
157+
};
158+
this.fireDataEvent("FileSelected", data);
159+
}
160+
},
161+
162+
__addTreeItem: function(tree, data) {
163+
let treeItem = this.__configureTreeItem(new qx.ui.tree.TreeFile(), data.name, data);
164+
tree.add(treeItem);
165+
166+
return treeItem;
167+
},
168+
169+
__configureTreeItem: function(treeItem, label, extraInfo) {
170+
// A left-justified icon
171+
treeItem.addWidget(new qx.ui.core.Spacer(16, 16));
172+
173+
// Here's our indentation and tree-lines
174+
treeItem.addSpacer();
175+
176+
if (treeItem instanceof qx.ui.tree.TreeFolder) {
177+
treeItem.addOpenButton();
178+
}
179+
180+
// The standard tree icon follows
181+
treeItem.addIcon();
182+
183+
// The label
184+
treeItem.addLabel(label);
185+
186+
// All else should be right justified
187+
treeItem.addWidget(new qx.ui.core.Spacer(), {
188+
flex: 1
189+
});
190+
191+
if (treeItem instanceof qx.ui.tree.TreeFile) {
192+
// Add a file size, date and mode
193+
const formattedSize = qxapp.utils.Utils.formatBytes(extraInfo.size);
194+
let text = new qx.ui.basic.Label(formattedSize);
195+
text.setWidth(80);
196+
treeItem.addWidget(text);
197+
198+
text = new qx.ui.basic.Label((new Date(extraInfo.lastModified)).toUTCString());
199+
text.setWidth(200);
200+
treeItem.addWidget(text);
201+
}
202+
203+
return treeItem;
204+
}
205+
}
206+
});

services/web/client/source/class/qxapp/data/Fake.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ qx.Class.define("qxapp.data.Fake", {
2222
prjId: null
2323
}),
2424

25+
getUsername: function() {
26+
return "bizzy";
27+
},
28+
29+
getS3PublicBucketName: function() {
30+
return "simcore";
31+
},
32+
2533
/**
2634
* Returns a qx array with projects associated to a user
2735
*/
@@ -614,23 +622,11 @@ qx.Class.define("qxapp.data.Fake", {
614622
"port": null
615623
}
616624
}, {
617-
"key": "FileReader",
625+
"key": "FileManager",
618626
"tag": "1.0",
619-
"name": "File Reader",
620-
"desc": "File Reader",
621-
"inputs": [{
622-
"key": "in_1",
623-
"label": "File",
624-
"desc": "File",
625-
"type": "string",
626-
"defaultValue": null
627-
}, {
628-
"key": "in_2",
629-
"label": "File type",
630-
"desc": "File type",
631-
"type": "string",
632-
"defaultValue": ""
633-
}],
627+
"name": "File Manager",
628+
"desc": "File Manager",
629+
"inputs": [],
634630
"outputs": [{
635631
"key": "out_1",
636632
"label": "File-url",

services/web/client/source/class/qxapp/utils/Utils.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ qx.Class.define("qxapp.utils.Utils", {
3434

3535
getKeyByValue(object, value) {
3636
return Object.keys(object).find(key => object[key] === value);
37+
},
38+
39+
formatBytes: function(bytes) {
40+
const precision = 2;
41+
if (bytes < 1024) {
42+
return bytes + " B";
43+
} else if (bytes < (1024*1024)) {
44+
return (bytes / 1024).toFixed(precision) + " KB";
45+
} else if (bytes < (1024*1024*1024)) {
46+
return (bytes / (1024*1024)).toFixed(precision) + " MB";
47+
}
48+
return (bytes / (1024*1024*1024)).toFixed(precision) + " GB";
3749
}
3850
}
3951
});

services/web/client/source/class/qxapp/wrappers/SvgWrapper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ qx.Class.define("qxapp.wrappers.SvgWrapper", {
7272
});
7373
path.marker("start", marker1);
7474

75-
const arrowSize = 5;
75+
const arrowSize = 4;
7676
let marker2 = draw.marker(arrowSize, arrowSize, function(add) {
7777
add.path("M 0 0 V 4 L 2 2 Z")
7878
.fill(linkColor)

services/web/client/source/class/qxapp/wrappers/WebSocket.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
/* global window */
88
/* global io */
9-
/* eslint valid-jsdoc: "error" */
10-
/* eslint-env es6 */
119

1210
qx.Class.define("qxapp.wrappers.WebSocket", {
1311
extend: qx.core.Object,
@@ -212,6 +210,14 @@ qx.Class.define("qxapp.wrappers.WebSocket", {
212210
}
213211
}
214212
return false;
213+
},
214+
215+
removeSlot: function(name) {
216+
var index = this.__name.indexOf(name);
217+
if (index > -1) {
218+
this.getSocket().removeAllListeners(this.__name[index]);
219+
this.__name.splice(index, 1);
220+
}
215221
}
216222
},
217223

services/web/server/requirements/common.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ psycopg2==2.7.4
77
python-socketio==1.9.0
88
requests==2.19.0
99
sqlalchemy==1.2.8
10+
minio==4.0.1

0 commit comments

Comments
 (0)