Skip to content
This repository

Dashboard improvement (necessary merge of #1658 and #1676 + fix #1492) #1739

Merged
merged 22 commits into from almost 2 years ago

5 participants

Matthias Bussonnier Brian E. Granger Fernando Perez Jens H Nielsen Thomas Kluyver
Matthias Bussonnier
Collaborator
Carreau commented May 15, 2012

This is merge of #1658 (alternate upload) and #1676 (shutdown fron dashboard) that were conflicting, because #1658 (alternate upload) is necessary for #1676 (shutdown fron dashboard), otherwise you can't upload file as dashbord refresh just after drag and drop...

In addition :

One small bug fix of #1658.

fixes #1492 ( bigger drag target when no notebook), it show "list is empty" when no notebooks.

finally on master.

added some commits April 25, 2012
Matthias Bussonnier alternate notebook upload methods
fixes #1562
334470c
Matthias Bussonnier multifile selection f01957f
Matthias Bussonnier clean html, style logon form ff6b85d
Matthias Bussonnier kernel status 341b864
Matthias Bussonnier proof of concept f9c25e3
Matthias Bussonnier clean unused command ede97ff
Matthias Bussonnier dashboard autorefresh
refresh notebook list and cluster list when :
- page get focus
- every 60 sec when page is on focus

stop refreshing every 60 sec when page loose focus
bd82968
Matthias Bussonnier test kernel for undefined rather than off b60b6de
Matthias Bussonnier rename kernel_status -> kernel_id c2d2e34
Matthias Bussonnier space around assignement 7244b9c
Matthias Bussonnier Check for null rather than undefined
set kernel id to None/null if not started
a809dd9
Matthias Bussonnier prevent autorefresh when pending upload
don't clear list if 'upload' button are present to avoid clearing the
list and the upload form
7f9bda0
Matthias Bussonnier add 'Close and halt' in notebook filemenu 68c8ac9
Matthias Bussonnier remove extra console.log 83c278d
Matthias Bussonnier bold click here in dashboard 214f204
Matthias Bussonnier remove underscore in filename 98aa620
Matthias Bussonnier i,b, in boilerplater.css 7a1e763
Matthias Bussonnier replace b by strong for semantic, more change in boilerplate 078e522
Matthias Bussonnier do not expose enable/disable autorefresh 04af03c
Matthias Bussonnier Drag target bigger for empty notebook dashboard
When no notebooks, show "Notebook list empty", which increase the size
of the drag target.

fixes #1492
f4a9b71
Matthias Bussonnier fix upload button selection by class
superseed #1676 #1658 (and correct bug in 1676 where one con't upload
notebook because of refresh)
df90df6
Brian E. Granger
Owner

@fperez, can you test this branch on Linux to see if this fixes the uploadability of notebooks? (in your vast spare time ;-)

Fernando Perez
Owner
fperez commented May 23, 2012

I greatly appreciate the subtle sense of humor, @ellisonbg ;)

Sure, I'll try to take this for a spin tomorrow; I'm fried for the day. Thanks for reminding me...

Jens H Nielsen

I have tested uploading notebooks via both the button and drag and drop on linux (ubuntu 12.04) in Firefox and Chromium and it appears to be working fine in all cases.
The shutdown button also works as expected and is a great feature. One small issue that I see is that if you keep a notebook open
but shut it down from the dash and try to execute code in the notebook afterwards the restart kernel dialog pops up as expected but if you press restart the cell is not actually executed until you rerun it but it appears busy i.e it shows up as "In [*]:" until you rerun it.

Matthias Bussonnier
Collaborator
Carreau commented May 25, 2012

I think this is a larger issue, this appends also if the kernel dies by itself.
Even more, you can save notebook with running prompt, and load them afterwards. They will still show cells with running prompt even if the new kernel is, of course, idle.

Jens H Nielsen

@Carreau Yes you are right this change only makes it easier to expose the bug and I don't think that this
should hold back the merge of this pull request.

Matthias Bussonnier
Collaborator
Carreau commented May 30, 2012

I'll merge in a few hours if nobody as objection as it only adds some functionality and seem to work in linux.

Thomas Kluyver
Collaborator

@Carreau : Let's give people about a day, to make sure people in California have had a chance to look at it, though I expect it will be all OK.

Fernando Perez
Owner
fperez commented May 30, 2012

