Merge pull request #1383 from ellisonbg/nbparallel

IPython clusters can now be managed using the Notebook.
Brian E. Granger 14 years ago
commit 5a444db9f7

@ -0,0 +1,175 @@
"""Manage IPython.parallel clusters in the notebook.
Authors:
* Brian Granger
"""
#-----------------------------------------------------------------------------
# Copyright (C) 2008-2011 The IPython Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
import os
from tornado import web
from zmq.eventloop import ioloop
from IPython.config.configurable import LoggingConfigurable
from IPython.config.loader import load_pyconfig_files
from IPython.utils.traitlets import Dict, Instance, CFloat
from IPython.parallel.apps.ipclusterapp import IPClusterStart
from IPython.core.profileapp import list_profiles_in
from IPython.core.profiledir import ProfileDir
from IPython.utils.path import get_ipython_dir
from IPython.utils.sysinfo import num_cpus
#-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------
class DummyIPClusterStart(IPClusterStart):
"""Dummy subclass to skip init steps that conflict with global app.
Instantiating and initializing this class should result in fully configured
launchers, but no other side effects or state.
"""
def init_signal(self):
pass
def init_logging(self):
pass
def reinit_logging(self):
pass
class ClusterManager(LoggingConfigurable):
profiles = Dict()
delay = CFloat(1., config=True,
help="delay (in s) between starting the controller and the engines")
loop = Instance('zmq.eventloop.ioloop.IOLoop')
def _loop_default(self):
from zmq.eventloop.ioloop import IOLoop
return IOLoop.instance()
def build_launchers(self, profile_dir):
starter = DummyIPClusterStart(log=self.log)
starter.initialize(['--profile-dir', profile_dir])
cl = starter.controller_launcher
esl = starter.engine_launcher
n = starter.n
return cl, esl, n
def get_profile_dir(self, name, path):
p = ProfileDir.find_profile_dir_by_name(path,name=name)
return p.location
def update_profiles(self):
"""List all profiles in the ipython_dir and cwd.
"""
for path in [get_ipython_dir(), os.getcwdu()]:
for profile in list_profiles_in(path):
pd = self.get_profile_dir(profile, path)
if profile not in self.profiles:
self.log.debug("Overwriting profile %s" % profile)
self.profiles[profile] = {
'profile': profile,
'profile_dir': pd,
'status': 'stopped'
}
def list_profiles(self):
self.update_profiles()
result = [self.profile_info(p) for p in self.profiles.keys()]
result.sort()
return result
def check_profile(self, profile):
if profile not in self.profiles:
raise web.HTTPError(404, u'profile not found')
def profile_info(self, profile):
self.check_profile(profile)
result = {}
data = self.profiles.get(profile)
result['profile'] = profile
result['profile_dir'] = data['profile_dir']
result['status'] = data['status']
if 'n' in data:
result['n'] = data['n']
return result
def start_cluster(self, profile, n=None):
"""Start a cluster for a given profile."""
self.check_profile(profile)
data = self.profiles[profile]
if data['status'] == 'running':
raise web.HTTPError(409, u'cluster already running')
cl, esl, default_n = self.build_launchers(data['profile_dir'])
n = n if n is not None else default_n
def clean_data():
data.pop('controller_launcher',None)
data.pop('engine_set_launcher',None)
data.pop('n',None)
data['status'] = 'stopped'
def engines_stopped(r):
self.log.debug('Engines stopped')
if cl.running:
cl.stop()
clean_data()
esl.on_stop(engines_stopped)
def controller_stopped(r):
self.log.debug('Controller stopped')
if esl.running:
esl.stop()
clean_data()
cl.on_stop(controller_stopped)
dc = ioloop.DelayedCallback(lambda: cl.start(), 0, self.loop)
dc.start()
dc = ioloop.DelayedCallback(lambda: esl.start(n), 1000*self.delay, self.loop)
dc.start()
self.log.debug('Cluster started')
data['controller_launcher'] = cl
data['engine_set_launcher'] = esl
data['n'] = n
data['status'] = 'running'
return self.profile_info(profile)
def stop_cluster(self, profile):
"""Stop a cluster for a given profile."""
self.check_profile(profile)
data = self.profiles[profile]
if data['status'] == 'stopped':
raise web.HTTPError(409, u'cluster not running')
data = self.profiles[profile]
cl = data['controller_launcher']
esl = data['engine_set_launcher']
if cl.running:
cl.stop()
if esl.running:
esl.stop()
# Return a temp info dict, the real one is updated in the on_stop
# logic above.
result = {
'profile': data['profile'],
'profile_dir': data['profile_dir'],
'status': 'stopped'
}
return result
def stop_all_clusters(self):
for p in self.profiles.keys():
self.stop_cluster(profile)

