Files
FCCS/fccs_check.py
2026-07-11 12:27:40 -05:00

99 lines
3.2 KiB
Python

"""
Utility: Check whether specific drawers actually finished exporting.
Interactive — prompts for one or more drawer IDs, then for each drawer compares
its manifest (the documents FCCS said it would export, captured during Step 2)
against the files actually sitting in the export folder, and reports any
missing documents.
Handles two quirks of the FCCS export (see fccs_config.evaluate_drawer):
1. Page-splitting: a single manifest document (e.g. "Donations") is exported
as one file if it's a single page, or as "Donations Page 1",
"Donations Page 2", ... for a multi-page document. All of those count as
that one document being present.
2. Filename sanitization: FCCS strips characters that are illegal in Windows
filenames (: / \\ ? * " < > |) from document titles, so comparison is done
on a normalized key so these still match.
fccs_verify.py applies this same logic in batch across every drawer; this tool
is for spot-checking specific drawers (e.g. ones the log marked failed).
USAGE
-----
python fccs_check.py
Drawer ID(s): 08097 18430
"""
import os
import re
import sys
from fccs_config import (
parse_args, load_config, evaluate_drawer, index_files_by_drawer,
)
def report_drawer(drawer_id, files, manifest_dir, out):
"""Evaluate one drawer and print a human-readable completeness report."""
r = evaluate_drawer(drawer_id, files, manifest_dir)
out("")
if not r["has_manifest"]:
out(f"[{drawer_id}] NO MANIFEST at {r['manifest_path']} — cannot verify "
"(was this drawer exported by the tool?)")
return
expected = r["expected"]
missing = r["missing"]
out(f"[{drawer_id}] manifest lists {len(expected)} document(s); "
f"{r['file_count']} file(s) in export folder.")
if missing:
out(f" INCOMPLETE — {len(missing)} document(s) missing from export:")
for m in missing:
out(f" - {m}")
else:
out(f" COMPLETE — all {len(expected)} manifest document(s) present.")
if r["extras"]:
out(f" Note: {len(r['extras'])} exported document(s) not in the manifest:")
for e in r["extras"]:
out(f" - {e}")
if r["unparsed"]:
out(f" Note: {r['unparsed']} file(s) had no recognizable date anchor "
"and were skipped.")
def main():
args = parse_args()
cfg = load_config(args.config)
export_dir = cfg.get("paths", "export_dir")
manifest_dir = cfg.get("paths", "manifest_dir")
if not os.path.isdir(export_dir):
print(f"ERROR: export directory not found: {export_dir}")
sys.exit(1)
files_by_drawer = index_files_by_drawer(export_dir)
print("FCCS export completeness check")
print(f" export folder : {export_dir}")
print(f" manifests : {manifest_dir}")
print("Enter drawer ID(s) separated by spaces or commas (blank to quit).")
while True:
try:
raw = input("\nDrawer ID(s): ").strip()
except EOFError:
break
if not raw:
break
ids = [i for i in re.split(r"[\s,]+", raw) if i]
for drawer_id in ids:
report_drawer(drawer_id, files_by_drawer.get(drawer_id, []),
manifest_dir, print)
if __name__ == "__main__":
main()