Skip to content

Commit 5c3803b

Browse files
committed
Add Transifex automation scripts and update workflow for translation statistics
1 parent 01d255b commit 5c3803b

6 files changed

Lines changed: 554 additions & 0 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Update Translation Statistics
2+
3+
on:
4+
schedule:
5+
- cron: '0 0 * * 6' # Run at midnight on Saturdays
6+
workflow_dispatch: # Allow manual trigger
7+
8+
jobs:
9+
update-stats:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v3
13+
14+
- name: Set up Python
15+
uses: actions/setup-python@v4
16+
with:
17+
python-version: '3.10'
18+
19+
- name: Install dependencies
20+
run: |
21+
python -m pip install --upgrade pip
22+
pip install -r requirements.txt
23+
sudo apt-get update
24+
sudo apt-get install -y chromium-browser
25+
26+
- name: Run statistics update
27+
env:
28+
TRANSIFEX_USERNAME: ${{ secrets.TRANSIFEX_USERNAME }}
29+
TRANSIFEX_PASSWORD: ${{ secrets.TRANSIFEX_PASSWORD }}
30+
CI: true
31+
run: |
32+
cd scripts/transifex
33+
python main.py
34+
35+
- name: Update README
36+
run: python scripts/transifex/update_readme.py
37+
38+
- name: Commit and push changes
39+
run: |
40+
git config --local user.email "github-actions@github.com"
41+
git config --local user.name "GitHub Action"
42+
git add reports/ README.md
43+
git diff --quiet && git diff --staged --quiet || git commit -m "Update translation statistics"
44+
git push

scripts/transifex/main.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import sys
2+
import os
3+
from transifex_browser import get_driver_with_login, save_cookies, kill_browser
4+
from visualizer import visualize_string_counts, visualize_user_contributions
5+
6+
7+
def main():
8+
# Check for environment variables first (for CI/CD)
9+
username = os.environ.get("TRANSIFEX_USERNAME")
10+
password = os.environ.get("TRANSIFEX_PASSWORD")
11+
12+
# Fall back to command line args if env vars not found
13+
if not username or not password:
14+
if len(sys.argv) != 3:
15+
print("Usage: python main.py <username> <password>")
16+
print("Or set TRANSIFEX_USERNAME and TRANSIFEX_PASSWORD environment variables")
17+
sys.exit(1)
18+
username = sys.argv[1]
19+
password = sys.argv[2]
20+
21+
driver = get_driver_with_login(username, password)
22+
# Navigate to a specific URL if needed.
23+
driver.get("https://app.transifex.com/python-doc/python-newest/translate/#fa/$")
24+
# Allow time for the page to load.
25+
driver.implicitly_wait(10)
26+
# Update cookies after visiting the page.
27+
save_cookies(driver)
28+
visualize_user_contributions()
29+
visualize_string_counts()
30+
kill_browser()
31+
32+
33+
if __name__ == "__main__":
34+
main()

scripts/transifex/requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
matplotlib
2+
requests
3+
helium
4+
selenium
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import json
2+
from helium import start_chrome, click, write, kill_browser
3+
from selenium.webdriver.common.by import By
4+
from time import sleep
5+
6+
COOKIE_FILE = "transifex_cookies.json"
7+
TRANSIFEX_URL = "https://www.transifex.com/dashboard/"
8+
9+
10+
def save_cookies(driver):
11+
cookies = driver.get_cookies()
12+
with open(COOKIE_FILE, "w") as f:
13+
json.dump(cookies, f)
14+
return cookies
15+
16+
17+
def is_logged_in(driver):
18+
# Check for an element that only exists when logged in.
19+
try:
20+
driver.find_element(By.CSS_SELECTOR, ".user-profile")
21+
return True
22+
except Exception:
23+
return False
24+
25+
26+
def login_transifex(driver, username, password):
27+
driver.get("https://app.transifex.com/signin/")
28+
sleep(5)
29+
write(username, into="Email")
30+
write(password, into="Password")
31+
click("Log in") # Adjust selector if needed.
32+
# Allow time for login and cookie propagation.
33+
driver.implicitly_wait(10)
34+
save_cookies(driver)
35+
return driver
36+
37+
38+
def get_driver_with_login(username, password):
39+
driver = start_chrome(headless=False) # FIXME
40+
driver.get(TRANSIFEX_URL)
41+
sleep(5)
42+
login_transifex(driver, username, password)
43+
return driver

