update
This commit is contained in:
@@ -29,8 +29,21 @@ BADGE = ("kern", "optional")
|
||||
# „funktionen" gehört der Inhaltsprüfung, hat aber dieselbe Gestalt.
|
||||
ZEILENFELDER = ("regeln", "testen", "funktionen")
|
||||
|
||||
# Ein Funktionstitel nennt eine Handlung. Ohne Verb am Ende steht dort ein Bereich
|
||||
# („Galerie im Modal") oder ein Zustand („Preis aktualisiert") — beides beschreibt
|
||||
# nichts, was man für sich testen könnte. Deutsche Infinitive enden auf -en, -ln,
|
||||
# -rn; wie Füllwörter und Wortgrenzen ist auch das an die Sprache gebunden.
|
||||
_INFINITIV = re.compile(r"(?:e[lr]?n|ln|rn)$", re.I)
|
||||
# Eine Handlung steht als Infinitiv am Anfang oder am Ende der Zeile („Bestätigen im
|
||||
# Modal", „Text ändern") oder beginnt mit einer Taste. Alles andere ist ein Ergebnis.
|
||||
# Nur Anfang und Ende, nicht irgendwo: deutsche Mehrzahl endet auch auf -en.
|
||||
_TASTE = re.compile(r"^(?:LMB|RMB|MMB|Klick|Doppelklick|Enter|Escape|Strg|Tab)\b", re.I)
|
||||
|
||||
SATZENDE = ".;"
|
||||
_KLAMMER = re.compile(r"[()/]")
|
||||
# Eine Vorlage ist kein Inhalt. Das Modell schlug `<testschritt>` als Testschritt
|
||||
# vor — formal richtig, gemeint war „hier fehlt noch etwas".
|
||||
_LUECKE = re.compile(r"<[^<>]*>|\.\.\.|\bTODO\b|\bXXX\b", re.I)
|
||||
_STRICH = re.compile(r"^[-*]\s*")
|
||||
|
||||
|
||||
@@ -68,9 +81,15 @@ def _umschreiben(bereich: str, feld: str, zeile: str) -> list[str]:
|
||||
gruende = []
|
||||
if _KLAMMER.search(zeile):
|
||||
gruende.append("Klammer oder Schrägstrich")
|
||||
if _LUECKE.search(zeile):
|
||||
gruende.append("Platzhalter statt Inhalt")
|
||||
grenze = WORTGRENZEN.get((bereich, feld))
|
||||
if grenze and len(zeile.split()) > grenze:
|
||||
gruende.append(f"Mehr als {grenze} Wörter")
|
||||
if bereich == "funktion" and feld == "titel":
|
||||
woerter = zeile.split()
|
||||
if woerter and not _INFINITIV.search(woerter[-1]):
|
||||
gruende.append("Kein Verb im Infinitiv am Ende")
|
||||
if feld == "ziel":
|
||||
saetze = [s.strip() for s in re.split(r"[.!?]+", zeile) if s.strip()]
|
||||
if len(saetze) > ZIEL_SAETZE:
|
||||
@@ -80,6 +99,30 @@ def _umschreiben(bereich: str, feld: str, zeile: str) -> list[str]:
|
||||
return gruende
|
||||
|
||||
|
||||
def _handlung(zeile: str) -> bool:
|
||||
woerter = zeile.split()
|
||||
if not woerter:
|
||||
return True
|
||||
return bool(_TASTE.match(zeile) or _INFINITIV.search(woerter[-1])
|
||||
or _INFINITIV.search(woerter[0]))
|
||||
|
||||
|
||||
def _zweiter_ablauf(zeilen: list[str]) -> str:
|
||||
"""Die Zeile, ab der ein zweiter Durchlauf beginnt — sonst leer.
|
||||
|
||||
Ein Ablauf ist: handeln, handeln, Ergebnis. Steht nach dem Ergebnis wieder eine
|
||||
Handlung, sind es zwei Abläufe — und damit zwei Funktionen. Genau so entstand
|
||||
„Bildergalerie bedienen" mit blättern, Miniatur wählen und Großansicht in einem.
|
||||
"""
|
||||
ergebnis = False
|
||||
for zeile in zeilen:
|
||||
if ergebnis and _handlung(zeile):
|
||||
return zeile
|
||||
if not _handlung(zeile):
|
||||
ergebnis = True
|
||||
return ""
|
||||
|
||||
|
||||
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."""
|
||||
@@ -94,13 +137,28 @@ def _zeile(bereich: str, feld: str, zeile: str) -> list[dict]:
|
||||
def befunde(bereich: str, felder: dict) -> list[dict]:
|
||||
"""Alle messbaren Verstöße einer Prüfstelle, in Reihenfolge der Felder."""
|
||||
raus = []
|
||||
# Ein bloßer Titel beschreibt nichts — und lässt sich über alles stülpen. Der
|
||||
# Prüfer hielt damit einen Druckknopf für „Modal mit Tastatur steuern".
|
||||
# Nur wenn die ganze Funktion vorliegt: bei einem einzelnen Feld sagt das Fehlen
|
||||
# der anderen nichts.
|
||||
inhaltsfelder = ("ziel", "regeln", "testen")
|
||||
if bereich == "funktion" and all(f in felder for f in inhaltsfelder) \
|
||||
and not any((felder.get(f) or "").strip() for f in inhaltsfelder):
|
||||
raus.append(_problem("testen", "", "Funktion ohne Ziel, Regeln und Testen"))
|
||||
return 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 ""):
|
||||
zeilen = _zeilen(feld, text or "")
|
||||
for zeile in zeilen:
|
||||
raus += _zeile(bereich, feld, zeile)
|
||||
if feld == "testen" and not raus:
|
||||
# Erst prüfen, wenn die Zeilen für sich in Ordnung sind: sonst hängen
|
||||
# zwei Vorschläge am selben Feld und der zweite findet seine Stelle nicht.
|
||||
if bruch := _zweiter_ablauf(zeilen):
|
||||
raus.append(_problem(feld, bruch, "Zweiter Ablauf in einer Funktion"))
|
||||
return raus
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user