179 lines
5.7 KiB
Python
Executable File
179 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Recover missing camera4.json files for downloaded standard-path issue data."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
FILE = Path(__file__).resolve()
|
|
ROOT = FILE.parents[2]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.append(str(ROOT))
|
|
|
|
from tools.feishu_project.case_calib_recovery import CalibRecoveryResult, recover_camera4_json
|
|
|
|
|
|
DEFAULT_DOWNLOAD_ROOT = Path("/data1/dongying/Mono3d/G1Q3/feishu_project/downloaded_issue_data")
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--download-root",
|
|
default=str(DEFAULT_DOWNLOAD_ROOT),
|
|
help="Root directory containing downloaded issue data.",
|
|
)
|
|
parser.add_argument(
|
|
"--issue-id",
|
|
action="append",
|
|
dest="issue_ids",
|
|
type=int,
|
|
help="Only process the specified issue id. Can be repeated.",
|
|
)
|
|
parser.add_argument(
|
|
"--path-root",
|
|
action="append",
|
|
default=[],
|
|
help=(
|
|
"Optional explicit copied target root to recover. Can be repeated. "
|
|
"Example: downloaded_issue_data/issue_x/path_01/case_dir"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--manifest-path",
|
|
default="",
|
|
help="Optional explicit manifest path. Defaults to <download-root>/calib_recovery_manifest.json",
|
|
)
|
|
parser.add_argument("--dry-run", action="store_true")
|
|
return parser.parse_args()
|
|
|
|
|
|
def log_progress(message: str) -> None:
|
|
timestamp = datetime.now().astimezone().strftime("%Y-%m-%d %H:%M:%S")
|
|
print(f"[recover_downloaded_issue_calibs {timestamp}] {message}", flush=True)
|
|
|
|
|
|
def discover_target_roots(download_root: Path, issue_ids: list[int], explicit_roots: list[str]) -> list[Path]:
|
|
if explicit_roots:
|
|
return sorted({Path(path).resolve() for path in explicit_roots})
|
|
|
|
issue_dirs: list[Path]
|
|
if issue_ids:
|
|
issue_dirs = [download_root / f"issue_{issue_id}" for issue_id in sorted(set(issue_ids))]
|
|
else:
|
|
issue_dirs = sorted(path for path in download_root.glob("issue_*") if path.is_dir())
|
|
|
|
targets: list[Path] = []
|
|
seen: set[Path] = set()
|
|
for issue_dir in issue_dirs:
|
|
if not issue_dir.is_dir():
|
|
continue
|
|
for path_dir in sorted(path for path in issue_dir.glob("path_*") if path.is_dir()):
|
|
direct_case = path_dir / "sigmastar.1" / "camera4.bin"
|
|
if direct_case.is_file() and path_dir not in seen:
|
|
seen.add(path_dir)
|
|
targets.append(path_dir)
|
|
|
|
for child in sorted(path_dir.iterdir()):
|
|
if not child.is_dir() or child.name == "test_data":
|
|
continue
|
|
if child in seen:
|
|
continue
|
|
seen.add(child)
|
|
targets.append(child)
|
|
return targets
|
|
|
|
|
|
def result_to_dict(target_root: Path, result: CalibRecoveryResult) -> dict:
|
|
return {
|
|
"target_root": str(target_root),
|
|
"target_path": str(result.target_path),
|
|
"status": result.status,
|
|
"detail": result.detail,
|
|
"source_path": None if result.source_path is None else str(result.source_path),
|
|
"source_kind": result.source_kind,
|
|
}
|
|
|
|
|
|
def build_manifest(
|
|
download_root: Path,
|
|
dry_run: bool,
|
|
issue_ids: list[int],
|
|
target_roots: list[Path],
|
|
results: list[dict],
|
|
) -> dict:
|
|
summary: dict[str, int] = {}
|
|
for result in results:
|
|
status = result["status"]
|
|
summary[status] = summary.get(status, 0) + 1
|
|
|
|
return {
|
|
"generated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
|
|
"download_root": str(download_root),
|
|
"dry_run": dry_run,
|
|
"issue_filter": issue_ids,
|
|
"target_roots": [str(path) for path in target_roots],
|
|
"summary": summary,
|
|
"results": results,
|
|
}
|
|
|
|
|
|
def print_summary(manifest: dict) -> None:
|
|
print(f"download_root: {manifest['download_root']}")
|
|
print(f"dry_run: {manifest['dry_run']}")
|
|
print(f"target_roots: {len(manifest['target_roots'])}")
|
|
for status, count in sorted(manifest["summary"].items()):
|
|
print(f"{status}: {count}")
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
download_root = Path(args.download_root).resolve()
|
|
manifest_path = (
|
|
Path(args.manifest_path).resolve()
|
|
if args.manifest_path
|
|
else download_root / "calib_recovery_manifest.json"
|
|
)
|
|
target_roots = discover_target_roots(download_root, args.issue_ids or [], args.path_root)
|
|
log_progress(f"targets_to_process: {len(target_roots)}")
|
|
|
|
results: list[dict] = []
|
|
for index, target_root in enumerate(target_roots, start=1):
|
|
log_progress(f"[{index}/{len(target_roots)}] start: {target_root}")
|
|
recovery = recover_camera4_json(
|
|
source_root=target_root,
|
|
target_root=target_root,
|
|
dry_run=args.dry_run,
|
|
)
|
|
results.append(result_to_dict(target_root, recovery))
|
|
log_progress(f"[{index}/{len(target_roots)}] done: {recovery.status}")
|
|
|
|
manifest = build_manifest(
|
|
download_root=download_root,
|
|
dry_run=args.dry_run,
|
|
issue_ids=args.issue_ids or [],
|
|
target_roots=target_roots,
|
|
results=results,
|
|
)
|
|
if not args.dry_run:
|
|
manifest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
manifest_path.write_text(
|
|
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
print_summary(manifest)
|
|
if args.dry_run:
|
|
print(f"manifest (not written in dry-run): {manifest_path}")
|
|
else:
|
|
print(f"manifest: {manifest_path}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|