@ -211,6 +211,7 @@ class LoginHandler(AuthenticatedHandler):
read_only=self.read_only,
logged_in=self.logged_in,
login_available=self.login_available,
base_project_url=self.application.ipython_app.base_project_url,
message=message
)
@ -246,6 +247,7 @@ class LogoutHandler(AuthenticatedHandler):
read_only=self.read_only,
logged_in=self.logged_in,
login_available=self.login_available,
base_project_url=self.application.ipython_app.base_project_url,
message=message)
@ -587,7 +589,6 @@ class NotebookRootHandler(AuthenticatedHandler):
@authenticate_unless_readonly
def get(self):
nbm = self.application.notebook_manager
files = nbm.list_notebooks()
self.finish(jsonapi.dumps(files))
@ -661,6 +662,44 @@ class NotebookCopyHandler(AuthenticatedHandler):
mathjax_url=self.application.ipython_app.mathjax_url,
)
#-----------------------------------------------------------------------------
# Cluster handlers
#-----------------------------------------------------------------------------
class MainClusterHandler(AuthenticatedHandler):
@web.authenticated
def get(self):
cm = self.application.cluster_manager
self.finish(jsonapi.dumps(cm.list_profiles()))
class ClusterProfileHandler(AuthenticatedHandler):
@web.authenticated
def get(self, profile):
cm = self.application.cluster_manager
self.finish(jsonapi.dumps(cm.profile_info(profile)))
class ClusterActionHandler(AuthenticatedHandler):
@web.authenticated
def post(self, profile, action):
cm = self.application.cluster_manager
if action == 'start':
n = self.get_argument('n',default=None)
if n is None:
data = cm.start_cluster(profile)
else:
data = cm.start_cluster(profile,int(n))
if action == 'stop':
data = cm.stop_cluster(profile)
self.finish(jsonapi.dumps(data))
#-----------------------------------------------------------------------------
# RST web service handlers
#-----------------------------------------------------------------------------

@ -49,9 +49,11 @@ from .handlers import (LoginHandler, LogoutHandler,
ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler
RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
MainClusterHandler, ClusterProfileHandler, ClusterActionHandler
)
from .notebookmanager import NotebookManager
from .clustermanager import ClusterManager
from IPython.config.application import catch_config_error, boolean_flag
from IPython.core.application import BaseIPythonApplication
@ -74,6 +76,9 @@ from IPython.utils import py3compat
_kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
_kernel_action_regex = r"(?P<action>restart|interrupt)"
_notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
_profile_regex = r"(?P<profile>[a-zA-Z0-9]+)"
_cluster_action_regex = r"(?P<action>start|stop)"
LOCALHOST = '127.0.0.1'
@ -101,7 +106,8 @@ def url_path_join(a,b):
class NotebookWebApplication(web.Application):
def __init__(self, ipython_app, kernel_manager, notebook_manager, log,
def __init__(self, ipython_app, kernel_manager, notebook_manager,
cluster_manager, log,
base_project_url, settings_overrides):
handlers = [
(r"/", ProjectDashboardHandler),
@ -120,6 +126,9 @@ class NotebookWebApplication(web.Application):
(r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
(r"/rstservice/render", RSTHandler),
(r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
(r"/clusters", MainClusterHandler),
(r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
(r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
]
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
@ -151,10 +160,11 @@ class NotebookWebApplication(web.Application):
super(NotebookWebApplication, self).__init__(new_handlers, **settings)
self.kernel_manager = kernel_manager
self.log = log
self.notebook_manager = notebook_manager
self.cluster_manager = cluster_manager
self.ipython_app = ipython_app
self.read_only = self.ipython_app.read_only
self.log = log
#-----------------------------------------------------------------------------
@ -395,6 +405,8 @@ class NotebookApp(BaseIPythonApplication):
)
self.notebook_manager = NotebookManager(config=self.config, log=self.log)
self.notebook_manager.list_notebooks()
self.cluster_manager = ClusterManager(config=self.config, log=self.log)
self.cluster_manager.update_profiles()
def init_logging(self):
super(NotebookApp, self).init_logging()
@ -406,7 +418,8 @@ class NotebookApp(BaseIPythonApplication):
def init_webapp(self):
"""initialize tornado webapp and httpserver"""
self.web_app = NotebookWebApplication(
self, self.kernel_manager, self.notebook_manager, self.log,
self, self.kernel_manager, self.notebook_manager,
self.cluster_manager, self.log,
self.base_project_url, self.webapp_settings
)
if self.certfile:

@ -0,0 +1,6 @@
#main_app {
height: 100px;
width: 350px;
margin: 50px auto;
}

@ -0,0 +1,7 @@
#main_app {
height: 100px;
width: 200px;
margin: 50px auto;
}

@ -6,14 +6,6 @@
body {
background-color: white;
/* This makes sure that the body covers the entire window and needs to
be in a different element than the display: box in wrapper below */
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
overflow: hidden;
}
@ -31,11 +23,6 @@ span#notebook_name {
font-size: 146.5%;
}
#menubar {
/* Initially hidden to prevent FLOUC */
display: none;
}
.ui-menubar-item .ui-button .ui-button-text {
padding: 0.4em 1.0em;
font-size: 100%;
@ -69,8 +56,6 @@ span#notebook_name {
}
#toolbar {
/* Initially hidden to prevent FLOUC */
display: none;
padding: 3px 15px;
}
@ -78,6 +63,12 @@ span#notebook_name {
font-size: 85%;
}
div#main_app {
width: 100%;
position: relative;
}
span#quick_help_area {
position: static;
padding: 5px 0px;