I've confirmed that it works on Linux, and that indeed it allows uploading into Chrome when SSL is on. I double-checked that situation, and it looks like it's a chrome-specific thing (and could be just on linux), because with Firefox the d'n'd upload works fine. So this is a most welcome fix! Since it has already had good review otherwise, go for it.

My only suggestion would be to change the phrase in the dashboard

"Drag files onto the list, or click here, to import notebooks"

to

"To import a notebook, drag the file onto the listing below or click here."

I just think it reads better, that's all.

Matthias Bussonnier
Collaborator
Carreau commented May 31, 2012

Ok, changed, merging then.

Matthias Bussonnier Carreau merged commit 2caea25 into from May 31, 2012
Matthias Bussonnier Carreau closed this May 31, 2012
Jens H Nielsen

Thanks for this improvement. I think a small bug appeared when the text was changed.
To me it seems like you only moved the test but did not change the position where one has to click
to open the file dialog. I have to click on either the or listing now to open the file dialog.

Matthias Bussonnier
Collaborator
Carreau commented May 31, 2012

Can you try to flush your browser cache,(css is often cached longer that html). I did change the size of the click area.
Actually you should be able to to click anywhere on the whole sentence to have the dialog opening.

Thanks.

Jens H Nielsen

Yes I see. It works correctly in chrome. In firefox neither clearing the cache nor using a different user profile seems to work.

Matthias Bussonnier
Collaborator
Carreau commented May 31, 2012

hum... right, no idea why, the firefox css inspector does say that the fileinput have the right size, but dont correctly respond to click.

@fperez , should we then get back to
"Drag files onto the list, or click here, to import notebooks"
to be more aligned with FF ?

Fernando Perez
Owner
fperez commented May 31, 2012

Mmh, I don't particularly like having to keep a contorted sentence to satisfy a bizarre browser bug... I guess if it's broken we do need to fix it, but in that case please open an issue about this so that we can return later to the problem (or somebody who knows more about browsers can). I find it truly strange that word order can affect clicking response...

From experimenting a bit with FF, it seems that it only responds to clicks in the area from the start of the sentence up to around 260 pixels, which is ~58% of the 447px input area.

So I suggest instead the following wording:

"To import a notebook, click here or drag a file onto the list below:"

Hopefully that will work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 22 unique commits by 1 author.

May 15, 2012
Matthias Bussonnier alternate notebook upload methods
fixes #1562
334470c
Matthias Bussonnier multifile selection f01957f
Matthias Bussonnier clean html, style logon form ff6b85d
Matthias Bussonnier kernel status 341b864
Matthias Bussonnier proof of concept f9c25e3
Matthias Bussonnier clean unused command ede97ff
Matthias Bussonnier dashboard autorefresh
refresh notebook list and cluster list when :
- page get focus
- every 60 sec when page is on focus

stop refreshing every 60 sec when page loose focus
bd82968
Matthias Bussonnier test kernel for undefined rather than off b60b6de
Matthias Bussonnier rename kernel_status -> kernel_id c2d2e34
Matthias Bussonnier space around assignement 7244b9c
Matthias Bussonnier Check for null rather than undefined
set kernel id to None/null if not started
a809dd9
Matthias Bussonnier prevent autorefresh when pending upload
don't clear list if 'upload' button are present to avoid clearing the
list and the upload form
7f9bda0
Matthias Bussonnier add 'Close and halt' in notebook filemenu 68c8ac9
Matthias Bussonnier remove extra console.log 83c278d
Matthias Bussonnier bold click here in dashboard 214f204
Matthias Bussonnier remove underscore in filename 98aa620
Matthias Bussonnier i,b, in boilerplater.css 7a1e763
Matthias Bussonnier replace b by strong for semantic, more change in boilerplate 078e522
Matthias Bussonnier do not expose enable/disable autorefresh 04af03c
Matthias Bussonnier Drag target bigger for empty notebook dashboard
When no notebooks, show "Notebook list empty", which increase the size
of the drag target.

fixes #1492
f4a9b71
Matthias Bussonnier fix upload button selection by class
superseed #1676 #1658 (and correct bug in 1676 where one con't upload
notebook because of refresh)
df90df6
May 31, 2012
Matthias Bussonnier change upload sentense 21b1b8d
This page is out of date. Refresh to see the latest.
3  IPython/frontend/html/notebook/handlers.py
@@ -590,7 +590,10 @@ class NotebookRootHandler(AuthenticatedHandler):
590 590
     @authenticate_unless_readonly
