Giter VIP home page Giter VIP logo

dashboards_server's Introduction

npm version Build Status Google Group

[RETIRED] Jupyter Dashboards Server

This project has been retired. See the proposal to move the project to jupyter-attic, announcement of the proposal on the mailing list, and Steering Council vote on the proposal PR for more information.


A NodeJS application that can display Jupyter notebooks as dynamic dashboards outside of the Jupyter Notebook server.

Dashboards server screenshot

The Jupyter Incubator Dashboards effort covers:

  1. Arranging notebook outputs in a grid- or report-like layout
  2. Bundling notebooks and associated assets for deployment as dashboards
  3. Serving notebook-defined dashboards as standalone web apps

This repository focuses on (3) above, while jupyter-incubator/dashboards handles (1) and jupyter-incubator/dashboards_bundlers implements (2).

See https://github.com/jupyter-incubator/dashboards/wiki for an overview of the entire dashboard incubation effort.

What it Gives You

  • Ability to run some Jupyter notebooks as standalone dashboard applications [1]
  • Ability to navigate a list of multiple notebooks and select one to run as a dashboard
  • Optional shared login to secure access to the dashboard server
  • Ability to add custom authentication mechanisms using the Passport middleware for Node.js
  • An API for POSTing notebooks to the server at runtime with optional authentication (/_api/notebooks)

The qualification in [1] stems from the fact that supporting one-click deploy of notebooks with arbitrary JavaScript and kernel dependencies is a "Really Hard Problem." We've invested effort in getting these dashboard, visualization, and widget libraries working in the dashboard server.

  • jupyter_dashboards 0.6.x
  • jupyter_dashboards_bundlers 0.8.x
  • ipywidgets 5.2.x
  • jupyter_declarativewidgets 0.6.x
  • matplotlib 1.5.x
  • Bokeh 0.11.x
  • Plotly 1.9.x

If you try another library and find that it does not work in the dashboard server, see the wiki page about Widget Support below for steps you might take to resolve the problem.

Install it

Install Node 5.x and npm 3.5.x. Use npm to install the jupyter-dashboards-server package.

npm install -g jupyter-dashboards-server

You can then run the dashboard server from the command line. See the next section about how to install and configure the other prerequisite components.

# shows a list of all nconf options
jupyter-dashboards-server --help

# runs the server pointing to a public kernel gateway
jupyter-dashboards-server --KERNEL_GATEWAY_URL=http://my.gateway.com/

# runs the server pointing to a kernel gateway that requires token auth
export KG_AUTH_TOKEN='somesecretinenvironment'
jupyter-dashboards-server --KERNEL_GATEWAY_URL=http://my.gateway.com/

Run It

The dashboard server is meant to support the layout-bundler-deploy workflow described on the project overview page. This workflow requires multiple components working in concert.

Minimal dashboard app deployment diagram

