Files
Excel-to-SQL_Server/sync.py
Misaka_Company d224b98893 feat: unified logging module and remove redundant first_run logic
- Add logger.py with TimedRotatingFileHandler (daily rotation, 30-day retention)
- Replace all print() in migrate.py with logging calls
- Add logging to sync.py for sync status tracking
- Refactor watch.py to use logger.py, remove duplicate first_run branch
- All logs now go to logs/app.log consistently
- Add logging config section to config.yaml, add logs/ to .gitignore

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

98 lines
2.9 KiB
Python

"""
LAN file sync module.
Detects file changes, copies from LAN UNC paths to local, and determines which tables need migration.
"""
import logging
import os
import shutil
logger = logging.getLogger('app')
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):
logger.error(f"LAN 路径不可达: {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:
logger.debug(f"跳过 (mtime一致): {os.path.basename(local_path)}")
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:
logger.info(f"copied: {os.path.basename(local_path)}")
return 'copied', None
else:
logger.info(f"updated: {os.path.basename(local_path)}")
return 'updated', None
except Exception as e:
logger.error(f"同步失败: {local_path} - {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