update
This commit is contained in:
@@ -403,16 +403,21 @@ def _konsens(fassungen: list[dict]) -> dict:
|
||||
return max(fassungen, key=naehe)
|
||||
|
||||
|
||||
def _mehrheit(proben: list[dict]) -> dict:
|
||||
"""Nur was mehrere Stimmen unabhängig bemängeln, wird ein Vorschlag.
|
||||
def _mehrheit(proben: list[dict], gemessen: tuple = ()) -> dict:
|
||||
"""Nur was alle Stimmen unabhängig bemängeln, wird ein Vorschlag.
|
||||
|
||||
Bei Grenzfällen urteilt ein Modell mal so, mal so — einzelne Stimmen führten
|
||||
dazu, dass jede Runde andere Felder aufschlugen und der Lauf nie zur Ruhe kam.
|
||||
|
||||
Gemessene Verstöße sind davon ausgenommen. Sie stehen fest, bevor gefragt wird;
|
||||
zur Abstimmung steht nur noch der Wortlaut. Vorher verschwand ein gezählter
|
||||
Verstoß, sobald eine einzige Stimme das Feld nicht anfasste.
|
||||
"""
|
||||
zaehler = Counter(feld for p in proben for feld in p)
|
||||
return {
|
||||
feld: _konsens([p[feld] for p in proben if feld in p])
|
||||
for feld, treffer in zaehler.items() if treffer >= MEHRHEIT
|
||||
for feld, treffer in zaehler.items()
|
||||
if treffer >= (1 if feld in gemessen else MEHRHEIT)
|
||||
}
|
||||
|
||||
|
||||
@@ -426,7 +431,18 @@ async def _neue_felder(system: str, aufgabe: dict, offen: list, erlaubt: tuple,
|
||||
gute = [p for p in proben if isinstance(p, dict)]
|
||||
if not gute:
|
||||
raise next(p for p in proben if isinstance(p, BaseException))
|
||||
return _mehrheit(gute)
|
||||
gemessen = tuple({b["feld"] for b in offen})
|
||||
vorschlaege = _mehrheit(gute, gemessen)
|
||||
|
||||
# Bleibt ein gezählter Verstoß ohne Vorschlag, greift der eigene Ersatz des
|
||||
# Messers. Sonst bliebe ein Fehler stehen, den der Code sicher kennt.
|
||||
for b in offen:
|
||||
if b["feld"] not in vorschlaege and b["vorschlag"]:
|
||||
alt = struktur._zeilen(b["feld"], aufgabe["felder"].get(b["feld"], ""))
|
||||
neu = [b["vorschlag"] if z == b["original"] else z for z in alt]
|
||||
vorschlaege[b["feld"]] = {"neu": "\n".join(neu), "grund": b["problem"],
|
||||
"regel": "Vom System gemessen"}
|
||||
return vorschlaege
|
||||
|
||||
|
||||
async def _pruefe(stil: dict, aufgabe: dict) -> list[dict]:
|
||||
@@ -457,38 +473,73 @@ async def _stil_aufgaben(projekt: str):
|
||||
"""Prüfstellen der Stilprüfung samt der Kataloge, die sie brauchen."""
|
||||
stil = {b: _stil_lesen(b) for b in BEREICHE}
|
||||
liste = [a for f in features.liste(projekt) for a in aufgaben(f)]
|
||||
return liste, lambda a: _pruefe(stil, a)
|
||||
return liste, lambda a, _zaehl: _pruefe(stil, a)
|
||||
|
||||
|
||||
async def _inhalt_aufgaben(projekt: str):
|
||||
"""Ein Abgleich über alle Karten — Vollständigkeit ist eine Eigenschaft der
|
||||
Menge, nicht der einzelnen Karte."""
|
||||
def _quelle(projekt: str):
|
||||
alle = features.liste(projekt)
|
||||
quelle = quellcode.dateien()
|
||||
if not quelle:
|
||||
raise ValueError("Kein Quelltext gefunden")
|
||||
return alle, quelle
|
||||
|
||||
|
||||
async def _feature_aufgaben(projekt: str):
|
||||
"""Ein Lauf über alle Funktionen: steht hinter jeder auch Code?
|
||||
|
||||
Ein Aufruf statt vieler, weil das Ergebnis je Karte gebündelt wird — zwei
|
||||
Vorschläge für dieselbe Funktionsliste wären nicht unabhängig anwendbar.
|
||||
Der Zähler läuft trotzdem je Funktion mit.
|
||||
"""
|
||||
alle, quelle = _quelle(projekt)
|
||||
text = quellcode.als_text(quelle)
|
||||
anzahl = sum(len(f["sektionen"]["funktionen"]) for f in alle)
|
||||
|
||||
async def abgleich(_):
|
||||
probleme, _inv = await inhaltspruefung.pruefe(alle, quelle, text)
|
||||
return probleme
|
||||
async def abgleich(_, zaehl):
|
||||
return await inhaltspruefung.pruefe_feature_im_code(alle, quelle, text, zaehl)
|
||||
|
||||
return [{"feature": "", "funktion": ""}], abgleich
|
||||
return _platzhalter(anzahl), abgleich
|
||||
|
||||
|
||||
ARTEN = {"stil": _stil_aufgaben, "inhalt": _inhalt_aufgaben}
|
||||
async def _code_aufgaben(projekt: str):
|
||||
"""Ein Lauf über alle Bedienstellen — Vollständigkeit ist eine Eigenschaft der
|
||||
Menge, nicht der einzelnen Karte."""
|
||||
alle, quelle = _quelle(projekt)
|
||||
punkte = quellcode.interaktionen(quelle)
|
||||
|
||||
async def abgleich(_, zaehl):
|
||||
return await inhaltspruefung.pruefe_code_im_feature(alle, quelle, zaehl)
|
||||
|
||||
return _platzhalter(len(punkte)), abgleich
|
||||
|
||||
|
||||
def _platzhalter(anzahl: int) -> list:
|
||||
"""Eine echte Aufgabe, der Rest nur für den Fortschrittsbalken."""
|
||||
return [True] + [None] * max(0, anzahl - 1)
|
||||
|
||||
|
||||
ARTEN = {
|
||||
"stil": _stil_aufgaben,
|
||||
"feature": _feature_aufgaben, # Feature im Code: was steht ohne Code da?
|
||||
"code": _code_aufgaben, # Code im Feature: was fehlt auf den Karten?
|
||||
}
|
||||
|
||||
|
||||
async def _lauf(projekt: str, liste: list[dict], arbeite):
|
||||
lauf = _laeufe[projekt]
|
||||
schranke = asyncio.Semaphore(config.PRUEFUNG_PARALLEL)
|
||||
|
||||
def zaehl():
|
||||
lauf["fertig"] += 1
|
||||
|
||||
async def eine(aufgabe):
|
||||
if aufgabe is None:
|
||||
return # Platzhalter: zählt der Sammellauf selbst hoch
|
||||
async with schranke:
|
||||
if _laeufe.get(projekt) is not lauf:
|
||||
return # verworfen (Panel geschlossen) — keine weitere Anfrage
|
||||
try:
|
||||
lauf["probleme"].extend(await arbeite(aufgabe))
|
||||
lauf["probleme"].extend(await arbeite(aufgabe, zaehl))
|
||||
except Exception as e:
|
||||
# Zählt trotzdem als erledigt, sonst hängt der Fortschritt.
|
||||
# Die Meldung nennt die Zahl, damit „100 %" nicht vollständig vortäuscht.
|
||||
@@ -496,7 +547,8 @@ async def _lauf(projekt: str, liste: list[dict], arbeite):
|
||||
lauf["meldung"] = (
|
||||
f"{lauf['fehler']} von {lauf['gesamt']} Prüfungen fehlgeschlagen: {e}"
|
||||
)
|
||||
lauf["fertig"] += 1
|
||||
if aufgabe is not True:
|
||||
lauf["fertig"] += 1
|
||||
|
||||
try:
|
||||
await asyncio.gather(*(eine(a) for a in liste))
|
||||
|
||||
Reference in New Issue
Block a user