scripts/transifex/update_readme.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import os
2+
import re
3+
import glob
4+
from datetime import datetime
5+
6+
# Calculate the path to the repository root (2 levels up from this script)
7+
REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
8+
README_PATH = os.path.join(REPO_ROOT, "README.md")
9+
contributors_dir = os.path.join(REPO_ROOT, "reports", "contributors")
10+
progress_dir = os.path.join(REPO_ROOT, "reports", "progress")
11+
12+
13+
def find_latest_image(directory):
14+
"""Find the latest image file in the given directory based on filename."""
15+
pattern = os.path.join(directory, "*.png")
16+
files = glob.glob(pattern)
17+
if not files:
18+
return None
19+
# Sort the files by name (which should sort by date if the format is consistent)
20+
latest_file = sorted(files)[-1]
21+
# Return the path relative to the REPO_ROOT
22+
return os.path.relpath(latest_file, REPO_ROOT).replace("\\", "/")
23+
24+
25+
def update_readme():
26+
"""Update the README.md file with the latest report images."""
27+
today = datetime.now().strftime("%Y-%m-%d")
28+
latest_contributors_img = find_latest_image(contributors_dir)
29+
latest_progress_img = find_latest_image(progress_dir)
30+
31+
# Check if both image files exist
32+
if not latest_contributors_img or not latest_progress_img:
33+
print("Warning: One or both image files not found.")
34+
if latest_contributors_img:
35+
print(f"Found contributors image: {latest_contributors_img}")
36+
if latest_progress_img:
37+
print(f"Found progress image: {latest_progress_img}")
38+
return False
39+
40+
# Read the current README content
41+
with open(README_PATH, "r", encoding="utf-8") as file:
42+
content = file.read()
43+
44+
# Define the stats section
45+
stats_section = f"""## آمار ترجمه
46+
47+
### مشارکت‌های این هفته کاربران
48+
49+
![مشارکت‌های این هفته کاربران]({latest_contributors_img})
50+
51+
(به‌روزرسانی: {today})
52+
53+
### پیشرفت ترجمه
54+
55+
![پیشرفت ترجمه]({latest_progress_img})
56+
57+
(به‌روزرسانی: {today})"""
58+
59+
# Check if stats section already exists with markers
60+
if "<!-- STATS_START -->" in content and "<!-- STATS_END -->" in content:
61+
# Replace the existing stats section between markers
62+
pattern = r"<!-- STATS_START -->.*?<!-- STATS_END -->"
63+
updated_content = re.sub(
64+
pattern,
65+
f"<!-- STATS_START -->\n{stats_section}\n<!-- STATS_END -->",
66+
content,
67+
flags=re.DOTALL,
68+
)
69+
elif "## آمار ترجمه" in content:
70+
# If old format exists without markers, replace that section
71+
pattern = r"## آمار ترجمه.*?(?=\n##|\Z)"
72+
updated_content = re.sub(
73+
pattern,
74+
f"<!-- STATS_START -->\n{stats_section}\n<!-- STATS_END -->",
75+
content,
76+
flags=re.DOTALL,
77+
)
78+
else:
79+
# Append the stats section to the end of the README
80+
updated_content = (
81+
content
82+
+ "\n\n<!-- STATS_START -->\n"
83+
+ stats_section
84+
+ "\n<!-- STATS_END -->"
85+
)
86+
87+
# Write the updated content back to the README
88+
with open(README_PATH, "w", encoding="utf-8") as file:
89+
file.write(updated_content)
90+
91+
print(f"README.md updated successfully with:")
92+
print(f"- Contributors image: {latest_contributors_img}")
93+
print(f"- Progress image: {latest_progress_img}")
94+
print(f"- Update date: {today}")
95+
return True
96+
97+
98+
if __name__ == "__main__":
99+
update_readme()

0 commit comments

Comments
 (0)