Skip to content

Commit

Permalink
Support six different development paths
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Duberstein committed Aug 24, 2019
1 parent 24fefaf commit ef4a582
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 199 deletions.
6 changes: 6 additions & 0 deletions bindings/python/.gitignore
Expand Up @@ -151,3 +151,9 @@ $RECYCLE.BIN/
# Cut JS files created in build process
**/pydeck/nbextension/static/nb_extension.js
**/pydeck/nbextension/static/nb_extension.js.map

# Cut JS files created in build process
**/pydeck/nbextension/static/nb_extension.js
**/pydeck/nbextension/static/nb_extension.js.map
**/pydeck/nbextension/static/extensionRequires.js
**/pydeck/io/templates/requirejs_dependencies.json
9 changes: 1 addition & 8 deletions bindings/python/pydeck/MANIFEST.in
Expand Up @@ -5,10 +5,6 @@ include setupbase.py
include pytest.ini
include .coverage.rc

include package.json
include webpack.config.js
include pydeck/labextension/*.tgz

# Examples
graft examples

Expand All @@ -18,10 +14,7 @@ prune tests/build

# Javascript files
graft pydeck/nbextension
graft src
prune **/node_modules
prune coverage
prune lib
prune setup

# Patterns to exclude from any directory
global-exclude *~
Expand Down
6 changes: 4 additions & 2 deletions bindings/python/pydeck/README.md
Expand Up @@ -39,11 +39,13 @@ python setup.py install

First, run `yarn bootstrap` at the root of this repository.

Run `npx webpack-dev-server` in the `modules/jupyter-widget`, which will enable hot-reloading at `localhost:8080`.
Run `npx webpack-dev-server` in the `modules/jupyter-widget` and in a separate terminal `export PYDECK_DEV_SERVER=http://localhost:8080`.

To specifiy a non-default URL for the webpack dev server, you can set a URL in the `PYDECK_DEV_SERVER` environment variable,
e.g., `export PYDECK_DEV_SERVER=http://localhost:8080`.

Finally, run `pip install -e .` for a development installation.

Additionally, `export PYDECK_DEVELOPMENT_SERVER=http://localhost:8080/index`

### Tests

Expand Down
92 changes: 92 additions & 0 deletions bindings/python/pydeck/dependency_managers/__init__.py
@@ -0,0 +1,92 @@
"""This file handles the creation of a config for RequireJS
pydeck supports two strategies to render deck.gl maps:
- A Juypter Notebook Widget
- A standalone set of HTML templates
RequireJS manages dependencies in Jupyter environments.
To adhere to Jupyter standards and minimize code duplication,
both pydeck rendering strategies use RequireJS.
The RequireJS configuration is viewable in the root of the pydeck repo.
The functions below serve to convert these dependencies into a RequireJS config
for testing, development, and production builds.
"""
import os
import json
import urllib

from jinja2 import Template

def _is_open(url):
'''Verifies that hot reloading dev server URL is running'''
if not dev_server_url:
return
try:
code = urllib.request.urlopen(url).getcode()
if code != 200:
raise Exception('Is your dev server running? Non-200 status at {url}: {code}'.format(url, code))
except Exception as e:
raise Exception('Dev server connection issue', e)


here = os.path.dirname(os.path.abspath(__file__))
dev_server_url = os.getenv('PYDECK_DEV_SERVER')
_is_open(dev_server_url)
WIDGET_PATH = os.path.join(here, '../../../../modules/jupyter-widget/dist')


