Release standalone PPT template workflow 0.5.0
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
PPT Master - Examples Index Generator
|
||||
|
||||
Automatically scans the examples directory and generates a README.md index file.
|
||||
|
||||
Usage:
|
||||
python3 scripts/generate_examples_index.py
|
||||
python3 scripts/generate_examples_index.py examples
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from console_encoding import configure_utf8_stdio
|
||||
|
||||
configure_utf8_stdio()
|
||||
|
||||
try:
|
||||
from project_utils import find_all_projects, get_project_info, CANVAS_FORMATS
|
||||
except ImportError:
|
||||
print("Error: Cannot import the project_utils module")
|
||||
print("Please ensure project_utils.py is in the same directory")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def generate_examples_index(examples_dir: str = 'examples') -> str:
|
||||
"""
|
||||
Generate a README.md index for the examples directory
|
||||
|
||||
Args:
|
||||
examples_dir: Path to the examples directory
|
||||
|
||||
Returns:
|
||||
Generated README.md content
|
||||
"""
|
||||
examples_path = Path(examples_dir)
|
||||
skill_dir = Path(__file__).resolve().parent.parent
|
||||
|
||||
if not examples_path.exists():
|
||||
print(f"[ERROR] Directory not found: {examples_dir}")
|
||||
return ""
|
||||
|
||||
def skill_link(target: Path) -> str:
|
||||
"""Return a link from the generated index to a packaged Skill resource."""
|
||||
return Path(
|
||||
os.path.relpath(target, start=examples_path.resolve())
|
||||
).as_posix()
|
||||
|
||||
print(f"[SCAN] Scanning directory: {examples_dir}")
|
||||
|
||||
# Find all projects
|
||||
projects = find_all_projects(examples_dir)
|
||||
|
||||
if not projects:
|
||||
print("[WARN] No projects found")
|
||||
return ""
|
||||
|
||||
print(f"Found {len(projects)} project(s)")
|
||||
|
||||
# Collect project information
|
||||
projects_info = []
|
||||
for project_path in projects:
|
||||
info = get_project_info(str(project_path))
|
||||
projects_info.append(info)
|
||||
|
||||
# Sort by date (newest first)
|
||||
projects_info.sort(key=lambda x: x['date'], reverse=True)
|
||||
|
||||
# Group by format
|
||||
by_format = defaultdict(list)
|
||||
for info in projects_info:
|
||||
by_format[info['format']].append(info)
|
||||
|
||||
# Generate README content
|
||||
content = []
|
||||
content.append("# PPT Master Example Projects Index\n")
|
||||
content.append("> This file is auto-generated by the packaged `scripts/generate_examples_index.py`\n")
|
||||
content.append(f"> Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
|
||||
# Overview statistics
|
||||
content.append("## [Stats] Overview\n")
|
||||
content.append(f"- **Total projects**: {len(projects_info)}")
|
||||
content.append(f"- **Canvas formats**: {len(by_format)} type(s)")
|
||||
|
||||
total_svgs = sum(info['svg_count'] for info in projects_info)
|
||||
content.append(f"- **SVG files**: {total_svgs}")
|
||||
|
||||
# Statistics by format
|
||||
content.append("\n### Format Distribution\n")
|
||||
for fmt_key in sorted(by_format.keys(), key=lambda x: len(by_format[x]), reverse=True):
|
||||
count = len(by_format[fmt_key])
|
||||
fmt_name = CANVAS_FORMATS.get(fmt_key, {}).get('name', fmt_key)
|
||||
content.append(f"- **{fmt_name}**: {count} project(s)")
|
||||
|
||||
# Recently updated
|
||||
content.append("\n## [New] Recently Updated\n")
|
||||
for info in projects_info[:5]:
|
||||
content.append(
|
||||
f"- **{info['name']}** ({info['format_name']}) - {info['date_formatted']}")
|
||||
|
||||
# Project list by format
|
||||
content.append("\n## [List] Project List\n")
|
||||
|
||||
# Define format display order
|
||||
format_order = ['ppt169', 'ppt43', 'wechat',
|
||||
'xiaohongshu', 'moments', 'story', 'banner', 'a4']
|
||||
|
||||
for fmt_key in format_order:
|
||||
if fmt_key not in by_format:
|
||||
continue
|
||||
|
||||
fmt_info = CANVAS_FORMATS.get(fmt_key, {})
|
||||
fmt_name = fmt_info.get('name', fmt_key)
|
||||
dimensions = fmt_info.get('dimensions', '')
|
||||
|
||||
content.append(f"\n### {fmt_name} ({dimensions})\n")
|
||||
|
||||
projects_list = by_format[fmt_key]
|
||||
# Sort by date
|
||||
projects_list.sort(key=lambda x: x['date'], reverse=True)
|
||||
|
||||
for info in projects_list:
|
||||
# Project name and link
|
||||
project_link = f"./{info['dir_name']}"
|
||||
|
||||
# Build project entry
|
||||
line = f"- **[{info['name']}]({project_link})**"
|
||||
|
||||
# Add date
|
||||
line += f" - {info['date_formatted']}"
|
||||
|
||||
# Add SVG count
|
||||
line += f" - {info['svg_count']} page(s)"
|
||||
|
||||
content.append(line)
|
||||
|
||||
# Other uncategorized formats
|
||||
other_formats = set(by_format.keys()) - set(format_order)
|
||||
if other_formats:
|
||||
content.append("\n### Other Formats\n")
|
||||
for fmt_key in sorted(other_formats):
|
||||
projects_list = by_format[fmt_key]
|
||||
for info in projects_list:
|
||||
project_link = f"./{info['dir_name']}"
|
||||
line = f"- **[{info['name']}]({project_link})**"
|
||||
line += f" ({info['format_name']}) - {info['date_formatted']}"
|
||||
line += f" - {info['svg_count']} page(s)"
|
||||
content.append(line)
|
||||
|
||||
# Usage instructions
|
||||
content.append("\n## [Docs] Usage Instructions\n")
|
||||
content.append("### Preview Projects\n")
|
||||
content.append("Each project contains the following files:\n")
|
||||
content.append("- `README.md` - Project documentation")
|
||||
content.append("- `Design Spec & Content Outline.md` - Full design specification")
|
||||
content.append("- `svg_output/` - SVG output files\n")
|
||||
|
||||
content.append("**Method 1: Using an HTTP server (recommended)**\n")
|
||||
content.append("```bash")
|
||||
content.append(
|
||||
"python3 -m http.server --directory examples/<project_name>/svg_output 8000")
|
||||
content.append("# Visit http://localhost:8000")
|
||||
content.append("```\n")
|
||||
|
||||
content.append("**Method 2: Open SVG directly**\n")
|
||||
content.append("```bash")
|
||||
content.append(
|
||||
"open examples/<project_name>/svg_output/slide_01_cover.svg")
|
||||
content.append("```\n")
|
||||
|
||||
# Create new project
|
||||
content.append("### Create a New Project\n")
|
||||
content.append("Refer to existing project structures, or use the project management tool:\n")
|
||||
content.append("```bash")
|
||||
content.append(
|
||||
"python3 scripts/project_manager.py init my_project --format ppt169")
|
||||
content.append("```\n")
|
||||
|
||||
# Contribution guidelines
|
||||
content.append("## [Contribute] Contributing Example Projects\n")
|
||||
content.append("We welcome you to share your projects in the examples directory!\n")
|
||||
content.append("### Project Requirements\n")
|
||||
content.append("1. Follow the standard project structure")
|
||||
content.append("2. Include a complete README.md and design specification")
|
||||
content.append("3. SVG files must comply with technical specifications")
|
||||
content.append("4. Directory naming format: `{project_name}_{format}_{YYYYMMDD}`\n")
|
||||
|
||||
content.append("### Submission Process\n")
|
||||
content.append("1. Create a project under the `examples/` directory")
|
||||
content.append(
|
||||
"2. Validate the project: `python3 scripts/project_manager.py validate examples/<project>`")
|
||||
content.append("3. Update the index: `python3 scripts/generate_examples_index.py`")
|
||||
content.append("4. Submit a Pull Request\n")
|
||||
|
||||
# Related resources
|
||||
content.append("## [Resources] Related Resources\n")
|
||||
content.append(f"- [Workflow]({skill_link(skill_dir / 'SKILL.md')})")
|
||||
content.append(
|
||||
f"- [Canvas Formats]({skill_link(skill_dir / 'references' / 'canvas-formats.md')})")
|
||||
content.append(
|
||||
f"- [Role Definitions]({skill_link(skill_dir / 'references')})")
|
||||
content.append(
|
||||
f"- [Visualization Templates]({skill_link(skill_dir / 'templates' / 'README.md')})\n")
|
||||
|
||||
# Footer
|
||||
content.append("---\n")
|
||||
content.append(
|
||||
f"*Auto-generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} by PPT Master*")
|
||||
|
||||
return "\n".join(content)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Run the CLI entry point."""
|
||||
examples_dir = 'examples'
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1] in {'-h', '--help', 'help'}:
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
|
||||
examples_dir = sys.argv[1]
|
||||
|
||||
print("=" * 80)
|
||||
print("PPT Master - Examples Index Generator")
|
||||
print("=" * 80 + "\n")
|
||||
|
||||
# Generate index content
|
||||
content = generate_examples_index(examples_dir)
|
||||
|
||||
if not content:
|
||||
print("\n[ERROR] Generation failed")
|
||||
sys.exit(1)
|
||||
|
||||
# Write to file
|
||||
output_file = Path(examples_dir) / 'README.md'
|
||||
|
||||
try:
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"\n[OK] Index file generated: {output_file}")
|
||||
print(f" Contains {len(content.splitlines())} lines")
|
||||
|
||||
# Display statistics
|
||||
projects_count = content.count('- **[')
|
||||
print(f" Indexed {projects_count} project(s)")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n[ERROR] Failed to write file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user