Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Almost everything works though
  • Loading branch information
yuvipanda committed Jan 4, 2019
1 parent 4ba37b9 commit 07c97c0
Show file tree
Hide file tree
Showing 18 changed files with 1,132 additions and 2 deletions.
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

48 changes: 48 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
==========================
Jupyter Launcher Shortcuts
==========================

Extensions for JupyterLab and classic Jupyter Notebook to add
**user defined** 'launcher' shortcuts. Primarily useful in
JupyterHub / Binder situations.

For JupyterLab, they're added in the launcher interface.

.. image:: labextension.png

For classic Jupyter Notebook, they are added under the 'New' button

.. image:: nbextension.png

Installation
============

The supporting notebook server extension & classic notebook extension
can be installed with ``pip``.

.. code:: bash
pip install jupyter-launcher-shortcuts
The JupyterLab extension needs to be installed separately.

.. code:: bash
jupyter labextension install jupyterlab-launcher-shortcuts
Configuring
===========

The extension can be configured in a ``jupyter_notebook_config.py``
file created in any of the directories under ``config`` in the
output of ``jupyter --paths`` command.

.. code:: python
c.LauncherShortcuts.shortcuts = {
'my-shiny-application': {
'title': 'Human Readable Shortcut Title',
'target': '{base_url}shiny/my-shiny-application-directory/',
'icon_path': '/path/to/svg/file'
}
}
72 changes: 72 additions & 0 deletions jupyter_launcher_shortcuts/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from notebook.utils import url_path_join as ujoin
from .api import ShortcutsHandler, IconHandler
from traitlets import Dict
from traitlets.config import Configurable
from collections import namedtuple

Shortcut = namedtuple('Shortcut', ['name', 'title', 'icon_path', 'target'])

class LauncherShortcuts(Configurable):
shortcuts = Dict(
{},
help="""
Dictionary of shortcuts to put in launchers.
Key should be the name of the shortcut.
Value should be a dictionary with the following keys:
title
Human readable title for the shortcut. Defaults to the name.
target
URL to open when shortcut is clicked. {base_url} is replaced with
the notebook server's base_url
icon_path
Full path to an svg icon that could be used with a launcher. Currently only used by the
JupyterLab launcher
""",
config=True
)

def shortcut_from_dict(name, shortcut_dict):
return Shortcut(
name,
title=shortcut_dict.get('title', name),
icon_path=shortcut_dict.get('icon_path'),
target=shortcut_dict['target']
)

def load_jupyter_server_extension(nbapp):
# Set up handlers picked up via config
base_url = nbapp.web_app.settings['base_url']
shortcuts = [
shortcut_from_dict(k, v)
for k, v in LauncherShortcuts(parent=nbapp).shortcuts.items()
]

icons = {}
for ls in shortcuts:
if ls.icon_path:
icons[ls.name] = ls.icon_path

nbapp.web_app.add_handlers('.*', [
(ujoin(base_url, 'launcher-shortcuts/shortcuts'), ShortcutsHandler, {'shortcuts': shortcuts}),
(ujoin(base_url, 'launcher-shortcuts/icon/(.*)'), IconHandler, {'icons': icons})
])


# Jupyter Extension points
def _jupyter_server_extension_paths():
return [{
'module': 'jupyter_launcher_shortcuts',
}]

def _jupyter_nbextension_paths():
return [{
"section": "tree",
"dest": "jupyter_launcher_shortcuts",
'src': 'static',
"require": "jupyter_launcher_shortcuts/tree"
}]
60 changes: 60 additions & 0 deletions jupyter_launcher_shortcuts/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from tornado import web
import mimetypes
from notebook.base.handlers import IPythonHandler
from notebook.utils import url_path_join as ujoin

class ShortcutsHandler(IPythonHandler):
def initialize(self, shortcuts):
self.shortcuts = shortcuts

@web.authenticated
async def get(self):
data = []
for ls in self.shortcuts:
item = {
'name': ls.name,
'title': ls.title,
'target': ls.target.format(base_url=self.base_url),
}
if ls.icon_path:
icon_url = ujoin(self.base_url, 'launcher-shortcuts', 'icon', sp.name)
data.append(item)

self.write({'shortcuts': data})


# FIXME: Should be a StaticFileHandler subclass
class IconHandler(IPythonHandler):
"""
Serve launcher icons
"""
def initialize(self, icons):
"""
icons is a dict of titles to paths
"""
self.icons = icons

async def get(self, name):
if name not in self.icons:
raise web.HTTPError(404)
path = self.icons[name]