def create_notebook_requirejs(dependencies, base_path, setup_environment='development'):
'''Embeds environment-appropriate RequireJS configuration into the Jupyter widget's module'''
# Prepares JS dependencies for the notebook requirejs file
if setup_environment == 'development' and dev_server_url:
# Notebook with hot reloading development on http://localhost:8080
dependencies['paths']['nbextension/pydeck'] = dev_server_url
elif setup_environment in ('test', 'production', 'development'):
# Notebook using the JS bundle built from webpack command in @deck.gl/jupyter-widget
# The notebook dependency manager will be written to ./pydeck/nbextension/static/extensionRequires.js
# If this changes, ./pydeck/nbextension/__init__.py must also change
dependencies['map']['*']['@deck.gl/jupyter-widget'] = 'nbextensions/pydeck/nb_extension'
del dependencies['paths']['nbextension/pydeck']
else:
raise Exception('Unrecognized setup environment')

# Creates the notebook widget dependency manager
# This file is excluded from version control
notebook_output_js = os.path.join(
base_path, 'pydeck', 'nbextension', 'static', 'extensionRequires.js'
)
with open(notebook_output_js, 'w+') as f:
f.write(embed_in_widget_template(dependencies))


def create_standalone_render_requirejs(dependencies, base_path, setup_environment='development'):
'''Embeds environment-appropriate RequireJS configuration into the standalone HTML renderer's module'''
# Creates the standalone HTML renderer dependency file base_path
if setup_environment == 'development' and dev_server_url:
# Supports standalone HTML renderer with hot reloading
dependencies['paths']['nbextension/pydeck'] = dev_server_url
# TODO verify this path
elif setup_environment == 'production':
# Standalone HTML renderer in production requires reading from CDN
CDN_URL = 'https://cdn.jsdelivr.net/npm/@deck.gl/jupyter-widget@latest/dist'
dependencies['paths']['nbextension/pydeck'] = CDN_URL
elif setup_environment == 'development':
# Standalone HTML renderer with static reloading requires a bundled JS file of the Jupyter widget module
dependencies['paths']['nbextension/pydeck'] = WIDGET_PATH
else:
raise Exception('Unrecognized setup environment')
# Writes a file set near the standalone rendering code
# This file is excluded from version control
standalone_html_output_js = os.path.join(
base_path, 'pydeck', 'io', 'templates', 'requirejs_dependencies.json'
)
with open(standalone_html_output_js, 'w+') as f:
f.write(json.dumps(dependencies, indent=2))

