feat: wikitool move - eine kb-Seite folgt ihrem Subtype ins berechnete Verzeichnis (#56)
Files changed: - CHANGES.md - VERSION - instructions/page-lifecycle.md - instructions/publish-cycle.md - tools/CONTRACT.md - tools/chemenu/cli.py - tools/chemenu/commands/log_append.py - tools/chemenu/commands/migrate_cmd.py - tools/chemenu/commands/new_page.py - tools/chemenu/commands/page_ops.py - tools/chemenu/corpus_diff.py - tools/chemenu/lint_core.py - tools/chemenu/tests/test_corpus_diff.py - tools/chemenu/tests/test_lint.py - tools/chemenu/tests/test_migrate_cmd.py - tools/chemenu/tests/test_page_ops.py - tools/chemenu/tests/test_type_resolver.py - tools/chemenu/type_resolver.py
This commit is contained in:
@@ -27,6 +27,7 @@ from chemenu.provenance import legacy_source_pages as find_legacy_source_pages
|
||||
from chemenu.provenance import orphan_footnote_defs as find_orphan_footnote_defs
|
||||
from chemenu.provenance import uncovered_raw_files as find_uncovered_raw_files
|
||||
from chemenu.provenance import undefined_footnote_refs as find_undefined_footnote_refs
|
||||
from chemenu.page import Page
|
||||
from chemenu.version import Version
|
||||
from chemenu.kb_scan import (
|
||||
GENERATED_INDEX,
|
||||
@@ -76,6 +77,52 @@ def count_quote_blocks(body: str) -> int:
|
||||
return count
|
||||
|
||||
|
||||
def _display(path: Path) -> str:
|
||||
"""A path for a report line: repo-root-relative if possible, the raw path
|
||||
otherwise (a fixture tree in a test, or any tree `config.ROOT` does not
|
||||
contain)."""
|
||||
try:
|
||||
return str(path.relative_to(config.ROOT))
|
||||
except ValueError:
|
||||
return str(path)
|
||||
|
||||
|
||||
def find_misplaced(pages: dict[str, Page]) -> list[tuple[str, Page, Path]]:
|
||||
"""(title, page, target_dir) for every page whose type resolves and whose
|
||||
current directory differs from the one `TypeResolver.compute_target_dir`
|
||||
would place it under - the same rule `new` places a page by. Shared by
|
||||
`misplaced_pages()` below and `wikitool move --reconcile`, which is the
|
||||
fix for what this finds.
|
||||
|
||||
A page with no `type:` or an unresolvable one is skipped - each is
|
||||
reported separately, as `frontmatter_errors`/`type_resolution_errors`."""
|
||||
found = []
|
||||
for title, page in sorted(pages.items()):
|
||||
type_path = page.frontmatter.get("type")
|
||||
if not type_path:
|
||||
continue
|
||||
try:
|
||||
target_dir = resolver.compute_target_dir(type_path, page.frontmatter, page.path)
|
||||
except ValueError:
|
||||
continue
|
||||
if page.path.parent.resolve() != target_dir.resolve():
|
||||
found.append((title, page, target_dir))
|
||||
return found
|
||||
|
||||
|
||||
def misplaced_pages(pages: dict[str, Page]) -> list[dict]:
|
||||
"""Report form of `find_misplaced`: `{"page", "at", "should_be"}` per
|
||||
finding, for the lint report.
|
||||
|
||||
Advisory rather than a hard error (see `HARD_ERROR_KEYS` below): plenty of
|
||||
existing instances predate `move`, and a hand-placed page is not a
|
||||
broken one - only one `wikitool move --page "<Title>"` would relocate."""
|
||||
return [
|
||||
{"page": title, "at": _display(page.path.parent), "should_be": _display(target_dir)}
|
||||
for title, page, target_dir in find_misplaced(pages)
|
||||
]
|
||||
|
||||
|
||||
def run_lint(kb_dir: Path) -> dict:
|
||||
pages = load_kb_pages(kb_dir)
|
||||
duplicate_titles = find_duplicate_title_paths(kb_dir, config.ROOT)
|
||||
@@ -139,6 +186,8 @@ def run_lint(kb_dir: Path) -> dict:
|
||||
if h1 is not None and h1 != title:
|
||||
title_mismatches.append({"page": title, "h1": h1})
|
||||
|
||||
misplaced = misplaced_pages(pages)
|
||||
|
||||
unmarked_provenance = []
|
||||
for title, page in sorted(pages.items()):
|
||||
if page.kind not in ("entity", "concept"):
|
||||
@@ -318,6 +367,7 @@ def run_lint(kb_dir: Path) -> dict:
|
||||
"dangling_index_entries": dangling_index_entries,
|
||||
"title_mismatches": title_mismatches,
|
||||
"duplicate_titles": duplicate_titles,
|
||||
"misplaced_pages": misplaced,
|
||||
"uncovered_raw_files": find_uncovered_raw_files(config.RAW_DIR, pages),
|
||||
"broken_raw_refs": find_broken_raw_refs(pages),
|
||||
"duplicate_raw_file_owners": find_duplicate_raw_file_owners(pages),
|
||||
@@ -381,6 +431,12 @@ def render_markdown(report: dict) -> str:
|
||||
lines, "Filename / H1 Title Mismatches", report["title_mismatches"],
|
||||
lambda i: f"[[{i['page']}]] H1 is '{i['h1']}'",
|
||||
)
|
||||
_section(
|
||||
lines, "Misplaced Pages (not under their type-spec's computed directory)",
|
||||
report.get("misplaced_pages", []),
|
||||
lambda i: f"[[{i['page']}]] is at `{i['at']}`, should be under `{i['should_be']}` "
|
||||
f"- `wikitool move --page \"{i['page']}\"`",
|
||||
)
|
||||
_section(
|
||||
lines, "Uncovered Raw Files (no source page)", report["uncovered_raw_files"],
|
||||
lambda i: f"`{i}`",
|
||||
@@ -529,6 +585,11 @@ def default_report_path(report: dict) -> Path:
|
||||
# is no version at which the redundancy becomes an error, only a sweep someone
|
||||
# does or does not get to.
|
||||
#
|
||||
# `misplaced_pages` is the same shape again: it arrived long after most
|
||||
# instances' corpora were hand-placed, a hand-placed page is not a broken one,
|
||||
# and there is no version at which "not under the computed directory" becomes
|
||||
# wrong - only `wikitool move` someone does or does not get to run.
|
||||
#
|
||||
# `malformed_edges` and `unbalanced_markers` are hard from the start: neither
|
||||
# describes an unconverted page, only a broken one.
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user