unification: SUMMARY → CANON v1 (Final counts, at-a-glance renamed, Authors—EN→RU generated for sec02/03 from dossiers, legacy blocks → Notes ###; sec04 totals row added; 0 rows lost)

This commit is contained in:
Dmitry Kokorin 2026-09-21 23:56:54 +03:00
parent a5d6b9a0d9
commit 680bf45ee1
6 changed files with 581 additions and 339 deletions

View file

@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Build `## Authors — EN → RU` table for a section SUMMARY from AUTHORS-EN.md + dossiers.
Usage: python3 tools/make_authors_table.py <section>"""
import re, sys, os
def ru_name_of(dossier):
if not os.path.exists(dossier):
return '—'
c = open(dossier, encoding='utf-8').read()
# bold field forms
for m in re.finditer(r'\*\*RU name[^:]*:\*\* (.+)', c):
return m.group(1).strip()
# section forms
m = re.search(r'^(?:## |\*\*)?RU name[^:*\n]*:?\*\*?\n(.+)', c, re.M)
if m:
return m.group(1).strip()
m = re.search(r'^## [^\n]*RU name[^\n]*\n+(.+)', c, re.M | re.I)
if m:
return m.group(1).strip()
return '—'
def main():
sec = sys.argv[1]
idx = f'sections/{sec}/AUTHORS-EN.md'
c = open(idx).read()
rows = []
for l in c.split('\n'):
if l.startswith('|') and not re.match(r'^\|\s*item', l) and not re.match(r'^\|[-\s|]+\|', l):
cells = [x.strip() for x in l.strip().strip('|').split('|')]
if len(cells) >= 3:
items, en, d = cells[0], cells[1], cells[2]
if d.startswith('..'):
d = os.path.normpath(os.path.join('sections', sec, d))
else:
d = d.lstrip('./')
rows.append((items, en, ru_name_of(d)))
out = ['| item(s) | EN author | RU name | verified via |',
'|---------|-----------|---------|--------------|']
for items, en, ru in rows:
out.append(f'| {items} | {en} | {ru} | dossier |')
print('\n'.join(out))
if __name__ == '__main__':
main()

View file

@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""Restructure section SUMMARY.md to CANON v1 (lossless: legacy blocks → ### under Notes).
Usage: python3 tools/restructure_summary.py <section> [--dry-run]"""
import re, sys, os, subprocess, glob
NAMES = {'01-fundamentals': 'Fundamentals', '02-dreams': 'Dreams',
'03-myths-fairy-tales': 'Myths & Fairy Tales', '04-pictures': 'Pictures'}
def main():
sec = sys.argv[1]
dry = '--dry-run' in sys.argv
p = f'sections/{sec}/SUMMARY.md'
c = open(p).read()
lines = c.split('\n')
h1 = lines[0]
# split into (heading, body) blocks; preface = lines before first ##
blocks, preface, cur = [], [], None
for l in lines[1:]:
if l.startswith('## '):
if cur:
blocks.append(cur)
cur = [l, []]
elif cur:
cur[1].append(l)
else:
preface.append(l)
if cur:
blocks.append(cur)
preface = '\n'.join(preface).strip()
glance = authors = None
rest = []
for h, body in blocks:
name = h[3:].strip()
b = '\n'.join(body).strip()
if 'at a glance' in name.lower():
glance = (name, b)
elif re.match(r'^Table 1 — Authors|^Authors', name):
authors = (name, b)
else:
rest.append((name, b))
if not authors:
gen = subprocess.run(['python3', 'tools/make_authors_table.py', sec],
capture_output=True, text=True).stdout.strip()
authors = ('Authors', gen)
if not glance:
print('ERROR: no at-a-glance table'); sys.exit(1)
# Final counts: from card statuses + manifest totals
marks = {'✅': 0, '🔶': 0, '❌': 0}
for cp in glob.glob(f'sections/{sec}/[0-9][0-9]-*.md'):
m = re.search(r'\*\*Status:\*\* (.)', open(cp).read())
if m and m.group(1) in marks:
marks[m.group(1)] += 1
nitems = sum(marks.values())
mtot = re.search(r'^## Totals\n\n(.+)', open(f'downloads/{sec}/MANIFEST.md').read(), re.M)
mtot = mtot.group(1).strip() if mtot else '—'
final = (f"**{sec[:2]} COMPLETE.** Final: {marks['✅']} ✅ / {marks['🔶']} 🔶 / {marks['❌']} ❌ "
f"({nitems} items) · Downloads: {mtot}")
gname = '## Publication status at a glance'
aname = '## Authors — EN → RU (how verified)'
out = f'# Section {sec[:2]} — {NAMES[sec]}: summary\n\n'
out += '## Final counts\n\n' + final + '\n\n'
out += gname + '\n\n' + glance[1] + '\n\n'
out += aname + '\n\n' + authors[1] + '\n\n'
if rest:
out += '## Notes\n'
for name, b in rest:
out += f'\n### {name}\n\n{b}\n'
txt = out.rstrip() + '\n'
if dry:
print(txt)
else:
open(p, 'w').write(txt)
print(f'restructured {p}: glance kept, authors {"" if re.search(r"Table 1 — Authors", "\n".join(h for h,_ in blocks)) else "GENERATED"}, {len(rest)} legacy blocks → Notes')
if __name__ == '__main__':
main()