def embed_in_widget_template(dependencies):
with open(os.path.join(here, 'notebook_requirejs_config.j2')) as f:
template = Template(f.read())
rendered = template.render(dependencies=json.dumps(dependencies, indent=4))
return rendered
@@ -0,0 +1,14 @@
{#- Requirements handler for Jupyter notebook extension -#}
{#- Loads dependencies defined in the pydeck/requirejs_dependencies.json -#}
/* eslint-disable */
define(function() {
'use strict';
requirejs.config(
{{- dependencies -}}
);
// Export the required load_ipython_extension function
return {
load_ipython_extension: function() {}
};
});

156 changes: 5 additions & 151 deletions bindings/python/pydeck/examples/01 - Introduction.ipynb
Expand Up @@ -21,7 +21,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -43,78 +43,9 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>lng</th>\n",
" <th>lat</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>-0.198465</td>\n",
" <td>51.505538</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>-0.178838</td>\n",
" <td>51.491836</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>-0.205590</td>\n",
" <td>51.514910</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>-0.208327</td>\n",
" <td>51.514952</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>-0.206022</td>\n",
" <td>51.496572</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" lng lat\n",
"0 -0.198465 51.505538\n",
"1 -0.178838 51.491836\n",
"2 -0.205590 51.514910\n",
"3 -0.208327 51.514952\n",
"4 -0.206022 51.496572"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"outputs": [],
"source": [
"import pandas as pd\n",
"\n",
Expand Down Expand Up @@ -158,26 +89,11 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "be1250477f804be19da48c359b75a9e3",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"DeckGLWidget(json_input='{\"initialViewState\": {\"bearing\": -27.396, \"latitude\": 52.2323, \"longitude\": -1.415, \""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"outputs": [],
"source": [
"layer = pdk.Layer(\n",
" 'HexagonLayer',\n",
Expand Down Expand Up @@ -263,68 +179,6 @@
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {
"3b9786c3664248aeb722a1086e848318": {
"model_module": "@deck.gl/jupyter-widget",
"model_module_version": "^7.1.0-alpha.1",
"model_name": "DeckGLModel",
"state": {
"_model_module_version": "^7.1.0-alpha.1",
"_view_module_version": "^7.1.0-alpha.1",
"json_input": "{\"initialViewState\": {\"bearing\": -27.396, \"latitude\": 52.2323, \"longitude\": -1.415, \"maxZoom\": 15, \"minZoom\": 5, \"pitch\": 40.5, \"zoom\": 6.6}, \"layers\": [{\"colorRange\": [[1, 152, 189], [73, 227, 206], [216, 254, 181], [254, 237, 177], [254, 173, 84], [209, 55, 78]], \"coverage\": 1, \"data\": \"https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv\", \"elevationRange\": [0, 3000], \"elevationScale\": 50, \"extruded\": true, \"getPosition\": \"-\", \"getRadius\": 1000, \"id\": \"71799c82-d104-4ca1-8d02-b5d48460708c\", \"lightSettings\": {\"ambientRatio\": 0.7, \"diffuseRatio\": 0.6, \"lightsPosition\": [-0.144528, 49.739968, 8000, -3.807751, 54.104682, 10000], \"numberOfLights\": 2}, \"opacity\": 1, \"radius\": 1000, \"type\": \"HexagonLayer\", \"upperPercentile\": 100}], \"mapStyle\": \"mapbox://styles/mapbox/dark-v9\", \"views\": [{\"controller\": true, \"type\": \"MapView\"}]}",
"layout": "IPY_MODEL_c93002f6c4a842e9bc47cba1f0a47022",
"mapbox_key": "pk.eyJ1IjoiZHViZXJzYWoyIiwiYSI6ImNqcHhmYzcwNTBrZHc0OW9udGljMm1pNXQifQ.DxYvaredXVDkvS0nSAuf_Q"
}
},
"55a8acca3fa64c49b8c2b76dc260a8d3": {
"model_module": "@deck.gl/jupyter-widget",
"model_module_version": "^7.1.0-alpha.1",
"model_name": "DeckGLModel",
"state": {
"_model_module_version": "^7.1.0-alpha.1",
"_view_module_version": "^7.1.0-alpha.1",
"json_input": "{\"initialViewState\": {\"bearing\": -27.396, \"latitude\": 52.2323, \"longitude\": -1.415, \"maxZoom\": 15, \"minZoom\": 5, \"pitch\": 40.5, \"zoom\": 6}, \"layers\": [{\"colorRange\": [[1, 152, 189], [73, 227, 206], [216, 254, 181], [254, 237, 177], [254, 173, 84], [209, 55, 78]], \"coverage\": 1, \"data\": \"https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv\", \"elevationRange\": [0, 9000], \"elevationScale\": 50, \"extruded\": true, \"getPosition\": \"-\", \"getRadius\": 1000, \"id\": \"24807c08-e7be-4aa4-8575-5462f16c6313\", \"opacity\": 1, \"radius\": 1000, \"type\": \"HexagonLayer\", \"upperPercentile\": 100}], \"mapStyle\": \"mapbox://styles/mapbox/dark-v9\", \"views\": [{\"controller\": true, \"type\": \"MapView\"}]}",
"layout": "IPY_MODEL_cfb0222b37ef4555b285b0c42ff0a0e8",
"mapbox_key": "pk.eyJ1IjoiZHViZXJzYWoyIiwiYSI6ImNqcHhmYzcwNTBrZHc0OW9udGljMm1pNXQifQ.DxYvaredXVDkvS0nSAuf_Q"
}
},
"5654614a55664d91a75ad631467d2511": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.1.0",
"model_name": "LayoutModel",
"state": {}
},
"89e5bc7802ef460d8e03ab16fcfe0adf": {
"model_module": "@deck.gl/jupyter-widget",
"model_module_version": "^7.1.0-alpha.1",
"model_name": "DeckGLModel",
"state": {
"_model_module_version": "^7.1.0-alpha.1",
"_view_module_version": "^7.1.0-alpha.1",
"json_input": "{\"initialViewState\": {\"bearing\": -27.396, \"latitude\": 52.2323, \"longitude\": -1.415, \"maxZoom\": 15, \"minZoom\": 5, \"pitch\": 40.5, \"zoom\": 6.6}, \"layers\": [{\"colorRange\": [[1, 152, 189], [73, 227, 206], [216, 254, 181], [254, 237, 177], [254, 173, 84], [209, 55, 78]], \"coverage\": 1, \"data\": \"https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv\", \"elevationRange\": [0, 3000], \"elevationScale\": 50, \"extruded\": true, \"getPosition\": \"-\", \"getRadius\": 1000, \"id\": \"61da0d21-e44c-4c15-8c56-e8f6555f5675\", \"opacity\": 1, \"radius\": 1000, \"type\": \"HexagonLayer\", \"upperPercentile\": 100}], \"mapStyle\": \"mapbox://styles/mapbox/dark-v9\", \"views\": [{\"controller\": true, \"type\": \"MapView\"}]}",
"layout": "IPY_MODEL_5654614a55664d91a75ad631467d2511",
"mapbox_key": "pk.eyJ1IjoiZHViZXJzYWoyIiwiYSI6ImNqcHhmYzcwNTBrZHc0OW9udGljMm1pNXQifQ.DxYvaredXVDkvS0nSAuf_Q"
}
},
"c93002f6c4a842e9bc47cba1f0a47022": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.1.0",
"model_name": "LayoutModel",
"state": {}
},
"cfb0222b37ef4555b285b0c42ff0a0e8": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.1.0",
"model_name": "LayoutModel",
"state": {}
}
},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
Expand Down
10 changes: 0 additions & 10 deletions bindings/python/pydeck/pydeck/io/html.py
Expand Up @@ -12,20 +12,10 @@
j2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(TEMPLATES_PATH),
trim_blocks=True)

