Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jupyter-images/spring-2026/uga/.condarc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
envs_dirs:
- /home/jovyan/additional-envs
43 changes: 43 additions & 0 deletions jupyter-images/spring-2026/uga/Acknowledgements.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "c86cd54f-b73c-4781-b6eb-89c79d3d3b22",
"metadata": {},
"source": [
"## Acknowledgements\n",
"\n",
"Launching this JupyterHub server is the result of a collaboration between several research and academic institutions and their staff. For Jetstream2 and JupyterHub expertise, we thank Andrea Zonca (San Diego Supercomputing Center), Jeremy Fischer, Mike Lowe (Indiana University), the NSF Jetstream2 (`doi:10.1145/3437359.3465565`) team.\n",
"\n",
"This work employs the NSF Jetstream2 Cloud at Indiana University through allocation EES220002 from the Advanced Cyberinfrastructure Coordination Ecosystem: Services & Support (ACCESS) program, which is supported by National Science Foundation grants #2138259, #2138286, #2138307, #2137603, and #2138296.\n",
"\n",
"Unidata is one of the University Corporation for Atmospheric Research (UCAR)'s Community Programs (UCP), and is funded primarily by the National Science Foundation (AGS-2403649).\n",
"\n",
"## To Acknowledge This JupyterHub and the Unidata Science Gateway\n",
"\n",
"If you have benefited from the Unidata Science Gateway, please cite `doi:10.5065/688s-2w73`. Additional citation information can be found in this [Citation File Format file](https://raw.githubusercontent.com/Unidata/science-gateway/master/CITATION.cff).\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
57 changes: 57 additions & 0 deletions jupyter-images/spring-2026/uga/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Heavily borrowed from docker-stacks/minimal-notebook/
# https://github.com/jupyter/docker-stacks/blob/main/minimal-notebook/Dockerfile

ARG BASE_CONTAINER=quay.io/jupyter/minimal-notebook
FROM $BASE_CONTAINER

ENV DEFAULT_ENV_NAME=uga26s EDITOR=nano VISUAL=nano

LABEL maintainer="Unidata <support-gateway@unidata.ucar.edu>"

USER root

RUN apt-get update && \
apt-get install -y --no-install-recommends vim nano curl zip unzip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

COPY jupyter_server_proxy_config.py /etc/jupyter/jupyter_server_proxy_config.py
COPY setup-unidata-desktop.sh /usr/local/bin/before-notebook.d/
COPY IDV.desktop /usr/local/share/applications/
COPY cave.desktop /usr/local/share/applications/
RUN chmod +x /usr/local/share/applications/IDV.desktop && \
chmod +x /usr/local/share/applications/cave.desktop && \
cat /etc/jupyter/jupyter_server_proxy_config.py >> /etc/jupyter/jupyter_server_config.py

RUN mkdir -p /usr/local/share/icons

COPY unidata.svg /usr/local/share/icons

USER $NB_UID

ADD environment.yml /tmp

RUN mamba create --name $DEFAULT_ENV_NAME --quiet --yes && \
mamba install --quiet --yes \
'conda-forge::nodejs' \
'conda-forge::nb_conda_kernels' \
'conda-forge::jupyterlab-git' \
'conda-forge::ipywidgets' \
'conda-forge::jupyter-server-proxy' && \
mamba env update --name $DEFAULT_ENV_NAME -f /tmp/environment.yml && \
pip install --no-cache-dir nbgitpuller && \
mamba clean --all -f -y && \
jupyter lab clean -y && \
npm cache clean --force && \
rm -rf /home/$NB_USER/.cache/yarn && \
rm -rf /home/$NB_USER/.node-gyp && \
fix-permissions $CONDA_DIR && \
fix-permissions /home/$NB_USER

COPY additional_kernels.ipynb update_material.ipynb Acknowledgements.ipynb \
default_kernel.py .condarc /

ARG JUPYTER_SETTINGS_DIR=/opt/conda/share/jupyter/lab/settings/
COPY overrides.json $JUPYTER_SETTINGS_DIR

USER $NB_UID
8 changes: 8 additions & 0 deletions jupyter-images/spring-2026/uga/IDV.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Desktop Entry]
Version=6.3
Type=Application
Name=IDV
Exec=/opt/IDV/runIDV
Icon=/opt/IDV/idv.gif
Terminal=false
Categories=Utility;
58 changes: 58 additions & 0 deletions jupyter-images/spring-2026/uga/additional_kernels.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "40a205e0-d595-4969-9d97-9e9584605002",
"metadata": {},
"source": [
"### Creating Additional Kernels\n",
"\n",
"You can add new kernels to JupyterLab so they appear in the **Kernel** menu.\n",
"Each environment must include `nb_conda_kernels` and `ipykernel`.\n",
"\n",
"For example, to create an environment with the `seaborn` package, make an `environment.yml` file (using `pico` in the terminal or the **Text File** editor in JupyterLab):\n",
"\n",
"```yaml\n",
"name: myenv\n",
"channels:\n",
" - conda-forge\n",
"dependencies:\n",
" - python=3\n",
" - seaborn\n",
" - nb_conda_kernels\n",
" - ipykernel\n",
"```\n",
"\n",
"Then run:\n",
"\n",
"```bash\n",
"mamba env create -f environment.yml\n",
"```\n",
"\n",
"Finally, go to **File → Hub Control Panel…**, stop your server, and restart it.\n",
"After restarting, open or create a notebook and choose **Kernel → Change Kernel → myenv**."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
65 changes: 65 additions & 0 deletions jupyter-images/spring-2026/uga/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/bash

# Exit immediately if a command exits with a non-zero status
set -e

usage() {
echo "Usage: $0 <image-name> [--push] [-f <Dockerfile>]"
echo " <image-name> Name of the Docker image to build"
echo " --push Optionally push the Docker image to DockerHub"
echo " -f <Dockerfile> Specify an alternate Dockerfile to use"
exit 1
}

if [ -z "$1" ]; then
echo "Error: No image name provided."
usage
fi

IMAGE_NAME=$1
PUSH_IMAGE=false
DOCKERFILE="Dockerfile"

# Parse optional arguments
shift
while [[ $# -gt 0 ]]; do
case "$1" in
--push)
PUSH_IMAGE=true
shift
;;
-f)
if [ -z "$2" ]; then
echo "Error: -f requires a Dockerfile path"
usage
fi
DOCKERFILE="$2"
shift 2
;;
*)
echo "Unknown option: $1"
usage
;;
esac
done

