68 lines
2.5 KiB
Markdown
68 lines
2.5 KiB
Markdown
# 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:
|
|
|
|
1. Include both old (del) and new (ins) text in the concatenation, so neither the old NOR new text matches cleanly
|
|
2. 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:
|
|
|
|
```python
|
|
# 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_search` text has been altered by prior tracked_replace calls
|
|
|
|
## Correct Operation Order
|
|
|
|
1. All `ed.tracked_replace(...)` calls first
|
|
2. Then find target paragraphs by scanning the body with accepted-view text extraction
|
|
3. Insert new paragraphs directly via `addnext`
|
|
4. `ed.validate()` + `ed.save()`
|
|
|
|
## Verification
|
|
|
|
After save, scan paragraphs and confirm new clauses appear in accepted-view text at the expected positions.
|