import os import gradio as gr import git import tempfile import shutil from datetime import datetime from typing import List, Dict, Tuple, Optional import re class TranslationTracker: def __init__(self): self.repo_url = "https://github.com/huggingface/transformers.git" self.repo_path = None self.en_docs_path = None self.ko_docs_path = None self.repo = None def clone_repo(self, progress=gr.Progress()): """Transformers 레포지토리를 클론합니다.""" try: # 임시 디렉토리 생성 temp_dir = tempfile.mkdtemp() progress(0.1, desc="레포지토리 클론 준비 중...") # depth=1로 shallow clone 수행 (더 빠름) progress(0.2, desc="레포지토리 클론 중...") self.repo = git.Repo.clone_from(self.repo_url, temp_dir, depth=1) self.repo_path = temp_dir # 문서 경로 설정 self.en_docs_path = os.path.join(self.repo_path, "docs", "source", "en") self.ko_docs_path = os.path.join(self.repo_path, "docs", "source", "ko") progress(1.0, desc="레포지토리 클론 완료") return f"레포지토리 클론 완료: {self.repo_path}" except Exception as e: return f"레포지토리 클론 실패: {str(e)}" def cleanup(self): """임시 디렉토리 정리""" if self.repo_path and os.path.exists(self.repo_path): shutil.rmtree(self.repo_path) self.repo_path = None self.repo = None def get_last_commit_date(self, file_path: str) -> Optional[datetime]: """파일의 마지막 커밋 날짜를 가져옵니다.""" try: # 파일이 존재하지 않으면 None 반환 if not os.path.exists(file_path): return None # 파일의 상대 경로 구하기 rel_path = os.path.relpath(file_path, self.repo_path) # 마지막 커밋 정보 찾기 for commit in self.repo.iter_commits(paths=rel_path, max_count=1): return commit.committed_datetime return None except Exception: return None def get_translation_status(self, progress=gr.Progress()) -> List[Dict]: """모든 영어 문서와 해당하는 한글 번역 상태를 확인합니다.""" if not self.repo_path: return [{"error": "레포지토리가 클론되지 않았습니다. 먼저 클론을 수행하세요."}] results = [] en_md_files = [] # 영어 마크다운 파일 수집 progress(0.1, desc="영어 문서 스캔 중...") for root, _, files in os.walk(self.en_docs_path): for file in files: if file.endswith(".md"): en_md_files.append(os.path.join(root, file)) total_files = len(en_md_files) # 각 파일에 대한 번역 상태 확인 for i, en_file in enumerate(en_md_files): progress((i + 1) / total_files, desc=f"번역 상태 확인 중 ({i+1}/{total_files})...") # 상대 경로 계산 (docs/source/en 이후 부분) rel_path = os.path.relpath(en_file, self.en_docs_path) ko_file = os.path.join(self.ko_docs_path, rel_path) # 파일 이름 추출 file_name = os.path.basename(en_file) # 영어 문서 마지막 커밋 날짜 en_commit_date = self.get_last_commit_date(en_file) # 번역 문서 마지막 커밋 날짜 ko_commit_date = self.get_last_commit_date(ko_file) # 번역 상태 결정 if ko_commit_date is None: status = "미번역" outdate = False else: if en_commit_date > ko_commit_date: status = "번역됨 (업데이트 필요)" outdate = True else: status = "번역됨 (최신)" outdate = False # 결과 추가 results.append({ "file_name": file_name, "en_path": rel_path, "ko_path": rel_path if os.path.exists(ko_file) else "없음", "en_last_commit": en_commit_date.strftime("%Y-%m-%d %H:%M:%S") if en_commit_date else "알 수 없음", "ko_last_commit": ko_commit_date.strftime("%Y-%m-%d %H:%M:%S") if ko_commit_date else "없음", "status": status, "outdate": outdate }) # 파일명으로 정렬 results.sort(key=lambda x: x["file_name"]) return results def create_ui(): """Gradio UI 생성""" tracker = TranslationTracker() with gr.Blocks(title="Transformers 문서 번역 추적기") as app: gr.Markdown("# Transformers 문서 번역 추적기") gr.Markdown("HuggingFace Transformers 레포지토리의 문서 번역 상태를 확인합니다.") with gr.Row(): clone_btn = gr.Button("레포지토리 클론", variant="primary") status_btn = gr.Button("번역 상태 확인", variant="secondary") cleanup_btn = gr.Button("정리", variant="stop") status_output = gr.Textbox(label="상태") with gr.Tabs(): with gr.TabItem("모든 문서"): all_table = gr.DataFrame( headers=["파일명", "영어 경로", "한글 경로", "영어 마지막 커밋", "한글 마지막 커밋", "상태"], datatype=["str", "str", "str", "str", "str", "str"], label="번역 상태" ) with gr.TabItem("번역 필요"): untranslated_table = gr.DataFrame( headers=["파일명", "영어 경로", "상태"], datatype=["str", "str", "str"], label="번역이 필요한 문서" ) with gr.TabItem("업데이트 필요"): outdated_table = gr.DataFrame( headers=["파일명", "영어 경로", "영어 마지막 커밋", "한글 마지막 커밋"], datatype=["str", "str", "str", "str"], label="업데이트가 필요한 문서" ) # 이벤트 핸들러 clone_btn.click(tracker.clone_repo, outputs=status_output) def process_translation_status(): results = tracker.get_translation_status() if results and "error" in results[0]: return status_output.update(results[0]["error"]), None, None, None # 전체 데이터 all_data = [[r["file_name"], r["en_path"], r["ko_path"], r["en_last_commit"], r["ko_last_commit"], r["status"]] for r in results] # 미번역 데이터 untranslated = [[r["file_name"], r["en_path"], r["status"]] for r in results if r["status"] == "미번역"] # 업데이트 필요 데이터 outdated = [[r["file_name"], r["en_path"], r["en_last_commit"], r["ko_last_commit"]] for r in results if r["outdate"]] return "번역 상태 조회 완료", all_data, untranslated, outdated status_btn.click( process_translation_status, outputs=[status_output, all_table, untranslated_table, outdated_table] ) cleanup_btn.click( lambda: (tracker.cleanup(), "정리 완료"), outputs=status_output ) return app def main(): app = create_ui() app.launch() if __name__ == "__main__": main()