DATE_TAG=$(date "+%Y%b%d_%H%M%S")
RANDOM_HEX=$(openssl rand -hex 2)
TAG="${DATE_TAG}_${RANDOM_HEX}"
FULL_TAG="unidata/$IMAGE_NAME:$TAG"

# Build the Docker image
echo "Building Docker image with tag: $FULL_TAG using Dockerfile: $DOCKERFILE"
docker build --no-cache --pull --tag "$FULL_TAG" -f "$DOCKERFILE" .

echo "Docker image built successfully: $FULL_TAG"

if $PUSH_IMAGE; then
echo "Pushing Docker image to DockerHub: $FULL_TAG"
docker push "$FULL_TAG"
echo "Docker image pushed successfully: $FULL_TAG"
else
echo "Skipping Docker image push. Use '--push' to push the image."
fi

exit 0
10 changes: 10 additions & 0 deletions jupyter-images/spring-2026/uga/cave.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
Encoding=UTF-8
Version=1.0
Name=AWIPS CAVE
Comment=Unidata AWIPS CAVE Visualization Client
Icon=/awips2/cave/cave.png
Exec=/opt/cave/cave.sh
Categories=Network;WebBrowser
X-Desktop-File-Install-Version=0.15
67 changes: 67 additions & 0 deletions jupyter-images/spring-2026/uga/default_kernel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python

import argparse
import glob
import json
import os
import re


def update_kernelspec_in_notebooks(directory, new_name):
"""
Updates the kernelspec in all Jupyter Notebook files within the specified
directory and its subdirectories, while preserving the original file
formatting.

Args:
directory (str): The path to the directory containing .ipynb files.
new_name (str): The new name to set in the kernelspec.
"""
for file_path in glob.glob(f'{directory}/**/*.ipynb', recursive=True):
try:
with open(file_path, 'r', encoding='utf-8') as file:
file_contents = file.read()
notebook = json.loads(file_contents)

if 'kernelspec' not in notebook.get('metadata', {}):
print(f"No kernelspec found in {file_path}. Skipping file.")
continue

kernelspec = notebook['metadata']['kernelspec']
kernelspec['display_name'] = f"Python [conda env:{new_name}]"
kernelspec['name'] = f"conda-env-{new_name}-py"

# Convert the updated kernelspec dictionary to a JSON-formatted
# string with indentation
updated_kernelspec = json.dumps(kernelspec, indent=4)

# Replace the existing kernelspec section in the original file
# contents with the updated JSON string. The regular expression
# looks for the "kernelspec" key and replaces its entire value
# (including nested structures), preserving the overall structure
# and formatting of the file.
updated_contents = re.sub(
r'"kernelspec": *\{.*?\}',
f'"kernelspec": {updated_kernelspec}',
file_contents, flags=re.DOTALL
)

with open(file_path, 'w', encoding='utf-8') as file:
file.write(updated_contents)

except Exception as e:
print(f"Error processing file {file_path}: {e}")


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Update the kernel name in "
"Jupyter Notebook files in directory "
"tree.")
parser.add_argument("new_kernel_name", help="New kernel name to set.")
parser.add_argument("directory_path", nargs='?', default=os.getcwd(),
help="Directory containing .ipynb files (default: "
"current directory).")

args = parser.parse_args()

update_kernelspec_in_notebooks(args.directory_path, args.new_kernel_name)
26 changes: 26 additions & 0 deletions jupyter-images/spring-2026/uga/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: uga26s
channels:
- conda-forge
dependencies:
# Required by JupyterLab
- 'python<3.13'
- nb_conda_kernels
- ipykernel
# User requested packages
- numpy
- matplotlib
- cartopy
- metpy
- siphon
- pandas
- pip
- xarray
- ipywidgets
- python-awips
- scikit-learn
- seaborn
- herbie-data
- pip:
# It is recommended to install a package using pip as a last resort, i.e.
# when it is not found in the conda repos
- palmerpenguins
20 changes: 20 additions & 0 deletions jupyter-images/spring-2026/uga/jupyter_server_proxy_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
######
# Begin Jupyter Server Proxy Config
######

c.ServerProxy.servers = {
"Virtual-Desktop": {
"command": [], # Unmanaged mode, since you're launching it elsewhere
"port": 6080,
"launcher_entry": {
"title": "NSF Unidata Desktop for IDV and AWIPS CAVE",
"path_info": "proxy/6080/vnc.html?resize=scale",
"icon_path": "/usr/local/share/icons/unidata.svg"
}
}
}


######
# End Jupyter Server Proxy Config
######
7 changes: 7 additions & 0 deletions jupyter-images/spring-2026/uga/overrides.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"@jupyterlab/docmanager-extension:plugin": {
"defaultViewers": {
"markdown": "Markdown Preview"
}
}
}
Loading