| 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="레포지토리 클론 준비 중...") |
| |
| |
| 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: |
| |
| 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})...") |
| |
| |
| 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() |