From 6a13210c51ca74a7f54aa9d4e9167dc32dde049d Mon Sep 17 00:00:00 2001 From: Robert Raposa Date: Tue, 2 Jun 2026 17:11:10 -0400 Subject: [PATCH 1/3] =?UTF-8?q?docs:=20reorganize=20toctree=20=E2=80=94=20?= =?UTF-8?q?merge=20how-tos/TinyMCE=20into=20Concepts,=20move=20app=20docs?= =?UTF-8?q?=20into=20References?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename "Concepts and Guides" section to "Concepts and \"How-To\" Guides" - Move how-tos (celery, use_the_api) and TinyMCE plugins into Concepts toctree (flat) - Delete docs/how-tos/ directory; add redirects.txt entries for moved pages - Move generated app index and app ADR index into docs/references/ as sibling items ("docs - App index", "docs - App ADR index") alongside the existing "docs - tree" - Update conf.py and repository_docs.py output paths and titles accordingly - Update docs/index.rst hidden toctree and grid cards to match new structure Co-Authored-By: Claude Sonnet 4.6 --- docs/{how-tos => concepts}/celery.rst | 0 docs/concepts/index.rst | 7 +- docs/{how-tos => concepts}/use_the_api.rst | 0 docs/conf.py | 6 +- docs/decisions/index.rst | 1 - docs/decisions/toctree-reorganization-plan.md | 154 ++++++++++++++++++ docs/how-tos/index.rst | 8 - docs/index.rst | 23 +-- docs/redirects.txt | 2 + docs/repository_docs.py | 31 ++-- 10 files changed, 183 insertions(+), 49 deletions(-) rename docs/{how-tos => concepts}/celery.rst (100%) rename docs/{how-tos => concepts}/use_the_api.rst (100%) create mode 100644 docs/decisions/toctree-reorganization-plan.md delete mode 100644 docs/how-tos/index.rst diff --git a/docs/how-tos/celery.rst b/docs/concepts/celery.rst similarity index 100% rename from docs/how-tos/celery.rst rename to docs/concepts/celery.rst diff --git a/docs/concepts/index.rst b/docs/concepts/index.rst index fa4a02125ffa..d89283610218 100644 --- a/docs/concepts/index.rst +++ b/docs/concepts/index.rst @@ -1,5 +1,5 @@ -Concepts and Guides -################### +Concepts and "How-To" Guides +############################ .. toctree:: :maxdepth: 2 @@ -11,3 +11,6 @@ Concepts and Guides frontend/bootstrap frontend/static_assets rest_apis + ../extensions/tinymce_plugins + celery + use_the_api diff --git a/docs/how-tos/use_the_api.rst b/docs/concepts/use_the_api.rst similarity index 100% rename from docs/how-tos/use_the_api.rst rename to docs/concepts/use_the_api.rst diff --git a/docs/conf.py b/docs/conf.py index 65dee72cf4c4..6f1c397b6179 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -339,9 +339,9 @@ def on_init(app): # pylint: disable=redefined-outer-name, unused-argument """ repo_docs_build_path = f'{root}/docs/references/docs' repo_docs = RepositoryDocs(root, repo_docs_build_path) - repo_docs.build_rst_docs() - repo_docs.build_apps_index(root / 'docs' / 'apps' / 'index.rst') - repo_docs.build_decisions_index(root / 'docs' / 'decisions' / 'app_decisions.rst') + repo_docs.build_rst_docs(root_title='docs - tree') + repo_docs.build_apps_index(root / 'docs' / 'references' / 'app_index.rst') + repo_docs.build_decisions_index(root / 'docs' / 'references' / 'app_adr_index.rst') docs_path = root / 'docs' apidoc_path = 'sphinx-apidoc' diff --git a/docs/decisions/index.rst b/docs/decisions/index.rst index e6bc0a14a70e..beed26a342a9 100644 --- a/docs/decisions/index.rst +++ b/docs/decisions/index.rst @@ -2,4 +2,3 @@ .. toctree:: 0* - app_decisions diff --git a/docs/decisions/toctree-reorganization-plan.md b/docs/decisions/toctree-reorganization-plan.md new file mode 100644 index 000000000000..307e20ee11a8 --- /dev/null +++ b/docs/decisions/toctree-reorganization-plan.md @@ -0,0 +1,154 @@ +# Plan: Re-organize ReadTheDocs Tree + +## Before / After (sidebar toctree) + +``` +BEFORE AFTER +--------------------------------- --------------------------------- +"How-To" Guides <- top References + How to write a celery task ...existing items... + How To Use the REST API docs - App ADR index <- moved, renamed +References docs - App index <- moved, renamed + ...existing items... docs - tree <- title changed + docs <- auto-generated Python Docstrings + Python Docstrings Concepts and "How-To" Guides <- renamed +Concepts and Guides <- top extension_points + extension_points testing/testing + testing/testing frontend/* + frontend/* rest_apis + rest_apis TinyMCE Plugins <- moved +TinyMCE (Visual Text/HTML How to write a celery task <- moved + Editor) Plugins <- top How To Use the REST API <- moved +App-Level Architecture + Decision Records <- top (no children) +App-Level Documentation <- top (no children) +``` + +(Full TinyMCE title: "TinyMCE (Visual Text/HTML Editor) Plugins") + +Note: `decisions/index.rst` has no valid RST title (`# edx-platform Technical +Decisions` lacks an underline), so Sphinx drops its toctree children entirely. +The numbered decisions (0000-0026) are not in the sidebar. `app_decisions` appears +because `docs/index.rst` also references it directly via an inline `:doc:` link in +the "App Documentation" grid card. Fixing `decisions/index.rst` is out of scope here. + +--- + +## Context + +The docs tree has two structural issues to fix: +1. How-To Guides and TinyMCE plugins are top-level sections, but belong under Concepts. +2. App-Level Documentation and App-Level ADRs are top-level items, but belong under References. + +Generated files are produced during the Sphinx `builder-inited` hook (`docs/conf.py` -> +`on_init()` -> `docs/repository_docs.py`). They must not be edited statically. + +--- + +## Changes + +### 1. `docs/repository_docs.py` + +**a. `_create_index_rst_file()`** -- add optional `title` parameter so the root +`docs/references/docs/index.rst` can have a custom title instead of just "docs": +```python +def _create_index_rst_file(self, directory_path, title=None): + ... + if title is None: + title = directory_name + file_content = f"""{title}\n{len(title) * '='}\n\n...""" +``` + +**b. `build_rst_docs()`** -- accept and forward `root_title`: +```python +def build_rst_docs(self, root_title=None): + os.makedirs(self.build_path, exist_ok=True) + self._create_index_rst_file(self.build_path, title=root_title) + ... +``` + +**c. `build_apps_index()`** -- output moves to `docs/references/` directly. Update: +- Title: `"docs - App index"` +- `:doc:` links: relative from `docs/references/` -> `docs/{rel}/index` + +**d. `build_decisions_index()`** -- same path move. Update: +- Title: `"docs - App ADR index"` +- Intro sentence: `'Links to per-app Architecture Decision Records (ADR) directories, supplementing the top-level'` +- Cross-ref to repo-wide decisions: `../decisions/0001-courses-in-lms` +- `:doc:` links: relative from `docs/references/` -> `docs/{rel_from_root}/index` + +### 2. `docs/conf.py` (`on_init`) + +```python +repo_docs.build_rst_docs(root_title='docs - tree') +repo_docs.build_apps_index(root / 'docs' / 'references' / 'app_index.rst') +repo_docs.build_decisions_index(root / 'docs' / 'references' / 'app_adr_index.rst') +``` +Old paths `docs/apps/` and `docs/decisions/app_decisions.rst` are removed. + +No changes needed to `docs/references/index.rst` -- its existing `*` glob picks up +the new files automatically; `docs/index` and `docstrings/index` are already there. + +### 3. Move how-to files; delete `docs/how-tos/` + +Physically move: +- `docs/how-tos/celery.rst` -> `docs/concepts/celery.rst` +- `docs/how-tos/use_the_api.rst` -> `docs/concepts/use_the_api.rst` + +Delete `docs/how-tos/` entirely (its `index.rst` was a bare glob toctree with no content). + +### 4. `docs/concepts/index.rst` + +- Rename title: `Concepts and Guides` -> `Concepts and "How-To" Guides` +- Add to toctree (flat -- no subsection wrapper): + ``` + ../extensions/tinymce_plugins + celery + use_the_api + ``` + +### 5. `docs/decisions/index.rst` + +Remove `app_decisions` from the toctree (it is no longer generated at that path). + +### 6. `docs/index.rst` + +**Hidden toctree** -- remove these entries (now included via other sections): +- `how-tos/index` (now under concepts) +- `extensions/tinymce_plugins` (now under concepts) +- `apps/index` (no longer generated at this path) + +**Grid cards** (independent of toctree navigation, but `:doc:` links inside them are +validated by Sphinx and must point to existing files): +- Remove **"How-tos"** card (merged into Concepts). +- Rename **"Concepts"** card -> **"Concepts and How-To Guides"**. +- Remove the `:doc:` line for `extensions/tinymce_plugins` from **"Hooks and Extensions"** (external link stays). +- **"App Documentation"** card stays, but update its `:doc:` references to new paths: + - `:doc:`decisions/app_decisions`` -> `:doc:`references/app_adr_index`` + - `:doc:`apps/index`` -> `:doc:`references/app_index`` + - `button-ref:: apps/index` -> `button-ref:: references/app_index` + +### 7. `docs/redirects.txt` + +The repo uses `sphinxext.rediraffe` for internal page moves (see existing entries). +Add two entries for the moved how-tos: + +``` +"how-tos/celery.rst" "concepts/celery.rst" +"how-tos/use_the_api.rst" "concepts/use_the_api.rst" +``` + +(`redirects` dict in `conf.py` is for external URL redirects only.) + +--- + +## Verification + +``` +cd docs && make html +``` +- "Concepts and How-To Guides" section contains TinyMCE and the how-to pages (flat). +- References sidebar shows `docs - App ADR index`, `docs - App index`, `docs - tree` + as sibling items alongside the existing references. +- No orphan-document Sphinx warnings. +- "App Documentation" grid card on the homepage links correctly to the new paths. diff --git a/docs/how-tos/index.rst b/docs/how-tos/index.rst deleted file mode 100644 index cb85be6be4b7..000000000000 --- a/docs/how-tos/index.rst +++ /dev/null @@ -1,8 +0,0 @@ -"How-To" Guides -############### - - -.. toctree:: - :glob: - - * diff --git a/docs/index.rst b/docs/index.rst index edec6a2f4fda..c42d1ad00062 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,28 +30,14 @@ locations. .. toctree:: :hidden: - how-tos/index references/index concepts/index - extensions/tinymce_plugins decisions/index - apps/index .. grid:: 1 2 2 2 :gutter: 3 :padding: 0 - .. grid-item-card:: How-tos - :class-card: sd-shadow-md sd-p-2 - :class-footer: sd-border-0 - - * :doc:`how-tos/celery` - +++ - .. button-ref:: how-tos/index - :color: primary - :outline: - :expand: - .. grid-item-card:: References :class-card: sd-shadow-md sd-p-2 :class-footer: sd-border-0 @@ -65,7 +51,7 @@ locations. :outline: :expand: - .. grid-item-card:: Concepts + .. grid-item-card:: Concepts and How-To Guides :class-card: sd-shadow-md sd-p-2 :class-footer: sd-border-0 @@ -83,7 +69,6 @@ locations. :class-footer: sd-border-0 * `Hooks Extensions Framework`_ - * :doc:`extensions/tinymce_plugins` +++ .. button-link:: https://docs.openedx.org/en/latest/developers/concepts/hooks_extension_framework.html :color: primary @@ -96,10 +81,10 @@ locations. :class-card: sd-shadow-md sd-p-2 :class-footer: sd-border-0 - * :doc:`apps/index` - * :doc:`decisions/app_decisions` + * :doc:`references/app_index` + * :doc:`references/app_adr_index` +++ - .. button-ref:: apps/index + .. button-ref:: references/app_index :color: primary :outline: :expand: diff --git a/docs/redirects.txt b/docs/redirects.txt index 140b518c1d8a..b5d757236d79 100644 --- a/docs/redirects.txt +++ b/docs/redirects.txt @@ -1,2 +1,4 @@ "featuretoggles.rst" "references/featuretoggles.rst" "settings.rst" "references/settings.rst" +"how-tos/celery.rst" "concepts/celery.rst" +"how-tos/use_the_api.rst" "concepts/use_the_api.rst" diff --git a/docs/repository_docs.py b/docs/repository_docs.py index 52861878c8f5..fcb61f866e11 100644 --- a/docs/repository_docs.py +++ b/docs/repository_docs.py @@ -34,9 +34,9 @@ def __init__( self.patterns_to_exclude_dirs = patterns_to_exclude_dirs self.patterns_to_exclude_files = patterns_to_exclude_files - def build_rst_docs(self): + def build_rst_docs(self, root_title=None): os.makedirs(self.build_path, exist_ok=True) - self._create_index_rst_file(self.build_path) + self._create_index_rst_file(self.build_path, title=root_title) rst_files = self._find_rst_files() self._copy_files(rst_files) @@ -62,13 +62,15 @@ def _get_directories_list_on_path(self, path): path = os.path.dirname(path) return directory_paths - def _create_index_rst_file(self, directory_path): + def _create_index_rst_file(self, directory_path, title=None): directory_name = os.path.basename(directory_path) file_path = os.path.join(directory_path, "index.rst") if os.path.exists(file_path): return - file_content = f"""{directory_name} -{len(directory_name) * '='} + if title is None: + title = directory_name + file_content = f"""{title} +{len(title) * '='} .. toctree:: :glob: @@ -113,8 +115,8 @@ def build_apps_index(self, output_path): Written to output_path (overwritten on each build). """ lines = [ - 'App-Level Documentation', - '=======================', + 'docs - App index', + '================', '', 'Quick-scan index of Django apps that have README files or documentation.', 'Each link opens the auto-generated index for that app.', @@ -134,7 +136,6 @@ def build_apps_index(self, output_path): has_readme = os.path.isfile(os.path.join(app_path, 'README.rst')) has_docs = os.path.isdir(os.path.join(app_path, 'docs')) if has_readme or has_docs: - # Path relative to docs/references/docs/ (where the generated tree lives) rel = f'{service_dir}/{app_name}' app_entries.append((app_name, rel)) @@ -144,7 +145,7 @@ def build_apps_index(self, output_path): heading = service_dir lines += [heading, '-' * len(heading), ''] for app_name, rel in app_entries: - lines.append(f'* :doc:`{app_name} <../references/docs/{rel}/index>`') + lines.append(f'* :doc:`{app_name} `') lines.append('') os.makedirs(os.path.dirname(output_path), exist_ok=True) @@ -178,11 +179,11 @@ def build_decisions_index(self, output_path): decisions_by_service[service_dir] = sorted(entries) lines = [ - 'App-Level Architecture Decision Records', - '========================================', + 'docs - App ADR index', + '====================', '', - 'Links to per-app ADR directories, supplementing the top-level', - ':doc:`repo-wide decisions <0001-courses-in-lms>`.', + 'Links to per-app Architecture Decision Records (ADR) directories, supplementing the top-level', + ':doc:`repo-wide decisions <../decisions/0001-courses-in-lms>`.', '', ] @@ -190,9 +191,7 @@ def build_decisions_index(self, output_path): heading = service_dir lines += [heading, '-' * len(heading), ''] for label, rel_from_root in entries: - # Path relative to docs/decisions/ (where this file lives) - link = f'../references/docs/{rel_from_root}/index' - # Normalise to forward slashes + link = f'docs/{rel_from_root}/index' link = link.replace(os.sep, '/') lines.append(f'* :doc:`{label} <{link}>`') lines.append('') From 0235a483222a4b3af39ef47d9435c2ccb7ba3564 Mon Sep 17 00:00:00 2001 From: Robert Raposa Date: Tue, 2 Jun 2026 17:42:04 -0400 Subject: [PATCH 2/3] fix: overwrite root index.rst when a custom title is given The early-return guard in _create_index_rst_file skipped writing when the file already existed, so incremental builds kept a stale title. Now the guard only applies when using the default (directory-name) title; an explicit title always overwrites so the generated heading stays in sync. Co-Authored-By: Claude Sonnet 4.6 --- docs/repository_docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/repository_docs.py b/docs/repository_docs.py index fcb61f866e11..7b8ae2887261 100644 --- a/docs/repository_docs.py +++ b/docs/repository_docs.py @@ -65,7 +65,7 @@ def _get_directories_list_on_path(self, path): def _create_index_rst_file(self, directory_path, title=None): directory_name = os.path.basename(directory_path) file_path = os.path.join(directory_path, "index.rst") - if os.path.exists(file_path): + if os.path.exists(file_path) and title is None: return if title is None: title = directory_name From e53e0bce82ac2e5889ba2450ccf8f6d79ed0e518 Mon Sep 17 00:00:00 2001 From: Robert Raposa Date: Tue, 2 Jun 2026 17:42:18 -0400 Subject: [PATCH 3/3] docs: restore comment explaining :doc: path relativity in build_apps_index Co-Authored-By: Claude Sonnet 4.6 --- docs/repository_docs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/repository_docs.py b/docs/repository_docs.py index 7b8ae2887261..9d673d14a804 100644 --- a/docs/repository_docs.py +++ b/docs/repository_docs.py @@ -136,6 +136,7 @@ def build_apps_index(self, output_path): has_readme = os.path.isfile(os.path.join(app_path, 'README.rst')) has_docs = os.path.isdir(os.path.join(app_path, 'docs')) if has_readme or has_docs: + # :doc: links below are relative to docs/references/ (where this file is generated) rel = f'{service_dir}/{app_name}' app_entries.append((app_name, rel))