ci-server-engineer/tools/manual_diff.py
cliu af9a1bbe29 Full-text manual diff R1.03 -> R1.05 (catch unlisted R1.04 edits)
tools/manual_diff.py: word-level diff of one manual between two doc folders.
version-differences.md: the 20 manuals whose revision tables list only
contact information were diffed; E10 and E30 have real content changes,
the rest wording only. CLAUDE.md: re-checks use the full diff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:35:39 +11:00

71 lines
2.4 KiB
Python

"""Word-level diff of one CI Server manual between two releases (e.g. R1.03 and R1.05).
Catches changes the revision table does not mention (for example R1.04 edits listed only as
"Added contact information"). Needs `pdftotext` (Poppler / Git for Windows) on PATH.
Usage:
python tools/manual_diff.py <old_pdf_dir> <new_pdf_dir> IM36K10L40-01EN [more IMs] [-o out_dir]
Prints one block per change: [replace]/[insert]/[delete] with old (-) and new (+) words.
Page footers, edition lines and dates are removed first, so only text changes remain.
Writes nothing into the repo; with -o it saves <IM>.diff.txt files in out_dir.
"""
import difflib
import os
import re
import subprocess
import sys
import tempfile
NOISE = re.compile(
r'IM ?36K\w+-01EN( \d+\w\w Edition)?( : [A-Za-z]+\.? ?\d+, ?\d+-00)?'
r'|\d+(st|nd|rd|th) Edition|Toc-\d+|Rev-\d+|App\.\d+-\d+|\b\d+-\d+\b')
def words(pdf):
with tempfile.TemporaryDirectory() as d:
out = os.path.join(d, 'm.txt')
subprocess.run(['pdftotext', pdf, out], check=True)
text = open(out, encoding='utf-8', errors='replace').read()
return NOISE.sub(' ', text).split()
def diff(old_pdf, new_pdf):
a, b = words(old_pdf), words(new_pdf)
sm = difflib.SequenceMatcher(None, a, b, autojunk=False)
lines = []
for op, a1, a2, b1, b2 in sm.get_opcodes():
if op == 'equal':
continue
lines.append('[%s] at word %d\n - %s\n + %s' % (
op, a1, ' '.join(a[a1:a2]), ' '.join(b[b1:b2])))
return lines
def main(argv):
out_dir = None
if '-o' in argv:
i = argv.index('-o')
out_dir = argv[i + 1]
del argv[i:i + 2]
if len(argv) < 3:
sys.exit(__doc__)
old_dir, new_dir, ims = argv[0], argv[1], argv[2:]
for im in ims:
old_pdf = os.path.join(old_dir, im + '.pdf')
new_pdf = os.path.join(new_dir, im + '.pdf')
if not os.path.exists(old_pdf):
print('=====', im, ': new manual (no old edition)')
continue
lines = diff(old_pdf, new_pdf)
report = '\n'.join(lines)
print('=====', im, ':', len(lines), 'changes')
if out_dir:
os.makedirs(out_dir, exist_ok=True)
open(os.path.join(out_dir, im + '.diff.txt'), 'w', encoding='utf-8').write(report)
else:
print(report)
if __name__ == '__main__':
main(sys.argv[1:])