""" LAN file sync module. Detects file changes, copies from LAN UNC paths to local, and determines which tables need migration. """ import os import shutil def sync_file(local_path, lan_path): """Sync a single file from LAN to local. Returns: (status, error_msg) status: 'copied' | 'updated' | 'skipped' | 'error' """ try: if not os.path.exists(lan_path): return 'error', f'LAN path not accessible: {lan_path}' lan_mtime = os.path.getmtime(lan_path) local_existed = os.path.exists(local_path) if local_existed: local_mtime = os.path.getmtime(local_path) if local_mtime == lan_mtime: return 'skipped', None # Ensure parent directory exists os.makedirs(os.path.dirname(local_path), exist_ok=True) # Atomic copy: write to .tmp first, then os.replace tmp_path = local_path + '.tmp' try: shutil.copy2(lan_path, tmp_path) os.replace(tmp_path, local_path) except Exception: # Clean up temp file on failure if os.path.exists(tmp_path): os.remove(tmp_path) raise if not local_existed: return 'copied', None else: return 'updated', None except Exception as e: return 'error', str(e) def sync_all_files(cfg, excel_dir): """Sync all LAN source files to local. Returns: {rel_path: (status, error_msg)} """ lan_sources = cfg.get('lan_sources', {}) results = {} for rel_path, lan_path in lan_sources.items(): local_path = os.path.join(excel_dir, rel_path) status, error_msg = sync_file(local_path, lan_path) results[rel_path] = (status, error_msg) return results def determine_tables_to_migrate(cfg, sync_results): """Determine which tables need migration based on sync results. A table needs migration if ANY of its source files were copied or updated. Returns: set of table names that need migration """ tables_to_migrate = set() for table_cfg in cfg.get('tables', []): table_name = table_cfg['table'] for src in table_cfg['sources']: file_rel = src['file'] if file_rel in sync_results: status, _ = sync_results[file_rel] if status in ('copied', 'updated'): tables_to_migrate.add(table_name) break return tables_to_migrate