591 591
     def get(self):
592 592
         nbm = self.application.notebook_manager
  593
+        km = self.application.kernel_manager
593 594
         files = nbm.list_notebooks()
  595
+        for f in files :
  596
+            f['kernel_id'] = km.kernel_for_notebook(f['notebook_id'])
594 597
         self.finish(jsonapi.dumps(files))
595 598
 
596 599
     @web.authenticated
23  IPython/frontend/html/notebook/static/css/alternateuploadform.css
... ...
@@ -0,0 +1,23 @@
  1
+/* We need an invisible input field on top of the sentense*/
  2
+/* "Drag file onto the list ..." */
  3
+
  4
+.alternate_upload
  5
+{
  6
+    background-color:none;
  7
+    display: inline;
  8
+}
  9
+
  10
+.alternate_upload.form
  11
+{
  12
+    padding: 0;
  13
+    margin:0;
  14
+}
  15
+
  16
+.alternate_upload input.fileinput
  17
+{
  18
+    background-color:red;
  19
+    position:relative;
  20
+    opacity: 0;
  21
+    z-index: 2;
  22
+    width: 447px;
  23
+}
3  IPython/frontend/html/notebook/static/css/boilerplate.css
@@ -70,4 +70,5 @@ select, input, textarea, button { font:99% sans-serif; }
70 70
    en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome */
71 71
 pre, code, kbd, samp { font-family: monospace, sans-serif; }
72 72
 
73  
-
  73
+em,i { font-style: italic; }
  74
