update
This commit is contained in:
@@ -18,10 +18,18 @@ from collections import Counter
|
||||
import agents
|
||||
import config
|
||||
import features
|
||||
import inhalt as inhaltspruefung
|
||||
import quellcode
|
||||
import ledger
|
||||
import struktur
|
||||
|
||||
class LaeuftBereits(ValueError):
|
||||
"""Eigener Typ, weil 409 genau eine Bedeutung haben muss.
|
||||
|
||||
Vorher war jeder Fehlstart ein 409 — auch ein fehlendes Quellverzeichnis. Das
|
||||
Frontend liest 409 als „läuft schon" und hängt sich an einen Lauf, den es nicht
|
||||
gibt. Ergebnis: „Keine Probleme gefunden" statt der Fehlermeldung.
|
||||
"""
|
||||
|
||||
|
||||
# projektname -> {"laeuft": bool, "fertig": int, "gesamt": int, "fehler": int,
|
||||
# "probleme": [...], "meldung": str}
|
||||
_laeufe = {}
|
||||
@@ -445,7 +453,17 @@ async def _neue_felder(system: str, aufgabe: dict, offen: list, erlaubt: tuple,
|
||||
return vorschlaege
|
||||
|
||||
|
||||
async def _pruefe(stil: dict, aufgabe: dict) -> list[dict]:
|
||||
def _stilschluessel(aufgabe: dict) -> str:
|
||||
return ledger.schluessel(aufgabe["feature"], aufgabe["funktion"])
|
||||
|
||||
|
||||
def _stilabdruck(aufgabe: dict, system: str) -> str:
|
||||
"""Woran das Urteil hängt: der geprüfte Ausschnitt, der Katalog, die Frage.
|
||||
Ändert sich eines davon, ist das gemerkte Urteil von gestern."""
|
||||
return ledger.abdruck(system, inhalt(aufgabe))
|
||||
|
||||
|
||||
async def _pruefe(stil: dict, aufgabe: dict, stand: dict = None) -> list[dict]:
|
||||
"""Erst messen, dann umschreiben lassen, dann den Unterschied ausrechnen."""
|
||||
bereich = BEREICHE[aufgabe["bereich"]]
|
||||
system = SYSTEM.format(
|
||||
@@ -455,6 +473,15 @@ async def _pruefe(stil: dict, aufgabe: dict) -> list[dict]:
|
||||
hinweis=bereich["hinweis"],
|
||||
)
|
||||
offen = struktur.befunde(aufgabe["bereich"], aufgabe["felder"])
|
||||
|
||||
# Gemessene Verstöße stehen fest — die dürfen nie aus dem Gedächtnis kommen.
|
||||
# Sonst bliebe eine neu hinzugekommene Messregel an alten Karten wirkungslos.
|
||||
schluessel, wie_gehabt = _stilschluessel(aufgabe), _stilabdruck(aufgabe, system)
|
||||
if stand is not None and not offen:
|
||||
gemerkt = stand["stil"].get(schluessel)
|
||||
if gemerkt and gemerkt["abdruck"] == wie_gehabt:
|
||||
return [dict(p) for p in gemerkt["probleme"]]
|
||||
|
||||
vorschlaege = await _neue_felder(system, aufgabe, offen, bereich["felder"],
|
||||
katalogzeilen(stil[aufgabe["bereich"]]))
|
||||
|
||||
@@ -466,83 +493,33 @@ async def _pruefe(stil: dict, aufgabe: dict) -> list[dict]:
|
||||
for p in probleme:
|
||||
p["feature"] = aufgabe["feature"]
|
||||
p["funktion"] = aufgabe["funktion"]
|
||||
if stand is not None:
|
||||
stand["stil"][schluessel] = {"abdruck": wie_gehabt, "probleme": probleme}
|
||||
return probleme
|
||||
|
||||
|
||||
async def _stil_aufgaben(projekt: str):
|
||||
"""Prüfstellen der Stilprüfung samt der Kataloge, die sie brauchen."""
|
||||
"""Prüfstellen der Stilprüfung samt der Kataloge, die sie brauchen.
|
||||
|
||||
Auch hier ein Gedächtnis: 176 Anfragen je Lauf brachten den Anbieter zum
|
||||
Drosseln, und die ausgefallenen Stellen meldeten dann nichts. Unveränderter
|
||||
Ausschnitt bei unverändertem Katalog heißt: das Urteil von vorhin gilt noch.
|
||||
"""
|
||||
stil = {b: _stil_lesen(b) for b in BEREICHE}
|
||||
liste = [a for f in features.pruefbar(projekt) for a in aufgaben(f)]
|
||||
return liste, lambda a, _zaehl: _pruefe(stil, a)
|
||||
|
||||
|
||||
def _quelle(projekt: str):
|
||||
alle = features.pruefbar(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? Und beschreibt
|
||||
keine zwei Funktionen dieselbe Sache?
|
||||
|
||||
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(_, zaehl):
|
||||
return await inhaltspruefung.pruefe_feature_im_code(
|
||||
projekt, alle, quelle, text, zaehl, _melder(projekt))
|
||||
|
||||
return _platzhalter(anzahl), abgleich
|
||||
|
||||
|
||||
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(
|
||||
projekt, alle, quelle, zaehl, _melder(projekt))
|
||||
|
||||
return _platzhalter(len(punkte)), abgleich
|
||||
|
||||
|
||||
def _melder(projekt: str):
|
||||
"""Sagt dem Lauf, was schiefging, ohne ihn abzubrechen.
|
||||
|
||||
Ein Sammellauf ist eine einzige Aufgabe. Flöge er wegen einer Stelle, wären
|
||||
alle anderen Befunde weg — und der Balken stünde trotzdem auf 100 %. So bleibt
|
||||
das Ergebnis erhalten und der Fehlstand steht daneben.
|
||||
"""
|
||||
def melde(text: str, fehler: int = 0):
|
||||
if lauf := _laeufe.get(projekt):
|
||||
lauf["meldung"] = text
|
||||
lauf["fehler"] += fehler
|
||||
return melde
|
||||
|
||||
|
||||
def _platzhalter(anzahl: int) -> list:
|
||||
"""Eine echte Aufgabe, der Rest nur für den Fortschrittsbalken."""
|
||||
return [True] + [None] * max(0, anzahl - 1)
|
||||
stand = ledger.lade(projekt)
|
||||
stand["stil"] = ledger.behalte(stand["stil"], {_stilschluessel(a) for a in liste})
|
||||
return (liste,
|
||||
lambda a, _zaehl: _pruefe(stil, a, stand),
|
||||
lambda: ledger.sichere(projekt, stand))
|
||||
|
||||
|
||||
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):
|
||||
async def _lauf(projekt: str, liste: list[dict], arbeite, abschluss=None):
|
||||
lauf = _laeufe[projekt]
|
||||
schranke = asyncio.Semaphore(config.PRUEFUNG_PARALLEL)
|
||||
|
||||
@@ -570,6 +547,9 @@ async def _lauf(projekt: str, liste: list[dict], arbeite):
|
||||
try:
|
||||
await asyncio.gather(*(eine(a) for a in liste))
|
||||
finally:
|
||||
# Auch ein abgebrochener Lauf hat schon Urteile gefällt; die sind bezahlt
|
||||
if abschluss:
|
||||
abschluss()
|
||||
lauf["laeuft"] = False
|
||||
|
||||
|
||||
@@ -578,10 +558,10 @@ async def starte(projekt: str, art: str = "stil") -> dict:
|
||||
if art not in ARTEN:
|
||||
raise ValueError(f"Unbekannte Prüfart „{art}“")
|
||||
if status(projekt)["laeuft"]:
|
||||
raise ValueError("Prüfung läuft bereits")
|
||||
raise LaeuftBereits("Prüfung läuft bereits")
|
||||
agents.pruefe_konfiguration() # früher Stopp statt halbem Lauf
|
||||
# Vorbereiten vor dem Task: ein Fehler darin wäre sonst unsichtbar
|
||||
liste, arbeite = await ARTEN[art](projekt)
|
||||
liste, arbeite, *rest = await ARTEN[art](projekt)
|
||||
_laeufe[projekt] = {
|
||||
"laeuft": True,
|
||||
"art": art,
|
||||
@@ -591,7 +571,7 @@ async def starte(projekt: str, art: str = "stil") -> dict:
|
||||
"probleme": [],
|
||||
"meldung": "",
|
||||
}
|
||||
aufgabe = asyncio.create_task(_lauf(projekt, liste, arbeite))
|
||||
aufgabe = asyncio.create_task(_lauf(projekt, liste, arbeite, *rest))
|
||||
_aufgaben.add(aufgabe)
|
||||
aufgabe.add_done_callback(_aufgaben.discard)
|
||||
return _laeufe[projekt]
|
||||
|
||||
Reference in New Issue
Block a user