Upload 3 files
Browse files- README.md +41 -13
- main.py +198 -0
- requirements.txt +2 -0
README.md
CHANGED
@@ -1,13 +1,41 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Transformers 문서 번역 추적기
|
2 |
+
|
3 |
+
HuggingFace Transformers 레포지토리의 문서 번역 상태를 추적하는 Gradio 애플리케이션입니다.
|
4 |
+
|
5 |
+
## 기능
|
6 |
+
|
7 |
+
- Transformers 레포지토리 자동 클론
|
8 |
+
- 영어 문서와 한국어 번역 문서 비교
|
9 |
+
- 번역 상태 확인: 미번역, 번역됨, 업데이트 필요
|
10 |
+
- 번역이 필요한 문서와 업데이트가 필요한 문서 목록 제공
|
11 |
+
|
12 |
+
## 번역 상태 판단 기준
|
13 |
+
|
14 |
+
1. 문서 원문 경로: `docs/source/en/<title>.md`
|
15 |
+
2. 번역 문서 경로: `docs/source/ko/<title>.md`
|
16 |
+
3. 번역 문서가 존재하지 않으면 미번역
|
17 |
+
4. 번역 문서가 존재하면 번역됨
|
18 |
+
5. 원본 문서의 커밋 시간이 번역 문서의 커밋 시간보다 최신이면 업데이트 필요
|
19 |
+
|
20 |
+
## 설치 및 실행
|
21 |
+
|
22 |
+
```bash
|
23 |
+
# 의존성 설치
|
24 |
+
pip install -e .
|
25 |
+
|
26 |
+
# 애플리케이션 실행
|
27 |
+
python main.py
|
28 |
+
```
|
29 |
+
|
30 |
+
## 사용법
|
31 |
+
|
32 |
+
1. "레포지토리 클론" 버튼을 클릭하여 Transformers 레포지토리를 클론합니다.
|
33 |
+
2. "번역 상태 확인" 버튼을 클릭하여 번역 상태를 확인합니다.
|
34 |
+
3. 탭을 전환하여 전체 문서, 번역 필요, 업데이트 필요 문서를 확인합니다.
|
35 |
+
4. 작업 완료 후 "정리" 버튼을 클릭하여 임시 파일을 정리합니다.
|
36 |
+
|
37 |
+
## 요구사항
|
38 |
+
|
39 |
+
- Python 3.11 이상
|
40 |
+
- Gradio 4.0.0 이상
|
41 |
+
- GitPython 3.1.0 이상
|
main.py
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import gradio as gr
|
3 |
+
import git
|
4 |
+
import tempfile
|
5 |
+
import shutil
|
6 |
+
from datetime import datetime
|
7 |
+
from typing import List, Dict, Tuple, Optional
|
8 |
+
import re
|
9 |
+
|
10 |
+
class TranslationTracker:
|
11 |
+
def __init__(self):
|
12 |
+
self.repo_url = "https://github.com/huggingface/transformers.git"
|
13 |
+
self.repo_path = None
|
14 |
+
self.en_docs_path = None
|
15 |
+
self.ko_docs_path = None
|
16 |
+
self.repo = None
|
17 |
+
|
18 |
+
def clone_repo(self, progress=gr.Progress()):
|
19 |
+
"""Transformers 레포지토리를 클론합니다."""
|
20 |
+
try:
|
21 |
+
# 임시 디렉토리 생성
|
22 |
+
temp_dir = tempfile.mkdtemp()
|
23 |
+
progress(0.1, desc="레포지토리 클론 준비 중...")
|
24 |
+
|
25 |
+
# depth=1로 shallow clone 수행 (더 빠름)
|
26 |
+
progress(0.2, desc="레포지토리 클론 중...")
|
27 |
+
self.repo = git.Repo.clone_from(self.repo_url, temp_dir, depth=1)
|
28 |
+
self.repo_path = temp_dir
|
29 |
+
|
30 |
+
# 문서 경로 설정
|
31 |
+
self.en_docs_path = os.path.join(self.repo_path, "docs", "source", "en")
|
32 |
+
self.ko_docs_path = os.path.join(self.repo_path, "docs", "source", "ko")
|
33 |
+
|
34 |
+
progress(1.0, desc="레포지토리 클론 완료")
|
35 |
+
return f"레포지토리 클론 완료: {self.repo_path}"
|
36 |
+
except Exception as e:
|
37 |
+
return f"레포지토리 클론 실패: {str(e)}"
|
38 |
+
|
39 |
+
def cleanup(self):
|
40 |
+
"""임시 디렉토리 정리"""
|
41 |
+
if self.repo_path and os.path.exists(self.repo_path):
|
42 |
+
shutil.rmtree(self.repo_path)
|
43 |
+
self.repo_path = None
|
44 |
+
self.repo = None
|
45 |
+
|
46 |
+
def get_last_commit_date(self, file_path: str) -> Optional[datetime]:
|
47 |
+
"""파일의 마지막 커밋 날짜를 가져옵니다."""
|
48 |
+
try:
|
49 |
+
# 파일이 존재하지 않으면 None 반환
|
50 |
+
if not os.path.exists(file_path):
|
51 |
+
return None
|
52 |
+
|
53 |
+
# 파일의 상대 경로 구하기
|
54 |
+
rel_path = os.path.relpath(file_path, self.repo_path)
|
55 |
+
|
56 |
+
# 마지막 커밋 정보 찾기
|
57 |
+
for commit in self.repo.iter_commits(paths=rel_path, max_count=1):
|
58 |
+
return commit.committed_datetime
|
59 |
+
|
60 |
+
return None
|
61 |
+
except Exception:
|
62 |
+
return None
|
63 |
+
|
64 |
+
def get_translation_status(self, progress=gr.Progress()) -> List[Dict]:
|
65 |
+
"""모든 영어 문서와 해당하는 한글 번역 상태를 확인합니다."""
|
66 |
+
if not self.repo_path:
|
67 |
+
return [{"error": "레포지토리가 클론되지 않았습니다. 먼저 클론을 수행하세요."}]
|
68 |
+
|
69 |
+
results = []
|
70 |
+
en_md_files = []
|
71 |
+
|
72 |
+
# 영어 마크다운 파일 수집
|
73 |
+
progress(0.1, desc="영어 문서 스캔 중...")
|
74 |
+
for root, _, files in os.walk(self.en_docs_path):
|
75 |
+
for file in files:
|
76 |
+
if file.endswith(".md"):
|
77 |
+
en_md_files.append(os.path.join(root, file))
|
78 |
+
|
79 |
+
total_files = len(en_md_files)
|
80 |
+
|
81 |
+
# 각 파일에 대한 번역 상태 확인
|
82 |
+
for i, en_file in enumerate(en_md_files):
|
83 |
+
progress((i + 1) / total_files, desc=f"번역 상태 확인 중 ({i+1}/{total_files})...")
|
84 |
+
|
85 |
+
# 상대 경로 계산 (docs/source/en 이후 부분)
|
86 |
+
rel_path = os.path.relpath(en_file, self.en_docs_path)
|
87 |
+
ko_file = os.path.join(self.ko_docs_path, rel_path)
|
88 |
+
|
89 |
+
# 파일 이름 추출
|
90 |
+
file_name = os.path.basename(en_file)
|
91 |
+
|
92 |
+
# 영어 문서 마지막 커밋 날짜
|
93 |
+
en_commit_date = self.get_last_commit_date(en_file)
|
94 |
+
|
95 |
+
# 번역 문서 마지막 커밋 날짜
|
96 |
+
ko_commit_date = self.get_last_commit_date(ko_file)
|
97 |
+
|
98 |
+
# 번역 상태 결정
|
99 |
+
if ko_commit_date is None:
|
100 |
+
status = "미번역"
|
101 |
+
outdate = False
|
102 |
+
else:
|
103 |
+
if en_commit_date > ko_commit_date:
|
104 |
+
status = "번역됨 (업데이트 필요)"
|
105 |
+
outdate = True
|
106 |
+
else:
|
107 |
+
status = "번역됨 (최신)"
|
108 |
+
outdate = False
|
109 |
+
|
110 |
+
# 결과 추가
|
111 |
+
results.append({
|
112 |
+
"file_name": file_name,
|
113 |
+
"en_path": rel_path,
|
114 |
+
"ko_path": rel_path if os.path.exists(ko_file) else "없음",
|
115 |
+
"en_last_commit": en_commit_date.strftime("%Y-%m-%d %H:%M:%S") if en_commit_date else "알 수 없음",
|
116 |
+
"ko_last_commit": ko_commit_date.strftime("%Y-%m-%d %H:%M:%S") if ko_commit_date else "없음",
|
117 |
+
"status": status,
|
118 |
+
"outdate": outdate
|
119 |
+
})
|
120 |
+
|
121 |
+
# 파일명으로 정렬
|
122 |
+
results.sort(key=lambda x: x["file_name"])
|
123 |
+
return results
|
124 |
+
|
125 |
+
def create_ui():
|
126 |
+
"""Gradio UI 생성"""
|
127 |
+
tracker = TranslationTracker()
|
128 |
+
|
129 |
+
with gr.Blocks(title="Transformers 문서 번역 추적기") as app:
|
130 |
+
gr.Markdown("# Transformers 문서 번역 추적기")
|
131 |
+
gr.Markdown("HuggingFace Transformers 레포지토리의 문서 번역 상태를 확인합니다.")
|
132 |
+
|
133 |
+
with gr.Row():
|
134 |
+
clone_btn = gr.Button("레포지토리 클론", variant="primary")
|
135 |
+
status_btn = gr.Button("번역 상태 확인", variant="secondary")
|
136 |
+
cleanup_btn = gr.Button("정리", variant="stop")
|
137 |
+
|
138 |
+
status_output = gr.Textbox(label="상태")
|
139 |
+
|
140 |
+
with gr.Tabs():
|
141 |
+
with gr.TabItem("모든 문서"):
|
142 |
+
all_table = gr.DataFrame(
|
143 |
+
headers=["파일명", "영어 경로", "한글 경로", "영어 마지막 커밋", "한글 마지막 커밋", "상태"],
|
144 |
+
datatype=["str", "str", "str", "str", "str", "str"],
|
145 |
+
label="번역 상태"
|
146 |
+
)
|
147 |
+
|
148 |
+
with gr.TabItem("번역 필요"):
|
149 |
+
untranslated_table = gr.DataFrame(
|
150 |
+
headers=["파일명", "영어 경로", "상태"],
|
151 |
+
datatype=["str", "str", "str"],
|
152 |
+
label="번역이 필요한 문서"
|
153 |
+
)
|
154 |
+
|
155 |
+
with gr.TabItem("업데이트 필요"):
|
156 |
+
outdated_table = gr.DataFrame(
|
157 |
+
headers=["파일명", "영어 경로", "영어 마지막 커밋", "한글 마지막 커밋"],
|
158 |
+
datatype=["str", "str", "str", "str"],
|
159 |
+
label="업데이트가 필요한 문서"
|
160 |
+
)
|
161 |
+
|
162 |
+
# 이벤트 핸들러
|
163 |
+
clone_btn.click(tracker.clone_repo, outputs=status_output)
|
164 |
+
|
165 |
+
def process_translation_status():
|
166 |
+
results = tracker.get_translation_status()
|
167 |
+
if results and "error" in results[0]:
|
168 |
+
return status_output.update(results[0]["error"]), None, None, None
|
169 |
+
|
170 |
+
# 전체 데이터
|
171 |
+
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]
|
172 |
+
|
173 |
+
# 미번역 데이터
|
174 |
+
untranslated = [[r["file_name"], r["en_path"], r["status"]] for r in results if r["status"] == "미번역"]
|
175 |
+
|
176 |
+
# 업데이트 필요 데이터
|
177 |
+
outdated = [[r["file_name"], r["en_path"], r["en_last_commit"], r["ko_last_commit"]] for r in results if r["outdate"]]
|
178 |
+
|
179 |
+
return "번역 상태 조회 완료", all_data, untranslated, outdated
|
180 |
+
|
181 |
+
status_btn.click(
|
182 |
+
process_translation_status,
|
183 |
+
outputs=[status_output, all_table, untranslated_table, outdated_table]
|
184 |
+
)
|
185 |
+
|
186 |
+
cleanup_btn.click(
|
187 |
+
lambda: (tracker.cleanup(), "정리 완료"),
|
188 |
+
outputs=status_output
|
189 |
+
)
|
190 |
+
|
191 |
+
return app
|
192 |
+
|
193 |
+
def main():
|
194 |
+
app = create_ui()
|
195 |
+
app.launch()
|
196 |
+
|
197 |
+
if __name__ == "__main__":
|
198 |
+
main()
|
requirements.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
gradio>=4.0.0
|
2 |
+
gitpython>=3.1.0
|