import time import os from watchdog.observers import Observer from watchdog.observers.polling import PollingObserver # 필요시 사용 from watchdog.events import FileSystemEventHandler import threading import pos_update_bill import pos_update_daily_product DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../data')) FILE_EXTENSIONS = ('.xls', '.xlsx') BILL_PREFIX = "영수증별매출상세현황" DAILY_PRODUCT_PREFIX = "일자별 (상품별)" class NewFileHandler(FileSystemEventHandler): def __init__(self): super().__init__() self._lock = threading.Lock() self._processing_files = set() def on_created(self, event): self._handle_event(event) def on_moved(self, event): # moved event는 dest_path 사용 self._handle_event(event, moved=True) def on_modified(self, event): self._handle_event(event) def _handle_event(self, event, moved=False): if event.is_directory: return filepath = event.dest_path if moved else event.src_path filename = os.path.basename(filepath) if not filename.endswith(FILE_EXTENSIONS): return if filename.startswith(BILL_PREFIX) or filename.startswith(DAILY_PRODUCT_PREFIX): with self._lock: if filename in self._processing_files: print(f"[WATCHER] {filename} 이미 처리 중") return self._processing_files.add(filename) threading.Thread(target=self.process_file, args=(filepath, filename), daemon=True).start() def process_file(self, filepath, filename): try: # 파일 완전 생성/복사 대기 (파일 크기 변화 없을 때까지 기다림) if not self.wait_for_file_stable(filepath): print(f"[WATCHER] 파일 안정화 실패, 처리 중단: {filename}") return print(f"[WATCHER] 파일 처리 시작: {filename}") if filename.startswith(BILL_PREFIX): pos_update_bill.main() elif filename.startswith(DAILY_PRODUCT_PREFIX): pos_update_daily_product.main() else: print(f"[WATCHER] 처리 대상이 아님: {filename}") return except Exception as e: print(f"[WATCHER] 처리 중 오류 발생: {filename} / {e}") else: try: os.remove(filepath) print(f"[WATCHER] 파일 처리 완료 및 삭제: {filename}") except Exception as e: print(f"[WATCHER] 파일 삭제 실패: {filename} / {e}") finally: with self._lock: self._processing_files.discard(filename) def wait_for_file_stable(self, filepath, wait_seconds=10, interval=1): """ 파일 크기가 interval 간격으로 변하지 않으면 안정화된 것으로 판단 """ last_size = -1 stable_count = 0 required_stable_count = int(wait_seconds / interval) for _ in range(required_stable_count): try: current_size = os.path.getsize(filepath) except FileNotFoundError: # 파일이 없어지면 중단 return False if current_size == last_size: stable_count += 1 if stable_count >= required_stable_count: return True else: stable_count = 0 last_size = current_size time.sleep(interval) return False def start_watching(): print(f"[WATCHER] '{DATA_DIR}' 폴더 감시 시작") event_handler = NewFileHandler() # 기본 Observer 대신 PollingObserver로 변경 가능 (Docker 환경에서 안정적) #observer = Observer() observer = PollingObserver() # 필요시 이걸로 바꿔서 사용 observer.schedule(event_handler, DATA_DIR, recursive=False) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: print("[WATCHER] 감시 종료 요청 수신, 종료 중...") observer.stop() observer.join() if __name__ == "__main__": start_watching()