Created
February 11, 2026 13:23
-
-
Save flodolo/c8aeae0f4fdd6992cd724f8cfe525325 to your computer and use it in GitHub Desktop.
Firefox Onboarding Refresh completion status (v148)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Pontoon translation coverage report for a given search query. | |
| - Queries Pontoon search/translations endpoint (custom User-Agent). | |
| - Extracts entity IDs. | |
| - Fetches translations for each entity. | |
| - Computes per-locale completion across the entity set. | |
| Requires: requests | |
| pip install requests | |
| """ | |
| import sys | |
| import time | |
| from typing import Dict, List, Optional, Set | |
| import requests | |
| SEARCH_URL = "https://pontoon.mozilla.org/api/v2/search/translations/" | |
| ENTITY_URL = "https://pontoon.mozilla.org/api/v2/entities/{id}/" | |
| def build_session(user_agent: str) -> requests.Session: | |
| session = requests.Session() | |
| session.headers.update( | |
| { | |
| "User-Agent": user_agent, | |
| "Accept": "application/json", | |
| } | |
| ) | |
| return session | |
| def fetch_all_entity_ids( | |
| session: requests.Session, | |
| locale: str, | |
| text: str, | |
| project: str, | |
| search_identifiers: bool = True, | |
| page_size: int = 100, | |
| timeout: int = 30, | |
| ) -> Set[int]: | |
| params = { | |
| "locale": locale, | |
| "text": text, | |
| "search_identifiers": "true" if search_identifiers else "false", | |
| "project": project, | |
| "page_size": page_size, | |
| } | |
| ids: Set[int] = set() | |
| url = SEARCH_URL | |
| while True: | |
| r = session.get(url, params=params, timeout=timeout) | |
| r.raise_for_status() | |
| data = r.json() | |
| for item in data.get("results", []): | |
| entity_id = item.get("id") | |
| if entity_id: | |
| ids.add(entity_id) | |
| next_url = data.get("next") | |
| if not next_url: | |
| break | |
| url = next_url | |
| params = {} | |
| return ids | |
| def fetch_entity_locale_set( | |
| session: requests.Session, | |
| entity_id: int, | |
| timeout: int = 30, | |
| ) -> Set[str]: | |
| url = ENTITY_URL.format(id=entity_id) | |
| params = {"include_translations": "true"} | |
| r = session.get(url, params=params, timeout=timeout) | |
| r.raise_for_status() | |
| data = r.json() | |
| locales_with_translation: Set[str] = set() | |
| for t in data.get("translations", []): | |
| locale_code = t.get("locale", {}).get("code") | |
| if locale_code: | |
| locales_with_translation.add(locale_code) | |
| return locales_with_translation | |
| def compute_grouped_completion( | |
| entity_ids: Set[int], | |
| entity_to_locales: Dict[int, Set[str]], | |
| ) -> Dict[int, List[str]]: | |
| """ | |
| Returns: | |
| { | |
| percentage_int: [locale1, locale2, ...] | |
| } | |
| """ | |
| total = len(entity_ids) | |
| per_locale_count: Dict[str, int] = {} | |
| for eid in entity_ids: | |
| for loc in entity_to_locales.get(eid, set()): | |
| per_locale_count[loc] = per_locale_count.get(loc, 0) + 1 | |
| grouped: Dict[int, List[str]] = {} | |
| for loc, count in per_locale_count.items(): | |
| percentage = round((count / total) * 100) if total else 0 | |
| grouped.setdefault(percentage, []).append(loc) | |
| # Sort locales alphabetically inside each percentage | |
| for loc_list in grouped.values(): | |
| loc_list.sort() | |
| return grouped | |
| def main(argv: Optional[List[str]] = None) -> int: | |
| user_agent = "l10n-team-stats/1.0" | |
| timeout = 30 | |
| sleep = 0 | |
| locale = "it" | |
| text = "onboarding-refresh" | |
| project = "firefox" | |
| session = build_session(user_agent) | |
| print("Searching entity IDs...") | |
| entity_ids = fetch_all_entity_ids( | |
| session=session, | |
| locale=locale, | |
| text=text, | |
| project=project, | |
| timeout=timeout, | |
| ) | |
| if not entity_ids: | |
| print("No entity IDs found. Exiting.") | |
| return 0 | |
| print(f"Found {len(entity_ids)} unique entity IDs.") | |
| entity_to_locales: Dict[int, Set[str]] = {} | |
| for i, eid in enumerate(entity_ids, start=1): | |
| try: | |
| entity_to_locales[eid] = fetch_entity_locale_set( | |
| session, eid, timeout=timeout | |
| ) | |
| except requests.HTTPError as e: | |
| print(f"[WARN] Failed entity {eid}: {e}", file=sys.stderr) | |
| entity_to_locales[eid] = set() | |
| if sleep > 0 and i < len(entity_ids): | |
| time.sleep(sleep) | |
| if i % 25 == 0 or i == len(entity_ids): | |
| print(f" Processed {i}/{len(entity_ids)} entities...") | |
| grouped = compute_grouped_completion(entity_ids, entity_to_locales) | |
| print("\nCompletion by locale:\n") | |
| for percentage in sorted(grouped.keys(), reverse=True): | |
| locales = grouped[percentage] | |
| print(f"{percentage}%: {', '.join(locales)}") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment