From 355dd5754fc3df0675fabd347b534fa99f0154a2 Mon Sep 17 00:00:00 2001 From: Pedro Brochado Date: Thu, 18 Jan 2024 10:30:32 -0300 Subject: [PATCH] Refactor navigation module The purpose is to make it simpler to understand and modify the presentation layer. --- .vagrant/machines/default/libvirt/vagrant_cwd | 1 - .vagrant/rgloader/loader.rb | 9 - src/pulp_docs/mkdocs_macros.py | 2 +- src/pulp_docs/navigation.py | 226 +++++++++++++----- src/pulp_docs/repository.py | 14 +- 5 files changed, 169 insertions(+), 83 deletions(-) delete mode 100644 .vagrant/machines/default/libvirt/vagrant_cwd delete mode 100644 .vagrant/rgloader/loader.rb diff --git a/.vagrant/machines/default/libvirt/vagrant_cwd b/.vagrant/machines/default/libvirt/vagrant_cwd deleted file mode 100644 index 4b4445c..0000000 --- a/.vagrant/machines/default/libvirt/vagrant_cwd +++ /dev/null @@ -1 +0,0 @@ -/home/pbrochad/workspace/multirepo-prototype/pulp-docs \ No newline at end of file diff --git a/.vagrant/rgloader/loader.rb b/.vagrant/rgloader/loader.rb deleted file mode 100644 index c3c05b0..0000000 --- a/.vagrant/rgloader/loader.rb +++ /dev/null @@ -1,9 +0,0 @@ -# This file loads the proper rgloader/loader.rb file that comes packaged -# with Vagrant so that encoded files can properly run with Vagrant. - -if ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"] - require File.expand_path( - "rgloader/loader", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]) -else - raise "Encoded files can't be read outside of the Vagrant installer." -end diff --git a/src/pulp_docs/mkdocs_macros.py b/src/pulp_docs/mkdocs_macros.py index a43d853..bb80dfb 100644 --- a/src/pulp_docs/mkdocs_macros.py +++ b/src/pulp_docs/mkdocs_macros.py @@ -131,7 +131,7 @@ def prepare_repositories(TMPDIR: Path, repos: Repos, config: Config): with as_file(data_file_docs) as _docs: shutil.copytree(_docs, repo_docs / "pulp-docs") shutil.copy( - repo_sources / repos.core.name / SRC_DOCS_DIRNAME / "index.md", + repo_sources / repos.core_repo.name / SRC_DOCS_DIRNAME / "index.md", repo_docs / "index.md", ) diff --git a/src/pulp_docs/navigation.py b/src/pulp_docs/navigation.py index a6a00d8..119a711 100644 --- a/src/pulp_docs/navigation.py +++ b/src/pulp_docs/navigation.py @@ -1,3 +1,31 @@ +""" +The navigation generator module. + +Responsible for creating a specific structure out of the available content-types, personas +and repository types. This structure should be compatible with how it would be written +in mkdocs.yml. + +`get_navigation` is the entrypoint used for the final render. + +``` +# yml +- pageA: some-file.md +- some-other-file.md + +# python +[ + {"pageA": "/abs/path/to/some-file.md"}, + "/abs/path/to/some-other-file.md", +] +``` + +Note that you either specify: +1. {title: file.md} -> define title explicitly +2. "/path/to/file.md" -> uses markdown '# title' or the filename, if former isnt present. + +""" +from __future__ import annotations + import typing as t from pathlib import Path @@ -5,90 +33,65 @@ def get_navigation(tmpdir: Path, repos: Repos): - """The dynamic generated 'nav' section of mkdocs.yml""" + """ + The entrypoint for dynamic 'nav' generation for mkdocs. - # {repo}/docs/{persona}/{content-type}/*md - # {repo}/docs/reference/*md - def get_children(path: t.Union[str, Path]): - _path = tmpdir / path if isinstance(path, str) else path - result = [ - str(file.relative_to(tmpdir)) - for file in _path.glob("*.md") - if not file.name.startswith("_") - ] - return result + Replace by another generation function to change how content is structured. + """ + NAV_GENERATOR_FUNCTION = grouped_by_content_type + return NAV_GENERATOR_FUNCTION(tmpdir, repos) - def expand_repos(template_str: str): - _nav = {} - for repo in repos.content: - lookup_path = tmpdir / template_str.format(repo=repo.name) - _repo_content = get_children(lookup_path) - _nav[repo.title] = _repo_content - return _nav - def expand_reference(template_str: str): - _nav = {} - for repo in repos.all: - lookup_path = tmpdir / template_str.format(repo=repo.name) - _repo_content = get_children(lookup_path) - reference_section = [ - {"REST API": f"{repo.name}/docs/rest_api.md"}, - {"Readme": f"{repo.name}/README.md"}, - {"Code API": _repo_content}, - {"Changelog": f"{repo.name}/CHANGELOG.md"}, - ] - _nav[repo.title] = reference_section - return _nav +def grouped_by_content_type(tmpdir: Path, repos: Repos): + """ + A specific nav generator function. - def from_core(url: str): - corename = "pulpcore" - return f"{corename}/{url}" + Organizes content (roughly) with the pattern. + {content-type}/ + Overview/ + {persona}/ + {repos} + """ + f = AgregationUtils(tmpdir, repos) + # Aggregation and Grouping getting_started = [ - {"Overview": from_core("docs/sections/getting_started/index.md")}, + {"Overview": f.section_file("getting_started/index.md")}, + {"Quickstart": f.section_children("getting_started/quickstart")}, + {"Fundamentals": f.section_children("getting_started/fundamentals")}, + ] + guides = [ + {"Overview": f.section_file("guides/index.md")}, { - "Quickstart": get_children( - from_core("docs/sections/getting_started/quickstart") + "For Content-Management": f.repo_grouping( + "{repo}/docs/content-manager/guides" ) }, + {"For Sys-Admins": f.repo_grouping("{repo}/docs/sys-admin/guides")}, + ] + learn = [ + {"Overview": f.section_file("learn/index.md")}, { - "Fundamentals": get_children( - from_core("docs/sections/getting_started/fundamentals") + "For Content-Management": f.repo_grouping( + "{repo}/docs/content-manager/learn" ) }, - ] - guides = [ - {"Overview": from_core("docs/sections/guides/index.md")}, - {"For Content-Management": expand_repos("{repo}/docs/content-manager/guides")}, - {"For Sys-Admins": expand_repos("{repo}/docs/sys-admin/guides")}, - ] - learn = [ - {"Overview": from_core("docs/sections/learn/index.md")}, - {"For Content-Management": expand_repos("{repo}/docs/content-manager/learn")}, - {"For Sys-Admins": expand_repos("{repo}/docs/sys-admin/learn")}, + {"For Sys-Admins": f.repo_grouping("{repo}/docs/sys-admin/learn")}, ] reference = [ - {"Overview": from_core("docs/sections/reference/index.md")}, - {"Repository Map": from_core("docs/sections/reference/01-repository-map.md")}, - {"Glossary": from_core("docs/sections/reference/02-glossary.md")}, - {"Repositories": expand_reference("{repo}/docs/reference")}, + {"Overview": f.section_file("reference/index.md")}, + {"Repository Map": f.section_file("reference/01-repository-map.md")}, + {"Glossary": f.section_file("reference/02-glossary.md")}, + {"Repositories": f.repo_reference_grouping()}, ] development = [ - {"Overview": from_core("docs/sections/development/index.md")}, - { - "Quickstart": get_children( - from_core("docs/sections/development/quickstart/") - ) - }, - { - "Onboarding": get_children( - from_core("docs/sections/development/onboarding/") - ) - }, - {"Guides": get_children("core/docs/sections/development/guides/")}, + {"Overview": f.section_file("development/index.md")}, + {"Quickstart": f.section_children("development/quickstart/")}, + {"Onboarding": f.section_children("development/onboarding/")}, + {"Guides": f.section_children("/development/guides/")}, ] - # main navigation + # Main Section navigation = [ {"Home": "index.md"}, {"Getting Started": getting_started}, @@ -98,3 +101,94 @@ def from_core(url: str): {"Development": development}, ] return navigation + + +class AgregationUtils: + def __init__(self, tmpdir: Path, repos: Repos): + self.tmpdir = tmpdir + self.repos = repos + + def get_children(self, path: t.Union[str, Path]) -> list[str]: + """ + Get all markdown files contained in @path non recursively. + + Excludes files which startswith "_". + """ + _path = self.tmpdir / path if isinstance(path, str) else path + result = [ + str(file.relative_to(self.tmpdir)) + for file in _path.glob("*.md") + if not file.name.startswith("_") + ] + return result + + def repo_grouping(self, template_str: str): + """ + Get all markdown files that matches @template_str basepath and group by repos. + + The @template_str should contain: + {repo} - use 'content' and 'other' repo types + {content-repo} - use 'content' repos only + {other-repo} - use 'other' repos only + + Example: + >>> expand_repos("{repo}/docs/dev/guides") + { + "repoA": ["docs/dev/guides/file1.md", "docs/dev/guides/file2.md"], + "repoB": ["docs/dev/guides/file3.md", "docs/dev/guides/file4.md"], + } + """ + _nav = {} + selected_repos = None + if "{repo}" in template_str: + selected_repos = self.repos.other_repos + self.repos.content_repos + elif "{content-repo}" in template_str: + selected_repos = self.repos.content_repos + elif "{other-repo}" in template_str: + selected_repos = self.repos.other_repos + else: + raise ValueError("Malformed template_str. See docstring for usage.") + + for repo in selected_repos: + lookup_path = self.tmpdir / template_str.format(repo=repo.name) + _repo_content = self.get_children(lookup_path) + _nav[repo.title] = _repo_content + return _nav + + def repo_reference_grouping(self): + """ + Create reference section by aggregating some specific files. + + Group according to the pattern: + {repo}/ + Readme.md + Rest Api.md + Code Api/ + Module A.md + Module B.md + Changelog.md + + """ + template_str = "{repo}/docs/reference" + _nav = {} + for repo in self.repos.all: + lookup_path = self.tmpdir / template_str.format(repo=repo.name) + _repo_content = self.get_children(lookup_path) + reference_section = [ + {"REST API": f"{repo.name}/docs/rest_api.md"}, + {"Readme": f"{repo.name}/README.md"}, + {"Code API": _repo_content}, + {"Changelog": f"{repo.name}/CHANGELOG.md"}, + ] + _nav[repo.title] = reference_section + return _nav + + def section_file(self, url: str): + """Get a markdown file from the website section folder.""" + basepath = "pulpcore/docs/sections" + return f"{basepath}/{url}" + + def section_children(self, url: str): + """Get children markdown files from the website section folder.""" + basepath = "pulpcore/docs/sections" + return self.get_children(f"{basepath}/{url}") diff --git a/src/pulp_docs/repository.py b/src/pulp_docs/repository.py index ea8b348..4ef9f33 100644 --- a/src/pulp_docs/repository.py +++ b/src/pulp_docs/repository.py @@ -180,9 +180,9 @@ def download_from_gh_latest(dest_dir: Path, owner: str, name: str): class Repos: """A collection of Repos""" - core: Repo - content: t.List[Repo] = field(default_factory=list) - other: t.List[Repo] = field(default_factory=list) + core_repo: Repo + content_repos: t.List[Repo] = field(default_factory=list) + other_repos: t.List[Repo] = field(default_factory=list) def update_local_checkouts(self): """Update repos to use local checkout, if exists in the parent dir of CWD""" @@ -199,7 +199,7 @@ def update_local_checkouts(self): @property def all(self): - return [self.core] + self.content + self.other + return [self.core_repo] + self.content_repos + self.other_repos @classmethod def from_yaml(cls, path: str): @@ -231,7 +231,9 @@ def from_yaml(cls, path: str): core_repo = Repo(**repos["core"][0], type="core") content_repos = [Repo(**repo, type="content") for repo in repos["content"]] other_repos = [Repo(**repo, type="other") for repo in repos["other"]] - return Repos(core=core_repo, content=content_repos, other=other_repos) + return Repos( + core_repo=core_repo, content_repos=content_repos, other_repos=other_repos + ) @classmethod def test_fixtures(cls): @@ -253,4 +255,4 @@ def test_fixtures(cls): ), Repo("Maven", "new_repo3", local_basepath=FIXTURE_WORKDIR, type="content"), ] - return Repos(core=DEFAULT_CORE, content=DEFAULT_CONTENT_REPOS) + return Repos(core_repo=DEFAULT_CORE, content_repos=DEFAULT_CONTENT_REPOS)