Skip to content

Commit 8115356

Browse files
authored
Refactor, improvements (#42)
Introduce dataclasses, track generation time, ensure two decimal places in completion number, remove leading zero in date from day and hour, include TRANSLATORS file in translators number calc, add charset meta tag, add visitors column comment, break bottom description into paragraphs, refactor code in main module
1 parent add228d commit 8115356

File tree

5 files changed

+125
-86
lines changed

5 files changed

+125
-86
lines changed

completion.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
from dataclasses import dataclass
23
from functools import cache
34
from pathlib import Path
45
from tempfile import TemporaryDirectory
@@ -19,9 +20,7 @@ def branches_from_devguide(devguide_dir: Path) -> list[str]:
1920
]
2021

2122

22-
def get_completion(
23-
clones_dir: str, repo: str
24-
) -> tuple[float, int, str | Literal[False]]:
23+
def get_completion(clones_dir: str, repo: str) -> tuple[float, 'TranslatorsData']:
2524
clone_path = Path(clones_dir, repo)
2625
for branch in branches_from_devguide(Path(clones_dir, 'devguide')) + ['master']:
2726
try:
@@ -30,12 +29,12 @@ def get_completion(
3029
)
3130
except git.GitCommandError:
3231
print(f'failed to clone {repo} {branch}')
33-
translators_number = 0
34-
translators_link: str | Literal[False] = False
32+
translators_data = TranslatorsData(0, False)
3533
continue
3634
else:
3735
translators_number = translators.get_number(clone_path)
3836
translators_link = translators.get_link(clone_path, repo, branch)
37+
translators_data = TranslatorsData(translators_number, translators_link)
3938
break
4039
with TemporaryDirectory() as tmpdir:
4140
completion = potodo.merge_and_scan_path(
@@ -45,4 +44,10 @@ def get_completion(
4544
hide_reserved=False,
4645
api_url='',
4746
).completion
48-
return completion, translators_number, translators_link
47+
return completion, translators_data
48+
49+
50+
@dataclass(frozen=True)
51+
class TranslatorsData:
52+
number: int
53+
link: str | Literal[False]

generate.py

Lines changed: 41 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,25 @@
1010
# ///
1111
import subprocess
1212
from collections.abc import Iterator
13+
from dataclasses import dataclass
1314
from datetime import datetime, timezone
15+
from logging import info
1416
from pathlib import Path
1517
from tempfile import TemporaryDirectory
16-
from typing import cast, Literal
1718

1819
from git import Repo
1920
from jinja2 import Template
2021

2122
import contribute
22-
import repositories
2323
import build_status
24-
import visitors
25-
from completion import branches_from_devguide, get_completion
24+
from visitors import get_number_of_visitors
25+
from completion import branches_from_devguide, get_completion, TranslatorsData
26+
from repositories import get_languages_and_repos, Language
2627

2728
generation_time = datetime.now(timezone.utc)
2829

2930

30-
def get_completion_progress() -> (
31-
Iterator[
32-
tuple[
33-
str,
34-
str,
35-
str,
36-
float,
37-
int,
38-
str | Literal[False],
39-
int,
40-
bool,
41-
bool | None,
42-
bool,
43-
str | None,
44-
]
45-
]
46-
):
31+
def get_completion_progress() -> Iterator['LanguageProjectData']:
4732
with TemporaryDirectory() as clones_dir:
4833
Repo.clone_from(
4934
'https://github.com/python/devguide.git',
@@ -53,59 +38,56 @@ def get_completion_progress() -> (
5338
latest_branch = branches_from_devguide(devguide_dir)[0]
5439
Repo.clone_from(
5540
'https://github.com/python/cpython.git',
56-
Path(clones_dir, 'cpython'),
41+
cpython_dir := Path(clones_dir, 'cpython'),
5742
depth=1,
5843
branch=latest_branch,
5944
)
60-
subprocess.run(
61-
['make', '-C', Path(clones_dir, 'cpython/Doc'), 'venv'], check=True
62-
)
63-
subprocess.run(
64-
['make', '-C', Path(clones_dir, 'cpython/Doc'), 'gettext'], check=True
65-
)
45+
subprocess.run(['make', '-C', cpython_dir / 'Doc', 'venv'], check=True)
46+
subprocess.run(['make', '-C', cpython_dir / 'Doc', 'gettext'], check=True)
6647
languages_built = dict(build_status.get_languages())
67-
for lang, lang_name, repo in repositories.get_languages_and_repos(devguide_dir):
68-
built = lang in languages_built
69-
in_switcher = languages_built.get(lang)
70-
tx = lang in contribute.pulling_from_transifex
71-
contrib_link = contribute.get_contrib_link(lang, repo)
72-
if not repo:
73-
yield (
74-
lang,
75-
lang_name,
76-
cast(str, repo),
77-
0.0,
78-
0,
79-
False,
80-
0,
81-
built,
82-
in_switcher,
83-
False,
84-
None,
85-
)
86-
continue
87-
completion, translators, translators_link = get_completion(clones_dir, repo)
88-
visitors_num = visitors.get_number_of_visitors(lang) if built else 0
89-
yield (
90-
lang,
91-
lang_name,
48+
for language, repo in get_languages_and_repos(devguide_dir):
49+
built = language.code in languages_built
50+
if repo:
51+
completion, translators_data = get_completion(clones_dir, repo)
52+
visitors_num = get_number_of_visitors(language.code) if built else 0
53+
else:
54+
completion = 0.0
55+
translators_data = TranslatorsData(0, False)
56+
visitors_num = 0
57+
yield LanguageProjectData(
58+
language,
9259
repo,
9360
completion,
94-
translators,
95-
translators_link,
61+
translators_data,
9662
visitors_num,
9763
built,
98-
in_switcher,
99-
tx,
100-
contrib_link,
64+
in_switcher=languages_built.get(language.code),
65+
uses_platform=language.code in contribute.pulling_from_transifex,
66+
contribution_link=contribute.get_contrib_link(language.code, repo),
10167
)
10268

10369

70+
@dataclass(frozen=True)
71+
class LanguageProjectData:
72+
language: Language
73+
repository: str | None
74+
completion: float
75+
translators: TranslatorsData
76+
visitors: int
77+
built: bool
78+
in_switcher: bool | None
79+
uses_platform: bool
80+
contribution_link: str | None
81+
82+
10483
if __name__ == '__main__':
84+
info(f'starting at {generation_time}')
10585
template = Template(Path('template.html.jinja').read_text())
10686

10787
output = template.render(
108-
completion_progress=get_completion_progress(), generation_time=generation_time
88+
completion_progress=list(get_completion_progress()),
89+
generation_time=generation_time,
90+
duration=(datetime.now(timezone.utc) - generation_time).seconds,
10991
)
11092

11193
Path('index.html').write_text(output)

repositories.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import re
22
from collections.abc import Iterator
3+
from dataclasses import dataclass
34
from pathlib import Path
45
from tempfile import TemporaryDirectory
56

@@ -10,7 +11,7 @@
1011

1112
def get_languages_and_repos(
1213
devguide_dir: Path,
13-
) -> Iterator[tuple[str, str, str | None]]:
14+
) -> Iterator[tuple['Language', str | None]]:
1415
translating = devguide_dir.joinpath('documentation/translating.rst').read_text()
1516
doctree = core.publish_doctree(translating)
1617

@@ -26,7 +27,16 @@ def get_languages_and_repos(
2627
language_name = language_match.group(1)
2728
language_code = language_match.group(2).lower().replace('_', '-')
2829
repo_match = re.match(':github:`GitHub <(.*)>`', repo)
29-
yield language_code, language_name, repo_match and repo_match.group(1)
30+
yield (
31+
Language(language_code, language_name),
32+
repo_match and repo_match.group(1),
33+
)
34+
35+
36+
@dataclass(frozen=True)
37+
class Language:
38+
code: str
39+
name: str
3040

3141

3242
if __name__ == '__main__':

template.html.jinja

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<head>
33
<title>Python Docs Translation Dashboard</title>
44
<link rel="stylesheet" href="style.css">
5+
<meta charset="UTF-8">
56
</head>
67
<body>
78
<h1>Python Docs Translation Dashboard</h1>
@@ -11,50 +12,52 @@
1112
<th>language</th>
1213
<th>contribute</th>
1314
<th>build</th>
14-
<th><a href="https://plausible.io/data-policy#how-we-count-unique-users-without-cookies" target="_blank">visitors</a></th>
15+
<th>visitors*</th>
1516
<th>translators</th>
1617
<th>completion</th>
1718
</tr>
1819
</thead>
1920
<tbody>
20-
{% for language, language_name, repo, completion, translators, translators_link, visitors, build, in_switcher, on_platform, contrib_link in completion_progress | sort(attribute='3,4') | reverse %}
21+
{% for project in completion_progress | sort(attribute='completion,translators.number') | reverse %}
2122
<tr>
22-
<td data-label="language">{{ language_name }} ({{ language }})</td>
23+
<td data-label="language">{{ project.language.name }} ({{ project.language.code }})</td>
2324
<td data-label="contribute">
24-
{% if contrib_link %}<a href="{{ contrib_link }}" target="_blank">{% endif %}
25-
{% if on_platform %}platform{% else %}repository{% endif %}
26-
{% if contrib_link %}</a>{% endif %}
25+
{% if project.contribution_link %}<a href="{{ project.contribution_link }}" target="_blank">{% endif %}
26+
{% if project.uses_platform %}platform{% else %}repository{% endif %}
27+
{% if project.contribution_link %}</a>{% endif %}
2728
</td>
2829
<td data-label="build">
29-
{% if build %}
30-
<a href="https://docs.python.org/{{ language }}/" target="_blank">✓{% if in_switcher %} in switcher{% endif %}</a>
30+
{% if project.built %}
31+
<a href="https://docs.python.org/{{ project.language.code }}/" target="_blank">✓{% if project.in_switcher %} in switcher{% endif %}</a>
3132
{% else %}
3233
3334
{% endif %}
3435
</td>
3536
<td data-label="visitors">
36-
{% if build %}
37-
<a href="https://plausible.io/docs.python.org?filters=((contains,page,(/{{ language }}/)))" target="_blank">
38-
{{ '{:,}'.format(visitors) }}
37+
{% if project.built %}
38+
<a href="https://plausible.io/docs.python.org?filters=((contains,page,(/{{ project.language.code }}/)))" target="_blank">
39+
{{ '{:,}'.format(project.visitors) }}
3940
</a>
4041
{% else %}
41-
{{ '{:,}'.format(visitors) }}
42+
{{ '{:,}'.format(project.visitors) }}
4243
{% endif %}
4344
</td>
4445
<td data-label="translators">
45-
{% if translators_link %}<a href="{{ translators_link }}" target="_blank">{% endif %}
46-
{{ translators }}
47-
{% if translators_link %}</a>{% endif %}
46+
{% if project.translators.link %}<a href="{{ project.translators.link }}" target="_blank">{% endif %}
47+
{{ project.translators.number }}
48+
{% if project.translators.link %}</a>{% endif %}
4849
</td>
4950
<td data-label="completion">
50-
<div class="progress-bar" style="width: {{ completion }}%;">{{ completion | round(2) }}%</div>
51-
<div class="progress-bar-outer-label">{{ completion | round(2) }}%</div>
51+
<div class="progress-bar" style="width: {{ project.completion }}%;">{{ "{:.2f}".format(project.completion) }}%</div>
52+
<div class="progress-bar-outer-label">{{ "{:.2f}".format(project.completion) }}%</div>
5253
</td>
5354
</tr>
5455
{% endfor %}
5556
</tbody>
5657
</table>
57-
<p>For more information about translations, see the <a href="https://devguide.python.org/documentation/translating/" target="_blank">Python Developer’s Guide</a>. <br> Last updated at {{ generation_time.strftime('%A, %d %B %Y, %X %Z') }}.</p>
58+
<p>* sum of <a href="https://plausible.io/data-policy#how-we-count-unique-users-without-cookies" target="_blank">daily unique visitors</a> since 8 June 2024</p>
59+
<p>For more information about translations, see the <a href="https://devguide.python.org/documentation/translating/" target="_blank">Python Developer’s Guide</a>.</p>
60+
<p>Last updated at {{ generation_time.strftime('%A, %-d %B %Y, %-H:%M:%S %Z') }} (in {{ duration // 60 }}:{{ "{:02}".format(duration % 60) }} minutes).</p>
5861
</body>
5962
<script>
6063
function updateProgressBarVisibility() {

translators.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from collections.abc import Iterator
22
from pathlib import Path
3+
from re import fullmatch
4+
from tempfile import TemporaryDirectory
35
from typing import Literal
46

57
from git import Repo
@@ -9,7 +11,8 @@
911
def get_number(path: Path) -> int:
1012
from_headers = len(set(yield_from_headers(path)))
1113
from_git_history = get_number_from_git_history(path)
12-
return max(from_headers, from_git_history)
14+
from_translators_file = len(get_from_translators_file(path))
15+
return max(from_headers, from_git_history, from_translators_file)
1316

1417

1518
def get_number_from_git_history(path: Path) -> int:
@@ -33,8 +36,44 @@ def yield_from_headers(path: Path) -> Iterator[str]:
3336
yield translator
3437

3538

39+
def get_from_translators_file(path: Path) -> list[str]:
40+
if not (file := path.joinpath('TRANSLATORS')).exists():
41+
return []
42+
return list(
43+
line
44+
for line in file.read_text().splitlines()
45+
if line != 'Translators'
46+
and not fullmatch(r'-*', line)
47+
and not line.startswith('# ')
48+
)
49+
50+
3651
def get_link(clone_path: Path, repo: str, branch: str) -> str | Literal[False]:
3752
return (
3853
clone_path.joinpath('TRANSLATORS').exists()
3954
and f'https://github.com/{repo}/blob/{branch}/TRANSLATORS'
4055
)
56+
57+
58+
if __name__ == '__main__':
59+
for lang, branch in (
60+
('es', '3.13'),
61+
('hu', '3.6'),
62+
('pl', '3.13'),
63+
('zh-tw', '3.13'),
64+
('id', '3.9'),
65+
('fr', '3.13'),
66+
('hu', '3.6'),
67+
):
68+
with TemporaryDirectory() as directory:
69+
Repo.clone_from(
70+
f'https://github.com/python/python-docs-{lang}',
71+
directory,
72+
branch=branch,
73+
)
74+
from_headers = len(set(yield_from_headers(path := Path(directory))))
75+
from_git_history = get_number_from_git_history(path)
76+
from_translators_file = len(get_from_translators_file(path))
77+
print(
78+
f'{lang}: {from_headers=}, {from_git_history=}, {from_translators_file=}'
79+
)

0 commit comments

Comments
 (0)