Skip to content

Commit 5357550

Browse files
committed
Add status dashboard generation step on websoite deploy
1 parent 351b43c commit 5357550

4 files changed

Lines changed: 176 additions & 0 deletions

File tree

.github/workflows/deploy_website.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ jobs:
3636
- name: Install Dart Sass Embedded # Installs dart-sass
3737
run: sudo snap install dart-sass-embedded
3838

39+
- name: Install python and dependencies
40+
uses: actions/setup-python@v5
41+
with:
42+
python-version: 'pypy3.13'
43+
- run: |
44+
python -m pip install --upgrade pip
45+
python -m pip install crowdin-api-client pygithub
46+
47+
- name: Update dashboard
48+
run: |
49+
python /scripts/update_dashboard.py
50+
3951
- name: Build with Hugo
4052
run: |
4153
git submodule update --init --recursive # fetch theme

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.hugo_build.lock
22
public/
33
resources/_gen/
4+
.DS_Store

content/status.md

Whitespace-only changes.

scripts/update_dashboard.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import json
2+
import yaml
3+
import os
4+
import traceback
5+
import tempfile
6+
from datetime import datetime
7+
from subprocess import Popen, PIPE
8+
from pathlib import Path
9+
10+
from crowdin_api import CrowdinClient # type: ignore
11+
from github import Github, Auth
12+
13+
14+
15+
def parse_input() -> dict:
16+
gh_input = {
17+
# Automations Bot account
18+
"username": "scientificpythontranslations",
19+
"crowdin_token": os.environ["CROWDIN_TOKEN"],
20+
# Provided by gpg action based on organization secrets
21+
"name": os.environ["GPG_NAME"],
22+
"email": os.environ["GPG_EMAIL"],
23+
}
24+
return gh_input
25+
26+
27+
class ScientificCrowdinClient:
28+
29+
def __init__(self, token: str, organization: str):
30+
self._token = token
31+
self._organization = organization
32+
self._client = CrowdinClient(token=token, organization=organization)
33+
34+
def get_projects(self) -> dict:
35+
"""Get projects from Crowdin."""
36+
result = {}
37+
projects = self._client.projects.with_fetch_all().list_projects()
38+
for project in projects["data"]:
39+
result[project["data"]["name"]] = project["data"]["id"]
40+
return result
41+
42+
def get_project_id(self, project_name: str) -> int:
43+
"""Get project ID from Crowdin."""
44+
projects = self._client.projects.with_fetch_all().list_projects()
45+
for project in projects["data"]:
46+
if project["data"]["name"] == project_name:
47+
return project["data"]["id"]
48+
else:
49+
raise ValueError(f"Project '{project_name}' not found.")
50+
51+
def get_project_status(self, project_name: str) -> dict:
52+
"""Get project status from Crowdin."""
53+
results = {}
54+
for p_name, project_id in self.get_projects().items():
55+
if project_name != p_name:
56+
continue
57+
58+
languages = self._client.translation_status.get_project_progress(
59+
project_id
60+
)["data"]
61+
for language in languages:
62+
language_id = language["data"]["language"]["id"]
63+
results[language_id] = {
64+
"language_name": language["data"]["language"]["name"],
65+
"progress": language["data"]["translationProgress"],
66+
"approval": language["data"]["approvalProgress"],
67+
}
68+
return results
69+
70+
def get_project_languages(self, project_name: str) -> list:
71+
"""Get project languages from Crowdin."""
72+
projects = self._client.projects.with_fetch_all().list_projects()
73+
for project in projects["data"]:
74+
if project["data"]["name"] == project_name:
75+
return project["data"]["targetLanguageIds"]
76+
else:
77+
raise ValueError(f"Project '{project_name}' not found.")
78+
79+
def get_valid_languages(
80+
self, project_name: str, translation_percentage: int, approval_percentage: int
81+
) -> dict:
82+
"""Get valid languages based on translation and approval percentage.
83+
84+
Parameters
85+
----------
86+
project_name : str
87+
Name of the project.
88+
translation_percentage : int
89+
Minimum translation percentage.
90+
approval_percentage : int
91+
Minimum approval percentage.
92+
93+
Returns
94+
-------
95+
valid_languages : dict
96+
Dictionary of valid languages.
97+
"""
98+
valid_languages = {}
99+
project_languages = self.get_project_status(project_name)
100+
# print(json.dumps(project_languages, sort_keys=True, indent=4))
101+
for language_id, data in project_languages.items():
102+
approval = data["approval"]
103+
progress = data["progress"]
104+
language_name = data["language_name"]
105+
if progress >= translation_percentage and approval >= approval_percentage:
106+
# print(f"\n{language_id} {language_name}: {progress}% / {approval}%")
107+
valid_languages[language_id] = {
108+
"language_name": language_name,
109+
"progress": progress,
110+
"approval": approval,
111+
}
112+
return valid_languages
113+
114+
def get_project_translators(self, project_name: str) -> dict:
115+
"""Get project translators from Crowdin."""
116+
results: dict = {}
117+
project_id = self.get_project_id(project_name)
118+
languages = self.get_project_languages(project_name)
119+
for lang in sorted(languages):
120+
results[lang] = []
121+
offset = 0
122+
limit = 500
123+
while True:
124+
items = self._client.string_translations.list_language_translations(
125+
lang, project_id, limit=limit, offset=offset
126+
)
127+
if data := items["data"]:
128+
for item in data:
129+
user_data = {
130+
"username": item["data"]["user"]["username"],
131+
"name": item["data"]["user"]["fullName"],
132+
"img_link": item["data"]["user"]["avatarUrl"].replace(
133+
"/medium/", "/large/"
134+
),
135+
}
136+
if user_data not in results[lang]:
137+
results[lang].append(user_data)
138+
offset += limit
139+
else:
140+
break
141+
142+
return results
143+
144+
145+
def main() -> None:
146+
"""Main function to run the script."""
147+
try:
148+
gh_input = parse_input()
149+
crowdin_project = gh_input["crowdin_project"]
150+
client = ScientificCrowdinClient(
151+
token=gh_input["crowdin_token"], organization="Scientific-python"
152+
)
153+
valid_languages = client.get_valid_languages(
154+
crowdin_project,
155+
int(gh_input["translation_percentage"]),
156+
int(gh_input["approval_percentage"]),
157+
)
158+
translators = client.get_project_translators(
159+
crowdin_project,
160+
)
161+
162+
if __name__ == "__main__":
163+
main()

0 commit comments

Comments
 (0)