""" Step 1: Scan a restored FCCS backup directory for drawer IDs. Every subdirectory in the backup root corresponds to a drawer. Writes the sorted list of drawer IDs to the configured drawer_id_file. """ import os import sys from fccs_config import ( parse_args, load_config, make_logger, load_lines, check_for_clashes, ) _IGNORE_HEADER = [ "# Drawer IDs to SKIP during export (one ID per line).", "# Lines starting with # are comments and are ignored.", "#", "# Add password-protected or otherwise-excluded drawers here.", "#", "# The IDs auto-added below are prefix clashes: searching the base ID in", "# FCCS pops a selection box that breaks the plain export. Only the base", "# (shorter) ID is listed — the longer, more-specific IDs export normally.", "# The base IDs are exported separately by fccs_export_clashes.py, which", "# handles the selection box. DELETE or comment out any you'd rather skip.", "", ] def update_ignore_file(ignore_file, clashes, log): """Create ignore.txt if missing and seed it with clash base (prefix) IDs. Only the shorter (prefix) ID of each clash is added — that's the one that triggers the FCCS selection box and needs special handling. The longer, more-specific IDs (e.g. '02218A') search fine and export normally. Never overwrites existing entries — only appends prefixes not already present. Returns the list of IDs added. """ existing = set(load_lines(ignore_file)) file_exists = os.path.exists(ignore_file) to_add = [(short, matches) for short, matches in clashes if short not in existing] # Existing file already covers every clash — leave it untouched. if file_exists and not to_add: return [] lines = [] if not file_exists: lines.extend(_IGNORE_HEADER) for short, matches in to_add: lines.append(f"# clashes with: {', '.join(matches)}") lines.append(short) mode = "a" if file_exists else "w" with open(ignore_file, mode, encoding="utf-8") as f: if file_exists: f.write("\n") # separate the new block from prior content f.write("\n".join(lines) + "\n") added = [short for short, _ in to_add] if not file_exists: log(f"Created ignore file: {ignore_file}") if added: log(f"Added {len(added)} clash base ID(s) to ignore list: " f"{', '.join(added)}") return added def main(): args = parse_args() cfg = load_config(args.config) log = make_logger(cfg.get("paths", "log_file")) backup_dir = cfg.get("paths", "backup_dir") output_file = cfg.get("paths", "drawer_id_file") ignore_file = cfg.get("paths", "ignore_file") if not os.path.isdir(backup_dir): log(f"ERROR: backup directory not found: {backup_dir}") sys.exit(1) # Only subdirectories are drawers. When pointed at FCCS's live data dir # (e.g. the Restore directory) there are also system folders that start with # "$" and miscellaneous loose files — skip both. The isdir check drops the # files; the "$" prefix check drops the system folders. drawer_ids = sorted( name for name in os.listdir(backup_dir) if not name.startswith("$") and os.path.isdir(os.path.join(backup_dir, name)) ) with open(output_file, "w", encoding="utf-8") as f: for did in drawer_ids: f.write(did + "\n") log(f"Found {len(drawer_ids)} drawers, written to {output_file}") # Flag prefix clashes: FCCS search on the shorter ID pops a selection box. clashes = check_for_clashes(drawer_ids) if clashes: log("-" * 60) log(f"WARNING: {len(clashes)} potential drawer ID clash(es) found.") log("Searching the shorter ID in FCCS may show a selection box " "instead of navigating directly, which breaks the export.") for short, matches in clashes: log(f" {short} -> also matches: {', '.join(matches)}") # Seed ignore.txt with the clash prefixes for review (never overwrites). update_ignore_file(ignore_file, clashes, log) log(f"Review the ignore list: {ignore_file}") log("-" * 60) else: log("No drawer ID prefix clashes found.") # Report which drawers will be skipped by the export. ignored = set(load_lines(ignore_file)) if ignored: present = sorted(ignored & set(drawer_ids)) log(f"Ignore list ({ignore_file}): {len(ignored)} IDs listed, " f"{len(present)} present in this backup — these will be SKIPPED " f"by fccs_export.py: {', '.join(present) if present else '(none present)'}") if __name__ == "__main__": main()