A code example for building web apps without writing HTML, CSS. and JavaScript.
Got a Python project or Jupyter notebook? Want to turn it into a web applilcation?
This repository contains easy-to-modify Python code. It demonstrates using ipywidgets to build an interactive web application. It relies on Jupyter notebook infrasturcture. However, the app looks like a web app, not a notebook. Further, it demonstrates the use of Docker and Voilà to allow the app to be hosted on composable infrastructure. The example code uses pandas for data access and Matplotlib to generate plots.
The template was developed so researchers can quickly and easily put their project on the web without getting bogged down in conventional web developement (AJAX, HTML, CSS, JS, etc.). The example notebook uses global temperature data from NASA to show how users can view, search, download, and plot data using an interactive, web enabled tool.
Source code is organized in a loose Model-view-controller pattern. Most of code is divided into three files in the "nb" directory:
- Model: Works with data and storage (file system, database, etc)
- View: Builds the user interface (widgets, plots, etc.)
- Controller: Responds to user actions (button presses, menu selections, etc.)
The Jupyter notebook ('notebook.ipynb") contains just one code cell. This kicks off the MVC code and the web app starts running.
The code relies on widgets and callback methods. Once it's up and running, the code waits for the user to make changes to user interface widgets. But widget updates also work in both directions. When the user interacts with a widget in their browser, an assigned callback method runs. And when some code changes a widget, those changes appear in the browser.
For example, the view object creates a button named "select_btn_apply". The controller specifies that, when this button is pressed, its "when_apply_select()" method should be called ("view.select_btn_apply.on_click(self.when_apply_select)"). The "when_apply_select()" method then directs the model to perform the query and then updates the view's output widget, "view.select_output".
This project requires Python, Jupyter, and a number of Python packages. One options is to manually install the packages listed under "dependencies" in environment.yml
. Simply use your OS's package manager and/or the pip
command. The recommended option is to use the conda package management system to create an isolated environment. This prevents package installations from affecting your other projects.
- Install Anaconda on your workstation.
- At the OS command line, run:
conda env create --file environment.yml
. This creates a conda environment called "nbtmpl" and installs packages into it. Answer "y" to prompts.
- Start a command line (terminal) session.
- If using conda, enter
conda activate nbtmpl
- Browse to the "nbtmpl" directory
- Enter
voila notebook.ipynb
. A browser window shoudd open and run the app. If the app doesn't run enter URL "http://localhost:8866/".
Run the notebook in Jupyter Lab by opening the "Run" menu and selecting "Restart Kernel and Run All Cells..". Then press the the "Restart" button that appears. This will allow you to view any exceptions and errors.
For simple bugs, use the log and print debugging (logging.debug(...)
) to display values of variables. Specify log=True
when calling the view's start()
method.
For more difficult bugs, use Jupyter Lab's debugger. See below:
- Start a command line (terminal) session.
- If using conda, enter
conda activate nbtmpl
- Enter
jupyter-lab
. (See Starting JupyterLab for more info. A browser window should appear. If the displayed page indicates access was denied, close the browser window and start another using one of the other URLs listed in the Jupyter Lab command output. Use a URL starting with "http://localhost...". - Browse to the "nbtmpl" directory and double click on the
notebook.ipynb
file.
- Enable debugging using the bug buggon near the upper right corner of the notebook cells window.
- Reference this [animation]](https://jupyterlab.readthedocs.io/en/stable/user/debugger.html#usage)
- First Set a breakpoint at the "controller.start(..." line in the notebook cell.
- Using the play button (triangle), run to that breakpoint.
- Next, step into that line using the "Step In (F11) button" (next to the word "CALLSTACK", right side of window).
- Then, set a breakpoitn in the Controller - or - step into the model or viewcode and set breakpoints as needed.
Currently, only the Docker container system is documented here. Additional container systems should be added later.
NOTE: The following steps pull the notebook code from a repository. If you've customized the template:
- Create a git repository to host your code.
- Commit your latest changes to that repo.
- Substiture your repo's URL and name in "repourl=..." and "repodir=..." below.
- Install Docker on your workstation. Note that you may need admin access to run Docker commands.
- Start a command line (terminal) session.
- Make sure Docker is running by entering:
docker info
. - Build the Docker image by entering the following (note: final "
.
" is required.):docker build -t nbtmpl1 --build-arg repourl=https://github.com/rcpurdue/nbtmpl.git .
- Run the image:
docker run -p 8866:8866 nbtmpl1
- Run the app (notebook) in your browser:
http://localhost:8866/
NOTE: In the commands above:
nbtmpl1
= arbitrary name for the Docker image - use whatever you wanthttps://...
= repository URL - substitute your own when you customize the codenbtmpl
= repo name (and, therefore, the name of the repo's directory) - change if customized8866:8866
= connection port mapping within and out of container - change if conflicts
To allow others to run your app, it must be hosted on a publicly available Docker hosting system. There are a wide variety of options available including commercial sites like Amazon's AWS. Some institutions maintain their own Docker hosting systems. Depending on the specific reqirements of the hosting system you select, you'll need to provide either:
- a
Dockerfile
similar to one included in this repo, or - a Docker image like the one built above. You might be required to access the host system's Kubernetes management system (e.g. Rancher) to create the container and allocate resources.
An alternate Dockerfile is provided to facilitate development iterations (write code, test, repeat). In this scenario the container reaches out onto your workstation's filesystem to read the code and data. This allows you to make changes to the code or data and then just refresh the page in your browser to test it (rather than rebuilding the image). A separate development image is built and run below. Notice the special development Dockerfile that's used (./dev/Dockerfile
):
- Build the dev image:
docker build -t nbtmpl_dev1 dev/Dockerfile .
- Run the devimage:
docker run -p 8866:8866 --mount type=bind,src=/home/rcampbel/repos/nbtmpl,target=/home/jovyan/external nbtmpl_dev1
NOTE: Change "/home/rcampbel/repos/nbtmpl
" to the full path to your local repo.