Bildleser: den Titel gegenlesen -- und ein Protokollwort, das kein Satz war
ZWEI KLEINE SACHEN, BEIDE DERSELBEN ART: Etwas steht still da und sieht fast richtig aus. 1. DER TITEL WIRD GEGENGELESEN Anlass war ein echter Fall aus dem Betrieb: Filipe hat den Bildweg benutzt, und aus "Refined visuals Goal 1.0" wurde "Refined visvals Goal 1.0" -- ein u als v. Genau die Verwechslung, die man beim Korrekturlesen ueberliest, weil das Wort noch fast richtig aussieht. NACHGEMESSEN, OB DIE SPRACHWAHL HILFT: auf dem Server gegen die gespeicherte Pruefdatei mit deu, eng, deu+eng und eng+deu -- alle vier lesen "visuals" RICHTIG. Der Fehler liess sich also nicht nachstellen, und eine Einstellung zu aendern, deren Wirkung man nicht messen kann, ist Raten, das danach wie eine Loesung aussieht. Es bleibt bei `deu`. Was stattdessen hilft: Der Titel steht auf der Seite ZWEIMAL -- oben als Ueberschrift (gross, fett) und im Raster hinter "Gruppenname" (klein, normal). Zwei Schriftgroessen heisst zwei Gelegenheiten, sich zu verlesen, und zwar unabhaengig voneinander. Gehen die Lesungen auseinander, sagt die Kachel es, und beide stehen im Auszug. EIN HINWEIS, KEINE ABLEHNUNG -- anders als beim Zeitraum. Ein falsches Datum ist eine Falschauskunft: Die Kachel behauptet einen Termin, den sie nicht kennt, und niemand sieht es ihr an. Ein verlesener Buchstabe im Titel ist ein Schoenheitsfehler; daran eine ganze Kachel scheitern zu lassen waere unverhaeltnismaessig. 2. "Kampagne bremse" IST KEIN SATZ Die Protokollseite baut ihren Anzeigetext aus dem Schluessel: `aktionLesbar()` in personen.js macht aus Unterstrichen Leerzeichen und schreibt nur den ersten Buchstaben gross. Aus `kampagne_bremse` wurde damit "Kampagne bremse" -- Maschinensprache mit einem grossen Anfangsbuchstaben. Gemessen an allen sechs Woertern dieses Hauses war es das einzige kaputte: "Kampagne eingelesen", "Kampagne gescheitert", "Event geteilt" und sogar "Event zurueckgezogen" (die Umlautliste macht daraus "zurueckgezogen" mit u-Umlaut) lesen sich als Satz. Die Regel dahinter: Hauptwort plus Mittelwort, nie zwei Hauptwoerter. UND DAS UMBENENNEN WAR UMSONST ZU HABEN -- vorher gezaehlt, weil es bei Altbestand eine andere Rechnung waere: In der Live-Datenbank steht KEINE Zeile mit dem alten Wort (die Bremse hat nie gegriffen; zum Vergleich event_geteilt 7, kampagne_eingelesen 3). Es gibt also nichts, was danach krumm angezeigt wuerde. GEPRUEFT: 48 (Bildleser, +5 mit Gegenprobe, dass der Hinweis NICHT immer kommt), 190 (Route, unveraendert). Keine ausgelieferte Datei beruehrt, also kein neuer Stempel noetig. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -402,14 +402,47 @@ export function leseBild(text) {
|
|||||||
/* ERST „Gruppenname", DANN die Kopfzeile. Im Raster steht der Titel
|
/* ERST „Gruppenname", DANN die Kopfzeile. Im Raster steht der Titel
|
||||||
ohne Abzeichen und ohne Bedienelemente daneben -- also sauberer.
|
ohne Abzeichen und ohne Bedienelemente daneben -- also sauberer.
|
||||||
Die Kopfzeile ist die Ausweiche. */
|
Die Kopfzeile ist die Ausweiche. */
|
||||||
let titel = titelSaeubern(feld.gruppenname || "");
|
const titelRaster = titelSaeubern(feld.gruppenname || "");
|
||||||
if (!titel || titel.length < 2) titel = titelSaeubern(alle[0]);
|
const titelKopf = titelSaeubern(alle[0] || "");
|
||||||
|
let titel = titelRaster && titelRaster.length >= 2 ? titelRaster : titelKopf;
|
||||||
if (!titel || titel.length < 2) {
|
if (!titel || titel.length < 2) {
|
||||||
throw new BildUnlesbar(
|
throw new BildUnlesbar(
|
||||||
"auf dem Bild war kein Titel zu lesen", "pflicht");
|
"auf dem Bild war kein Titel zu lesen", "pflicht");
|
||||||
}
|
}
|
||||||
titel = titel.slice(0, 160);
|
titel = titel.slice(0, 160);
|
||||||
|
|
||||||
|
/* ---- DER TITEL WIRD GEGENGELESEN (08.10.2026) ----------------
|
||||||
|
|
||||||
|
Er steht auf der Seite ZWEIMAL: oben als Ueberschrift (gross
|
||||||
|
und fett) und im Raster hinter „Gruppenname" (klein und
|
||||||
|
normal). Zwei Schriftgroessen heisst zwei Gelegenheiten, sich
|
||||||
|
zu verlesen -- und die Texterkennung verliest sich bei beiden
|
||||||
|
unabhaengig voneinander.
|
||||||
|
|
||||||
|
ANLASS WAR EIN ECHTER FALL: Am 08.10.2026 wurde aus „Refined
|
||||||
|
visuals Goal 1.0" ein „Refined visvals Goal 1.0". Ein u als v
|
||||||
|
-- genau die Verwechslung, die man beim Korrekturlesen
|
||||||
|
ueberliest, weil das Wort noch fast richtig aussieht. Auf der
|
||||||
|
Kachel stand sie still da.
|
||||||
|
|
||||||
|
EIN HINWEIS, KEINE ABLEHNUNG -- anders als beim Zeitraum. Ein
|
||||||
|
falsches Datum ist eine Falschauskunft: Die Kachel behauptet
|
||||||
|
einen Termin, den sie nicht kennt, und niemand sieht es ihr an.
|
||||||
|
Ein verlesener Buchstabe im Titel ist ein Schoenheitsfehler;
|
||||||
|
daran eine ganze Kachel scheitern zu lassen waere unverhaeltnis-
|
||||||
|
maessig. Also: anlegen, und dazusagen, wo man hinsehen soll.
|
||||||
|
|
||||||
|
VERGLICHEN WIRD OHNE GROSS-/KLEINSCHREIBUNG und ohne
|
||||||
|
Leerraum: Die Ueberschrift steht in einer anderen Schrift, und
|
||||||
|
ob dort ein Leerzeichen mehr steht, ist kein Befund. */
|
||||||
|
const nurKern = (s) => String(s || "").toLocaleLowerCase("de-DE")
|
||||||
|
.replace(/\s+/g, " ").trim();
|
||||||
|
if (titelRaster && titelKopf && nurKern(titelRaster) !== nurKern(titelKopf)) {
|
||||||
|
hinweise.push(`Der Titel steht zweimal auf dem Bild und wurde `
|
||||||
|
+ `unterschiedlich gelesen („${titelRaster}“ und „${titelKopf}“) `
|
||||||
|
+ "— bitte den Titel kurz nachsehen.");
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Zeitzone -------------------------------------------------- */
|
/* ---- Zeitzone -------------------------------------------------- */
|
||||||
const zoneTreffer = ZONE.exec(roh);
|
const zoneTreffer = ZONE.exec(roh);
|
||||||
const zone = zoneTreffer ? zoneTreffer[1] : null;
|
const zone = zoneTreffer ? zoneTreffer[1] : null;
|
||||||
@@ -593,6 +626,12 @@ export function leseBild(text) {
|
|||||||
erkannter_text: roh.slice(0, 8000),
|
erkannter_text: roh.slice(0, 8000),
|
||||||
erkannte_zeilen: alle.length,
|
erkannte_zeilen: alle.length,
|
||||||
zeitraum_quelle: quelle,
|
zeitraum_quelle: quelle,
|
||||||
|
/* BEIDE TITEL KOMMEN MIT. Faellt spaeter auf, dass der
|
||||||
|
falsche gewonnen hat, steht der andere schon da -- und man
|
||||||
|
sieht, ob die Texterkennung sich einmal oder zweimal
|
||||||
|
verlesen hat. */
|
||||||
|
titel_raster: titelRaster || null,
|
||||||
|
titel_kopf: titelKopf || null,
|
||||||
zeitraum_gegenprobe: gegenStart ? `${gegenStart}..${gegenEnde}` : null,
|
zeitraum_gegenprobe: gegenStart ? `${gegenStart}..${gegenEnde}` : null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -243,6 +243,70 @@ melde("=== 2. Lieber keine Kachel als eine mit erfundenem Termin ===");
|
|||||||
ok(h.hinweise.some((x) => /nur an einer Stelle/i.test(x)),
|
ok(h.hinweise.some((x) => /nur an einer Stelle/i.test(x)),
|
||||||
` und die Kachel sagt, dass nicht gegengeprueft werden konnte`);
|
` und die Kachel sagt, dass nicht gegengeprueft werden konnte`);
|
||||||
|
|
||||||
|
/* ---- DER TITEL WIRD GEGENGELESEN (08.10.2026) --------------
|
||||||
|
|
||||||
|
ANLASS WAR EIN ECHTER FALL: Filipe hat den Bildweg benutzt, und
|
||||||
|
aus „Refined visuals Goal 1.0“ wurde „Refined visvals Goal 1.0“
|
||||||
|
— ein u als v. Genau die Verwechslung, die man beim
|
||||||
|
Korrekturlesen überliest, weil das Wort noch fast richtig
|
||||||
|
aussieht. Auf der Kachel stand sie still da.
|
||||||
|
|
||||||
|
NACHGEMESSEN, OB DIE SPRACHWAHL HILFT: Auf dem Server gegen die
|
||||||
|
gespeicherte Prüfdatei mit `deu`, `eng`, `deu+eng` und
|
||||||
|
`eng+deu` — alle vier lesen „visuals“ RICHTIG. Der Fehler ließ
|
||||||
|
sich also nicht nachstellen, und eine Einstellung zu ändern,
|
||||||
|
deren Wirkung man nicht messen kann, ist Raten. Es bleibt bei
|
||||||
|
`deu`.
|
||||||
|
|
||||||
|
WAS STATTDESSEN HILFT: Der Titel steht auf der Seite ZWEIMAL —
|
||||||
|
oben als Überschrift (groß, fett) und im Raster hinter
|
||||||
|
„Gruppenname“ (klein, normal). Zwei Schriftgrößen heißt zwei
|
||||||
|
Gelegenheiten, sich zu verlesen, und zwar unabhängig
|
||||||
|
voneinander. Gehen die beiden Lesungen auseinander, sagt die
|
||||||
|
Kachel es.
|
||||||
|
|
||||||
|
EIN HINWEIS, KEINE ABLEHNUNG — anders als beim Zeitraum. Ein
|
||||||
|
falsches Datum ist eine Falschauskunft; ein verlesener
|
||||||
|
Buchstabe im Titel ein Schönheitsfehler. Daran eine ganze
|
||||||
|
Kachel scheitern zu lassen wäre unverhältnismäßig. */
|
||||||
|
{
|
||||||
|
const zweierlei = [
|
||||||
|
"【Star Light】Refined visvals Goal 1.0 In Bearbeitung",
|
||||||
|
"Gruppenname 【Star Light】Refined visuals Goal 1.0 Zeitzone Europe/Berlin",
|
||||||
|
"Daten 01.10.2026 00:00 ~ 31.10.2026 22:59",
|
||||||
|
].join("\n");
|
||||||
|
const g = leseBild(zweierlei);
|
||||||
|
/* ERST BELEGEN, DASS DIE KACHEL ÜBERHAUPT ENTSTEHT. Ohne diese
|
||||||
|
Zeile wäre das Folgende auch bei einer Ablehnung grün. */
|
||||||
|
ok(g.pflicht.titel.includes("Refined"),
|
||||||
|
`die Kachel entsteht trotzdem: ${JSON.stringify(g.pflicht.titel)}`);
|
||||||
|
ok(g.hinweise.some((h) => /zweimal auf dem Bild/i.test(h)),
|
||||||
|
`und sagt, dass der Titel zweimal verschieden gelesen wurde: `
|
||||||
|
+ `${JSON.stringify(g.hinweise.find((h) => /zweimal/i.test(h)) || null)}`);
|
||||||
|
/* BEIDE LESUNGEN KOMMEN MIT. Fällt später auf, dass die falsche
|
||||||
|
gewonnen hat, steht die andere schon da. */
|
||||||
|
ok(g.auszug.titel_raster && g.auszug.titel_kopf
|
||||||
|
&& g.auszug.titel_raster !== g.auszug.titel_kopf,
|
||||||
|
`und beide stehen im Auszug: ${JSON.stringify(
|
||||||
|
[g.auszug.titel_raster, g.auszug.titel_kopf])}`);
|
||||||
|
|
||||||
|
/* GEGENPROBE: Stimmen beide überein, gibt es KEINEN Hinweis.
|
||||||
|
Ohne sie wäre „der Hinweis kommt“ auch dann grün, wenn er
|
||||||
|
immer käme — und ein Hinweis, der immer kommt, wird
|
||||||
|
überlesen (CLAUDE.md, 03.09.2026). */
|
||||||
|
const einig = zweierlei.replace("visvals", "visuals");
|
||||||
|
const h = leseBild(einig);
|
||||||
|
ok(!h.hinweise.some((x) => /zweimal auf dem Bild/i.test(x)),
|
||||||
|
`Gegenprobe: bei zwei gleichen Lesungen kommt kein Hinweis `
|
||||||
|
+ `(${h.hinweise.length} Hinweise)`);
|
||||||
|
|
||||||
|
/* UND DER ECHTE ERKENNUNGSTEXT LÖST IHN AUCH NICHT AUS. Sonst
|
||||||
|
stünde er an jeder Kachel, und das wäre derselbe Fehler. */
|
||||||
|
const echt = leseBild(ERKANNT);
|
||||||
|
ok(!echt.hinweise.some((x) => /zweimal auf dem Bild/i.test(x)),
|
||||||
|
"und am gespeicherten Erkennungstext ebenso wenig");
|
||||||
|
}
|
||||||
|
|
||||||
/* Ein Ende vor dem Anfang ist ein Zahlendreher, kein Zeitraum. */
|
/* Ein Ende vor dem Anfang ist ein Zahlendreher, kein Zeitraum. */
|
||||||
wirft("Kampagne Z\nGruppenname Kampagne Z\nDaten 20.11.2026 ~ 05.11.2026",
|
wirft("Kampagne Z\nGruppenname Kampagne Z\nDaten 20.11.2026 ~ 05.11.2026",
|
||||||
"pflicht", "Ende vor Anfang");
|
"pflicht", "Ende vor Anfang");
|
||||||
|
|||||||
@@ -1487,7 +1487,7 @@ melde("=== 14. Zwanzig in einer Stunde, dann ist Schluss ===");
|
|||||||
/* UND DIE BREMSE VERLÄNGERT SICH NICHT SELBST: Ihre eigene Meldung
|
/* UND DIE BREMSE VERLÄNGERT SICH NICHT SELBST: Ihre eigene Meldung
|
||||||
zählt nicht mit, sonst käme man nie wieder heraus. */
|
zählt nicht mit, sonst käme man nie wieder heraus. */
|
||||||
const bremsZaehler = dbLesen("SELECT COUNT(*) AS n FROM protokoll"
|
const bremsZaehler = dbLesen("SELECT COUNT(*) AS n FROM protokoll"
|
||||||
+ " WHERE aktion = 'kampagne_bremse'")[0].n;
|
+ " WHERE aktion = 'kampagne_gebremst'")[0].n;
|
||||||
ok(bremsZaehler >= 1, `${bremsZaehler} Bremsvorgänge stehen im Protokoll`);
|
ok(bremsZaehler >= 1, `${bremsZaehler} Bremsvorgänge stehen im Protokoll`);
|
||||||
const gezaehlt = dbLesen("SELECT COUNT(*) AS n FROM protokoll"
|
const gezaehlt = dbLesen("SELECT COUNT(*) AS n FROM protokoll"
|
||||||
+ " WHERE aktion IN ('kampagne_eingelesen','kampagne_gescheitert','kampagne_schon_da')"
|
+ " WHERE aktion IN ('kampagne_eingelesen','kampagne_gescheitert','kampagne_schon_da')"
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ export function istCreatorNetwork(roh) {
|
|||||||
* einen Abruf nach draussen aus, ohne gezaehlt zu werden. Gemessen:
|
* einen Abruf nach draussen aus, ohne gezaehlt zu werden. Gemessen:
|
||||||
* 21 Versuche, 0 gebremst.
|
* 21 Versuche, 0 gebremst.
|
||||||
*
|
*
|
||||||
* "kampagne_bremse" steht BEWUSST nicht in der Liste: Sonst zaehlte
|
* "kampagne_gebremst" steht BEWUSST nicht in der Liste: Sonst zaehlte
|
||||||
* die Bremse ihre eigenen Meldungen mit und verlaengerte sich
|
* die Bremse ihre eigenen Meldungen mit und verlaengerte sich
|
||||||
* selbst -- man kaeme nie wieder heraus. */
|
* selbst -- man kaeme nie wieder heraus. */
|
||||||
function versucheLetzteStunde(personId) {
|
function versucheLetzteStunde(personId) {
|
||||||
@@ -572,7 +572,30 @@ kampagneRouter.post("/workspace/api/kampagne/einlesen", async (req, res) => {
|
|||||||
const min = Math.max(1, Math.ceil((frei.getTime() - Date.now()) / 60000));
|
const min = Math.max(1, Math.ceil((frei.getTime() - Date.now()) / 60000));
|
||||||
/* Hier NICHT über `schiefgegangen`: Sonst zählte die Bremse
|
/* Hier NICHT über `schiefgegangen`: Sonst zählte die Bremse
|
||||||
ihre eigene Meldung mit und verlängerte sich selbst. */
|
ihre eigene Meldung mit und verlängerte sich selbst. */
|
||||||
protokolliere("kampagne_bremse", {
|
/* HAUPTWORT PLUS MITTELWORT, NIE ZWEI HAUPTWOERTER
|
||||||
|
(berichtigt 08.10.2026).
|
||||||
|
|
||||||
|
Die Protokollseite baut ihren Anzeigetext aus dem
|
||||||
|
Schluessel: `aktionLesbar()` in personen.js macht aus
|
||||||
|
Unterstrichen Leerzeichen und schreibt nur den ERSTEN
|
||||||
|
Buchstaben gross. Aus `kampagne_bremse` wurde damit
|
||||||
|
„Kampagne bremse" -- Maschinensprache mit einem grossen
|
||||||
|
Anfangsbuchstaben, und genau davor warnt
|
||||||
|
pruef-meldungen.
|
||||||
|
|
||||||
|
Gemessen an den anderen fuenf Woertern dieses Hauses:
|
||||||
|
„Kampagne eingelesen", „Kampagne gescheitert", „Event
|
||||||
|
geteilt", „Event zurueckgezogen" (die Umlautliste macht
|
||||||
|
daraus „zurückgezogen") lesen sich als Satz. Nur dieses
|
||||||
|
eine nicht.
|
||||||
|
|
||||||
|
UMBENENNEN WAR HIER UMSONST ZU HABEN: Gemessen am
|
||||||
|
08.10.2026 steht in der Live-Datenbank KEINE einzige
|
||||||
|
Zeile mit dem alten Wort -- die Bremse hat noch nie
|
||||||
|
gegriffen. Es gibt also keinen Altbestand, der danach
|
||||||
|
krumm angezeigt wuerde. Bei einem Wort mit Zeilen in der
|
||||||
|
Tabelle waere die Rechnung eine andere. */
|
||||||
|
protokolliere("kampagne_gebremst", {
|
||||||
personId: person.id, rolle: person.rolle, ip,
|
personId: person.id, rolle: person.rolle, ip,
|
||||||
detail: `${anzahl} Versuche in einer Stunde`,
|
detail: `${anzahl} Versuche in einer Stunde`,
|
||||||
});
|
});
|
||||||
@@ -871,7 +894,8 @@ kampagneRouter.post("/workspace/api/kampagne/bild",
|
|||||||
if (anzahl >= KAMPAGNEN_JE_STUNDE) {
|
if (anzahl >= KAMPAGNEN_JE_STUNDE) {
|
||||||
const frei = new Date(Date.parse(aeltester) + 3600_000);
|
const frei = new Date(Date.parse(aeltester) + 3600_000);
|
||||||
const min = Math.max(1, Math.ceil((frei.getTime() - Date.now()) / 60000));
|
const min = Math.max(1, Math.ceil((frei.getTime() - Date.now()) / 60000));
|
||||||
protokolliere("kampagne_bremse", {
|
/* Dasselbe Wort wie oben -- Begruendung dort. */
|
||||||
|
protokolliere("kampagne_gebremst", {
|
||||||
personId: person.id, rolle: person.rolle, ip,
|
personId: person.id, rolle: person.rolle, ip,
|
||||||
detail: kurz(`bild: ${anzahl} in der letzten Stunde`),
|
detail: kurz(`bild: ${anzahl} in der letzten Stunde`),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user