diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index dbe47d6f2..302a684ad 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -21,7 +21,6 @@ jobs: - run: sudo apt-get install -y gettext - run: pip install -r requirements.txt - run: uv run generate.py # generates index.html and index.json - - run: mkdir -p build && cp index.* style.css build - name: Deploy 🚀 if: github.event_name != 'pull_request' uses: JamesIves/github-pages-deploy-action@v4 @@ -55,10 +54,9 @@ jobs: if: github.event_name == 'pull_request' run: | curl -Lo index.html-public https://github.com/python-docs-translations/dashboard/raw/refs/heads/gh-pages/index.html - diff --color=always -u index.html-public index.html || : - cat index.html + diff --color=always -u index.html-public build/index.html || : + cat build/index.html - run: uv run generate_metadata.py # generates metadata.html - - run: cp metadata.html warnings* build - name: Deploy metadata view 🚀 if: github.event_name != 'pull_request' uses: JamesIves/github-pages-deploy-action@v4 diff --git a/build_status.py b/build_status.py index fdc2323a4..f266b98d3 100644 --- a/build_status.py +++ b/build_status.py @@ -1,8 +1,7 @@ """ -Fetch build status of languages in the https://docs.python.org. +Fetch translated names of languages. -Yield a tuple of language code and a Boolean indicating -whether it is in the language switcher. +Yield a tuple of language code and a string with the translated name. """ import tomllib @@ -11,7 +10,7 @@ from urllib3 import PoolManager -def get_languages(http: PoolManager) -> Iterator[tuple[str, bool]]: +def get_languages(http: PoolManager) -> Iterator[tuple[str, str]]: data = http.request( 'GET', 'https://raw.githubusercontent.com/python/docsbuild-scripts/refs/heads/main/config.toml', @@ -19,18 +18,5 @@ def get_languages(http: PoolManager) -> Iterator[tuple[str, bool]]: config = tomllib.loads(data.decode()) for code, language in config['languages'].items(): language_code = code.lower().replace('_', '-') - in_switcher = language.get('in_prod', config['defaults']['in_prod']) - yield language_code, in_switcher - - -def main() -> None: - languages = { - language: in_switcher for language, in_switcher in get_languages(PoolManager()) - } - print(languages) - for code in ('en', 'pl', 'ar', 'zh-cn', 'id'): - print(f'{code}: {code in languages} {languages.get(code)}') - - -if __name__ == '__main__': - main() + translated_name = language.get('translated_name') + yield language_code, translated_name diff --git a/build_warnings.py b/build_warnings.py index d26a518e4..87761036e 100644 --- a/build_warnings.py +++ b/build_warnings.py @@ -33,5 +33,5 @@ def number(clones_dir: str, repo: str, language_code: str) -> int: f'./sphinxbuild/{language_code}', # outputdir ) ) - copyfile(warning_file, f'warnings-{language_code}.txt') + copyfile(warning_file, f'build/warnings-{language_code}.txt') return len(findall('ERROR|WARNING', Path(warning_file).read_text())) diff --git a/generate.py b/generate.py index 5df32e98a..ed1c2fad5 100644 --- a/generate.py +++ b/generate.py @@ -9,7 +9,7 @@ from pathlib import Path from git import Repo -from jinja2 import Template +from jinja2 import Environment, FileSystemLoader from urllib3 import PoolManager import build_status @@ -37,7 +37,11 @@ def get_completion_progress() -> Iterator['LanguageProjectData']: ) subprocess.run(['make', '-C', cpython_dir / 'Doc', 'venv'], check=True) subprocess.run(['make', '-C', cpython_dir / 'Doc', 'gettext'], check=True) - languages_built = dict(build_status.get_languages(PoolManager())) + + languages_built: dict[str, str] = { + language: translated_name + for language, translated_name in build_status.get_languages(PoolManager()) + } with concurrent.futures.ThreadPoolExecutor() as executor: return executor.map( @@ -51,7 +55,7 @@ def get_completion_progress() -> Iterator['LanguageProjectData']: def get_project_data( language: Language, repo: str | None, - languages_built: dict[str, bool], + languages_built: dict[str, str], clones_dir: str, ) -> 'LanguageProjectData': built = language.code in languages_built @@ -62,6 +66,7 @@ def get_project_data( translators_data = TranslatorsData(0, False) change = 0.0 branch = '' + return LanguageProjectData( language, repo, @@ -70,7 +75,7 @@ def get_project_data( change, translators_data, built, - in_switcher=languages_built.get(language.code), + translated_name=languages_built.get(language.code, ''), uses_platform=language.code in contribute.pulling_from_transifex, contribution_link=contribute.get_contrib_link(language.code, repo), ) @@ -85,7 +90,7 @@ class LanguageProjectData: change: float translators: TranslatorsData built: bool - in_switcher: bool | None + translated_name: str uses_platform: bool contribution_link: str | None @@ -93,16 +98,22 @@ class LanguageProjectData: if __name__ == '__main__': logging.basicConfig(level=logging.INFO) logging.info(f'starting at {generation_time}') - template = Template(Path('template.html.jinja').read_text()) + Path('build').mkdir(parents=True, exist_ok=True) - output = template.render( - completion_progress=(completion_progress := list(get_completion_progress())), + completion_progress = list(get_completion_progress()) + counts = get_counts(Path('clones', 'cpython', 'Doc', 'build', 'gettext')) + + env = Environment(loader=FileSystemLoader('templates')) + index = env.get_template('index.html.jinja').render( + completion_progress=completion_progress, generation_time=generation_time, duration=(datetime.now(timezone.utc) - generation_time).seconds, - counts=get_counts(Path('clones', 'cpython', 'Doc', 'build', 'gettext')), ) - Path('index.html').write_text(output) - Path('index.json').write_text( - json.dumps(completion_progress, indent=2, default=asdict) + Path('build/style.css').write_bytes(Path('src/style.css').read_bytes()) + Path('build/logo.png').write_bytes(Path('src/logo.png').read_bytes()) + Path('build/index.html').write_text(index) + + Path('build/index.json').write_text( + json.dumps([asdict(project) for project in completion_progress], indent=2) ) diff --git a/generate_metadata.py b/generate_metadata.py index 1c0f534a8..dbceee294 100644 --- a/generate_metadata.py +++ b/generate_metadata.py @@ -9,7 +9,7 @@ import dacite from git import Repo -from jinja2 import Template +from jinja2 import Environment, FileSystemLoader from urllib3 import request import build_warnings @@ -55,9 +55,9 @@ def get_language_repo_and_completion( if __name__ == '__main__': logging.basicConfig(level=logging.INFO) logging.info(f'starting at {generation_time}') - template = Template(Path('metadata.html.jinja').read_text()) - if (index_path := Path('index.json')).exists(): - index_json = loads(Path('index.json').read_text()) + env = Environment(loader=FileSystemLoader('templates')) + if (index_path := Path('build/index.json')).exists(): + index_json = loads(Path('build/index.json').read_text()) else: index_json = request('GET', argv[1]).json() @@ -65,10 +65,10 @@ def get_language_repo_and_completion( dacite.from_dict(LanguageProjectData, project) for project in index_json ] - output = template.render( + output = env.get_template('metadata.html.jinja').render( metadata=zip(completion_progress, get_projects_metadata(completion_progress)), generation_time=generation_time, duration=(datetime.now(timezone.utc) - generation_time).seconds, ) - Path('metadata.html').write_text(output) + Path('build/metadata.html').write_text(output) diff --git a/src/logo.png b/src/logo.png new file mode 100644 index 000000000..cc4111478 Binary files /dev/null and b/src/logo.png differ diff --git a/src/style.css b/src/style.css new file mode 100644 index 000000000..dc642e2f6 --- /dev/null +++ b/src/style.css @@ -0,0 +1,121 @@ +/* ---------------------------- Global Styles ------------------------------- */ + +body { + padding-top: 4.5rem; + padding-left: 1rem; + padding-right: 1rem; + padding-bottom: 3rem; + color: #5a5a5a; + background-color: rgb(245, 245, 245); +} + +a { + color: #0d0d0d; +} + +a:hover { + color: #1595fe; +} + +/* -------------------------------- navbar ---------------------------------- */ + +.navbar { + background-color: rgb(245, 245, 245, 0.6); + backdrop-filter: blur(10px); + border-bottom: 0.25px solid #999; + -webkit-backdrop-filter: blur(10px); +} + +.navbar li { + transition: 0.3s background-color; + text-align: center; + background-color: transparent; + padding: 0rem 1rem; + text-decoration: none; + border-radius: 0.3rem; +} + +.navbar li:hover { + background-color: #d0cccd; +} + +.navbar li .nav-link { + color: #0d0d0d; +} + +/* ----------------------- Cards and card columns --------------------------- */ + +.row > [class^="col-"] { + display: flex; +} + +.card { + flex: 1; +} + +/* ------------------------------ Index ------------------------------------- */ + +.progress-bar-container { + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.2); + height: 20px; + overflow: hidden; + position: relative; +} + +.progress-bar-index { + display: inline-block; + color: white; + height: 100%; + line-height: 20px; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} + +/* ------------------------------ Metadata ---------------------------------- */ + +table { + border-collapse: collapse; +} +th, td { + border: 1px solid #ddd; + padding: 8px 12px; + text-align: left; + white-space: nowrap; +} +th { + background-color: #d6d6d6; +} +hr { + color: #f4f4f4; +} +td[data-label="warnings"], td[data-label="lint"] { + text-align: right; +} + +@media screen and (max-width: 600px) { + table, thead, tbody, th, td, tr { + display: block; + } + th { + position: absolute; + top: -9999px; + left: -9999px; + } + tr { + border: 1px solid #ccc; + margin-bottom: 5px; + } + td { + border: none; + border-bottom: 1px solid #eee; + padding-left: 50%; + position: relative; + } + td:before { + content: attr(data-label); + font-weight: bold; + left: 10px; + position: absolute; + } +} diff --git a/style.css b/style.css deleted file mode 100644 index 866b7454d..000000000 --- a/style.css +++ /dev/null @@ -1,94 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif; -} -table { - border-collapse: collapse; -} -th, td { - border: 1px solid #ddd; - padding: 8px 12px; - text-align: left; - white-space: nowrap; -} -th { - background-color: #f4f4f4; -} -hr { - color: #f4f4f4; -} -.progress-bar { - display: inline-block; - color: white; - height: 20px; - line-height: 20px; - text-align: center; - overflow: hidden; - white-space: nowrap; - box-sizing: border-box; -} -.progress-bar-outer-label { - display: none; - padding-left: .5em; - height: 20px; - line-height: 20px; - overflow: hidden; -} -.progress-bar.low { - color: transparent; - user-select: none; -} -.progress-bar.low + .progress-bar-outer-label { - display: inline-block; -} -td[data-label="translators"], td[data-label="warnings"], td[data-label="lint"] { - text-align: right; -} -td[data-label="completion"] { - width: 100%; - line-height: 0; -} -.switchpages{ - position: absolute; - top: 10px; - right: 10px; - } - -@media screen and (max-width: 675px) { - .switchpages{ - all: unset; - } -} - -@media screen and (max-width: 600px) { - table, thead, tbody, th, td, tr { - display: block; - } - th { - position: absolute; - top: -9999px; - left: -9999px; - } - tr { - border: 1px solid #ccc; - margin-bottom: 5px; - } - td { - border: none; - border-bottom: 1px solid #eee; - padding-left: 50%; - position: relative; - } - td:before { - content: attr(data-label); - font-weight: bold; - left: 10px; - position: absolute; - } - td[data-label="completion"] { - width: inherit; - } - .progress-bar { - min-width: 0; - width: 100% !important; - } -} diff --git a/template.html.jinja b/template.html.jinja deleted file mode 100644 index f19b4e166..000000000 --- a/template.html.jinja +++ /dev/null @@ -1,88 +0,0 @@ - -
-| language | -switcher | -translators | -completion* | -
|---|---|---|---|
| - {% if project.contribution_link %} - {{ project.language.name }} ({{ project.language.code }}) - {% else %} - {{ project.language.name }} ({{ project.language.code }}) - {% endif %} - | -- {% if project.in_switcher %} - ✓ - {% elif project.built %} - ✗ - {% endif %} - | -- {% if project.translators.link %}{% endif %} - {{ project.translators.number }} - {% if project.translators.link %}{% endif %} - | -- - - | -
* the number in parentheses shows change in the last 30 days, included in the total completion
--Currently being translated into {{ completion_progress|length }} languages. -The documentation has a total string count of {{ '{:,}'.format(counts[0]) }} ({{ '{:,}'.format(counts[1]) }} words). -For more information about translations, see the Python Developer’s Guide. -
-You can download the data on this page in JSON format.
-You can also find the scripts used to generate these pages here.
-Last updated at {{ generation_time.strftime('%A, %-d %B %Y, %-H:%M:%S %Z') }} (in {{ duration // 60 }}:{{ "{:02}".format(duration % 60) }} minutes).
- - - diff --git a/templates/base.html.jinja b/templates/base.html.jinja new file mode 100644 index 000000000..538fc19c3 --- /dev/null +++ b/templates/base.html.jinja @@ -0,0 +1,69 @@ + + + + + + + + +Completion: {{ '{:.2f}%'.format(project.completion) }}
+30 days progress: {{ '{:.2f}%'.format(project.change) }}
++ {% if project.built %} + View + • + {% endif %} + {% if project.contribution_link %} + Contribute + {% elif project.uses_platform %} + Contribute + {% else %} + Contribute + {% endif %} +
+ +
+ Last updated {{ generation_time.strftime('on %A %-d %B %Y at %-H:%M:%S %Z') }} (in {{ duration // 60 }} minutes and {{ duration % 60 }} seconds).
+ You can find the scripts used to generate this website on GitHub.
+ You can download the data on this page in JSON format.
+
* number of Sphinx build process warnings
Last updated at {{ generation_time.strftime('%A, %-d %B %Y, %-H:%M:%S %Z') }} (in {{ duration // 60 }}:{{ "{:02}".format(duration % 60) }} minutes).
- - +{% endblock %}