diff --git a/.gitignore b/.gitignore index 66611a2..6ad5f26 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,8 @@ /jupyterhub_volumes/jupyterhub /jupyterhub_volumes/caddy /**/.DS_Store - +/web_src/**/*.RData +/web_src/**/*.pdf +/web_src/**/*_files +/web_src/**/*_cache /.luarc.json diff --git a/jupyterhub_volumes/web/img/welcome_metabar.webp b/jupyterhub_volumes/web/img/welcome_metabar.webp index 8bebc61..5a12c21 100644 Binary files a/jupyterhub_volumes/web/img/welcome_metabar.webp and b/jupyterhub_volumes/web/img/welcome_metabar.webp differ diff --git a/obijupyterhub/Dockerfile b/obijupyterhub/Dockerfile index f8854c0..b937ae9 100644 --- a/obijupyterhub/Dockerfile +++ b/obijupyterhub/Dockerfile @@ -30,6 +30,7 @@ RUN apt-get update && apt-get install -y \ r-base \ libcurl4-openssl-dev libssl-dev libxml2-dev \ curl \ + git \ texlive-xetex texlive-fonts-recommended texlive-plain-generic \ ruby ruby-dev \ vim nano \ @@ -37,9 +38,16 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Installer R et packages -RUN R -e "install.packages(c('IRkernel','tidyverse','vegan','ade4','BiocManager','remotes'), repos='http://cran.rstudio.com/')" && \ +RUN R -e "install.packages(c('IRkernel','tidyverse','vegan','ade4','BiocManager','remotes','igraph'), \ + dependencies=TRUE, \ + repos='http://cran.rstudio.com/')" && \ R -e "BiocManager::install('biomformat')" && \ R -e "remotes::install_github('metabaRfactory/metabaR')" && \ + R -e "remotes::install_git('https://forge.metabarcoding.org/obitools/ROBIUtils.git')" && \ + R -e "remotes::install_git('https://forge.metabarcoding.org/obitools/ROBITaxonomy.git')" && \ + R -e "remotes::install_git('https://forge.metabarcoding.org/obitools/ROBITools.git')" && \ + R -e "remotes::install_git('https://forge.metabarcoding.org/obitools/ROBITaxonomy.git')" && \ + R -e "remotes::install_git('https://forge.metabarcoding.org/MetabarcodingSchool/biodiversity-metrics.git')" && \ R -e "IRkernel::installspec(user = FALSE)" && \ rm -rf /tmp/Rtmp* diff --git a/obijupyterhub/docker-compose.yml b/obijupyterhub/docker-compose.yml index ba13735..742dc90 100644 --- a/obijupyterhub/docker-compose.yml +++ b/obijupyterhub/docker-compose.yml @@ -13,8 +13,8 @@ services: - /var/run/docker.sock:/var/run/docker.sock # JupyterHub database persistence - data:/srv/jupyterhub - # The Jupyter user volumes - - users:/volumes + # Mount the parent volumes directory to access users/, shared/, course/ + - ../jupyterhub_volumes:/volumes # Mount config file directly (for easy modifications) - ./jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro networks: @@ -27,6 +27,8 @@ services: JUPYTERHUB_ADMIN_PASSWORD: admin2025 # Optional environment variables DOCKER_NOTEBOOK_DIR: /home/jovyan/work + # Use PWD to get absolute path relative to docker-compose.yml location + HOST_VOLUMES_PATH: ${PWD}/../jupyterhub_volumes # ---------- Nginx ---------- diff --git a/obijupyterhub/jupyterhub_config.py b/obijupyterhub/jupyterhub_config.py index e951187..9cb5fae 100644 --- a/obijupyterhub/jupyterhub_config.py +++ b/obijupyterhub/jupyterhub_config.py @@ -10,7 +10,8 @@ c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner' c.JupyterHub.log_level = 'DEBUG' c.Spawner.debug = True -VOLUMES_BASE_PATH = '/volumes' +VOLUMES_BASE_PATH = '/volumes/users' # Path as seen from JupyterHub container (for user dirs) +HOST_VOLUMES_PATH = os.environ.get('HOST_VOLUMES_PATH', '/volumes') # Real path on host machine (parent dir) # Docker image to use for student containers c.DockerSpawner.image = 'jupyterhub-student:latest' @@ -52,31 +53,28 @@ c.DockerSpawner.notebook_dir = notebook_dir async def create_user_dir(spawner): """Create user directory if it doesn't exist""" user_dir = os.path.join(VOLUMES_BASE_PATH, spawner.user.name) - spawner.log.info(f"Ensured user directory exists: {user_dir}") Path(user_dir).mkdir(parents=True, exist_ok=True) + + # Change owner to jovyan user (UID 1000, GID 100 in Jupyter images) + os.chown(user_dir, 1000, 100) os.chmod(user_dir, 0o755) + spawner.log.info(f"Created user directory with correct permissions: {user_dir}") + c.Spawner.pre_spawn_hook = create_user_dir c.DockerSpawner.volumes = { - # Personal volume (persistent) - root directory - 'obijupyterhub_shared-{username}' : '/home/jovyan/work', + # Personal volume - bind mount from REAL host path + f'{HOST_VOLUMES_PATH}/users/{{username}}': '/home/jovyan/work', # Shared volume between all students - under work/ - 'obijupyterhub_shared': '/home/jovyan/work/shared', + f'{HOST_VOLUMES_PATH}/shared': '/home/jovyan/work/shared', # Shared read-only volume for course files - under work/ - 'obijupyterhub_course': { + f'{HOST_VOLUMES_PATH}/course': { 'bind': '/home/jovyan/work/course', 'mode': 'ro' # read-only } } -c.DockerSpawner.volume_driver = 'local' -c.DockerSpawner.volume_driver_opts = { - 'type': 'none', - 'device': '/volumes', - 'o': 'bind' -} - # Memory and CPU configuration (adjust according to your needs) c.DockerSpawner.mem_limit = '2G' c.DockerSpawner.cpu_limit = 1.0 diff --git a/start-jupyterhub.sh b/start-jupyterhub.sh index 900e4d0..15003be 100755 --- a/start-jupyterhub.sh +++ b/start-jupyterhub.sh @@ -49,18 +49,27 @@ docker ps -aq --filter name=jupyter- | xargs -r docker rm -f 2>/dev/null || true # Build student image echo "" echo -e "${BLUE}🔨 Building student image...${NC}" -docker build -t jupyterhub-student:latest -f Dockerfile . +#docker build -t jupyterhub-student:latest -f Dockerfile . # Build hub image echo "" echo -e "${BLUE}🔨 Building JupyterHub image...${NC}" -docker build -t jupyterhub-hub:latest -f Dockerfile.hub . +#docker build -t jupyterhub-hub:latest -f Dockerfile.hub . # Compile the web site echo "" echo -e "${BLUE}🔨 Building web site...${NC}" pushd ../web_src quarto render +find . -name '*.pdf' -print \ +| while read pdfname ; do + dest="../jupyterhub_volumes/web/pages/${pdfname}" + dirdest=$(dirname "$dest") + mkdir -p "$dirdest" + echo "cp '${pdfname}' '${dest}'" + done \ +| bash +python3 ../tools/generate_pdf_galleries.py python3 ../tools/generate_pages_json.py popd diff --git a/tools/generate_pdf_galleries.py b/tools/generate_pdf_galleries.py new file mode 100644 index 0000000..76b87ee --- /dev/null +++ b/tools/generate_pdf_galleries.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +"""Generate lightweight HTML galleries for PDF files within PAGES_DIR.""" +from __future__ import annotations + +import html +import os +import re +from pathlib import Path +from typing import Iterable, List, Tuple +from urllib.parse import quote + +SCRIPT_DIR = Path(__file__).resolve().parent +PAGES_DIR = (SCRIPT_DIR / ".." / "jupyterhub_volumes" / "web" / "pages").resolve() + +GALLERY_TEMPLATE = """ + +
+ + +