2072db4980
Inside a columns fenced block (verbatim, so Pandoc doesn't process Markdown), convert *italic* / **bold** / ***bold-italic*** to wikitext emphasis. Asterisk style, single line; precedence bold-italic > bold > italic. Raw inline HTML still works via the existing HTML-unescape path. Docs + example updated to use ** ** rather than <b>. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
88 lines
3.3 KiB
Python
88 lines
3.3 KiB
Python
"""Shared utilities for novoyuuparosk-auto-wiki pipelines."""
|
|
|
|
import html
|
|
import os
|
|
import re
|
|
import subprocess
|
|
from urllib.parse import urlparse
|
|
|
|
import mwclient
|
|
|
|
AUTO_BANNER_PREFIX = "{{Auto-generated"
|
|
|
|
# A ```columns fenced block is passed through Pandoc verbatim as
|
|
# <pre class="columns">…</pre>; columns within it are separated by a line of ===.
|
|
_COLUMNS_BLOCK_RE = re.compile(r'<pre class="columns">(.*?)</pre>', re.DOTALL)
|
|
_COLUMN_SEP_RE = re.compile(r"^\s*===\s*$", re.MULTILINE)
|
|
|
|
# Minimal Markdown emphasis → wikitext, for use inside a verbatim columns block
|
|
# (Pandoc doesn't process Markdown there). Asterisk style only, single line.
|
|
# Order matters: bold-italic (***) before bold (**) before italic (*).
|
|
_BOLD_ITALIC_RE = re.compile(r"\*\*\*(.+?)\*\*\*")
|
|
_BOLD_RE = re.compile(r"\*\*(.+?)\*\*")
|
|
_ITALIC_RE = re.compile(r"\*(.+?)\*")
|
|
|
|
|
|
def _md_emphasis_to_wikitext(text: str) -> str:
|
|
text = _BOLD_ITALIC_RE.sub(r"'''''\1'''''", text)
|
|
text = _BOLD_RE.sub(r"'''\1'''", text)
|
|
text = _ITALIC_RE.sub(r"''\1''", text)
|
|
return text
|
|
|
|
|
|
def strip_first_h1(text: str) -> str:
|
|
"""Remove the first '# Heading' line and any immediately following blank line."""
|
|
lines = text.split("\n")
|
|
for i, line in enumerate(lines):
|
|
if line.strip().startswith("# "):
|
|
del lines[i]
|
|
if i < len(lines) and lines[i].strip() == "":
|
|
del lines[i]
|
|
break
|
|
return "\n".join(lines)
|
|
|
|
|
|
def expand_columns(wikitext: str) -> str:
|
|
"""Expand ```columns fenced blocks into a flex row of <poem> columns.
|
|
|
|
Authors write a fenced code block tagged ``columns``; Pandoc passes its body
|
|
through verbatim as ``<pre class="columns">…</pre>`` (line breaks and blank
|
|
lines preserved, inline markup entity-escaped). Columns within the block are
|
|
separated by a line containing only ``===``. Each column is wrapped in
|
|
<poem> so its line breaks survive MediaWiki parsing. Content is
|
|
HTML-unescaped (so inline markup such as <b>…</b> renders rather than
|
|
appearing as literal text) and Markdown emphasis (``*italic*``, ``**bold**``,
|
|
``***bold-italic***``) is converted to wikitext, since Pandoc does not
|
|
process Markdown inside the verbatim block.
|
|
"""
|
|
|
|
def render(match: re.Match) -> str:
|
|
body = _md_emphasis_to_wikitext(html.unescape(match.group(1)))
|
|
columns = _COLUMN_SEP_RE.split(body)
|
|
poems = "".join("<poem>\n" + col.strip("\n") + "\n</poem>" for col in columns)
|
|
return '<div style="display:flex; gap:3em; align-items:flex-start">' + poems + "</div>"
|
|
|
|
return _COLUMNS_BLOCK_RE.sub(render, wikitext)
|
|
|
|
|
|
def markdown_to_wikitext(body: str) -> str:
|
|
result = subprocess.run(
|
|
["pandoc", "-f", "markdown", "-t", "mediawiki"],
|
|
input=body,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if result.returncode != 0:
|
|
raise RuntimeError(f"pandoc failed: {result.stderr.strip()}")
|
|
return expand_columns(result.stdout)
|
|
|
|
|
|
def connect_wiki() -> mwclient.Site:
|
|
api_url = os.environ["WIKI_API_URL"]
|
|
parsed = urlparse(api_url)
|
|
host = parsed.netloc
|
|
path = parsed.path[: parsed.path.rfind("/") + 1] or "/"
|
|
site = mwclient.Site(host, path=path, scheme=parsed.scheme)
|
|
site.login(os.environ["WIKI_BOT_USER"], os.environ["WIKI_BOT_PASSWORD"])
|
|
return site
|