feat: export core Hermes skills
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
# Merge Layered Revisions with Priority (Accept Inner Author's Edits)
|
||||
|
||||
## Scenario (2026-07-03 模特合作协议案)
|
||||
|
||||
File has two layers of tracked changes:
|
||||
- **Layer 1 (WB)**: Original review modifications
|
||||
- **Layer 2 (华诚-Z)**: User edited on top of WB's tracked changes
|
||||
|
||||
Result: 华诚-Z's `w:del` elements are **nested inside** WB's `w:ins` elements — meaning 华诚-Z deleted portions of what WB had inserted.
|
||||
|
||||
User instruction: "以华诚-Z为准" (prioritize 华诚-Z), then unify all author names to WB.
|
||||
|
||||
## Three-Step Algorithm
|
||||
|
||||
### Step 1: Accept nested deletions (inner author wins)
|
||||
|
||||
Find all `w:del[author=华诚-Z]` nested inside `w:ins[author=WB]` and remove them (= accept the deletion):
|
||||
|
||||
```python
|
||||
def accept_nested_deletions(body, inner_author='华诚-Z', outer_author='WB'):
|
||||
for ins_elem in body.findall(f'.//{W}ins'):
|
||||
if ins_elem.get(f'{W}author') != outer_author:
|
||||
continue
|
||||
for del_elem in ins_elem.findall(f'.//{W}del'):
|
||||
if del_elem.get(f'{W}author') == inner_author:
|
||||
parent = del_elem.getparent()
|
||||
parent.remove(del_elem)
|
||||
```
|
||||
|
||||
### Step 2: Remove empty outer elements
|
||||
|
||||
After accepting nested deletions, some WB ins elements may be empty (all their content was deleted by 华诚-Z):
|
||||
|
||||
```python
|
||||
def remove_empty_ins(body):
|
||||
for ins_elem in body.findall(f'.//{W}ins'):
|
||||
has_text = False
|
||||
for t in ins_elem.findall(f'.//{W}t'):
|
||||
if t.text and t.text.strip():
|
||||
has_text = True
|
||||
break
|
||||
if not has_text:
|
||||
parent = ins_elem.getparent()
|
||||
if parent is not None:
|
||||
parent.remove(ins_elem)
|
||||
```
|
||||
|
||||
### Step 3: Unify author names
|
||||
|
||||
```python
|
||||
def rename_author(body, old_author, new_author):
|
||||
count = 0
|
||||
for elem in body.iter():
|
||||
author = elem.get(f'{W}author')
|
||||
if author == old_author:
|
||||
elem.set(f'{W}author', new_author)
|
||||
count += 1
|
||||
return count
|
||||
```
|
||||
|
||||
## Complete Flow
|
||||
|
||||
```python
|
||||
from docx import Document
|
||||
from lxml import etree
|
||||
|
||||
doc = Document('input.docx')
|
||||
body = doc.element.body
|
||||
|
||||
# Step 1: Accept 华诚-Z deletions of WB content
|
||||
accept_nested_deletions(body, inner_author='华诚-Z', outer_author='WB')
|
||||
|
||||
# Step 2: Clean up empty WB ins elements
|
||||
remove_empty_ins(body)
|
||||
|
||||
# Step 3: Rename 华诚-Z → WB
|
||||
rename_author(body, '华诚-Z', 'WB')
|
||||
|
||||
doc.save('output.docx')
|
||||
```
|
||||
|
||||
## After Merge: Additional Modifications
|
||||
|
||||
After merging, you can continue adding new WB tracked changes on the unified file (e.g., reverting specific clauses to template wording). Use standard tracked change creation:
|
||||
|
||||
```python
|
||||
def make_del(text, rPr=None, author='WB', date='2026-07-03T06:00:00Z'):
|
||||
d = etree.Element(f'{W}del')
|
||||
d.set(f'{W}id', str(abs(hash(text)) % 100000))
|
||||
d.set(f'{W}author', author)
|
||||
d.set(f'{W}date', date)
|
||||
r = etree.SubElement(d, f'{W}r')
|
||||
if rPr is not None:
|
||||
r.append(deepcopy(rPr))
|
||||
dt = etree.SubElement(r, f'{W}delText')
|
||||
dt.set('{http://www.w3.org/XML/1998/namespace}space', 'preserve')
|
||||
dt.text = text
|
||||
return d
|
||||
|
||||
def make_ins(text, rPr=None, author='WB', date='2026-07-03T06:00:00Z'):
|
||||
ins = etree.Element(f'{W}ins')
|
||||
ins.set(f'{W}id', str(abs(hash(text + 'ins')) % 100000))
|
||||
ins.set(f'{W}author', author)
|
||||
ins.set(f'{W}date', date)
|
||||
r = etree.SubElement(ins, f'{W}r')
|
||||
if rPr is not None:
|
||||
r.append(deepcopy(rPr))
|
||||
t = etree.SubElement(r, f'{W}t')
|
||||
t.set('{http://www.w3.org/XML/1998/namespace}space', 'preserve')
|
||||
t.text = text
|
||||
return ins
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
After merge:
|
||||
- `set(elem.get(W+'author') for elem in body.iter() if elem.get(W+'author'))` should return `{'WB'}` only
|
||||
- Count ins/del elements to confirm reasonable numbers
|
||||
- Verify key clauses read correctly in "accepted" view
|
||||
|
||||
## Key Distinction from `unify-author-wb.py`
|
||||
|
||||
The `scripts/unify-author-wb.py` script **only renames authors** — it does NOT handle nested deletions. If 华诚-Z has `w:del` inside WB's `w:ins`, just running unify will rename the del to WB but **leave the deleted content still marked as deleted inside the insertion** — creating a confusing state where WB appears to both insert and delete the same text.
|
||||
|
||||
**Always run the three-step algorithm** when inner author has modified outer author's tracked changes.
|
||||
Reference in New Issue
Block a user