Files
Excel-to-SQL_Server/sync.py
Misaka_Company 7edbd987f0 feat: add scheduled sync daemon and TRUNCATE migration mode
- Add sync.py for LAN file sync with atomic writes and mtime-based change detection
- Add watch.py daemon that periodically syncs files and migrates only changed tables
- Add TRUNCATE mode to migrate.py (--truncate flag) to preserve table structure
- Update config.yaml schema with check_interval_minutes and lan_sources
- Update README with new features documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-01 13:29:23 +08:00

90 lines
2.5 KiB
Python

"""
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