update
This commit is contained in:
107
backend/struktur.py
Normal file
107
backend/struktur.py
Normal file
@@ -0,0 +1,107 @@
|
||||
"""Stufe 1 der Prüfung: alles, was sich zählen lässt, zählt der Code.
|
||||
|
||||
Das Modell erfand hier Regeln, die im Katalog nicht stehen — „Im" als Füllwort,
|
||||
Satzzeichen, wo keins ist. Diese Klasse ist jetzt abschließend hier erledigt, und
|
||||
der Prompt verbietet dem Modell, sie noch einmal zu melden.
|
||||
|
||||
Ein Befund mit Vorschlag geht ohne Anfrage durch. Ein Befund ohne Vorschlag heißt:
|
||||
der Code weiß, dass die Stelle falsch ist, aber nicht, was richtig wäre — den
|
||||
Auftrag bekommt das Modell.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
# Höchstzahl Wörter je Zeile, nach (bereich, feld)
|
||||
WORTGRENZEN = {
|
||||
("feature", "titel"): 3,
|
||||
("funktion", "titel"): 5,
|
||||
("feature", "regeln"): 10,
|
||||
("funktion", "regeln"): 10,
|
||||
("funktion", "testen"): 8,
|
||||
}
|
||||
ZIEL_SAETZE = 2
|
||||
ZIEL_WOERTER = 12
|
||||
|
||||
# Abschließend. Genau diese Wörter, keine sinnverwandten.
|
||||
FUELLWOERTER = ("auch", "noch", "einfach", "jeweils", "ggf")
|
||||
BADGE = ("kern", "optional")
|
||||
ZEILENFELDER = ("regeln", "testen")
|
||||
|
||||
SATZENDE = ".;"
|
||||
_KLAMMER = re.compile(r"[()/]")
|
||||
_STRICH = re.compile(r"^[-*]\s*")
|
||||
|
||||
|
||||
def _zeilen(feld: str, text: str) -> list[str]:
|
||||
"""regeln und testen sind Listen, alles andere ist ein Block."""
|
||||
if feld in ZEILENFELDER:
|
||||
return [_STRICH.sub("", z.strip()) for z in text.splitlines() if z.strip()]
|
||||
return [text.strip()] if text.strip() else []
|
||||
|
||||
|
||||
def _problem(feld: str, original: str, problem: str, vorschlag: str = "") -> dict:
|
||||
return {"feld": feld, "original": original, "problem": problem, "vorschlag": vorschlag}
|
||||
|
||||
|
||||
def _ohne_wort(zeile: str, wort: str) -> str:
|
||||
neu = re.sub(rf"\s*\b{re.escape(wort)}\b\.?", "", zeile, flags=re.I).strip()
|
||||
return re.sub(r"\s{2,}", " ", neu)
|
||||
|
||||
|
||||
def _reparierbar(zeile: str) -> tuple[list[str], str]:
|
||||
"""Verstöße, die sich wegstreichen lassen, samt der bereinigten Zeile."""
|
||||
gruende, neu = [], zeile
|
||||
if neu[-1] in SATZENDE:
|
||||
gruende.append("Satzzeichen am Zeilenende")
|
||||
neu = neu[:-1].rstrip()
|
||||
for wort in FUELLWOERTER:
|
||||
if re.search(rf"\b{wort}\b", neu, re.I):
|
||||
gruende.append(f"Füllwort „{wort}“")
|
||||
neu = _ohne_wort(neu, wort)
|
||||
return gruende, neu
|
||||
|
||||
|
||||
def _umschreiben(bereich: str, feld: str, zeile: str) -> list[str]:
|
||||
"""Verstöße, die der Code sieht, aber nicht beheben kann."""
|
||||
gruende = []
|
||||
if _KLAMMER.search(zeile):
|
||||
gruende.append("Klammer oder Schrägstrich")
|
||||
grenze = WORTGRENZEN.get((bereich, feld))
|
||||
if grenze and len(zeile.split()) > grenze:
|
||||
gruende.append(f"Mehr als {grenze} Wörter")
|
||||
if feld == "ziel":
|
||||
saetze = [s.strip() for s in re.split(r"[.!?]+", zeile) if s.strip()]
|
||||
if len(saetze) > ZIEL_SAETZE:
|
||||
gruende.append(f"Mehr als {ZIEL_SAETZE} Sätze")
|
||||
if any(len(s.split()) > ZIEL_WOERTER for s in saetze):
|
||||
gruende.append(f"Satz mit mehr als {ZIEL_WOERTER} Wörtern")
|
||||
return gruende
|
||||
|
||||
|
||||
def _zeile(bereich: str, feld: str, zeile: str) -> list[dict]:
|
||||
"""Höchstens ein Befund je Zeile. Zwei Vorschläge für dieselbe Zeile wären nicht
|
||||
unabhängig — der zweite fände seine Stelle nach dem ersten nicht mehr."""
|
||||
gruende, neu = _reparierbar(zeile)
|
||||
offen = _umschreiben(bereich, feld, zeile)
|
||||
if offen:
|
||||
# Muss ohnehin neu geschrieben werden — dann alles in einem Zug, vom Modell
|
||||
return [_problem(feld, zeile, ", ".join(gruende + offen))]
|
||||
return [_problem(feld, zeile, ", ".join(gruende), neu)] if gruende else []
|
||||
|
||||
|
||||
def befunde(bereich: str, felder: dict) -> list[dict]:
|
||||
"""Alle messbaren Verstöße einer Prüfstelle, in Reihenfolge der Felder."""
|
||||
raus = []
|
||||
for feld, text in felder.items():
|
||||
if feld == "badge":
|
||||
if text and text not in BADGE:
|
||||
raus.append(_problem(feld, text, "Weder kern noch optional", "kern"))
|
||||
continue
|
||||
for zeile in _zeilen(feld, text or ""):
|
||||
raus += _zeile(bereich, feld, zeile)
|
||||
return raus
|
||||
|
||||
|
||||
def auftrag(befund: dict) -> str:
|
||||
"""Zeile für das Modell: der Code hat gemessen, den Ersatz schreibt das Modell."""
|
||||
return f'- {befund["feld"]} „{befund["original"]}“: {befund["problem"]}'
|
||||
Reference in New Issue
Block a user