1 #!/usr/bin/env python3 2 # SPDX-License-Identifier: GPL-2.0 3 4 """ 5 This script helps track the translation status 6 in different locales, e.g., zh_CN. More specia 7 commit to find the latest english commit from 8 (order by author date) and the latest english 9 differences occur, report the file and commits 10 11 The usage is as follows: 12 - ./scripts/checktransupdate.py -l zh_CN 13 This will print all the files that need to be 14 - ./scripts/checktransupdate.py Documentation/ 15 This will only print the status of the specifi 16 17 The output is something like: 18 Documentation/dev-tools/kfence.rst 19 No translation in the locale of zh_CN 20 21 Documentation/translations/zh_CN/dev-tools/tes 22 commit 42fb9cfd5b18 ("Documentation: dev-tools 23 1 commits needs resolving in total 24 """ 25 26 import os 27 import time 28 import logging 29 from argparse import ArgumentParser, ArgumentT 30 from datetime import datetime 31 32 33 def get_origin_path(file_path): 34 """Get the origin path from the translatio 35 paths = file_path.split("/") 36 tidx = paths.index("translations") 37 opaths = paths[:tidx] 38 opaths += paths[tidx + 2 :] 39 return "/".join(opaths) 40 41 42 def get_latest_commit_from(file_path, commit): 43 """Get the latest commit from the specifie 44 command = f"git log --pretty=format:%H%n%a 45 logging.debug(command) 46 pipe = os.popen(command) 47 result = pipe.read() 48 result = result.split("\n") 49 if len(result) <= 1: 50 return None 51 52 logging.debug("Result: %s", result[0]) 53 54 return { 55 "hash": result[0], 56 "author_date": datetime.strptime(resul 57 "commit_date": datetime.strptime(resul 58 "message": result[4:], 59 } 60 61 62 def get_origin_from_trans(origin_path, t_from_ 63 """Get the latest origin commit from the t 64 o_from_t = get_latest_commit_from(origin_p 65 while o_from_t is not None and o_from_t["a 66 o_from_t = get_latest_commit_from(orig 67 if o_from_t is not None: 68 logging.debug("tracked origin commit i 69 return o_from_t 70 71 72 def get_commits_count_between(opath, commit1, 73 """Get the commits count between two commi 74 command = f"git log --pretty=format:%H {co 75 logging.debug(command) 76 pipe = os.popen(command) 77 result = pipe.read().split("\n") 78 # filter out empty lines 79 result = list(filter(lambda x: x != "", re 80 return result 81 82 83 def pretty_output(commit): 84 """Pretty print the commit message""" 85 command = f"git log --pretty='format:%h (\ 86 logging.debug(command) 87 pipe = os.popen(command) 88 return pipe.read() 89 90 91 def valid_commit(commit): 92 """Check if the commit is valid or not""" 93 msg = pretty_output(commit) 94 return "Merge tag" not in msg 95 96 def check_per_file(file_path): 97 """Check the translation status for the sp 98 opath = get_origin_path(file_path) 99 100 if not os.path.isfile(opath): 101 logging.error("Cannot find the origin 102 return 103 104 o_from_head = get_latest_commit_from(opath 105 t_from_head = get_latest_commit_from(file_ 106 107 if o_from_head is None or t_from_head is N 108 logging.error("Cannot find the latest 109 return 110 111 o_from_t = get_origin_from_trans(opath, t_ 112 113 if o_from_t is None: 114 logging.error("Error: Cannot find the 115 return 116 117 if o_from_head["hash"] == o_from_t["hash"] 118 logging.debug("No update needed for %s 119 else: 120 logging.info(file_path) 121 commits = get_commits_count_between( 122 opath, o_from_t["hash"], o_from_he 123 ) 124 count = 0 125 for commit in commits: 126 if valid_commit(commit): 127 logging.info("commit %s", pret 128 count += 1 129 logging.info("%d commits needs resolvi 130 131 132 def valid_locales(locale): 133 """Check if the locale is valid or not""" 134 script_path = os.path.dirname(os.path.absp 135 linux_path = os.path.join(script_path, ".. 136 if not os.path.isdir(f"{linux_path}/Docume 137 raise ArgumentTypeError("Invalid local 138 return locale 139 140 141 def list_files_with_excluding_folders(folder, 142 """List all files with the specified suffi 143 files = [] 144 stack = [folder] 145 146 while stack: 147 pwd = stack.pop() 148 # filter out the exclude folders 149 if os.path.basename(pwd) in exclude_fo 150 continue 151 # list all files and folders 152 for item in os.listdir(pwd): 153 ab_item = os.path.join(pwd, item) 154 if os.path.isdir(ab_item): 155 stack.append(ab_item) 156 else: 157 if ab_item.endswith(include_su 158 files.append(ab_item) 159 160 return files 161 162 163 class DmesgFormatter(logging.Formatter): 164 """Custom dmesg logging formatter""" 165 def format(self, record): 166 timestamp = time.time() 167 formatted_time = f"[{timestamp:>10.6f} 168 log_message = f"{formatted_time} {reco 169 return log_message 170 171 172 def config_logging(log_level, log_file="checkt 173 """configure logging based on the log leve 174 # set up the root logger 175 logger = logging.getLogger() 176 logger.setLevel(log_level) 177 178 # Create console handler 179 console_handler = logging.StreamHandler() 180 console_handler.setLevel(log_level) 181 182 # Create file handler 183 file_handler = logging.FileHandler(log_fil 184 file_handler.setLevel(log_level) 185 186 # Create formatter and add it to the handl 187 formatter = DmesgFormatter() 188 console_handler.setFormatter(formatter) 189 file_handler.setFormatter(formatter) 190 191 # Add the handler to the logger 192 logger.addHandler(console_handler) 193 logger.addHandler(file_handler) 194 195 196 def main(): 197 """Main function of the script""" 198 script_path = os.path.dirname(os.path.absp 199 linux_path = os.path.join(script_path, ".. 200 201 parser = ArgumentParser(description="Check 202 parser.add_argument( 203 "-l", 204 "--locale", 205 default="zh_CN", 206 type=valid_locales, 207 help="Locale to check when files are n 208 ) 209 210 parser.add_argument( 211 "--print-missing-translations", 212 action=BooleanOptionalAction, 213 default=True, 214 help="Print files that do not have tra 215 ) 216 217 parser.add_argument( 218 '--log', 219 default='INFO', 220 choices=['DEBUG', 'INFO', 'WARNING', ' 221 help='Set the logging level') 222 223 parser.add_argument( 224 '--logfile', 225 default='checktransupdate.log', 226 help='Set the logging file (default: c 227 228 parser.add_argument( 229 "files", nargs="*", help="Files to che 230 ) 231 args = parser.parse_args() 232 233 # Configure logging based on the --log arg 234 log_level = getattr(logging, args.log.uppe 235 config_logging(log_level) 236 237 # Get files related to linux path 238 files = args.files 239 if len(files) == 0: 240 offical_files = list_files_with_exclud 241 os.path.join(linux_path, "Document 242 ) 243 244 for file in offical_files: 245 # split the path into parts 246 path_parts = file.split(os.sep) 247 # find the index of the "Documenta 248 kindex = path_parts.index("Documen 249 # insert the translations and loca 250 new_path_parts = path_parts[:kinde 251 + path_parts[kindex 252 # join the path parts back togethe 253 new_file = os.sep.join(new_path_pa 254 if os.path.isfile(new_file): 255 files.append(new_file) 256 else: 257 if args.print_missing_translat 258 logging.info(os.path.relpa 259 logging.info("No translati 260 261 files = list(map(lambda x: os.path.relpath 262 263 # cd to linux root directory 264 os.chdir(linux_path) 265 266 for file in files: 267 check_per_file(file) 268 269 270 if __name__ == "__main__": 271 main()
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.