Skip to content

Commit 4236998

Browse files
committed
Refactor, introduce named tuples, iterate down to 3.6 to include Hungarian language progress, 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
1 parent 33bd29b commit 4236998

File tree

5 files changed

+139
-72
lines changed

5 files changed

+139
-72
lines changed

completion.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import json
2+
from collections.abc import Iterator
23
from functools import cache
34
from pathlib import Path
45
from tempfile import TemporaryDirectory
5-
from typing import Literal
6+
from typing import Literal, NamedTuple
67

78
import git
89
from potodo import potodo
@@ -11,31 +12,40 @@
1112

1213

1314
@cache
14-
def branches_from_devguide(devguide_dir: Path) -> list[str]:
15+
def latest_branch_from_devguide(devguide_dir: Path) -> str:
1516
p = devguide_dir.joinpath('include/release-cycle.json')
16-
data = json.loads(p.read_text())
17-
return [
18-
branch for branch in data if data[branch]['status'] in ('bugfix', 'security')
19-
]
17+
for branch in (data := json.loads(p.read_text())):
18+
if data[branch]['status'] in ('bugfix', 'security'):
19+
return branch
20+
raise ValueError(f'Supported release not found in {p}')
2021

2122

22-
def get_completion(
23-
clones_dir: str, repo: str
24-
) -> tuple[float, int, str | Literal[False]]:
23+
def iterate_branches(latest: str) -> Iterator[str]:
24+
yield latest
25+
major, minor = latest.split('.')
26+
while int(minor) > 6: # hu has 3.6 branch, hi has 3.7
27+
minor = str(int(minor) - 1)
28+
yield f'{major}.{minor}'
29+
yield 'master'
30+
31+
32+
def get_completion(clones_dir: str, repo: str) -> tuple[float, 'TranslatorsData']:
2533
clone_path = Path(clones_dir, repo)
26-
for branch in branches_from_devguide(Path(clones_dir, 'devguide')) + ['master']:
34+
for branch in iterate_branches(
35+
latest_branch_from_devguide(Path(clones_dir, 'devguide'))
36+
):
2737
try:
2838
git.Repo.clone_from(
2939
f'https://github.com/{repo}.git', clone_path, branch=branch
3040
)
3141
except git.GitCommandError:
3242
print(f'failed to clone {repo} {branch}')
33-
translators_number = 0
34-
translators_link: str | Literal[False] = False
43+
translators_data = TranslatorsData(0, False)
3544
continue
3645
else:
3746
translators_number = translators.get_number(clone_path)
3847
translators_link = translators.get_link(clone_path, repo, branch)
48+
translators_data = TranslatorsData(translators_number, translators_link)
3949
break
4050
with TemporaryDirectory() as tmpdir:
4151
completion = potodo.merge_and_scan_path(
@@ -45,4 +55,14 @@ def get_completion(
4555
hide_reserved=False,
4656
api_url='',
4757
).completion
48-
return completion, translators_number, translators_link
58+
return completion, translators_data
59+
60+
61+
class TranslatorsData(NamedTuple):
62+
number: int
63+
link: str | Literal[False]
64+
65+
66+
if __name__ == '__main__':
67+
for branch in iterate_branches('3.13'):
68+
print(branch)

generate.py

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
import subprocess
1212
from collections.abc import Iterator
1313
from datetime import datetime, timezone
14+
from logging import info
1415
from pathlib import Path
1516
from tempfile import TemporaryDirectory
16-
from typing import cast, Literal
17+
from typing import cast, NamedTuple
1718

1819
from git import Repo
1920
from jinja2 import Template
@@ -22,40 +23,24 @@
2223
import repositories
2324
import build_status
2425
import visitors
25-
from completion import branches_from_devguide, get_completion
26+
from completion import latest_branch_from_devguide, get_completion, TranslatorsData
27+
from repositories import Language
2628

2729
generation_time = datetime.now(timezone.utc)
2830

2931

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-
):
32+
def get_completion_progress() -> Iterator['LanguageProjectData']:
4733
with TemporaryDirectory() as clones_dir:
4834
Repo.clone_from(
4935
'https://github.com/python/devguide.git',
5036
devguide_dir := Path(clones_dir, 'devguide'),
5137
depth=1,
5238
)
53-
latest_branch = branches_from_devguide(devguide_dir)[0]
5439
Repo.clone_from(
5540
'https://github.com/python/cpython.git',
5641
Path(clones_dir, 'cpython'),
5742
depth=1,
58-
branch=latest_branch,
43+
branch=latest_branch_from_devguide(devguide_dir),
5944
)
6045
subprocess.run(
6146
['make', '-C', Path(clones_dir, 'cpython/Doc'), 'venv'], check=True
@@ -64,35 +49,33 @@ def get_completion_progress() -> (
6449
['make', '-C', Path(clones_dir, 'cpython/Doc'), 'gettext'], check=True
6550
)
6651
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)
52+
for language, repo in repositories.get_languages_and_repos(devguide_dir):
53+
built = language.code in languages_built
54+
in_switcher = languages_built.get(language.code)
55+
tx = language.code in contribute.pulling_from_transifex
56+
contrib_link = contribute.get_contrib_link(language.code, repo)
7257
if not repo:
73-
yield (
74-
lang,
75-
lang_name,
58+
yield LanguageProjectData(
59+
language,
7660
cast(str, repo),
7761
0.0,
78-
0,
79-
False,
62+
TranslatorsData(0, False),
8063
0,
8164
built,
8265
in_switcher,
8366
False,
8467
None,
8568
)
8669
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,
70+
completion, translators_data = get_completion(clones_dir, repo)
71+
visitors_num = (
72+
visitors.get_number_of_visitors(language.code) if built else 0
73+
)
74+
yield LanguageProjectData(
75+
language,
9276
repo,
9377
completion,
94-
translators,
95-
translators_link,
78+
translators_data,
9679
visitors_num,
9780
built,
9881
in_switcher,
@@ -101,11 +84,26 @@ def get_completion_progress() -> (
10184
)
10285

10386

87+
class LanguageProjectData(NamedTuple):
88+
language: Language
89+
repository: str
90+
completion: float
91+
translators: 'TranslatorsData'
92+
visitors: int
93+
built: bool
94+
in_switcher: bool | None
95+
uses_platform: bool
96+
contribution_link: str | None
97+
98+
10499
if __name__ == '__main__':
100+
info(f'starting at {generation_time}')
105101
template = Template(Path('template.html.jinja').read_text())
106102

107103
output = template.render(
108-
completion_progress=get_completion_progress(), generation_time=generation_time
104+
completion_progress=list(get_completion_progress()),
105+
generation_time=generation_time,
106+
duration=(datetime.now(timezone.utc) - generation_time).seconds,
109107
)
110108

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

repositories.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections.abc import Iterator
33
from pathlib import Path
44
from tempfile import TemporaryDirectory
5+
from typing import NamedTuple
56

67
from docutils import core
78
from docutils.nodes import table, row
@@ -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,15 @@ 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+
class Language(NamedTuple):
37+
code: str
38+
name: str
3039

3140

3241
if __name__ == '__main__':

template.html.jinja

Lines changed: 18 additions & 17 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>
@@ -17,44 +18,44 @@
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>Last updated at {{ generation_time.strftime('%A, %d %B %Y, %X %Z') }}.</p>
58+
<p>Last updated at {{ generation_time.strftime('%A, %-d %B %Y, %-H:%M:%S %Z') }} (in {{ duration // 60 }}:{{ "{:02}".format(duration % 60) }} minutes).</p>
5859
</body>
5960
<script>
6061
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)