@ -14,7 +14,7 @@ body {
right: 0px;
top: 0px;
bottom: 0px;
overflow: hidden;
overflow: visible;
}
@ -41,11 +41,9 @@ span#ipython_notebook h1 img {
color: black;
}
div#main_app {
/* Initially hidden to prevent FLOUC */
display: none;
#site {
width: 100%;
position: relative;
display: none;
}
/* We set the fonts by hand here to override the values in the theme */
@ -63,11 +61,17 @@ div#main_app {
font-size: 77%;
}
input.ui-button {
padding: 0.3em 0.9em;
}
span#login_widget {
float: right;
}
/* generic class for hidden objects */
.hidden {
display: none;
.border-box-sizing {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}

@ -5,70 +5,68 @@
* Author: IPython Development Team
*/
body {
background-color: white;
/* This makes sure that the body covers the entire window and needs to
be in a different element than the display: box in wrapper below */
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
overflow: auto;
}
#left_panel {
#main_app {
width: 920px;
margin: 30px auto 0px auto;
}
#drop_zone {
height: 200px;
width: 200px
#tabs {
border-style: none;
}
#content_panel {
width: 600px;
#tab1, #tab2 {
padding: 1em 0em;
}
#content_toolbar {
.list_toolbar {
padding: 5px;
height: 25px;
line-height: 25px;
}
#header_border {
width: 100%;
height: 2px;
}
#app_hbox {
width: 100%;
}
#drag_info {
.toolbar_info {
float: left;
}
#notebooks_buttons {
.toolbar_buttons {
float: right;
}
#project_name {
.list_header {
height: 25px;
line-height: 25px;
padding: 3px;
padding: 3px 5px;
}
.notebook_item {
.list_item {
height: 25px;
line-height: 25px;
padding: 3px;
padding: 3px 5px;
}
.notebook_item a {
text-decoration: none;
}
.profile_col {
}
.status_col {
float: right;
width: 325px;
}
.engines_col {
float: right;
width: 325px;
}
.action_col {
float: right;
}
.item_buttons {
float: right;
}
@ -80,3 +78,7 @@ body {
.highlight_text {
color: blue;
}
.ui-tabs .ui-tabs-nav li a {
padding: .3em .5em;
}

@ -0,0 +1,180 @@
//----------------------------------------------------------------------------
// Copyright (C) 2008-2011 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------
//============================================================================
// NotebookList
//============================================================================
var IPython = (function (IPython) {
var ClusterList = function (selector) {
this.selector = selector;
if (this.selector !== undefined) {
this.element = $(selector);
this.style();
this.bind_events();
}
};
ClusterList.prototype.style = function () {
$('#cluster_toolbar').addClass('list_toolbar');
$('#cluster_list_info').addClass('toolbar_info');
$('#cluster_buttons').addClass('toolbar_buttons');
$('div#cluster_header').addClass('list_header ui-widget ui-widget-header ui-helper-clearfix');
$('div#cluster_header').children().eq(0).addClass('profile_col');
$('div#cluster_header').children().eq(1).addClass('action_col');
$('div#cluster_header').children().eq(2).addClass('engines_col');
$('div#cluster_header').children().eq(3).addClass('status_col');
$('#refresh_cluster_list').button({
icons : {primary: 'ui-icon-arrowrefresh-1-s'},
text : false
});
};
ClusterList.prototype.bind_events = function () {
var that = this;
$('#refresh_cluster_list').click(function () {
that.load_list();
});
};
ClusterList.prototype.load_list = function () {
var settings = {
processData : false,
cache : false,
type : "GET",
dataType : "json",
success : $.proxy(this.load_list_success, this)
};
var url = $('body').data('baseProjectUrl') + 'clusters';
$.ajax(url, settings);
};
ClusterList.prototype.clear_list = function () {
this.element.children('.list_item').remove();
}
ClusterList.prototype.load_list_success = function (data, status, xhr) {
this.clear_list();
var len = data.length;
for (var i=0; i<len; i++) {
var item_div = $('<div/>');
var item = new ClusterItem(item_div);
item.update_state(data[i]);
item_div.data('item', item);
this.element.append(item_div);
};
};
var ClusterItem = function (element) {
this.element = $(element);
this.data = null;
this.style();
};
ClusterItem.prototype.style = function () {
this.element.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
this.element.css('border-top-style','none');
}
ClusterItem.prototype.update_state = function (data) {
this.data = data;
if (data.status === 'running') {
this.state_running();
} else if (data.status === 'stopped') {
this.state_stopped();
};
}
ClusterItem.prototype.state_stopped = function () {
var that = this;
this.element.empty();
var profile_col = $('<span/>').addClass('profile_col').text(this.data.profile);
var status_col = $('<span/>').addClass('status_col').html('stopped');
var engines_col = $('<span/>').addClass('engines_col');
var input = $('<input/>').attr('type','text').
attr('size',3).addClass('engine_num_input');
engines_col.append(input);
var action_col = $('<span/>').addClass('action_col');
var start_button = $('<button>Start</button>').button();
action_col.append(start_button);
this.element.append(profile_col).
append(action_col).
append(engines_col).
append(status_col);
start_button.click(function (e) {
var n = that.element.find('.engine_num_input').val();
if (!/^\d+$/.test(n) && n.length>0) {
status_col.html('invalid engine #');
} else {
var settings = {
cache : false,
data : {n:n},
type : "POST",
dataType : "json",
success : function (data, status, xhr) {
that.update_state(data);
},
error : function (data, status, xhr) {
status_col.html("error starting cluster")
}
};
status_col.html('starting');
var url = $('body').data('baseProjectUrl') + 'clusters/' + that.data.profile + '/start';
$.ajax(url, settings);
};
});
};
ClusterItem.prototype.state_running = function () {
this.element.empty();
var that = this;
var profile_col = $('<span/>').addClass('profile_col').text(this.data.profile);
var status_col = $('<span/>').addClass('status_col').html('running');
var engines_col = $('<span/>').addClass('engines_col').html(this.data.n);
var action_col = $('<span/>').addClass('action_col');
var stop_button = $('<button>Stop</button>').button();
action_col.append(stop_button);
this.element.append(profile_col).
append(action_col).
append(engines_col).
append(status_col);
stop_button.click(function (e) {
var settings = {
cache : false,
type : "POST",
dataType : "json",
success : function (data, status, xhr) {
that.update_state(data);
},
error : function (data, status, xhr) {
console.log('error',data);
status_col.html("error stopping cluster")
}
};
status_col.html('stopping')
var url = $('body').data('baseProjectUrl') + 'clusters/' + that.data.profile + '/stop';
$.ajax(url, settings);
});
};
IPython.ClusterList = ClusterList;
IPython.ClusterItem = ClusterItem;
return IPython;
}(IPython));

@ -12,19 +12,11 @@
$(document).ready(function () {
$('div#header').addClass('border-box-sizing');
$('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content');
IPython.page = new IPython.Page();
$('input#login_submit').button();
$('div#main_app').addClass('border-box-sizing ui-widget');
$('div#app_hbox').addClass('hbox');
$('div#left_panel').addClass('box-flex');
$('div#right_panel').addClass('box-flex');
$('input#signin').button();
// These have display: none in the css file and are made visible here to prevent FLOUC.
$('div#header').css('display','block');
$('div#main_app').css('display','block');
IPython.page.show();
$('input#password_input').focus();
});

@ -24,6 +24,8 @@ var IPython = (function (IPython) {
this.element.find('button#logout').button();
this.element.find('button#login').button();
};
LoginWidget.prototype.bind_events = function () {
var that = this;
this.element.find("button#logout").click(function () {

@ -0,0 +1,20 @@
//----------------------------------------------------------------------------
// Copyright (C) 2008-2011 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------
//============================================================================
// On document ready
//============================================================================
$(document).ready(function () {
IPython.page = new IPython.Page();
$('div#main_app').addClass('border-box-sizing ui-widget');
IPython.page.show();
});

@ -22,6 +22,7 @@ var IPython = (function (IPython) {
MenuBar.prototype.style = function () {
this.element.addClass('border-box-sizing');
$('ul#menus').menubar({
select : function (event, ui) {
// The selected cell loses focus when the menu is entered, so we

@ -21,8 +21,14 @@ var IPython = (function (IPython) {
};
NotebookList.prototype.style = function () {
this.element.addClass('ui-widget ui-widget-content');
$('div#project_name').addClass('ui-widget ui-widget-header');
$('#notebook_toolbar').addClass('list_toolbar');
$('#drag_info').addClass('toolbar_info');
$('#notebook_buttons').addClass('toolbar_buttons');
$('div#project_name').addClass('list_header ui-widget ui-widget-header');
$('#refresh_notebook_list').button({
icons : {primary: 'ui-icon-arrowrefresh-1-s'},
text : false
});
};
@ -31,6 +37,9 @@ var IPython = (function (IPython) {
return;
}
var that = this;
$('#refresh_notebook_list').click(function () {
that.load_list();
});
this.element.bind('dragover', function () {
return false;
});
@ -62,7 +71,13 @@ var IPython = (function (IPython) {
};
NotebookList.prototype.clear_list = function () {
this.element.children('.list_item').remove();
}
NotebookList.prototype.load_list = function () {
this.clear_list();
var settings = {
processData : false,
cache : false,
@ -93,7 +108,8 @@ var IPython = (function (IPython) {
NotebookList.prototype.new_notebook_item = function (index) {
var item = $('<div/>');
item.addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix');
item.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
item.css('border-top-style','none');
var item_name = $('<span/>').addClass('item_name');
item.append(item_name);
@ -156,7 +172,7 @@ var IPython = (function (IPython) {
var that = $(this);
// We use the nbname and notebook_id from the parent notebook_item element's
// data because the outer scopes values change as we iterate through the loop.
var parent_item = that.parents('div.notebook_item');
var parent_item = that.parents('div.list_item');
var nbname = parent_item.data('nbname');
var notebook_id = parent_item.data('notebook_id');
var dialog = $('<div/>');

@ -11,75 +11,17 @@
$(document).ready(function () {
if (window.MathJax){
// MathJax loaded
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
},
displayAlign: 'left', // Change this to 'center' to center equations.
"HTML-CSS": {
styles: {'.MathJax_Display': {"margin": 0}}
}
});
}else if (window.mathjax_url != ""){
// Don't have MathJax, but should. Show dialog.
var dialog = $('<div></div>')
.append(
$("<p></p>").addClass('dialog').html(
"Math/LaTeX rendering will be disabled."
)
).append(
$("<p></p>").addClass('dialog').html(
"If you have administrative access to the notebook server and" +
" a working internet connection, you can install a local copy" +
" of MathJax for offline use with the following command on the server" +
" at a Python or IPython prompt:"
)
).append(
$("<pre></pre>").addClass('dialog').html(
">>> from IPython.external import mathjax; mathjax.install_mathjax()"
)
).append(
$("<p></p>").addClass('dialog').html(
"This will try to install MathJax into the IPython source directory."
)
).append(
$("<p></p>").addClass('dialog').html(
"If IPython is installed to a location that requires" +
" administrative privileges to write, you will need to make this call as" +
" an administrator, via 'sudo'."
)
).append(
$("<p></p>").addClass('dialog').html(
"When you start the notebook server, you can instruct it to disable MathJax support altogether:"
)
).append(
$("<pre></pre>").addClass('dialog').html(
"$ ipython notebook --no-mathjax"
)
).append(
$("<p></p>").addClass('dialog').html(
"which will prevent this dialog from appearing."
)
).dialog({
title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
width: "70%",
modal: true,
})
}else{
// No MathJax, but none expected. No dialog.
}
IPython.markdown_converter = new Markdown.Converter();
IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
IPython.init_mathjax();
$('div#header').addClass('border-box-sizing');
$('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content');
IPython.read_only = $('body').data('readOnly') === 'True';
$('div#main_app').addClass('border-box-sizing ui-widget');
$('div#notebook_panel').addClass('border-box-sizing ui-widget');
// The header's bottom border is provided by the menu bar so we remove it.
$('div#header').css('border-bottom-style','none');
IPython.page = new IPython.Page();
IPython.markdown_converter = new Markdown.Converter();
IPython.layout_manager = new IPython.LayoutManager();
IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
IPython.quick_help = new IPython.QuickHelp('span#quick_help_area');
@ -92,22 +34,16 @@ $(document).ready(function () {
IPython.layout_manager.do_resize();
// These have display: none in the css file and are made visible here to prevent FLOUC.
$('div#header').css('display','block');
if(IPython.read_only){
// hide various elements from read-only view
$('div#pager').remove();
$('div#pager_splitter').remove();
$('span#login_widget').removeClass('hidden');
// set the notebook name field as not modifiable
$('#notebook_name').attr('disabled','disabled')
}
$('div#menubar').css('display','block');
$('div#toolbar').css('display','block');
$('div#main_app').css('display','block');
IPython.page.show();
IPython.layout_manager.do_resize();
$([IPython.events]).on('notebook_loaded.Notebook', function () {

@ -0,0 +1,59 @@
//----------------------------------------------------------------------------
// Copyright (C) 2008-2011 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------
//============================================================================
// Global header/site setup.
//============================================================================
var IPython = (function (IPython) {
var Page = function () {
this.style();
this.bind_events();
};
Page.prototype.style = function () {
$('div#header').addClass('border-box-sizing').
addClass('ui-widget ui-widget-content').
css('border-top-style','none').
css('border-left-style','none').
css('border-right-style','none');
$('div#site').addClass('border-box-sizing')
};
Page.prototype.bind_events = function () {
};
Page.prototype.show = function () {
// The header and site divs start out hidden to prevent FLOUC.
// Main scripts should call this method after styling everything.
this.show_header();
this.show_site();
};
Page.prototype.show_header = function () {
// The header and site divs start out hidden to prevent FLOUC.
// Main scripts should call this method after styling everything.
$('div#header').css('display','block');
};
Page.prototype.show_site = function () {
// The header and site divs start out hidden to prevent FLOUC.
// Main scripts should call this method after styling everything.
$('div#site').css('display','block');
};
IPython.Page = Page;
return IPython;
}(IPython));

@ -0,0 +1,19 @@
//----------------------------------------------------------------------------
// Copyright (C) 2008-2011 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------
//============================================================================
// On document ready
//============================================================================
$(document).ready(function () {
IPython.page = new IPython.Page();
IPython.page.show();
});

@ -12,31 +12,28 @@
$(document).ready(function () {
$('div#header').addClass('border-box-sizing');
$('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content');
IPython.page = new IPython.Page();
$('div#tabs').tabs();
$('div#tabs').on('tabsselect', function (event, ui) {
var new_url = $('body').data('baseProjectUrl') + '#' + ui.panel.id;
window.history.replaceState({}, '', new_url);
});
$('div#main_app').addClass('border-box-sizing ui-widget');
$('div#app_hbox').addClass('hbox');
$('div#content_toolbar').addClass('ui-widget ui-helper-clearfix');
$('div#notebooks_toolbar').addClass('ui-widget ui-helper-clearfix');
$('#new_notebook').button().click(function (e) {
window.open($('body').data('baseProjectUrl')+'new');
});
$('div#left_panel').addClass('box-flex');
$('div#right_panel').addClass('box-flex');
IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
IPython.read_only = $('body').data('readOnly') === 'True';
IPython.notebook_list = new IPython.NotebookList('div#notebook_list');
IPython.cluster_list = new IPython.ClusterList('div#cluster_list');
IPython.login_widget = new IPython.LoginWidget('span#login_widget');
IPython.notebook_list.load_list();
IPython.cluster_list.load_list();
// These have display: none in the css file and are made visible here to prevent FLOUC.
$('div#header').css('display','block');
$('div#main_app').css('display','block');
IPython.page.show();
});

@ -22,7 +22,11 @@ var IPython = (function (IPython) {
ToolBar.prototype.style = function () {
this.element.addClass('border-box-sizing');
this.element.addClass('border-box-sizing').
addClass('ui-widget ui-widget-content').
css('border-top-style','none').
css('border-left-style','none').
css('border-right-style','none');
this.element.find('#cell_type').addClass('ui-widget ui-widget-content');
this.element.find('#save_b').button({
icons : {primary: 'ui-icon-disk'},

@ -1,26 +1,42 @@
{% extends layout.html %}
{% extends page.html %}
{% block content_panel %}
{% block stylesheet %}
{% if login_available %}
<link rel="stylesheet" href="{{static_url("css/login.css") }}" type="text/css"/>
{% end %}
{% block login_widget %}
{% end %}
{% block site %}
<div id="main_app">
{% if login_available %}
<form action="/login?next={{url_escape(next)}}" method="post">
Password: <input type="password" name="password" id="focus">
<input type="submit" value="Sign in" id="signin">
Password: <input type="password" name="password" id="password_input">
<input type="submit" value="Log in" id="login_submit">
</form>
{% end %}
{% if message %}
{% for key in message %}
<div class="message {{key}}">
{{message[key]}}
</div>
{% end %}
{% end %}
{% end %}
<div/>
{% block login_widget %}
{% end %}
{% block script %}
<script type="text/javascript">
$(document).ready(function() {
IPython.login_widget = new IPython.LoginWidget('span#login_widget');
$('#focus').focus();
});
</script>
<script src="{{static_url("js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
{% end %}

@ -1,28 +1,40 @@
{% extends layout.html %}
{% extends page.html %}
{% block content_panel %}
<ul>
{% if read_only or not login_available %}
{% block stylesheet %}
Proceed to the <a href="/">list of notebooks</a>.</li>
<link rel="stylesheet" href="{{static_url("css/logout.css") }}" type="text/css"/>
{% else %}
{% end %}
{% block login_widget %}
{% end %}
{% block site %}
Proceed to the <a href="/login">login page</a>.</li>
<div id="main_app">
{% if message %}
{% for key in message %}
<div class="message {{key}}">
{{message[key]}}
</div>
{% end %}
{% end %}
</ul>
{% if read_only or not login_available %}
Proceed to the <a href="/">dashboard</a>.
{% else %}
Proceed to the <a href="/login">login page</a>.
{% end %}
{% end %}
{% block login_widget %}
<div/>
{% end %}
{% block script %}
<script type="text/javascript">
$(document).ready(function() {
IPython.login_widget = new IPython.LoginWidget('span#login_widget');
});
</script>
<script src="{{static_url("js/logoutmain.js") }}" type="text/javascript" charset="utf-8"></script>
{% end %}

@ -1,60 +1,49 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>IPython Notebook</title>
{% if mathjax_url %}
<script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
{% end %}
<script type="text/javascript">
// MathJax disabled, set as null to distingish from *missing* MathJax,
// where it will be undefined, and should prompt a dialog later.
window.mathjax_url = "{{mathjax_url}}";
</script>
<link rel="stylesheet" href="{{ static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
<link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
<link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
<link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
<link rel="stylesheet" href="{{ static_url("css/boilerplate.css") }}" type="text/css" />
<link rel="stylesheet" href="{{ static_url("css/layout.css") }}" type="text/css" />
<link rel="stylesheet" href="{{ static_url("css/base.css") }}" type="text/css" />
<link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
<link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
{% comment In the notebook, the read-only flag is used to determine %}
{% comment whether to hide the side panels and switch off input %}
<meta name="read_only" content="{{read_only and not logged_in}}"/>
</head>
<body
data-project={{project}} data-notebook-id={{notebook_id}}
data-base-project-url={{base_project_url}} data-base-kernel-url={{base_kernel_url}}
>
<div id="header">
<span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='{{static_url("ipynblogo.png")}}' alt='IPython Notebook'/></a></h1></span>
<span id="save_widget">
<span id="notebook_name"></span>
<span id="save_status"></span>
</span>
{% extends page.html %}
<span id="login_widget">
{% comment This is a temporary workaround to hide the logout button %}
{% comment when appropriate until notebook.html is templated %}
{% if logged_in %}
<button id="logout">Logout</button>
{% elif not logged_in and login_available %}
<button id="login">Login</button>
{% end %}
</span>
</div>
{% block stylesheet %}
{% if mathjax_url %}
<script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
{% end %}
<script type="text/javascript">
// MathJax disabled, set as null to distingish from *missing* MathJax,
// where it will be undefined, and should prompt a dialog later.
window.mathjax_url = "{{mathjax_url}}";
</script>
<link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
<link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
<link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
<link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
<link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
{% end %}
{% block params %}
data-project={{project}}
data-base-project-url={{base_project_url}}
data-base-kernel-url={{base_kernel_url}}
data-read-only={{read_only and not logged_in}}
data-notebook-id={{notebook_id}}
{% end %}
{% block header %}
<span id="save_widget">
<span id="notebook_name"></span>
<span id="save_status"></span>
</span>
{% end %}
{% block site %}
<div id="menubar_container">
<div id="menubar">
@ -204,8 +193,10 @@
</div>
<script src="{{ static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
{% end %}
{% block script %}
<script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
<script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
@ -221,17 +212,16 @@
<script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
<script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
<script src="{{ static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/initmathjax.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/layout.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
@ -239,6 +229,5 @@
<script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
</body>
{% end %}
</html>

@ -8,8 +8,8 @@
<link rel="stylesheet" href="{{static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
<link rel="stylesheet" href="{{static_url("css/boilerplate.css") }}" type="text/css" />
<link rel="stylesheet" href="{{static_url("css/layout.css") }}" type="text/css" />
<link rel="stylesheet" href="{{static_url("css/base.css") }}" type="text/css"/>
<link rel="stylesheet" href="{{static_url("css/fbm.css") }}" type="text/css" />
<link rel="stylesheet" href="{{static_url("css/page.css") }}" type="text/css"/>
{% block stylesheet %}
{% end %}
@ -21,7 +21,7 @@
<body {% block params %}{% end %}>
<div id="header">
<span id="ipython_notebook"><h1><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></h1></span>
<span id="ipython_notebook"><h1><a href={{base_project_url}} alt='dashboard'><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></a></h1></span>
{% block login_widget %}
@ -39,43 +39,15 @@
{% end %}
</div>
<div id="header_border"></div>
<div id="main_app">
<div id="app_hbox">
<div id="left_panel">
{% block left_panel %}
{% end %}
</div>
<div id="content_panel">
{% if message %}
{% for key in message %}
<div class="message {{key}}">
{{message[key]}}
</div>
{% end %}
{% end %}
{% block content_panel %}
{% end %}
</div>
<div id="right_panel">
{% block right_panel %}
{% end %}
</div>
</div>
<div id="site">
{% block site %}
{% end %}
</div>
<script src="{{static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("js/page.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
{% block script %}

@ -1,43 +1,77 @@
{% extends layout.html %}
{% extends page.html %}
{% block title %}
IPython Dashboard
{% end %}
{% block title %}IPython Dashboard{% end %}
{% block stylesheet %}
<link rel="stylesheet" href="{{static_url("css/projectdashboard.css") }}" type="text/css" />
{% end %}
{% block meta %}
<meta name="read_only" content="{{read_only}}"/>
{% end %}
{% block params %}
data-project={{project}}
data-base-project-url={{base_project_url}}
data-base-kernel-url={{base_kernel_url}}
data-read-only={{read_only}}
{% end %}
{% block content_panel %}
{% if logged_in or not read_only %}
<div id="content_toolbar">
<span id="drag_info">Drag files onto the list to import
notebooks.</span>
{% block site %}
<div id="main_app">
<div id="tabs">
<ul>
<li><a href="#tab1">Notebooks</a></li>
<li><a href="#tab2">Clusters</a></li>
</ul>
<span id="notebooks_buttons">
<button id="new_notebook">New Notebook</button>
</span>
<div id="tab1">
{% if logged_in or not read_only %}
<div id="notebook_toolbar">
<span id="drag_info">Drag files onto the list to import
notebooks.</span>
<span id="notebook_buttons">
<button id="refresh_notebook_list" title="Refresh notebook list">Refresh</button>
<button id="new_notebook" title="Create new notebook">New Notebook</button>
</span>
</div>
{% end %}
<div id="notebook_list">
<div id="project_name"><h2>{{project}}</h2></div>
</div>
</div>
<div id="tab2">
<div id="cluster_toolbar">
<span id="cluster_list_info">IPython parallel computing clusters</span>
{% end %}
<span id="cluster_buttons">
<button id="refresh_cluster_list" title="Refresh cluster list">Refresh</button>
</span>
</div>
<div id="cluster_list">
<div id="cluster_header">
<span>profile</span>
<span>action</span>
<span title="Enter the number of engines to start or empty for default"># of engines</span>
<span>status</span>
</div>
</div>
<div id="notebook_list">
<div id="project_name"><h2>{{project}}</h2></div>
</div>
</div>
</div>
{% end %}
{% block script %}
<script src="{{static_url("js/notebooklist.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("js/clusterlist.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("js/projectdashboardmain.js") }}" type="text/javascript" charset="utf-8"></script>
{% end %}

Loading…
Cancel
Save