# Guess mimetype appropriately
# Stolen from https://github.com/tornadoweb/tornado/blob/b399a9d19c45951e4561e6e580d7e8cf396ef9ff/tornado/web.py#L2881
mime_type, encoding = mimetypes.guess_type(path)
if encoding == "gzip":
content_type = "application/gzip"
# As of 2015-07-21 there is no bzip2 encoding defined at
# http://www.iana.org/assignments/media-types/media-types.xhtml
# So for that (and any other encoding), use octet-stream.
elif encoding is not None:
content_type = "application/octet-stream"
elif mime_type is not None:
content_type = mime_type
# if mime_type not detected, use application/octet-stream
else:
content_type = "application/octet-stream"

with open(self.icons[name]) as f:
self.write(f.read())
self.set_header('Content-Type', content_type)
8 changes: 8 additions & 0 deletions jupyter_launcher_shortcuts/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
Traitlets based configuration for jupyter_server_proxy
"""
from notebook.utils import url_path_join as ujoin
from .handlers import SuperviseAndProxyHandler, AddSlashHandler
import pkg_resources
from collections import namedtuple

6 changes: 6 additions & 0 deletions jupyter_launcher_shortcuts/etc/nbextension.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"load_extensions": {
"jupyter_launcher_extensions/tree": true
}
}

8 changes: 8 additions & 0 deletions jupyter_launcher_shortcuts/etc/serverextension.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"NotebookApp": {
"nbserver_extensions": {
"jupyter_launcher_shortcuts": true
}
}
}

51 changes: 51 additions & 0 deletions jupyter_launcher_shortcuts/static/tree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
define(['jquery', 'base/js/namespace', 'base/js/utils'], function($, Jupyter, utils) {
var $ = require('jquery');
var Jupyter = require('base/js/namespace');
var utils = require('base/js/utils');

var base_url = utils.get_body_data('baseUrl');

function load() {
if (!Jupyter.notebook_list) return;

var servers_info_url = base_url + 'launcher-shortcuts/shortcuts' ;
$.get(servers_info_url, function(data) {
/* locate the right-side dropdown menu of apps and notebooks */
var $menu = $('.tree-buttons').find('.dropdown-menu');

/* create a divider */
var $divider = $('<li>')
.attr('role', 'presentation')
.addClass('divider');


/* add the divider */
$menu.append($divider);

$.each(data.shortcuts, function(_, shortcut) {
/* create our list item */
var $entry_container = $('<li>')
.attr('role', 'presentation')
.addClass('new-rstudio');

/* create our list item's link */
var $entry_link = $('<a>')
.attr('role', 'menuitem')
.attr('tabindex', '-1')
.attr('href', shortcut.target)
.attr('target', '_blank')
.text(shortcut.title);

/* add the link to the item and
* the item to the menu */
$entry_container.append($entry_link);
$menu.append($entry_container);

});
});
}

return {
load_ipython_extension: load
};
});
5 changes: 5 additions & 0 deletions jupyterlab-launcher-shortcuts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.bundle.*
lib/
node_modules/
*.egg-info/
.ipynb_checkpoints
32 changes: 32 additions & 0 deletions jupyterlab-launcher-shortcuts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# jupyterlab-launcher-shortcuts

Configurable launcher shortcuts for JupyterLab.


## Prerequisites

* JupyterLab

## Installation

```bash
jupyter labextension install jupyterlab-launcher-shortcuts
```

## Development

For a development install (requires npm version 4 or later), do the following in the repository directory:

```bash
npm install
npm run build
jupyter labextension link .
```

To rebuild the package and the JupyterLab app:

```bash
npm run build
jupyter lab build
```

43 changes: 43 additions & 0 deletions jupyterlab-launcher-shortcuts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "jupyterlab-launcher-shortcuts",
"version": "1.0.0-beta1",
"description": "Provide arbitrary shortcuts for apps in JupyterLab",
"keywords": [
"jupyter",
"jupyterlab",
"jupyterlab-extension"
],
"homepage": "https://github.com/yuvipanda/jupyter-launcher-shortcuts",
"bugs": {
"url": "https://github.com/yuvipanda/jupyter-launcher-shortcuts/issues"
},
"license": "BSD-3-Clause",
"author": "Yuvi Panda",
"files": [
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
"style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}"
],
"main": "lib/index.js",
"types": "lib/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/yuvipanda/jupyter-launcher-shortcuts.git"
},
"scripts": {
"build": "tsc",
"clean": "rimraf lib",
"prepare": "npm run clean && npm run build",
"watch": "tsc -w"
},
"dependencies": {
"@jupyterlab/application": "^0.19.1",
"@jupyterlab/launcher": "^0.19.1"
},
"devDependencies": {
"rimraf": "^2.6.1",
"typescript": "~3.1.1"
},
"jupyterlab": {
"extension": true
}
}
Loading

0 comments on commit 07c97c0

Please sign in to comment.