FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

tools extended and improved · python/python-docs-de@7d6b736 · GitHub

Commit 7d6b736

Browse files
committed
tools extended and improved
1 parent 47af148 commit 7d6b736

8 files changed

Lines changed: 3271 additions & 0 deletions

File tree

‎branch_diff.md‎

Lines changed: 568 additions & 0 deletions
Large diffs are not rendered by default.

‎jyst-woche.md‎

Lines changed: 939 additions & 0 deletions
Large diffs are not rendered by default.

‎markup_bericht.md‎

Lines changed: 382 additions & 0 deletions
Large diffs are not rendered by default.

‎tools/branch_diff.py‎

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
#!/usr/bin/env python3
2+
"""Vergleicht die Uebersetzungen zweier Branches Datei fuer Datei.
3+
4+
Fuer jede .po-Datei wird verglichen, wie viele Eintraege in beiden Branches
5+
denselben msgstr haben. Der Prozentwert bezieht sich auf die Anzahl der
6+
Eintraege im *neueren* Branch (Vorgabe 3.15), weil der die Zielversion ist:
7+
8+
Aehnlichkeit = identische Eintraege / Eintraege in 3.15
9+
10+
Ein Eintrag gilt als identisch, wenn msgid (samt msgctxt) und msgstr in
11+
beiden Branches exakt uebereinstimmen -- auch dann, wenn beide leer sind.
12+
Obsolete Eintraege (#~) und der Kopfblock bleiben aussen vor.
13+
14+
Aufruf:
15+
python branch_diff.py # 3.14 gegen 3.15
16+
python branch_diff.py 3.14 main
17+
python branch_diff.py --csv bericht.csv
18+
python branch_diff.py --nur-abweichungen
19+
20+
Es wird nichts ausgecheckt und nichts geaendert -- die Inhalte kommen
21+
ueber "git show" direkt aus den Branches.
22+
"""
23+
24+
import argparse
25+
import csv
26+
import os
27+
import subprocess
28+
import sys
29+
import tempfile
30+
31+
try:
32+
import polib
33+
except ImportError:
34+
sys.exit("polib fehlt. Im aktiven venv installieren: pip install polib")
35+
36+
AUSSCHLUSS = (".venv/", ".git/")
37+
38+
KLASSEN = [
39+
(100.0, "deckungsgleich"),
40+
(80.0, "ueber 80 %"),
41+
(50.0, "ueber 50 %"),
42+
(30.0, "ueber 30 %"),
43+
(0.0, "unter 30 %"),
44+
]
45+
46+
47+
def git(*args: str) -> str:
48+
"""git aufrufen und stdout zurueckgeben."""
49+
return subprocess.run(
50+
["git", *args], capture_output=True, text=True, check=True
51+
).stdout
52+
53+
54+
def po_dateien(branch: str) -> dict:
55+
"""Alle .po-Dateien eines Branches als {pfad: blob-hash}."""
56+
aus = {}
57+
for zeile in git("ls-tree", "-r", branch).splitlines():
58+
kopf, pfad = zeile.split("\t", 1)
59+
_modus, art, blob = kopf.split()
60+
if art != "blob" or not pfad.endswith(".po"):
61+
continue
62+
if pfad.startswith(AUSSCHLUSS):
63+
continue
64+
aus[pfad] = blob
65+
return aus
66+
67+
68+
def eintraege(blob: str) -> dict:
69+
"""Blob-Inhalt parsen: {(msgctxt, msgid): msgstr}."""
70+
inhalt = git("cat-file", "blob", blob)
71+
# polib will einen Dateipfad -- also kurz zwischenspeichern.
72+
with tempfile.NamedTemporaryFile(
73+
"w", suffix=".po", encoding="utf-8", delete=False
74+
) as f:
75+
f.write(inhalt)
76+
tmp = f.name
77+
try:
78+
po = polib.pofile(tmp)
79+
return {(e.msgctxt, e.msgid): e.msgstr for e in po if not e.obsolete}
80+
finally:
81+
os.unlink(tmp)
82+
83+
84+
def klasse_von(prozent: float) -> str:
85+
for grenze, name in KLASSEN:
86+
if prozent >= grenze:
87+
return name
88+
return KLASSEN[-1][1]
89+
90+
91+
def uebersetzt(werte) -> int:
92+
return sum(1 for v in werte if v.strip())
93+
94+
95+
def main() -> None:
96+
p = argparse.ArgumentParser(
97+
description=__doc__,
98+
formatter_class=argparse.RawDescriptionHelpFormatter,
99+
)
100+
p.add_argument("alt", nargs="?", default="3.14", help="aelterer Branch")
101+
p.add_argument("neu", nargs="?", default="3.15", help="neuerer Branch")
102+
p.add_argument("--csv", metavar="DATEI", help="Ergebnis zusaetzlich als CSV")
103+
p.add_argument(
104+
"--nur-abweichungen",
105+
action="store_true",
106+
help="deckungsgleiche Dateien in der Tabelle weglassen",
107+
)
108+
args = p.parse_args()
109+
110+
alt_dateien = po_dateien(args.alt)
111+
neu_dateien = po_dateien(args.neu)
112+
alle = sorted(set(alt_dateien) | set(neu_dateien))
113+
print(f"{len(alle)} Dateien werden verglichen ...", file=sys.stderr)
114+
115+
zeilen = []
116+
for nr, pfad in enumerate(alle, 1):
117+
if nr % 50 == 0:
118+
print(f" {nr}/{len(alle)}", file=sys.stderr)
119+
120+
a_blob = alt_dateien.get(pfad)
121+
n_blob = neu_dateien.get(pfad)
122+
123+
if a_blob is None:
124+
n = eintraege(n_blob)
125+
zeilen.append((pfad, 0, len(n), 0, uebersetzt(n.values()),
126+
0, None, f"nur in {args.neu}"))
127+
continue
128+
if n_blob is None:
129+
a = eintraege(a_blob)
130+
zeilen.append((pfad, len(a), 0, uebersetzt(a.values()), 0,
131+
0, None, f"nur in {args.alt}"))
132+
continue
133+
134+
if a_blob == n_blob:
135+
# Gleicher Blob -- Inhalt garantiert identisch, kein Parsen noetig.
136+
a = eintraege(a_blob)
137+
u = uebersetzt(a.values())
138+
zeilen.append((pfad, len(a), len(a), u, u, len(a), 100.0,
139+
"deckungsgleich"))
140+
continue
141+
142+
a = eintraege(a_blob)
143+
n = eintraege(n_blob)
144+
gleich = sum(1 for k, v in n.items() if k in a and a[k] == v)
145+
prozent = 100.0 * gleich / len(n) if n else 0.0
146+
zeilen.append((pfad, len(a), len(n), uebersetzt(a.values()),
147+
uebersetzt(n.values()), gleich, prozent,
148+
klasse_von(prozent)))
149+
150+
# --- Tabelle -----------------------------------------------------------
151+
rang = {name: i for i, (_g, name) in enumerate(KLASSEN)}
152+
rang[f"nur in {args.neu}"] = 90
153+
rang[f"nur in {args.alt}"] = 91
154+
zeilen.sort(key=lambda z: (rang.get(z[7], 99), -(z[6] or 0), z[0]))
155+
156+
kopf = (f"| Datei | Eintraege {args.alt} | Eintraege {args.neu} | "
157+
f"uebers. {args.alt} | uebers. {args.neu} | identisch | "
158+
f"Aehnlichkeit | Klasse |")
159+
print()
160+
print(kopf)
161+
print("|---|---:|---:|---:|---:|---:|---:|---|")
162+
for pfad, ea, en, ua, un, gl, pz, kl in zeilen:
163+
if args.nur_abweichungen and kl == "deckungsgleich":
164+
continue
165+
pzt = "—" if pz is None else f"{pz:.1f} %"
166+
print(f"| `{pfad}` | {ea} | {en} | {ua} | {un} | {gl} | {pzt} | {kl} |")
167+
168+
# --- Zusammenfassung ---------------------------------------------------
169+
print("\n### Zusammenfassung\n")
170+
print("| Klasse | Dateien |")
171+
print("|---|---:|")
172+
for _grenze, name in KLASSEN:
173+
anzahl = sum(1 for z in zeilen if z[7] == name)
174+
print(f"| {name} | {anzahl} |")
175+
for name in (f"nur in {args.neu}", f"nur in {args.alt}"):
176+
anzahl = sum(1 for z in zeilen if z[7] == name)
177+
if anzahl:
178+
print(f"| {name} | {anzahl} |")
179+
print(f"| **gesamt** | **{len(zeilen)}** |")
180+
181+
if args.csv:
182+
with open(args.csv, "w", newline="", encoding="utf-8") as f:
183+
w = csv.writer(f)
184+
w.writerow(["datei", f"eintraege_{args.alt}", f"eintraege_{args.neu}",
185+
f"uebersetzt_{args.alt}", f"uebersetzt_{args.neu}",
186+
"identisch", "aehnlichkeit_prozent", "klasse"])
187+
for z in zeilen:
188+
w.writerow([z[0], z[1], z[2], z[3], z[4], z[5],
189+
"" if z[6] is None else f"{z[6]:.1f}", z[7]])
190+
print(f"\nCSV geschrieben: {args.csv}", file=sys.stderr)
191+
192+
193+
if __name__ == "__main__":
194+
main()

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL