mirror of
https://github.com/jupyter/notebook.git
synced 2025-01-24 12:05:22 +08:00
Basic notebook saving and loading.
* The logic in the server and javascript frontend is there for a basic JSON notebook format with a .ipynb extension. * To save a new notebook: "%notebook save filename.ipynb" * To save a notebook that is already saved: "%notebook save" * To load a notebook from the cwd: "notebook load filename.ipynb"
This commit is contained in:
parent
b584914bfb
commit
005820c005
@ -126,7 +126,7 @@ class NotebookRootHandler(web.RequestHandler):
|
|||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
files = os.listdir(os.getcwd())
|
files = os.listdir(os.getcwd())
|
||||||
files = [file for file in files if file.endswith(".nb")]
|
files = [file for file in files if file.endswith(".ipynb")]
|
||||||
self.write(json.dumps(files))
|
self.write(json.dumps(files))
|
||||||
|
|
||||||
|
|
||||||
@ -135,7 +135,9 @@ class NotebookHandler(web.RequestHandler):
|
|||||||
SUPPORTED_METHODS = ("GET", "DELETE", "PUT")
|
SUPPORTED_METHODS = ("GET", "DELETE", "PUT")
|
||||||
|
|
||||||
def find_path(self, filename):
|
def find_path(self, filename):
|
||||||
filename = urllib.unquote(filename) + ".nb"
|
filename = urllib.unquote(filename)
|
||||||
|
if not filename.endswith('.ipynb'):
|
||||||
|
raise web.HTTPError(400)
|
||||||
path = os.path.join(os.getcwd(), filename)
|
path = os.path.join(os.getcwd(), filename)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@ -1,26 +1,6 @@
|
|||||||
var IPYTHON = {};
|
var IPYTHON = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// $.get("/notebooks/number2.nb",function (data, status, xhr) {console.log(data);});
|
|
||||||
//
|
|
||||||
// settings = {
|
|
||||||
// processData : false,
|
|
||||||
// cache : false,
|
|
||||||
// type : "DELETE",
|
|
||||||
// success : function (data, status, xhr) {console.log(data);}
|
|
||||||
// }
|
|
||||||
// $.ajax("/notebooks/number2.nb",settings)
|
|
||||||
//
|
|
||||||
// settings = {
|
|
||||||
// processData : false,
|
|
||||||
// cache : false,
|
|
||||||
// type : "PUT",
|
|
||||||
// success : function (data, status, xhr) {console.log(data);}
|
|
||||||
// }
|
|
||||||
// $.ajax("/notebooks/number2.nb",settings)
|
|
||||||
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
// Utilities
|
// Utilities
|
||||||
//============================================================================
|
//============================================================================
|
||||||
@ -82,6 +62,7 @@ function fixConsole(txt) {
|
|||||||
return txt.trim()
|
return txt.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
// Notebook
|
// Notebook
|
||||||
//============================================================================
|
//============================================================================
|
||||||
@ -94,6 +75,10 @@ var Notebook = function (selector) {
|
|||||||
this.next_prompt_number = 1;
|
this.next_prompt_number = 1;
|
||||||
this.kernel = null;
|
this.kernel = null;
|
||||||
this.msg_cell_map = {};
|
this.msg_cell_map = {};
|
||||||
|
this.filename = null;
|
||||||
|
this.notebook_load_re = /%notebook load/
|
||||||
|
this.notebook_save_re = /%notebook save/
|
||||||
|
this.notebook_filename_re = /(\w)+.ipynb/
|
||||||
this.bind_events();
|
this.bind_events();
|
||||||
this.start_kernel();
|
this.start_kernel();
|
||||||
};
|
};
|
||||||
@ -113,12 +98,29 @@ Notebook.prototype.bind_events = function () {
|
|||||||
// The focus is not quite working here.
|
// The focus is not quite working here.
|
||||||
var cell = that.selected_cell();
|
var cell = that.selected_cell();
|
||||||
var cell_index = that.find_cell_index(cell);
|
var cell_index = that.find_cell_index(cell);
|
||||||
|
// TODO: the logic here needs to be moved into appropriate
|
||||||
|
// methods of Notebook.
|
||||||
if (cell instanceof CodeCell) {
|
if (cell instanceof CodeCell) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
cell.clear_output();
|
cell.clear_output();
|
||||||
cell.hide_output_prompt();
|
cell.hide_output_prompt();
|
||||||
var msg_id = that.kernel.execute(cell.get_code());
|
var code = cell.get_code();
|
||||||
that.msg_cell_map[msg_id] = cell.cell_id;
|
if (that.notebook_load_re.test(code)) {
|
||||||
|
var code_parts = code.split(' ');
|
||||||
|
if (code_parts.length === 3) {
|
||||||
|
that.load_notebook(code_parts[2]);
|
||||||
|
};
|
||||||
|
} else if (that.notebook_save_re.test(code)) {
|
||||||
|
var code_parts = code.split(' ');
|
||||||
|
if (code_parts.length === 3) {
|
||||||
|
that.save_notebook(code_parts[2]);
|
||||||
|
} else {
|
||||||
|
that.save_notebook()
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var msg_id = that.kernel.execute(cell.get_code());
|
||||||
|
that.msg_cell_map[msg_id] = cell.cell_id;
|
||||||
|
};
|
||||||
if (cell_index === (that.ncells()-1)) {
|
if (cell_index === (that.ncells()-1)) {
|
||||||
that.insert_code_cell_after();
|
that.insert_code_cell_after();
|
||||||
} else {
|
} else {
|
||||||
@ -129,7 +131,7 @@ Notebook.prototype.bind_events = function () {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var cell = that.selected_cell();
|
var cell = that.selected_cell();
|
||||||
if (cell instanceof CodeCell) {
|
if (cell instanceof CodeCell) {
|
||||||
var ta = cell.element.find("textarea.input_area");
|
var ta = cell.element.find("textarea.input_textarea");
|
||||||
ta.val(ta.val() + " ");
|
ta.val(ta.val() + " ");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -386,8 +388,7 @@ Notebook.prototype.text_to_code = function (index) {
|
|||||||
if (source_cell instanceof TextCell) {
|
if (source_cell instanceof TextCell) {
|
||||||
this.insert_code_cell_after(i);
|
this.insert_code_cell_after(i);
|
||||||
var target_cell = this.cells()[i+1];
|
var target_cell = this.cells()[i+1];
|
||||||
var text = source_element.find("textarea.text_cell_input").val();
|
target_cell.set_code(source_cell.get_text());
|
||||||
target_cell.element.find("textarea.input_area").val(text);
|
|
||||||
source_element.remove();
|
source_element.remove();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -401,12 +402,9 @@ Notebook.prototype.code_to_text = function (index) {
|
|||||||
if (source_cell instanceof CodeCell) {
|
if (source_cell instanceof CodeCell) {
|
||||||
this.insert_text_cell_after(i);
|
this.insert_text_cell_after(i);
|
||||||
var target_cell = this.cells()[i+1];
|
var target_cell = this.cells()[i+1];
|
||||||
var text = source_element.find("textarea.input_area").val();
|
var text = source_cell.get_code();
|
||||||
if (text === "") {text = target_cell.placeholder;};
|
if (text === "") {text = target_cell.placeholder;};
|
||||||
target_cell.element.find("textarea.text_cell_input").val(text);
|
target_cell.set_text(text);
|
||||||
target_cell.element.find("textarea.text_cell_input").html(text);
|
|
||||||
target_cell.element.find("div.text_cell_render").html(text);
|
|
||||||
|
|
||||||
source_element.remove();
|
source_element.remove();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -476,6 +474,94 @@ Notebook.prototype._handle_execute_reply = function (reply, cell) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Persistance and loading
|
||||||
|
|
||||||
|
|
||||||
|
Notebook.prototype.fromJSON = function (data) {
|
||||||
|
var ncells = this.ncells();
|
||||||
|
for (var i=0; i<ncells; i++) {
|
||||||
|
// Always delete cell 0 as they get renumbered as they are deleted.
|
||||||
|
this.delete_cell(0);
|
||||||
|
};
|
||||||
|
var new_cells = data.cells;
|
||||||
|
ncells = new_cells.length;
|
||||||
|
var cell_data = null;
|
||||||
|
for (var i=0; i<ncells; i++) {
|
||||||
|
cell_data = new_cells[i];
|
||||||
|
if (cell_data.cell_type == 'code') {
|
||||||
|
this.insert_code_cell_after();
|
||||||
|
this.selected_cell().fromJSON(cell_data);
|
||||||
|
} else if (cell_data.cell_type === 'text') {
|
||||||
|
this.insert_text_cell_after();
|
||||||
|
this.selected_cell().fromJSON(cell_data);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Notebook.prototype.toJSON = function () {
|
||||||
|
var cells = this.cells();
|
||||||
|
var ncells = cells.length;
|
||||||
|
cell_array = new Array(ncells);
|
||||||
|
for (var i=0; i<ncells; i++) {
|
||||||
|
cell_array[i] = cells[i].toJSON();
|
||||||
|
};
|
||||||
|
json = {
|
||||||
|
cells : cell_array
|
||||||
|
};
|
||||||
|
return json
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Notebook.prototype.test_filename = function (filename) {
|
||||||
|
if (this.notebook_filename_re.test(filename)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
var bad_filename = $('<div/>');
|
||||||
|
bad_filename.html(
|
||||||
|
"The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
|
||||||
|
);
|
||||||
|
bad_filename.dialog({title: 'Invalid filename', modal: true});
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Notebook.prototype.save_notebook = function (filename) {
|
||||||
|
this.filename = filename || this.filename || '';
|
||||||
|
if (this.filename === '') {
|
||||||
|
var no_filename = $('<div/>');
|
||||||
|
no_filename.html(
|
||||||
|
"This notebook has no filename, please specify a filename of the form: foo.ipynb"
|
||||||
|
);
|
||||||
|
no_filename.dialog({title: 'Missing filename', modal: true});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.test_filename(this.filename)) {return;}
|
||||||
|
var thedata = this.toJSON();
|
||||||
|
var settings = {
|
||||||
|
processData : false,
|
||||||
|
cache : false,
|
||||||
|
type : "PUT",
|
||||||
|
data : JSON.stringify(thedata),
|
||||||
|
success : function (data, status, xhr) {console.log(data);}
|
||||||
|
};
|
||||||
|
$.ajax("/notebooks/" + this.filename, settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Notebook.prototype.load_notebook = function (filename) {
|
||||||
|
if (!this.test_filename(filename)) {return;}
|
||||||
|
var that = this;
|
||||||
|
$.getJSON("/notebooks/" + filename,
|
||||||
|
function (data, status, xhr) {
|
||||||
|
that.fromJSON(data);
|
||||||
|
that.filename = filename;
|
||||||
|
that.kernel.restart();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
// Cell
|
// Cell
|
||||||
//============================================================================
|
//============================================================================
|
||||||
@ -620,16 +706,32 @@ CodeCell.prototype.set_output_prompt = function (number) {
|
|||||||
this.element.find('div.output_prompt').html('Out[' + n + ']:');
|
this.element.find('div.output_prompt').html('Out[' + n + ']:');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
CodeCell.prototype.hide_output_prompt = function () {
|
CodeCell.prototype.hide_output_prompt = function () {
|
||||||
this.element.find('div.output_prompt').hide();
|
this.element.find('div.output_prompt').hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
CodeCell.prototype.show_output_prompt = function () {
|
CodeCell.prototype.show_output_prompt = function () {
|
||||||
this.element.find('div.output_prompt').show();
|
this.element.find('div.output_prompt').show();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
CodeCell.prototype.get_code = function () {
|
CodeCell.prototype.get_code = function () {
|
||||||
return this.element.find("textarea.input_area").val();
|
return this.element.find("textarea.input_textarea").val();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
CodeCell.prototype.set_code = function (code) {
|
||||||
|
return this.element.find("textarea.input_textarea").val(code);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
CodeCell.prototype.fromJSON = function (data) {
|
||||||
|
if (data.cell_type === 'code') {
|
||||||
|
this.set_code(data.code);
|
||||||
|
this.set_input_prompt(data.prompt_number);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -640,6 +742,7 @@ CodeCell.prototype.toJSON = function () {
|
|||||||
prompt_number : this.input_prompt_number
|
prompt_number : this.input_prompt_number
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
// TextCell
|
// TextCell
|
||||||
//============================================================================
|
//============================================================================
|
||||||
@ -714,6 +817,32 @@ TextCell.prototype.config_mathjax = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TextCell.prototype.get_text = function() {
|
||||||
|
return this.element.find("textarea.text_cell_input").val();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TextCell.prototype.set_text = function(text) {
|
||||||
|
this.element.find("textarea.text_cell_input").val(text);
|
||||||
|
this.element.find("textarea.text_cell_input").html(text);
|
||||||
|
this.element.find("div.text_cell_render").html(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TextCell.prototype.fromJSON = function (data) {
|
||||||
|
if (data.cell_type === 'text') {
|
||||||
|
this.set_text(data.text);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TextCell.prototype.toJSON = function () {
|
||||||
|
return {
|
||||||
|
cell_type : 'text',
|
||||||
|
text : this.get_text(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
// On document ready
|
// On document ready
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
|
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<span id="ipython_notebook"><h1>|I|Python Notebook</h1></span>
|
<span id="ipython_notebook"><h1>[I]:Python Notebook</h1></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user