2.5 KiB
2.5 KiB
add_clause after_search Fails After tracked_replace — Use Direct lxml Insertion
Problem (2026-07-01 凤雅幼儿园劳务派遣协议)
After calling ed.tracked_replace(old, new) on multiple paragraphs, subsequent ed.add_clause(text, after_search="...") calls silently fail — the new paragraph doesn't appear in the output. The function returns without error but the clause is not inserted.
Root Cause
add_clause's after_search parameter searches paragraph text by concatenating all <w:t> elements. After tracked_replace, the paragraph's XML contains interleaved <w:del> and <w:ins> elements. The after_search text-matching logic may:
- Include both old (del) and new (ins) text in the concatenation, so neither the old NOR new text matches cleanly
- Match the wrong paragraph if the search string appears in unexpected combinations of del+ins text
Solution: Direct lxml addnext Insertion
After all tracked_replace calls, insert new clauses directly using lxml:
# Find reference paragraph by index or by scanning accepted-view text
paras = ed.body.findall(f'{WNS}p')
ref_para = paras[target_index] # e.g., P68
# Build INS paragraph
new_p = etree.Element(f'{WNS}p')
new_p.append(copy.deepcopy(ref_ppr)) # Clone paragraph formatting
ins = etree.SubElement(new_p, f'{WNS}ins')
ins.set(f'{WNS}id', next_rev_id())
ins.set(f'{WNS}author', 'WB')
ins.set(f'{WNS}date', rev_date)
r = etree.SubElement(ins, f'{WNS}r')
r.set(f'{WNS}rsidR', rsid)
r.insert(0, copy.deepcopy(ref_rpr))
t = etree.SubElement(r, f'{WNS}t')
t.set(XML_SPACE, 'preserve')
t.text = clause_text
# Mark paragraph itself as inserted (pPr/rPr/ins)
ppr = new_p.find(f'{WNS}pPr')
ppr_rpr = etree.SubElement(ppr, f'{WNS}rPr')
ppr_ins = etree.SubElement(ppr_rpr, f'{WNS}ins')
ppr_ins.set(f'{WNS}id', next_rev_id())
ppr_ins.set(f'{WNS}author', 'WB')
ppr_ins.set(f'{WNS}date', rev_date)
# Insert after reference
ref_para.addnext(new_p)
ref_para = new_p # Chain subsequent inserts
When This Applies
- You need to both modify existing clauses (tracked_replace) AND add new clauses in the same editing session
- The
after_searchtext has been altered by prior tracked_replace calls
Correct Operation Order
- All
ed.tracked_replace(...)calls first - Then find target paragraphs by scanning the body with accepted-view text extraction
- Insert new paragraphs directly via
addnext ed.validate()+ed.save()
Verification
After save, scan paragraphs and confirm new clauses appear in accepted-view text at the expected positions.