Commit 83b4c91b authored by Sebastian's avatar Sebastian

Update: README and add many comments to the source code

parent 0689d482
# Notes # Concept:
It is required to overwrite NotebookApp.contents_manager_class to avoid a * Inject rsvp, renderJS and jIO into the default jupyter page (after it is rendered) and manually bootstrap renderJS. Entrypoint is jiocontents.js which is loaded by jupyter as part of the nbextension
* This injection is done with the mother-gadget gadget_jupyter_page which has the subgadget gadget_jiocontents responsible for handling the API calls which come from jupyter to the jiocontentsmanager
## Exmaple of API call processing:
1. User presses "Create New Notebook Button" and fills in the form
2. Jupyter internally calls the contents API which is rerouted to this javascript nbextension "jiocontents"
3. The function `Contents.prototype.new_untitled` is called
4. We wait for the waitForReadyPromise resolve (resolves once renderJS injection is complete) or reject after a timeout of 3s
5. A custom event is dispatched to the gadget_jiocontents gadget which has access to JIO containing the information jupyter provides (here only the path of the directory from which the new notebook was created).
Additionally we supply handles to the resolve / reject functions of the wrapping promise so gadget_jiocontents can resolve the wrapping promise after the information was exchanged with JIO
6. In gadget_jiocontents we create an empty notebook json and jio.post it to ERP5
7. If this JIO action succeedes, we resolve the wrapping promise with the notebook json which is then further processed internally by jupyter
All jupyter-actions are processed in this way. We create a wrapping promise and pass the work on to gadget_jiocontents, which communicates via JIO and resolves/rejects
this promise based on the communication with the ERP5.
# Important Notes
* It is required to overwrite NotebookApp.contents_manager_class to avoid a
immediate redirect to 404 page upon notebook-loading. Currently this is done immediate redirect to 404 page upon notebook-loading. Currently this is done
inline in jupyter_notebook_config.py, but should better be moved to a seperate inline in jupyter_notebook_config.py, but should better be moved to a seperate
python file and imported in the config. However, I was unable to get the include python file and imported in the config. However, I was unable to get the include
paths right with slapos. paths right with slapos.
A lot of weirdness comes with loading notebooks into the page. de-stringyfied * A lot of weirdness comes with loading notebooks into the page. de-stringyfied
JSONs must be further processed because of unlined-strings. See JSONs must be further processed because of unlined-strings. See
https://github.com/jupyter/jupyter-drive/blob/master/jupyterdrive/gdrive/notebook_model.js#L45 https://github.com/jupyter/jupyter-drive/blob/master/jupyterdrive/gdrive/notebook_model.js#L45
https://github.com/jupyter/jupyter-drive/pull/124 https://github.com/jupyter/jupyter-drive/pull/124
\ No newline at end of file
/*global window, rJS, jIO, FormData */ /*global window, rJS, jIO, FormData */
/*jslint indent: 2, maxerr: 3 */ /*jslint indent: 2, maxerr: 3 */
(function (window, rJS) { (function (window, rJS) {
"use strict"; "use strict";
/*
Next two functions deal with a weird issue of unlin-ed strings, ie. list function createBlankNotebook(kernel_type) {
of strings instead a single joined string. Adpoted from return {
https://github.com/jupyter/jupyter-drive/blob/master/jupyterdrive/gdrive/notebook_model.js#L45 "cells": [
with adjustment to match updates in jupyter's data model. { "cell_type": "code",
*/ "execution_count": null,
"metadata": { "collapsed": true },
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
}
},
"nbformat": 4,
"nbformat_minor": 2
};
}
/**
* Next two functions deal with a weird issue of unlin-ed strings, ie. list
* of strings instead a single joined string. Adpoted from
* https://github.com/jupyter/jupyter-drive/blob/master/jupyterdrive/gdrive/notebook_model.js#L45
* with adjustment to match updates in jupyter's data model.
*/
function unsplit_lines(multiline_string) { function unsplit_lines(multiline_string) {
if (Array.isArray(multiline_string)) { if (Array.isArray(multiline_string)) {
return multiline_string.join(''); return multiline_string.join('');
...@@ -40,8 +63,8 @@ ...@@ -40,8 +63,8 @@
}); });
} }
// Convert what we get from ERP5 to the internal jupyter representation
function toNotebookModel(id, obj, isRootDir) { function toNotebookModel(id, obj, isRootDir) {
//console.log(obj.value.text_content);
var cont = null; var cont = null;
var title = obj.title; var title = obj.title;
if(isRootDir) { if(isRootDir) {
...@@ -65,10 +88,13 @@ ...@@ -65,10 +88,13 @@
return nbobj; return nbobj;
} }
/*
/**
* *
*
* Event handlers API <-> JIO connection points * Event handlers API <-> JIO connection points
* *
*
*/ */
function handleGet(e) { function handleGet(e) {
...@@ -79,7 +105,7 @@ ...@@ -79,7 +105,7 @@
query: 'portal_type: "Web JSON"', query: 'portal_type: "Web JSON"',
select_list : ["text_content", "title", "reference", "creation_date", "modification_date"], select_list : ["text_content", "title", "reference", "creation_date", "modification_date"],
sort_on: [["modification_date", "descending"]], sort_on: [["modification_date", "descending"]],
limit: [0, 20] limit: [0, 200]
}) })
.push(function(result) { .push(function(result) {
var nbs = []; var nbs = [];
...@@ -134,7 +160,7 @@ ...@@ -134,7 +160,7 @@
portal_type: "Web JSON", portal_type: "Web JSON",
parent_relative_url: "web_page_module", parent_relative_url: "web_page_module",
title: "untitled_notebook", title: "untitled_notebook",
reference: "ut_nb_from_" + now.getTime(), reference: "ut_nb_from_" + now.getTime(), // this is an arbitrary choice and should be unique
text_content: JSON.stringify(createBlankNotebook()) text_content: JSON.stringify(createBlankNotebook())
}; };
var gadget = this; var gadget = this;
...@@ -172,10 +198,12 @@ ...@@ -172,10 +198,12 @@
}); });
} }
/* /**
* *
* RJS Stuffs *
* RJS Functions
* *
*
*/ */
rJS(window) rJS(window)
...@@ -196,28 +224,6 @@ ...@@ -196,28 +224,6 @@
document.jiocontentsReady = true; document.jiocontentsReady = true;
return this; return this;
}); });
function createBlankNotebook(kernel_type) {
return {
"cells": [
{ "cell_type": "code",
"execution_count": null,
"metadata": { "collapsed": true },
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
}
},
"nbformat": 4,
"nbformat_minor": 2
};
}
}(window, rJS)); }(window, rJS));
/*global window, rJS, RSVP, Handlebars, URI */ /*global window, rJS, RSVP, Handlebars, URI */
/*jslint nomen: true, indent: 2, maxerr: 3 */ /*jslint nomen: true, indent: 2, maxerr: 3 */
/**
*
*
* This is an adaption of the erp5_launcher_nojqm.js in which all UI components
* are removed and a custom hateoas URL calculation based on the instance-parameter
* configuration is added.
*
* Currently it is hardcoded that web_site_module/renderjs_runner is used
* for the hateoas URL. This should be changed and configurable.
*
*
*
*/
(function (window, rJS, RSVP) { (function (window, rJS, RSVP) {
"use strict"; "use strict";
var gadget_klass = rJS(window); var gadget_klass = rJS(window);
// SNIP
function callJioGadget(gadget, method, param_list) { function callJioGadget(gadget, method, param_list) {
var called = false; var called = false;
...@@ -65,12 +79,15 @@ ...@@ -65,12 +79,15 @@
setting[key] = value; setting[key] = value;
} }
// Original:
// Calculate erp5 hateoas url // Calculate erp5 hateoas url
//setting.hateoas_url = (new URI(gadget.props.hateoas_url)) //setting.hateoas_url = (new URI(gadget.props.hateoas_url))
// .absoluteTo(location.href) // .absoluteTo(location.href)
// .toString(); // .toString();
// Read erp5 URL /**
* Set the hateoas URL! XXX Remove the hardcoded hateoas suffix XXX
*/
setting.hateoas_url = _erp5_hateoas_url + "web_site_module/renderjs_runner/hateoas/"; setting.hateoas_url = _erp5_hateoas_url + "web_site_module/renderjs_runner/hateoas/";
if (setting.hasOwnProperty('service_worker_url') && if (setting.hasOwnProperty('service_worker_url') &&
......
// Copyright (c) Jupyter Development Team. // Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License. // Distributed under the terms of the Modified BSD License.
/* /**
* For reference see https://github.com/ipython/ipython/wiki/IPEP-27%3A-Contents-Service * For reference see https://github.com/ipython/ipython/wiki/IPEP-27%3A-Contents-Service
*/ */
...@@ -11,16 +11,27 @@ define(function(require) { ...@@ -11,16 +11,27 @@ define(function(require) {
var $ = require('jquery'); var $ = require('jquery');
var utils = require('base/js/utils'); var utils = require('base/js/utils');
/**
*
*
* Setup for RenderJS
*
*
*/
var baseUrl = "/nbextensions/jiocontents/"; var baseUrl = "/nbextensions/jiocontents/";
var gadget_main_div = document.createElement("div"); // create and append the div element which contains the bootstrap renderjs gadget
gadget_main_div.setAttribute("data-gadget-url", baseUrl + "src/gadget_jupyter_page.html"); var gadgetMainDiv = document.createElement("div");
gadget_main_div.setAttribute("class", "gadget-jupyter-page-container"); gadgetMainDiv.setAttribute("data-gadget-url", baseUrl + "src/gadget_jupyter_page.html");
document.body.append(gadget_main_div); gadgetMainDiv.setAttribute("class", "gadget-jupyter-page-container");
document.body.append(gadgetMainDiv);
// this flag is needed to have an indicator to when all scripts are (asych)
// injected and all gadgets ready. This flag is set to true in render
// function of gadget_jiocontents.js
document.jiocontentsReady = false; document.jiocontentsReady = false;
// inject the scripts into the default jupyter page
var s1 = document.createElement("script"); s1.type = "text/javascript"; var s1 = document.createElement("script"); s1.type = "text/javascript";
s1.onload = function() { s1.onload = function() {
console.log("loaded rsvp"); console.log("loaded rsvp");
...@@ -32,6 +43,8 @@ define(function(require) { ...@@ -32,6 +43,8 @@ define(function(require) {
console.log("loaded erp5 config"); console.log("loaded erp5 config");
rJS.manualBootstrap(); rJS.manualBootstrap();
}; };
// this file comes from the slapos build system and is defined via
// an instance parameter
s3.src = baseUrl + "jiocontents_erp5_config.js"; s3.src = baseUrl + "jiocontents_erp5_config.js";
document.head.append(s3); document.head.append(s3);
}; };
...@@ -41,10 +54,9 @@ define(function(require) { ...@@ -41,10 +54,9 @@ define(function(require) {
s1.src = baseUrl + "src/rsvp.js"; s1.src = baseUrl + "src/rsvp.js";
document.head.append(s1); document.head.append(s1);
/*
.then on this method to be sure the rjs-setup was completed and // .then on this method to be sure the rjs-setup was completed and
.onEvent are available to receive events // all the ".onEvent" are available to receive events
*/
function waitForReadyPromise() { function waitForReadyPromise() {
if(document.jiocontentsReady) { if(document.jiocontentsReady) {
return Promise.resolve(); // rjs-setup is already complete return Promise.resolve(); // rjs-setup is already complete
...@@ -53,58 +65,31 @@ define(function(require) { ...@@ -53,58 +65,31 @@ define(function(require) {
var timerId = window.setInterval(function() { var timerId = window.setInterval(function() {
if(document.jiocontentsReady) { if(document.jiocontentsReady) {
resolve(); resolve();
clearInterval(timerId); clearInterval(timerId); // clear the timeout reject timer
} }
}, 100); // Check every 100ms to see if rjs-setup is complete and eventlistener available }, 100); // Check every 100ms to see if rjs-setup is complete
window.setTimeout(function() { window.setTimeout(function() {
if(!document.jiocontentsReady) { if(!document.jiocontentsReady) {
reject(); reject();
} }
}, 3000) // Reject after 3sec }, 3000) // timeout reject after 3sec
}); });
} }
function getJiocontentsDiv() { function getJiocontentsDiv() {
return document.querySelector(".jiocontents_gadget"); return document.querySelector(".jiocontents_gadget");
} }
var Contents = function(options) {
this.base_url = options.base_url;
};
/** Error type */
Contents.DIRECTORY_NOT_EMPTY_ERROR = 'DirectoryNotEmptyError';
Contents.DirectoryNotEmptyError = function() {
this.message = 'A directory must be empty before being deleted.';
};
Contents.DirectoryNotEmptyError.prototype =
Object.create(Error.prototype);
Contents.DirectoryNotEmptyError.prototype.name =
Contents.DIRECTORY_NOT_EMPTY_ERROR;
Contents.prototype.api_url = function() {
var url_parts = [
this.base_url, 'api/contents',
utils.url_join_encode.apply(null, arguments),
];
return utils.url_path_join.apply(null, url_parts);
};
Contents.prototype.create_basic_error_handler = function(callback) {
if (!callback) {
return utils.log_ajax_error;
}
return function(xhr, status, error) {
callback(utils.wrap_ajax_error(xhr, status, error));
};
};
/** /**
* File Functions (including notebook operations) *
*
* Implementation of contents API functions
*
*
*/ */
// Is called when a notebook should be displayed, either in the list of notebooks
// or the notebook itself
Contents.prototype.get = function (path, options) { Contents.prototype.get = function (path, options) {
return waitForReadyPromise() return waitForReadyPromise()
.then(function() { .then(function() {
...@@ -141,6 +126,7 @@ define(function(require) { ...@@ -141,6 +126,7 @@ define(function(require) {
}); });
}; };
// Currently the jIO Erp5 Storage does not support delete
Contents.prototype.delete = function(path) { Contents.prototype.delete = function(path) {
return Promise.reject("Delete is currently not supported!"); return Promise.reject("Delete is currently not supported!");
}; };
...@@ -152,7 +138,7 @@ define(function(require) { ...@@ -152,7 +138,7 @@ define(function(require) {
var ev = new CustomEvent("rename_event", { var ev = new CustomEvent("rename_event", {
detail: { detail: {
path: path, path: path,
// get rid of path-prefix web_page_module. Not used in titles! // get rid of path-prefix "web_page_module". Not used in titles!
new_title: new_path.split("/")[1], new_title: new_path.split("/")[1],
resolve: resolve, resolve: resolve,
reject: reject reject: reject
...@@ -180,14 +166,13 @@ define(function(require) { ...@@ -180,14 +166,13 @@ define(function(require) {
}); });
}; };
// TODO: implement copy
Contents.prototype.copy = function(from_file, to_dir) { Contents.prototype.copy = function(from_file, to_dir) {
return Promise.reject("Copy is currently not supported!"); return Promise.reject("Copy is currently not supported!");
}; };
/** // Checkpoints are not used in this implementation of the contents API
* Checkpointing Functions // Instead only the save-action is executed
*/
Contents.prototype.create_checkpoint = function(path) { Contents.prototype.create_checkpoint = function(path) {
return Promise.resolve({ return Promise.resolve({
id: "dummy-id", id: "dummy-id",
...@@ -207,10 +192,8 @@ define(function(require) { ...@@ -207,10 +192,8 @@ define(function(require) {
return Promise.resolve(); return Promise.resolve();
}; };
/** // Get a list of all notebooks in the given diretory. In this implementation
* File management functions // we do not support directories other than "" which menas root
*/
Contents.prototype.list_contents = function(path) { Contents.prototype.list_contents = function(path) {
return this.get(path, {type: 'directory'}); return this.get(path, {type: 'directory'});
}; };
......
  • Hi guys are you looking for a website where you can download the latest video for Raksha Bandhan video status download this is the best website because they daily post new video for their visitors

Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment