vibe-coding-cn/assets/repo/prompts-library/scripts/start_convert.py

189 lines
7.5 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
start_convert.py
Launcher that orchestrates conversions between Excel workbooks and prompt documents
using the following conventions:
Input locations (relative to repo root):
- ./prompt_excel/ # place .xlsx files here for Excel → Docs
- ./prompt_docs/ # place prompt folders here for Docs → Excel
Output locations (under repo root, named by source file/folder mtime):
- ./prompt_docs_YYYYMMDD_HHMMSS/ # Excel → Docs results (copies of prompts/*)
- ./prompt_excel_YYYYMMDD_HHMMSS/ # Docs → Excel results (rebuilt.xlsx)
Usage:
# Auto mode: if there are .xlsx under prompt_excel, run Excel→Docs;
# if there is a docs set under prompt_docs, run Docs→Excel.
python prompt-library/scripts/start_convert.py
# Force a mode:
python prompt-library/scripts/start_convert.py --mode excel2docs
python prompt-library/scripts/start_convert.py --mode docs2excel
Notes:
- No interactive prompts; behavior is driven by the file presence and CLI flags
- Requires pandas, openpyxl, PyYAML (see scripts/requirements.txt)
"""
from __future__ import annotations
import argparse
import importlib.util
import shutil
import sys
from datetime import datetime
from pathlib import Path
from typing import List
def ts_from_path(p: Path) -> str:
st = p.stat()
# Prefer creation/birth time when available; fall back to mtime
ts = getattr(st, "st_birthtime", None)
if ts is None:
# On Windows, st_ctime is creation; on Linux it's inode change time
# We still prefer mtime for consistency if birthtime is unavailable.
ts = st.st_mtime
# Format: YYYY_MMDD_HHMMSS per requirement example 2025_0102_2309
return datetime.fromtimestamp(ts).strftime("%Y_%m%d_%H%M%S")
def load_module(py_path: Path, module_name: str):
spec = importlib.util.spec_from_file_location(module_name, str(py_path))
if spec is None or spec.loader is None:
raise RuntimeError(f"Unable to load module: {py_path}")
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module) # type: ignore
return module
def run_excel_to_docs_for_file(excel_path: Path, prompt_library_dir: Path, out_root: Path) -> Path:
convert_path = prompt_library_dir / "scripts" / "convert_local.py"
mod = load_module(convert_path, "convert_local")
project_root = prompt_library_dir.parent
# Prepare snapshot output directory under repo_root/prompt_docs/
base_dir = out_root / "prompt_docs"
base_dir.mkdir(parents=True, exist_ok=True)
out_dir = base_dir / f"prompt_docs_{ts_from_path(excel_path)}"
if out_dir.exists():
shutil.rmtree(out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
converter = mod.ExcelPromptConverter(
project_root=project_root,
prompt_library_dir=prompt_library_dir,
excel_path=excel_path,
category_name="prompt-category",
config_path=None,
output_root=out_dir,
)
converter.convert()
return out_dir
def run_docs_to_excel_for_dir(prompts_dir: Path, scripts_dir: Path, out_root: Path) -> Path:
docs2excel_path = scripts_dir / "docs_to_excel.py"
mod = load_module(docs2excel_path, "docs_to_excel")
# Determine timestamp from folder creation (or mtime fallback)
base_dir = out_root / "prompt_excel"
base_dir.mkdir(parents=True, exist_ok=True)
ts_fmt = ts_from_path(prompts_dir)
out_dir = base_dir / f"prompt_excel_{ts_fmt}"
out_dir.mkdir(parents=True, exist_ok=True)
out_path = out_dir / "rebuilt.xlsx"
# Resolve actual prompts root (support either the prompts/ subfolder or direct sheet folders)
prompts_root = prompts_dir / "prompts" if (prompts_dir / "prompts").exists() else prompts_dir
# Invoke module's main via argparse emulation
sys.argv = [str(docs2excel_path), "--prompts-dir", str(prompts_root), "--out", str(out_path)]
mod.main() # type: ignore
return out_dir
def find_xlsx_files(input_excel_dir: Path) -> List[Path]:
if not input_excel_dir.exists():
return []
return sorted([p for p in input_excel_dir.iterdir() if p.is_file() and p.suffix.lower() in {".xlsx"}], key=lambda p: p.stat().st_mtime)
def has_prompt_files(input_docs_dir: Path) -> bool:
if not input_docs_dir.exists():
return False
for p in input_docs_dir.rglob("*.md"):
if p.name.startswith("(") and ")_" in p.name:
return True
return False
def main() -> None:
parser = argparse.ArgumentParser(description="Start conversion between Excel and prompt docs")
parser.add_argument("--mode", choices=["auto", "excel2docs", "docs2excel"], default="auto")
parser.add_argument("--excel-dir", default="prompt_excel", help="Input directory containing .xlsx files")
parser.add_argument("--docs-dir", default="prompt_docs", help="Input directory containing prompt folders")
parser.add_argument("--select", type=str, default=None, help="Optional path to a specific Excel file or prompts folder to convert")
args = parser.parse_args()
script_path = Path(__file__).resolve()
prompt_library_dir = script_path.parent.parent # repo root (prompt-library)
project_root = prompt_library_dir # use prompt-library as root for I/O
input_excel_dir = (prompt_library_dir / args.excel_dir).resolve()
input_docs_dir = (prompt_library_dir / args.docs_dir).resolve()
ran_any = False
if args.mode in ("auto", "excel2docs"):
# If user explicitly selected a file, prefer it
if args.select:
sel = Path(args.select)
if not sel.is_absolute():
sel = (project_root / sel).resolve()
if sel.is_file() and sel.suffix.lower() == ".xlsx":
out_dir = run_excel_to_docs_for_file(sel, prompt_library_dir, project_root)
rel = out_dir.relative_to(prompt_library_dir)
print(f"✅ Excel→Docs OK: {sel.name}{rel}")
ran_any = True
else:
xlsx_files = find_xlsx_files(input_excel_dir)
for xlsx in xlsx_files:
out_dir = run_excel_to_docs_for_file(xlsx, prompt_library_dir, project_root)
rel = out_dir.relative_to(prompt_library_dir)
print(f"✅ Excel→Docs OK: {xlsx.name}{rel}")
ran_any = True
if args.mode in ("auto", "docs2excel"):
if args.select:
sel = Path(args.select)
if not sel.is_absolute():
sel = (project_root / sel).resolve()
if sel.exists() and sel.is_dir():
out_dir = run_docs_to_excel_for_dir(sel, prompt_library_dir / "scripts", project_root)
rel = out_dir.relative_to(prompt_library_dir)
# show sel relative as well when possible
try:
sel_rel = Path(sel).relative_to(prompt_library_dir)
except Exception:
sel_rel = Path(sel)
print(f"✅ Docs→Excel OK: {sel_rel}{rel}")
ran_any = True
else:
if has_prompt_files(input_docs_dir):
out_dir = run_docs_to_excel_for_dir(input_docs_dir, prompt_library_dir / "scripts", project_root)
rel = out_dir.relative_to(prompt_library_dir)
print(f"✅ Docs→Excel OK: {args.docs_dir}{rel}")
ran_any = True
if not ran_any:
print(" Nothing to do. Place .xlsx under ./prompt_excel or prompt docs under ./prompt_docs, or use --mode to force.")
if __name__ == "__main__":
main()