Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/update-translation-stats.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Update Translation Statistics
on:
schedule:
- cron: '0 0 * * 6'
workflow_dispatch:
jobs:
update-stats:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r scripts/transifex/requirements.txt
- name: Generate all stats
run: python -m scripts.transifex.main generate-all-stats
env:
TRANSIFEX_API_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }}
- name: Commit and push if changes
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add RESOURCE.md TEAM.md reports/ README.md
git commit -m "Update translation statistics [skip ci]" || exit 0
git push
Empty file added scripts/transifex/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions scripts/transifex/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from transifex.api import transifex_api
from .config import PROJECT, LANGUAGE, ORGANISATION

_api_cache = {}


def _fetch_from_api(cache_key, api_call_func, *args, **kwargs):
if cache_key not in _api_cache:
_api_cache[cache_key] = api_call_func(*args, **kwargs)
return _api_cache[cache_key]


def get_all_resources():
"""Fetches all resources for the configured project, with caching."""
cache_key = "all_resources"
return _fetch_from_api(
cache_key, transifex_api.Resource.filter, project=PROJECT
).all()


def get_resource_language_stats():
"""Fetches language statistics for all resources, with caching."""
cache_key = "resource_language_stats"
return _fetch_from_api(
cache_key,
transifex_api.ResourceLanguageStats.filter,
project=PROJECT,
language=LANGUAGE,
).all()


def get_team_members():
"""Fetches all team members, with caching."""
cache_key = "team_members"
# Fetching with 'user' include to get user details like username
return (
_fetch_from_api(
cache_key,
transifex_api.TeamMembership.filter,
organization=ORGANISATION,
language=LANGUAGE,
)
.include("user")
.all()
)


def get_resource_translations(resource):
"""Fetches translations for a given resource, with caching per resource."""
resource_id = resource.id if hasattr(resource, "id") else str(resource)
cache_key = f"resource_translations_{resource_id}"
return _fetch_from_api(
cache_key,
transifex_api.ResourceTranslation.filter,
resource=resource,
language=LANGUAGE,
).all()
82 changes: 82 additions & 0 deletions scripts/transifex/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
from pathlib import Path
from transifex.api import transifex_api

# Transifex API Authentication
TRANSIFEX_AUTH_TOKEN = os.getenv("TRANSIFEX_API_TOKEN")
if not TRANSIFEX_AUTH_TOKEN:
raise ValueError("TRANSIFEX_API_TOKEN environment variable not set.")
transifex_api.setup(auth=TRANSIFEX_AUTH_TOKEN)

# Language and Project Configuration
LANG = "fa"
ORGANISATION_ID = "o:python-doc"
PROJECT_ID = "o:python-doc:p:python-newest"
LANGUAGE_ID = f"l:{LANG}"

try:
ORGANISATION = transifex_api.Organization.get(id=ORGANISATION_ID)
PROJECT = transifex_api.Project.get(id=PROJECT_ID)
LANGUAGE = transifex_api.Language.get(id=LANGUAGE_ID)
except Exception as e:
print(f"Error initializing Transifex API objects: {e}")
raise

# Calculate the project root (assuming config.py is in scripts/transifex/)
SCRIPT_DIR = Path(__file__).resolve().parent
PROJECT_ROOT = SCRIPT_DIR.parent.parent

# Resource Mapping
RESOURCE_NAME_MAP = {"glossary_": "glossary"}

# Output file paths
TX_CONFIG_PATH = PROJECT_ROOT / ".tx/config"
RESOURCE_STATS_MD_PATH = PROJECT_ROOT / "RESOURCE.md"
TEAM_STATS_MD_PATH = PROJECT_ROOT / "TEAM.md"
CONTRIBUTOR_CHART_DIR = PROJECT_ROOT / "reports"
CONTRIBUTOR_CHART_FILENAME_PREFIX = "contributor_stats_"

# README Update Configuration
README_PATH = PROJECT_ROOT / "README.md"
README_STATS_START_MARKER = "<!-- STATS_START -->"
README_STATS_END_MARKER = "<!-- STATS_END -->"
README_CONTRIBUTORS_HEADER = "مشارکت‌های کاربران"
README_PROGRESS_HEADER = "پیشرفت کلی ترجمه"
README_UPDATED_ON = "به‌روزرسانی"

CHART_PASTEL_COLORS = [
"#A6C7E8", # Pastel blue
"#B5EAD7", # Pastel green
"#FFDFD3", # Pastel pink
"#FFF1AC", # Pastel yellow
"#E2D1F9", # Pastel lavender
"#FFD7BA", # Pastel orange
"#FFABAB", # Pastel coral
"#C7F0DB", # Pastel mint
"#FFDAC1", # Pastel peach
"#C7CEEA", # Pastel sky blue
]

REPORT_HEADERS = {
"resource_stats": {
"file": "File",
"translated": "Translated",
"reviewed": "Reviewed",
"proofread": "Proofread",
"alignment": "|:-----|:-----------:|:-----------:|:-----------:|\n",
},
"team_stats": {
"user": "User",
"role": "Role",
"translated_count": "Translated Count",
"reviewed_count": "Reviewed Count",
"proofread_count": "Proofread Count",
"alignment": "|:-----|:------:|:------------------:|:-------------------:|:----------------------:|\n",
},
"contributor_chart": {
"title_base": "User Contributions",
"title_top_n_suffix": " (Top {top_n})",
"xlabel_username": "Username",
"ylabel_total_contributions": "Total Contributions",
},
}
32 changes: 32 additions & 0 deletions scripts/transifex/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import argparse
import sys
from .reporting import REPORTERS


def main():
parser = argparse.ArgumentParser(
description="Transifex utility scripts for Python docs (Persian team)."
)

valid_commands = list(REPORTERS.keys())
parser.add_argument(
"command",
choices=valid_commands,
help=f"The command to execute. Available commands: {', '.join(valid_commands)}",
)

args = parser.parse_args()

selected_reporter_class = REPORTERS.get(args.command)

if selected_reporter_class:
reporter_instance = selected_reporter_class()
reporter_instance.generate()
else:
print(f"Error: Unknown command '{args.command}'.", file=sys.stderr)
parser.print_help(sys.stderr)
sys.exit(1)


if __name__ == "__main__":
main()
Loading