Repository: incubator-zeppelin Updated Branches: refs/heads/master f162615fb -> fa5057cfd
ZEPPELIN-371 : Import a notebook Import a notebook by either an external URL or uploading a valid JSON. <img width="429" alt="screen shot 2015-10-28 at 4 53 58 pm" src="https://cloud.githubusercontent.com/assets/674497/10787159/c15c0dd6-7d94-11e5-9617-29d2361d046b.png"> <img width="1335" alt="screen shot 2015-10-28 at 4 56 30 pm" src="https://cloud.githubusercontent.com/assets/674497/10787208/046af056-7d95-11e5-99a5-3387fee393ef.png"> <img width="1171" alt="screen shot 2015-10-28 at 4 56 55 pm" src="https://cloud.githubusercontent.com/assets/674497/10787209/046e05fc-7d95-11e5-813d-ec3856a5c207.png"> <img width="1440" alt="screen shot 2015-10-28 at 4 55 29 pm" src="https://cloud.githubusercontent.com/assets/674497/10787158/c15be87e-7d94-11e5-9cde-39c0008a53bb.png"> Author: Prabhjyot Singh <[email protected]> Closes #374 from prabhjyotsingh/ZEPPELIN-371 and squashes the following commits: 35e1e02 [Prabhjyot Singh] review changes; uniform naming: renaming IMPORT_NOTEBOOK to IMPORT_NOTE 246ae8b [Prabhjyot Singh] Fixing test case 3b8816f [Prabhjyot Singh] Import a notebook as JSON file or external URL Project: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/commit/fa5057cf Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/fa5057cf Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/fa5057cf Branch: refs/heads/master Commit: fa5057cfd084ed36093fe032faad7f59ff22c28e Parents: f162615 Author: Prabhjyot Singh <[email protected]> Authored: Tue Nov 24 11:28:33 2015 +0530 Committer: Lee moon soo <[email protected]> Committed: Wed Nov 25 02:52:25 2015 +0900 ---------------------------------------------------------------------- .../org/apache/zeppelin/socket/Message.java | 2 + .../apache/zeppelin/socket/NotebookServer.java | 69 +++- .../java/org/apache/zeppelin/ZeppelinIT.java | 2 +- .../zeppelin/socket/NotebookServerTest.java | 21 + zeppelin-web/src/app/home/home.css | 382 ++++++++++++++++++- zeppelin-web/src/app/home/home.html | 2 + .../noteName-import/note-import-dialog.html | 76 ++++ .../notenameImport.controller.js | 110 ++++++ .../websocketEvents/websocketMsg.service.js | 9 + zeppelin-web/src/index.html | 4 + 10 files changed, 667 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java ---------------------------------------------------------------------- diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java index c51cfe3..7a1c749 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java @@ -55,6 +55,8 @@ public class Message { CLONE_NOTE, // [c-s] clone new notebook // @param id id of note to clone // @param name name fpor the cloned note + IMPORT_NOTE, // [c-s] import notebook + // @param object notebook NOTE_UPDATE, RUN_PARAGRAPH, // [c-s] run paragraph http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java ---------------------------------------------------------------------- diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java index 654d782..114582f 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java @@ -16,20 +16,17 @@ */ package org.apache.zeppelin.socket; import java.io.IOException; -import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import javax.servlet.http.HttpServletRequest; + import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars; import org.apache.zeppelin.display.AngularObject; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.AngularObjectRegistryListener; +import org.apache.zeppelin.display.Input; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterSetting; import org.apache.zeppelin.notebook.JobListenerFactory; @@ -119,6 +116,9 @@ public class NotebookServer extends WebSocketServlet implements case CLONE_NOTE: cloneNote(conn, notebook, messagereceived); break; + case IMPORT_NOTE: + importNote(conn, notebook, messagereceived); + break; case COMMIT_PARAGRAPH: updateParagraph(conn, notebook, messagereceived); break; @@ -171,7 +171,7 @@ public class NotebookServer extends WebSocketServlet implements } } - private Message deserializeMessage(String msg) { + protected Message deserializeMessage(String msg) { return gson.fromJson(msg, Message.class); } @@ -467,6 +467,61 @@ public class NotebookServer extends WebSocketServlet implements broadcastNoteList(); } + protected Note importNote(NotebookSocket conn, Notebook notebook, Message fromMessage) + throws IOException { + + Note note = notebook.createNote(); + if (fromMessage != null) { + String noteName = (String) ((Map) fromMessage.get("notebook")).get("name"); + if (noteName == null || noteName.isEmpty()) { + noteName = "Note " + note.getId(); + } + note.setName(noteName); + ArrayList<Map> paragraphs = ((Map<String, ArrayList>) fromMessage.get("notebook")) + .get("paragraphs"); + if (paragraphs.size() > 0) { + for (Map paragraph : paragraphs) { + try { + Paragraph p = note.addParagraph(); + String text = (String) paragraph.get("text"); + p.setText(text); + p.setTitle((String) paragraph.get("title")); + Map<String, Object> params = (Map<String, Object>) ((Map) paragraph + .get("settings")).get("params"); + Map<String, Input> forms = (Map<String, Input>) ((Map) paragraph + .get("settings")).get("forms"); + if (params != null) { + p.settings.setParams(params); + } + if (forms != null) { + p.settings.setForms(forms); + } + Map<String, Object> result = (Map) paragraph.get("result"); + if (result != null) { + InterpreterResult.Code code = InterpreterResult.Code + .valueOf((String) result.get("code")); + InterpreterResult.Type type = InterpreterResult.Type + .valueOf((String) result.get("type")); + String msg = (String) result.get("msg"); + p.setReturn(new InterpreterResult(code, type, msg), null); + } + + Map<String, Object> config = (Map<String, Object>) paragraph + .get("config"); + p.setConfig(config); + } catch (Exception e) { + LOG.error("Exception while setting parameter in paragraph", e); + } + } + } + } + + note.persist(); + broadcastNote(note); + broadcastNoteList(); + return note; + } + private void removeParagraph(NotebookSocket conn, Notebook notebook, Message fromMessage) throws IOException { final String paragraphId = (String) fromMessage.get("id"); http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java ---------------------------------------------------------------------- diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java index dc188f8..c1b10ce 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java @@ -299,7 +299,7 @@ public class ZeppelinIT { notebookTitles.add(el.getText()); } - WebElement createNoteLink = driver.findElement(By.xpath("//div[contains(@class, \"col-md-4\")]/div/h5/a")); + WebElement createNoteLink = driver.findElement(By.xpath("//div[contains(@class, \"col-md-4\")]/div/h5/a[contains(.,'Create new note')]")); createNoteLink.click(); WebDriverWait block = new WebDriverWait(driver, 10); http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java ---------------------------------------------------------------------- diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java index 5275d81..faef287 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java @@ -136,6 +136,27 @@ public class NotebookServerTest extends AbstractTestRestApi { notebook.removeNote(note1.getId()); } + @Test + public void testImportNotebook() throws IOException { + String msg = "{\"op\":\"IMPORT_NOTE\",\"data\":" + + "{\"notebook\":{\"paragraphs\": [{\"text\": \"Test " + + "paragraphs import\",\"config\":{},\"settings\":{}}]," + + "\"name\": \"Test Zeppelin notebook import\",\"config\": " + + "{}}}}"; + Message messageReceived = notebookServer.deserializeMessage(msg); + Note note = null; + try { + note = notebookServer.importNote(null, notebook, messageReceived); + } catch (NullPointerException e) { + //broadcastNoteList(); failed nothing to worry. + } + + assertNotEquals(null, notebook.getNote(note.getId())); + assertEquals("Test Zeppelin notebook import", notebook.getNote(note.getId()).getName()); + assertEquals("Test paragraphs import", notebook.getNote(note.getId()).getParagraphs().get(0).getText()); + notebook.removeNote(note.getId()); + } + private NotebookSocket createWebSocket() { NotebookSocket sock = mock(NotebookSocket.class); when(sock.getRequest()).thenReturn(createHttpServletRequest()); http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-web/src/app/home/home.css ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/home/home.css b/zeppelin-web/src/app/home/home.css index 0c62279..70baee9 100644 --- a/zeppelin-web/src/app/home/home.css +++ b/zeppelin-web/src/app/home/home.css @@ -130,30 +130,37 @@ a.navbar-brand:hover { .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #D3D3D3; } + .navbar-nav .open .dropdown-menu > .scrollbar-container > li > a { padding: 5px 15px 5px 25px; line-height: 20px; } + .navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > li > a { color: #D3D3D3; } + .navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > li > a:focus { color: #fff; background-color: transparent; } + .navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > .active > a:focus { color: #fff; background-color: #080808; } - .server-status{ + + .server-status { float: right; } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { background-color: #3071A9; } + .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { border-color: #3071A9; } @@ -210,7 +217,7 @@ a.navbar-brand:hover { .box-heading { position: relative; max-width: 100%; - font-weight:300; + font-weight: 300; white-space: nowrap; text-overflow: ellipsis; vertical-align: middle; @@ -310,3 +317,374 @@ This part should be removed when new version of bootstrap handles this issue. .btn-group > .popover + .btn { margin-left: -1px; } + +.display-inline { + display: inline; + float: left; +} + +#noteImportModal .modal-body { + min-height: 420px; + overflow: hidden; +} + +#noteImportModal .modal-footer { + min-height: 65px; +} + +#noteImportModal .display-inline a { + background-color: #fff; + border: 1px solid #ddd; + border-radius: 5px; + color: #999; + cursor: pointer; + display: block; + float: left; + font-size: 98px; + height: 240px; + margin: 0 10px 16px; + padding-top: 60px; + text-align: center; + text-decoration: none; + width: 264px; +} + +#noteImportModal .display-inline a:hover { + background-color: #eee; +} + +#noteImportModal .display-inline a p { + font-size: 14px; +} + +/* ------------------------------------------- */ +/* Slide Top +/* ------------------------------------------- */ +.slide-top { + -webkit-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -moz-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -ms-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -o-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + /* easeOutQuad */ + -webkit-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -moz-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -ms-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -o-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + /* easeOutQuad */ +} + +.slide-top.ng-enter { + transform: translateY(60px); + -ms-transform: translateY(60px); + -webkit-transform: translateY(60px); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 0; +} + +.slide-top.ng-enter-active { + transform: translateY(0); + -ms-transform: translateY(0); + -webkit-transform: translateY(0); + opacity: 1; +} + +.slide-top.ng-leave { + transform: translateY(0); + -ms-transform: translateY(0); + -webkit-transform: translateY(0); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 1; +} + +.slide-top.ng-leave-active { + transform: translateY(60px); + -ms-transform: translateY(60px); + -webkit-transform: translateY(60px); + opacity: 0; +} + +.slide-top.ng-hide-add { + transform: translateY(0); + -ms-transform: translateY(0); + -webkit-transform: translateY(0); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 1; +} + +.slide-top.ng-hide-add.ng-hide-add-active { + transform: translateY(60px); + -ms-transform: translateY(60px); + -webkit-transform: translateY(60px); + opacity: 0; +} + +.slide-top.ng-hide-remove { + transform: translateY(60px); + -ms-transform: translateY(60px); + -webkit-transform: translateY(60px); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + display: block !important; + opacity: 0; +} + +.slide-top.ng-hide-remove.ng-hide-remove-active { + transform: translateY(0); + -ms-transform: translateY(0); + -webkit-transform: translateY(0); + opacity: 1; +} + +/* ------------------------------------------- */ +/* Slide Right +/* ------------------------------------------- */ +.slide-right { + -webkit-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -moz-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -ms-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -o-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + /* easeOutQuad */ + -webkit-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -moz-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -ms-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -o-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + /* easeOutQuad */ +} + +.slide-right.ng-enter { + transform: translateX(60px); + -ms-transform: translateX(60px); + -webkit-transform: translateX(60px); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 0; +} + +.slide-right.ng-enter-active { + transform: translateX(0); + -ms-transform: translateX(0); + -webkit-transform: translateX(0); + opacity: 1; +} + +.slide-right.ng-leave { + transform: translateX(0); + -ms-transform: translateX(0); + -webkit-transform: translateX(0); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 1; +} + +.slide-right.ng-leave-active { + transform: translateX(60px); + -ms-transform: translateX(60px); + -webkit-transform: translateX(60px); + opacity: 0; +} + +.slide-right.ng-hide-add { + transform: translateX(0); + -ms-transform: translateX(0); + -webkit-transform: translateX(0); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 1; +} + +.slide-right.ng-hide-add.ng-hide-add-active { + transform: translateX(60px); + -ms-transform: translateX(60px); + -webkit-transform: translateX(60px); + opacity: 0; +} + +.slide-right.ng-hide-remove { + transform: translateX(60px); + -ms-transform: translateX(60px); + -webkit-transform: translateX(60px); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + display: block !important; + opacity: 0; +} + +.slide-right.ng-hide-remove.ng-hide-remove-active { + transform: translateX(0); + -ms-transform: translateX(0); + -webkit-transform: translateX(0); + opacity: 1; +} + +/* ------------------------------------------- */ +/* Slide Left +/* ------------------------------------------- */ +.slide-left { + -webkit-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -moz-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -ms-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -o-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + /* easeOutQuad */ + -webkit-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -moz-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -ms-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -o-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + /* easeOutQuad */ +} + +.slide-left.ng-enter { + transform: translateX(-60px); + -ms-transform: translateX(-60px); + -webkit-transform: translateX(-60px); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 0; +} + +.slide-left.ng-enter-active { + transform: translateX(0); + -ms-transform: translateX(0); + -webkit-transform: translateX(0); + opacity: 1; +} + +.slide-left.ng-leave { + transform: translateX(0); + -ms-transform: translateX(0); + -webkit-transform: translateX(0); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 1; +} + +.slide-left.ng-leave-active { + transform: translateX(-60px); + -ms-transform: translateX(-60px); + -webkit-transform: translateX(-60px); + opacity: 0; +} + +.slide-left.ng-hide-add { + transform: translateX(0); + -ms-transform: translateX(0); + -webkit-transform: translateX(0); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 1; +} + +.slide-left.ng-hide-add.ng-hide-add-active { + transform: translateX(-60px); + -ms-transform: translateX(-60px); + -webkit-transform: translateX(-60px); + opacity: 0; +} + +.slide-left.ng-hide-remove { + transform: translateX(-60px); + -ms-transform: translateX(-60px); + -webkit-transform: translateX(-60px); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + display: block !important; + opacity: 0; +} + +.slide-left.ng-hide-remove.ng-hide-remove-active { + transform: translateX(0); + -ms-transform: translateX(0); + -webkit-transform: translateX(0); + opacity: 1; +} + +/* ------------------------------------------- */ +/* Slide Down +/* ------------------------------------------- */ +.slide-down { + -webkit-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -moz-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -ms-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + -o-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94); + /* easeOutQuad */ + -webkit-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -moz-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -ms-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + -o-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + /* easeOutQuad */ +} + +.slide-down.ng-enter { + transform: translateY(-60px); + -ms-transform: translateY(-60px); + -webkit-transform: translateY(-60px); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 0; +} + +.slide-down.ng-enter-active { + transform: translateY(0); + -ms-transform: translateY(0); + -webkit-transform: translateY(0); + opacity: 1; +} + +.slide-down.ng-leave { + transform: translateY(0); + -ms-transform: translateY(0); + -webkit-transform: translateY(0); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 1; +} + +.slide-down.ng-leave-active { + transform: translateY(-60px); + -ms-transform: translateY(-60px); + -webkit-transform: translateY(-60px); + opacity: 0; +} + +.slide-down.ng-hide-add { + transform: translateY(0); + -ms-transform: translateY(0); + -webkit-transform: translateY(0); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + opacity: 1; +} + +.slide-down.ng-hide-add.ng-hide-add-active { + transform: translateY(-60px); + -ms-transform: translateY(-60px); + -webkit-transform: translateY(-60px); + opacity: 0; +} + +.slide-down.ng-hide-remove { + transform: translateY(-60px); + -ms-transform: translateY(-60px); + -webkit-transform: translateY(-60px); + transition-duration: 250ms; + -webkit-transition-duration: 250ms; + display: block !important; + opacity: 0; +} + +.slide-down.ng-hide-remove.ng-hide-remove-active { + transform: translateY(0); + -ms-transform: translateY(0); + -webkit-transform: translateY(0); + opacity: 1; +} http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-web/src/app/home/home.html ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/home/home.html b/zeppelin-web/src/app/home/home.html index cdefa69..56e6dc4 100644 --- a/zeppelin-web/src/app/home/home.html +++ b/zeppelin-web/src/app/home/home.html @@ -29,6 +29,8 @@ limitations under the License. <h4>Notebook</h4> <div> + <h5><a href="" data-toggle="modal" data-target="#noteImportModal" style="text-decoration: none;"> + <i style="font-size: 15px;" class="fa fa-upload"></i> Import note</a></h5> <h5><a href="" data-toggle="modal" data-target="#noteNameModal" style="text-decoration: none;"> <i style="font-size: 15px;" class="icon-notebook"></i> Create new note</a></h5> <ul style="list-style-type: none;"> http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-web/src/components/noteName-import/note-import-dialog.html ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/components/noteName-import/note-import-dialog.html b/zeppelin-web/src/components/noteName-import/note-import-dialog.html new file mode 100644 index 0000000..524e4d2 --- /dev/null +++ b/zeppelin-web/src/components/noteName-import/note-import-dialog.html @@ -0,0 +1,76 @@ +<!-- +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + + <div id="noteImportModal" class="modal fade" role="dialog" + tabindex='-1'> + <div class="modal-dialog"> + + <!-- Modal content--> + <div class="modal-content" id="NoteImportCtrl" ng-init="NoteImportInit"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal">×</button> + <h4 class="modal-title">Import new note</h4> + </div> + <div class="modal-body"> + + <div class="form-group"> + <label for="noteImportName">Import AS</label> + <input placeholder="Note name" type="text" class="form-control" id="noteImportName" + ng-model="note.noteImportName"> + </div> + + <div class="form-group" ng-show="note.errorText"> + <div class="alert alert-danger">{{note.errorText}}</div> + </div> + + <div class="form-group slide-left" ng-show="note.step1"> + <div class="display-inline"> + <a class="fa fa-cloud-upload import-file-upload" ng-click="uploadFile()"> + <p>Choose a JSON here</p> + </a> + </div> + <div style="display: none"> + <input placeholder="Note name" type="file" class="form-control" id="noteImportFile" + ng-model="note.importFile" onchange="angular.element(this).scope().importFile(this)"> + </div> + <div class="display-inline"> + <a href="javascript:void(0);" ng-click="uploadURL()" class="fa fa-link"> + <p>Add from URL</p> + </a> + </div> + </div> + + <div class="form-group slide-right" ng-show="note.step2"> + + <label for="noteImportUrl">URL</label> + <input placeholder="Note name" type="text" class="form-control" id="noteImportUrl" + ng-model="note.importUrl"> + </div> + + </div> + <div class="modal-footer"> + <div ng-show="note.step2"> + <button type="button" id="importBackButton" + class="btn btn-default" + ng-click="noteimportctrl.importBack()">Back + </button> + <button type="button" id="importNoteButton" + class="btn btn-default" + ng-click="noteimportctrl.importNote()">Import Note + </button> + </div> + </div> + </div> + </div> + </div> http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-web/src/components/noteName-import/notenameImport.controller.js ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/components/noteName-import/notenameImport.controller.js b/zeppelin-web/src/components/noteName-import/notenameImport.controller.js new file mode 100644 index 0000000..d48179d --- /dev/null +++ b/zeppelin-web/src/components/noteName-import/notenameImport.controller.js @@ -0,0 +1,110 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +angular.module('zeppelinWebApp').controller('NoteImportCtrl', function($scope, $timeout, websocketMsgSrv) { + var vm = this; + $scope.note = {}; + $scope.note.step1 = true; + $scope.note.step2 = false; + + vm.resetFlags = function() { + $scope.note = {}; + $scope.note.step1 = true; + $scope.note.step2 = false; + angular.element('#noteImportFile').val(''); + }; + + $scope.uploadFile = function() { + angular.element('#noteImportFile').click(); + }; + + $scope.importFile = function(element) { + $scope.note.errorText = ''; + $scope.note.importFile = element.files[0]; + var file = $scope.note.importFile; + var reader = new FileReader(); + + reader.onloadend = function() { + vm.processImportJson(reader.result); + }; + + if (file) { + reader.readAsText(file); + } + }; + + $scope.uploadURL = function() { + $scope.note.errorText = ''; + $scope.note.step1 = false; + $timeout(function() { + $scope.note.step2 = true; + }, 400); + }; + + vm.importBack = function() { + $scope.note.errorText = ''; + $timeout(function() { + $scope.note.step1 = true; + }, 400); + $scope.note.step2 = false; + }; + + vm.importNote = function() { + $scope.note.errorText = ''; + if ($scope.note.importUrl) { + jQuery.getJSON($scope.note.importUrl, function(result) { + vm.processImportJson(result); + }).fail(function() { + $scope.note.errorText = 'Unable to Fetch URL'; + $scope.$apply(); + }); + } + else { + $scope.note.errorText = 'Enter URL'; + $scope.$apply(); + } + }; + + vm.processImportJson = function(result) { + if (typeof result !== 'object') { + try { + result = JSON.parse(result); + } catch (e) { + $scope.note.errorText = 'JSON parse exception'; + $scope.$apply(); + return; + } + + } + if (result.paragraphs && result.paragraphs.length > 0) { + if (!$scope.note.noteImportName) { + $scope.note.noteImportName = result.name; + } else { + result.name = $scope.note.noteImportName; + } + websocketMsgSrv.importNotebook(result); + //angular.element('#noteImportModal').modal('hide'); + } else { + $scope.note.errorText = 'Invalid JSON'; + } + $scope.$apply(); + }; + + $scope.$on('setNoteMenu', function(event, notes) { + vm.resetFlags(); + angular.element('#noteImportModal').modal('hide'); + }); +}); http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js index 6022543..a10bc87 100644 --- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js +++ b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js @@ -113,6 +113,15 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope, }); }, + importNotebook: function(notebook) { + websocketEvents.sendNewEvent({ + op: 'IMPORT_NOTE', + data: { + notebook: notebook + } + }); + }, + isConnected: function(){ return websocketEvents.isConnected(); } http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/fa5057cf/zeppelin-web/src/index.html ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html index b8dd489..0d065a5 100644 --- a/zeppelin-web/src/index.html +++ b/zeppelin-web/src/index.html @@ -73,6 +73,9 @@ limitations under the License. <div ng-controller="NotenameCtrl as notenamectrl"> <div id="note-modal-container" ng-include src="'components/noteName-create/note-name-dialog.html'"></div> </div> + <div ng-controller="NoteImportCtrl as noteimportctrl"> + <div id="note-import-container" ng-include src="'components/noteName-import/note-import-dialog.html'"></div> + </div> <!-- build:js(.) scripts/oldieshim.js --> <!--[if lt IE 9]> <script src="bower_components/es5-shim/es5-shim.js"></script> @@ -129,6 +132,7 @@ limitations under the License. <script src="components/navbar/navbar.controller.js"></script> <script src="components/ngescape/ngescape.directive.js"></script> <script src="components/noteName-create/notename.controller.js"></script> + <script src="components/noteName-import/notenameImport.controller.js"></script> <script src="components/popover-html-unsafe/popover-html-unsafe.directive.js"></script> <script src="components/ngenter/ngenter.directive.js"></script> <script src="components/dropdowninput/dropdowninput.directive.js"></script>
