/
ArtKAtMan
/
arbat
Обзор
Документация
Войти
/
ArtKAtMan
/
arbat
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
scripts/fix-articles.py
155 строк
5 KB
Mavis (M3)
fix: article markdown — bold/FAQ rendering, wider container, related grid 4-col
10 июл 2026, 18:41
10 июл 2026, 18:41
a416f50
Код
Авторство
О чём код?
#!/usr/bin/env python3 """ One-shot fixer for arbat-seo articles. Problems to fix: 1. First # heading duplicates the h1 in hero. Remove if it matches the title. 2. ### H3. prefix on H3 subheadings — render artifact, remove it. 3. **bold** glued to surrounding text without spaces. Add space inside markers. 4. FAQ: each Q/A currently collapsed into one giant H2 line. Split on ** markers and add blank lines so markdown renders them as paragraphs/headings properly. Usage: python fix_articles.py """ import re from pathlib import Path CONTENT_DIR = Path('src/content/articles') # Step 1: split frontmatter and body FM_RE = re.compile(r'^---\s*\n(.*?)\n---\s*\n(.*)$', re.DOTALL) # Step 2: H3 prefix to remove H3_PREFIX = re.compile(r'^(#{3,6})\s*H\d+\.\s+', re.MULTILINE) # Step 3: bold that needs spaces inside # `**Word**` (no spaces) -> `** Word **` BOLD_NO_SPACES = re.compile(r'\*\*(\S[^*]{0,200}?)\*\*') # Step 4: a single H2 that has a big block of `**Q?** A. **Q2?** A2. ...` # We replace the H2 with proper structure: H2, then each Q+A on its own block. # Pattern: `## FAQ` followed by `**Q?** ... **Q2?** ...` all in one chunk. FAQ_RE = re.compile( r'^(##\s*FAQ\s*)(\*\*[^*]+\?\*\*[\s\S]+?)(?=^##\s|\Z)', re.MULTILINE, ) def split_frontmatter(text: str): m = FM_RE.match(text) if not m: return text, '' return m.group(1), m.group(2) def join_frontmatter(fm: str, body: str) -> str: if not fm: return body return f'---\n{fm}\n---\n\n{body}' def remove_first_h1(body: str, title: str) -> str: """Remove the first # heading if it matches the article title.""" lines = body.split('\n') out = [] removed = False title_norm = title.strip().lower() for line in lines: if not removed and line.startswith('# ') and not line.startswith('## '): heading = line[2:].strip().lower() if heading == title_norm or heading.startswith(title_norm[:40]): removed = True continue out.append(line) return '\n'.join(out) def fix_h3_prefix(body: str) -> str: return H3_PREFIX.sub(r'\1 ', body) def fix_bold_spaces(body: str) -> str: # **слово** (no surrounding spaces) -> ** слово ** def repl(m: re.Match) -> str: inner = m.group(1) # already has surrounding spaces? leave alone if inner.startswith(' ') or inner.endswith(' '): return m.group(0) return f'** {inner} **' # Run multiple times in case of nested patterns for _ in range(2): new = BOLD_NO_SPACES.sub(repl, body) if new == body: break body = new return body def fix_faq_block(body: str) -> str: """Replace the giant one-line H2-FAQ with a proper H2 + per-question block.""" def repl(m: re.Match) -> str: h2 = m.group(1).rstrip() # `## FAQ` or `## FAQ ` content = m.group(2).strip() # Split by `**...?**` markers # Each Q ends with `?` and the answer is the text up to the next ** parts = re.split(r'(\*\*[^*]+\?\*\*)', content) # parts: [pre, Q1, A1, Q2, A2, ...] out = [h2, ''] i = 1 while i < len(parts): q = parts[i].strip() a = parts[i + 1].strip() if i + 1 < len(parts) else '' out.append(q) if a: out.append('') out.append(a) out.append('') i += 2 return '\n'.join(out).rstrip() + '\n\n' return FAQ_RE.sub(repl, body) def process_file(path: Path) -> bool: raw = path.read_text(encoding='utf-8') fm, body = split_frontmatter(raw) if not fm: return False # extract title title_match = re.search(r'^title:\s*["\']?(.+?)["\']?\s*$', fm, re.MULTILINE) title = title_match.group(1) if title_match else '' original = body body = remove_first_h1(body, title) body = fix_h3_prefix(body) body = fix_bold_spaces(body) body = fix_faq_block(body) if body == original: return False new_text = join_frontmatter(fm, body) path.write_text(new_text, encoding='utf-8') return True def main(): files = sorted(CONTENT_DIR.glob('*.md')) changed = 0 for f in files: try: if process_file(f): changed += 1 print(f'fixed: {f.name}') else: print(f'unchanged: {f.name}') except Exception as e: print(f'ERROR {f.name}: {e}') print(f'\nTotal changed: {changed} / {len(files)}') if __name__ == '__main__': main()