@farazirfan/costar-server-executor 1.7.37 → 1.7.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/agent.d.ts +90 -0
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +606 -0
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/pi-embedded-runner/run.d.ts.map +1 -1
- package/dist/agent/pi-embedded-runner/run.js +2 -1
- package/dist/agent/pi-embedded-runner/run.js.map +1 -1
- package/dist/agent/pi-embedded-runner/system-prompt.d.ts.map +1 -1
- package/dist/agent/pi-embedded-runner/system-prompt.js +16 -37
- package/dist/agent/pi-embedded-runner/system-prompt.js.map +1 -1
- package/dist/agent/pi-embedded-runner/tools.d.ts +4 -1
- package/dist/agent/pi-embedded-runner/tools.d.ts.map +1 -1
- package/dist/agent/pi-embedded-runner/tools.js +3 -1
- package/dist/agent/pi-embedded-runner/tools.js.map +1 -1
- package/dist/agent/pi-embedded-runner/types.d.ts +4 -0
- package/dist/agent/pi-embedded-runner/types.d.ts.map +1 -1
- package/dist/cli/env-loader.d.ts.map +1 -1
- package/dist/cli/env-loader.js +1 -0
- package/dist/cli/env-loader.js.map +1 -1
- package/dist/cli/setup.js +2 -2
- package/dist/cli/setup.js.map +1 -1
- package/dist/cron/normalize.d.ts +31 -0
- package/dist/cron/normalize.d.ts.map +1 -0
- package/dist/cron/normalize.js +211 -0
- package/dist/cron/normalize.js.map +1 -0
- package/dist/cron/scheduler.d.ts +33 -3
- package/dist/cron/scheduler.d.ts.map +1 -1
- package/dist/cron/scheduler.js +253 -48
- package/dist/cron/scheduler.js.map +1 -1
- package/dist/heartbeat/runner.d.ts +27 -12
- package/dist/heartbeat/runner.d.ts.map +1 -1
- package/dist/heartbeat/runner.js +82 -104
- package/dist/heartbeat/runner.js.map +1 -1
- package/dist/infra/heartbeat-events-filter.d.ts +29 -0
- package/dist/infra/heartbeat-events-filter.d.ts.map +1 -0
- package/dist/infra/heartbeat-events-filter.js +80 -0
- package/dist/infra/heartbeat-events-filter.js.map +1 -0
- package/dist/infra/index.d.ts +9 -0
- package/dist/infra/index.d.ts.map +1 -0
- package/dist/infra/index.js +9 -0
- package/dist/infra/index.js.map +1 -0
- package/dist/infra/system-events.d.ts +58 -2
- package/dist/infra/system-events.d.ts.map +1 -1
- package/dist/infra/system-events.js +80 -14
- package/dist/infra/system-events.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +6 -1
- package/dist/server.js.map +1 -1
- package/dist/services/platform-keys.d.ts +19 -0
- package/dist/services/platform-keys.d.ts.map +1 -0
- package/dist/services/platform-keys.js +74 -0
- package/dist/services/platform-keys.js.map +1 -0
- package/dist/subagent/registry.d.ts +96 -0
- package/dist/subagent/registry.d.ts.map +1 -0
- package/dist/subagent/registry.js +180 -0
- package/dist/subagent/registry.js.map +1 -0
- package/dist/tools/complete-turn.d.ts +2 -2
- package/dist/tools/complete-turn.js +10 -10
- package/dist/tools/complete-turn.js.map +1 -1
- package/dist/tools/contacts.d.ts +13 -0
- package/dist/tools/contacts.d.ts.map +1 -0
- package/dist/tools/contacts.js +80 -0
- package/dist/tools/contacts.js.map +1 -0
- package/dist/tools/cron.d.ts +17 -2
- package/dist/tools/cron.d.ts.map +1 -1
- package/dist/tools/cron.js +117 -35
- package/dist/tools/cron.js.map +1 -1
- package/dist/tools/google-maps.d.ts +6 -6
- package/dist/tools/google-maps.d.ts.map +1 -1
- package/dist/tools/google-maps.js +207 -262
- package/dist/tools/google-maps.js.map +1 -1
- package/dist/tools/index.d.ts +17 -7
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +40 -9
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/phone-call.d.ts +11 -0
- package/dist/tools/phone-call.d.ts.map +1 -0
- package/dist/tools/phone-call.js +151 -0
- package/dist/tools/phone-call.js.map +1 -0
- package/dist/tools/sessions-spawn.d.ts +33 -0
- package/dist/tools/sessions-spawn.d.ts.map +1 -0
- package/dist/tools/sessions-spawn.js +164 -0
- package/dist/tools/sessions-spawn.js.map +1 -0
- package/dist/tools/spotify.d.ts +12 -0
- package/dist/tools/spotify.d.ts.map +1 -0
- package/dist/tools/spotify.js +251 -0
- package/dist/tools/spotify.js.map +1 -0
- package/dist/tools/subagents.d.ts +23 -0
- package/dist/tools/subagents.d.ts.map +1 -0
- package/dist/tools/subagents.js +209 -0
- package/dist/tools/subagents.js.map +1 -0
- package/dist/tools/whatsapp.d.ts +13 -0
- package/dist/tools/whatsapp.d.ts.map +1 -0
- package/dist/tools/whatsapp.js +215 -0
- package/dist/tools/whatsapp.js.map +1 -0
- package/dist/tools/youtube.d.ts +12 -0
- package/dist/tools/youtube.d.ts.map +1 -0
- package/dist/tools/youtube.js +218 -0
- package/dist/tools/youtube.js.map +1 -0
- package/dist/utils/asterizk-auth.d.ts +43 -0
- package/dist/utils/asterizk-auth.d.ts.map +1 -0
- package/dist/utils/asterizk-auth.js +125 -0
- package/dist/utils/asterizk-auth.js.map +1 -0
- package/dist/web-server.d.ts.map +1 -1
- package/dist/web-server.js +132 -0
- package/dist/web-server.js.map +1 -1
- package/dist/workspace/index.d.ts +3 -4
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +3 -4
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/templates.d.ts +8 -7
- package/dist/workspace/templates.d.ts.map +1 -1
- package/dist/workspace/templates.js +18 -127
- package/dist/workspace/templates.js.map +1 -1
- package/dist/workspace/workspace.d.ts +2 -4
- package/dist/workspace/workspace.d.ts.map +1 -1
- package/dist/workspace/workspace.js +7 -16
- package/dist/workspace/workspace.js.map +1 -1
- package/package.json +1 -1
- package/public/index.html +231 -0
- package/skills/docx/SKILL.md +468 -0
- package/skills/docx/scripts/__init__.py +1 -0
- package/skills/docx/scripts/accept_changes.py +181 -0
- package/skills/docx/scripts/comment.py +347 -0
- package/skills/docx/scripts/helpers/__init__.py +0 -0
- package/skills/docx/scripts/helpers/merge_runs.py +231 -0
- package/skills/docx/scripts/helpers/simplify_redlines.py +240 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/docx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/docx/scripts/ooxml/scripts/pack.py +159 -0
- package/skills/docx/scripts/ooxml/scripts/unpack.py +29 -0
- package/skills/docx/scripts/ooxml/scripts/validate.py +106 -0
- package/skills/docx/scripts/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/docx/scripts/ooxml/scripts/validation/base.py +1023 -0
- package/skills/docx/scripts/ooxml/scripts/validation/docx.py +519 -0
- package/skills/docx/scripts/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/docx/scripts/ooxml/scripts/validation/redlining.py +284 -0
- package/skills/docx/scripts/pack.py +166 -0
- package/skills/docx/scripts/templates/comments.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/skills/docx/scripts/templates/people.xml +3 -0
- package/skills/docx/scripts/unpack.py +134 -0
- package/skills/longform-video-generation/SKILL.md +298 -0
- package/skills/longform-video-generation/references/advanced_techniques.md +474 -0
- package/skills/longform-video-generation/references/google_api_guide.md +288 -0
- package/skills/longform-video-generation/scripts/video_generator.py +579 -0
- package/skills/pdf/FORMS.md +305 -0
- package/skills/pdf/REFERENCE.md +612 -0
- package/skills/pdf/SKILL.md +293 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/skills/pdf/scripts/create_validation_image.py +41 -0
- package/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/skills/pdf/scripts/extract_form_structure.py +124 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +116 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +136 -0
- package/skills/pptx/SKILL.md +171 -0
- package/skills/pptx/editing.md +205 -0
- package/skills/pptx/pptxgenjs.md +377 -0
- package/skills/pptx/scripts/add_slide.py +225 -0
- package/skills/pptx/scripts/clean.py +309 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/pptx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/pptx/scripts/ooxml/scripts/pack.py +159 -0
- package/skills/pptx/scripts/ooxml/scripts/unpack.py +29 -0
- package/skills/pptx/scripts/ooxml/scripts/validate.py +106 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/base.py +1023 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/docx.py +519 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/redlining.py +284 -0
- package/skills/pptx/scripts/pack.py +168 -0
- package/skills/pptx/scripts/thumbnail.py +318 -0
- package/skills/pptx/scripts/unpack.py +86 -0
- package/skills/xlsx/SKILL.md +291 -0
- package/skills/xlsx/recalc.py +247 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""Simplify tracked changes by merging adjacent w:ins or w:del elements.
|
|
2
|
+
|
|
3
|
+
Merges adjacent <w:ins> elements from the same author into a single element.
|
|
4
|
+
Same for <w:del> elements. This makes heavily-redlined documents easier to
|
|
5
|
+
work with by reducing the number of tracked change wrappers.
|
|
6
|
+
|
|
7
|
+
Rules:
|
|
8
|
+
- Only merges w:ins with w:ins, w:del with w:del (same element type)
|
|
9
|
+
- Only merges if same author (ignores timestamp differences)
|
|
10
|
+
- Only merges if truly adjacent (only whitespace between them)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import xml.etree.ElementTree as ET
|
|
14
|
+
import zipfile
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import defusedxml.minidom
|
|
18
|
+
|
|
19
|
+
WORD_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def simplify_redlines(input_dir: str) -> tuple[int, str]:
|
|
23
|
+
"""Merge adjacent tracked changes from the same author in document.xml.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
input_dir: Path to unpacked DOCX directory
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
(merge_count, message)
|
|
30
|
+
"""
|
|
31
|
+
doc_xml = Path(input_dir) / "word" / "document.xml"
|
|
32
|
+
|
|
33
|
+
if not doc_xml.exists():
|
|
34
|
+
return 0, f"Error: {doc_xml} not found"
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
dom = defusedxml.minidom.parseString(doc_xml.read_text(encoding="utf-8"))
|
|
38
|
+
root = dom.documentElement
|
|
39
|
+
|
|
40
|
+
merge_count = 0
|
|
41
|
+
|
|
42
|
+
# Find all paragraphs and table cells (containers for content)
|
|
43
|
+
containers = _find_elements(root, "p") + _find_elements(root, "tc")
|
|
44
|
+
|
|
45
|
+
for container in containers:
|
|
46
|
+
merge_count += _merge_tracked_changes_in(container, "ins")
|
|
47
|
+
merge_count += _merge_tracked_changes_in(container, "del")
|
|
48
|
+
|
|
49
|
+
doc_xml.write_bytes(dom.toxml(encoding="UTF-8"))
|
|
50
|
+
return merge_count, f"Simplified {merge_count} tracked changes"
|
|
51
|
+
|
|
52
|
+
except Exception as e:
|
|
53
|
+
return 0, f"Error: {e}"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _merge_tracked_changes_in(container, tag: str) -> int:
|
|
57
|
+
"""Merge adjacent w:ins or w:del elements from the same author."""
|
|
58
|
+
merge_count = 0
|
|
59
|
+
|
|
60
|
+
# Get direct children that are tracked changes of this type
|
|
61
|
+
tracked = [
|
|
62
|
+
child
|
|
63
|
+
for child in container.childNodes
|
|
64
|
+
if child.nodeType == child.ELEMENT_NODE and _is_element(child, tag)
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
if len(tracked) < 2:
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
# Process from front: merge next into current when possible
|
|
71
|
+
i = 0
|
|
72
|
+
while i < len(tracked) - 1:
|
|
73
|
+
curr = tracked[i]
|
|
74
|
+
next_elem = tracked[i + 1]
|
|
75
|
+
|
|
76
|
+
if _can_merge_tracked(curr, next_elem):
|
|
77
|
+
_merge_tracked_content(curr, next_elem)
|
|
78
|
+
container.removeChild(next_elem)
|
|
79
|
+
tracked.pop(i + 1)
|
|
80
|
+
merge_count += 1
|
|
81
|
+
# Don't increment i - try to merge more into curr
|
|
82
|
+
else:
|
|
83
|
+
i += 1
|
|
84
|
+
|
|
85
|
+
return merge_count
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _is_element(node, tag: str) -> bool:
|
|
89
|
+
"""Check if node matches the given tag name."""
|
|
90
|
+
name = node.localName or node.tagName
|
|
91
|
+
return name == tag or name.endswith(f":{tag}")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _get_author(elem) -> str:
|
|
95
|
+
"""Get the author attribute from a tracked change element."""
|
|
96
|
+
author = elem.getAttribute("w:author")
|
|
97
|
+
if not author:
|
|
98
|
+
for attr in elem.attributes.values():
|
|
99
|
+
if attr.localName == "author" or attr.name.endswith(":author"):
|
|
100
|
+
return attr.value
|
|
101
|
+
return author
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _can_merge_tracked(elem1, elem2) -> bool:
|
|
105
|
+
"""Check if two tracked change elements can be merged."""
|
|
106
|
+
# Must be same author
|
|
107
|
+
if _get_author(elem1) != _get_author(elem2):
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
# Must be truly adjacent (only whitespace between them)
|
|
111
|
+
node = elem1.nextSibling
|
|
112
|
+
while node and node != elem2:
|
|
113
|
+
if node.nodeType == node.ELEMENT_NODE:
|
|
114
|
+
return False
|
|
115
|
+
if node.nodeType == node.TEXT_NODE and node.data.strip():
|
|
116
|
+
return False
|
|
117
|
+
node = node.nextSibling
|
|
118
|
+
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _merge_tracked_content(target, source):
|
|
123
|
+
"""Move all children from source tracked change to target."""
|
|
124
|
+
while source.firstChild:
|
|
125
|
+
child = source.firstChild
|
|
126
|
+
source.removeChild(child)
|
|
127
|
+
target.appendChild(child)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _find_elements(root, tag: str) -> list:
|
|
131
|
+
"""Find all elements matching tag name (with or without namespace)."""
|
|
132
|
+
results = []
|
|
133
|
+
|
|
134
|
+
def traverse(node):
|
|
135
|
+
if node.nodeType == node.ELEMENT_NODE:
|
|
136
|
+
name = node.localName or node.tagName
|
|
137
|
+
if name == tag or name.endswith(f":{tag}"):
|
|
138
|
+
results.append(node)
|
|
139
|
+
for child in node.childNodes:
|
|
140
|
+
traverse(child)
|
|
141
|
+
|
|
142
|
+
traverse(root)
|
|
143
|
+
return results
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def get_tracked_change_authors(doc_xml_path: Path) -> dict[str, int]:
|
|
147
|
+
"""Get authors and their tracked change counts from a document.xml file.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Dict mapping author name to count of tracked changes (w:ins + w:del)
|
|
151
|
+
"""
|
|
152
|
+
if not doc_xml_path.exists():
|
|
153
|
+
return {}
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
tree = ET.parse(doc_xml_path)
|
|
157
|
+
root = tree.getroot()
|
|
158
|
+
except ET.ParseError:
|
|
159
|
+
return {}
|
|
160
|
+
|
|
161
|
+
namespaces = {"w": WORD_NS}
|
|
162
|
+
author_attr = f"{{{WORD_NS}}}author"
|
|
163
|
+
|
|
164
|
+
authors: dict[str, int] = {}
|
|
165
|
+
for tag in ["ins", "del"]:
|
|
166
|
+
for elem in root.findall(f".//w:{tag}", namespaces):
|
|
167
|
+
author = elem.get(author_attr)
|
|
168
|
+
if author:
|
|
169
|
+
authors[author] = authors.get(author, 0) + 1
|
|
170
|
+
|
|
171
|
+
return authors
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _get_authors_from_docx(docx_path: Path) -> dict[str, int]:
|
|
175
|
+
"""Get authors and counts from a packed DOCX file."""
|
|
176
|
+
try:
|
|
177
|
+
with zipfile.ZipFile(docx_path, "r") as zf:
|
|
178
|
+
if "word/document.xml" not in zf.namelist():
|
|
179
|
+
return {}
|
|
180
|
+
with zf.open("word/document.xml") as f:
|
|
181
|
+
tree = ET.parse(f)
|
|
182
|
+
root = tree.getroot()
|
|
183
|
+
|
|
184
|
+
namespaces = {"w": WORD_NS}
|
|
185
|
+
author_attr = f"{{{WORD_NS}}}author"
|
|
186
|
+
|
|
187
|
+
authors: dict[str, int] = {}
|
|
188
|
+
for tag in ["ins", "del"]:
|
|
189
|
+
for elem in root.findall(f".//w:{tag}", namespaces):
|
|
190
|
+
author = elem.get(author_attr)
|
|
191
|
+
if author:
|
|
192
|
+
authors[author] = authors.get(author, 0) + 1
|
|
193
|
+
return authors
|
|
194
|
+
except (zipfile.BadZipFile, ET.ParseError):
|
|
195
|
+
return {}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def infer_author(modified_dir: Path, original_docx: Path, default: str = "Costar") -> str:
|
|
199
|
+
"""Infer the author to validate by finding who added tracked changes.
|
|
200
|
+
|
|
201
|
+
Compares tracked change counts between modified and original documents.
|
|
202
|
+
Returns the author who added new tracked changes.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
modified_dir: Path to unpacked DOCX directory
|
|
206
|
+
original_docx: Path to original DOCX file
|
|
207
|
+
default: Default author if no new changes found
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Author name to use for validation
|
|
211
|
+
|
|
212
|
+
Raises:
|
|
213
|
+
ValueError: If multiple authors added new changes (ambiguous)
|
|
214
|
+
"""
|
|
215
|
+
modified_xml = modified_dir / "word" / "document.xml"
|
|
216
|
+
modified_authors = get_tracked_change_authors(modified_xml)
|
|
217
|
+
|
|
218
|
+
if not modified_authors:
|
|
219
|
+
return default
|
|
220
|
+
|
|
221
|
+
original_authors = _get_authors_from_docx(original_docx)
|
|
222
|
+
|
|
223
|
+
# Calculate new changes per author (modified count - original count)
|
|
224
|
+
new_changes: dict[str, int] = {}
|
|
225
|
+
for author, count in modified_authors.items():
|
|
226
|
+
original_count = original_authors.get(author, 0)
|
|
227
|
+
diff = count - original_count
|
|
228
|
+
if diff > 0:
|
|
229
|
+
new_changes[author] = diff
|
|
230
|
+
|
|
231
|
+
if not new_changes:
|
|
232
|
+
return default
|
|
233
|
+
|
|
234
|
+
if len(new_changes) == 1:
|
|
235
|
+
return next(iter(new_changes))
|
|
236
|
+
|
|
237
|
+
raise ValueError(
|
|
238
|
+
f"Multiple authors added new changes: {new_changes}. "
|
|
239
|
+
"Cannot infer which author to validate."
|
|
240
|
+
)
|