def render_dependencies(in_notebook=False):
bundle_path = os.getenv('PYDECK_DEVELOPMENT_URL', 'nbextensions/pydeck/nb_extension')
if in_notebook:
dependencies = j2_env.get_template('notebook_require.j2')
else:
dependencies = j2_env.get_template('dependencies.json')
return dependencies.render(bundle_path=bundle_path)


def render_json_to_html(json_input, mapbox_key=None):
js = j2_env.get_template('index.j2')
html_str = js.render(
mapbox_key=mapbox_key,
dependencies=render_dependencies(),
json_input=json_input,
mapbox_gl_version='1.2.1')
return html_str
Expand Down
2 changes: 1 addition & 1 deletion bindings/python/pydeck/pydeck/io/templates/js.j2
@@ -1,4 +1,4 @@
const DEPENDENCIES = {% include 'dependencies.json' %};
const DEPENDENCIES = {% include 'requirejs_dependencies.json' %};
const jsonInput = {{json_input}};
const MAPBOX_API_KEY = '{{mapbox_key}}';

Expand Down
10 changes: 0 additions & 10 deletions bindings/python/pydeck/pydeck/io/templates/notebook_require.j2

This file was deleted.

5 changes: 2 additions & 3 deletions bindings/python/pydeck/pydeck/nbextension/__init__.py
@@ -1,10 +1,9 @@
#!/usr/bin/env python
# coding: utf-8
def _jupyter_nbextension_paths():
paths = {
return [{
'section': 'notebook',
'src': 'nbextension/static',
'dest': 'pydeck',
'require': 'pydeck/extensionRequires'
}
return [paths]
}]

0 comments on commit ef4a582

Please sign in to comment.