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