##// END OF EJS Templates
Merge pull request #1383 from ellisonbg/nbparallel...
Brian E. Granger -
r6214:0f5060ee merge
parent child Browse files
Show More
@@ -0,0 +1,175 b''
1 """Manage IPython.parallel clusters in the notebook.
2
3 Authors:
4
5 * Brian Granger
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 import os
20
21 from tornado import web
22 from zmq.eventloop import ioloop
23
24 from IPython.config.configurable import LoggingConfigurable
25 from IPython.config.loader import load_pyconfig_files
26 from IPython.utils.traitlets import Dict, Instance, CFloat
27 from IPython.parallel.apps.ipclusterapp import IPClusterStart
28 from IPython.core.profileapp import list_profiles_in
29 from IPython.core.profiledir import ProfileDir
30 from IPython.utils.path import get_ipython_dir
31 from IPython.utils.sysinfo import num_cpus
32
33
34 #-----------------------------------------------------------------------------
35 # Classes
36 #-----------------------------------------------------------------------------
37
38
39 class DummyIPClusterStart(IPClusterStart):
40 """Dummy subclass to skip init steps that conflict with global app.
41
42 Instantiating and initializing this class should result in fully configured
43 launchers, but no other side effects or state.
44 """
45
46 def init_signal(self):
47 pass
48 def init_logging(self):
49 pass
50 def reinit_logging(self):
51 pass
52
53
54 class ClusterManager(LoggingConfigurable):
55
56 profiles = Dict()
57
58 delay = CFloat(1., config=True,
59 help="delay (in s) between starting the controller and the engines")
60
61 loop = Instance('zmq.eventloop.ioloop.IOLoop')
62 def _loop_default(self):
63 from zmq.eventloop.ioloop import IOLoop
64 return IOLoop.instance()
65
66 def build_launchers(self, profile_dir):
67 starter = DummyIPClusterStart(log=self.log)
68 starter.initialize(['--profile-dir', profile_dir])
69 cl = starter.controller_launcher
70 esl = starter.engine_launcher
71 n = starter.n
72 return cl, esl, n
73
74 def get_profile_dir(self, name, path):
75 p = ProfileDir.find_profile_dir_by_name(path,name=name)
76 return p.location
77
78 def update_profiles(self):
79 """List all profiles in the ipython_dir and cwd.
80 """
81 for path in [get_ipython_dir(), os.getcwdu()]:
82 for profile in list_profiles_in(path):
83 pd = self.get_profile_dir(profile, path)
84 if profile not in self.profiles:
85 self.log.debug("Overwriting profile %s" % profile)
86 self.profiles[profile] = {
87 'profile': profile,
88 'profile_dir': pd,
89 'status': 'stopped'
90 }
91
92 def list_profiles(self):
93 self.update_profiles()
94 result = [self.profile_info(p) for p in self.profiles.keys()]
95 result.sort()
96 return result
97
98 def check_profile(self, profile):
99 if profile not in self.profiles:
100 raise web.HTTPError(404, u'profile not found')
101
102 def profile_info(self, profile):
103 self.check_profile(profile)
104 result = {}
105 data = self.profiles.get(profile)
106 result['profile'] = profile
107 result['profile_dir'] = data['profile_dir']
108 result['status'] = data['status']
109 if 'n' in data:
110 result['n'] = data['n']
111 return result
112
113 def start_cluster(self, profile, n=None):
114 """Start a cluster for a given profile."""
115 self.check_profile(profile)
116 data = self.profiles[profile]
117 if data['status'] == 'running':
118 raise web.HTTPError(409, u'cluster already running')
119 cl, esl, default_n = self.build_launchers(data['profile_dir'])
120 n = n if n is not None else default_n
121 def clean_data():
122 data.pop('controller_launcher',None)
123 data.pop('engine_set_launcher',None)
124 data.pop('n',None)
125 data['status'] = 'stopped'
126 def engines_stopped(r):
127 self.log.debug('Engines stopped')
128 if cl.running:
129 cl.stop()
130 clean_data()
131 esl.on_stop(engines_stopped)
132 def controller_stopped(r):
133 self.log.debug('Controller stopped')
134 if esl.running:
135 esl.stop()
136 clean_data()
137 cl.on_stop(controller_stopped)
138
139 dc = ioloop.DelayedCallback(lambda: cl.start(), 0, self.loop)
140 dc.start()
141 dc = ioloop.DelayedCallback(lambda: esl.start(n), 1000*self.delay, self.loop)
142 dc.start()
143
144 self.log.debug('Cluster started')
145 data['controller_launcher'] = cl
146 data['engine_set_launcher'] = esl
147 data['n'] = n
148 data['status'] = 'running'
149 return self.profile_info(profile)
150
151 def stop_cluster(self, profile):
152 """Stop a cluster for a given profile."""
153 self.check_profile(profile)
154 data = self.profiles[profile]
155 if data['status'] == 'stopped':
156 raise web.HTTPError(409, u'cluster not running')
157 data = self.profiles[profile]
158 cl = data['controller_launcher']
159 esl = data['engine_set_launcher']
160 if cl.running:
161 cl.stop()
162 if esl.running:
163 esl.stop()
164 # Return a temp info dict, the real one is updated in the on_stop
165 # logic above.
166 result = {
167 'profile': data['profile'],
168 'profile_dir': data['profile_dir'],
169 'status': 'stopped'
170 }
171 return result
172
173 def stop_all_clusters(self):
174 for p in self.profiles.keys():
175 self.stop_cluster(profile)
@@ -0,0 +1,6 b''
1
2 #main_app {
3 height: 100px;
4 width: 350px;
5 margin: 50px auto;
6 }
@@ -0,0 +1,7 b''
1
2 #main_app {
3 height: 100px;
4 width: 200px;
5 margin: 50px auto;
6 }
7
@@ -0,0 +1,180 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // NotebookList
10 //============================================================================
11
12 var IPython = (function (IPython) {
13
14 var ClusterList = function (selector) {
15 this.selector = selector;
16 if (this.selector !== undefined) {
17 this.element = $(selector);
18 this.style();
19 this.bind_events();
20 }
21 };
22
23 ClusterList.prototype.style = function () {
24 $('#cluster_toolbar').addClass('list_toolbar');
25 $('#cluster_list_info').addClass('toolbar_info');
26 $('#cluster_buttons').addClass('toolbar_buttons');
27 $('div#cluster_header').addClass('list_header ui-widget ui-widget-header ui-helper-clearfix');
28 $('div#cluster_header').children().eq(0).addClass('profile_col');
29 $('div#cluster_header').children().eq(1).addClass('action_col');
30 $('div#cluster_header').children().eq(2).addClass('engines_col');
31 $('div#cluster_header').children().eq(3).addClass('status_col');
32 $('#refresh_cluster_list').button({
33 icons : {primary: 'ui-icon-arrowrefresh-1-s'},
34 text : false
35 });
36 };
37
38
39 ClusterList.prototype.bind_events = function () {
40 var that = this;
41 $('#refresh_cluster_list').click(function () {
42 that.load_list();
43 });
44 };
45
46
47 ClusterList.prototype.load_list = function () {
48 var settings = {
49 processData : false,
50 cache : false,
51 type : "GET",
52 dataType : "json",
53 success : $.proxy(this.load_list_success, this)
54 };
55 var url = $('body').data('baseProjectUrl') + 'clusters';
56 $.ajax(url, settings);
57 };
58
59
60 ClusterList.prototype.clear_list = function () {
61 this.element.children('.list_item').remove();
62 }
63
64 ClusterList.prototype.load_list_success = function (data, status, xhr) {
65 this.clear_list();
66 var len = data.length;
67 for (var i=0; i<len; i++) {
68 var item_div = $('<div/>');
69 var item = new ClusterItem(item_div);
70 item.update_state(data[i]);
71 item_div.data('item', item);
72 this.element.append(item_div);
73 };
74 };
75
76
77 var ClusterItem = function (element) {
78 this.element = $(element);
79 this.data = null;
80 this.style();
81 };
82
83
84 ClusterItem.prototype.style = function () {
85 this.element.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
86 this.element.css('border-top-style','none');
87 }
88
89 ClusterItem.prototype.update_state = function (data) {
90 this.data = data;
91 if (data.status === 'running') {
92 this.state_running();
93 } else if (data.status === 'stopped') {
94 this.state_stopped();
95 };
96
97 }
98
99
100 ClusterItem.prototype.state_stopped = function () {
101 var that = this;
102 this.element.empty();
103 var profile_col = $('<span/>').addClass('profile_col').text(this.data.profile);
104 var status_col = $('<span/>').addClass('status_col').html('stopped');
105 var engines_col = $('<span/>').addClass('engines_col');
106 var input = $('<input/>').attr('type','text').
107 attr('size',3).addClass('engine_num_input');
108 engines_col.append(input);
109 var action_col = $('<span/>').addClass('action_col');
110 var start_button = $('<button>Start</button>').button();
111 action_col.append(start_button);
112 this.element.append(profile_col).
113 append(action_col).
114 append(engines_col).
115 append(status_col);
116 start_button.click(function (e) {
117 var n = that.element.find('.engine_num_input').val();
118 if (!/^\d+$/.test(n) && n.length>0) {
119 status_col.html('invalid engine #');
120 } else {
121 var settings = {
122 cache : false,
123 data : {n:n},
124 type : "POST",
125 dataType : "json",
126 success : function (data, status, xhr) {
127 that.update_state(data);
128 },
129 error : function (data, status, xhr) {
130 status_col.html("error starting cluster")
131 }
132 };
133 status_col.html('starting');
134 var url = $('body').data('baseProjectUrl') + 'clusters/' + that.data.profile + '/start';
135 $.ajax(url, settings);
136 };
137 });
138 };
139
140
141 ClusterItem.prototype.state_running = function () {
142 this.element.empty();
143 var that = this;
144 var profile_col = $('<span/>').addClass('profile_col').text(this.data.profile);
145 var status_col = $('<span/>').addClass('status_col').html('running');
146 var engines_col = $('<span/>').addClass('engines_col').html(this.data.n);
147 var action_col = $('<span/>').addClass('action_col');
148 var stop_button = $('<button>Stop</button>').button();
149 action_col.append(stop_button);
150 this.element.append(profile_col).
151 append(action_col).
152 append(engines_col).
153 append(status_col);
154 stop_button.click(function (e) {
155 var settings = {
156 cache : false,
157 type : "POST",
158 dataType : "json",
159 success : function (data, status, xhr) {
160 that.update_state(data);
161 },
162 error : function (data, status, xhr) {
163 console.log('error',data);
164 status_col.html("error stopping cluster")
165 }
166 };
167 status_col.html('stopping')
168 var url = $('body').data('baseProjectUrl') + 'clusters/' + that.data.profile + '/stop';
169 $.ajax(url, settings);
170 });
171 };
172
173
174 IPython.ClusterList = ClusterList;
175 IPython.ClusterItem = ClusterItem;
176
177 return IPython;
178
179 }(IPython));
180
@@ -0,0 +1,83 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // MathJax initialization
10 //============================================================================
11
12 var IPython = (function (IPython) {
13
14 var init_mathjax = function () {
15 if (window.MathJax) {
16 // MathJax loaded
17 MathJax.Hub.Config({
18 tex2jax: {
19 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
20 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
21 },
22 displayAlign: 'left', // Change this to 'center' to center equations.
23 "HTML-CSS": {
24 styles: {'.MathJax_Display': {"margin": 0}}
25 }
26 });
27 } else if (window.mathjax_url != "") {
28 // Don't have MathJax, but should. Show dialog.
29 var dialog = $('<div></div>')
30 .append(
31 $("<p></p>").addClass('dialog').html(
32 "Math/LaTeX rendering will be disabled."
33 )
34 ).append(
35 $("<p></p>").addClass('dialog').html(
36 "If you have administrative access to the notebook server and" +
37 " a working internet connection, you can install a local copy" +
38 " of MathJax for offline use with the following command on the server" +
39 " at a Python or IPython prompt:"
40 )
41 ).append(
42 $("<pre></pre>").addClass('dialog').html(
43 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
44 )
45 ).append(
46 $("<p></p>").addClass('dialog').html(
47 "This will try to install MathJax into the IPython source directory."
48 )
49 ).append(
50 $("<p></p>").addClass('dialog').html(
51 "If IPython is installed to a location that requires" +
52 " administrative privileges to write, you will need to make this call as" +
53 " an administrator, via 'sudo'."
54 )
55 ).append(
56 $("<p></p>").addClass('dialog').html(
57 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
58 )
59 ).append(
60 $("<pre></pre>").addClass('dialog').html(
61 "$ ipython notebook --no-mathjax"
62 )
63 ).append(
64 $("<p></p>").addClass('dialog').html(
65 "which will prevent this dialog from appearing."
66 )
67 ).dialog({
68 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
69 width: "70%",
70 modal: true,
71 })
72 } else {
73 // No MathJax, but none expected. No dialog.
74 };
75 };
76
77
78 // Set module variables
79 IPython.init_mathjax = init_mathjax;
80
81 return IPython;
82
83 }(IPython)); No newline at end of file
@@ -0,0 +1,20 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // On document ready
10 //============================================================================
11
12
13 $(document).ready(function () {
14
15 IPython.page = new IPython.Page();
16 $('div#main_app').addClass('border-box-sizing ui-widget');
17 IPython.page.show();
18
19 });
20
@@ -0,0 +1,59 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // Global header/site setup.
10 //============================================================================
11
12 var IPython = (function (IPython) {
13
14 var Page = function () {
15 this.style();
16 this.bind_events();
17 };
18
19 Page.prototype.style = function () {
20 $('div#header').addClass('border-box-sizing').
21 addClass('ui-widget ui-widget-content').
22 css('border-top-style','none').
23 css('border-left-style','none').
24 css('border-right-style','none');
25 $('div#site').addClass('border-box-sizing')
26 };
27
28
29 Page.prototype.bind_events = function () {
30 };
31
32
33 Page.prototype.show = function () {
34 // The header and site divs start out hidden to prevent FLOUC.
35 // Main scripts should call this method after styling everything.
36 this.show_header();
37 this.show_site();
38 };
39
40
41 Page.prototype.show_header = function () {
42 // The header and site divs start out hidden to prevent FLOUC.
43 // Main scripts should call this method after styling everything.
44 $('div#header').css('display','block');
45 };
46
47
48 Page.prototype.show_site = function () {
49 // The header and site divs start out hidden to prevent FLOUC.
50 // Main scripts should call this method after styling everything.
51 $('div#site').css('display','block');
52 };
53
54
55 IPython.Page = Page;
56
57 return IPython;
58
59 }(IPython));
@@ -0,0 +1,19 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // On document ready
10 //============================================================================
11
12
13 $(document).ready(function () {
14
15 IPython.page = new IPython.Page();
16 IPython.page.show();
17
18 });
19
@@ -676,3 +676,27 b' class KVArgParseConfigLoader(ArgParseConfigLoader):'
676 sub_parser.load_config(self.extra_args)
676 sub_parser.load_config(self.extra_args)
677 self.config._merge(sub_parser.config)
677 self.config._merge(sub_parser.config)
678 self.extra_args = sub_parser.extra_args
678 self.extra_args = sub_parser.extra_args
679
680
681 def load_pyconfig_files(config_files, path):
682 """Load multiple Python config files, merging each of them in turn.
683
684 Parameters
685 ==========
686 config_files : list of str
687 List of config files names to load and merge into the config.
688 path : unicode
689 The full path to the location of the config files.
690 """
691 config = Config()
692 for cf in config_files:
693 loader = PyFileConfigLoader(cf, path=path)
694 try:
695 next_config = loader.load_config()
696 except ConfigFileNotFound:
697 pass
698 except:
699 raise
700 else:
701 config._merge(next_config)
702 return config
@@ -92,6 +92,29 b' ipython profile list -h # show the help string for the list subcommand'
92 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
93
93
94
94
95 def list_profiles_in(path):
96 """list profiles in a given root directory"""
97 files = os.listdir(path)
98 profiles = []
99 for f in files:
100 full_path = os.path.join(path, f)
101 if os.path.isdir(full_path) and f.startswith('profile_'):
102 profiles.append(f.split('_',1)[-1])
103 return profiles
104
105
106 def list_bundled_profiles():
107 """list profiles that are bundled with IPython."""
108 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
109 files = os.listdir(path)
110 profiles = []
111 for profile in files:
112 full_path = os.path.join(path, profile)
113 if os.path.isdir(full_path):
114 profiles.append(profile)
115 return profiles
116
117
95 class ProfileList(Application):
118 class ProfileList(Application):
96 name = u'ipython-profile'
119 name = u'ipython-profile'
97 description = list_help
120 description = list_help
@@ -115,35 +138,15 b' class ProfileList(Application):'
115 the environment variable IPYTHON_DIR.
138 the environment variable IPYTHON_DIR.
116 """
139 """
117 )
140 )
118
141
119 def _list_profiles_in(self, path):
142
120 """list profiles in a given root directory"""
121 files = os.listdir(path)
122 profiles = []
123 for f in files:
124 full_path = os.path.join(path, f)
125 if os.path.isdir(full_path) and f.startswith('profile_'):
126 profiles.append(f.split('_',1)[-1])
127 return profiles
128
129 def _list_bundled_profiles(self):
130 """list profiles in a given root directory"""
131 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
132 files = os.listdir(path)
133 profiles = []
134 for profile in files:
135 full_path = os.path.join(path, profile)
136 if os.path.isdir(full_path):
137 profiles.append(profile)
138 return profiles
139
140 def _print_profiles(self, profiles):
143 def _print_profiles(self, profiles):
141 """print list of profiles, indented."""
144 """print list of profiles, indented."""
142 for profile in profiles:
145 for profile in profiles:
143 print ' %s' % profile
146 print ' %s' % profile
144
147
145 def list_profile_dirs(self):
148 def list_profile_dirs(self):
146 profiles = self._list_bundled_profiles()
149 profiles = list_bundled_profiles()
147 if profiles:
150 if profiles:
148 print
151 print
149 print "Available profiles in IPython:"
152 print "Available profiles in IPython:"
@@ -153,13 +156,13 b' class ProfileList(Application):'
153 print " into your IPython directory (%s)," % self.ipython_dir
156 print " into your IPython directory (%s)," % self.ipython_dir
154 print " where you can customize it."
157 print " where you can customize it."
155
158
156 profiles = self._list_profiles_in(self.ipython_dir)
159 profiles = list_profiles_in(self.ipython_dir)
157 if profiles:
160 if profiles:
158 print
161 print
159 print "Available profiles in %s:" % self.ipython_dir
162 print "Available profiles in %s:" % self.ipython_dir
160 self._print_profiles(profiles)
163 self._print_profiles(profiles)
161
164
162 profiles = self._list_profiles_in(os.getcwdu())
165 profiles = list_profiles_in(os.getcwdu())
163 if profiles:
166 if profiles:
164 print
167 print
165 print "Available profiles in current directory (%s):" % os.getcwdu()
168 print "Available profiles in current directory (%s):" % os.getcwdu()
@@ -1,3 +1,4 b''
1 # coding: utf-8
1 """Tests for profile-related functions.
2 """Tests for profile-related functions.
2
3
3 Currently only the startup-dir functionality is tested, but more tests should
4 Currently only the startup-dir functionality is tested, but more tests should
@@ -25,9 +26,12 b' import shutil'
25 import sys
26 import sys
26 import tempfile
27 import tempfile
27
28
29 from unittest import TestCase
30
28 import nose.tools as nt
31 import nose.tools as nt
29 from nose import SkipTest
32 from nose import SkipTest
30
33
34 from IPython.core.profileapp import list_profiles_in, list_bundled_profiles
31 from IPython.core.profiledir import ProfileDir
35 from IPython.core.profiledir import ProfileDir
32
36
33 from IPython.testing import decorators as dec
37 from IPython.testing import decorators as dec
@@ -79,35 +83,69 b' def win32_without_pywin32():'
79 return False
83 return False
80
84
81
85
82 @dec.skipif(win32_without_pywin32(), "Test requires pywin32 on Windows")
86 class ProfileStartupTest(TestCase):
83 def test_startup_py():
87 def setUp(self):
84 # create profile dir
88 # create profile dir
85 pd = ProfileDir.create_profile_dir_by_name(IP_TEST_DIR, 'test')
89 self.pd = ProfileDir.create_profile_dir_by_name(IP_TEST_DIR, 'test')
86 # write startup python file
90 self.options = ['--ipython-dir', IP_TEST_DIR, '--profile', 'test']
87 with open(os.path.join(pd.startup_dir, '00-start.py'), 'w') as f:
91 self.fname = os.path.join(TMP_TEST_DIR, 'test.py')
88 f.write('zzz=123\n')
92
89 # write simple test file, to check that the startup file was run
93 def tearDown(self):
90 fname = os.path.join(TMP_TEST_DIR, 'test.py')
94 # We must remove this profile right away so its presence doesn't
91 with open(fname, 'w') as f:
95 # confuse other tests.
92 f.write(py3compat.doctest_refactor_print('print zzz\n'))
96 shutil.rmtree(self.pd.location)
93 # validate output
97
94 tt.ipexec_validate(fname, '123', '',
98 def init(self, startup_file, startup, test):
95 options=['--ipython-dir', IP_TEST_DIR, '--profile', 'test'])
99 # write startup python file
96
100 with open(os.path.join(self.pd.startup_dir, startup_file), 'w') as f:
97 @dec.skipif(win32_without_pywin32(), "Test requires pywin32 on Windows")
101 f.write(startup)
98 def test_startup_ipy():
102 # write simple test file, to check that the startup file was run
99 # create profile dir
103 with open(self.fname, 'w') as f:
100 pd = ProfileDir.create_profile_dir_by_name(IP_TEST_DIR, 'test')
104 f.write(py3compat.doctest_refactor_print(test))
101 # write startup ipython file
105
102 with open(os.path.join(pd.startup_dir, '00-start.ipy'), 'w') as f:
106 def validate(self, output):
103 f.write('%profile\n')
107 tt.ipexec_validate(self.fname, output, '', options=self.options)
104 # write empty script, because we don't need anything to happen
108
105 # after the startup file is run
109 @dec.skipif(win32_without_pywin32(), "Test requires pywin32 on Windows")
106 fname = os.path.join(TMP_TEST_DIR, 'test.py')
110 def test_startup_py(self):
107 with open(fname, 'w') as f:
111 self.init('00-start.py', 'zzz=123\n',
108 f.write('')
112 py3compat.doctest_refactor_print('print zzz\n'))
109 # validate output
113 self.validate('123')
110 tt.ipexec_validate(fname, 'test', '',
114
111 options=['--ipython-dir', IP_TEST_DIR, '--profile', 'test'])
115 @dec.skipif(win32_without_pywin32(), "Test requires pywin32 on Windows")
116 def test_startup_ipy(self):
117 self.init('00-start.ipy', '%profile\n', '')
118 self.validate('test')
119
112
120
121 def test_list_profiles_in():
122 # No need to remove these directories and files, as they will get nuked in
123 # the module-level teardown.
124 td = tempfile.mkdtemp(dir=TMP_TEST_DIR)
125 td = py3compat.str_to_unicode(td)
126 for name in ('profile_foo', u'profile_ΓΌnicode', 'profile_hello',
127 'not_a_profile'):
128 os.mkdir(os.path.join(td, name))
129 with open(os.path.join(td, 'profile_file'), 'w') as f:
130 f.write("I am not a profile directory")
131 profiles = list_profiles_in(td)
113
132
133 # unicode normalization can turn u'ΓΌnicode' into u'u\0308nicode',
134 # so only check for *nicode, and that creating a ProfileDir from the
135 # name remains valid
136 found_unicode = False
137 for p in list(profiles):
138 if p.endswith('nicode'):
139 pd = ProfileDir.find_profile_dir_by_name(td, p)
140 profiles.remove(p)
141 found_unicode = True
142 break
143 nt.assert_true(found_unicode)
144 nt.assert_equals(set(profiles), set(['foo', 'hello']))
145
146
147 def test_list_bundled_profiles():
148 # This variable will need to be updated when a new profile gets bundled
149 bundled_true = [u'cluster', u'math', u'pysh', u'python3', u'sympy']
150 bundled = sorted(list_bundled_profiles())
151 nt.assert_equals(bundled, bundled_true)
@@ -211,6 +211,7 b' class LoginHandler(AuthenticatedHandler):'
211 read_only=self.read_only,
211 read_only=self.read_only,
212 logged_in=self.logged_in,
212 logged_in=self.logged_in,
213 login_available=self.login_available,
213 login_available=self.login_available,
214 base_project_url=self.application.ipython_app.base_project_url,
214 message=message
215 message=message
215 )
216 )
216
217
@@ -246,6 +247,7 b' class LogoutHandler(AuthenticatedHandler):'
246 read_only=self.read_only,
247 read_only=self.read_only,
247 logged_in=self.logged_in,
248 logged_in=self.logged_in,
248 login_available=self.login_available,
249 login_available=self.login_available,
250 base_project_url=self.application.ipython_app.base_project_url,
249 message=message)
251 message=message)
250
252
251
253
@@ -587,7 +589,6 b' class NotebookRootHandler(AuthenticatedHandler):'
587
589
588 @authenticate_unless_readonly
590 @authenticate_unless_readonly
589 def get(self):
591 def get(self):
590
591 nbm = self.application.notebook_manager
592 nbm = self.application.notebook_manager
592 files = nbm.list_notebooks()
593 files = nbm.list_notebooks()
593 self.finish(jsonapi.dumps(files))
594 self.finish(jsonapi.dumps(files))
@@ -661,6 +662,44 b' class NotebookCopyHandler(AuthenticatedHandler):'
661 mathjax_url=self.application.ipython_app.mathjax_url,
662 mathjax_url=self.application.ipython_app.mathjax_url,
662 )
663 )
663
664
665
666 #-----------------------------------------------------------------------------
667 # Cluster handlers
668 #-----------------------------------------------------------------------------
669
670
671 class MainClusterHandler(AuthenticatedHandler):
672
673 @web.authenticated
674 def get(self):
675 cm = self.application.cluster_manager
676 self.finish(jsonapi.dumps(cm.list_profiles()))
677
678
679 class ClusterProfileHandler(AuthenticatedHandler):
680
681 @web.authenticated
682 def get(self, profile):
683 cm = self.application.cluster_manager
684 self.finish(jsonapi.dumps(cm.profile_info(profile)))
685
686
687 class ClusterActionHandler(AuthenticatedHandler):
688
689 @web.authenticated
690 def post(self, profile, action):
691 cm = self.application.cluster_manager
692 if action == 'start':
693 n = self.get_argument('n',default=None)
694 if n is None:
695 data = cm.start_cluster(profile)
696 else:
697 data = cm.start_cluster(profile,int(n))
698 if action == 'stop':
699 data = cm.stop_cluster(profile)
700 self.finish(jsonapi.dumps(data))
701
702
664 #-----------------------------------------------------------------------------
703 #-----------------------------------------------------------------------------
665 # RST web service handlers
704 # RST web service handlers
666 #-----------------------------------------------------------------------------
705 #-----------------------------------------------------------------------------
@@ -49,9 +49,11 b' from .handlers import (LoginHandler, LogoutHandler,'
49 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
49 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
50 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
50 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
51 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
51 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
52 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler
52 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
53 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler
53 )
54 )
54 from .notebookmanager import NotebookManager
55 from .notebookmanager import NotebookManager
56 from .clustermanager import ClusterManager
55
57
56 from IPython.config.application import catch_config_error, boolean_flag
58 from IPython.config.application import catch_config_error, boolean_flag
57 from IPython.core.application import BaseIPythonApplication
59 from IPython.core.application import BaseIPythonApplication
@@ -74,6 +76,9 b' from IPython.utils import py3compat'
74 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
76 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
75 _kernel_action_regex = r"(?P<action>restart|interrupt)"
77 _kernel_action_regex = r"(?P<action>restart|interrupt)"
76 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
78 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
79 _profile_regex = r"(?P<profile>[a-zA-Z0-9]+)"
80 _cluster_action_regex = r"(?P<action>start|stop)"
81
77
82
78 LOCALHOST = '127.0.0.1'
83 LOCALHOST = '127.0.0.1'
79
84
@@ -101,7 +106,8 b' def url_path_join(a,b):'
101
106
102 class NotebookWebApplication(web.Application):
107 class NotebookWebApplication(web.Application):
103
108
104 def __init__(self, ipython_app, kernel_manager, notebook_manager, log,
109 def __init__(self, ipython_app, kernel_manager, notebook_manager,
110 cluster_manager, log,
105 base_project_url, settings_overrides):
111 base_project_url, settings_overrides):
106 handlers = [
112 handlers = [
107 (r"/", ProjectDashboardHandler),
113 (r"/", ProjectDashboardHandler),
@@ -120,6 +126,9 b' class NotebookWebApplication(web.Application):'
120 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
126 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
121 (r"/rstservice/render", RSTHandler),
127 (r"/rstservice/render", RSTHandler),
122 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
128 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
129 (r"/clusters", MainClusterHandler),
130 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
131 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
123 ]
132 ]
124 settings = dict(
133 settings = dict(
125 template_path=os.path.join(os.path.dirname(__file__), "templates"),
134 template_path=os.path.join(os.path.dirname(__file__), "templates"),
@@ -151,10 +160,11 b' class NotebookWebApplication(web.Application):'
151 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
160 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
152
161
153 self.kernel_manager = kernel_manager
162 self.kernel_manager = kernel_manager
154 self.log = log
155 self.notebook_manager = notebook_manager
163 self.notebook_manager = notebook_manager
164 self.cluster_manager = cluster_manager
156 self.ipython_app = ipython_app
165 self.ipython_app = ipython_app
157 self.read_only = self.ipython_app.read_only
166 self.read_only = self.ipython_app.read_only
167 self.log = log
158
168
159
169
160 #-----------------------------------------------------------------------------
170 #-----------------------------------------------------------------------------
@@ -395,6 +405,8 b' class NotebookApp(BaseIPythonApplication):'
395 )
405 )
396 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
406 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
397 self.notebook_manager.list_notebooks()
407 self.notebook_manager.list_notebooks()
408 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
409 self.cluster_manager.update_profiles()
398
410
399 def init_logging(self):
411 def init_logging(self):
400 super(NotebookApp, self).init_logging()
412 super(NotebookApp, self).init_logging()
@@ -406,7 +418,8 b' class NotebookApp(BaseIPythonApplication):'
406 def init_webapp(self):
418 def init_webapp(self):
407 """initialize tornado webapp and httpserver"""
419 """initialize tornado webapp and httpserver"""
408 self.web_app = NotebookWebApplication(
420 self.web_app = NotebookWebApplication(
409 self, self.kernel_manager, self.notebook_manager, self.log,
421 self, self.kernel_manager, self.notebook_manager,
422 self.cluster_manager, self.log,
410 self.base_project_url, self.webapp_settings
423 self.base_project_url, self.webapp_settings
411 )
424 )
412 if self.certfile:
425 if self.certfile:
@@ -1,10 +1,4 b''
1
1
2 .border-box-sizing {
3 box-sizing: border-box;
4 -moz-box-sizing: border-box;
5 -webkit-box-sizing: border-box;
6 }
7
8 /* Flexible box model classes */
2 /* Flexible box model classes */
9 /* Taken from Alex Russell http://infrequently.org/2009/08/css-3-progress/ */
3 /* Taken from Alex Russell http://infrequently.org/2009/08/css-3-progress/ */
10
4
@@ -101,30 +95,3 b''
101 -moz-box-pack: center;
95 -moz-box-pack: center;
102 box-pack: center;
96 box-pack: center;
103 }
97 }
104
105 .message {
106 border-width: 1px;
107 border-style: solid;
108 text-align: center;
109 padding: 0.5em;
110 margin: 0.5em 0;
111 }
112
113 .message.error {
114 background-color: #FFD3D1;
115 border-color: red;
116 }
117
118 .message.warning {
119 background-color: #FFD09E;
120 border-color: orange;
121 }
122
123 .message.info {
124 background-color: #CBFFBA;
125 border-color: green;
126 }
127
128 #content_panel {
129 margin: 0.5em;
130 } No newline at end of file
@@ -6,14 +6,6 b''
6
6
7
7
8 body {
8 body {
9 background-color: white;
10 /* This makes sure that the body covers the entire window and needs to
11 be in a different element than the display: box in wrapper below */
12 position: absolute;
13 left: 0px;
14 right: 0px;
15 top: 0px;
16 bottom: 0px;
17 overflow: hidden;
9 overflow: hidden;
18 }
10 }
19
11
@@ -31,11 +23,6 b' span#notebook_name {'
31 font-size: 146.5%;
23 font-size: 146.5%;
32 }
24 }
33
25
34 #menubar {
35 /* Initially hidden to prevent FLOUC */
36 display: none;
37 }
38
39 .ui-menubar-item .ui-button .ui-button-text {
26 .ui-menubar-item .ui-button .ui-button-text {
40 padding: 0.4em 1.0em;
27 padding: 0.4em 1.0em;
41 font-size: 100%;
28 font-size: 100%;
@@ -69,8 +56,6 b' span#notebook_name {'
69 }
56 }
70
57
71 #toolbar {
58 #toolbar {
72 /* Initially hidden to prevent FLOUC */
73 display: none;
74 padding: 3px 15px;
59 padding: 3px 15px;
75 }
60 }
76
61
@@ -78,6 +63,12 b' span#notebook_name {'
78 font-size: 85%;
63 font-size: 85%;
79 }
64 }
80
65
66
67 div#main_app {
68 width: 100%;
69 position: relative;
70 }
71
81 span#quick_help_area {
72 span#quick_help_area {
82 position: static;
73 position: static;
83 padding: 5px 0px;
74 padding: 5px 0px;
@@ -14,7 +14,7 b' body {'
14 right: 0px;
14 right: 0px;
15 top: 0px;
15 top: 0px;
16 bottom: 0px;
16 bottom: 0px;
17 overflow: hidden;
17 overflow: visible;
18 }
18 }
19
19
20
20
@@ -41,11 +41,9 b' span#ipython_notebook h1 img {'
41 color: black;
41 color: black;
42 }
42 }
43
43
44 div#main_app {
44 #site {
45 /* Initially hidden to prevent FLOUC */
46 display: none;
47 width: 100%;
45 width: 100%;
48 position: relative;
46 display: none;
49 }
47 }
50
48
51 /* We set the fonts by hand here to override the values in the theme */
49 /* We set the fonts by hand here to override the values in the theme */
@@ -63,11 +61,17 b' div#main_app {'
63 font-size: 77%;
61 font-size: 77%;
64 }
62 }
65
63
64 input.ui-button {
65 padding: 0.3em 0.9em;
66 }
67
66 span#login_widget {
68 span#login_widget {
67 float: right;
69 float: right;
68 }
70 }
69
71
70 /* generic class for hidden objects */
72 .border-box-sizing {
71 .hidden {
73 box-sizing: border-box;
72 display: none;
74 -moz-box-sizing: border-box;
75 -webkit-box-sizing: border-box;
73 }
76 }
77
@@ -5,70 +5,68 b''
5 * Author: IPython Development Team
5 * Author: IPython Development Team
6 */
6 */
7
7
8
8 #main_app {
9 body {
9 width: 920px;
10 background-color: white;
10 margin: 30px auto 0px auto;
11 /* This makes sure that the body covers the entire window and needs to
12 be in a different element than the display: box in wrapper below */
13 position: absolute;
14 left: 0px;
15 right: 0px;
16 top: 0px;
17 bottom: 0px;
18 overflow: auto;
19 }
20
21 #left_panel {
22 }
11 }
23
12
24 #drop_zone {
13 #tabs {
25 height: 200px;
14 border-style: none;
26 width: 200px
27 }
15 }
28
16
29 #content_panel {
17 #tab1, #tab2 {
30 width: 600px;
18 padding: 1em 0em;
31 }
19 }
32
20
33 #content_toolbar {
21 .list_toolbar {
34 padding: 5px;
22 padding: 5px;
35 height: 25px;
23 height: 25px;
36 line-height: 25px;
24 line-height: 25px;
37 }
25 }
38
26
39 #header_border {
27 .toolbar_info {
40 width: 100%;
41 height: 2px;
42 }
43
44 #app_hbox {
45 width: 100%;
46 }
47
48 #drag_info {
49 float: left;
28 float: left;
50 }
29 }
51
30
52 #notebooks_buttons {
31 .toolbar_buttons {
53 float: right;
32 float: right;
54 }
33 }
55
34
56 #project_name {
35 .list_header {
57 height: 25px;
36 height: 25px;
58 line-height: 25px;
37 line-height: 25px;
59 padding: 3px;
38 padding: 3px 5px;
60 }
39 }
61
40
62 .notebook_item {
41
42
43 .list_item {
63 height: 25px;
44 height: 25px;
64 line-height: 25px;
45 line-height: 25px;
65 padding: 3px;
46 padding: 3px 5px;
66 }
47 }
67
48
68 .notebook_item a {
49 .notebook_item a {
69 text-decoration: none;
50 text-decoration: none;
70 }
51 }
71
52
53 .profile_col {
54 }
55
56 .status_col {
57 float: right;
58 width: 325px;
59 }
60
61 .engines_col {
62 float: right;
63 width: 325px;
64 }
65
66 .action_col {
67 float: right;
68 }
69
72 .item_buttons {
70 .item_buttons {
73 float: right;
71 float: right;
74 }
72 }
@@ -80,3 +78,7 b' body {'
80 .highlight_text {
78 .highlight_text {
81 color: blue;
79 color: blue;
82 }
80 }
81
82 .ui-tabs .ui-tabs-nav li a {
83 padding: .3em .5em;
84 }
@@ -38,9 +38,9 b' var IPython = (function (IPython) {'
38 } else {
38 } else {
39 toolbar_height = $('div#toolbar').outerHeight(true);
39 toolbar_height = $('div#toolbar').outerHeight(true);
40 }
40 }
41 var app_height = h-header_height-menubar_height-toolbar_height-2; // content height
41 var app_height = h-header_height-menubar_height-toolbar_height; // content height
42
42
43 $('div#main_app').height(app_height + 2); // content+padding+border height
43 $('div#main_app').height(app_height); // content+padding+border height
44
44
45 var pager_height = IPython.pager.percentage_height*app_height;
45 var pager_height = IPython.pager.percentage_height*app_height;
46 var pager_splitter_height = $('div#pager_splitter').outerHeight(true);
46 var pager_splitter_height = $('div#pager_splitter').outerHeight(true);
@@ -12,19 +12,11 b''
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14
14
15 $('div#header').addClass('border-box-sizing');
15 IPython.page = new IPython.Page();
16 $('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content');
16 $('input#login_submit').button();
17
18 $('div#main_app').addClass('border-box-sizing ui-widget');
17 $('div#main_app').addClass('border-box-sizing ui-widget');
19 $('div#app_hbox').addClass('hbox');
18 IPython.page.show();
20
19 $('input#password_input').focus();
21 $('div#left_panel').addClass('box-flex');
22 $('div#right_panel').addClass('box-flex');
23 $('input#signin').button();
24
25 // These have display: none in the css file and are made visible here to prevent FLOUC.
26 $('div#header').css('display','block');
27 $('div#main_app').css('display','block');
28
20
29 });
21 });
30
22
@@ -24,6 +24,8 b' var IPython = (function (IPython) {'
24 this.element.find('button#logout').button();
24 this.element.find('button#logout').button();
25 this.element.find('button#login').button();
25 this.element.find('button#login').button();
26 };
26 };
27
28
27 LoginWidget.prototype.bind_events = function () {
29 LoginWidget.prototype.bind_events = function () {
28 var that = this;
30 var that = this;
29 this.element.find("button#logout").click(function () {
31 this.element.find("button#logout").click(function () {
@@ -22,6 +22,7 b' var IPython = (function (IPython) {'
22
22
23
23
24 MenuBar.prototype.style = function () {
24 MenuBar.prototype.style = function () {
25 this.element.addClass('border-box-sizing');
25 $('ul#menus').menubar({
26 $('ul#menus').menubar({
26 select : function (event, ui) {
27 select : function (event, ui) {
27 // The selected cell loses focus when the menu is entered, so we
28 // The selected cell loses focus when the menu is entered, so we
@@ -21,8 +21,14 b' var IPython = (function (IPython) {'
21 };
21 };
22
22
23 NotebookList.prototype.style = function () {
23 NotebookList.prototype.style = function () {
24 this.element.addClass('ui-widget ui-widget-content');
24 $('#notebook_toolbar').addClass('list_toolbar');
25 $('div#project_name').addClass('ui-widget ui-widget-header');
25 $('#drag_info').addClass('toolbar_info');
26 $('#notebook_buttons').addClass('toolbar_buttons');
27 $('div#project_name').addClass('list_header ui-widget ui-widget-header');
28 $('#refresh_notebook_list').button({
29 icons : {primary: 'ui-icon-arrowrefresh-1-s'},
30 text : false
31 });
26 };
32 };
27
33
28
34
@@ -31,6 +37,9 b' var IPython = (function (IPython) {'
31 return;
37 return;
32 }
38 }
33 var that = this;
39 var that = this;
40 $('#refresh_notebook_list').click(function () {
41 that.load_list();
42 });
34 this.element.bind('dragover', function () {
43 this.element.bind('dragover', function () {
35 return false;
44 return false;
36 });
45 });
@@ -62,7 +71,13 b' var IPython = (function (IPython) {'
62 };
71 };
63
72
64
73
74 NotebookList.prototype.clear_list = function () {
75 this.element.children('.list_item').remove();
76 }
77
78
65 NotebookList.prototype.load_list = function () {
79 NotebookList.prototype.load_list = function () {
80 this.clear_list();
66 var settings = {
81 var settings = {
67 processData : false,
82 processData : false,
68 cache : false,
83 cache : false,
@@ -93,7 +108,8 b' var IPython = (function (IPython) {'
93
108
94 NotebookList.prototype.new_notebook_item = function (index) {
109 NotebookList.prototype.new_notebook_item = function (index) {
95 var item = $('<div/>');
110 var item = $('<div/>');
96 item.addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix');
111 item.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
112 item.css('border-top-style','none');
97 var item_name = $('<span/>').addClass('item_name');
113 var item_name = $('<span/>').addClass('item_name');
98
114
99 item.append(item_name);
115 item.append(item_name);
@@ -156,7 +172,7 b' var IPython = (function (IPython) {'
156 var that = $(this);
172 var that = $(this);
157 // We use the nbname and notebook_id from the parent notebook_item element's
173 // We use the nbname and notebook_id from the parent notebook_item element's
158 // data because the outer scopes values change as we iterate through the loop.
174 // data because the outer scopes values change as we iterate through the loop.
159 var parent_item = that.parents('div.notebook_item');
175 var parent_item = that.parents('div.list_item');
160 var nbname = parent_item.data('nbname');
176 var nbname = parent_item.data('nbname');
161 var notebook_id = parent_item.data('notebook_id');
177 var notebook_id = parent_item.data('notebook_id');
162 var dialog = $('<div/>');
178 var dialog = $('<div/>');
@@ -11,75 +11,17 b''
11
11
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14 if (window.MathJax){
15 // MathJax loaded
16 MathJax.Hub.Config({
17 tex2jax: {
18 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
19 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
20 },
21 displayAlign: 'left', // Change this to 'center' to center equations.
22 "HTML-CSS": {
23 styles: {'.MathJax_Display': {"margin": 0}}
24 }
25 });
26 }else if (window.mathjax_url != ""){
27 // Don't have MathJax, but should. Show dialog.
28 var dialog = $('<div></div>')
29 .append(
30 $("<p></p>").addClass('dialog').html(
31 "Math/LaTeX rendering will be disabled."
32 )
33 ).append(
34 $("<p></p>").addClass('dialog').html(
35 "If you have administrative access to the notebook server and" +
36 " a working internet connection, you can install a local copy" +
37 " of MathJax for offline use with the following command on the server" +
38 " at a Python or IPython prompt:"
39 )
40 ).append(
41 $("<pre></pre>").addClass('dialog').html(
42 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
43 )
44 ).append(
45 $("<p></p>").addClass('dialog').html(
46 "This will try to install MathJax into the IPython source directory."
47 )
48 ).append(
49 $("<p></p>").addClass('dialog').html(
50 "If IPython is installed to a location that requires" +
51 " administrative privileges to write, you will need to make this call as" +
52 " an administrator, via 'sudo'."
53 )
54 ).append(
55 $("<p></p>").addClass('dialog').html(
56 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
57 )
58 ).append(
59 $("<pre></pre>").addClass('dialog').html(
60 "$ ipython notebook --no-mathjax"
61 )
62 ).append(
63 $("<p></p>").addClass('dialog').html(
64 "which will prevent this dialog from appearing."
65 )
66 ).dialog({
67 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
68 width: "70%",
69 modal: true,
70 })
71 }else{
72 // No MathJax, but none expected. No dialog.
73 }
74
75
14
76 IPython.markdown_converter = new Markdown.Converter();
15 IPython.init_mathjax();
77 IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
78
16
79 $('div#header').addClass('border-box-sizing');
17 IPython.read_only = $('body').data('readOnly') === 'True';
80 $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content');
18 $('div#main_app').addClass('border-box-sizing ui-widget');
81 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
19 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
20 // The header's bottom border is provided by the menu bar so we remove it.
21 $('div#header').css('border-bottom-style','none');
82
22
23 IPython.page = new IPython.Page();
24 IPython.markdown_converter = new Markdown.Converter();
83 IPython.layout_manager = new IPython.LayoutManager();
25 IPython.layout_manager = new IPython.LayoutManager();
84 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
26 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
85 IPython.quick_help = new IPython.QuickHelp('span#quick_help_area');
27 IPython.quick_help = new IPython.QuickHelp('span#quick_help_area');
@@ -92,22 +34,16 b' $(document).ready(function () {'
92
34
93 IPython.layout_manager.do_resize();
35 IPython.layout_manager.do_resize();
94
36
95 // These have display: none in the css file and are made visible here to prevent FLOUC.
96 $('div#header').css('display','block');
97
98 if(IPython.read_only){
37 if(IPython.read_only){
99 // hide various elements from read-only view
38 // hide various elements from read-only view
100 $('div#pager').remove();
39 $('div#pager').remove();
101 $('div#pager_splitter').remove();
40 $('div#pager_splitter').remove();
102 $('span#login_widget').removeClass('hidden');
103
41
104 // set the notebook name field as not modifiable
42 // set the notebook name field as not modifiable
105 $('#notebook_name').attr('disabled','disabled')
43 $('#notebook_name').attr('disabled','disabled')
106 }
44 }
107
45
108 $('div#menubar').css('display','block');
46 IPython.page.show();
109 $('div#toolbar').css('display','block');
110 $('div#main_app').css('display','block');
111
47
112 IPython.layout_manager.do_resize();
48 IPython.layout_manager.do_resize();
113 $([IPython.events]).on('notebook_loaded.Notebook', function () {
49 $([IPython.events]).on('notebook_loaded.Notebook', function () {
@@ -11,81 +11,18 b''
11
11
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14 if (window.MathJax){
15 // MathJax loaded
16 MathJax.Hub.Config({
17 tex2jax: {
18 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
19 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
20 },
21 displayAlign: 'left', // Change this to 'center' to center equations.
22 "HTML-CSS": {
23 styles: {'.MathJax_Display': {"margin": 0}}
24 }
25 });
26 }else if (window.mathjax_url != ""){
27 // Don't have MathJax, but should. Show dialog.
28 var dialog = $('<div></div>')
29 .append(
30 $("<p></p>").addClass('dialog').html(
31 "Math/LaTeX rendering will be disabled."
32 )
33 ).append(
34 $("<p></p>").addClass('dialog').html(
35 "If you have administrative access to the notebook server and" +
36 " a working internet connection, you can install a local copy" +
37 " of MathJax for offline use with the following command on the server" +
38 " at a Python or IPython prompt:"
39 )
40 ).append(
41 $("<pre></pre>").addClass('dialog').html(
42 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
43 )
44 ).append(
45 $("<p></p>").addClass('dialog').html(
46 "This will try to install MathJax into the IPython source directory."
47 )
48 ).append(
49 $("<p></p>").addClass('dialog').html(
50 "If IPython is installed to a location that requires" +
51 " administrative privileges to write, you will need to make this call as" +
52 " an administrator, via 'sudo'."
53 )
54 ).append(
55 $("<p></p>").addClass('dialog').html(
56 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
57 )
58 ).append(
59 $("<pre></pre>").addClass('dialog').html(
60 "$ ipython notebook --no-mathjax"
61 )
62 ).append(
63 $("<p></p>").addClass('dialog').html(
64 "which will prevent this dialog from appearing."
65 )
66 ).dialog({
67 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
68 width: "70%",
69 modal: true,
70 })
71 }else{
72 // No MathJax, but none expected. No dialog.
73 }
74
75 IPython.markdown_converter = new Markdown.Converter();
76 IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
77
14
78 $('div#header').addClass('border-box-sizing');
15 IPython.init_mathjax();
79 $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content');
16
17 IPython.read_only = $('body').data('readOnly') === 'True';
18 $('div#main_app').addClass('border-box-sizing ui-widget');
80 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
19 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
81
20
21 IPython.page = new IPython.Page();
22 IPython.markdown_converter = new Markdown.Converter();
82 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
23 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
83 IPython.notebook = new IPython.Notebook('div#notebook');
24 IPython.notebook = new IPython.Notebook('div#notebook');
84 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
25 IPython.page.show_site();
85
86 // These have display: none in the css file and are made visible here to prevent FLOUC.
87 $('div#header').css('display','block');
88 $('div#main_app').css('display','block');
89
26
90 IPython.notebook.load_notebook($('body').data('notebookId'));
27 IPython.notebook.load_notebook($('body').data('notebookId'));
91
28
@@ -12,31 +12,28 b''
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14
14
15 $('div#header').addClass('border-box-sizing');
15 IPython.page = new IPython.Page();
16 $('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content');
17
16
17 $('div#tabs').tabs();
18 $('div#tabs').on('tabsselect', function (event, ui) {
19 var new_url = $('body').data('baseProjectUrl') + '#' + ui.panel.id;
20 window.history.replaceState({}, '', new_url);
21 });
18 $('div#main_app').addClass('border-box-sizing ui-widget');
22 $('div#main_app').addClass('border-box-sizing ui-widget');
19 $('div#app_hbox').addClass('hbox');
23 $('div#notebooks_toolbar').addClass('ui-widget ui-helper-clearfix');
20
21 $('div#content_toolbar').addClass('ui-widget ui-helper-clearfix');
22
23 $('#new_notebook').button().click(function (e) {
24 $('#new_notebook').button().click(function (e) {
24 window.open($('body').data('baseProjectUrl')+'new');
25 window.open($('body').data('baseProjectUrl')+'new');
25 });
26 });
26
27
27 $('div#left_panel').addClass('box-flex');
28 IPython.read_only = $('body').data('readOnly') === 'True';
28 $('div#right_panel').addClass('box-flex');
29
30 IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
31 IPython.notebook_list = new IPython.NotebookList('div#notebook_list');
29 IPython.notebook_list = new IPython.NotebookList('div#notebook_list');
30 IPython.cluster_list = new IPython.ClusterList('div#cluster_list');
32 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
31 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
33
32
34 IPython.notebook_list.load_list();
33 IPython.notebook_list.load_list();
34 IPython.cluster_list.load_list();
35
35
36 // These have display: none in the css file and are made visible here to prevent FLOUC.
36 IPython.page.show();
37 $('div#header').css('display','block');
38 $('div#main_app').css('display','block');
39
40
37
41 });
38 });
42
39
@@ -22,7 +22,11 b' var IPython = (function (IPython) {'
22
22
23
23
24 ToolBar.prototype.style = function () {
24 ToolBar.prototype.style = function () {
25 this.element.addClass('border-box-sizing');
25 this.element.addClass('border-box-sizing').
26 addClass('ui-widget ui-widget-content').
27 css('border-top-style','none').
28 css('border-left-style','none').
29 css('border-right-style','none');
26 this.element.find('#cell_type').addClass('ui-widget ui-widget-content');
30 this.element.find('#cell_type').addClass('ui-widget ui-widget-content');
27 this.element.find('#save_b').button({
31 this.element.find('#save_b').button({
28 icons : {primary: 'ui-icon-disk'},
32 icons : {primary: 'ui-icon-disk'},
@@ -1,26 +1,42 b''
1 {% extends layout.html %}
1 {% extends page.html %}
2
2
3 {% block content_panel %}
3 {% block stylesheet %}
4
4
5 {% if login_available %}
5 <link rel="stylesheet" href="{{static_url("css/login.css") }}" type="text/css"/>
6
7 {% end %}
8
9
10 {% block login_widget %}
11 {% end %}
12
13
14 {% block site %}
6
15
16 <div id="main_app">
17
18 {% if login_available %}
7 <form action="/login?next={{url_escape(next)}}" method="post">
19 <form action="/login?next={{url_escape(next)}}" method="post">
8 Password: <input type="password" name="password" id="focus">
20 Password: <input type="password" name="password" id="password_input">
9 <input type="submit" value="Sign in" id="signin">
21 <input type="submit" value="Log in" id="login_submit">
10 </form>
22 </form>
23 {% end %}
11
24
25 {% if message %}
26 {% for key in message %}
27 <div class="message {{key}}">
28 {{message[key]}}
29 </div>
30 {% end %}
12 {% end %}
31 {% end %}
13
32
14 {% end %}
33 <div/>
15
34
16 {% block login_widget %}
17 {% end %}
35 {% end %}
18
36
37
19 {% block script %}
38 {% block script %}
20 <script type="text/javascript">
39
21 $(document).ready(function() {
40 <script src="{{static_url("js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
22 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
41
23 $('#focus').focus();
24 });
25 </script>
26 {% end %}
42 {% end %}
@@ -1,28 +1,40 b''
1 {% extends layout.html %}
1 {% extends page.html %}
2
2
3 {% block content_panel %}
3 {% block stylesheet %}
4 <ul>
5 {% if read_only or not login_available %}
6
4
7 Proceed to the <a href="/">list of notebooks</a>.</li>
5 <link rel="stylesheet" href="{{static_url("css/logout.css") }}" type="text/css"/>
8
6
9 {% else %}
7 {% end %}
8
9
10 {% block login_widget %}
11 {% end %}
12
13 {% block site %}
10
14
11 Proceed to the <a href="/login">login page</a>.</li>
15 <div id="main_app">
12
16
17 {% if message %}
18 {% for key in message %}
19 <div class="message {{key}}">
20 {{message[key]}}
21 </div>
22 {% end %}
13 {% end %}
23 {% end %}
14
24
15 </ul>
25 {% if read_only or not login_available %}
26 Proceed to the <a href="/">dashboard</a>.
27 {% else %}
28 Proceed to the <a href="/login">login page</a>.
29 {% end %}
16
30
17 {% end %}
18
31
19 {% block login_widget %}
32 <div/>
33
20 {% end %}
34 {% end %}
21
35
22 {% block script %}
36 {% block script %}
23 <script type="text/javascript">
37
24 $(document).ready(function() {
38 <script src="{{static_url("js/logoutmain.js") }}" type="text/javascript" charset="utf-8"></script>
25 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
39
26 });
27 </script>
28 {% end %}
40 {% end %}
@@ -1,60 +1,49 b''
1 <!DOCTYPE HTML>
1 {% extends page.html %}
2 <html>
3
4 <head>
5 <meta charset="utf-8">
6
7 <title>IPython Notebook</title>
8
9 {% if mathjax_url %}
10 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
11 {% end %}
12 <script type="text/javascript">
13 // MathJax disabled, set as null to distingish from *missing* MathJax,
14 // where it will be undefined, and should prompt a dialog later.
15 window.mathjax_url = "{{mathjax_url}}";
16 </script>
17
18 <link rel="stylesheet" href="{{ static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
20 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
21
22 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
23
24 <link rel="stylesheet" href="{{ static_url("css/boilerplate.css") }}" type="text/css" />
25 <link rel="stylesheet" href="{{ static_url("css/layout.css") }}" type="text/css" />
26 <link rel="stylesheet" href="{{ static_url("css/base.css") }}" type="text/css" />
27 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
28 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
29
30 {% comment In the notebook, the read-only flag is used to determine %}
31 {% comment whether to hide the side panels and switch off input %}
32 <meta name="read_only" content="{{read_only and not logged_in}}"/>
33
34 </head>
35
36 <body
37 data-project={{project}} data-notebook-id={{notebook_id}}
38 data-base-project-url={{base_project_url}} data-base-kernel-url={{base_kernel_url}}
39 >
40
41 <div id="header">
42 <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='{{static_url("ipynblogo.png")}}' alt='IPython Notebook'/></a></h1></span>
43 <span id="save_widget">
44 <span id="notebook_name"></span>
45 <span id="save_status"></span>
46 </span>
47
2
48 <span id="login_widget">
3 {% block stylesheet %}
49 {% comment This is a temporary workaround to hide the logout button %}
4
50 {% comment when appropriate until notebook.html is templated %}
5 {% if mathjax_url %}
51 {% if logged_in %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
52 <button id="logout">Logout</button>
7 {% end %}
53 {% elif not logged_in and login_available %}
8 <script type="text/javascript">
54 <button id="login">Login</button>
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
55 {% end %}
10 // where it will be undefined, and should prompt a dialog later.
56 </span>
11 window.mathjax_url = "{{mathjax_url}}";
57 </div>
12 </script>
13
14 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
16
17 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
18
19 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
20 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
21
22 {% end %}
23
24
25 {% block params %}
26
27 data-project={{project}}
28 data-base-project-url={{base_project_url}}
29 data-base-kernel-url={{base_kernel_url}}
30 data-read-only={{read_only and not logged_in}}
31 data-notebook-id={{notebook_id}}
32
33 {% end %}
34
35
36 {% block header %}
37
38 <span id="save_widget">
39 <span id="notebook_name"></span>
40 <span id="save_status"></span>
41 </span>
42
43 {% end %}
44
45
46 {% block site %}
58
47
59 <div id="menubar_container">
48 <div id="menubar_container">
60 <div id="menubar">
49 <div id="menubar">
@@ -204,8 +193,10 b''
204
193
205 </div>
194 </div>
206
195
207 <script src="{{ static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
196 {% end %}
208 <script src="{{ static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
197
198
199 {% block script %}
209
200
210 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
201 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
211 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
202 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
@@ -221,17 +212,16 b''
221 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
212 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
222 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
213 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
223
214
224 <script src="{{ static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
225 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
215 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
226 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
216 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
217 <script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
218 <script src="{{ static_url("js/initmathjax.js") }}" type="text/javascript" charset="utf-8"></script>
227 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
219 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
228 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
220 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
229 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
221 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
230 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
222 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
231 <script src="{{ static_url("js/layout.js") }}" type="text/javascript" charset="utf-8"></script>
232 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
223 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
233 <script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
224 <script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
234 <script src="{{ static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
235 <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
225 <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
236 <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
226 <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
237 <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
227 <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
@@ -239,6 +229,5 b''
239 <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
229 <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
240 <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
230 <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
241
231
242 </body>
232 {% end %}
243
233
244 </html>
@@ -8,8 +8,8 b''
8
8
9 <link rel="stylesheet" href="{{static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
9 <link rel="stylesheet" href="{{static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
10 <link rel="stylesheet" href="{{static_url("css/boilerplate.css") }}" type="text/css" />
10 <link rel="stylesheet" href="{{static_url("css/boilerplate.css") }}" type="text/css" />
11 <link rel="stylesheet" href="{{static_url("css/layout.css") }}" type="text/css" />
11 <link rel="stylesheet" href="{{static_url("css/fbm.css") }}" type="text/css" />
12 <link rel="stylesheet" href="{{static_url("css/base.css") }}" type="text/css"/>
12 <link rel="stylesheet" href="{{static_url("css/page.css") }}" type="text/css"/>
13 {% block stylesheet %}
13 {% block stylesheet %}
14 {% end %}
14 {% end %}
15
15
@@ -21,7 +21,7 b''
21 <body {% block params %}{% end %}>
21 <body {% block params %}{% end %}>
22
22
23 <div id="header">
23 <div id="header">
24 <span id="ipython_notebook"><h1><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></h1></span>
24 <span id="ipython_notebook"><h1><a href={{base_project_url}} alt='dashboard'><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></a></h1></span>
25
25
26 {% block login_widget %}
26 {% block login_widget %}
27
27
@@ -39,43 +39,15 b''
39 {% end %}
39 {% end %}
40 </div>
40 </div>
41
41
42 <div id="header_border"></div>
42 <div id="site">
43
43 {% block site %}
44 <div id="main_app">
44 {% end %}
45
46 <div id="app_hbox">
47
48 <div id="left_panel">
49 {% block left_panel %}
50 {% end %}
51 </div>
52
53 <div id="content_panel">
54 {% if message %}
55
56 {% for key in message %}
57 <div class="message {{key}}">
58 {{message[key]}}
59 </div>
60 {% end %}
61 {% end %}
62
63 {% block content_panel %}
64 {% end %}
65 </div>
66 <div id="right_panel">
67 {% block right_panel %}
68 {% end %}
69 </div>
70
71 </div>
72
73 </div>
45 </div>
74
46
75 <script src="{{static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
47 <script src="{{static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
76 <script src="{{static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
48 <script src="{{static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
77 <script src="{{static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
49 <script src="{{static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
78 <script src="{{static_url("js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
50 <script src="{{static_url("js/page.js") }}" type="text/javascript" charset="utf-8"></script>
79 <script src="{{static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
51 <script src="{{static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
80
52
81 {% block script %}
53 {% block script %}
@@ -1,63 +1,44 b''
1 <!DOCTYPE HTML>
1 {% extends page.html %}
2 <html>
3
4 <head>
5 <meta charset="utf-8">
6
7 <title>IPython Notebook</title>
8
9 {% if mathjax_url %}
10 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
11 {% end %}
12 <script type="text/javascript">
13 // MathJax disabled, set as null to distingish from *missing* MathJax,
14 // where it will be undefined, and should prompt a dialog later.
15 window.mathjax_url = "{{mathjax_url}}";
16 </script>
17
18 <link rel="stylesheet" href="{{ static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
20 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
21
22 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
23
24 <link rel="stylesheet" href="{{ static_url("css/boilerplate.css") }}" type="text/css" />
25 <link rel="stylesheet" href="{{ static_url("css/layout.css") }}" type="text/css" />
26 <link rel="stylesheet" href="{{ static_url("css/base.css") }}" type="text/css" />
27 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
28 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" />
29 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
30
31 {% comment In the notebook, the read-only flag is used to determine %}
32 {% comment whether to hide the side panels and switch off input %}
33 <meta name="read_only" content="{{read_only and not logged_in}}"/>
34
35 </head>
36
37 <body
38 data-project={{project}} data-notebook-id={{notebook_id}}
39 data-base-project-url={{base_project_url}} data-base-kernel-url={{base_kernel_url}}
40 >
41
42 <div id="header">
43 <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></a></h1></span>
44 <span id="save_widget">
45 <span id="notebook_name"></span>
46 <span id="save_status"></span>
47 </span>
48
49 <span id="login_widget">
50 {% comment This is a temporary workaround to hide the logout button %}
51 {% comment when appropriate until notebook.html is templated %}
52 {% if logged_in %}
53 <button id="logout">Logout</button>
54 {% elif not logged_in and login_available %}
55 <button id="login">Login</button>
56 {% end %}
57 </span>
58
2
59 </div>
3 {% block stylesheet %}
4
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
7 {% end %}
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 // where it will be undefined, and should prompt a dialog later.
11 window.mathjax_url = "{{mathjax_url}}";
12 </script>
13
14 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
16
17 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
18
19 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
20 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" />
21 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
22
23 {% end %}
24
25
26 {% block params %}
60
27
28 data-project={{project}}
29 data-base-project-url={{base_project_url}}
30 data-base-kernel-url={{base_kernel_url}}
31 data-read-only={{read_only and not logged_in}}
32 data-notebook-id={{notebook_id}}
33
34 {% end %}
35
36
37 {% block header %}
38 {% end %}
39
40
41 {% block site %}
61
42
62 <div id="main_app">
43 <div id="main_app">
63
44
@@ -67,8 +48,10 b''
67
48
68 </div>
49 </div>
69
50
70 <script src="{{ static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
51 {% end %}
71 <script src="{{ static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
52
53
54 {% block script %}
72
55
73 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
56 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
74 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
57 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
@@ -84,19 +67,14 b''
84 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
67 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
85 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
68 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
86
69
87 <script src="{{ static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
88 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
70 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
89 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
71 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
72 <script src="{{ static_url("js/initmathjax.js") }}" type="text/javascript" charset="utf-8"></script>
90 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
73 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
91 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
74 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
92 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
75 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
93 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
76 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
94 <script src="{{ static_url("js/kernelstatus.js") }}" type="text/javascript" charset="utf-8"></script>
95 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
96 <script src="{{ static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
97 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
77 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
98 <script src="{{ static_url("js/printnotebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
78 <script src="{{ static_url("js/printnotebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
99
79
100 </body>
80 {% end %}
101
102 </html>
@@ -1,43 +1,77 b''
1 {% extends layout.html %}
1 {% extends page.html %}
2
2
3 {% block title %}
3 {% block title %}IPython Dashboard{% end %}
4 IPython Dashboard
5 {% end %}
6
4
7 {% block stylesheet %}
5 {% block stylesheet %}
8 <link rel="stylesheet" href="{{static_url("css/projectdashboard.css") }}" type="text/css" />
6 <link rel="stylesheet" href="{{static_url("css/projectdashboard.css") }}" type="text/css" />
9 {% end %}
7 {% end %}
10
8
11 {% block meta %}
12 <meta name="read_only" content="{{read_only}}"/>
13 {% end %}
14
9
15 {% block params %}
10 {% block params %}
11
16 data-project={{project}}
12 data-project={{project}}
17 data-base-project-url={{base_project_url}}
13 data-base-project-url={{base_project_url}}
18 data-base-kernel-url={{base_kernel_url}}
14 data-base-kernel-url={{base_kernel_url}}
15 data-read-only={{read_only}}
16
19 {% end %}
17 {% end %}
20
18
21 {% block content_panel %}
22 {% if logged_in or not read_only %}
23
19
24 <div id="content_toolbar">
20 {% block site %}
25 <span id="drag_info">Drag files onto the list to import
21
26 notebooks.</span>
22 <div id="main_app">
23
24 <div id="tabs">
25 <ul>
26 <li><a href="#tab1">Notebooks</a></li>
27 <li><a href="#tab2">Clusters</a></li>
28 </ul>
27
29
28 <span id="notebooks_buttons">
30 <div id="tab1">
29 <button id="new_notebook">New Notebook</button>
31 {% if logged_in or not read_only %}
30 </span>
32 <div id="notebook_toolbar">
33 <span id="drag_info">Drag files onto the list to import
34 notebooks.</span>
35
36 <span id="notebook_buttons">
37 <button id="refresh_notebook_list" title="Refresh notebook list">Refresh</button>
38 <button id="new_notebook" title="Create new notebook">New Notebook</button>
39 </span>
40 </div>
41 {% end %}
42
43 <div id="notebook_list">
44 <div id="project_name"><h2>{{project}}</h2></div>
45 </div>
31 </div>
46 </div>
47 <div id="tab2">
48
49 <div id="cluster_toolbar">
50 <span id="cluster_list_info">IPython parallel computing clusters</span>
32
51
33 {% end %}
52 <span id="cluster_buttons">
53 <button id="refresh_cluster_list" title="Refresh cluster list">Refresh</button>
54 </span>
55 </div>
56
57 <div id="cluster_list">
58 <div id="cluster_header">
59 <span>profile</span>
60 <span>action</span>
61 <span title="Enter the number of engines to start or empty for default"># of engines</span>
62 <span>status</span>
63 </div>
64 </div>
34
65
35 <div id="notebook_list">
36 <div id="project_name"><h2>{{project}}</h2></div>
37 </div>
66 </div>
67 </div>
68
69 </div>
70
38 {% end %}
71 {% end %}
39
72
40 {% block script %}
73 {% block script %}
41 <script src="{{static_url("js/notebooklist.js") }}" type="text/javascript" charset="utf-8"></script>
74 <script src="{{static_url("js/notebooklist.js") }}" type="text/javascript" charset="utf-8"></script>
75 <script src="{{static_url("js/clusterlist.js") }}" type="text/javascript" charset="utf-8"></script>
42 <script src="{{static_url("js/projectdashboardmain.js") }}" type="text/javascript" charset="utf-8"></script>
76 <script src="{{static_url("js/projectdashboardmain.js") }}" type="text/javascript" charset="utf-8"></script>
43 {% end %}
77 {% end %}
@@ -106,8 +106,35 b' NO_CLUSTER = 12'
106
106
107
107
108 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
109 # Utilities
110 #-----------------------------------------------------------------------------
111
112 def find_launcher_class(clsname, kind):
113 """Return a launcher for a given clsname and kind.
114
115 Parameters
116 ==========
117 clsname : str
118 The full name of the launcher class, either with or without the
119 module path, or an abbreviation (MPI, SSH, SGE, PBS, LSF,
120 WindowsHPC).
121 kind : str
122 Either 'EngineSet' or 'Controller'.
123 """
124 if '.' not in clsname:
125 # not a module, presume it's the raw name in apps.launcher
126 if kind and kind not in clsname:
127 # doesn't match necessary full class name, assume it's
128 # just 'PBS' or 'MPI' prefix:
129 clsname = clsname + kind + 'Launcher'
130 clsname = 'IPython.parallel.apps.launcher.'+clsname
131 klass = import_item(clsname)
132 return klass
133
134 #-----------------------------------------------------------------------------
109 # Main application
135 # Main application
110 #-----------------------------------------------------------------------------
136 #-----------------------------------------------------------------------------
137
111 start_help = """Start an IPython cluster for parallel computing
138 start_help = """Start an IPython cluster for parallel computing
112
139
113 Start an ipython cluster by its profile name or cluster
140 Start an ipython cluster by its profile name or cluster
@@ -303,15 +330,8 b' class IPClusterEngines(BaseParallelApplication):'
303
330
304 def build_launcher(self, clsname, kind=None):
331 def build_launcher(self, clsname, kind=None):
305 """import and instantiate a Launcher based on importstring"""
332 """import and instantiate a Launcher based on importstring"""
306 if '.' not in clsname:
307 # not a module, presume it's the raw name in apps.launcher
308 if kind and kind not in clsname:
309 # doesn't match necessary full class name, assume it's
310 # just 'PBS' or 'MPI' prefix:
311 clsname = clsname + kind + 'Launcher'
312 clsname = 'IPython.parallel.apps.launcher.'+clsname
313 try:
333 try:
314 klass = import_item(clsname)
334 klass = find_launcher_class(clsname, kind)
315 except (ImportError, KeyError):
335 except (ImportError, KeyError):
316 self.log.fatal("Could not import launcher class: %r"%clsname)
336 self.log.fatal("Could not import launcher class: %r"%clsname)
317 self.exit(1)
337 self.exit(1)
@@ -492,7 +512,6 b' class IPClusterStart(IPClusterEngines):'
492 def init_launchers(self):
512 def init_launchers(self):
493 self.controller_launcher = self.build_launcher(self.controller_launcher_class, 'Controller')
513 self.controller_launcher = self.build_launcher(self.controller_launcher_class, 'Controller')
494 self.engine_launcher = self.build_launcher(self.engine_launcher_class, 'EngineSet')
514 self.engine_launcher = self.build_launcher(self.engine_launcher_class, 'EngineSet')
495 self.controller_launcher.on_stop(self.stop_launchers)
496
515
497 def engines_stopped(self, r):
516 def engines_stopped(self, r):
498 """prevent parent.engines_stopped from stopping everything on engine shutdown"""
517 """prevent parent.engines_stopped from stopping everything on engine shutdown"""
@@ -500,6 +519,7 b' class IPClusterStart(IPClusterEngines):'
500
519
501 def start_controller(self):
520 def start_controller(self):
502 self.log.info("Starting Controller with %s", self.controller_launcher_class)
521 self.log.info("Starting Controller with %s", self.controller_launcher_class)
522 self.controller_launcher.on_stop(self.stop_launchers)
503 self.controller_launcher.start()
523 self.controller_launcher.start()
504
524
505 def stop_controller(self):
525 def stop_controller(self):
@@ -1164,14 +1164,16 b' class IPClusterLauncher(LocalProcessLauncher):'
1164 ipcluster_cmd = List(ipcluster_cmd_argv, config=True,
1164 ipcluster_cmd = List(ipcluster_cmd_argv, config=True,
1165 help="Popen command for ipcluster")
1165 help="Popen command for ipcluster")
1166 ipcluster_args = List(
1166 ipcluster_args = List(
1167 ['--clean-logs', '--log-to-file', '--log-level=%i'%logging.INFO], config=True,
1167 ['--clean-logs=True', '--log-to-file', '--log-level=%i'%logging.INFO], config=True,
1168 help="Command line arguments to pass to ipcluster.")
1168 help="Command line arguments to pass to ipcluster.")
1169 ipcluster_subcommand = Unicode('start')
1169 ipcluster_subcommand = Unicode('start')
1170 ipcluster_n = Integer(2)
1170 profile = Unicode('default')
1171 n = Integer(2)
1171
1172
1172 def find_args(self):
1173 def find_args(self):
1173 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
1174 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
1174 ['--n=%i'%self.ipcluster_n] + self.ipcluster_args
1175 ['--n=%i'%self.n, '--profile=%s'%self.profile] + \
1176 self.ipcluster_args
1175
1177
1176 def start(self):
1178 def start(self):
1177 return super(IPClusterLauncher, self).start()
1179 return super(IPClusterLauncher, self).start()
General Comments 0
You need to be logged in to leave comments. Login now