-
Notifications
You must be signed in to change notification settings - Fork 0
Create an OutputDisplay
Before we talk about output displays, let's make sure it is clear what is going on when a code cell is evaluated.
When you open and look at a Beaker notebook, it usually contains one or a couple code cells
, each code cell has two parts: input and output. The input part is where user enter code. The output part is where the result of an evaluation show up. (Note that the output part could be invisible if the code cell has not been evaluate, and hence has empty output).
Behind each code cell, there is a code cell model, which is a JavaScript object. And the code cell model has 2 main sub-parts: the input model and the output model. Here is an example of a code cell model:
{
"id": "code001",
"type": "code",
"evaluator": "IPython",
"input": {
"body": "import pandas as pd\ndf = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B'])\ndf"
},
"output": {
"selectedType": "Table",
"result": {
"type": "TableDisplay",
"tableDisplayModel": {
"columnNames": ["", "A", "B"],
"values": [
["0", " 1", " 2"],
["1", " 1", " 3"],
["2", " 4", " 6"]]
},
"columnNames": ["", "A", "B"],
"values": [
["0", " 1", " 2"],
["1", " 1", " 3"],
["2", " 4", " 6"]]
},
"outputArrived": true,
"elapsedTime": 208
}
}
When user adds a code cell to the notebook, a cell model with empty input and output model will be created and added to the notebook model. When user enter code in the UI, the code is stored in the code cell input.body
. And when a code cell is evaluated, the evaluation result returned from the evaluator will be recored to output.result
, In the example above, the "output" model has a property "result", which has a "type".
Note, You can debug the notebook model by going to menu bar, Notebook > Plugin Manager, a dialog should popup, in the popped up dialog, click the 'Menus' tab, and make sure the text box content is ./plugin/menu/debug.js, click the button Add Plugin From URL. Click the "Close" button to exit the dialog. Now the "Debug" menu will show up in the menu bar. Click the menu item "Debug" to enter debug model.
Upon the result is received and attached to the code cell output model, This is where OutputDisplays come in. Beaker will look at the type of the result and use a map, from output type to a list OutputDisplay names, to determine how to render and present the output to the user. The reason that it is a 'list' of names instead of a single name is because that there could be a couple output displays that are all applicable to a single return type. For example, looking at outputdisplayfactory-service.js#L168:
"TableDisplay": ["Table", "Text"]
you will find that the output result type - "TableDisplay" is mapped to 2 available OutputDisplays: "Table" and "Text". And since "Table" is the first in the list, it will be used as the default OutputDisplay. User can use the cell menu to switch between available output displays through UI.
The output display are written and should be encapsulated in a JavaScript file that would be loaded, registered, and used by Beaker.
Beaker, by default, loads a couple output displays. You can find them in this directory: https://github.com/twosigma/beaker-notebook/tree/master/core/src/main/web/outputdisplay
Beaker OutputDisplay is written in the similar format as AngularJS Directives. Here is an example:
(function () {
'use strict';
beaker.bkoDirective("SimpleExample", function () {
return {
template: "<div>" +
"<span>Hi, {{ name }}</span>" +
"<img src='http://www.twosigma.com/assets/images/logo.png'>" +
"<span>The raw output model is {{rawoutput}}</span>" +
"</div>",
link: function (scope, element, attrs) {
scope.name = "there";
scope.rawOutput = JSON.stringify(scope.model.getCellModel());
}
};
});
})();
The 2 important parts here are the template
and the link
function. And the result in the aforementioned output model is available through scope.model.getCellModel(); As for how {{}}
in the template work with the link function, please consult the AngularJS directive documentation.
Note that we should be able to provide UI to load OutputDisplay plugins as well as provide ways for specifying a list of default output display as preference soon. Before that happens, here is how you can dynamically load a plugin.
First start Beaker, and the click on the "New Notebook" button
A notebook is created, in the input cell, enter the following:
import pandas as pd
df = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B'])
df
and then Ctrl + Enter
to evaluate the code. Upon evaluation finishes, your notebook should look like this:
That is, the cell evaluation returns a result of type "TableDisplay" and Beaker picked it up and use the default mapping to identify that the OutputDisplay it should be using is "Table". Hence a table is displayed.
Keep this notebook opened.
Now, let's override this behavior with a custom OutputDisplay. The goal here is, we want evaluation result of type "TableDisplay" to be displayed differently.
create a new file with this content:
(function () {
'use strict';
beaker.bkoDirective("SimpleExample", function () {
return {
template: "<div>" +
"<span>Hi, {{ name }}</span>" +
"<img src='http://www.twosigma.com/assets/images/logo.png'>" +
"<span>The raw output model is {{rawoutput}}</span>" +
"</div>",
link: function (scope, element, attrs) {
scope.name = "there";
scope.rawOutput = JSON.stringify(scope.model.getCellModel());
}
};
});
})();
and put the file to somewhere that can be reference with an URL. Here is an example:
http://anglee.org/temp/bkoSimpleExample.js
Go back to Beaker, where the notebook left opened, open the console (in Chrome, go to menu, Tools > JavaScript Console, or ctrl + shift + j
, or ⌘ + ⌥ + j
). Enter the following in the console:
bkHelper.loadJS("http://anglee.org/temp/bkoSimpleExample.js"); // or replace with the URL you stored the OutputDisplay code.
now our "SimpleExample" OutputDisplay is loaded, there is still one more thing need to be done. That is, we need to tell Beaker to use it or make it available when the type of result in output model is of type "TableDisplay". Remember before we changed the mapping, "TableDisplay" is mapped to ["Table", "Text"]
, we want to make it so that the mapping becomes ["SimpleExample", "Table", "Text"]
after.
This can be done by running the following in the browser console:
beaker.registerOutputDisplay("TableDisplay", ["SimpleExample"], 0);
The line above basically says, for mapping of the output result type "TableDisplay", insert the list ["SimpleExample"] to its mapping at index 0.
Now, move focus back to the input cell and run the code cell again (Ctrl+Enter), you should see Beaker use our SimpleExample OutputDisplay now: