diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..46c3e70
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+**/requirements.txt text eol=lf
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 20aad76..2544760 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -116,7 +116,7 @@ jobs:
- run: pip install -e ".[docs,dev]"
- name: Install PyKale (the tutorials import kale at execution time)
# Only this job executes tutorials, so only it needs kale. Matches the notebook install cell
- # (_shared/gallery/notebook_first_cell.py), which installs the same source on Colab.
+ # (source/_shared/gallery/notebook_first_cell.py), which installs the same source on Colab.
run: pip install "git+https://github.com/pykale/pykale.git"
- name: Install runnable tutorials' extra dependencies
run: |
diff --git a/.gitignore b/.gitignore
index ac00e27..454fb0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,13 +42,13 @@ htmlcov/
Thumbs.db
# Generated tutorial thumbnails (deterministic, rebuilt by scripts/generate_thumbnails.py)
-_static/thumbnails/
+source/_static/thumbnails/
# Generated homepage partials
_generated/
-_static/tutorials.json
-# copied at setup from _shared/assets/logos
-_static/pykale_logo.png
+source/_static/tutorials.json
+# copied at setup from source/_shared/assets/logos
+source/_static/pykale_logo.png
# Internal working note, kept local (not published)
docs/PYTORCH_ALIGNMENT.md
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0ff9215..29490a7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,5 +1,4 @@
-# Hook versions are pinned to match pykale/.pre-commit-config.yaml so a contributor moving between
-# the two repos gets identical formatting decisions.
+# Hook versions are pinned for reproducible formatting and a working local setup.
repos:
- repo: https://github.com/datarootsio/databooks
rev: 1.3.7
@@ -19,7 +18,7 @@ repos:
- id: mixed-line-ending
- id: trailing-whitespace
- id: debug-statements
- exclude: ^tutorials/
+ exclude: ^(others|protein|cancer|cardiac|materials)/
- id: check-yaml
- id: requirements-txt-fixer
@@ -30,8 +29,8 @@ repos:
additional_dependencies: [flake8-print, "importlib-metadata<5.0.0"]
args: [--config=setup.cfg]
# Migrated example code is kept byte-identical to upstream pykale/examples, which is itself
- # excluded from flake8 there. Only tutorial.py, authored here, is linted.
- exclude: ^tutorials/(?!.*tutorial\.py$).*
+ # excluded from flake8 there. Only plot_.py, authored here, is linted.
+ exclude: ^(others|protein|cancer|cardiac|materials)/(?!.*plot_[^/]+\.py$).*
- repo: https://github.com/psf/black
rev: 23.11.0
@@ -39,18 +38,18 @@ repos:
- id: black
language_version: python3
additional_dependencies: ['click==8.0.4']
- exclude: ^tutorials/(?!.*tutorial\.py$).*
+ exclude: ^(others|protein|cancer|cardiac|materials)/(?!.*plot_[^/]+\.py$).*
- repo: https://github.com/pycqa/isort
- rev: 5.11.2
+ rev: 5.13.2
hooks:
- id: isort
args: [--settings-path, ./pyproject.toml]
- exclude: ^tutorials/(?!.*tutorial\.py$).*
+ exclude: ^(others|protein|cancer|cardiac|materials)/(?!.*plot_[^/]+\.py$).*
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.0
hooks:
- id: mypy
- files: ^(scripts|_shared)/
+ files: ^(scripts|source/_shared)/
additional_dependencies: [pydantic>=2.9, types-PyYAML]
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cdc9daa..469b1d6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,20 +11,20 @@ pre-commit install
1. **Pick the folder.** The path fixes the domain and the task, so this decision is the important one. Domain buckets are `others`, `protein`, `cancer`, `cardiac`, `materials`. Use `others` when the tutorial demonstrates a method rather than an application. If the tutorial is one of a related set, nest it in a group folder as `others/image_classification/` does.
-2. **Copy the templates.**
+2. **Copy the templates.** For example, to add `others/my_tutorial`:
```bash
- mkdir -p /
- cp _shared/templates/metadata.template.yml //metadata.yml
- cp _shared/templates/README.template.md //README.md
- cp _shared/templates/tutorial.py.template //tutorial.py
+ mkdir -p others/my_tutorial
+ cp source/_shared/templates/metadata.template.yml others/my_tutorial/metadata.yml
+ cp source/_shared/templates/README.template.md others/my_tutorial/README.md
+ cp source/_shared/templates/plot.py.template others/my_tutorial/plot_my_tutorial.py
```
3. **Register it** in `tutorial_catalog.yml` under the right bucket, and in the group's `tutorials:` list if it belongs to one. Order within a list is the order it appears in the gallery, so put the gentlest introduction first.
4. **Fill in `metadata.yml`.** Every field is validated. Derive `kale_api` from the imports the code actually makes, not from prose — upstream READMEs have drifted from their code more than once.
-5. **Write `tutorial.py`.** See the conventions below.
+5. **Write `plot_.py`** (replace `` with the folder name). See the conventions below.
6. **Check it.**
@@ -33,7 +33,7 @@ pre-commit install
make html-noplot
```
-## Conventions for `tutorial.py`
+## Conventions for `plot_.py`
Sphinx-gallery turns this file into an HTML page, a notebook and a downloadable script. It is the only file in the folder that the doc build executes.
@@ -42,7 +42,7 @@ Sphinx-gallery turns this file into an HTML page, a notebook and a downloadable
- **Make the first plot the best one.** It becomes the gallery thumbnail.
- **Show the mechanism, not just the score.** A metric says whether it worked; a plot of what the model learned says why.
- **Set the seed** with `kale.utils.seed.set_seed`, which covers cuDNN as well as Python, NumPy and torch.
-- Sections are `# %%` blocks with an rST heading. Only `tutorial.py` is linted and formatted; migrated example code is deliberately left byte-identical to upstream.
+- Sections are `# %%` blocks with an rST heading. The authored `plot_.py` narrative is linted and formatted; migrated example code is deliberately left byte-identical to upstream.
## Migrated examples
@@ -52,7 +52,7 @@ Files copied from `pykale/examples` are kept unmodified so that upstream fixes c
python scripts/migrate_examples.py --pykale-root ../pykale --dry-run
```
-The sync never overwrites `tutorial.py`, `metadata.yml`, `README.md` or `requirements.txt`. Where a copied file genuinely must change — an import that assumed the `examples` package, for instance — record it in `POST_MIGRATION_PATCHES` in `scripts/migrate_examples.py` rather than editing the file directly, so a re-sync cannot quietly undo it.
+The sync never overwrites `plot_.py`, `metadata.yml`, `README.md` or `requirements.txt`. Where a copied file genuinely must change — an import that assumed the `examples` package, for instance — record it in `POST_MIGRATION_PATCHES` in `scripts/migrate_examples.py` rather than editing the file directly, so a re-sync cannot quietly undo it.
## Data
diff --git a/Makefile b/Makefile
index 667500d..eadf0a5 100644
--- a/Makefile
+++ b/Makefile
@@ -7,8 +7,8 @@ PYTHON ?= python
help:
@echo "install install docs + dev dependencies"
@echo "validate check catalog, metadata, schema and thumbnails agree (run before pushing)"
- @echo "schema regenerate _shared/metadata/tutorial.schema.json"
- @echo "export regenerate the landing-page + _static tutorials.json"
+ @echo "schema regenerate source/_shared/metadata/tutorial.schema.json"
+ @echo "export regenerate the landing-page + source/_static tutorials.json"
@echo "pages regenerate the per-domain pages"
@echo "thumbnails regenerate the placeholder SVG thumbnails"
@echo "homepage regenerate the homepage/list-page partials"
diff --git a/README.md b/README.md
index 34d0295..4fa85fc 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,15 @@
# PyKale Tutorials
-Runnable tutorials for [PyKale](https://github.com/pykale/pykale), published as a [sphinx-gallery](https://sphinx-gallery.github.io/) site.
+[](https://github.com/pykale/tutorials/actions/workflows/docs.yml)
+[](https://pykale.github.io/tutorials/)
+[](https://pypi.org/project/pykale/)
+[](https://www.python.org/)
+[](LICENSE)
+[](https://github.com/psf/black)
-Each tutorial is a self-contained folder holding two things: a narrated `tutorial.py` that runs in minutes and explains the method, and the full `main.py` experiment it was drawn from. Read the first, run the second when you want real numbers.
+Runnable tutorials for [PyKale](https://github.com/pykale/pykale), published at **[pykale.github.io/tutorials](https://pykale.github.io/tutorials/)** as a [sphinx-gallery](https://sphinx-gallery.github.io/) site.
+
+Each tutorial is a self-contained folder holding two things: a narrated `plot_.py` that runs in minutes and explains the method, and the full `main.py` experiment it was drawn from. Read the first, run the second when you want real numbers.
## Layout
@@ -23,15 +30,17 @@ Supporting directories:
| Path | What it holds |
|---|---|
-| `_shared/` | Gallery config, metadata schema, contribution templates, launch-badge builders, assets |
+| `source/` | Build infrastructure, kept out of the root: Sphinx `_static` assets, `_templates`, shared gallery/launch code and metadata schema (`_shared`), and the news `_data` |
+| `pages/` | Standalone content pages: getting started, all tutorials, learning paths, tutorial status, and about. `index.rst` remains at the root as Sphinx's entry page. |
| `scripts/` | Migration, validation, schema generation, landing-page export |
| `tutorial_catalog.yml` | Source of truth: which tutorials exist and in what order |
## Running a tutorial
```bash
-pip install -r requirements.txt
cd others/image_classification/digit_domain_adaptation
+pip install pykale
+pip install -r requirements.txt
python main.py --cfg configs/MN2UP-DANN.yaml --devices 1
```
@@ -39,7 +48,7 @@ Every tutorial's README states its datasets, hardware needs and expected runtime
## Building the site
-The tasks are available three ways. The **direct Python commands work everywhere** — use these if in doubt (Windows has no `make` by default):
+Install the site dependencies and validate the catalogue:
```bash
pip install -e ".[docs,dev]"
@@ -48,20 +57,27 @@ pip install -e ".[docs,dev]"
python scripts/generate_schema.py --check
python scripts/validate_catalog.py
-# fast build, no tutorial execution (what CI runs on PRs)
-python scripts/generate_bucket_pages.py
-python -m sphinx -b html -W --keep-going . _build/html # (bash: prefix TUTORIALS_NOPLOT=1)
-
-# full build, executes every runnable tutorial.py
-python scripts/generate_bucket_pages.py
-python -m sphinx -b html -W --keep-going . _build/html
```
-Shortcuts wrapping exactly those commands:
+For a build without tutorial execution, use the platform shortcut. Both run the thumbnail,
+homepage, and domain-page generators before Sphinx:
- **Linux/macOS/CI:** `make validate`, `make html-noplot`, `make html` (needs GNU `make`).
- **Windows:** `.\make.bat validate`, `.\make.bat html-noplot`, `.\make.bat html` (PowerShell/cmd).
- From Git Bash, call it as `cmd //c ".\make.bat validate"`, or just run the Python commands above.
+ From Git Bash, call it as `cmd //c ".\make.bat html-noplot"`.
+
+To run the equivalent commands directly, generate the source files first:
+
+```bash
+python scripts/generate_thumbnails.py
+python scripts/generate_homepage.py
+python scripts/generate_bucket_pages.py
+```
+
+Then run `TUTORIALS_NOPLOT=1 python -m sphinx -b html -W --keep-going . _build/html`
+in bash, or set `$env:TUTORIALS_NOPLOT='1'` before the Sphinx command in PowerShell.
+For a full build that executes eligible `plot_.py` narratives, unset that variable and run
+the same Sphinx command after installing PyKale and each runnable tutorial's requirements.
Open `_build/html/index.html` when the build finishes.
@@ -71,11 +87,11 @@ Three layers, cheapest first — mirroring how PyTorch's tutorials CI works, min
1. **`validate` + unit tests** (every PR) — catalog/metadata/schema agree; the gallery filter, migration and generators are unit-tested. Fast, no runtime deps.
2. **`html-noplot`** (every PR) — renders every tutorial from source but executes none. Catches structural and parse errors without running anything.
-3. **Nightly executing build** (`nightly-exec` job) — actually *runs* the CPU-runnable, non-draft tutorials with their real dependencies, catching runtime breakage (a wrong `kale` call, a shape mismatch) that the parse-only layers miss. GPU-required tutorials are rendered but never executed, matching PyTorch's curated do-not-run list. The set that runs is defined once (`TutorialMetadata.is_runnable`) and shared by the build filter and `scripts/list_runnable.py`.
+3. **Manually triggered executing build** (`nightly-exec` job) — runs the CPU-runnable, non-draft tutorials with their real dependencies, catching runtime breakage (a wrong `kale` call, a shape mismatch) that the parse-only layers miss. Its nightly schedule is currently disabled. GPU-required tutorials are rendered but never executed. The set that runs is defined once (`TutorialMetadata.is_runnable`) and shared by the build filter and `scripts/list_runnable.py`.
## Status
-All 16 tutorials are migrated and runnable. Most `tutorial.py` narratives are still stubs, marked `draft: true` in their metadata and badged in the gallery — the underlying example is complete either way. Writing those narratives is the current work.
+All 16 tutorials are migrated. Most `plot_.py` narratives are still stubs, marked `draft: true` in their metadata and badged in the gallery — the underlying example is complete either way. Writing those narratives is the current work. Notebook launch actions remain disabled until generated notebooks are published.
## Relationship to the main library
@@ -83,7 +99,7 @@ These tutorials were migrated from `examples/` in the [pykale](https://github.co
## Contributing
-See [CONTRIBUTING.md](CONTRIBUTING.md). New tutorials start from the templates in `_shared/templates/`.
+See [CONTRIBUTING.md](CONTRIBUTING.md). New tutorials start from the templates in `source/_shared/templates/`.
## License
diff --git a/ROADMAP.md b/ROADMAP.md
index d0d303d..f12a126 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,15 +1,15 @@
# Roadmap
**Phase 1 — scaffold and migrate. _Done._**
-All 16 examples migrated from `pykale/examples`, `_shared/` infrastructure and build scripts in place, one complete narrative (`others/image_classification/digit_domain_adaptation`). Local repository only.
+All 16 examples migrated from `pykale/examples`, shared infrastructure and build scripts in place, one complete narrative (`others/image_classification/digit_domain_adaptation`).
-**Phase 2 — publish.**
-Create `pykale/tutorials`, push, enable Actions, set branch protection, and decide the docs host (ReadTheDocs project vs GitHub Pages). That decision fills in the deploy step `.github/workflows/docs.yml` deliberately leaves blank.
+**Phase 2 — publish the site. _Done._**
+The repository is at `pykale/tutorials`. The docs workflow builds the site and deploys pushes to `main` to GitHub Pages at `https://pykale.github.io/tutorials/`.
-Also required here: **Colab and Binder launch URLs do not resolve yet.** Both point at `notebooks///tutorial.ipynb`, on the assumption that CI commits sphinx-gallery's generated notebooks to `notebooks/` on main. Sphinx-gallery writes them under `_gallery/`, which is gitignored, and Colab cannot open a notebook from the docs site — only from GitHub, Drive or a gist. Until that CI step exists, every Colab and Binder badge is a 404.
+**Notebook launch actions remain pending.** Colab and Binder URLs expect `notebooks///plot_.ipynb` on `main`, but Sphinx-Gallery generates notebooks under the gitignored `_gallery/` directory. The site keeps notebook download and Colab actions disabled until notebooks are published and verified. Do not add launch badges to the README before that.
**Phase 3 — the remaining 15 narratives.**
-The bulk of the work. Small PRs, one bucket at a time, starting with `others/image_classification` since its four tutorials share a theme. Each landing PR clears that tutorial's `draft: true`. `make validate --strict-drafts` passes when the phase is complete.
+The bulk of the work. Small PRs, one bucket at a time, starting with `others/image_classification` since its four tutorials share a theme. Each landing PR clears that tutorial's `draft: true`. `python scripts/validate_catalog.py --strict-drafts` passes when the phase is complete. The executing CI job can be run manually; its nightly schedule is currently disabled.
**Phase 4 — landing page.**
Blocked on a framework decision. The data contract is already fixed and exported; see `../landing-page/README.md`.
diff --git a/cancer/multiomics_cancer_classification/README.md b/cancer/multiomics_cancer_classification/README.md
index 7f94799..a5d413c 100644
--- a/cancer/multiomics_cancer_classification/README.md
+++ b/cancer/multiomics_cancer_classification/README.md
@@ -2,7 +2,7 @@
Integrate mRNA expression, DNA methylation and miRNA expression with per-omics graph convolutional networks, fusing their predictions through a view-correlation discovery network.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_multiomics_cancer_classification.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/cancer/multiomics_cancer_classification/plot_multiomics_cancer_classification.py b/cancer/multiomics_cancer_classification/plot_multiomics_cancer_classification.py
index 09fc6fe..2e29618 100644
--- a/cancer/multiomics_cancer_classification/plot_multiomics_cancer_classification.py
+++ b/cancer/multiomics_cancer_classification/plot_multiomics_cancer_classification.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/cardiac/cardiac_mri_diagnosis/README.md b/cardiac/cardiac_mri_diagnosis/README.md
index 5e6afbf..fb649e8 100644
--- a/cardiac/cardiac_mri_diagnosis/README.md
+++ b/cardiac/cardiac_mri_diagnosis/README.md
@@ -2,7 +2,7 @@
Diagnose pulmonary arterial hypertension from short-axis cardiac MRI. MPCA keeps the tensor structure of the image stack intact, and the learned weights map back onto the anatomy.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_cardiac_mri_diagnosis.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/cardiac/cardiac_mri_diagnosis/plot_cardiac_mri_diagnosis.py b/cardiac/cardiac_mri_diagnosis/plot_cardiac_mri_diagnosis.py
index 507368c..2bfc3fd 100644
--- a/cardiac/cardiac_mri_diagnosis/plot_cardiac_mri_diagnosis.py
+++ b/cardiac/cardiac_mri_diagnosis/plot_cardiac_mri_diagnosis.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/cardiac/landmark_uncertainty_estimation/README.md b/cardiac/landmark_uncertainty_estimation/README.md
index 32e4202..e445f2f 100644
--- a/cardiac/landmark_uncertainty_estimation/README.md
+++ b/cardiac/landmark_uncertainty_estimation/README.md
@@ -2,7 +2,7 @@
Quantile Binning sorts landmark predictions into bins of increasing uncertainty with estimated error bounds, so the least reliable predictions can be filtered out before they are acted on.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_landmark_uncertainty_estimation.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/cardiac/landmark_uncertainty_estimation/plot_landmark_uncertainty_estimation.py b/cardiac/landmark_uncertainty_estimation/plot_landmark_uncertainty_estimation.py
index 670d7bd..2b951fe 100644
--- a/cardiac/landmark_uncertainty_estimation/plot_landmark_uncertainty_estimation.py
+++ b/cardiac/landmark_uncertainty_estimation/plot_landmark_uncertainty_estimation.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/conf.py b/conf.py
index a71d456..8978c96 100644
--- a/conf.py
+++ b/conf.py
@@ -1,8 +1,8 @@
"""Sphinx configuration for the PyKale tutorial gallery.
Everything tutorial-specific comes from ``tutorial_catalog.yml`` via
-``_shared/gallery/sphinx_gallery_config.py``. Adding a tutorial should never require editing this
-file.
+``source/_shared/gallery/sphinx_gallery_config.py``. Adding a tutorial should never require editing
+this file.
"""
from __future__ import annotations
@@ -13,12 +13,13 @@
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent
-sys.path.insert(0, str(REPO_ROOT))
+# Infrastructure (templates, static assets, shared build code, data) lives under source/; put it on
+# the path so ``import _shared`` resolves. Build scripts stay in scripts/.
+sys.path.insert(0, str(REPO_ROOT / "source"))
sys.path.insert(0, str(REPO_ROOT / "scripts"))
-from models import Catalog # noqa: E402
-
from _shared.gallery.sphinx_gallery_config import build_gallery_conf # noqa: E402
+from models import Catalog # noqa: E402
CATALOG = Catalog.load()
@@ -52,12 +53,12 @@
"notfound.extension",
# nbsphinx (for the upstream hand-written .ipynb) is intentionally NOT enabled yet: it needs
# pandoc, and it collides with sphinx-gallery over the generated plot_*.ipynb download files.
- # Surfacing the upstream notebooks is a later task (see ROADMAP Phase 2), with pandoc in CI.
+ # Surfacing the upstream notebooks is a separate task, with pandoc in CI.
]
source_suffix = {".rst": "restructuredtext", ".md": "markdown"}
master_doc = "index"
-templates_path = ["_templates"]
+templates_path = ["source/_templates"]
exclude_patterns = [
"_build",
# NB: _gallery is sphinx-gallery's *generated source*, not build output -- it must stay included
@@ -68,10 +69,10 @@
"ROADMAP.md", # contributor/dev doc, not part of the rendered site
".github/**",
".pytest_cache/**",
- "_shared/templates/**",
+ "source/_shared/templates/**",
"docs/**", # contributor/dev notes, not part of the rendered gallery
"_generated/**", # include fragments for the homepage, not standalone pages
- "_data/**",
+ "source/_data/**",
# Upstream READMEs are provenance, not documentation -- they would otherwise be picked up as
# standalone pages that duplicate each tutorial's own README.
"**/README.upstream.md",
@@ -105,18 +106,21 @@
# right, search, light/dark toggle. Org-neutral and pairs natively with sphinx-design cards.
html_theme = "pydata_sphinx_theme"
html_title = "PyKale Tutorials"
-html_static_path = ["_static"]
+html_static_path = ["source/_static"]
html_css_files = ["custom.css"]
html_js_files = ["js/catalog.js", "js/feedback.js", "js/search.js"]
-# _static/pykale_logo.png is gitignored and sourced from _shared/assets/logos. Copy it now, at config
-# import, so it exists before Sphinx validates html_logo -- a bare checkout (as CI runs) otherwise
-# warns that the logo is missing, which is fatal under -W. Runs before builder-inited, where the
-# logo check has already fired.
+# source/_static/pykale_logo.png is gitignored and sourced from source/_shared/assets/logos. Copy it
+# now, at config import, so it exists before Sphinx validates html_logo -- a bare checkout (as CI
+# runs) otherwise warns that the logo is missing, which is fatal under -W. Runs before builder-inited,
+# where the logo check has already fired.
import shutil as _shutil # noqa: E402
-_shutil.copyfile(REPO_ROOT / "_shared" / "assets" / "logos" / "pykale_logo.png", REPO_ROOT / "_static" / "pykale_logo.png")
+_shutil.copyfile(
+ REPO_ROOT / "source" / "_shared" / "assets" / "logos" / "pykale_logo.png",
+ REPO_ROOT / "source" / "_static" / "pykale_logo.png",
+)
-html_logo = "_static/pykale_logo.png"
+html_logo = "source/_static/pykale_logo.png"
# favicon handled by sphinx-favicon (see `favicons` below), not html_favicon.
from sidebar_context import sidebar_context # noqa: E402
@@ -135,7 +139,8 @@
html_theme_options = {
# Navbar order: logo, version chip, then the dropdown nav (left). Go-to-PyKale + utility icons
- # sit on the right. navbar-nav / navbar-version / navbar-goto are custom templates in _templates/.
+ # sit on the right. navbar-nav / navbar-version / navbar-goto are custom templates in
+ # source/_templates/.
"navbar_start": ["navbar-logo", "navbar-version"],
"navbar_center": ["navbar-nav"],
"navbar_end": ["navbar-goto", "theme-switcher", "navbar-icon-links"],
@@ -193,7 +198,7 @@
# The homepage and the standalone catalogue drop the *left* sidebar (they are landing pages, not
# docs). The homepage keeps a curated *right* sidebar (secondary_sidebar_items above).
-_LANDING_PAGES = {"index", "all-tutorials"}
+_LANDING_PAGES = {"index", "pages/all-tutorials"}
html_sidebars = {page: [] for page in _LANDING_PAGES}
@@ -260,7 +265,7 @@ def _ensure_static_catalog(app):
from export_landing_page import build_payload
- out = REPO_ROOT / "_static" / "tutorials.json"
+ out = REPO_ROOT / "source" / "_static" / "tutorials.json"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(build_payload(), indent=2) + "\n", encoding="utf-8")
@@ -290,9 +295,15 @@ def setup(app):
# sphinx-notfound-page: absolute URLs on the 404 so links work from any depth.
notfound_urls_prefix = "/tutorials/"
-# sphinx-reredirects: old-path -> new-path map for URLs that move. Empty until a page is renamed;
-# having the extension wired now means a future rename is a one-line entry, not a broken link.
-redirects: dict[str, str] = {}
+# sphinx-reredirects: old-path -> new-path map for URLs that move, so existing links do not 404.
+# Content pages moved from the site root into pages/; keep their old URLs alive.
+redirects: dict[str, str] = {
+ "all-tutorials": "pages/all-tutorials.html",
+ "getting-started": "pages/getting-started.html",
+ "learning-paths": "pages/learning-paths.html",
+ "tutorial-status": "pages/tutorial-status.html",
+ "about": "pages/about.html",
+}
# Mermaid diagrams render client-side; no local binary needed for the HTML build.
mermaid_version = "11.4.1"
diff --git a/index.rst b/index.rst
index 53b6ac0..3c2bb64 100644
--- a/index.rst
+++ b/index.rst
@@ -13,8 +13,8 @@ Learn with PyKale
interdisciplinary AI — organised by the domain that owns them.
@@ -61,7 +61,7 @@ Get started
Domain adaptation on digit images: the recommended starting point.
.. grid-item-card:: 3 · Explore a domain
- :link: all-tutorials
+ :link: pages/all-tutorials
:link-type: doc
Browse by Protein, Cancer, Cardiac, Materials or Others.
@@ -119,10 +119,10 @@ Contributing
:link: CONTRIBUTING
:link-type: doc
- Start from the templates in ``_shared/templates/``.
+ Start from the templates in ``source/_shared/templates/``.
.. grid-item-card:: Improve one
- :link: tutorial-status
+ :link: pages/tutorial-status
:link-type: doc
See which narratives are still drafts and pick one to write.
@@ -141,11 +141,11 @@ Contributing
.. toctree::
:hidden:
- getting-started
- all-tutorials
- learning-paths
- tutorial-status
- about
+ pages/getting-started
+ pages/all-tutorials
+ pages/learning-paths
+ pages/tutorial-status
+ pages/about
.. toctree::
:caption: Domains
diff --git a/make.bat b/make.bat
index ee3a929..29c78eb 100644
--- a/make.bat
+++ b/make.bat
@@ -27,7 +27,7 @@ goto end
:help
echo validate check catalog, metadata, schema and thumbnails agree
echo schema regenerate the metadata JSON schema
-echo export regenerate the landing-page + _static tutorials.json
+echo export regenerate the landing-page + source/_static tutorials.json
echo pages regenerate the per-domain pages
echo thumbnails regenerate the placeholder SVG thumbnails
echo homepage regenerate the homepage/list-page partials
diff --git a/others/action_recognition/README.md b/others/action_recognition/README.md
index 392e451..c337ebf 100644
--- a/others/action_recognition/README.md
+++ b/others/action_recognition/README.md
@@ -2,7 +2,7 @@
Transfer an action recogniser between egocentric video domains. Same adversarial adaptation pipeline as the digits tutorial, with a 3D-CNN feature extractor and video data loaders.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_action_recognition.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/action_recognition/plot_action_recognition.py b/others/action_recognition/plot_action_recognition.py
index 37d135d..0d5bee9 100644
--- a/others/action_recognition/plot_action_recognition.py
+++ b/others/action_recognition/plot_action_recognition.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/others/audio_visual_digit_classification/README.md b/others/audio_visual_digit_classification/README.md
index 6763dbf..ef990a0 100644
--- a/others/audio_visual_digit_classification/README.md
+++ b/others/audio_visual_digit_classification/README.md
@@ -2,7 +2,7 @@
Classify spoken-and-written digits from paired audio and image inputs, comparing late fusion, low-rank tensor fusion and bimodal interaction fusion on AVMNIST.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_audio_visual_digit_classification.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/audio_visual_digit_classification/plot_audio_visual_digit_classification.py b/others/audio_visual_digit_classification/plot_audio_visual_digit_classification.py
index 3e1e4fd..ac51a81 100644
--- a/others/audio_visual_digit_classification/plot_audio_visual_digit_classification.py
+++ b/others/audio_visual_digit_classification/plot_audio_visual_digit_classification.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/others/fewshot_image_classification/README.md b/others/fewshot_image_classification/README.md
index b6b0b73..74d646c 100644
--- a/others/fewshot_image_classification/README.md
+++ b/others/fewshot_image_classification/README.md
@@ -2,7 +2,7 @@
Prototypical Networks under N-way K-shot settings. Learn an embedding where class prototypes are the mean of their support samples, then classify query samples by distance to those prototypes.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_fewshot_image_classification.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/fewshot_image_classification/plot_fewshot_image_classification.py b/others/fewshot_image_classification/plot_fewshot_image_classification.py
index 2f9c7ac..ae2b504 100644
--- a/others/fewshot_image_classification/plot_fewshot_image_classification.py
+++ b/others/fewshot_image_classification/plot_fewshot_image_classification.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/others/image_classification/cifar_cnn_transformer/README.md b/others/image_classification/cifar_cnn_transformer/README.md
index 1c63999..b04ec83 100644
--- a/others/image_classification/cifar_cnn_transformer/README.md
+++ b/others/image_classification/cifar_cnn_transformer/README.md
@@ -2,7 +2,7 @@
Train an 8-layer CNN on CIFAR-10 as a baseline, then stack a Transformer encoder on the same backbone and compare validation accuracy across four Transformer sizes.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_cifar_cnn_transformer.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/image_classification/cifar_cnn_transformer/plot_cifar_cnn_transformer.py b/others/image_classification/cifar_cnn_transformer/plot_cifar_cnn_transformer.py
index 3599317..baf3e43 100644
--- a/others/image_classification/cifar_cnn_transformer/plot_cifar_cnn_transformer.py
+++ b/others/image_classification/cifar_cnn_transformer/plot_cifar_cnn_transformer.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/others/image_classification/cifar_isonet/README.md b/others/image_classification/cifar_isonet/README.md
index 9dc1855..4f9f573 100644
--- a/others/image_classification/cifar_isonet/README.md
+++ b/others/image_classification/cifar_isonet/README.md
@@ -2,7 +2,7 @@
Deep isometric learning on CIFAR-10/100. ISONet trains very deep networks without residual connections or batch normalisation by keeping the layer-wise transform close to an isometry.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_cifar_isonet.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/image_classification/cifar_isonet/plot_cifar_isonet.py b/others/image_classification/cifar_isonet/plot_cifar_isonet.py
index 5feb870..242ae53 100644
--- a/others/image_classification/cifar_isonet/plot_cifar_isonet.py
+++ b/others/image_classification/cifar_isonet/plot_cifar_isonet.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/others/image_classification/digit_domain_adaptation/requirements.txt b/others/image_classification/digit_domain_adaptation/requirements.txt
index 14128db..c2c7065 100644
--- a/others/image_classification/digit_domain_adaptation/requirements.txt
+++ b/others/image_classification/digit_domain_adaptation/requirements.txt
@@ -1,12 +1,12 @@
# Extra dependencies for this tutorial, on top of the repo core (see pyproject.toml).
# Install with: pip install -r requirements.txt
-matplotlib
# This tutorial imports kale.loaddata.image_access (for DigitDataset), which pulls in pykale's image
# stack at import time. Those live in pykale's optional `image` extra (setup.py image_requires), not
# its core install_requires, so a plain `pip install pykale` omits them -- list them here. Equivalent
# to installing pykale[image]; keep in sync if that extra changes upstream.
glob2
+matplotlib
pydicom
pylibjpeg
python-gdcm
diff --git a/others/image_classification/office_multisource_adaptation/README.md b/others/image_classification/office_multisource_adaptation/README.md
index 8781bee..5ead904 100644
--- a/others/image_classification/office_multisource_adaptation/README.md
+++ b/others/image_classification/office_multisource_adaptation/README.md
@@ -2,7 +2,7 @@
Adapt from three labelled source domains to one unlabelled target using moment matching (M3SDA) and multiple feature-space alignment (MFSAN), on Office-Caltech and digits.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_office_multisource_adaptation.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/image_classification/office_multisource_adaptation/plot_office_multisource_adaptation.py b/others/image_classification/office_multisource_adaptation/plot_office_multisource_adaptation.py
index c908e49..ba73574 100644
--- a/others/image_classification/office_multisource_adaptation/plot_office_multisource_adaptation.py
+++ b/others/image_classification/office_multisource_adaptation/plot_office_multisource_adaptation.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/others/multisite_neuroimaging_classification/README.md b/others/multisite_neuroimaging_classification/README.md
index cc413c1..d31f944 100644
--- a/others/multisite_neuroimaging_classification/README.md
+++ b/others/multisite_neuroimaging_classification/README.md
@@ -2,7 +2,7 @@
Autism detection from resting-state fMRI pooled across four ABIDE acquisition sites, using CoIRLS to regularise away site-driven covariate shift.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_multisite_neuroimaging_classification.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/multisite_neuroimaging_classification/plot_multisite_neuroimaging_classification.py b/others/multisite_neuroimaging_classification/plot_multisite_neuroimaging_classification.py
index d1dc9b7..85a5775 100644
--- a/others/multisite_neuroimaging_classification/plot_multisite_neuroimaging_classification.py
+++ b/others/multisite_neuroimaging_classification/plot_multisite_neuroimaging_classification.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/others/synthetic_domain_adaptation/README.md b/others/synthetic_domain_adaptation/README.md
index 6caf617..1ff2bac 100644
--- a/others/synthetic_domain_adaptation/README.md
+++ b/others/synthetic_domain_adaptation/README.md
@@ -2,7 +2,7 @@
The smallest possible domain adaptation example. Two blob-generated domains, two classes, and a CoIRLS classifier -- runs in seconds and makes the covariate-shift problem visible in one plot.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_synthetic_domain_adaptation.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/synthetic_domain_adaptation/plot_synthetic_domain_adaptation.py b/others/synthetic_domain_adaptation/plot_synthetic_domain_adaptation.py
index cf8bd2c..697e755 100644
--- a/others/synthetic_domain_adaptation/plot_synthetic_domain_adaptation.py
+++ b/others/synthetic_domain_adaptation/plot_synthetic_domain_adaptation.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/others/video_data_loading/README.md b/others/video_data_loading/README.md
index 6024c22..4fdb99b 100644
--- a/others/video_data_loading/README.md
+++ b/others/video_data_loading/README.md
@@ -2,7 +2,7 @@
Load, sample and augment video datasets with VideoFrameDataset. Sparse temporal sampling keeps memory flat regardless of clip length, and the loader plugs straight into torch DataLoader.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_video_data_loading.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/others/video_data_loading/plot_video_data_loading.py b/others/video_data_loading/plot_video_data_loading.py
index af98cc3..f91aa64 100644
--- a/others/video_data_loading/plot_video_data_loading.py
+++ b/others/video_data_loading/plot_video_data_loading.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/about.rst b/pages/about.rst
similarity index 94%
rename from about.rst
rename to pages/about.rst
index c739cbd..c6b47fe 100644
--- a/about.rst
+++ b/pages/about.rst
@@ -23,5 +23,5 @@ and downloads, themed with the `PyData Sphinx Theme `_ for what is planned.
diff --git a/all-tutorials.rst b/pages/all-tutorials.rst
similarity index 89%
rename from all-tutorials.rst
rename to pages/all-tutorials.rst
index 3977a62..5e496b7 100644
--- a/all-tutorials.rst
+++ b/pages/all-tutorials.rst
@@ -8,7 +8,7 @@ count and URL update as you go, so a filtered view can be shared.
.. raw:: html
-
+
diff --git a/getting-started.rst b/pages/getting-started.rst
similarity index 88%
rename from getting-started.rst
rename to pages/getting-started.rst
index c025812..6da57b2 100644
--- a/getting-started.rst
+++ b/pages/getting-started.rst
@@ -37,31 +37,31 @@ Tutorials are filed by the domain that owns them:
:gutter: 2
.. grid-item-card:: Protein
- :link: _pages/protein
+ :link: /_pages/protein
:link-type: doc
Drug-target binding and polypharmacy side effects.
.. grid-item-card:: Cancer
- :link: _pages/cancer
+ :link: /_pages/cancer
:link-type: doc
Multiomics integration for cancer classification.
.. grid-item-card:: Cardiac
- :link: _pages/cardiac
+ :link: /_pages/cardiac
:link-type: doc
Cardiac imaging and uncertainty estimation.
.. grid-item-card:: Others
- :link: _pages/others
+ :link: /_pages/others
:link-type: doc
Method demonstrations: recognition, domain adaptation, few-shot learning.
.. grid-item-card:: Materials
- :link: _pages/materials
+ :link: /_pages/materials
:link-type: doc
Reserved — tutorials in development.
@@ -71,4 +71,4 @@ Adapt an example to your own data
Every tutorial ships its complete ``main.py`` and config. Fork the folder, point
``DATASET.ROOT`` (or the equivalent config field) at your data, and adjust the model. See
-:doc:`CONTRIBUTING` for conventions, and :doc:`all-tutorials` to browse everything.
+:doc:`/CONTRIBUTING` for conventions, and :doc:`/pages/all-tutorials` to browse everything.
diff --git a/learning-paths.rst b/pages/learning-paths.rst
similarity index 87%
rename from learning-paths.rst
rename to pages/learning-paths.rst
index e19675b..829fa7f 100644
--- a/learning-paths.rst
+++ b/pages/learning-paths.rst
@@ -5,4 +5,4 @@ Curated routes through the tutorials. Each path is an ordered sequence — follo
jump to whichever step you need. Paths are defined in ``tutorial_catalog.yml`` and stay in sync with
the catalogue.
-.. include:: _generated/paths.rst
+.. include:: /_generated/paths.rst
diff --git a/tutorial-status.rst b/pages/tutorial-status.rst
similarity index 83%
rename from tutorial-status.rst
rename to pages/tutorial-status.rst
index 8479997..7ada3dd 100644
--- a/tutorial-status.rst
+++ b/pages/tutorial-status.rst
@@ -5,6 +5,6 @@ Every tutorial's runnable example is complete. The *narrated* walkthrough (``plo
written for some and still a draft for others — drafts render from source and are clearly labelled.
This table is generated from the catalogue.
-.. include:: _generated/status_table.rst
+.. include:: /_generated/status_table.rst
-Want to help? Pick a **Draft** row and write its narrative — see :doc:`CONTRIBUTING`.
+Want to help? Pick a **Draft** row and write its narrative — see :doc:`/CONTRIBUTING`.
diff --git a/protein/drug_target_affinity_prediction/README.md b/protein/drug_target_affinity_prediction/README.md
index a2ed88f..ca33ee2 100644
--- a/protein/drug_target_affinity_prediction/README.md
+++ b/protein/drug_target_affinity_prediction/README.md
@@ -2,7 +2,7 @@
Predict binding affinity between compounds and protein targets by encoding SMILES strings and amino-acid sequences with parallel CNNs, on three BindingDB subsets (Kd, IC50, Ki).
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_drug_target_affinity_prediction.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/protein/drug_target_affinity_prediction/plot_drug_target_affinity_prediction.py b/protein/drug_target_affinity_prediction/plot_drug_target_affinity_prediction.py
index ed793b5..459c50a 100644
--- a/protein/drug_target_affinity_prediction/plot_drug_target_affinity_prediction.py
+++ b/protein/drug_target_affinity_prediction/plot_drug_target_affinity_prediction.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/protein/drug_target_interaction_prediction/README.md b/protein/drug_target_interaction_prediction/README.md
index 3ed1db1..13174be 100644
--- a/protein/drug_target_interaction_prediction/README.md
+++ b/protein/drug_target_interaction_prediction/README.md
@@ -2,7 +2,7 @@
An interpretable bilinear attention network over drug molecular graphs and protein sequences, with optional domain adaptation for cross-domain generalisation on BindingDB and BioSNAP.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_drug_target_interaction_prediction.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/protein/drug_target_interaction_prediction/plot_drug_target_interaction_prediction.py b/protein/drug_target_interaction_prediction/plot_drug_target_interaction_prediction.py
index f64cb9d..035a9f8 100644
--- a/protein/drug_target_interaction_prediction/plot_drug_target_interaction_prediction.py
+++ b/protein/drug_target_interaction_prediction/plot_drug_target_interaction_prediction.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/protein/polypharmacy_side_effect_prediction/README.md b/protein/polypharmacy_side_effect_prediction/README.md
index e7d404b..49c2eea 100644
--- a/protein/polypharmacy_side_effect_prediction/README.md
+++ b/protein/polypharmacy_side_effect_prediction/README.md
@@ -2,7 +2,7 @@
Link prediction on a protein-drug knowledge graph. GripNet propagates messages along a task-specific path over a supergraph to predict side effects caused by drug combinations.
-> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
+> **Note:** the narrated `plot_polypharmacy_side_effect_prediction.py` in this folder is still a stub. The example below
> is complete and runnable.
## 1. Description
diff --git a/protein/polypharmacy_side_effect_prediction/plot_polypharmacy_side_effect_prediction.py b/protein/polypharmacy_side_effect_prediction/plot_polypharmacy_side_effect_prediction.py
index 5e490a2..7c25eac 100644
--- a/protein/polypharmacy_side_effect_prediction/plot_polypharmacy_side_effect_prediction.py
+++ b/protein/polypharmacy_side_effect_prediction/plot_polypharmacy_side_effect_prediction.py
@@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
-# ``_shared/templates/tutorial.py.template``.
+# ``source/_shared/templates/plot.py.template``.
print("See main.py in this folder for the runnable example.")
diff --git a/pyproject.toml b/pyproject.toml
index a6c40bf..0e3b6c0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -84,10 +84,10 @@ known_first_party = ["kale"]
python_version = "3.10"
ignore_missing_imports = true
# Only the build scripts are type-checked. Tutorial code mirrors upstream examples verbatim.
-files = ["scripts", "_shared"]
+files = ["scripts", "source/_shared"]
[tool.pytest.ini_options]
testpaths = ["tests"]
-# scripts/ holds the modules under test; _shared/ is imported by the gallery-config tests.
-pythonpath = ["scripts", "_shared"]
+# scripts/ holds the modules under test; source/_shared/ is imported by the gallery-config tests.
+pythonpath = ["scripts", "source/_shared"]
addopts = "-ra"
diff --git a/scripts/export_landing_page.py b/scripts/export_landing_page.py
index f685143..c494658 100644
--- a/scripts/export_landing_page.py
+++ b/scripts/export_landing_page.py
@@ -16,8 +16,9 @@
import yaml
from models import REPO_ROOT, Catalog, TutorialMetadata
+from site_meta import NOTEBOOKS_PUBLISHED
-sys.path.insert(0, str(REPO_ROOT / "_shared"))
+sys.path.insert(0, str(REPO_ROOT / "source" / "_shared"))
from launch.binder import binder_url # noqa: E402
from launch.colab import colab_url # noqa: E402
@@ -55,8 +56,8 @@ def _tutorial_entry(relative_path: str) -> Dict[str, Any]:
"links": {
"docs": f"{DOCS_BASE}/{metadata.domain.value}/{metadata.slug}.html",
"source": f"https://github.com/pykale/tutorials/tree/main/{relative_path}",
- "colab": colab_url(relative_path) if metadata.colab else None,
- "binder": binder_url(relative_path) if metadata.binder else None,
+ "colab": colab_url(relative_path) if NOTEBOOKS_PUBLISHED and metadata.colab else None,
+ "binder": binder_url(relative_path) if NOTEBOOKS_PUBLISHED and metadata.binder else None,
},
}
return entry
@@ -124,9 +125,9 @@ def main() -> int:
args.output.write_text(payload, encoding="utf-8")
print(f"wrote {args.output} ({len(payload.splitlines())} lines)")
- # The site's own faceted catalogue reads this from _static so the build is self-contained; the
- # landing-page copy is the separate downstream consumer.
- static_copy = REPO_ROOT / "_static" / "tutorials.json"
+ # The site's own faceted catalogue reads this from source/_static so the build is self-contained;
+ # the landing-page copy is the separate downstream consumer.
+ static_copy = REPO_ROOT / "source" / "_static" / "tutorials.json"
static_copy.write_text(payload, encoding="utf-8")
print(f"wrote {static_copy}")
return 0
diff --git a/scripts/generate_bucket_pages.py b/scripts/generate_bucket_pages.py
index f2aefb4..6e4212b 100644
--- a/scripts/generate_bucket_pages.py
+++ b/scripts/generate_bucket_pages.py
@@ -44,9 +44,7 @@ def build() -> int:
for bucket in catalog.buckets:
paths = bucket.iter_relative_paths()
count = len(paths)
- count_line = (
- "Tutorials in development." if count == 0 else f"{count} tutorial{'s' if count != 1 else ''}."
- )
+ count_line = "Tutorials in development." if count == 0 else f"{count} tutorial{'s' if count != 1 else ''}."
lines = [
f"{bucket.label}",
"=" * len(bucket.label),
@@ -81,7 +79,7 @@ def build() -> int:
f" .. grid-item-card:: {metadata.title}",
f" :link: {_gallery_doc(path)}",
" :link-type: doc",
- f" :img-top: /_static/thumbnails/{metadata.slug}.svg",
+ f" :img-top: /source/_static/thumbnails/{metadata.slug}.svg",
f" :class-card: pky-tut-card pky-{bucket.id.value}",
"",
f" {status}",
diff --git a/scripts/generate_homepage.py b/scripts/generate_homepage.py
index fe28fea..e808729 100644
--- a/scripts/generate_homepage.py
+++ b/scripts/generate_homepage.py
@@ -5,7 +5,7 @@
generated here into ``_generated/*.rst`` and pulled in with ``.. include::``, so:
* domain cards, counts and learning paths never drift from ``tutorial_catalog.yml``;
-* announcements live in ``_data/news.yml``, not buried in ``index.rst``.
+* announcements live in ``source/_data/news.yml``, not buried in ``index.rst``.
``_generated/`` is gitignored and excluded from the Sphinx source tree (the files are include
fragments, not standalone pages).
@@ -24,7 +24,7 @@
from models import REPO_ROOT, Catalog, TutorialMetadata
GENERATED_DIR = REPO_ROOT / "_generated"
-NEWS_FILE = REPO_ROOT / "_data" / "news.yml"
+NEWS_FILE = REPO_ROOT / "source" / "_data" / "news.yml"
# Each news badge gets a distinct sphinx-design colour role (styled in custom.css): New = green,
# Updated = blue, Planned = amber. Unknown badges fall back to a neutral secondary.
BADGE_ROLE = {"New": "bdg-success", "Updated": "bdg-info", "Planned": "bdg-warning"}
@@ -75,7 +75,7 @@ def _domains_rst() -> str:
f" .. grid-item-card:: {bucket.label}",
f" :link: _pages/{bucket.id.value}",
" :link-type: doc",
- f" :img-top: /_static/thumbnails/domain_{bucket.id.value}.svg",
+ f" :img-top: /source/_static/thumbnails/domain_{bucket.id.value}.svg",
f" :class-card: pky-domain-card pky-{bucket.id.value}",
"",
f" {status}",
diff --git a/scripts/generate_schema.py b/scripts/generate_schema.py
index 053d4cb..836cec3 100644
--- a/scripts/generate_schema.py
+++ b/scripts/generate_schema.py
@@ -1,4 +1,4 @@
-"""Emit ``_shared/metadata/tutorial.schema.json`` from the pydantic model.
+"""Emit ``source/_shared/metadata/tutorial.schema.json`` from the pydantic model.
The schema is generated, never hand-edited, so editors validating ``metadata.yml`` and
``validate_catalog.py`` can never drift apart. ``--check`` fails if the committed file is stale,
diff --git a/scripts/generate_thumbnails.py b/scripts/generate_thumbnails.py
index 4a29b34..2ed609d 100644
--- a/scripts/generate_thumbnails.py
+++ b/scripts/generate_thumbnails.py
@@ -7,8 +7,8 @@
Deterministic: the same metadata always yields byte-identical SVG (no timestamps, no randomness), so
the files are reproducible and a ``--check`` mode can verify them in CI. Output goes to
-``_static/thumbnails/`` (gitignored); the real sphinx-gallery figure supersedes the placeholder once a
-tutorial is written and executed (see :func:`resolve_thumbnail`).
+``source/_static/thumbnails/`` (gitignored); the real sphinx-gallery figure supersedes the placeholder
+once a tutorial is written and executed (see :func:`resolve_thumbnail`).
python scripts/generate_thumbnails.py # write all
python scripts/generate_thumbnails.py --check # fail if any is stale/missing
@@ -24,7 +24,7 @@
import yaml
from models import REPO_ROOT, Catalog, TutorialMetadata
-THUMBNAIL_DIR = REPO_ROOT / "_static" / "thumbnails"
+THUMBNAIL_DIR = REPO_ROOT / "source" / "_static" / "thumbnails"
WIDTH, HEIGHT = 400, 260
# Abstract domain symbols. Each returns SVG markup drawn in `accent`, centred around (cx, cy). Kept
@@ -61,8 +61,7 @@ def _sym_layers(accent: str) -> str: # Cancer: stacked multi-omics layers
for i, dy in enumerate((110, 135, 160)):
op = 0.35 + i * 0.25
out.append(
- f''
+ f''
)
return "".join(out)
@@ -131,9 +130,7 @@ def _escape(text: str) -> str:
return text.replace("&", "&").replace("<", "<").replace(">", ">")
-def thumbnail_svg(
- title: str, accent: str, symbol: str, *, badge: Optional[str] = None, bare: bool = False
-) -> str:
+def thumbnail_svg(title: str, accent: str, symbol: str, *, badge: Optional[str] = None, bare: bool = False) -> str:
"""Build a deterministic branded placeholder SVG.
Args:
@@ -174,7 +171,7 @@ def thumbnail_svg(
return (
f'