To bring all of these pieces together, you can start with the recipes in the jupyter-incubator/dashboards_setup repo. (We'll gladly take PRs that reduce the complexity of getting everything set up!)

Alternatively, you can clone this git repository and build the Docker images we use for development in order to run the demos in etc/notebooks. After setting up Docker (e.g. using docker-machine), run the following and then visit http://<your docker host ip>:3000.

make build
make examples
make demo-container

Run Behind a Proxy

The dashboards server can be run behind a reverse proxy. In order to do so, you will need to set the following options as command line args or in environment vars.

  • TRUST_PROXY - The simple option is to just set this to true. However, if you require further configuration on which requests to trust, this option can also take values as specified by the Express documentation.
  • BASE_URL - Specify the base URL (prefix) at which the dashboards server will run. The server supports two options here: passing the prefix along with the request or stripping the prefix off the request.

For example:

# allow proxying of "http://proxy_host/db/..." to "http://dashboards_host/db/..."
jupyter-dashboards-server --TRUST_PROXY=true --BASE_URL=/db --KERNEL_GATEWAY_URL=http://my.gateway.com/

# allow proxying of "http://proxy_host/db/..." to "http://dashboards_host/..."
jupyter-dashboards-server --TRUST_PROXY=true --BASE_URL='[/db]' --KERNEL_GATEWAY_URL=http://my.gateway.com/

Develop It

To setup a development environment, install these minimum versions on your host machine.

  • Node 5.5.0
  • npm 3.5.3
  • gulp 3.9.0
  • Docker 1.9.1

With these installed, you can use the make dev-* targets. Run make help to see the full gamut of targets and options. See the next few sections for the most common patterns.

Setup

# re-run if the Dockerfile.kernel changes
make kernel-gateway-image
# re-run if package.json changes
make dev-install
# run if you want to try the preliminary jupyter-incubator/declarativewidgets support
make examples

Dashboard Server w/ Auto Restart

# uses gulp:watch to restart on any changes
make dev
# mac shortcut for visiting URL in a browser
open http://127.0.0.1:3000

Dashboard Server w/ Auto Restart and Debug Console Logging

make dev-logging
# mac shortcut for visiting URL in a browser
open http://127.0.0.1:3000

Dashboard Server w/ Auto Restart and Remote Debugging

npm install -g node-inspector
make dev-debug
# a browser tab should open with the debugger visible
# refresh if it errors: the server might not be running yet

Dashboard Server w/ Auto Restart and Form Auth

make dev USERNAME=admin PASSWORD=password
# mac shortcut for visiting URL in a browser
open http://127.0.0.1:3000

See the Authentication wiki page for information about configuring alternative authentication mechanisms.

Dashboard Server w/ Auto Restart and Self-Signed HTTPS Certificate

make certs
make dev HTTPS_KEY_FILE=certs/server.pem HTTPS_CERT_FILE=certs/server.pem
# mac shortcut for visiting URL in a browser
open https://127.0.0.1:3001

Dashboard Server Tests

# unit tests
make test
# backend integration tests
make integration-test
# installation tests
make install-test

Technical Details

See the wiki attached to this project for additional technical details including the server API, authentication plugins, adding support for new widgets, and more.

dashboards_server's People

Contributors

banzo avatar bodri5 avatar dalogsdon avatar danielfrg avatar jameslmartin avatar jhpedemonte avatar jtyberg avatar minrk avatar parente avatar poplav avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dashboards_server's Issues

Only allow ipywidget comm channel messages to reach the kernel after initial top-to-bottom execute

Filter out any messages sent from the frontend client to the kernel at the backend server except for ipywidgets comm messages on the shell channel. Allowing any other message gives the user the ability to execute arbitrary code. (In fact, it's not yet know if locking down to comm messages only is good enough to prevent code execution or not yet, but it's a step in the right direction.)

See https://github.com/jupyter-incubator/dashboards/wiki/Deployed-Dashboard-Threat-Analysis

Use a real websocket library for decoding, filtering, encoding traffic

Right now we're using a somewhat home grown recipe that handles text frames well, but doesn't do anything with ping or pong or any of the other WS protocol op codes. This is biting us at the moment because the client's pongs are not making it to the kernel gateway in response to its pings, and so the KG kills the websocket connections after 90 seconds thinking they're dead.

I'm going to put in a quick hack fix for the pong problem, but we're pretty certain that it's not a robust solution. We need to move to something more robust eventually.

See sections 5.4 and 5.5.x of the websocket RFC https://tools.ietf.org/html/rfc6455#page-36

Images do not resize to cell width

In the dashboard notebook plugin, images in cells will resize to cell width:

image

In the dashboard server, images are always full size:

image

This is using all Markdown cells with <img> elements.

Include a standalone demo

Add a notebook to this repo and instructions in the README about how to bring up an instance of the node app and have the notebook run as a dashboard. You'll probably want to use Docker because you'll need all of:

  1. The notebook on disk
  2. The node app
  3. The kernel gateway
  4. The kernel used in the notebook
  5. Any other dependencies (matplotlib, node, npm, ...)

You could put all this in a single Docker container and launch two services for the demo (the node app + the kernel gateway). Or you could do a small docker-compose.yml with the kernel gateway running in one container (hopefully based on jupyter/docker-stacks#42 when it lands) and the node app in another.

The goal here is to show how the dashboard backend works independently of the bundler and the dashboard layout extension. Later, we can expand the README to link to those to paint the full picture.


TODO

  • Create standalone demo notebook
  • Create Docker container
  • Replace jupyter-incubator/all-spark-kernels image with the minimal-kernel image.
  • npm install jupyter-js-widgets instead of building it from source
  • Use docker link command to link together the containers, instead of querying IP of kernel gateway container.

Indicate a problem when the kernel sends an error message

Strawman: some indicator in the nav bar at the top. Sort of like what notebook does when a kernel dies or a websocket disconnects or ... Don't hard code the indicator to kernel errors only though: it's very likely we're going to want to use that same indicator area to give other types of status too.

POST handler for adding a notebook to the running instance

It would be handy if the node app supported POST /notebooks to add / overwrite an existing notebook with the same with a new one for quick and dirty deployment.

https://github.com/jupyter-incubator/dashboards_nodejs_app/blob/master/routes/index.js

Let's not worry yet about persisting these or PUT vs POST or overwrite vs error. I want to get a simple demonstration of how a bundler can be written for Jupyter Notebook server to post a simple notebook to this API to get it to show up as a dashboard instantly.

Dashboard list shows sub directories.

The dashboard list page shows all contents of the data directory, which includes sub directories. However, selecting an entry in the list will attempt to load that as a notebook.

The GET /dashboards/* API does allow for sub directories, e.g. GET /dashboards/subdir/foo.ipynb.

Should we support sub directories in the list page or remove support from the GET API?

Allow uploading of zip containing notebook with resources

The ability to upload a notebook to the dashboards server does not allow for resources to go with it, such as widget files and data. Allow a zip file to be uploaded and its resources unpacked and served without clobbering the resources of other notebooks.


Structure: [updated 2016-02-26]

<notebook_name>/
    index.ipynb
    urth_components/
        ...
    stylesheet.css
    image.jpg

Client/Server Tasks:

  • Support plain and bundled dashboards.
    • Plain & bundled should look same in UI.
    • Don't show ipynb extension.
  • Support GET of bundled resources (everything but the notebook itself).
  • Look for index.ipynb and always render for that dashboard for the dir, whether inside or outside of a "bundled" dashboard.
    • Look for index instead of __notebook__
  • Load declwidgets from within bundled dashboard
  • Create example installable through Makefile. Should test declwidgets, loading of local resources (CSS, images, etc).
  • Support POST of bundled dashboards into data dir
  • Create tests
    • Test loading index.ipynb (with/without extension). We should not get full contents back.
    • etc
  • Update README to describe use of index.ipynb in a dir and local resources.

Support /api/spawn for tmpnb

The current implementation assumes a standalone kernel gateway or notebook server. This is equivalent to tmpnb: false mode in thebe. It also needs to support the /api/spawn mode of operation to launch containers on tmpnb (or compatible) clusters.

Reason: tmpnb with its configurable-http-proxy has the only robust mechanism for reaping kernels around. A single kernel gateway is fine for testing, but it leaks kernels.

  • call /api/spawn on tmpnb to request a kernel gateway (make sure each client talks to its own kernel gateway instance)
  • support KERNEL_GATEWAY_URL and KERNEL_CLUSTER_URL properties (see comments)
  • support passing tmpnb auth token to /api/spawn

Multiple linked widgets do not render, error in JS console

Bottom left panel in https://github.com/jupyter-incubator/dashboards/blob/master/etc/notebooks/test/test_layout_basic.ipynb should have 3 linked widgets. Only one of them seems to render. The other two generate "Could not create view" errors in the JS console

Error: Could not create view↵    at new Error (native)↵    at Error.WrappedError (http://127.0.0.1:3000/components/jupyter-js-widgets.js:31415:26)↵    at promiseRejection (http://127.0.0.1:3000/components/jupyter-js-widgets.js:31493:34

screen shot 2016-02-09 at 11 09 16 pm

Cells overlapping again

No console errors. Looks like something else broke gridstack. Confirmed that lodash is pinned. Even tried setting it to 3.10.1 exactly.

screen shot 2016-02-04 at 10 24 28 am

Max dashboard layout fixed width

simple_dash

User lays out dashboard in the (mostly) fixed-width environment of the notebook server. We should match that experience when we display the notebook here. Depending on the browser width, the dashboard container width should snap to some maximum. (See the notebook CSS or the dashboards_bundlers CSS for what those breakpoints are.)

Apply rendered_html class to appropriately

In #83, we decided to quick fix the styling problems by slapping the class on all output areas. This will probably have side-effects. We should revisit and find a way to apply it only when proper, when the output is actual HTML. This probably involves looking at the mimetypes in the display_data and execute_result messages in the dashboards.js#messageHandlers. At present, that function only receives a model. Adding a class dynamically would require a reference to the view as well. (Not sure such a reference is a good thing thinking about MVC design.)

Allow dashboard clients to reconnect to running dashboards within a time window

Improve upon the immediate cleanup logic implemented in #25 to give the client time to try to reestablish its websocket connections.

Strawman: Set a timer in the nodejs app. If a client with the x-jupyter-session-id header returns before the timer fires, cancel the timer and continue forwarding messages to the associated kernel. If the timer goes off before a client reconnects, kill the kernel.

Of course, this also requires that the frontend JS attempt reconnections. It looks like jupyter-js-services already supports it, but we should check if it works in our use case.

Style login/list pages

Currently we have default bootstrap style login and dashboard list. If we do issue #87, we could follow up with login and list styling to match. I put these in a separate issue as I feel they are less important than the navbar since they reside in their own pages which are not the main focus of dashboards.

Option to support shared login

Support optional username and password configuration options via any nconf allowed method on server startup. If one is required, make sure the other is specified as well. If both are set, check whether or not all HTTP requests have a valid session cookie. If the user has authed (i.e., session cook is present and valid), service the HTTP request. If the user has not authed (i.e., the cookie does not exist, is expired, is invalid), show a basic login page prompting the user for the configured username and password. On successful login, set a temporary (1 day?) session cookie.

Use of client-side session cookies is fine here. (I think they're the express default still.)

Struggling to get make 'make run' and 'make run-tmpnb' to run on linux 14.04

Thanks for your efforts on this repo ... I'm watching it with interest ... however:

I ran make run ... which appeared to succeed in building images and then launching containers ... I'm not sure if the bower install step didn't get run properly or something ... the terminal logs would suggest maybe it's missing express (see logs lower down)

The screen that is meant to list notebooks was blank ... and this is what shows up for the simple notebook.
image

-- Starting proxy container
npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm info lifecycle [email protected]~prestart: [email protected]
npm info lifecycle [email protected]~start: [email protected]

> [email protected] start /home/node/app
> node ./bin/www

GET / 302 49.128 ms - 64
GET /notebooks 200 240.792 ms - 281
STACK: Error: Not Found
    at /home/node/app/app.js:106:15
    at Layer.handle [as handle_request] (/home/node/app/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/home/node/app/node_modules/express/lib/router/index.js:312:13)
    at /home/node/app/node_modules/express/lib/router/index.js:280:7
    at Function.process_params (/home/node/app/node_modules/express/lib/router/index.js:330:12)
    at next (/home/node/app/node_modules/express/lib/router/index.js:271:10)
    at /home/node/app/node_modules/express/lib/router/index.js:618:15
    at next (/home/node/app/node_modules/express/lib/router/index.js:256:14)
    at Function.handle (/home/node/app/node_modules/express/lib/router/index.js:176:3)
    at router (/home/node/app/node_modules/express/lib/router/index.js:46:12)
GET /sw-import.js?baseURI=http%3A%2F%2Flocalhost%3A3000%2Fbower_components%2Fplatinum-sw%2Fplatinum-sw-register.html&clientsClaim=true&defaultCacheStrategy=networkFirst&importscript=http%3A%2F%2Flocalhost%3A3000%2Fbower_components%2Fplatinum-sw%2Fbootstrap%2Fsw-toolbox-setup.js&precache=&skipWaiting=true&version=1.0 404 13.974 ms - 878
STACK: Error: Not Found

I tried to stop and restart the dashboard-proxy container, so that I might troubleshoot things manually inside the container ... but ctrl-c of the make run process appears to kill and remove that container.

I don't have that much experience with make, but each time a run that command, it appears to build a whole new image ... which obviously isn't as slow as the first time, as it uses caches etc ... but is there scope for getting this working using docker-compose, especially once the images are built?

When running make run-tmpnb I also hit a hurdle on it complaining that port 8000 is already being used ... Although I have other docker containers running ... none of them is touching port 8000 ... does this 1005/node on port 8000 look like anything related to this set-up?

Step 21 : CMD start
 ---> Using cache
 ---> 0e8b480a3420
Successfully built 0e8b480a3420
4eb7912c7e6fd15e096e18a8036bd476fd1c27c2dc831c084037d6286a6d3662
Error response from daemon: Cannot start container 4eb7912c7e6fd15e096e18a8036bd476fd1c27c2dc831c084037d6286a6d3662: failed to create endpoint tmpnb-proxy on network bridge: Error starting userland proxy: listen tcp 0.0.0.0:8000: bind: address already in use
make: *** [run-tmpnb-proxy] Error 1
mccoole@mccoole-ubuntu-W520:~/Development/Tools/jupyter/dashboards_nodejs_app$ netstat -tulpn | grep 8000
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      1005/node

Doc and automation

  • All params are nconf
  • Expose IP interface as setting (default to 127.0.0.1)
  • All params are documented in config.json and shown in make help
  • Complete README sections
  • make test within docker container
  • Make cert targets real targets in makefile

Update to jupyter-js-services 0.5

(We're currently at 0.3.x)

Major change is that commOpened signal is gone, replaced with registerCommTarget().


Tasks:

Support ipywidget based interactive elements

The impl in #1 will not be able to support ipywidgets or declarative widgets yet:

WRT ipywidgets support, there isn't support for jupyter-js-services yet. I'll probably end up moving to the jupyter-js-services comm API and shimming the current API (notebook compatibility), for ipywidgets 5.0 (next release) or 6.0 (depends on how much time I can allocate to 5.0).

From jupyter/dashboards#105 (comment)

Once the ipywidgets support for jupyter-js-services lands, the app here can start to take advantage of it.

Fix progress bar (and other styling)

Fix styling issues:

  • Busy indicator appears at bottom of page, doesn't animate and doesn't disappear.
  • Font sizes are different than dashboard in Notebook.
  • Stream message outputs get appended as new elements in output area
    • When phosphor-widget is updated to 1.0.0-rc1 or later, re-commit 94d734d.
  • Output area overlaps widget area, but only when running from Docker container (not from localhost).

Style navbar

We currently have a navbar with default bootstrap styling. We could style it to integrate better into the page and emphasize the page content.

Separate programmatic API from UI endpoint

  • POST /notebooks -> move to POST /api/notebooks and keep as the programmatic way to add a new notebook
  • GET / -> redirect and/or render /dashboards/index.ipynb if it exists or /dashboards if not.
  • GET /dashboards -> UI list of all dashboards
  • GET /dashboards/:name -> notebook rendered as a dashboard

Initial implementation

The initial implementation should have the following properties

  • fetch code from colocated notebook file on start
    • configurable path on disk
  • use jupyter-js-services and jupyter-js-output-area, not thebe
  • ability to filter websocket messages see issue #6
  • supports one or more frontend browser client connections at a time and properly

Other issues will be opened about follow-on features.

Remove use of bower, just use npm

We only use bower for Gridstack and its dependencies (jquery, jquery-ui, lodash). Should just simplify our build and install everything through npm.

Allow dashboard server to send auth headers to kernel gateway

  1. tmpnb might be configured to require an auth token to request a KG server
  2. KG server might be configured to require an auth token to request a kernel

Assume both of these keys can be set once at server start time via nconf, and just pass them along where appropriate. The keys should NOT be exposed to the frontend client.

  • Pass auth token to kernel gateway
  • Pass auth token to tmpnb
  • Tests?

Prevent re-running of code cells

Current implementation prevents arbitrary code from the browser to reach the kernel. However, the browser can instruct the proxy to resend one of the code cells from a notebook multiple times. A bad actor might take advantage of this to mess with state on the backend if the notebook code is not written to account for multiple runs (e.g., incrementing a counter too many times).

Still, this is not a serious bug: the code already has to run once on the first load of the dashboard.

Use Node streams instead of overriding `socket.emit`?

We have an ugly bit of code in api.js setupWSProxy() where we override socket.emit in order to add in the actual notebook code to be executed. It would be cleaner to use Node streams, if possible.

The http-proxy library does the proxying of websocket connections with the following code in ws-incoming.js:

proxySocket.pipe(socket).pipe(proxySocket);

I believe the socket variable above is what we pass in to the proxy lib in api.js:

proxy.ws(req, socket, head);

(not 100% sure about that, though).

If my assumptions above are correct, we should then be able to create our own Duplex Stream implementation that will change the incoming WS messages. In the end, we want the equivalent of:

proxySocket.pipe(wsRewriter).pipe(socket).pipe(proxySocket);

Since we don't care about the return WS msgs, we only need to insert our object (wsRewriter) on the incoming side.

Allow multiple connections to view currently running kernel

For a deployed dynamic dashboard to be usable, people need to load an existing dashboard and immediately see its current state. For dashboards that are long-running (streaming) or that take time to run (Spark), it is not practical to re-run the code in a new kernel each time the page is loaded.

We should allow new connections to an already running kernel and properly build the dashboard with the required cell outputs and interactivity without having to re-run code.

Do something less yucky at /

Right now, it's a vanilla page with text that says "List of notebooks".

  • Pretty it up to have the banner like the dashboard pages have when auth is enabled.
  • Show a bootstrap styled list of dashboard links
  • If there's a dashboard notebook called index.ipynb, redirect immediately to that instead of showing the list?

Error handling seems off

I'm now seeing a lot of these types of error in the server console:

STACK: Error: Not Found
    at /home/node/app/app.js:105:15
    at Layer.handle [as handle_request] (/home/node/app/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/home/node/app/node_modules/express/lib/router/index.js:312:13)
    at /home/node/app/node_modules/express/lib/router/index.js:280:7
    at Function.process_params (/home/node/app/node_modules/express/lib/router/index.js:330:12)
    at next (/home/node/app/node_modules/express/lib/router/index.js:271:10)
    at /home/node/app/node_modules/express/lib/router/index.js:618:15
    at next (/home/node/app/node_modules/express/lib/router/index.js:256:14)
    at Function.handle (/home/node/app/node_modules/express/lib/router/index.js:176:3)
    at router (/home/node/app/node_modules/express/lib/router/index.js:46:12)
GET /components/index.js.map 404 2.301 ms - 878

They don't really say much about what's going on. It just seems to be a generic error handler. @dalogsdon, I think we need to revisit how these error handlers are setup in the express app.

Dashboard backend can proxy to KGs that don't run at root of domain

Kernel gateway supports a base_url parameter that allows it to run under non-root paths. This is necessary when it's deployed using something like tmpnb which runs multiple instances of the KG.

Make sure the dashboard server can:

  • Proxy to a single kernel gateway that is running under a custom base_url
  • [ ] Respects the URL returned by tmpnb's /api/spawn (see #11)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.