aec0407b87
Convert Ext/Help/ru.html to Markdown and push to a wiki git repository.
200 lines
6.5 KiB
Python
200 lines
6.5 KiB
Python
"""Build markdown wiki pages from discovered Help HTML."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import shutil
|
||
from dataclasses import dataclass
|
||
from datetime import datetime, timezone
|
||
from pathlib import Path
|
||
|
||
from export1c_help.convert import default_link_rewrite, extract_h1, html_to_markdown
|
||
from export1c_help.discover import HelpPage, build_link_title_map, discover_help_pages
|
||
from export1c_help.naming import assign_titles
|
||
from export1c_help.types_map import DIR_TO_RU
|
||
from export1c_help.__version__ import __version__
|
||
|
||
|
||
@dataclass
|
||
class ExportStats:
|
||
pages_total: int = 0
|
||
pages_written: int = 0
|
||
pages_skipped_empty: int = 0
|
||
images_copied: int = 0
|
||
|
||
|
||
def _is_effectively_empty(md: str) -> bool:
|
||
plain = re_sub_md(md)
|
||
return len(plain) < 20
|
||
|
||
|
||
def re_sub_md(md: str) -> str:
|
||
import re
|
||
|
||
t = re.sub(r"\[\[([^\]|]+)(?:\|[^\]]+)?\]\]", r"\1", md)
|
||
t = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", t)
|
||
t = re.sub(r"[#*_`|-]", " ", t)
|
||
t = re.sub(r"\s+", " ", t).strip()
|
||
return t
|
||
|
||
|
||
def build_home(pages: list[HelpPage], titles: dict[str, str], *, source_label: str) -> str:
|
||
by_type: dict[str, list[HelpPage]] = {}
|
||
for p in pages:
|
||
by_type.setdefault(p.type_dir, []).append(p)
|
||
|
||
lines = [
|
||
f"# Справка конфигурации",
|
||
"",
|
||
f"Автогенерация из встроенной справки 1С (`Ext/Help/ru.html`).",
|
||
"",
|
||
f"- Источник: `{source_label}`",
|
||
f"- Страниц: **{len(pages)}**",
|
||
f"- Инструмент: `export_1c_help` v{__version__}",
|
||
f"- Дата: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}",
|
||
"",
|
||
"## Разделы",
|
||
"",
|
||
]
|
||
for type_dir in sorted(by_type, key=lambda d: DIR_TO_RU.get(d, d)):
|
||
label = DIR_TO_RU.get(type_dir, type_dir)
|
||
group = sorted(by_type[type_dir], key=lambda p: titles[p.meta_key].casefold())
|
||
lines.append(f"### {label} ({len(group)})")
|
||
lines.append("")
|
||
for p in group:
|
||
title = titles[p.meta_key]
|
||
lines.append(f"- [[{title}]]")
|
||
lines.append("")
|
||
return "\n".join(lines).rstrip() + "\n"
|
||
|
||
|
||
def build_sidebar(pages: list[HelpPage], titles: dict[str, str]) -> str:
|
||
by_type: dict[str, list[HelpPage]] = {}
|
||
for p in pages:
|
||
by_type.setdefault(p.type_dir, []).append(p)
|
||
lines = ["# Справка", "", "[[Home|Оглавление]]", ""]
|
||
for type_dir in sorted(by_type, key=lambda d: DIR_TO_RU.get(d, d)):
|
||
label = DIR_TO_RU.get(type_dir, type_dir)
|
||
lines.append(f"### {label}")
|
||
lines.append("")
|
||
# only object-level in sidebar to keep it usable
|
||
objs = [p for p in by_type[type_dir] if not p.is_form]
|
||
objs.sort(key=lambda p: titles[p.meta_key].casefold())
|
||
for p in objs[:80]:
|
||
lines.append(f"- [[{titles[p.meta_key]}]]")
|
||
if len(objs) > 80:
|
||
lines.append(f"- … ещё {len(objs) - 80} (см. [[Home]])")
|
||
lines.append("")
|
||
return "\n".join(lines).rstrip() + "\n"
|
||
|
||
|
||
def export_help(
|
||
src_root: Path,
|
||
out_dir: Path,
|
||
*,
|
||
include_forms: bool = True,
|
||
skip_empty: bool = True,
|
||
clean: bool = False,
|
||
source_label: str | None = None,
|
||
) -> ExportStats:
|
||
"""Convert all help pages into a wiki working tree at out_dir."""
|
||
src_root = src_root.resolve()
|
||
out_dir = out_dir.resolve()
|
||
stats = ExportStats()
|
||
|
||
pages = discover_help_pages(src_root, include_forms=include_forms)
|
||
stats.pages_total = len(pages)
|
||
|
||
h1_by_meta: dict[str, str | None] = {}
|
||
html_cache: dict[str, str] = {}
|
||
for p in pages:
|
||
html = p.path.read_text(encoding="utf-8-sig", errors="ignore")
|
||
html_cache[p.meta_key] = html
|
||
h1_by_meta[p.meta_key] = extract_h1(html)
|
||
|
||
titles, files = assign_titles(pages, h1_by_meta)
|
||
link_map = build_link_title_map(pages, titles)
|
||
|
||
if clean and out_dir.exists():
|
||
for child in out_dir.iterdir():
|
||
if child.name == ".git":
|
||
continue
|
||
if child.is_dir():
|
||
shutil.rmtree(child)
|
||
else:
|
||
child.unlink()
|
||
out_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
def rewrite(href: str, text: str) -> str:
|
||
return default_link_rewrite(href, text, link_map)
|
||
|
||
manifest_pages: list[dict] = []
|
||
|
||
for p in pages:
|
||
html = html_cache[p.meta_key]
|
||
title = titles[p.meta_key]
|
||
md = html_to_markdown(html, rewrite_link=rewrite, strip_first_h1=title)
|
||
if skip_empty and _is_effectively_empty(md):
|
||
stats.pages_skipped_empty += 1
|
||
continue
|
||
|
||
# footer
|
||
md = (
|
||
md.rstrip()
|
||
+ "\n\n---\n\n"
|
||
+ f"*Мета: `{p.meta_key}` · файл: `{p.rel_posix}`*\n"
|
||
)
|
||
|
||
fname = files[p.meta_key]
|
||
(out_dir / fname).write_text(md, encoding="utf-8")
|
||
stats.pages_written += 1
|
||
|
||
# copy sibling images
|
||
help_dir = p.path.parent
|
||
for img in help_dir.iterdir():
|
||
if img.suffix.lower() in {".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp"}:
|
||
target = out_dir / img.name
|
||
if not target.exists():
|
||
shutil.copy2(img, target)
|
||
stats.images_copied += 1
|
||
|
||
manifest_pages.append(
|
||
{
|
||
"meta_key": p.meta_key,
|
||
"title": title,
|
||
"filename": fname,
|
||
"type_dir": p.type_dir,
|
||
"object": p.object_name,
|
||
"form": p.form_name,
|
||
"source": p.rel_posix,
|
||
}
|
||
)
|
||
|
||
label = source_label or str(src_root)
|
||
written_keys = {m["meta_key"] for m in manifest_pages}
|
||
written_pages = [p for p in pages if p.meta_key in written_keys]
|
||
(out_dir / "Home.md").write_text(
|
||
build_home(written_pages, titles, source_label=label),
|
||
encoding="utf-8",
|
||
)
|
||
(out_dir / "_Sidebar.md").write_text(
|
||
build_sidebar(written_pages, titles),
|
||
encoding="utf-8",
|
||
)
|
||
|
||
manifest = {
|
||
"tool": "export_1c_help",
|
||
"version": __version__,
|
||
"source": label,
|
||
"exported_at": datetime.now(timezone.utc).isoformat(),
|
||
"pages_total": stats.pages_total,
|
||
"pages_written": stats.pages_written,
|
||
"pages_skipped_empty": stats.pages_skipped_empty,
|
||
"pages": manifest_pages,
|
||
}
|
||
(out_dir / "manifest.json").write_text(
|
||
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
|
||
encoding="utf-8",
|
||
)
|
||
return stats
|