Source: scripts/repair_dl3dv_frame_download.py · Plain text

scripts/repair_dl3dv_frame_download.py

"""Supplement missing protocol frames, retaining the original download evidence."""

from __future__ import annotations

import argparse
import hashlib
import json
from pathlib import Path

from mcss.data.dl3dv_download import (
    REVISION,
    atomic_write_json,
    download_selected,
    load_inventory_artifacts,
    protocol_frame_selection,
    select_files,
    write_inventory_artifacts,
)

PROJECT = Path(__file__).resolve().parents[1]


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--previous", type=Path, required=True)
    parser.add_argument("--output", type=Path, required=True)
    parser.add_argument("--data-root", type=Path, required=True)
    parser.add_argument("--protocol-json", type=Path, required=True)
    args = parser.parse_args()
    output = args.output.resolve()
    output.relative_to(PROJECT / "outputs")
    output.mkdir(parents=True, exist_ok=False)
    old_status_path = args.previous / "status.json"
    old_status = json.loads(old_status_path.read_text(encoding="utf-8"))
    old_verification_path = args.previous / "completion_verification.json"
    old_verification = json.loads(old_verification_path.read_text(encoding="utf-8"))
    if old_status.get("status") != "complete" or old_status.get("revision") != REVISION:
        raise ValueError("previous download must be complete and use the pinned revision")
    if old_verification.get("status") != "verified":
        raise ValueError("previous file-integrity verification is absent")
    inventory = load_inventory_artifacts(args.previous)
    frames = protocol_frame_selection(
        args.protocol_json, inventory, data_root=args.data_root, target_every=8,
    )
    selected = select_files(
        inventory, args.data_root, include_images4=True, frame_ids_by_scene=frames,
    )
    if len(selected) != 12777 or sum(x.remote.size for x in selected) != 11313259491:
        raise ValueError("corrected protocol selection differs from the audited selection")
    old_by_path = {entry["repo_path"]: entry for entry in old_status["entries"]}
    old_verified = {entry["repo_path"]: entry for entry in old_verification["entries"]}
    required = {x.remote.path for x in selected}
    missing = [x for x in selected if not x.destination.is_file()]
    if len(missing) != 281 or sum(x.remote.size for x in missing) != 245981462:
        raise ValueError("missing files differ from the audited 281-file supplement")
    plan = {
        "reason": "protocol frame positions were incorrectly mapped to filename index + 1",
        "revision": REVISION,
        "mapping": "transforms_frame_order",
        "required_file_count": len(selected),
        "required_bytes": sum(x.remote.size for x in selected),
        "missing_file_count": len(missing),
        "missing_bytes": sum(x.remote.size for x in missing),
        "previous_status_sha256": hashlib.sha256(old_status_path.read_bytes()).hexdigest(),
        "previous_verification_sha256": hashlib.sha256(
            old_verification_path.read_bytes()
        ).hexdigest(),
        "retained_unused_repo_paths": sorted(set(old_by_path) - required),
        "missing_repo_paths": [x.remote.path for x in missing],
        "independent_verification": "pending",
    }
    atomic_write_json(output / "repair_plan.json", plan)
    write_inventory_artifacts(inventory, output)
    print(json.dumps({k: v for k, v in plan.items() if not isinstance(v, list)}), flush=True)
    supplement = download_selected(
        missing, status_path=output / "supplement_status.json",
        lock_path=args.previous / "download.lock", workers=4, max_attempts=3,
    )
    downloaded = {entry["repo_path"]: entry for entry in supplement["entries"]}
    entries = []
    for item in selected:
        path = item.remote.path
        if path in downloaded:
            entry = downloaded[path]
        else:
            entry = dict(old_by_path[path])
            if (
                entry.get("status") not in {"reused", "downloaded"}
                or entry.get("sha256") != old_verified[path]["local_sha256"]
            ):
                raise ValueError(f"prior status and prior verifier disagree: {path}")
            entry["status"] = "reused"
        entries.append(entry)
    # This records transfer completion only. The separate verifier re-hashes
    # every required and retained file before any benchmark restart.
    atomic_write_json(output / "status.json", {
        "schema": "mcss.dl3dv.download.v1", "status": "complete",
        "revision": REVISION, "entries": entries, "repair_plan": str(output / "repair_plan.json"),
        "independent_verification": "pending",
    })
    print(json.dumps({"status": "complete", "supplement_files": len(missing)}), flush=True)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())