+b,strong { font-weight: bold; }
4  IPython/frontend/html/notebook/static/js/menubar.js
@@ -68,6 +68,10 @@ var IPython = (function (IPython) {
68 68
         this.element.find('button#print_notebook').click(function () {
69 69
             IPython.print_widget.print_notebook();
70 70
         });
  71
+        this.element.find('#kill_and_exit').click(function () {
  72
+            IPython.notebook.kernel.kill();
  73
+            setTimeout(function(){window.close();}, 200);
  74
+        });
71 75
         // Edit
72 76
         this.element.find('#cut_cell').click(function () {
73 77
             IPython.notebook.cut_cell();
104  IPython/frontend/html/notebook/static/js/notebooklist.js
@@ -43,33 +43,44 @@ var IPython = (function (IPython) {
43 43
         this.element.bind('dragover', function () {
44 44
             return false;
45 45
         });
46  
-        this.element.bind('drop', function (event) {
47  
-            var files = event.originalEvent.dataTransfer.files;
48  
-            for (var i = 0, f; f = files[i]; i++) {
49  
-                var reader = new FileReader();
50  
-                reader.readAsText(f);
51  
-                var fname = f.name.split('.'); 
52  
-                var nbname = fname.slice(0,-1).join('.');
53  
-                var nbformat = fname.slice(-1)[0];
54  
-                if (nbformat === 'ipynb') {nbformat = 'json';};
55  
-                if (nbformat === 'py' || nbformat === 'json') {
56  
-                    var item = that.new_notebook_item(0);
57  
-                    that.add_name_input(nbname, item);
58  
-                    item.data('nbformat', nbformat);
59  
-                    // Store the notebook item in the reader so we can use it later
60  
-                    // to know which item it belongs to.
61  
-                    $(reader).data('item', item);
62  
-                    reader.onload = function (event) {
63  
-                        var nbitem = $(event.target).data('item');
64  
-                        that.add_notebook_data(event.target.result, nbitem);
65  
-                        that.add_upload_button(nbitem);
66  
-                    };
67  
-                };
68  
-            }
  46
+        this.element.bind('drop', function(event){
  47
+            that.handelFilesUpload(event,'drop');
69 48
             return false;
70 49
         });
71 50
     };
72 51
 
  52
+    NotebookList.prototype.handelFilesUpload =  function(event, dropOrForm) {
  53
+        var that = this;
  54
+        var files;
  55
+        if(dropOrForm =='drop'){
  56
+            files = event.originalEvent.dataTransfer.files;
  57
+        } else 
  58
+        {
  59
+            files = event.originalEvent.target.files
  60
+        }
  61
+        for (var i = 0, f; f = files[i]; i++) {
  62
+            var reader = new FileReader();
  63
+            reader.readAsText(f);
  64
+            var fname = f.name.split('.'); 
  65
+            var nbname = fname.slice(0,-1).join('.');
  66
+            var nbformat = fname.slice(-1)[0];
  67
+            if (nbformat === 'ipynb') {nbformat = 'json';};
  68
+            if (nbformat === 'py' || nbformat === 'json') {
  69
+                var item = that.new_notebook_item(0);
  70
+                that.add_name_input(nbname, item);
  71
+                item.data('nbformat', nbformat);
  72
+                // Store the notebook item in the reader so we can use it later
  73
+                // to know which item it belongs to.
  74
+                $(reader).data('item', item);
  75
+                reader.onload = function (event) {
  76
+                    var nbitem = $(event.target).data('item');
  77
+                    that.add_notebook_data(event.target.result, nbitem);
  78
+                    that.add_upload_button(nbitem);
  79
+                };
  80
+            };
  81
+        }
  82
+        return false;
  83
+        };
73 84
 
74 85
     NotebookList.prototype.clear_list = function () {
75 86
         this.element.children('.list_item').remove();
@@ -77,7 +88,6 @@ var IPython = (function (IPython) {
77 88
 
78 89
 
79 90
     NotebookList.prototype.load_list = function () {
80  
-        this.clear_list();
81 91
         var settings = {
82 92
             processData : false,
83 93
             cache : false,
@@ -92,15 +102,30 @@ var IPython = (function (IPython) {
92 102
 
93 103
     NotebookList.prototype.list_loaded = function (data, status, xhr) {
94 104
         var len = data.length;
95  
-        // Todo: remove old children
  105
+        this.clear_list();
  106
+
  107
+        if(len == 0)
  108
+        {
  109
+            $(this.new_notebook_item(0))
  110
+                .append(
  111
+                    $('<div style="margin:auto;text-align:center;color:grey"/>')
  112
+                    .text('Notebook list empty.')
  113
+                    )
  114
+        }
  115
+
96 116
         for (var i=0; i<len; i++) {
97 117
             var notebook_id = data[i].notebook_id;
98 118
             var nbname = data[i].name;
  119
+            var kernel = data[i].kernel_id;
99 120
             var item = this.new_notebook_item(i);
100 121
             this.add_link(notebook_id, nbname, item);
101 122
             if (!IPython.read_only){
102 123
                 // hide delete buttons when readonly
103  
-                this.add_delete_button(item);
  124
+                if(kernel == null){
  125
+                    this.add_delete_button(item);
  126
+                } else {
  127
+                    this.add_shutdown_button(item,kernel);
  128
+                }
104 129
             }
105 130
         };
106 131
     };
@@ -164,6 +189,32 @@ var IPython = (function (IPython) {
164 189
     };
165 190
 
166 191
 
  192
+    NotebookList.prototype.add_shutdown_button = function (item,kernel) {
  193
+        var new_buttons = $('<span/>').addClass('item_buttons');
  194
+        var that = this;
  195
+        var shutdown_button = $('<button>Shutdown</button>').button().
  196
+            click(function (e) {
  197
+                var settings = {
  198
+                    processData : false,
  199
+                    cache : false,
  200
+                    type : "DELETE",
  201
+                    dataType : "json",
  202
+                    success : function (data, status, xhr) {
  203
+                        that.load_list();
  204
+                    }
  205
+                };
  206
+                var url = $('body').data('baseProjectUrl') + 'kernels/'+kernel;
  207
+                $.ajax(url, settings);
  208
+            });
  209
+        new_buttons.append(shutdown_button);
  210
+        var e = item.find('.item_buttons');
  211
+        if (e.length === 0) {
  212
+            item.append(new_buttons);
  213
+        } else {
  214
+            e.replaceWith(new_buttons);
  215
+        };
  216
+    };
  217
+
167 218
     NotebookList.prototype.add_delete_button = function (item) {
168 219
         var new_buttons = $('<span/>').addClass('item_buttons');
169 220
         var delete_button = $('<button>Delete</button>').button().
@@ -217,6 +268,7 @@ var IPython = (function (IPython) {
217 268
         var that = this;
218 269
         var new_buttons = $('<span/>').addClass('item_buttons');
219 270
         var upload_button = $('<button>Upload</button>').button().
  271
+            addClass('upload-button').
220 272
             click(function (e) {
221 273
                 var nbname = item.find('.item_name > input').attr('value');
222 274
                 var nbformat = item.data('nbformat');
47  IPython/frontend/html/notebook/static/js/projectdashboardmain.js
@@ -30,10 +30,53 @@ $(document).ready(function () {
30 30
     IPython.cluster_list = new IPython.ClusterList('div#cluster_list');
31 31
     IPython.login_widget = new IPython.LoginWidget('span#login_widget');
32 32
 
33  
-    IPython.notebook_list.load_list();
34  
-    IPython.cluster_list.load_list();
  33
+    var interval_id=0;
  34
+    // auto refresh every xx secondes, no need to be fast,
  35
+    //  update is done at least when page get focus
  36
+    var time_refresh = 60; // in sec
  37
+
  38
+    var enable_autorefresh = function(){
  39
+        //refresh immediately , then start interval
  40
+        if($('.upload_button').length == 0)
  41
+        {
  42
+            IPython.notebook_list.load_list();
  43
+            IPython.cluster_list.load_list();
  44
+        }
  45
+        if (!interval_id){
  46
+            interval_id = setInterval(function(){
  47
+                    if($('.upload_button').length == 0)
  48
+                    {
  49
+                        IPython.notebook_list.load_list();
  50
+                        IPython.cluster_list.load_list();
  51
+                    }
  52
+                }, time_refresh*1000);
  53
+            }
  54
+    }
  55
+
  56
+    var disable_autorefresh = function(){
  57
+        clearInterval(interval_id);
  58
+        interval_id = 0;
  59
+    }
  60
+
  61
+    // stop autorefresh when page lose focus
  62
+    $(window).blur(function() {
  63
+        disable_autorefresh();
  64
+    })
  65
+
  66
+    //re-enable when page get focus back
  67
+    $(window).focus(function() {
  68
+        enable_autorefresh();
  69
+    });
  70
+
  71
+    // finally start it, it will refresh immediately
  72
+    enable_autorefresh();
35 73
 
36 74
     IPython.page.show();
  75
+    
  76
+    // bound the upload method to the on change of the file select list
  77
+    $("#alternate_upload").change(function (event){
  78
+        IPython.notebook_list.handelFilesUpload(event,'form');
  79
+    });
37 80
 
38 81
 });
39 82
 
2  IPython/frontend/html/notebook/templates/login.html
@@ -17,7 +17,7 @@
17 17
 
18 18
     {% if login_available %}
19 19
     <form action="/login?next={{url_escape(next)}}" method="post">
20  
-        Password: <input type="password" name="password" id="password_input">
  20
+        Password: <input type="password" class='ui-widget ui-widget-content' name="password" id="password_input">
21 21
         <input type="submit" value="Log in" id="login_submit">
22 22
     </form>
23 23
     {% end %}
2  IPython/frontend/html/notebook/templates/notebook.html
@@ -65,6 +65,8 @@
65 65
                 </li>
66 66
                 <hr/>
67 67
                 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>
  68
+                <hr/>
  69
+                <li id="kill_and_exit"><a href="#" >Close and halt</a></li>
68 70
             </ul>
69 71
         </li>
70 72
         <li><a href="#">Edit</a>
10  IPython/frontend/html/notebook/templates/projectdashboard.html
@@ -4,6 +4,7 @@
4 4
 
5 5
 {% block stylesheet %}
6 6
     <link rel="stylesheet" href="{{static_url("css/projectdashboard.css") }}" type="text/css" />
  7
+    <link rel="stylesheet" href="{{static_url("css/alternateuploadform.css") }}" type="text/css" />
7 8
 {% end %}
8 9
 
9 10
 
@@ -30,9 +31,12 @@
30 31
     <div id="tab1">
31 32
         {% if logged_in or not read_only %}
32 33
         <div id="notebook_toolbar">
33  
-            <span id="drag_info">Drag files onto the list to import
34  
-            notebooks.</span>
35  
-
  34
+            <form id='alternate_upload'  class='alternate_upload' >
  35
+                <span id="drag_info" style="position:absolute" >
  36
+                    To import a notebook, drag the file onto the listing below or <strong>click here</strong>.
  37
+                    </span>
  38
+                <input type="file" name="datafile" class="fileinput" multiple='multiple'>
  39
+            </form>
36 40
             <span id="notebook_buttons">
37 41
                 <button id="refresh_notebook_list" title="Refresh notebook list">Refresh</button>
38 42
                 <button id="new_notebook" title="Create new notebook">New Notebook</button>
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.