From 9868a9748a622377cf3acf16beaabca74ef70f99 Mon Sep 17 00:00:00 2001 From: claude-dev Date: Sun, 2 Aug 2026 00:43:28 +0200 Subject: [PATCH] fix(recherche): Suchbegriffe treffen wieder, Verzeichnis ohne Dubletten Vier Befunde aus dem Vergleich vom 02.08.2026, sortiert nach Wirkung. 1. Mehrwort-Suchbegriffe fielen im RSS-Abgleich durch. Dieselbe Lage lieferte je nach Titel 79 oder 17 Treffer. Die Keyword-Erzeugung liefert bei politisch formulierten Titeln Phrasen ("migrationskrise spanien", "aufnahmeverfahren eu"), das Matching verglich sie als starre Zeichenfolge. Die Schlagzeile "Migrationskrise in Spanien" enthaelt "migrationskrise spanien" nicht, wegen des Wortes dazwischen. Aus 53 Artikeln wurden so 16. Eine Phrase trifft jetzt, wenn alle ihre Bestandteile im Text vorkommen, unabhaengig von Reihenfolge und Abstand. Die Genauigkeit bleibt erhalten, nur die starre Reihenfolge faellt weg. Dazu der Auftrag an die Keyword-Erzeugung: Einzelwoerter statt Phrasen, und die harten Eigennamen der Lage sind Pflicht. Bei den betroffenen Laeufen fehlte "ceuta" komplett, dafuer stand "europol migration" in der Liste, was den Europol-Feed anzog und sieben themenfremde Pressemitteilungen einbrachte, die der Topic-Filter danach wieder aussortieren musste. 2. Dubletten im Quellenverzeichnis. Lage 56 fuehrte 30 Eintraege fuer 28 Adressen, zweimal dieselbe tagesschau-Meldung unter je zwei Nummern. Ausgerechnet die strittigste Behauptung, der offene Brief der 22 Staaten, stuetzte sich damit auf "3 Quellen", von denen zwei derselbe Artikel waren. Gleiche Adresse heisst jetzt ein Eintrag, Verweise auf die entfernte Nummer werden auf den behaltenen umgebogen. Verglichen wird ohne Schema, www und Endschraegstrich. 3. Die Zeitleiste wird deterministisch sortiert. Im selben Bericht sprang der letzte Eintrag von 12:44 zurueck auf 17:47. Der Auftrag verlangt die Sortierung zwar vom Modell, im prominentesten Kapitel darf sie aber nicht von dessen Sorgfalt abhaengen. Gegenprobe an Lage 56: 23:23, 22:09, 17:47, 16:58 statt des Sprungs ans Ende. 4. Weiterleitungen zaehlen als ein Beleg. Sie verbergen ihr Zielmedium, also laesst sich nicht behaupten, dass dahinter verschiedene Redaktionen stehen. Die bisherige Einzelzaehlung stellte den Anthropic-Weg rechnerisch besser: in Lage 57 standen 10 ausgewiesene Belege fuer 7 Haeuser, weil vier der elf Adressen news.google.com-Weiterleitungen waren. Gegenprobe an beiden gespeicherten Laeufen: Lage 56 zeigt 28 statt 30 Verzeichniseintraege, beide Berichte zaehlen lueckenlos, die Zeitleiste ist sortiert. Neu sind 26 Pruefungen, insgesamt laufen 206 ohne Netzzugriff und ohne Kosten. Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 1 + src/agents/factchecker.py | 13 +++- src/agents/researcher.py | 9 +++ src/feeds/rss_parser.py | 30 +++++++- src/report_generator.py | 61 +++++++++++++++- tests/test_faktencheck_belegtiefe.py | 17 ++++- tests/test_quellenausgabe.py | 47 ++++++++++++ tests/test_rss_treffer.py | 105 +++++++++++++++++++++++++++ 8 files changed, 272 insertions(+), 11 deletions(-) create mode 100644 tests/test_rss_treffer.py diff --git a/CLAUDE.md b/CLAUDE.md index 39c97e7..2897198 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -165,6 +165,7 @@ tests/: test_qc_und_runden.py: "Absicherung der Duplikatpruefung und Abbruch der Suchrunden bei erreichter Treffergrenze" test_mehrfachantwort.py: "Antworten, in denen sich das Modell selbst korrigiert und mehrere Fassungen liefert, die ausfuehrlichste muss gewinnen" test_faktencheck_belegtiefe.py: "Belegzaehlung nach Medienhaus, Nachbedingung fuer bestaetigte Fakten, Sperre gegen das Hochstufen von developing, getrennte Bezeichnungen fuer Widerspruch und Widerlegung" + test_rss_treffer.py: "Keyword-Matching der RSS-Auswertung: Mehrwort-Begriffe treffen wortweise, Auftrag verlangt Einzelbegriffe und Eigennamen" test_bedrock_wiederholung.py: "Wiederholung des EU-Wegs bei Kapazitaets- und Kontingentfehlern, Staffelung, Zeit- und Wartebudget, Sichtbarkeit im Refresh-Protokoll" test_quellenausgabe.py: "Ausgabetreue des Quellenverzeichnisses: Nummern aus dem Lagebild statt Zeilennummern, keine Kappung, ein Verlag ein Name, Statistik deckungsgleich mit dem Verzeichnis" ``` diff --git a/src/agents/factchecker.py b/src/agents/factchecker.py index e848cd2..4f9deaa 100644 --- a/src/agents/factchecker.py +++ b/src/agents/factchecker.py @@ -434,9 +434,14 @@ def zaehle_medienhaeuser(urls: list[str]) -> int: Sechs France24-Treffer, davon vier Videofassungen derselben Meldung, sind ein Haus und kein sechsfacher Beleg. Gezaehlt wird deshalb nach - registrierbarer Domain. Weiterleitungsportale bekommen keine gemeinsame - Identitaet, weil hinter news.google.com beliebige Zeitungen stecken - koennen: dort zaehlt jede Adresse einzeln. + registrierbarer Domain. + + Weiterleitungen ueber Sammelportale zaehlen zusammen als EIN Beleg. Sie + verbergen ihr Zielmedium, also laesst sich nicht behaupten, dass dahinter + verschiedene Redaktionen stehen. Vier news.google.com-Adressen als vier + unabhaengige Quellen zu zaehlen, hat im Vergleich vom 02.08.2026 den + Anthropic-Weg rechnerisch bessergestellt: dort standen 10 Belege fuer + tatsaechlich 7 Haeuser, davon vier Weiterleitungen. """ from services import media_registry @@ -448,7 +453,7 @@ def zaehle_medienhaeuser(urls: list[str]) -> int: domain = media_registry.registrable_domain(sauber) if not domain: continue - haeuser.add(sauber.lower() if media_registry.ist_aggregator(sauber) else domain) + haeuser.add("weiterleitung" if media_registry.ist_aggregator(sauber) else domain) return len(haeuser) diff --git a/src/agents/researcher.py b/src/agents/researcher.py index ecea3e7..4d943a7 100644 --- a/src/agents/researcher.py +++ b/src/agents/researcher.py @@ -413,6 +413,15 @@ KEYWORDS-REGELN: - Wenn die Lage rein deutsch oder englisch ist und keine fremdsprachigen Feeds gewählt werden, reichen "de" und/oder "en". - Nur inhaltlich relevante Begriffe (Personen, Orte, Themen, Organisationen) +- EINZELWÖRTER, keine Phrasen. "ceuta" und "migranten" statt "migrationskrise spanien". + Die Begriffe werden gegen Schlagzeilen geprüft, und dort steht selten die exakte + Wortfolge einer Phrase. Nur wo ein Begriff untrennbar ist (Eigenname wie + "nord stream"), darf er aus zwei Wörtern bestehen. +- Die HARTEN Eigennamen der Lage MÜSSEN dabei sein: Schauplatz (Ort, Region), beteiligte + Länder, handelnde Personen, betroffene Organisationen. Eine Lage über Ceuta ohne das + Wort "ceuta" ist unbrauchbar. Diese Namen zuerst nennen, danach die Sachbegriffe. +- KEINE abstrakten Politikbegriffe als Suchwort ("aufnahmeverfahren", "grenzschutz europa"). + Sie stehen fast nie in einer Schlagzeile und ziehen dafür themenfremde Behördenfeeds an. - KEINE Jahreszahlen (2024, 2025, 2026 etc.) - KEINE Monatsnamen (Januar, Februar, März etc.) - KEINE generischen Wörter (aktuell, news, update etc.) diff --git a/src/feeds/rss_parser.py b/src/feeds/rss_parser.py index 1af2d52..ecd191d 100644 --- a/src/feeds/rss_parser.py +++ b/src/feeds/rss_parser.py @@ -18,6 +18,30 @@ from agents.researcher import keywords_for_language, flatten_keywords logger = logging.getLogger("osint.rss") +def wort_trifft(wort: str, text: str) -> bool: + """Prueft ein Suchwort gegen den Text, Mehrwort-Begriffe wortweise. + + Die Keyword-Erzeugung liefert je nach Lagentitel Einzelbegriffe ("ceuta") + oder Phrasen ("migrationskrise spanien"). Ein reiner Teilstring-Vergleich + laesst Phrasen praktisch immer durchfallen: Die Schlagzeile "Migrationskrise + in Spanien" enthaelt die Folge "migrationskrise spanien" nicht, wegen des + Wortes dazwischen. Am 01.08.2026 kostete das den Unterschied zwischen 79 + und 17 RSS-Treffern fuer dieselbe Lage. + + Deshalb: Eine Phrase trifft, wenn ALLE ihre Bestandteile im Text vorkommen, + unabhaengig von Reihenfolge und Abstand. Die Genauigkeit bleibt damit + erhalten, nur die starre Reihenfolge faellt weg. + """ + if not wort: + return False + if wort in text: + return True + teile = [t for t in wort.split() if t] + if len(teile) < 2: + return False + return all(t in text for t in teile) + + def _is_specific_word(w: str) -> bool: """Spezifisches Keyword = 1-Treffer reicht für Match. @@ -209,12 +233,14 @@ class RSSParser: # CJK/Arabisch/Hebräisch/Kyrillisch ≥3 Zeichen) im Text reicht 1 Treffer. # Damit matched z.B. "自衛隊" (3 Kanji) wie "buckelwal" (9 Zeichen). # - Sonst: alte Heuristik (mindestens halb der Wörter, max. 2). - specific_in_text = any(w in text for w in search_words if _is_specific_word(w)) + specific_in_text = any( + wort_trifft(w, text) for w in search_words if _is_specific_word(w) + ) if specific_in_text: min_matches = 1 else: min_matches = min(2, max(1, (len(search_words) + 1) // 2)) - match_count = sum(1 for word in search_words if word in text) + match_count = sum(1 for word in search_words if wort_trifft(word, text)) if match_count >= min_matches: published = None diff --git a/src/report_generator.py b/src/report_generator.py index d604ed6..0da3872 100644 --- a/src/report_generator.py +++ b/src/report_generator.py @@ -112,7 +112,49 @@ def _prepare_sources(incident: dict) -> list: aufbereitet.append(kopie) aufbereitet.sort(key=lambda s: s["nr"] if isinstance(s["nr"], int) else 10**6) - return aufbereitet + return _dubletten_zusammenfuehren(aufbereitet) + + +def _url_schluessel(url: str) -> str: + """Vergleichsform einer Adresse: ohne Schema, ohne www, ohne Endschraegstrich.""" + text = (url or "").strip().lower() + for praefix in ("https://", "http://"): + if text.startswith(praefix): + text = text[len(praefix):] + if text.startswith("www."): + text = text[4:] + return text.rstrip("/") + + +def _dubletten_zusammenfuehren(sources: list) -> list: + """Fuehrt Eintraege mit identischer Adresse auf eine Nummer zusammen. + + Im Bericht vom 02.08.2026 standen 30 Eintraege fuer 28 verschiedene + Adressen: zweimal dieselbe tagesschau-Meldung unter je zwei Nummern. Eine + Behauptung wirkte dadurch mit "3 Quellen" belegt, obwohl zwei davon + derselbe Artikel waren. Die kleinere Nummer gewinnt, die groessere wird + unter "alias_nrs" vermerkt, damit die Verweise im Text auf sie umgebogen + werden koennen. + """ + behalten: dict[str, dict] = {} + reihenfolge: list[str] = [] + for s in sources: + schluessel = _url_schluessel(s.get("url") or "") + if not schluessel: + reihenfolge.append(id(s)) + behalten[id(s)] = s + continue + if schluessel in behalten: + behalten[schluessel].setdefault("alias_nrs", []).append(s.get("nr")) + continue + behalten[schluessel] = s + reihenfolge.append(schluessel) + + ergebnis = [behalten[k] for k in reihenfolge] + entfernt = len(sources) - len(ergebnis) + if entfernt: + logger.info("Quellenverzeichnis: %d doppelte Adresse(n) zusammengefuehrt", entfernt) + return ergebnis def _renumber_sources(sources: list) -> tuple[list, dict]: @@ -133,6 +175,11 @@ def _renumber_sources(sources: list) -> tuple[list, dict]: alt = s.get("nr") if isinstance(alt, int): abbildung[alt] = position + # Zusammengefuehrte Dubletten zeigen auf denselben Eintrag, damit ein + # Verweis auf die entfernte Nummer nicht ins Leere laeuft. + for alias in kopie.pop("alias_nrs", []) or []: + if isinstance(alias, int): + abbildung[alias] = position kopie["nr"] = position neu.append(kopie) return neu, abbildung @@ -352,8 +399,16 @@ def _parse_developments_for_export(text: str) -> list[tuple[str, str]]: continue yy = year[-2:] if year else year2 label = f"{int(day):02d}.{int(month):02d}.{yy}, {time} Uhr" - result.append((label, body)) - return result + stunde, minute = (time.split(":") + ["0"])[:2] + sortierwert = (int(yy), int(month), int(day), int(stunde), int(minute)) + result.append((label, body, sortierwert)) + + # Neueste zuerst. Der Auftrag verlangt diese Reihenfolge zwar schon vom + # Modell, im Bericht vom 02.08.2026 sprang der letzte Eintrag aber von + # 12:44 zurueck auf 17:47. Im prominentesten Kapitel darf das nicht von + # der Sorgfalt des Modells abhaengen, deshalb hier deterministisch. + result.sort(key=lambda e: e[2], reverse=True) + return [(label, body) for label, body, _ in result] def _format_latest_developments_html(text: str) -> str: diff --git a/tests/test_faktencheck_belegtiefe.py b/tests/test_faktencheck_belegtiefe.py index 3c7e6b4..bfdf7a9 100644 --- a/tests/test_faktencheck_belegtiefe.py +++ b/tests/test_faktencheck_belegtiefe.py @@ -66,8 +66,21 @@ redirects = [ "https://news.google.com/rss/articles/AAA?oc=5", "https://news.google.com/rss/articles/BBB?oc=5", ] -pruefe("A3 zwei Weiterleitungen bleiben zwei, sie koennen zwei Zeitungen sein", - fc.zaehle_medienhaeuser(redirects) == 2, fc.zaehle_medienhaeuser(redirects)) +pruefe("A3 Weiterleitungen zaehlen zusammen als ein Beleg", + fc.zaehle_medienhaeuser(redirects) == 1, fc.zaehle_medienhaeuser(redirects)) +# Der gemeldete Fall aus Lage 57: 11 Adressen, davon vier Weiterleitungen, +# ergaben eine ausgewiesene Belegzahl von 10 bei tatsaechlich 7 Haeusern. +lage57 = [ + "https://news.google.com/rss/articles/A?oc=5", + "https://news.google.com/rss/articles/B?oc=5", + "https://news.google.com/rss/articles/C?oc=5", + "https://news.google.com/rss/articles/D?oc=5", + "https://www.tagesschau.de/a.html", "https://www.tagesschau.de/b.html", + "https://www.france24.com/x", "https://www.theguardian.com/y", + "https://www.ft.com/z", "https://www.aljazeera.com/q", "https://www.bbc.co.uk/r", +] +pruefe("A3b elf Adressen mit vier Weiterleitungen ergeben sieben Belege", + fc.zaehle_medienhaeuser(lage57) == 7, fc.zaehle_medienhaeuser(lage57)) pruefe("A4 Satzzeichen am Adressende stoeren nicht", fc.zaehle_medienhaeuser(["https://www.zeit.de/a.html)."]) == 1) pruefe("A5 leere Liste ergibt null", fc.zaehle_medienhaeuser([]) == 0) diff --git a/tests/test_quellenausgabe.py b/tests/test_quellenausgabe.py index 9e436df..8fddef3 100644 --- a/tests/test_quellenausgabe.py +++ b/tests/test_quellenausgabe.py @@ -148,6 +148,53 @@ pruefe("C9 leere Abbildung laesst den Text unveraendert", rg._apply_citation_map(text_alt, {}) == text_alt) +# --------------------------------------------------------------------------- +# C3. Doppelte Adressen +# --------------------------------------------------------------------------- +print("\nC3. Doppelte Adressen im Verzeichnis") +# Nachgebaut nach Lage 56: 30 Eintraege, aber nur 28 verschiedene Adressen. +# Zweimal dieselbe tagesschau-Meldung stuetzte dort die strittigste Behauptung. +doppelt = rg._prepare_sources({"sources_json": json.dumps([ + {"nr": 22, "name": "tagesschau", "url": "https://www.tagesschau.de/ausland/a-100.html"}, + {"nr": 23, "name": "tagesschau", "url": "https://www.tagesschau.de/ausland/a-100.html"}, + {"nr": 24, "name": "tagesschau", "url": "https://www.tagesschau.de/ausland/b-104.html"}, + {"nr": 25, "name": "tagesschau", "url": "http://tagesschau.de/ausland/b-104.html/"}, + {"nr": 26, "name": "ZDF heute", "url": "https://www.zdfheute.de/c.html"}, +], ensure_ascii=False)}) +pruefe("C10 gleiche Adresse ergibt einen Eintrag", len(doppelt) == 3, len(doppelt)) +pruefe("C11 die kleinere Nummer bleibt bestehen", + [s["nr"] for s in doppelt] == [22, 24, 26], [s["nr"] for s in doppelt]) +pruefe("C12 Schema, www und Endschraegstrich zaehlen nicht als Unterschied", + rg._url_schluessel("https://www.x.de/a/") == rg._url_schluessel("http://x.de/a")) + +neu_d, abb_d = rg._renumber_sources(doppelt) +pruefe("C13 Ausgabe zaehlt wieder lueckenlos", [s["nr"] for s in neu_d] == [1, 2, 3], + [s["nr"] for s in neu_d]) +pruefe("C14 Verweis auf die entfernte Nummer zeigt auf den behaltenen Eintrag", + abb_d.get(23) == abb_d.get(22) == 1 and abb_d.get(25) == abb_d.get(24) == 2, abb_d) +pruefe("C15 Text mit beiden Nummern bleibt aufloesbar", + rg._apply_citation_map("Beleg [22] und [23].", abb_d) == "Beleg [1] und [1].", + rg._apply_citation_map("Beleg [22] und [23].", abb_d)) + + +# --------------------------------------------------------------------------- +# C4. Zeitliche Sortierung der Neuesten Entwicklungen +# --------------------------------------------------------------------------- +print("\nC4. Neueste Entwicklungen") +# Nachgebaut nach Lage 56: der letzte Eintrag sprang von 12:44 zurueck auf 17:47. +roh = "\n".join([ + "- [01.08. 23:23] Barriere installiert. {A|https://a.de/1}", + "- [01.08. 12:44] Italien setzt Schengen aus. {B|https://b.de/1}", + "- [01.08. 17:47] Todeszahl steigt. {C|https://c.de/1}", + "- [02.08. 00:05] Neuer Tag. {D|https://d.de/1}", +]) +paare = rg._parse_developments_for_export(roh) +pruefe("C16 Eintraege stehen absteigend nach Zeit", + [p[0][:14] for p in paare] == ["02.08.26, 00:0", "01.08.26, 23:2", "01.08.26, 17:4", "01.08.26, 12:4"], + [p[0] for p in paare]) +pruefe("C17 kein Eintrag geht verloren", len(paare) == 4, len(paare)) + + # --------------------------------------------------------------------------- # D. Statistik und Verzeichnis widersprechen sich nicht mehr # --------------------------------------------------------------------------- diff --git a/tests/test_rss_treffer.py b/tests/test_rss_treffer.py new file mode 100644 index 0000000..e44cd42 --- /dev/null +++ b/tests/test_rss_treffer.py @@ -0,0 +1,105 @@ +"""Testet das Keyword-Matching der RSS-Auswertung. + +Hintergrund: Dieselbe Lage lieferte am 01.08.2026 je nach Titel 79 oder 17 +RSS-Treffer. Ursache war die Kombination aus zwei Dingen. Die +Keyword-Erzeugung lieferte fuer den politisch formulierten Titel Phrasen +("migrationskrise spanien", "aufnahmeverfahren eu") statt Einzelbegriffe, und +das Matching verglich diese Phrasen als starre Zeichenfolge. Die Schlagzeile +"Migrationskrise in Spanien" enthaelt "migrationskrise spanien" nicht, wegen +des Wortes dazwischen. Aus 53 Artikeln wurden so 16. + +Laeuft ohne Netzzugriff und ohne Kosten. + +Aufruf aus dem Projektstamm: + venv/bin/python tests/test_rss_treffer.py +""" +import os +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src")) + +from feeds.rss_parser import wort_trifft, _is_specific_word # noqa: E402 +from agents.researcher import FEED_SELECTION_PROMPT_TEMPLATE # noqa: E402 + +ok = 0 +fail = 0 + + +def pruefe(name, bedingung, extra=""): + global ok, fail + if bedingung: + ok += 1 + print(" OK " + name) + else: + fail += 1 + print(" FEHL " + name + " " + str(extra)) + + +SCHLAGZEILE = ( + "migrationskrise in spanien: eu-innenminister beraten am dienstag über ceuta" +).lower() + + +# --------------------------------------------------------------------------- +# A. Einzelbegriffe verhalten sich wie bisher +# --------------------------------------------------------------------------- +print("\nA. Einzelbegriffe") +pruefe("A1 enthaltenes Wort trifft", wort_trifft("ceuta", SCHLAGZEILE)) +pruefe("A2 nicht enthaltenes Wort trifft nicht", not wort_trifft("marokko", SCHLAGZEILE)) +pruefe("A3 Wortteil trifft weiterhin (Komposita)", wort_trifft("migration", SCHLAGZEILE)) +pruefe("A4 leeres Wort trifft nie", not wort_trifft("", SCHLAGZEILE)) + + +# --------------------------------------------------------------------------- +# B. Phrasen treffen wortweise +# --------------------------------------------------------------------------- +print("\nB. Mehrwort-Begriffe") +pruefe("B1 Phrase mit Wort dazwischen trifft jetzt", + wort_trifft("migrationskrise spanien", SCHLAGZEILE)) +pruefe("B2 Phrase in umgekehrter Reihenfolge trifft", + wort_trifft("spanien migrationskrise", SCHLAGZEILE)) +pruefe("B3 zusammenhaengende Phrase trifft weiterhin", + wort_trifft("eu-innenminister beraten", SCHLAGZEILE)) +pruefe("B4 fehlt ein Bestandteil, trifft die Phrase nicht", + not wort_trifft("migrationskrise portugal", SCHLAGZEILE)) +pruefe("B5 abstrakte Phrase ohne Bezug trifft nicht", + not wort_trifft("aufnahmeverfahren eu", SCHLAGZEILE)) + +# Der gemeldete Fall: zehn Begriffe, davon neun Phrasen. +KEYWORDS_LAGE_55 = [ + "migrationskrise spanien", "eu-innenminister", "südgrenze spanien", + "mittelmeer migration", "grenzschutz europa", "flüchtlinge mittelmeer", + "überfahrten mittelmeer", "schengen-außengrenzen", "europol migration", + "aufnahmeverfahren eu", +] +treffer = sum(1 for w in KEYWORDS_LAGE_55 if wort_trifft(w, SCHLAGZEILE)) +alt_treffer = sum(1 for w in KEYWORDS_LAGE_55 if w in SCHLAGZEILE) +pruefe("B6 der gemeldete Keyword-Satz trifft die Schlagzeile jetzt", + treffer >= 2, (treffer, alt_treffer)) +pruefe("B7 vorher traf nur der eine Einzelbegriff", alt_treffer == 1, alt_treffer) + +# Die Schwelle bleibt unveraendert streng: ohne spezifisches Wort im Text +# braucht es mehrere Treffer. +FREMDE = "fussball bundesliga spieltag zusammenfassung".lower() +pruefe("B8 fremde Schlagzeile bleibt ohne Treffer", + sum(1 for w in KEYWORDS_LAGE_55 if wort_trifft(w, FREMDE)) == 0) +pruefe("B9 Spezifik-Erkennung unveraendert", + _is_specific_word("migrationskrise spanien") and not _is_specific_word("eu")) + + +# --------------------------------------------------------------------------- +# C. Der Auftrag verlangt Einzelbegriffe und Eigennamen +# --------------------------------------------------------------------------- +print("\nC. Auftrag an die Keyword-Erzeugung") +for stichwort, name in ( + ("EINZELWÖRTER", "C1 Einzelwoerter statt Phrasen gefordert"), + ("Eigennamen der Lage MÜSSEN", "C2 Eigennamen sind Pflicht"), + ("Schauplatz", "C3 Schauplatz wird ausdruecklich genannt"), + ("KEINE abstrakten Politikbegriffe", "C4 abstrakte Begriffe ausgeschlossen"), +): + pruefe(name, stichwort in FEED_SELECTION_PROMPT_TEMPLATE) +pruefe("C5 Beispiel nennt den konkreten Fehlerfall", + "ceuta" in FEED_SELECTION_PROMPT_TEMPLATE.lower()) + +print("\nErgebnis: " + str(ok) + " bestanden, " + str(fail) + " fehlgeschlagen") +sys.exit(1 if fail else 0)