This commit is contained in:
2025-05-11 07:19:51 +00:00
parent d55abc7065
commit fb34310ac8

View File

@@ -4068,240 +4068,162 @@ class DataProcessor:
# --- Der Code fuer den naechsten Verarbeitungsschritt (Wikipedia) folgt im naechsten Block --- # --- Der Code fuer den naechsten Verarbeitungsschritt (Wikipedia) folgt im naechsten Block ---
# Definition der Methode _process_single_row wird in der naechsten Nachricht fortgesetzt. # Definition der Methode _process_single_row wird in der naechsten Nachricht fortgesetzt.
# --- 2. Wikipedia Handling (Search, Extraction, Status Reset) --- # ======================================================================
# === 2. Wikipedia Handling (Search, Extraction, Status Reset) ==========
# ======================================================================
# Dieser Schritt wird ausgefuehrt, wenn 'wiki' in steps_to_run enthalten ist UND # Dieser Schritt wird ausgefuehrt, wenn 'wiki' in steps_to_run enthalten ist UND
# (_needs_wiki_processing True ist ODER force_reeval True ist). # (_needs_wiki_processing True ist ODER force_reeval True ist).
# _needs_wiki_processing prueft AN und S='X (URL Copied)'. # _needs_wiki_processing prueft V (alt AN) und Y="X (URL Copied)" (alt S).
# Die Logik fuer S='X (URL Copied)' dient dazu, eine URL, die durch die Wiki-Update
# Funktion in M kopiert wurde, sofort neu extrahieren zu lassen.
# Nutzt interne Helfer: _get_cell_value_safe, _needs_wiki_processing.
# Nutzt globale Helfer: COLUMN_MAP, logger, wikipedia, wikipedia.exceptions,
# retry_on_failure, unquote, time, traceback.
# Nutzt die uebergebene wiki_scraper Instanz.
# Pruefen Sie, ob der Wiki-Schritt im aktuellen Lauf angefordert wurde
run_wiki_step = 'wiki' in steps_to_run run_wiki_step = 'wiki' in steps_to_run
# Pruefen Sie, ob der Wiki-Schritt laut Status oder Re-Eval noetig ist # _needs_wiki_processing (Block 18) nutzt die NEUEN Spaltennamen für Timestamps/Status
wiki_processing_needed_based_on_status = self._needs_wiki_processing(row_data, force_reeval) wiki_processing_needed_based_on_status = self._needs_wiki_processing(row_data, force_reeval)
# Wenn der Wiki-Schritt angefordert wurde UND laut Status/Re-Eval noetig ist
if run_wiki_step and wiki_processing_needed_based_on_status: if run_wiki_step and wiki_processing_needed_based_on_status:
any_processing_done = True # Markiere, dass in dieser Zeile etwas getan wird any_processing_done = True
# Bestimme den Grund fuer die Ausfuehrung dieses Schritts fuer das Logging
grund_message_parts = [] grund_message_parts = []
if force_reeval: grund_message_parts.append('Re-Eval') if force_reeval: grund_message_parts.append('Re-Eval')
# Pruefe, ob der Timestamp AN leer ist (nutzt interne Helfer) if not self._get_cell_value_safe(row_data, "Wikipedia Timestamp").strip(): grund_message_parts.append('V (Wikipedia Timestamp) leer') # Neuer Schlüssel
if not self._get_cell_value_safe(row_data, "Wikipedia Timestamp").strip(): grund_message_parts.append('AN leer') if self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() == "X (URL COPIED)": grund_message_parts.append("Y (Chat Wiki Konsistenzpruefung)='X (URL Copied)'") # Neuer Schlüssel
# Pruefe, ob Status S "X (URL Copied)" ist (nutzt interne Helfer) grund_message = ", ".join(filter(None, grund_message_parts)) or "Unbekannter Grund"
if self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() == "X (URL COPIED)": grund_message_parts.append("S='X (URL Copied)'")
grund_message = ", ".join(grund_message_parts)
self.logger.info(f"Zeile {row_num_in_sheet}: Fuehre WIKI Suche/Extraktion aus (Grund: {grund_message})...") # <<< GEÄNDERT self.logger.info(f"Zeile {row_num_in_sheet}: Fuehre WIKI Suche/Extraktion aus (Grund: {grund_message})...")
# Holen Sie die aktuelle Wiki URL aus Spalte M (nutzt interne Helfer) # Aktuelle Wiki URL aus Spalte N (alt M)
url_in_m = self._get_cell_value_safe(row_data, "Wiki URL").strip() url_in_n_sheet = self._get_cell_value_safe(row_data, "Wiki URL").strip() # Neuer Schlüssel "Wiki URL"
url_to_extract = None # Die URL, von der wir am Ende Daten extrahieren werden url_to_extract_from = None
search_was_needed = False # Flag, ob eine neue Suche durchgefuehrt wurde search_was_needed_flag = False
status_y_indicates_reparse = self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() == "X (URL COPIED)" # Neuer Schlüssel
timestamp_v_is_empty = not self._get_cell_value_safe(row_data, "Wikipedia Timestamp").strip() # Neuer Schlüssel
# --- Logik zur Bestimmung der URL, die verwendet werden soll --- n_url_looks_valid_in_sheet = url_in_n_sheet and isinstance(url_in_n_sheet, str) and "wikipedia.org/wiki/" in url_in_n_sheet.lower() and url_in_n_sheet.lower() not in ["k.a.", "kein artikel gefunden", "fehler bei suche", "http:"]
# Prioritaet (bei Ausfuehrung des Wiki-Schritts):
# 1. Wenn Status S == "X (URL Copied)": Ignoriere URL in M, fuehre neue Suche aus.
# 2. Wenn force_reeval True: Nimm URL in M, WENN gueltig aussehend. Sonst neue Suche.
# 3. Wenn AN leer (und kein S="X(URL Copied)", kein Re-Eval): Nimm URL in M, WENN valide. Sonst neue Suche.
status_s_indicates_reparse = self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() == "X (URL COPIED)"
an_value = self._get_cell_value_safe(row_data, "Wikipedia Timestamp").strip()
# Pruefe, ob die URL in M existiert und wie eine Wikipedia-URL aussieht (ignoriert "k.A." und Fehlerwerte)
m_url_exists_and_looks_valid = url_in_m and isinstance(url_in_m, str) and "wikipedia.org/wiki/" in url_in_m.lower() and url_in_m.lower() not in ["k.a.", "kein artikel gefunden", "fehler bei suche", "http:"] # Fuege "http:" hinzu
# Bestimmen Sie, ob eine neue Suche notwendig ist
if status_s_indicates_reparse:
# Wenn Status S signalisiert, dass eine neu kopierte URL extrahiert werden soll, fuehre immer eine Suche aus.
self.logger.warning(f" -> Status S ist 'X (URL Copied)', ignoriere URL '{url_in_m[:100]}...' in M und starte neue Suche...") # <<< GEÄNDERT
search_was_needed = True # Suche ist noetig
# --- Logik zur Bestimmung der URL und Priorisierung von SerpAPI ---
if status_y_indicates_reparse:
self.logger.info(f" -> Status Y ist 'X (URL Copied)', ignoriere URL '{url_in_n_sheet[:100]}...' in N. Starte neue Suche (primär SerpAPI)...")
search_was_needed_flag = True
elif force_reeval: elif force_reeval:
# Wenn Re-Eval erzwungen wird self.logger.debug(" -> Re-Eval Modus aktiv fuer Wiki-Schritt.")
self.logger.debug(" -> Re-Eval Modus aktiv fuer Wiki-Schritt.") # <<< GEÄNDERT if n_url_looks_valid_in_sheet:
# Wenn die URL in M existiert und gueltig aussieht self.logger.info(f" -> Re-Eval: Nutze vorhandene URL aus Spalte N direkt: {url_in_n_sheet[:100]}...")
if m_url_exists_and_looks_valid: url_to_extract_from = url_in_n_sheet
# Im Re-Eval Modus nehmen wir die URL aus M an, OHNE erneute Validierung oder Suche (Vertrauen auf M!).
self.logger.info(f" -> Re-Eval: Nutze vorhandene URL aus Spalte M direkt: {url_in_m[:100]}...") # <<< GEÄNDERT
url_to_extract = url_in_m # Verwende die URL aus M direkt
else: else:
# Wenn M leer/ungueltig ist, auch im Re-Eval Modus neu suchen self.logger.debug(f" -> Re-Eval: Spalte N leer/ungueltig ('{url_in_n_sheet[:100]}...'). Starte neue Suche (primär SerpAPI)...")
self.logger.debug(f" -> Re-Eval: Spalte M ist leer oder ungueltig ('{url_in_m[:100]}...'). Starte neue Suche...") # <<< GEÄNDERT search_was_needed_flag = True
search_was_needed = True # Suche ist noetig elif timestamp_v_is_empty: # Wikipedia Timestamp (V, alt AN) ist leer
if n_url_looks_valid_in_sheet:
elif not an_value: self.logger.debug(f" -> Wikipedia Timestamp (V) fehlt, pruefe Validitaet der URL aus N: {url_in_n_sheet[:100]}...")
# Wenn AN leer ist (und kein S="X(Copied)" oder Re-Eval)
# Wenn die URL in M existiert und gueltig aussieht
if m_url_exists_and_looks_valid:
# Wenn AN fehlt und M gefuellt ist, pruefen wir die Validitaet der M-URL ueber die wikipedia Bibliothek.
self.logger.debug(f" -> AN fehlt, pruefe Validitaet der URL aus M: {url_in_m[:100]}...") # <<< GEÄNDERT
try: try:
# Extrahieren des Titels aus der URL fuer wikipedia.page (nutzt globale Helfer) # Validierung mit der wiki_scraper Instanz
# Dieser Aufruf kann Exceptions werfen (PageError, DisambiguationError). # _validate_article erwartet ein Page-Objekt. Wir müssen es zuerst holen.
title_from_url_part = url_in_m.split('/wiki/', 1)[1].split('#')[0] # Titelteil nach /wiki/, Anker entfernen title_from_url_part = url_in_n_sheet.split('/wiki/', 1)[1].split('#')[0]
title_from_url = unquote(title_from_url_part).replace('_', ' ') # Dekodieren und Unterstriche ersetzen title_from_url = unquote(title_from_url_part).replace('_', ' ')
page_object_from_n = wikipedia.page(title_from_url, auto_suggest=False, preload=True) # Kann Exceptions werfen
# Laden des Page Objekts, um es mit _validate_article zu pruefen. if self.wiki_scraper._validate_article(page_object_from_n, company_name, website_url):
# wikipedia.page nutzt intern Requests und API calls, die fehlschlagen koennen. url_to_extract_from = page_object_from_n.url
# wikipedia.page selbst kann wikipedia.exceptions werfen. self.logger.info(f" -> Vorhandene URL aus N '{url_to_extract_from[:100]}...' ist valide und wird verwendet.")
# Wir fangen diese spezifischen wikipedia.exceptions hier ab, aber andere RequestsExceptions
# werden vom retry_on_failure auf extract_company_data gefangen (spaeter).
page_from_m = wikipedia.page(title_from_url, auto_suggest=False, preload=True)
# Validierung des Artikels mit der Scraper-Methode (nutzt interne Methode)
# _validate_article kann interne Fehler haben (z.B. bei HTML Parsing), aber faengt sie.
if self.wiki_scraper._validate_article(page_from_m, company_name, website_url):
url_to_extract = page_from_m.url # Die URL ist valide und wird verwendet
self.logger.info(f" -> Vorhandene URL aus M '{url_to_extract[:100]}...' ist valide und wird verwendet.") # <<< GEÄNDERT
else: else:
# Wenn der Artikel aus M nicht validiert wird self.logger.warning(f" -> Vorhandene URL aus N '{page_object_from_n.title[:100]}...' ist NICHT valide. Starte neue Suche (primär SerpAPI)...")
self.logger.warning(f" -> Vorhandene URL aus M '{page_from_m.title[:100]}...' ist NICHT valide. Starte neue Suche...") # <<< GEÄNDERT search_was_needed_flag = True
search_was_needed = True # Suche ist noetig except (wikipedia.exceptions.PageError, wikipedia.exceptions.DisambiguationError) as e_wiki_val:
self.logger.warning(f" -> Vorhandene URL aus N '{url_in_n_sheet[:100]}...' fuehrt zu Fehler ({type(e_wiki_val).__name__}). Starte neue Suche (primär SerpAPI)...")
except (wikipedia.exceptions.PageError, wikipedia.exceptions.DisambiguationError) as e_wiki_m: if isinstance(e_wiki_val, wikipedia.exceptions.DisambiguationError):
# Wenn die URL in M zu einem nicht existierenden Artikel oder einer Begriffsklaerung fuehrt self.logger.debug(f" -> Disambiguation Optionen: {str(e_wiki_val.options)[:100]}...")
self.logger.warning(f" -> Vorhandene URL aus M '{url_in_m[:100]}...' fuehrt zu Fehler ({type(e_wiki_m).__name__}). Starte neue Suche...") # <<< GEÄNDERT search_was_needed_flag = True
# Logge die Disambiguation Optionen auf Debug, falls vorhanden except Exception as e_val_m_general:
if isinstance(e_wiki_m, wikipedia.exceptions.DisambiguationError): self.logger.exception(f" -> Unerwarteter Fehler beim Pruefen/Laden der URL aus N '{url_in_n_sheet[:100]}...': {e_val_m_general}. Starte neue Suche (primär SerpAPI)...")
self.logger.debug(f" -> Disambiguation Optionen: {str(e_wiki_m.options)[:100]}...") # <<< GEÄNDERT search_was_needed_flag = True
search_was_needed = True # Suche ist noetig
pass # Faert fort
except Exception as e_val_m:
# Fange andere unerwartete Fehler beim Pruefen der URL aus M ab (z.B. URL-Parsing-Fehler vor wikipedia.page)
self.logger.exception(f" -> Unerwarteter Fehler beim Pruefen der URL aus M '{url_in_m[:100]}...': {e_val_m}. Starte neue Suche...") # <<< GEÄNDERT
search_was_needed = True # Suche ist noetig
pass # Faert fort
else: else:
# M ist leer/ungueltig und AN fehlt -> Suche starten self.logger.debug(f" -> Wikipedia Timestamp (V) fehlt und N leer/ungueltig ('{url_in_n_sheet[:100]}...'). Starte Wikipedia-Suche (primär SerpAPI) fuer '{company_name[:50]}...'...")
self.logger.debug(f" -> AN fehlt und M leer/ungueltig ('{url_in_m[:100]}...'). Starte Wikipedia-Suche fuer '{company_name[:100]}...'...") # <<< GEÄNDERT search_was_needed_flag = True
search_was_needed = True # Suche ist noetig
# --- Führe die Suche aus, wenn search_was_needed True ist --- if search_was_needed_flag:
if search_was_needed: self.logger.info(f" -> Suche nach Wikipedia-Artikel für '{company_name[:50]}...' via SerpAPI...")
self.logger.debug(f" -> Fuehre Wikipedia Suche ueber scraper durch...") # <<< GEÄNDERT search_name_for_serp = crm_kurzform if crm_kurzform and len(crm_kurzform) > 2 else company_name
self.logger.debug(f" Verwende Suchnamen für SerpAPI: '{search_name_for_serp}'")
try: try:
# Rufe die search_company_article Methode des Scrapers auf. # serp_wikipedia_lookup ist global und nutzt retry
# search_company_article ist mit retry_on_failure dekoriert und wirft bei endgueltigem Fehler eine Exception. new_url_from_serp = serp_wikipedia_lookup(search_name_for_serp, website=website_url)
# Nutzt die ggf. neue Website URL fuer Kontext im search_company_article. if new_url_from_serp:
validated_page = self.wiki_scraper.search_company_article(company_name, website_url) # Nutzt die uebergebene scraper Instanz url_to_extract_from = new_url_from_serp
self.logger.info(f" -> SerpAPI Suche erfolgreich, gefundene URL: {url_to_extract_from[:100]}...")
if validated_page:
# Wenn ein validierter Artikel gefunden wurde, setze die URL, von der extrahiert werden soll.
url_to_extract = validated_page.url
self.logger.info(f" -> Suche erfolgreich, validierte URL: {url_to_extract[:100]}...") # <<< GEÄNDERT
else: else:
# Wenn die Suche keinen validierten Artikel fand self.logger.warning(f" -> SerpAPI Suche fand keinen passenden Wikipedia-Artikel für '{search_name_for_serp}'.")
self.logger.debug(f" -> Suche fand keinen validierten Artikel fuer '{company_name[:100]}...'.") # <<< GEÄNDERT url_to_extract_from = 'Kein Artikel gefunden' # Expliziter Wert
url_to_extract = 'Kein Artikel gefunden' # Signalisiert kein Artikel gefunden except Exception as e_serp_wiki_search:
self.logger.error(f"FEHLER bei SerpAPI Wikipedia Suche für '{search_name_for_serp}': {e_serp_wiki_search}")
url_to_extract_from = f"FEHLER bei Suche (SerpAPI): {str(e_serp_wiki_search)[:50]}..."
except Exception as e_wiki_search: # --- Datenextraktion, wenn eine URL bestimmt wurde ---
# Wenn search_company_article eine Exception wirft (nach Retries) if url_to_extract_from and isinstance(url_to_extract_from, str) and url_to_extract_from.lower() not in ['kein artikel gefunden'] and not url_to_extract_from.startswith("FEHLER"):
# Der Fehler wird bereits vom retry_on_failure Decorator geloggt. self.logger.debug(f" -> Extrahiere Daten von URL: {url_to_extract_from[:100]}...")
self.logger.error(f"FEHLER bei Wikipedia Suche fuer '{company_name[:100]}...': {e_wiki_search}") # <<< GEÄNDERT
url_to_extract = f"FEHLER bei Suche: {str(e_wiki_search)[:50]}..." # Signalisiert Fehler bei Suche (gekuerzt)
# Pass, faert fort, um zumindest den Status zu setzen.
pass
# --- Datenextraktion, wenn eine URL bestimmt wurde, von der extrahiert werden soll ---
# Extrahiere Daten, wenn url_to_extract einen Wert hat, der NICHT "Kein Artikel gefunden" oder ein Fehlerstring ist.
if url_to_extract and isinstance(url_to_extract, str) and url_to_extract.lower() not in ['kein artikel gefunden'] and not url_to_extract.startswith("FEHLER"):
self.logger.debug(f" -> Extrahiere Daten von URL: {url_to_extract[:100]}...") # <<< GEÄNDERT
try: try:
# Rufe die extract_company_data Methode des Scrapers auf. extracted_data = self.wiki_scraper.extract_company_data(url_to_extract_from)
# extract_company_data ist mit retry_on_failure dekoriert und wirft bei endgueltigem Fehler eine Exception. if extracted_data and isinstance(extracted_data, dict) and extracted_data.get('url') != 'k.A.':
extracted_data = self.wiki_scraper.extract_company_data(url_to_extract) # Nutzt die uebergebene scraper Instanz final_wiki_data = extracted_data
wiki_data_updated_in_this_run = True
# Pruefen Sie, ob die Extraktion erfolgreich war (nicht None oder mit Fehlerwert) self.logger.info(f" -> Datenextraktion von {url_to_extract_from[:100]}... erfolgreich.")
if extracted_data and isinstance(extracted_data, dict) and extracted_data.get('url') != 'k.A.': # Pruefe auf gueltige Extraktion
final_wiki_data = extracted_data # Aktualisiere die Arbeitskopie der Wiki-Daten mit den extrahierten Daten.
wiki_data_updated_in_this_run = True # Markieren, dass extrahierte Daten da sind (Trigger fuer Chat).
self.logger.info(f" -> Datenextraktion von {url_to_extract[:100]}... erfolgreich.") # <<< GEÄNDERT
else: else:
# Wenn extrahierte Daten leer oder ungueltig sind (z.B. parse Fehler intern) self.logger.error(f" -> Fehler bei Datenextraktion von {url_to_extract_from[:100]}... oder Extraktion war leer. Setze Daten auf 'k.A.'")
self.logger.error(f" -> Fehler bei Datenextraktion von {url_to_extract[:100]}... oder Extraktion war leer. Setze Daten auf 'k.A.'") # <<< GEÄNDERT final_wiki_data = {'url': url_to_extract_from, 'sitz_stadt': 'k.A.', 'sitz_land': 'k.A.', 'first_paragraph': 'k.A. (Extraktion fehlgeschlagen)', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.'}
# Behalte die URL, aber setze alle anderen Felder auf k.A. oder Fehler. wiki_data_updated_in_this_run = True
final_wiki_data = {'url': url_to_extract, 'first_paragraph': 'k.A. (Extraktion fehlgeschlagen)', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.'}
wiki_data_updated_in_this_run = True # Markieren, dass die Daten ueberschrieben werden.
except Exception as e_wiki_extract: except Exception as e_wiki_extract:
# Wenn extract_company_data eine Exception wirft (nach Retries) self.logger.error(f"FEHLER bei Wikipedia Datenextraktion von {url_to_extract_from[:100]}...: {e_wiki_extract}")
self.logger.error(f"FEHLER bei Wikipedia Datenextraktion von {url_to_extract[:100]}...: {e_wiki_extract}") # <<< GEÄNDERT final_wiki_data = {'url': url_to_extract_from, 'sitz_stadt': 'k.A.', 'sitz_land': 'k.A.', 'first_paragraph': f'k.A. (FEHLER Extraktion: {str(e_wiki_extract)[:50]}...)', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.'}
# Setze Daten auf k.A., behalte aber die URL, von der extrahiert werden sollte wiki_data_updated_in_this_run = True
final_wiki_data = {'url': url_to_extract, 'first_paragraph': f'k.A. (FEHLER Extraktion: {str(e_wiki_extract)[:50]}...)', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.'} # Korrektur: e -> e_wiki_extract else: # Wenn keine gueltige URL zum Extrahieren bestimmt wurde
wiki_data_updated_in_this_run = True # Markieren, dass die Daten ueberschrieben werden. self.logger.debug(f" -> Keine gueltige URL zum Extrahieren bestimmt ('{url_to_extract_from}'). Wiki-Daten nicht extrahiert oder bleiben auf Fehlerstatus.")
pass # Faert fort if url_to_extract_from in ['Kein Artikel gefunden'] or (isinstance(url_to_extract_from, str) and url_to_extract_from.startswith("FEHLER")):
final_wiki_data['url'] = url_to_extract_from
for key_to_clear in ['sitz_stadt', 'sitz_land', 'first_paragraph', 'branche', 'umsatz', 'mitarbeiter', 'categories']:
final_wiki_data[key_to_clear] = 'k.A.'
wiki_data_updated_in_this_run = True
else: # --- Sheet Updates für N-AB (Wiki-Daten) und Timestamps V, W, X ---
# Wenn keine gueltige URL zum Extrahieren bestimmt wurde (z.B. Suche fand nichts oder Fehler bei Suche) # Verwende die NEUEN Spaltenschlüssel aus der aktuellen COLUMN_MAP
self.logger.debug(f" -> Keine gueltige URL zum Extrahieren bestimmt ('{url_to_extract}'). Wiki-Daten nicht extrahiert.") # <<< GEÄNDERT
# final_wiki_data behaelt die current_wiki_data Werte (initial geladen) oder wurde oben bei Suche auf "Kein Artikel gefunden"/"FEHLER" gesetzt.
# Stelle sicher, dass final_wiki_data die richtige URL enthaelt, auch wenn keine Extraktion stattfand.
if url_to_extract in ['Kein Artikel gefunden'] or (isinstance(url_to_extract, str) and url_to_extract.startswith("FEHLER")): # Korrektur Pruefung
final_wiki_data['url'] = url_to_extract # Update nur die URL im Ergebnis
# --- Sheet Updates fuer M-R und AN ---
# Diese Updates werden immer hinzugefuegt, WENN der WIKI-Schritt lief (run_wiki_step and wiki_processing_needed_based_on_status war True).
# Auch wenn die Suche/Extraktion fehlschlug (dann werden k.A. oder Fehlermeldungen geschrieben).
# Aktualisiere die Spalten M-R mit den finalen Daten im final_wiki_data Dictionary.
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki URL"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('url', 'k.A.')]]}) updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki URL"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('url', 'k.A.')]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Sitz Stadt"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('sitz_stadt', 'k.A.')]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Sitz Land"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('sitz_land', 'k.A.')]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Absatz"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('first_paragraph', 'k.A.')]]}) updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Absatz"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('first_paragraph', 'k.A.')]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Branche"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('branche', 'k.A.')]]}) updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Branche"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('branche', 'k.A.')]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Umsatz"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('umsatz', 'k.A.')]]}) updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Umsatz"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('umsatz', 'k.A.')]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Mitarbeiter"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('mitarbeiter', 'k.A.')]]}) updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Mitarbeiter"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('mitarbeiter', 'k.A.')]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Kategorien"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('categories', 'k.A.')]]}) updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Kategorien"] + 1)}{row_num_in_sheet}', 'values': [[final_wiki_data.get('categories', 'k.A.')]]})
# Setze den Wikipedia Timestamp (AN), da der Wiki-Schritt lief (auch wenn fehlerhaft) updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wikipedia Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]}) # Spalte V
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wikipedia Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
# Setze Y (Chat Wiki Konsistenzpruefung) und W (Wiki Verif. Timestamp) zurück, wenn Neubewertung nötig
status_y_from_sheet = self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() # Neuer Schlüssel Y
url_changed_and_is_valid_wiki_link = (url_in_n_sheet != final_wiki_data.get('url')) and \
isinstance(final_wiki_data.get('url'), str) and \
"wikipedia.org/wiki/" in final_wiki_data.get('url', '').lower() and \
final_wiki_data.get('url', '').lower() not in ["k.a.", "kein artikel gefunden"] and \
not final_wiki_data.get('url', '').startswith("FEHLER")
# --- Setze S ('Chat Wiki Konsistenzpruefung') und AX ('Wiki Verif. Timestamp') zurueck, wenn Neubewertung noetig ist --- if force_reeval or status_y_from_sheet == "X (URL COPIED)" or url_changed_and_is_valid_wiki_link:
# Eine Neubewertung (Zuruecksetzen von S und AX) ist noetig, wenn: y_idx = COLUMN_MAP.get("Chat Wiki Konsistenzpruefung")
# - force_reeval True ist (immer bei Re-Eval des Wiki-Schritts) w_idx = COLUMN_MAP.get("Wiki Verif. Timestamp")
# - Status S zuvor "X (URL Copied)" war (der Trigger fuer die Re-Extraktion) if y_idx is not None and w_idx is not None:
# - Die neue URL in M (final_wiki_data['url']) anders ist als die urspruengliche URL aus M (url_in_m), UND die neue URL gueltig ist. y_let = self.sheet_handler._get_col_letter(y_idx + 1)
w_let = self.sheet_handler._get_col_letter(w_idx + 1)
status_s_indicates_reparse = self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() == "X (URL COPIED)" updates.append({'range': f'{y_let}{row_num_in_sheet}', 'values': [["?"]]})
# Pruefe, ob die FINAL_wiki_data URL (nach Suche/Extraktion) anders ist als die URSPRUENGLICHE URL in M im Sheet. updates.append({'range': f'{w_let}{row_num_in_sheet}', 'values': [[""]]}) # W (alt AX) leeren
# UND stelle sicher, dass die neue URL eine gueltige URL ist (nicht "k.A." oder Fehlerstring). # Grund für Reset loggen
new_wiki_url = final_wiki_data.get('url') reset_reason_parts = []
url_changed_and_valid = (url_in_m != new_wiki_url) and isinstance(new_wiki_url, str) and new_wiki_url.lower() not in ["k.a.", "kein artikel gefunden"] and not new_wiki_url.startswith("FEHLER") # Korrektur Pruefung if force_reeval: reset_reason_parts.append('Re-Eval')
if status_y_from_sheet == "X (URL COPIED)": reset_reason_parts.append("Y='X (URL Copied)'")
# Bestimme, ob S und AX zurueckgesetzt werden sollen if url_changed_and_is_valid_wiki_link: reset_reason_parts.append('URL geändert und valide')
if force_reeval or status_s_indicates_reparse or url_changed_and_valid: self.logger.info(f" -> Status Y ('Chat Wiki Konsistenzpruefung') zurueckgesetzt auf '?' und Timestamp W ('Wiki Verif. Timestamp') geleert (Grund: {', '.join(reset_reason_parts) or 'Unbekannt'}).")
s_idx = COLUMN_MAP.get("Chat Wiki Konsistenzpruefung")
ax_idx = COLUMN_MAP.get("Wiki Verif. Timestamp")
if s_idx is not None and ax_idx is not None:
s_let = self.sheet_handler._get_col_letter(s_idx + 1)
ax_let = self.sheet_handler._get_col_letter(ax_idx + 1)
# Fuegen Sie die Updates zum Zuruecksetzen von S und AX hinzu
# S wird auf '?' gesetzt, um anzuzeigen, dass eine Verifizierung aussteht
updates.append({'range': f'{s_let}{row_num_in_sheet}', 'values': [["?"]]})
# AX wird geleert, um die Batch-Verifizierung zu triggern
updates.append({'range': f'{ax_let}{row_num_in_sheet}', 'values': [[""]]})
# Bestimme den Grund-String fuer das Logging
grund_message_parts = []
if force_reeval: grund_message_parts.append('Re-Eval')
if status_s_indicates_reparse: grund_message_parts.append("S='X (URL Copied)'")
if url_changed_and_valid: grund_message_parts.append('URL geaendert und gueltig')
grund_message_s_reset = ", ".join(grund_message_parts)
self.logger.info(f" -> Status S zurueckgesetzt auf '?' und Timestamp AX geleert fuer erneute Verifikation (Grund: {grund_message_s_reset}).") # <<< GEÄNDERT
else: else:
# Logge Fehler, wenn Spaltenindizes fehlen self.logger.error("FEHLER: Konnte Spaltenbuchstaben fuer Y oder W nicht ermitteln. Zuruecksetzen uebersprungen.")
self.logger.error("FEHLER: Konnte Spaltenbuchstaben fuer S oder AX nicht ermitteln. Zuruecksetzen uebersprungen.") # <<< GEÄNDERT
# else if run_wiki_step (aber nicht processing_needed_based_on_status):
# self.logger.debug(f"Zeile {row_num_in_sheet}: Ueberspringe WIKI Suche/Extraktion (Timestamp V vorhanden, Y nicht 'X (URL Copied)' und kein Re-Eval).")
# --- Ende Wikipedia Handling ---
# else if run_wiki_step: # else if run_wiki_step:
@@ -4962,47 +4884,33 @@ class DataProcessor:
# Verarbeitung der markierten Zeilen # Verarbeitung der markierten Zeilen
processed_count = 0 # Zaehlt Zeilen, fuer die _process_single_row aufgerufen wurde (im Rahmen des Limits). processed_count = 0
# Die Liste updates_clear_flag wird NICHT mehr hier gefuellt, da _process_single_row das Update selbst hinzufuegt (Block 21).
# Die Liste rows_actually_processed wird nicht mehr benoetigt.
# Iteriere ueber die gefundenen markierten Zeilen # Iteriere ueber die gefundenen markierten Zeilen
for task in rows_to_process: for task in rows_to_process:
# Ueberpruefen Sie das Limit fuer die zu verarbeitenden Zeilen VOR der Verarbeitung # === HIER DIE DEBUG-AUSGABE EINFÜGEN ===
self.logger.debug(f"Re-Eval Loop Check: processed_count={processed_count}, row_limit={row_limit}")
# === ENDE DEBUG-AUSGABE ===
if row_limit is not None and isinstance(row_limit, int) and row_limit > 0 and processed_count >= row_limit: if row_limit is not None and isinstance(row_limit, int) and row_limit > 0 and processed_count >= row_limit:
# Wenn das Limit erreicht ist und es ein positives Limit gibt self.logger.info(f"Zeilenlimit ({row_limit}) fuer Re-Evaluation erreicht. Breche weitere Verarbeitung ab. Processed: {processed_count}")
self.logger.info(f"Zeilenlimit ({row_limit}) fuer Re-Evaluation erreicht. Breche weitere Verarbeitung ab.") # <<< GEÄNDERT break
break # Brich die Schleife ab
row_num = task['row_num']
row_data = task['data']
row_num = task['row_num'] # 1-basierte Zeilennummer self.logger.info(f"Bearbeite Re-Eval Zeile {row_num}...")
row_data = task['data'] # Die Rohdaten fuer diese Zeile
self.logger.info(f"Bearbeite Re-Eval Zeile {row_num}...") # <<< GEÄNDERT
try: try:
# Rufe die Methode zur Verarbeitung einer einzelnen Zeile auf (_process_single_row Block 19).
# In diesem Modus setzen wir immer force_reeval=True.
# Wir uebergeben die aus CLI/Menue ausgewaehlten Schritte in steps_to_run_set.
# Wir uebergeben das clear_flag, damit _process_single_row weiss, ob das 'x' geloescht werden soll.
# _process_single_row fuehrt die Schritte durch, sammelt Updates (inkl. 'x'-Flag Update wenn clear_x_flag=True)
# und sendet das Batch-Update fuer diese Zeile.
self._process_single_row( self._process_single_row(
row_num_in_sheet = row_num, row_num_in_sheet = row_num,
row_data = row_data, row_data = row_data,
steps_to_run = steps_to_run_set, # <-- Uebergibt die aus CLI/Menue ausgewaehlten Schritte steps_to_run = steps_to_run_set,
force_reeval = True, # <-- Erzwingt Re-Evaluation unabhaengig von Timestamps fuer die ausgewaehlten Schritte force_reeval = True,
clear_x_flag = clear_flag # <-- Uebergibt, ob das 'x'-Flag von _process_single_row geloescht werden soll clear_x_flag = clear_flag
) )
# Zaehlen, wenn _process_single_row erfolgreich aufgerufen wurde (unabhaengig von internen Ueberspringungen in _process_single_row).
processed_count += 1 processed_count += 1
# Die Liste rows_actually_processed wird nicht mehr benoetigt.
except Exception as e_proc: except Exception as e_proc:
# Wenn _process_single_row einen Fehler wirft (nachdem interne Retries aufgaben), self.logger.exception(f"FEHLER bei Re-Evaluation von Zeile {row_num}: {e_proc}")
# fangen wir ihn hier, loggen ihn und fahren mit der naechsten Zeile fort.
# Das 'x'-Flag wird in diesem Fall NICHT geloescht, da _process_single_row nicht bis zum Ende kam.
self.logger.exception(f"FEHLER bei Re-Evaluation von Zeile {row_num}: {e_proc}") # <<< GEÄNDERT
# Hier koennen Sie z.B. einen Fehlerindikator in eine spezielle Spalte im Sheet schreiben lassen. # Hier koennen Sie z.B. einen Fehlerindikator in eine spezielle Spalte im Sheet schreiben lassen.
# Dieses Update muesste dann separat oder im naechsten Lauf behandelt werden. # Dieses Update muesste dann separat oder im naechsten Lauf behandelt werden.