Initial release: export 1C Help to Gitea wiki.
Convert Ext/Help/ru.html to Markdown and push to a wiki git repository.
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
"""Discover Ext/Help/ru.html pages in a 1C configuration dump."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from export1c_help.types_map import DIR_TO_LINK_TYPE
|
||||
|
||||
_SYN_RE = re.compile(
|
||||
r"<Synonym>\s*<v8:item>\s*<v8:lang>ru</v8:lang>\s*<v8:content>(.*?)</v8:content>",
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class HelpPage:
|
||||
"""One Help/ru.html page."""
|
||||
|
||||
path: Path
|
||||
src_root: Path
|
||||
type_dir: str
|
||||
object_name: str
|
||||
form_name: str | None
|
||||
meta_key: str
|
||||
synonym: str
|
||||
link_aliases: tuple[str, ...] = ()
|
||||
|
||||
@property
|
||||
def is_form(self) -> bool:
|
||||
return self.form_name is not None
|
||||
|
||||
@property
|
||||
def rel_posix(self) -> str:
|
||||
return self.path.relative_to(self.src_root).as_posix()
|
||||
|
||||
|
||||
def _xml_unescape(s: str) -> str:
|
||||
return (
|
||||
s.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace("&", "&")
|
||||
.replace(""", '"')
|
||||
.replace("'", "'")
|
||||
)
|
||||
|
||||
|
||||
def _read_ru_synonym(xml_path: Path) -> str | None:
|
||||
if not xml_path.is_file():
|
||||
return None
|
||||
try:
|
||||
text = xml_path.read_text(encoding="utf-8-sig", errors="ignore")
|
||||
except OSError:
|
||||
return None
|
||||
m = _SYN_RE.search(text)
|
||||
if m:
|
||||
return _xml_unescape(m.group(1).strip())
|
||||
try:
|
||||
root = ET.parse(xml_path).getroot()
|
||||
except ET.ParseError:
|
||||
return None
|
||||
for el in root.iter():
|
||||
if el.tag.endswith("content") and el.text and el.text.strip():
|
||||
return el.text.strip()
|
||||
return None
|
||||
|
||||
|
||||
def _subsystem_chain(parts: tuple[str, ...]) -> list[str] | None:
|
||||
"""
|
||||
From relative parts ending with Ext/Help/ru.html build subsystem name chain.
|
||||
|
||||
Subsystems/A/Ext/Help/ru.html → [A]
|
||||
Subsystems/A/Subsystems/B/Ext/Help/ru.html → [A, B]
|
||||
"""
|
||||
if parts[0] != "Subsystems":
|
||||
return None
|
||||
body = parts[1:-3] # drop Ext/Help/ru.html
|
||||
names: list[str] = []
|
||||
i = 0
|
||||
while i < len(body):
|
||||
if i == 0:
|
||||
names.append(body[i])
|
||||
i += 1
|
||||
continue
|
||||
if body[i] == "Subsystems" and i + 1 < len(body):
|
||||
names.append(body[i + 1])
|
||||
i += 2
|
||||
continue
|
||||
return None
|
||||
return names or None
|
||||
|
||||
|
||||
def _subsystem_xml(src_root: Path, chain: list[str]) -> Path:
|
||||
# Subsystems/A.xml or Subsystems/A/Subsystems/B.xml
|
||||
path = src_root / "Subsystems"
|
||||
for idx, name in enumerate(chain):
|
||||
if idx == 0:
|
||||
candidate = path / f"{name}.xml"
|
||||
path = path / name
|
||||
else:
|
||||
candidate = path / "Subsystems" / f"{name}.xml"
|
||||
path = path / "Subsystems" / name
|
||||
return candidate
|
||||
|
||||
|
||||
def _parse_help_path(src_root: Path, html_path: Path) -> HelpPage | None:
|
||||
try:
|
||||
rel = html_path.relative_to(src_root)
|
||||
except ValueError:
|
||||
return None
|
||||
parts = rel.parts
|
||||
if len(parts) < 4 or parts[-1] != "ru.html" or parts[-2] != "Help" or parts[-3] != "Ext":
|
||||
return None
|
||||
|
||||
type_dir = parts[0]
|
||||
link_type = DIR_TO_LINK_TYPE.get(type_dir)
|
||||
if not link_type:
|
||||
return None
|
||||
|
||||
form_name: str | None = None
|
||||
aliases: list[str] = []
|
||||
|
||||
if type_dir == "Subsystems":
|
||||
chain = _subsystem_chain(parts)
|
||||
if not chain:
|
||||
return None
|
||||
object_name = chain[-1]
|
||||
meta_key = "Subsystem." + ".Subsystem.".join(chain)
|
||||
# 1C help links usually use the leaf name only
|
||||
aliases.append(f"Subsystem.{object_name}")
|
||||
syn = _read_ru_synonym(_subsystem_xml(src_root, chain)) or object_name
|
||||
return HelpPage(
|
||||
path=html_path,
|
||||
src_root=src_root,
|
||||
type_dir=type_dir,
|
||||
object_name=object_name,
|
||||
form_name=None,
|
||||
meta_key=meta_key,
|
||||
synonym=syn,
|
||||
link_aliases=tuple(aliases),
|
||||
)
|
||||
|
||||
if type_dir == "CommonForms":
|
||||
object_name = parts[1]
|
||||
meta_key = f"CommonForm.{object_name}"
|
||||
syn = _read_ru_synonym(src_root / "CommonForms" / f"{object_name}.xml") or object_name
|
||||
return HelpPage(
|
||||
path=html_path,
|
||||
src_root=src_root,
|
||||
type_dir=type_dir,
|
||||
object_name=object_name,
|
||||
form_name=None,
|
||||
meta_key=meta_key,
|
||||
synonym=syn,
|
||||
)
|
||||
|
||||
if "Forms" in parts:
|
||||
try:
|
||||
forms_idx = parts.index("Forms")
|
||||
except ValueError:
|
||||
return None
|
||||
object_name = parts[1]
|
||||
form_name = parts[forms_idx + 1]
|
||||
meta_key = f"{link_type}.{object_name}.Form.{form_name}"
|
||||
form_xml = src_root / type_dir / object_name / "Forms" / f"{form_name}.xml"
|
||||
syn = _read_ru_synonym(form_xml) or form_name
|
||||
obj_syn = _read_ru_synonym(src_root / type_dir / f"{object_name}.xml")
|
||||
if obj_syn:
|
||||
syn = f"{obj_syn} ({syn})"
|
||||
return HelpPage(
|
||||
path=html_path,
|
||||
src_root=src_root,
|
||||
type_dir=type_dir,
|
||||
object_name=object_name,
|
||||
form_name=form_name,
|
||||
meta_key=meta_key,
|
||||
synonym=syn,
|
||||
)
|
||||
|
||||
object_name = parts[1]
|
||||
meta_key = f"{link_type}.{object_name}"
|
||||
syn = _read_ru_synonym(src_root / type_dir / f"{object_name}.xml") or object_name
|
||||
return HelpPage(
|
||||
path=html_path,
|
||||
src_root=src_root,
|
||||
type_dir=type_dir,
|
||||
object_name=object_name,
|
||||
form_name=None,
|
||||
meta_key=meta_key,
|
||||
synonym=syn,
|
||||
)
|
||||
|
||||
|
||||
def discover_help_pages(
|
||||
src_root: Path,
|
||||
*,
|
||||
include_forms: bool = True,
|
||||
) -> list[HelpPage]:
|
||||
"""Find all Help/ru.html under src_root."""
|
||||
src_root = src_root.resolve()
|
||||
pages: list[HelpPage] = []
|
||||
seen_meta: set[str] = set()
|
||||
for html in sorted(src_root.rglob("Ext/Help/ru.html")):
|
||||
page = _parse_help_path(src_root, html)
|
||||
if page is None:
|
||||
continue
|
||||
if page.is_form and not include_forms:
|
||||
continue
|
||||
if page.meta_key in seen_meta:
|
||||
# should not happen; keep first
|
||||
continue
|
||||
seen_meta.add(page.meta_key)
|
||||
pages.append(page)
|
||||
return pages
|
||||
|
||||
|
||||
def build_link_title_map(pages: list[HelpPage], titles: dict[str, str]) -> dict[str, str]:
|
||||
"""
|
||||
meta_key / alias → wiki title for href rewrite.
|
||||
|
||||
Leaf aliases (Subsystem.X) are registered only when unique.
|
||||
"""
|
||||
result: dict[str, str] = {}
|
||||
alias_owners: dict[str, list[str]] = {}
|
||||
for p in pages:
|
||||
result[p.meta_key] = titles[p.meta_key]
|
||||
for alias in p.link_aliases:
|
||||
alias_owners.setdefault(alias, []).append(p.meta_key)
|
||||
for alias, owners in alias_owners.items():
|
||||
if len(owners) == 1:
|
||||
result[alias] = titles[owners[0]]
|
||||
return result
|
||||
Reference in New Issue
Block a user