289 lines
7.8 KiB
Python
289 lines
7.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
원격 데이터베이스 동기화 모듈
|
|
로컬 SQLite와 원격 Supabase 간의 데이터 동기화를 관리합니다.
|
|
|
|
현재는 프로토타입으로 인터페이스만 정의되어 있으며,
|
|
추후 Supabase 연동 시 구현될 예정입니다.
|
|
"""
|
|
|
|
from abc import ABC, abstractmethod
|
|
from datetime import datetime
|
|
from typing import Optional, List, Dict, Any
|
|
from enum import Enum
|
|
|
|
from core.logger import get_logger
|
|
from core.signals import get_signals
|
|
|
|
# 로거 설정
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class SyncStatus(Enum):
|
|
"""동기화 상태"""
|
|
IDLE = "idle"
|
|
SYNCING = "syncing"
|
|
SUCCESS = "success"
|
|
FAILED = "failed"
|
|
OFFLINE = "offline"
|
|
|
|
|
|
class SyncDirection(Enum):
|
|
"""동기화 방향"""
|
|
UPLOAD = "upload" # 로컬 -> 원격
|
|
DOWNLOAD = "download" # 원격 -> 로컬
|
|
BOTH = "both" # 양방향
|
|
|
|
|
|
class BaseSyncManager(ABC):
|
|
"""
|
|
동기화 관리자 추상 클래스
|
|
|
|
원격 데이터베이스와의 동기화 인터페이스를 정의합니다.
|
|
"""
|
|
|
|
@abstractmethod
|
|
def connect(self) -> bool:
|
|
"""원격 데이터베이스에 연결합니다."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def disconnect(self):
|
|
"""연결을 종료합니다."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def is_connected(self) -> bool:
|
|
"""연결 상태를 확인합니다."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def sync_table(self, table_name: str, direction: SyncDirection) -> bool:
|
|
"""테이블을 동기화합니다."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def sync_all(self, direction: SyncDirection) -> bool:
|
|
"""모든 테이블을 동기화합니다."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_last_sync_time(self, table_name: str = None) -> Optional[datetime]:
|
|
"""마지막 동기화 시간을 반환합니다."""
|
|
pass
|
|
|
|
|
|
class LocalOnlySyncManager(BaseSyncManager):
|
|
"""
|
|
로컬 전용 동기화 관리자 (프로토타입)
|
|
|
|
원격 연결 없이 로컬 데이터베이스만 사용합니다.
|
|
Supabase 연동 전까지 이 클래스를 사용합니다.
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""초기화"""
|
|
self.signals = get_signals()
|
|
self._status = SyncStatus.OFFLINE
|
|
self._last_sync_time: Dict[str, datetime] = {}
|
|
logger.info("LocalOnlySyncManager 초기화 (원격 동기화 비활성화)")
|
|
|
|
def connect(self) -> bool:
|
|
"""
|
|
연결 시도 (항상 오프라인 상태)
|
|
|
|
Returns:
|
|
항상 False
|
|
"""
|
|
logger.info("원격 동기화가 비활성화되어 있습니다.")
|
|
self._status = SyncStatus.OFFLINE
|
|
return False
|
|
|
|
def disconnect(self):
|
|
"""연결 종료 (동작 없음)"""
|
|
pass
|
|
|
|
def is_connected(self) -> bool:
|
|
"""
|
|
연결 상태 확인
|
|
|
|
Returns:
|
|
항상 False
|
|
"""
|
|
return False
|
|
|
|
def sync_table(self, table_name: str, direction: SyncDirection) -> bool:
|
|
"""
|
|
테이블 동기화 (동작 없음)
|
|
|
|
Args:
|
|
table_name: 테이블 이름
|
|
direction: 동기화 방향
|
|
|
|
Returns:
|
|
항상 True (로컬 데이터는 항상 최신)
|
|
"""
|
|
self._last_sync_time[table_name] = datetime.now()
|
|
return True
|
|
|
|
def sync_all(self, direction: SyncDirection) -> bool:
|
|
"""
|
|
전체 동기화 (동작 없음)
|
|
|
|
Args:
|
|
direction: 동기화 방향
|
|
|
|
Returns:
|
|
항상 True
|
|
"""
|
|
logger.info("로컬 전용 모드: 동기화 건너뛰기")
|
|
return True
|
|
|
|
def get_last_sync_time(self, table_name: str = None) -> Optional[datetime]:
|
|
"""
|
|
마지막 동기화 시간 반환
|
|
|
|
Args:
|
|
table_name: 테이블 이름
|
|
|
|
Returns:
|
|
마지막 동기화 시간
|
|
"""
|
|
if table_name:
|
|
return self._last_sync_time.get(table_name)
|
|
return max(self._last_sync_time.values()) if self._last_sync_time else None
|
|
|
|
@property
|
|
def status(self) -> SyncStatus:
|
|
"""현재 동기화 상태"""
|
|
return self._status
|
|
|
|
|
|
class SupabaseSyncManager(BaseSyncManager):
|
|
"""
|
|
Supabase 동기화 관리자
|
|
|
|
추후 Supabase 연동 시 구현될 예정입니다.
|
|
|
|
Note:
|
|
이 클래스는 현재 스텁으로만 존재합니다.
|
|
실제 구현 시 supabase 패키지를 사용합니다.
|
|
"""
|
|
|
|
def __init__(self, url: str, key: str):
|
|
"""
|
|
초기화
|
|
|
|
Args:
|
|
url: Supabase 프로젝트 URL
|
|
key: Supabase API 키
|
|
"""
|
|
self.url = url
|
|
self.key = key
|
|
self.signals = get_signals()
|
|
self._client = None
|
|
self._status = SyncStatus.IDLE
|
|
self._last_sync_time: Dict[str, datetime] = {}
|
|
|
|
logger.info("SupabaseSyncManager 초기화 (추후 구현 예정)")
|
|
|
|
def connect(self) -> bool:
|
|
"""
|
|
Supabase에 연결합니다.
|
|
|
|
Returns:
|
|
연결 성공 여부
|
|
|
|
TODO:
|
|
supabase 패키지를 사용하여 실제 연결 구현
|
|
"""
|
|
# TODO: 실제 연결 구현
|
|
# from supabase import create_client
|
|
# self._client = create_client(self.url, self.key)
|
|
|
|
logger.warning("Supabase 연결 미구현")
|
|
return False
|
|
|
|
def disconnect(self):
|
|
"""연결을 종료합니다."""
|
|
self._client = None
|
|
self._status = SyncStatus.IDLE
|
|
|
|
def is_connected(self) -> bool:
|
|
"""연결 상태를 확인합니다."""
|
|
return self._client is not None
|
|
|
|
def sync_table(self, table_name: str, direction: SyncDirection) -> bool:
|
|
"""
|
|
테이블을 동기화합니다.
|
|
|
|
Args:
|
|
table_name: 테이블 이름
|
|
direction: 동기화 방향
|
|
|
|
Returns:
|
|
동기화 성공 여부
|
|
|
|
TODO:
|
|
- 마지막 동기화 시간 이후 변경된 레코드 조회
|
|
- 충돌 해결 로직 구현
|
|
- 배치 처리 구현
|
|
"""
|
|
# TODO: 실제 동기화 구현
|
|
logger.warning(f"테이블 동기화 미구현: {table_name}")
|
|
return False
|
|
|
|
def sync_all(self, direction: SyncDirection) -> bool:
|
|
"""
|
|
모든 테이블을 동기화합니다.
|
|
|
|
Args:
|
|
direction: 동기화 방향
|
|
|
|
Returns:
|
|
동기화 성공 여부
|
|
"""
|
|
tables = [
|
|
"instructions", "faults", "works", "miscs",
|
|
"daily_inspections", "todos", "memos"
|
|
]
|
|
|
|
success = True
|
|
for table in tables:
|
|
if not self.sync_table(table, direction):
|
|
success = False
|
|
|
|
if success:
|
|
self.signals.sync_completed.emit()
|
|
else:
|
|
self.signals.sync_error.emit("일부 테이블 동기화 실패")
|
|
|
|
return success
|
|
|
|
def get_last_sync_time(self, table_name: str = None) -> Optional[datetime]:
|
|
"""마지막 동기화 시간을 반환합니다."""
|
|
if table_name:
|
|
return self._last_sync_time.get(table_name)
|
|
return max(self._last_sync_time.values()) if self._last_sync_time else None
|
|
|
|
@property
|
|
def status(self) -> SyncStatus:
|
|
"""현재 동기화 상태"""
|
|
return self._status
|
|
|
|
|
|
def get_sync_manager() -> BaseSyncManager:
|
|
"""
|
|
동기화 관리자 인스턴스를 반환합니다.
|
|
|
|
현재는 LocalOnlySyncManager를 반환합니다.
|
|
추후 설정에 따라 SupabaseSyncManager를 반환할 수 있습니다.
|
|
|
|
Returns:
|
|
동기화 관리자 인스턴스
|
|
"""
|
|
# TODO: 설정에 따라 적절한 관리자 반환
|
|
return LocalOnlySyncManager()
|
|
|
|
|