返回 ppt-master
check_annotations.py
根目录 / skills / ppt-master / scripts / check_annotations.py
1 #!/usr/bin/env python3
2 """
3 PPT Master - SVG Annotation Checker
4
5 Scans SVG files for edit annotations (data-edit-target / data-edit-annotation attributes)
6 and prints a human-readable summary. Used by AI agents to discover pending annotations.
7
8 Usage:
9 python3 scripts/check_annotations.py <project_dir>
10 python3 scripts/check_annotations.py <svg_file>
11
12 Examples:
13 python3 scripts/check_annotations.py projects/my-project
14 python3 scripts/check_annotations.py projects/my-project/svg_output/slide_01.svg
15
16 Dependencies:
17 None (only uses standard library)
18 """
19
20 import argparse
21 import sys
22 import xml.etree.ElementTree as ET
23 from pathlib import Path
24 from typing import Optional
25
26 from console_encoding import configure_utf8_stdio
27 from slide_roster import discover_slide_svgs
28
29 configure_utf8_stdio()
30
31
32 def scan_svg_file(svg_path: Path) -> list[dict]:
33 """Scan a single SVG file for edit annotations."""
34 try:
35 tree = ET.parse(svg_path)
36 except ET.ParseError:
37 return []
38
39 root = tree.getroot()
40 annotations = []
41
42 for elem in root.iter():
43 if elem.get('data-edit-target') == 'true':
44 tag = elem.tag
45 if '}' in tag:
46 tag = tag.split('}', 1)[1]
47
48 content_preview = ''
49 if tag == 'text' and elem.text:
50 content_preview = elem.text.strip()[:50]
51
52 annotations.append({
53 'element_id': elem.get('id', '(no id)'),
54 'tag': tag,
55 'annotation': elem.get('data-edit-annotation', ''),
56 'content_preview': content_preview,
57 })
58
59 return annotations
60
61
62 def scan_directory(dir_path: Path) -> dict[str, list[dict]]:
63 """Scan all SVG files in svg_output/ for edit annotations."""
64 svg_dir = dir_path / 'svg_output'
65 if not svg_dir.exists():
66 return {}
67
68 results = {}
69 for svg_file in discover_slide_svgs(svg_dir):
70 annotations = scan_svg_file(svg_file)
71 if annotations:
72 results[svg_file.name] = annotations
73
74 return results
75
76
77 def print_results(results: dict[str, list[dict]]) -> None:
78 """Print annotation results in human-readable format."""
79 if not results:
80 print("[OK] No annotations found.")
81 return
82
83 total = sum(len(anns) for anns in results.values())
84 file_count = len(results)
85 ann_word = "annotation" if total == 1 else "annotations"
86 file_word = "file" if file_count == 1 else "files"
87 print(f"Found {total} {ann_word} in {file_count} {file_word}:\n")
88
89 for filename, annotations in results.items():
90 print(f"{filename}")
91 for i, ann in enumerate(annotations, 1):
92 content = f' "{ann["content_preview"]}"' if ann['content_preview'] else ''
93 print(f" [{i}] <{ann['tag']} id=\"{ann['element_id']}\">{content}")
94 print(f" → {ann['annotation']}")
95 print()
96
97
98 def build_parser() -> argparse.ArgumentParser:
99 parser = argparse.ArgumentParser(
100 description='Check SVG files for edit annotations',
101 formatter_class=argparse.RawDescriptionHelpFormatter,
102 )
103 parser.add_argument('path', help='Project directory or single SVG file path')
104 return parser
105
106
107 def main(argv: Optional[list[str]] = None) -> int:
108 parser = build_parser()
109 args = parser.parse_args(argv)
110
111 target = Path(args.path).resolve()
112
113 if not target.exists():
114 print(f"Error: Path not found: {target}", file=sys.stderr)
115 return 1
116
117 if target.is_file() and target.suffix == '.svg':
118 annotations = scan_svg_file(target)
119 results = {target.name: annotations} if annotations else {}
120 elif target.is_dir():
121 results = scan_directory(target)
122 else:
123 print(f"Error: Expected a project directory or .svg file, got: {target}", file=sys.stderr)
124 return 1
125
126 print_results(results)
127 return 0
128
129
130 if __name__ == '__main__':
131 raise SystemExit(main())
132
132 lines PYTHON