119 lines
3.4 KiB
Python
119 lines
3.4 KiB
Python
"""
|
|
Step 4 (optional): Verify exported files against manifests.
|
|
|
|
Compares the per-drawer manifests captured during export (Step 2)
|
|
against the actual files in the export directory to identify
|
|
missing or extra files.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
from fccs_config import parse_args, load_config, make_logger
|
|
|
|
|
|
def load_manifest(path):
|
|
"""Load a manifest file and return the list of document rows."""
|
|
items = []
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
for line in f:
|
|
line = line.rstrip("\n")
|
|
if line:
|
|
items.append(line.split("\t"))
|
|
return items
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
cfg = load_config(args.config)
|
|
log = make_logger(cfg.get("paths", "log_file"))
|
|
|
|
export_dir = cfg.get("paths", "export_dir")
|
|
manifest_dir = cfg.get("paths", "manifest_dir")
|
|
|
|
if not os.path.isdir(manifest_dir):
|
|
log(f"ERROR: manifest directory not found: {manifest_dir}")
|
|
log("Run fccs_export.py first to generate manifests.")
|
|
sys.exit(1)
|
|
|
|
if not os.path.isdir(export_dir):
|
|
log(f"ERROR: export directory not found: {export_dir}")
|
|
sys.exit(1)
|
|
|
|
# Load all manifests
|
|
manifests = {}
|
|
for fname in sorted(os.listdir(manifest_dir)):
|
|
if fname.endswith(".txt"):
|
|
drawer_id = os.path.splitext(fname)[0]
|
|
items = load_manifest(os.path.join(manifest_dir, fname))
|
|
manifests[drawer_id] = items
|
|
|
|
if not manifests:
|
|
log("No manifest files found.")
|
|
sys.exit(1)
|
|
|
|
# Index exported files by drawer ID prefix
|
|
exported_by_drawer = {}
|
|
all_exported = [
|
|
f for f in os.listdir(export_dir)
|
|
if os.path.isfile(os.path.join(export_dir, f))
|
|
]
|
|
|
|
for fname in all_exported:
|
|
sep = fname.find("_")
|
|
if sep != -1:
|
|
did = fname[:sep]
|
|
exported_by_drawer.setdefault(did, []).append(fname)
|
|
|
|
log("=" * 60)
|
|
log("Export Verification Report")
|
|
log("=" * 60)
|
|
log(f"Manifests loaded : {len(manifests)} drawers")
|
|
log(f"Exported files : {len(all_exported)} total")
|
|
log("")
|
|
|
|
total_expected = 0
|
|
total_actual = 0
|
|
mismatched = []
|
|
|
|
for drawer_id in sorted(manifests):
|
|
expected = manifests[drawer_id]
|
|
actual = exported_by_drawer.get(drawer_id, [])
|
|
n_expected = len(expected)
|
|
n_actual = len(actual)
|
|
total_expected += n_expected
|
|
total_actual += n_actual
|
|
|
|
if n_expected == n_actual:
|
|
log(f" {drawer_id}: OK ({n_actual} files)")
|
|
else:
|
|
diff = n_actual - n_expected
|
|
sign = "+" if diff > 0 else ""
|
|
log(f" {drawer_id}: MISMATCH — expected {n_expected}, "
|
|
f"got {n_actual} ({sign}{diff})")
|
|
mismatched.append(drawer_id)
|
|
|
|
# Check for exported files with no manifest
|
|
orphan_drawers = set(exported_by_drawer.keys()) - set(manifests.keys())
|
|
orphan_count = sum(len(exported_by_drawer[d]) for d in orphan_drawers)
|
|
|
|
log("")
|
|
log("-" * 60)
|
|
log(f"Expected total : {total_expected}")
|
|
log(f"Actual total : {total_actual}")
|
|
|
|
if orphan_drawers:
|
|
log(f"No manifest for : {', '.join(sorted(orphan_drawers))} "
|
|
f"({orphan_count} files)")
|
|
|
|
if mismatched:
|
|
log(f"Mismatched drawers: {', '.join(mismatched)}")
|
|
elif not orphan_drawers:
|
|
log("All drawers match.")
|
|
|
|
log("=" * 60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|