返回 ppt-master
cli.py
1 #!/usr/bin/env python3
2 """PPT Master SVG quality-check CLI implementation.
3
4 Parses the legacy command-line contract and delegates validation to the checker.
5
6 Usage:
7 python3 scripts/svg_quality_checker.py <svg_file_or_project> [options]
8
9 Examples:
10 python3 scripts/svg_quality_checker.py projects/demo --stage final --json
11
12 Dependencies:
13 Standard library plus local PPT Master validation modules.
14 """
15
16 import sys
17 from pathlib import Path
18
19 from attribution_guard import require_skill_integrity
20 from slide_roster import discover_slide_svgs
21
22 from .checker import SVGQualityChecker
23
24
25 def _first_page_target(target: str) -> str:
26 """Resolve a project/directory target to its first authored SVG page."""
27 path = Path(target)
28 if path.is_file():
29 return str(path)
30 svg_root = path / "svg_output" if (path / "svg_output").is_dir() else path
31 svg_files = discover_slide_svgs(svg_root) if svg_root.is_dir() else []
32 return str(svg_files[0]) if svg_files else target
33
34
35 def _default_json_report_path(
36 checker: SVGQualityChecker,
37 target: str,
38 stage: str,
39 ) -> Path:
40 """Choose a stage-specific report path without overwriting the final gate."""
41 target_path = Path(target)
42 project_path = checker._resolve_project_path(target_path)
43 report_name = (
44 "svg_quality_report.json"
45 if stage == "final"
46 else "svg_quality_first_page_report.json"
47 )
48 if (
49 (project_path / "svg_output").is_dir()
50 or (project_path / "design_spec.md").is_file()
51 ):
52 return project_path / "validation" / report_name
53 base = target_path if target_path.is_dir() else target_path.parent
54 return base / report_name
55
56
57 def print_usage() -> None:
58 """Print CLI usage information."""
59 print("PPT Master - SVG Quality Check Tool\n")
60 print("Usage:")
61 print(" python3 scripts/svg_quality_checker.py <svg_file>")
62 print(" python3 scripts/svg_quality_checker.py <directory>")
63 print(" python3 scripts/svg_quality_checker.py <workspace>/templates --template-mode")
64 print(" python3 scripts/svg_quality_checker.py --all examples")
65 print("\nExamples:")
66 print(" python3 scripts/svg_quality_checker.py examples/project/svg_output/slide_01.svg")
67 print(" python3 scripts/svg_quality_checker.py examples/project/svg_output")
68 print(" python3 scripts/svg_quality_checker.py examples/project")
69 print(" python3 scripts/svg_quality_checker.py templates/layouts/presentation_core/templates --template-mode")
70 print(" python3 scripts/svg_quality_checker.py templates/decks/中国电信/templates --template-mode")
71 print("\nOptions:")
72 print(" --format <ppt169|ppt43|...> Expected canvas format")
73 print(" --stage <first-page|final> first-page checks only the first authored SVG")
74 print(" with a partial structure roster; final (default)")
75 print(" requires the complete declared page roster.")
76 print(" --json Write a machine-readable quality report")
77 print(" --json-output <path> Override the JSON report path")
78 print(" --export Write a plain-text quality report")
79 print(" --output <path> Override the plain-text report path")
80 print(" --quick-generate Validate lockless flat Quick Generate SVGs;")
81 print(" ignore design_spec.md and spec_lock.md.")
82 print(" --template-mode Validate a template workspace's templates/ directory:")
83 print(" Brand/Style validate their portable workspace contracts;")
84 print(" Layout/Deck glob *.svg directly, skip spec_lock checks,")
85 print(" enforce roster consistency, and emit placeholder hints.")
86 print(" native_structure_mode: structured also enables complete")
87 print(" per-file and cross-page structure validation. Legacy")
88 print(" native_structure_mode: template fails and must be")
89 print(" re-created through create-template before validation.")
90 print(" Warnings are advisory: they require no modification and do not affect exit status;")
91 print(" only errors make the command exit with status 1.")
92
93
94 def main() -> None:
95 """Run the CLI entry point."""
96 require_skill_integrity()
97 if len(sys.argv) < 2:
98 print_usage()
99 sys.exit(0)
100
101 if sys.argv[1] in {"-h", "--help", "help"}:
102 print_usage()
103 sys.exit(0)
104
105 if sys.argv[1].startswith("--") and sys.argv[1] not in {"--all"}:
106 print(f"[ERROR] Missing target before option: {sys.argv[1]}")
107 print_usage()
108 sys.exit(1)
109
110 template_mode = "--template-mode" in sys.argv
111 quick_generate = "--quick-generate" in sys.argv
112 if template_mode and quick_generate:
113 print("[ERROR] --template-mode cannot be combined with --quick-generate")
114 sys.exit(1)
115 checker = SVGQualityChecker(
116 template_mode=template_mode,
117 quick_generate=quick_generate,
118 )
119
120 target = sys.argv[1]
121 expected_format = None
122 stage = "final"
123
124 if "--format" in sys.argv:
125 idx = sys.argv.index("--format")
126 if idx + 1 < len(sys.argv):
127 expected_format = sys.argv[idx + 1]
128 if "--stage" in sys.argv:
129 idx = sys.argv.index("--stage")
130 if idx + 1 >= len(sys.argv):
131 print("[ERROR] --stage requires first-page or final")
132 sys.exit(1)
133 stage = sys.argv[idx + 1]
134 if stage not in {"first-page", "final"}:
135 print(f"[ERROR] Unsupported quality-check stage: {stage}")
136 sys.exit(1)
137
138 if target == "--all":
139 if quick_generate:
140 print("[ERROR] --quick-generate does not support --all")
141 sys.exit(1)
142 if stage != "final":
143 print("[ERROR] --stage first-page does not support --all")
144 sys.exit(1)
145 base_dir = sys.argv[2] if len(sys.argv) > 2 else "examples"
146 from project_utils import find_all_projects
147
148 projects = find_all_projects(base_dir)
149
150 for project in projects:
151 print(f"\n{'=' * 80}")
152 print(f"Checking project: {project.name}")
153 print("=" * 80)
154 checker.check_directory(str(project))
155 else:
156 check_target = _first_page_target(target) if stage == "first-page" else target
157 checker.check_directory(check_target, expected_format)
158
159 if stage == "final" and Path(target).is_dir():
160 if checker._has_incomplete_page_roster:
161 print(
162 "[TIP] This final-stage run found an incomplete page roster. "
163 "During serial authoring, use --stage first-page for the first-page "
164 "gate; keep --stage final for the complete deck."
165 )
166
167 checker.print_summary()
168
169 if "--export" in sys.argv:
170 output_file = "svg_quality_report.txt"
171 if "--output" in sys.argv:
172 idx = sys.argv.index("--output")
173 if idx + 1 < len(sys.argv):
174 output_file = sys.argv[idx + 1]
175 checker.export_report(output_file)
176
177 if "--json" in sys.argv or "--json-output" in sys.argv:
178 if "--json-output" in sys.argv:
179 idx = sys.argv.index("--json-output")
180 if idx + 1 >= len(sys.argv):
181 print("[ERROR] --json-output requires a path")
182 sys.exit(1)
183 json_output = Path(sys.argv[idx + 1])
184 else:
185 json_output = _default_json_report_path(checker, target, stage)
186 checker.export_json_report(
187 str(json_output),
188 target=target,
189 stage=stage,
190 )
191
192 if checker.summary["errors"] > 0:
193 sys.exit(1)
194 else:
195 sys.exit(0)
196
196 lines PYTHON