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. +[![Docs build](https://github.com/pykale/tutorials/actions/workflows/docs.yml/badge.svg)](https://github.com/pykale/tutorials/actions/workflows/docs.yml) +[![Live site](https://img.shields.io/website?url=https%3A%2F%2Fpykale.github.io%2Ftutorials%2F&label=tutorials%20site&up_message=online)](https://pykale.github.io/tutorials/) +[![PyKale on PyPI](https://img.shields.io/pypi/v/pykale?label=pykale)](https://pypi.org/project/pykale/) +[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)](https://www.python.org/) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](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 -
+