mirror of
https://github.com/tradecatlabs/vibe-coding-cn.git
synced 2026-07-28 11:17:46 +00:00
189 lines
7.5 KiB
Python
189 lines
7.5 KiB
Python
|
|
#!/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()
|