This commit is contained in:
2025-05-27 06:37:18 +00:00
parent 77c9b51cf2
commit 335631152a

View File

@@ -4349,211 +4349,143 @@ class DataProcessor:
in Spalte A geloescht, wenn die Zeile verarbeitet wurde.
Defaults to False.
"""
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
self.logger.info(f"--- Starte Verarbeitung fuer Zeile {row_num_in_sheet} {'(Re-Eval)' if force_reeval else ''} (Schritte: {', '.join(steps_to_run) if steps_to_run else 'Keine ausgewählt'}) ---") # <<< GEÄNDERT
# Liste zur Sammlung von Sheet-Updates fuer diese Zeile
# Updates sind Dictionaries: {'range': 'A1', 'values': [['Wert']]}
self.logger.info(f"--- Starte Verarbeitung fuer Zeile {row_num_in_sheet} {'(Re-Eval)' if force_reeval else ''} (Schritte: {', '.join(steps_to_run) if steps_to_run else 'Keine ausgewählt'}) ---")
updates = []
# Aktueller Zeitstempel fuer die Timestamp-Spalten
now_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Flag, ob IRGENDEINE Verarbeitung in dieser Zeile stattgefunden hat
any_processing_done = False
# Flags, die den Zustand der Zeile waehrend DIESES Laufs verfolgen
wiki_data_updated_in_this_run = False # Wurden Wiki-Daten (M-R, AN) gerade aktualisiert? (Trigger fuer Chat)
chat_eval_just_ran = False # Liefen die ChatGPT-Evaluationen (AO) gerade? (Trigger fuer ML)
wiki_data_updated_in_this_run = False
chat_eval_just_ran = False
# --- Initiale Werte lesen ---
# Lesen Sie die Werte aus den Spalten, die fuer mehrere Schritte benoetigt werden.
# Nutzt die interne Helferfunktion _get_cell_value_safe, um sicher auf Zellen zuzugreifen.
# Stellen Sie sicher, dass alle benoetigten Spalten in COLUMN_MAP (Block 1) vorhanden sind.
# CRM Daten
company_name = self._get_cell_value_safe(row_data, "CRM Name").strip()
website_url = self._get_cell_value_safe(row_data, "CRM Website").strip() # Arbeitskopie der URL (kann sich aendern)
original_website_url_in_sheet = website_url # Originalwert aus Sheet behalten, fuer Website Lookup Logik
crm_kurzform = self._get_cell_value_safe(row_data, "CRM Kurzform").strip() # Benoetigt fuer Contact Search
website_url = self._get_cell_value_safe(row_data, "CRM Website").strip()
original_website_url_in_sheet = website_url
crm_kurzform = self._get_cell_value_safe(row_data, "CRM Kurzform").strip()
crm_branche = self._get_cell_value_safe(row_data, "CRM Branche").strip()
crm_beschreibung = self._get_cell_value_safe(row_data, "CRM Beschreibung").strip()
# CRM Umsatz und Mitarbeiter werden direkt aus row_data geholt, wenn benoetigt (z.B. fuer Konsolidierung)
# NEU: Wert aus Spalte D (Parent Account Name) hier lesen
parent_account_name_d = self._get_cell_value_safe(row_data, "Parent Account Name").strip()
self.logger.debug(f" Zeile {row_num_in_sheet}: Gelesener Parent Account (D): '{parent_account_name_d}'")
# Vorhandene Wikipedia-Daten (aus dem Sheet)
# Dies sind die Daten, die am ANFANG im Sheet stehen. Sie werden im Wiki-Schritt (Block 21)
# durch extrahierte Daten ersetzt, wenn der Wiki-Schritt laeuft.
# Initialisiere mit "k.A." wenn die Zelle leer ist.
# Vorgeschlagener Parent aus Spalte O (wird für Wiki-Suche und Parent-Vorschlagslogik benötigt)
system_suggested_parent_name_o = self._get_cell_value_safe(row_data, "System Vorschlag Parent Account").strip() # Spalte O
current_wiki_data = {
'url': self._get_cell_value_safe(row_data, "Wiki URL") or 'k.A.',
'sitz_stadt': self._get_cell_value_safe(row_data, "Wiki Sitz Stadt") or 'k.A.',
'sitz_land': self._get_cell_value_safe(row_data, "Wiki Sitz Land") or 'k.A.',
'first_paragraph': self._get_cell_value_safe(row_data, "Wiki Absatz") or 'k.A.',
'branche': self._get_cell_value_safe(row_data, "Wiki Branche") or 'k.A.',
'umsatz': self._get_cell_value_safe(row_data, "Wiki Umsatz") or 'k.A.',
'mitarbeiter': self._get_cell_value_safe(row_data, "Wiki Mitarbeiter") or 'k.A.',
'categories': self._get_cell_value_safe(row_data, "Wiki Kategorien") or 'k.A.'
}
# Arbeitskopie fuer die Wiki-Daten. Wird im Wiki-Schritt aktualisiert.
# Die Chat-Evaluationen (Block 22) und ML-Schaetzung (Block 23) nutzen diese finalen Daten.
final_wiki_data = current_wiki_data.copy()
website_raw = self._get_cell_value_safe(row_data, "Website Rohtext") or 'k.A.'
website_summary = self._get_cell_value_safe(row_data, "Website Zusammenfassung") or 'k.A.'
website_meta_details = self._get_cell_value_safe(row_data, "Website Meta-Details") or 'k.A.' # Spalte AI
url_pruefstatus = self._get_cell_value_safe(row_data, "URL Prüfstatus") or '' # Spalte AK
# Vorhandener Website-Rohtext und Zusammenfassung (aus dem Sheet)
# Dies sind die Daten, die am ANFANG im Sheet stehen. Sie werden im Website-Schritt (Block 20)
# durch gescrapte Daten ersetzt, wenn der Website-Schritt laeuft.
# Initialisiere mit "k.A." wenn die Zelle leer ist.
current_website_raw = self._get_cell_value_safe(row_data, "Website Rohtext") or 'k.A.'
current_website_summary = self._get_cell_value_safe(row_data, "Website Zusammenfassung") or 'k.A.'
# Arbeitskopien fuer die Website-Daten. Werden im Website-Schritt aktualisiert.
# Die Chat-Evaluationen (Block 22) und ML-Schaetzung (Block 23) nutzen diese finalen Daten.
website_raw = current_website_raw # Kann im Website-Schritt aktualisiert werden
website_summary = current_website_summary # Kann im Website-Schritt aktualisiert werden
# --- Die Logik fuer die einzelnen Verarbeitungsschritte folgt in den naechsten Bloecken ---
# Jeder Schritt prueft, ob er in steps_to_run enthalten ist UND (ob er laut Status noetig ist ODER force_reeval True ist).
# ======================================================================
# === Verarbeitungsschritte innerhalb von _process_single_row ==========
# ======================================================================
# --- 1. Website Handling (Lookup, Scraping, Summarization) ---
# Dieser Schritt wird ausgefuehrt, wenn 'web' in steps_to_run enthalten ist UND
# (_needs_website_processing True ist ODER force_reeval True ist).
# _needs_website_processing prueft AT. Die Lookup-Logik (D leer) ist separat.
# Die Website-Verarbeitung umfasst Lookup (optional), Scraping und Summarization.
# Nutzt interne Helfer: _get_cell_value_safe, _needs_website_processing.
# Nutzt globale Helfer: COLUMN_MAP, logger, serp_website_lookup, get_website_raw, summarize_website_content, datetime, time.
# Pruefen Sie, ob der Website-Schritt im aktuellen Lauf angefordert wurde
# --- 1. Website Handling (Lookup, Scraping, Summarization, Meta) ---
run_website_step = 'web' in steps_to_run
# Pruefen Sie, ob der Website-Schritt laut Status oder Re-Eval noetig ist
website_processing_needed_based_on_status = self._needs_website_processing(row_data, force_reeval)
# Wenn der Website-Schritt angefordert wurde UND laut Status/Re-Eval noetig ist
if run_website_step and website_processing_needed_based_on_status:
any_processing_done = True # Markiere, dass in dieser Zeile etwas getan wird
any_processing_done = True
grund_message_parts_web = []
if force_reeval: grund_message_parts_web.append('Re-Eval')
if not self._get_cell_value_safe(row_data, "Website Scrape Timestamp").strip(): grund_message_parts_web.append('AJ (Website Scrape Timestamp) leer') # Angepasster Key
grund_message_web = ", ".join(filter(None, grund_message_parts_web)) or "Unbekannter Grund"
self.logger.info(f"Zeile {row_num_in_sheet}: Fuehre WEBSITE Schritte aus (Grund: {grund_message_web})...")
# Bestimme den Grund fuer die Ausfuehrung dieses Schritts fuer das Logging
grund_message_parts = []
if force_reeval: grund_message_parts.append('Re-Eval')
if not self._get_cell_value_safe(row_data, "Website Scrape Timestamp").strip(): grund_message_parts.append('AT leer')
grund_message = ", ".join(grund_message_parts)
self.logger.info(f"Zeile {row_num_in_sheet}: Fuehre WEBSITE Schritte aus (Grund: {grund_message})...") # <<< GEÄNDERT
# Website Lookup nur, wenn die URL in Spalte D (CRM Website) leer oder "k.A." ist
# Nutzt die lokal gespeicherte Kopie der URL, die ggf. im Lookup ueberschrieben wird.
# a) Website Lookup (nur wenn D leer)
if not website_url or website_url.lower() == "k.a.":
self.logger.debug(" -> Website URL (D) leer oder k.A., suche ueber SERP...") # <<< GEÄNDERT
# Annahme: serp_website_lookup global definiert (Block 10) und nutzt logging/retry
try:
# Der serp_website_lookup Aufruf ist mit retry_on_failure dekoriert.
# Wenn er nach Retries fehlschlaegt, wirft er eine Exception.
new_website = serp_website_lookup(company_name) # Nutzt globalen Helfer (Block 10)
# Wenn eine neue Website gefunden wurde
if new_website != "k.A.":
# Ueberschreibe die lokale Variable website_url fuer den weiteren Schritt (Scraping)
website_url = new_website
# Fuegen Sie das Update fuer Spalte D zur Liste der Sheet-Updates hinzu
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["CRM Website"] + 1)}{row_num_in_sheet}', 'values': [[website_url]]})
self.logger.info(f" -> Neue Website gefunden und fuer Update D:{row_num_in_sheet} vorgemerkt: {website_url[:100]}...") # <<< GEÄNDERT
else:
# Wenn keine neue Website gefunden wurde
self.logger.warning(f" -> Keine neue Website ueber SERP gefunden fuer '{company_name[:100]}...'.") # <<< GEÄNDERT
# website_url bleibt leer oder k.A. in diesem Fall.
self.logger.debug(" -> Website URL (E) leer oder k.A., suche ueber SERP...")
try:
new_website = serp_website_lookup(company_name)
if new_website and new_website.lower() != "k.a." and not new_website.startswith("k.A. (Fehler"):
website_url = new_website # Aktualisiere Arbeitskopie
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["CRM Website"] + 1)}{row_num_in_sheet}', 'values': [[website_url]]})
self.logger.info(f" -> Neue Website gefunden und für Update E:{row_num_in_sheet} vorgemerkt: {website_url[:100]}...")
url_pruefstatus = "URL_OK_SERP" # Status aktualisieren
else:
self.logger.warning(f" -> Keine neue Website ueber SERP gefunden für '{company_name[:100]}...'.")
url_pruefstatus = "URL_SERP_FAILED" if not website_url or website_url.lower() == "k.a." else url_pruefstatus # Nur setzen, wenn vorher keine URL da war
except Exception as e_serp_lookup_web:
self.logger.error(f"FEHLER bei SERP Website Lookup für '{company_name[:100]}...': {e_serp_lookup_web}")
url_pruefstatus = "URL_SERP_ERROR"
except Exception as e_serp_lookup:
# Wenn serp_website_lookup eine Exception wirft (nach Retries)
self.logger.error(f"FEHLER bei SERP Website Lookup fuer '{company_name[:100]}...': {e_serp_lookup}") # <<< GEÄNDERT
# Bei Fehler bleibt website_url unveraendert (leer oder k.A.). Fahren Sie fort.
pass # Fahren Sie fort, falls eine URL im Sheet war oder gefunden wurde
# Führen Sie Scraping und Zusammenfassung nur durch, wenn eine gueltige Website URL vorhanden ist (lokale Variable website_url)
# Ueberpruefen Sie auf nicht-leere website_url und ungleich "k.A." oder Fehlerwerten.
# b) Scrape Rohtext, Meta-Details und c) Zusammenfassung (nur wenn URL vorhanden)
if website_url and website_url.lower() not in ["k.a.", "kein artikel gefunden", "fehler bei suche", "http:"]:
self.logger.debug(f" -> Scrape Rohtext von {website_url[:100]}...") # <<< GEÄNDERT
# Annahme: get_website_raw global definiert (Block 11) und nutzt logging/retry
try:
# Der get_website_raw Aufruf ist mit retry_on_failure dekoriert.
# Wenn er nach Retries fehlschlaegt, wirft er eine Exception oder gibt einen Fehlerwert zurueck.
new_website_raw = get_website_raw(website_url) # Nutzt globalen Helfer (Block 11)
website_raw = new_website_raw # Aktualisiere die lokale Variable (AR Wert)
self.logger.debug(f" -> Scrape Rohtext & Meta von {website_url[:100]}...")
new_website_raw_temp = "k.A." # Temporär für diesen Scope
try:
new_website_raw_temp = get_website_raw(website_url) # Nutzt globale Funktion
website_raw = new_website_raw_temp # Update der Hauptvariable
# Wenn get_website_raw einen URL_CHECK_MARKER zurückgibt
if website_raw == URL_CHECK_MARKER:
url_pruefstatus = URL_CHECK_MARKER
self.logger.warning(f" -> get_website_raw markierte URL {website_url} als '{URL_CHECK_MARKER}'. Überspringe weitere Web-Verarbeitung.")
website_summary = "k.A. (URL prüfen)" # Verhindere alte Zusammenfassung
website_meta_details = "k.A. (URL prüfen)"
elif website_raw and str(website_raw).strip().lower() not in ["k.a.", "k.a. (nur cookie-banner erkannt)", "k.a. (fehler)"]:
url_pruefstatus = "URL_OK_SCRAPED" # Wenn Scraping (auch nur teilweise) erfolgreich war
# Meta-Details scrapen (experimentell)
try:
meta_temp = scrape_website_details(website_url)
website_meta_details = meta_temp if meta_temp else "k.A. (Keine Meta-Details)"
except Exception as e_meta:
self.logger.error(f"FEHLER bei Meta-Detail Scraping für '{website_url[:100]}...': {e_meta}")
website_meta_details = f"k.A. (Fehler Meta: {str(e_meta)[:50]})"
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Meta-Details"] + 1)}{row_num_in_sheet}', 'values': [[website_meta_details]]}) # Spalte AI
# Zusammenfassung nur, wenn gueltiger Rohtext extrahiert wurde.
# Pruefen Sie auf nicht-leeren raw_text und ungleich Standard-Fehlerwerten.
if website_raw and str(website_raw).strip().lower() not in ["k.a.", "k.a. (nur cookie-banner erkannt)", "k.a. (fehler)"]:
self.logger.debug(f" -> Fasse Rohtext zusammen (Laenge: {len(str(website_raw))})...") # <<< GEÄNDERT
# Annahme: summarize_website_content global definiert (Block 9) und nutzt logging/retry
try:
# Der summarize_website_content Aufruf ist mit retry_on_failure dekoriert.
# Wenn er nach Retries fehlschlaegt, wirft er eine Exception oder gibt einen Fehlerwert zurueck.
new_website_summary = summarize_website_content(website_raw) # Nutzt globalen Helfer (Block 9)
# Aktualisiere die lokale Variable (AS Wert). Wenn das Ergebnis leer ist, setze "k.A.".
website_summary = new_website_summary if new_website_summary and new_website_summary.strip() else "k.A. (Keine Zusammenfassung erhalten)"
# Fuegen Sie das Update fuer Spalte AS (Website Zusammenfassung) zur Liste hinzu
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [[website_summary]]})
except Exception as e_summary:
# Wenn summarize_website_content eine Exception wirft (nach Retries)
self.logger.error(f"FEHLER bei Website Zusammenfassung fuer '{company_name[:100]}...': {e_summary}") # <<< GEÄNDERT
# Setze die lokale Variable auf einen Fehlerwert
website_summary = f"k.A. (Fehler Zusammenfassung: {str(e_summary)[:100]}...)" # Korrektur: e statt e_summary
# Fuegen Sie ein Update mit dem Fehlerwert fuer Spalte AS hinzu
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [[website_summary]]})
pass # Fahren Sie fort
else:
# Wenn kein gueltiger Rohtext zum Zusammenfassen vorhanden war
self.logger.debug(" -> Kein gueltiger Rohtext zum Zusammenfassen vorhanden. Zusammenfassung uebersprungen.") # <<< GEÄNDERT
# Stellen Sie sicher, dass die lokale Variable korrekt gesetzt ist, falls nicht zusammengefasst
website_summary = "k.A."
# Fuege 'k.A.' Update fuer AS hinzu (nur wenn es vorher nicht k.A. war?)
# Oder immer setzen, wenn der Schritt lief und keine Zusammenfassung erstellt wurde.
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [['k.A.']]})
# Zusammenfassung
self.logger.debug(f" -> Fasse Rohtext zusammen (Laenge: {len(str(website_raw))})...")
try:
summary_temp = summarize_website_content(website_raw)
website_summary = summary_temp if summary_temp and summary_temp.strip() else "k.A. (Keine Zusammenfassung erhalten)"
except Exception as e_summary_web:
self.logger.error(f"FEHLER bei Website Zusammenfassung für '{company_name[:100]}...': {e_summary_web}")
website_summary = f"k.A. (Fehler Zusammenfassung: {str(e_summary_web)[:50]})"
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [[website_summary]]}) # Spalte AH
else: # Fehler beim Scraping oder kein sinnvoller Text
if not website_raw.startswith("k.A. (Fehler"): # Wenn es kein expliziter Fehler war, sondern z.B. nur Cookie-Banner
url_pruefstatus = "URL_SCRAPE_EMPTY_OR_BANNER"
# website_raw enthält bereits den Fehler oder "k.A. (nur Cookie...)"
website_summary = "k.A." # Keine Zusammenfassung bei fehlerhaftem/leerem Rohtext
website_meta_details = "k.A."
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [[website_summary]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Meta-Details"] + 1)}{row_num_in_sheet}', 'values': [[website_meta_details]]})
# Fuegen Sie das Update fuer Spalte AR (Website Rohtext) zur Liste hinzu (auch wenn es ein Fehlerwert ist)
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Rohtext"] + 1)}{row_num_in_sheet}', 'values': [[website_raw]]})
except Exception as e_scrape:
# Wenn get_website_raw eine Exception wirft (nach Retries)
self.logger.error(f"FEHLER beim Website Scraping fuer '{company_name[:100]}' ({website_url[:100]}...): {e_scrape}") # <<< GEÄNDERT
# Setze die lokalen Variablen auf Fehlerwerte
website_raw = f"k.A. (Fehler Scraping: {str(e_scrape)[:100]}...)" # Korrektur: e statt e_scrape
website_summary = "k.A. (Fehler Zusammenfassung)" # Zusammenfassung fehlschlaegt auch
# Fuegen Sie Updates mit Fehlerwerten fuer AR und AS hinzu
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Rohtext"] + 1)}{row_num_in_sheet}', 'values': [[website_raw]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [[website_summary]]})
pass # Fahren Sie fort
else:
# Wenn keine gueltige Website URL vorhanden/gefunden wurde
self.logger.debug(f" -> Keine gueltige Website URL vorhanden/gefunden fuer '{company_name[:100]}...'. Website Verarbeitung uebersprungen.") # <<< GEÄNDERT
# Stellen Sie sicher, dass AR und AS auf k.A. gesetzt werden, wenn der Schritt lief, aber keine URL da war.
# Die lokalen Variablen behalten ihre initialen Werte (current_...) wenn der Schritt uebersprungen wurde,
# aber wenn der Schritt lief, aber keine URL da war, sollten sie auf k.A. gesetzt werden.
# Setzen Sie explizit auf k.A. oder Fehlerwerte, falls der Schritt lief aber fehlschlug wegen URL.
website_raw = "k.A."
website_summary = "k.A."
# Fuegen Sie Updates fuer AR und AS hinzu, falls noetig (Vermeidung von doppelten k.A. Updates)
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Rohtext"] + 1)}{row_num_in_sheet}', 'values': [['k.A.']]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [['k.A.']]})
# Setzen Sie den Website Scrape Timestamp (AT), da der Website-Schritt lief (auch wenn fehlerhaft)
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Scrape Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
# else if run_website_step:
# Der Website Schritt war angefordert, aber nicht noetig basierend auf Status/Re-Eval.
# Die lokalen Variablen website_raw und website_summary behalten ihre initialen Werte (current_...).
# self.logger.debug(f"Zeile {row_num_in_sheet}: Ueberspringe WEBSITE Schritte (AT vorhanden und kein Re-Eval).") # Zu viel Laerm im Debug
# --- Der Code fuer den naechsten Verarbeitungsschritt (Wikipedia) folgt im naechsten Block ---
# Definition der Methode _process_single_row wird in der naechsten Nachricht fortgesetzt.
except Exception as e_scrape_web:
self.logger.error(f"FEHLER beim Website Scraping für '{company_name[:100]}' ({website_url[:100]}...): {e_scrape_web}")
website_raw = f"k.A. (Fehler Scraping: {str(e_scrape_web)[:50]})"
website_summary = "k.A. (Scraping fehlgeschlagen)"
website_meta_details = "k.A. (Scraping fehlgeschlagen)"
url_pruefstatus = "URL_SCRAPE_ERROR"
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [[website_summary]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Meta-Details"] + 1)}{row_num_in_sheet}', 'values': [[website_meta_details]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Rohtext"] + 1)}{row_num_in_sheet}', 'values': [[website_raw]]}) # Spalte AG
else: # Keine gültige URL für Scraping
self.logger.debug(f" -> Keine gültige Website URL für '{company_name[:100]}...' vorhanden. Web-Verarbeitung übersprungen.")
website_raw = "k.A. (Keine URL)"
website_summary = "k.A."
website_meta_details = "k.A."
# Wenn url_pruefstatus noch leer ist (weil keine URL-Suche stattfand), setze es.
if not url_pruefstatus : url_pruefstatus = "URL_MISSING"
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Rohtext"] + 1)}{row_num_in_sheet}', 'values': [[website_raw]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Zusammenfassung"] + 1)}{row_num_in_sheet}', 'values': [[website_summary]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Meta-Details"] + 1)}{row_num_in_sheet}', 'values': [[website_meta_details]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["URL Prüfstatus"] + 1)}{row_num_in_sheet}', 'values': [[url_pruefstatus]]}) # Spalte AK
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Website Scrape Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]}) # Spalte AJ
# ======================================================================
# === 2. Wikipedia Handling (Search, Extraction, Status Reset) ==========
@@ -4742,92 +4674,309 @@ class DataProcessor:
# Timestamp für Wikipedia-Extraktion (Z) immer setzen, wenn der Wiki-Schritt lief
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wikipedia Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
# --- 2. Wikipedia Handling (Search, Extraction, Status Reset) ==========
run_wiki_step = 'wiki' in steps_to_run
wiki_processing_needed_based_on_status = self._needs_wiki_processing(row_data, force_reeval) # Prüft Z und AC
# Wenn eine SerpAPI-Suche für die Tochter durchgeführt wurde, setze den SerpAPI Wiki Search Timestamp (AB)
# Das Flag `search_for_daughter_wiki_needed` muss aus der Tochter-Logik kommen.
# if search_for_daughter_wiki_needed:
# updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["SerpAPI Wiki Search Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
if run_wiki_step and wiki_processing_needed_based_on_status:
any_processing_done = True
grund_message_parts_wiki = [] # Für Logging des Grundes
if force_reeval: grund_message_parts_wiki.append('Re-Eval')
if not self._get_cell_value_safe(row_data, "Wikipedia Timestamp").strip(): grund_message_parts_wiki.append('Z (Wikipedia Timestamp) leer')
if self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() == "X (URL COPIED)": grund_message_parts_wiki.append("AC (Chat Wiki Konsistenzpruefung)='X (URL COPIED)'")
grund_message_wiki = ", ".join(filter(None, grund_message_parts_wiki)) or "Unbekannter Grund (Wiki)"
self.logger.info(f"Zeile {row_num_in_sheet}: Fuehre WIKI Schritte aus (Grund: {grund_message_wiki})...")
# Status AC (Chat Wiki Konsistenzpruefung) und AA (Wiki Verif. Timestamp) zurücksetzen, wenn Neubewertung nötig
# Dies ist insbesondere dann der Fall, wenn eine neue URL (egal ob von Parent oder Tochter) in R geschrieben wurde,
# oder wenn Re-Eval forciert wurde.
# Die Logik hierfür muss die `source_of_wiki_data_origin` und `url_to_extract_from` vs. `current_wiki_url_in_sheet_for_daughter` vergleichen.
# Vereinfachte Reset-Logik: Wenn wiki_data_updated_in_this_run True ist UND der neue Status nicht "INFO_PARENT_WIKI" ist,
# dann setze AC auf "?" und leere AA.
ac_current_value = self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip()
# Wenn eine neue URL gefunden und extrahiert wurde (egal ob Parent oder Tochter-Suche)
# ODER wenn Re-Eval war und eine URL vorhanden ist, von der extrahiert wurde.
reset_ac_aa = False
if wiki_data_updated_in_this_run and \
final_wiki_data.get('url', 'k.A.').lower() not in ["k.a.", "kein artikel gefunden", "kein artikel gefunden (parent)"] and \
not final_wiki_data.get('url', '').startswith("FEHLER"):
# Hole aktuellen Wiki-URL der Tochter aus Spalte R
current_wiki_url_in_sheet_for_daughter = self._get_cell_value_safe(row_data, "Wiki URL").strip()
url_to_validate_and_extract = None # Die URL, die wir versuchen zu validieren und zu extrahieren
name_for_wiki_article_validation = company_name # Standard: Validiere gegen Tochter-Namen
source_of_wiki_data_origin = "Tochter"
additional_info_for_af_col = "" # Für Spalte AF "Begründung bei Abweichung"
# --- START: LOGIK FÜR PARENT-ACCOUNT (O) UND WIKI-URL DER TOCHTER (R) ---
if system_suggested_parent_name_o and system_suggested_parent_name_o.lower() != "k.a.":
# Fall 1: Parent (O) ist vorgeschlagen.
if not current_wiki_url_in_sheet_for_daughter or current_wiki_url_in_sheet_for_daughter.lower() == "k.a.":
# Fall 1.1: O gefüllt, R (Tochter-Wiki) leer/k.A. -> Suche Wiki für Parent O
self.logger.info(f" Zeile {row_num_in_sheet}: Parent (O) '{system_suggested_parent_name_o}' vorhanden, Wiki-URL der Tochter (R) leer. Versuche Wiki-Suche für Parent via SerpAPI...")
try:
parent_url_from_serp = serp_wikipedia_lookup(system_suggested_parent_name_o, website=None) # Kein spezifischer Website-Kontext für Parent-Suche
if parent_url_from_serp:
url_to_validate_and_extract = parent_url_from_serp
name_for_wiki_article_validation = system_suggested_parent_name_o # Validierung gegen Parent-Namen
source_of_wiki_data_origin = "Parent (O via SerpAPI)"
self.logger.info(f" -> Wiki-Artikel für Parent '{system_suggested_parent_name_o}' via SerpAPI gefunden: {url_to_validate_and_extract}")
else:
self.logger.warning(f" -> Kein Wiki-Artikel für Parent '{system_suggested_parent_name_o}' via SerpAPI gefunden. Fahre fort mit Standard-Tochter-Suche (falls nötig).")
# url_to_validate_and_extract bleibt None, Standard-Tochter-Suche wird unten getriggert, wenn nötig.
except Exception as e_parent_wiki_search:
self.logger.error(f" -> Fehler bei SerpAPI-Suche nach Wiki für Parent '{system_suggested_parent_name_o}': {e_parent_wiki_search}")
# Standard-Tochter-Suche wird unten getriggert, wenn nötig.
else:
# Fall 1.2: O gefüllt, R (Tochter-Wiki) auch gefüllt.
self.logger.info(f" Zeile {row_num_in_sheet}: Parent (O) '{system_suggested_parent_name_o}' vorhanden, aber auch Wiki-URL der Tochter (R) '{current_wiki_url_in_sheet_for_daughter}'. Tochter-Wiki (R) wird primär für Validierung/Extraktion verwendet, falls nötig.")
url_to_validate_and_extract = current_wiki_url_in_sheet_for_daughter # Nutze die vorhandene Tochter-URL
name_for_wiki_article_validation = company_name # Validierung gegen Tochter-Namen
source_of_wiki_data_origin = "Tochter (aus R, Parent O ignoriert)"
# --- ENDE: LOGIK FÜR PARENT-ACCOUNT (O) UND WIKI-URL DER TOCHTER (R) ---
# --- START: STANDARD-LOGIK FÜR TOCHTER-WIKI-SUCHE (falls noch keine URL von Parent O bestimmt wurde) ---
if url_to_validate_and_extract is None: # Nur wenn noch keine URL vom Parent oder aus R (Re-Eval) kam
search_for_daughter_wiki_needed = False
status_ac_is_reparse = self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() == "X (URL COPIED)"
timestamp_z_is_empty = not self._get_cell_value_safe(row_data, "Wikipedia Timestamp").strip()
r_url_is_valid_looking = current_wiki_url_in_sheet_for_daughter and \
"wikipedia.org/wiki/" in current_wiki_url_in_sheet_for_daughter.lower() and \
current_wiki_url_in_sheet_for_daughter.lower() not in ["k.a.", "kein artikel gefunden", "fehler bei suche", "http:"]
if status_ac_is_reparse:
self.logger.info(f" Zeile {row_num_in_sheet}: Status AC ist 'X (URL COPIED)'. Starte neue Wiki-Suche für Tochter '{company_name}'.")
search_for_daughter_wiki_needed = True
elif force_reeval: # Wenn Re-Eval und keine Parent-URL gefunden wurde
if r_url_is_valid_looking:
self.logger.info(f" Zeile {row_num_in_sheet}: Re-Eval Modus. Nutze vorhandene Tochter-URL (R): {current_wiki_url_in_sheet_for_daughter}")
url_to_validate_and_extract = current_wiki_url_in_sheet_for_daughter
name_for_wiki_article_validation = company_name
source_of_wiki_data_origin = "Tochter (aus R, Re-Eval)"
else:
self.logger.info(f" Zeile {row_num_in_sheet}: Re-Eval Modus. Tochter-URL (R) leer/ungültig. Starte neue Wiki-Suche für Tochter '{company_name}'.")
search_for_daughter_wiki_needed = True
elif timestamp_z_is_empty: # Wikipedia Timestamp (Z) fehlt
if r_url_is_valid_looking:
self.logger.info(f" Zeile {row_num_in_sheet}: Wikipedia Timestamp (Z) fehlt. Validiere vorhandene Tochter-URL (R): {current_wiki_url_in_sheet_for_daughter}")
url_to_validate_and_extract = current_wiki_url_in_sheet_for_daughter
name_for_wiki_article_validation = company_name
source_of_wiki_data_origin = "Tochter (aus R, Z leer)"
else:
self.logger.info(f" Zeile {row_num_in_sheet}: Wikipedia Timestamp (Z) fehlt und Tochter-URL (R) leer/ungültig. Starte neue Wiki-Suche für Tochter '{company_name}'.")
search_for_daughter_wiki_needed = True
elif not r_url_is_valid_looking: # Fallback, wenn Z nicht leer, aber R schlecht ist
self.logger.info(f" Zeile {row_num_in_sheet}: Tochter-URL (R) ist ungültig ('{current_wiki_url_in_sheet_for_daughter}'). Starte neue Wiki-Suche für Tochter '{company_name}'.")
search_for_daughter_wiki_needed = True
if source_of_wiki_data_origin == "Parent (O)":
# AC wurde bereits auf "INFO_PARENT_WIKI" gesetzt, AA geleert. Hier nichts weiter tun.
pass
elif current_wiki_url_in_sheet_for_daughter != final_wiki_data.get('url'): # URL der Tochter hat sich geändert
reset_ac_aa = True
elif force_reeval: # Re-Eval und eine URL wurde verarbeitet
reset_ac_aa = True
elif status_ac_indicates_reparse: # AC war "X (URL COPIED)"
reset_ac_aa = True
if search_for_daughter_wiki_needed:
self.logger.info(f" -> Suche nach Wikipedia-Artikel für Tochter '{company_name}' (Standard-Suche)...")
daughter_page_object = self.wiki_scraper.search_company_article(company_name=company_name, website=website_url)
if daughter_page_object:
url_to_validate_and_extract = daughter_page_object.url
name_for_wiki_article_validation = company_name
source_of_wiki_data_origin = "Tochter (Standard-Suche)"
self.logger.info(f" -> Suche für Tochter '{company_name}' erfolgreich, gefundene URL: {url_to_validate_and_extract}")
else:
self.logger.warning(f" -> Kein Wiki-Artikel für Tochter '{company_name}' via Standard-Suche gefunden.")
url_to_validate_and_extract = "Kein Artikel gefunden" # Signalwert
final_wiki_data['url'] = url_to_validate_and_extract # Dies wird direkt in R geschrieben
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
# Fallback, wenn immer noch keine URL zum Extrahieren, aber R valide war (z.B. bei _needs_wiki_processing=True wegen Z leer, aber R gut)
if url_to_validate_and_extract is None and r_url_is_valid_looking :
url_to_validate_and_extract = current_wiki_url_in_sheet_for_daughter
name_for_wiki_article_validation = company_name
source_of_wiki_data_origin = "Tochter (aus R, Fallback)"
self.logger.info(f" Zeile {row_num_in_sheet}: Fallback, nutze URL aus R: {url_to_validate_and_extract} für Validierung/Extraktion.")
# --- ENDE: STANDARD-LOGIK FÜR TOCHTER-WIKI-SUCHE ---
# --- START: VALIDIERUNG UND DATENEXTRAKTION DER ERMITTELTEN URL ---
artikel_ist_valide_fuer_extraktion = False # Zurücksetzen für diesen Abschnitt
if url_to_validate_and_extract and isinstance(url_to_validate_and_extract, str) and \
url_to_validate_and_extract.lower() not in ["k.a.", "kein artikel gefunden"] and \
not url_to_validate_and_extract.startswith("FEHLER"):
self.logger.info(f" Zeile {row_num_in_sheet}: Artikel '{url_to_validate_and_extract}' wird nun gegen '{name_for_wiki_article_validation}' validiert (Herkunft: {source_of_wiki_data_origin})...")
page_obj_for_validation = None # Explizit initialisieren
try:
if url_to_validate_and_extract.startswith("http"):
page_obj_for_validation = wikipedia.page(url=url_to_validate_and_extract, auto_suggest=False, preload=False)
else: # Annahme: Es ist ein Titel
page_obj_for_validation = wikipedia.page(title=url_to_validate_and_extract, auto_suggest=False, preload=False)
# Die Validierung verwendet die angepasste _validate_article Methode des WikiScrapers
if self.wiki_scraper._validate_article(page_obj_for_validation, name_for_wiki_article_validation, website_url): # website_url ist immer die der Tochter
artikel_ist_valide_fuer_extraktion = True
url_to_validate_and_extract = page_obj_for_validation.url # Kanonische URL übernehmen
self.logger.info(f" -> Artikel '{page_obj_for_validation.title}' (URL: {url_to_validate_and_extract}) erfolgreich gegen '{name_for_wiki_article_validation}' validiert.")
else: # Artikel nicht valide
self.logger.warning(f" -> Artikel '{page_obj_for_validation.title}' NICHT gegen '{name_for_wiki_article_validation}' validiert.")
additional_info_for_af_col += f"WARNUNG: Artikel '{page_obj_for_validation.url}' nicht valide für '{name_for_wiki_article_validation}'. "
final_wiki_data['url'] = page_obj_for_validation.url # Trotzdem URL speichern
for key_to_clear in ['sitz_stadt', 'sitz_land', 'first_paragraph', 'branche', 'umsatz', 'mitarbeiter', 'categories']:
final_wiki_data[key_to_clear] = 'k.A. (Artikel nicht valide)' if key_to_clear == 'first_paragraph' else 'k.A.'
wiki_data_updated_in_this_run = True # Es gab einen Versuch, die URL zu ändern/setzen
if reset_ac_aa and ac_current_value != "INFO_PARENT_WIKI": # Nicht zurücksetzen, wenn es gerade erst auf Parent-Info gesetzt wurde
self.logger.info(f" Zeile {row_num_in_sheet}: Setze AC ('Chat Wiki Konsistenzpruefung') auf '?' und leere AA ('Wiki Verif. Timestamp') aufgrund neuer/re-evaluierter Wiki-Daten.")
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Wiki Konsistenzpruefung"] + 1)}{row_num_in_sheet}', 'values': [['?']]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Verif. Timestamp"] + 1)}{row_num_in_sheet}', 'values': [['']]})
# Auch die abhängigen Spalten AD, AE leeren
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Begründung Wiki Inkonsistenz"] + 1)}{row_num_in_sheet}', 'values': [['']]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Vorschlag Wiki Artikel"] + 1)}{row_num_in_sheet}', 'values': [['']]})
except wikipedia.exceptions.PageError:
self.logger.warning(f" -> Wiki-Seite für '{url_to_validate_and_extract}' nicht gefunden (PageError) bei Validierungsversuch.")
additional_info_for_af_col += f"FEHLER: Seite '{url_to_validate_and_extract}' nicht gefunden. "
final_wiki_data['url'] = f"Fehler: Seite '{url_to_validate_and_extract}' nicht gefunden"
wiki_data_updated_in_this_run = True
except wikipedia.exceptions.DisambiguationError as e_dis_val_wiki:
self.logger.warning(f" -> Wiki-Seite für '{url_to_validate_and_extract}' ist eine Begriffsklärung: {e_dis_val_wiki.title}. Optionen: {str(e_dis_val_wiki.options)[:100]}...")
additional_info_for_af_col += f"FEHLER: Seite '{url_to_validate_and_extract}' ist Begriffsklärung. "
final_wiki_data['url'] = f"Fehler: Begriffsklärung ({url_to_validate_and_extract})"
wiki_data_updated_in_this_run = True
except Exception as e_val_load_page_wiki:
self.logger.error(f" -> Unerwarteter Fehler beim Laden/Validieren der Wiki-Seite '{url_to_validate_and_extract}': {e_val_load_page_wiki}")
additional_info_for_af_col += f"FEHLER: Laden/Validieren von '{url_to_validate_and_extract}' fehlgeschlagen. "
final_wiki_data['url'] = f"Fehler Validierung: {str(e_val_load_page_wiki)[:50]}"
wiki_data_updated_in_this_run = True
if artikel_ist_valide_fuer_extraktion:
self.logger.debug(f" -> Extrahiere Wiki-Daten von validierter URL ({source_of_wiki_data_origin}): {url_to_validate_and_extract[:100]}...")
try:
extracted_data = self.wiki_scraper.extract_company_data(url_to_validate_and_extract)
if extracted_data and isinstance(extracted_data, dict) and extracted_data.get('url') != 'k.A.':
final_wiki_data = extracted_data # Überschreibe die Arbeitskopie
wiki_data_updated_in_this_run = True
self.logger.info(f" -> Datenextraktion von {url_to_validate_and_extract[:100]}... ({source_of_wiki_data_origin}) erfolgreich.")
if source_of_wiki_data_origin.startswith("Parent"):
ac_wert_fuer_parent_wiki = "INFO_PARENT_WIKI" # Spezieller Status
self.logger.info(f" -> Setze AC auf '{ac_wert_fuer_parent_wiki}', da Wiki-Daten von Parent.")
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Wiki Konsistenzpruefung"] + 1)}{row_num_in_sheet}', 'values': [[ac_wert_fuer_parent_wiki]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Begründung Wiki Inkonsistenz"] + 1)}{row_num_in_sheet}', 'values': [[f"Daten von Parent: {name_for_wiki_article_validation}"]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Vorschlag Wiki Artikel"] + 1)}{row_num_in_sheet}', 'values': [['']]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Verif. Timestamp"] + 1)}{row_num_in_sheet}', 'values': [['']]})
additional_info_for_af_col = f"INFO: Wiki-Daten von Parent '{name_for_wiki_article_validation}' übernommen. " # Überschreibe, da erfolgreich
else: # Fehler bei Extraktion oder leeres Ergebnis
self.logger.error(f" -> Fehler bei Datenextraktion von {url_to_validate_and_extract[:100]}... (Extraktion leer/ungültig).")
final_wiki_data['url'] = url_to_validate_and_extract
for key_to_clear in ['sitz_stadt', 'sitz_land', 'first_paragraph', 'branche', 'umsatz', 'mitarbeiter', 'categories']:
final_wiki_data[key_to_clear] = 'k.A. (Extraktion fehlgeschlagen)' if key_to_clear == 'first_paragraph' else 'k.A.'
wiki_data_updated_in_this_run = True
except Exception as e_wiki_extract_final_val:
self.logger.error(f"FEHLER bei Wikipedia Datenextraktion von {url_to_validate_and_extract[:100]}...: {e_wiki_extract_final_val}")
final_wiki_data['url'] = url_to_validate_and_extract
for key_to_clear in ['sitz_stadt', 'sitz_land', 'first_paragraph', 'branche', 'umsatz', 'mitarbeiter', 'categories']:
final_wiki_data[key_to_clear] = f'k.A. (FEHLER Extraktion)' if key_to_clear == 'first_paragraph' else 'k.A.'
wiki_data_updated_in_this_run = True
# --- Sheet Updates für Wiki-Daten (R-Y) und Timestamps (Z, AA, AB) ---
if wiki_data_updated_in_this_run: # Nur updaten, wenn sich was geändert hat oder Fehler auftrat
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 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 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.')]]})
# Timestamp für Wikipedia-Extraktion (Z) immer setzen, wenn der Wiki-Schritt lief
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wikipedia Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
# Spalte AF Begründung
current_af_val = self._get_cell_value_safe(row_data, "Begründung bei Abweichung").strip()
if additional_info_for_af_col:
new_af_val = (current_af_val + "; " + additional_info_for_af_col if current_af_val else additional_info_for_af_col).strip()
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Begründung bei Abweichung"] + 1)}{row_num_in_sheet}', 'values': [[new_af_val]]})
# Reset AC, AA, AD, AE falls nötig (Logik von oben wiederholt, angepasst)
ac_current_value = self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip()
reset_ac_aa_final = False
if final_wiki_data.get('url', 'k.A.').lower() not in ["k.a.", "kein artikel gefunden"] and not final_wiki_data.get('url', '').startswith("FEHLER"):
if source_of_wiki_data_origin.startswith("Parent") and ac_current_value != "INFO_PARENT_WIKI":
# Für Parent-Daten wurde AC schon oben auf INFO_PARENT_WIKI gesetzt, hier nicht nochmal ändern
pass
elif current_wiki_url_in_sheet_for_daughter != final_wiki_data.get('url'):
reset_ac_aa_final = True
elif force_reeval:
reset_ac_aa_final = True
elif self._get_cell_value_safe(row_data, "Chat Wiki Konsistenzpruefung").strip().upper() == "X (URL COPIED)": # status_ac_is_reparse
reset_ac_aa_final = True
if reset_ac_aa_final and ac_current_value != "INFO_PARENT_WIKI":
self.logger.info(f" Zeile {row_num_in_sheet}: Setze AC auf '?' und leere AA, AD, AE aufgrund neuer/re-evaluierter Wiki-Daten für '{name_for_wiki_article_validation}'.")
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Wiki Konsistenzpruefung"] + 1)}{row_num_in_sheet}', 'values': [['?']]}) # AC
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Wiki Verif. Timestamp"] + 1)}{row_num_in_sheet}', 'values': [['']]}) # AA
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Begründung Wiki Inkonsistenz"] + 1)}{row_num_in_sheet}', 'values': [['']]}) # AD
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Vorschlag Wiki Artikel"] + 1)}{row_num_in_sheet}', 'values': [['']]}) # AE
# --- 2b. System Vorschlag Parent Account (Spalte O, P, Q) ---
# Dieser Schritt wird ausgeführt, wenn 'wiki' in steps_to_run enthalten ist (oder ein spezifischer 'parent_suggest'-Schritt)
run_parent_suggest_step = 'wiki' in steps_to_run # Oder ein dedizierter Schritt-Key
# 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).")
system_vorschlag_parent_o_value = self._get_cell_value_safe(row_data, "System Vorschlag Parent Account").strip()
parent_vorschlag_status_p_value = self._get_cell_value_safe(row_data, "Parent Vorschlag Status").strip()
# --- Ende Wikipedia Handling ---
needs_parent_suggestion = (not system_vorschlag_parent_o_value or system_vorschlag_parent_o_value.lower() == 'k.a.') and parent_vorschlag_status_p_value != '-'
if force_reeval and parent_vorschlag_status_p_value != '-':
needs_parent_suggestion = True # Auch wenn O schon gefüllt war, aber nicht abgelehnt
if run_parent_suggest_step and needs_parent_suggestion:
any_processing_done = True # Zählt als Verarbeitung
self.logger.info(f" Zeile {row_num_in_sheet}: Versuche Parent-Account vorzuschlagen...")
try:
# _suggest_parent_account braucht jetzt auch CRM-Daten und die aktuellen Wiki-Daten als Input
suggested_parent_info = self._suggest_parent_account(
company_name=company_name,
website_url=website_url,
crm_beschreibung=crm_beschreibung,
# final_wiki_data enthält hier die möglicherweise vom Parent (O) stammenden Wiki-Daten,
# was für die Heuristik relevant sein kann (z.B. um nicht sich selbst vorzuschlagen).
# Es ist wichtig, dass _suggest_parent_account dies berücksichtigt.
wiki_data_dict=final_wiki_data
)
if suggested_parent_info and suggested_parent_info.get("parent_name"):
# Nur schreiben, wenn der Vorschlag sich vom aktuellen Wert in D unterscheidet (falls D gesetzt)
# oder D leer ist. Und wenn der Vorschlag nicht der Firmenname selbst ist.
if suggested_parent_info["parent_name"].strip().lower() != company_name.strip().lower() and \
(not parent_account_name_d or suggested_parent_info["parent_name"].strip().lower() != parent_account_name_d.strip().lower()):
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["System Vorschlag Parent Account"] + 1)}{row_num_in_sheet}', 'values': [[suggested_parent_info["parent_name"]]]}) # Spalte O
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Parent Vorschlag Status"] + 1)}{row_num_in_sheet}', 'values': [['?']]}) # Spalte P, initial '?'
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Parent Vorschlag Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]}) # Spalte Q
self.logger.info(f" -> Parent-Vorschlag für O: '{suggested_parent_info['parent_name']}' (Grund: {suggested_parent_info.get('reason', 'N/A')})")
else:
self.logger.info(f" -> Parent-Vorschlag '{suggested_parent_info['parent_name']}' ist identisch mit Firma oder D. Kein Update für O.")
# Optional: Timestamp trotzdem setzen, dass versucht wurde?
if system_vorschlag_parent_o_value.lower() == 'k.a.' or not system_vorschlag_parent_o_value: # Wenn O vorher leer war
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Parent Vorschlag Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
# else if run_wiki_step:
# Der Wiki Schritt war angefordert, aber nicht noetig basierend auf Status/Re-Eval.
# Die lokalen Variablen final_wiki_data behaelt die initialen Werte (current_...).
# self.logger.debug(f"Zeile {row_num_in_sheet}: Ueberspringe WIKI Suche/Extraktion (AN vorhanden, S nicht 'X (URL Copied)' und kein Re-Eval).") # Zu viel Laerm im Debug
elif suggested_parent_info and suggested_parent_info.get("status") == "NO_SUGGESTION":
self.logger.info(f" -> Kein Parent-Vorschlag gemacht (Grund: {suggested_parent_info.get('reason', 'N/A')}). P/Q nicht geändert, falls O leer war.")
# Wenn O vorher leer war, Timestamp setzen, dass es versucht wurde.
if system_vorschlag_parent_o_value.lower() == 'k.a.' or not system_vorschlag_parent_o_value:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Parent Vorschlag Timestamp"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
# --- Der Code fuer den naechsten Verarbeitungsschritt (ChatGPT Evaluationen) folgt im naechsten Block ---
# Definition der Methode _process_single_row wird in der naechsten Nachricht fortgesetzt.
else:
self.logger.info(f" -> Kein Parent-Vorschlag von _suggest_parent_account erhalten oder ungültiges Format.")
except Exception as e_suggest_parent:
self.logger.error(f" -> Fehler bei _suggest_parent_account für Zeile {row_num_in_sheet}: {e_suggest_parent}")
# --- ENDE: 2b. Parent-Account-Vorschlag ---
# --- Ende des Wikipedia-Blocks ---
# Der nächste Block wäre ChatGPT-Evaluationen
# ======================================================================
# --- 3. ChatGPT Evaluationen (Branch, FSM, Emp, Umsatz Schaetzungen etc.) ---
# Dieser Schritt wird ausgefuehrt, wenn 'chat' in steps_to_run enthalten ist UND
# (_needs_chat_evaluations True ist ODER force_reeval True ist).
# _needs_chat_evaluations prueft BC (alt AO) oder ob Wiki-Daten in diesem Lauf gerade aktualisiert wurden.
# Nutzt interne Helfer: _get_cell_value_safe, _needs_chat_evaluations.
# Nutzt globale Helfer: COLUMN_MAP, logger, datetime, time,
# evaluate_branche_chatgpt (Block 10),
# get_numeric_filter_value (Block 5).
# Nutzt lokale Variablen: crm_branche, crm_beschreibung, final_wiki_data, website_summary, wiki_data_updated_in_this_run.
run_chat_step = 'chat' in steps_to_run
# wiki_data_updated_in_this_run wurde im Wikipedia-Block oben gesetzt
chat_processing_needed_based_on_status = self._needs_chat_evaluations(row_data, force_reeval, wiki_data_updated_in_this_run)
if run_chat_step and chat_processing_needed_based_on_status:
any_processing_done = True
chat_eval_just_ran = True
chat_eval_just_ran = True # Wichtig für ML-Schritt später
grund_message_parts = []
if force_reeval: grund_message_parts.append('Re-Eval')
if not self._get_cell_value_safe(row_data, "Timestamp letzte Pruefung").strip(): grund_message_parts.append('BC (Timestamp letzte Pruefung) leer') # Neuer Schlüssel BC
if wiki_data_updated_in_this_run: grund_message_parts.append('Wiki Daten gerade aktualisiert')
grund_message = ", ".join(filter(None, grund_message_parts)) or "Unbekannter Grund"
# ... (Grund Message für Log erstellen) ...
grund_message_parts_chat = []
if force_reeval: grund_message_parts_chat.append('Re-Eval')
if not self._get_cell_value_safe(row_data, "Timestamp letzte Prüfung").strip(): grund_message_parts_chat.append('BN (Timestamp letzte Prüfung) leer')
if wiki_data_updated_in_this_run: grund_message_parts_chat.append('Wiki Daten gerade aktualisiert')
grund_message_chat = ", ".join(filter(None, grund_message_parts_chat)) or "Unbekannter Grund (Chat)"
self.logger.info(f"Zeile {row_num_in_sheet}: Fuehre CHATGPT Evaluationen & Plausi aus (Grund: {grund_message_chat})...")
self.logger.info(f"Zeile {row_num_in_sheet}: Fuehre CHATGPT Evaluationen aus (Grund: {grund_message})...")
# --- 3a. Branchen-Einstufung (AH, AI, AJ, AK) ---
self.logger.info(f"Zeile {row_num_in_sheet}: Starte Branchen-Einstufung ueber ChatGPT...") # Log-Level auf INFO geändert
# --- 3a. Branchen-Einstufung (AL, AM, AN, AO) ---
# ... (Dieser Teil bleibt wie er war, nutzt final_wiki_data, website_summary) ...
# ... (Logik für evaluate_branche_chatgpt und Updates für AL, AM, AN, AO) ...
self.logger.info(f"Zeile {row_num_in_sheet}: Starte Branchen-Einstufung ueber ChatGPT...")
try:
# final_wiki_data enthält hier ggf. die Daten des Parents
branch_result = evaluate_branche_chatgpt(
crm_branche,
crm_beschreibung,
@@ -4835,164 +4984,94 @@ class DataProcessor:
final_wiki_data.get('categories', 'k.A.'),
website_summary
)
# ... (Updates für Spalten AL, AM, AN, AO)
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Vorschlag Branche"] + 1)}{row_num_in_sheet}', 'values': [[branch_result.get("branch", "FEHLER BRANCH")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Branche Konfidenz"] + 1)}{row_num_in_sheet}', 'values': [[branch_result.get("confidence", "N/A CONF")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Konsistenz Branche"] + 1)}{row_num_in_sheet}', 'values': [[branch_result.get("consistency", "error CONS")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Begruendung Abweichung Branche"] + 1)}{row_num_in_sheet}', 'values': [[branch_result.get("justification", "No JUST")]]})
except Exception as e_branch_eval_row:
self.logger.error(f"FEHLER bei Branchen-Einstufung für Zeile {row_num_in_sheet}: {e_branch_eval_row}")
# ... (Fehlerwerte schreiben) ...
# --- 3b, 3c, 3d: Weitere ChatGPT Evaluationen (FSM, Mitarbeiter, Umsatz) ---
# ... (Diese bleiben strukturell gleich, nutzen aber das ggf. aktualisierte final_wiki_data) ...
# Zum Beispiel für Mitarbeiterschätzung:
# if not final_wiki_data.get('mitarbeiter') or final_wiki_data.get('mitarbeiter', 'k.A.').lower() == 'k.a.':
# # ... call_openai_chat für Mitarbeiterschätzung ...
# ... etc. für andere ChatGPT-Schritte ...
# HIER BEGINNT DIE WICHTIGE ÄNDERUNG FÜR KONSOLIDIERUNG UND PLAUSI
# --- 3e. Konsolidierung Umsatz/Mitarbeiter (BD, BE) ---
self.logger.debug(f" Zeile {row_num_in_sheet}: Konsolidiere Umsatz (BD) und Mitarbeiter (BE) unter Berücksichtigung von Parent (D)...")
final_umsatz_str_konsolidiert = "k.A."
final_ma_str_konsolidiert = "k.A."
try:
crm_umsatz_val_str = self._get_cell_value_safe(row_data, "CRM Umsatz") # Spalte L
wiki_umsatz_val_str = final_wiki_data.get('umsatz', 'k.A.') # Aus final_wiki_data (kann Parent sein), Spalte W
crm_ma_val_str = self._get_cell_value_safe(row_data, "CRM Anzahl Mitarbeiter") # Spalte M
wiki_ma_val_str = final_wiki_data.get('mitarbeiter', 'k.A.') # Aus final_wiki_data, Spalte X
self.logger.debug(f"Zeile {row_num_in_sheet}: branch_result von evaluate_branche_chatgpt: {branch_result}") # DEBUG 1
# Numerische Werte für die Logik holen
num_crm_umsatz = get_numeric_filter_value(crm_umsatz_val_str, is_umsatz=True)
num_wiki_umsatz = get_numeric_filter_value(wiki_umsatz_val_str, is_umsatz=True)
num_crm_ma = get_numeric_filter_value(crm_ma_val_str, is_umsatz=False)
num_wiki_ma = get_numeric_filter_value(wiki_ma_val_str, is_umsatz=False)
# Update für "Chat Vorschlag Branche"
vorschlag_key = "Chat Vorschlag Branche"
vorschlag_col_idx = COLUMN_MAP.get(vorschlag_key)
if vorschlag_col_idx is not None:
range_str_vorschlag = f'{self.sheet_handler._get_col_letter(vorschlag_col_idx + 1)}{row_num_in_sheet}'
wert_vorschlag = branch_result.get("branch", "FEHLER BRANCH")
updates.append({'range': range_str_vorschlag, 'values': [[wert_vorschlag]]})
self.logger.debug(f"Zeile {row_num_in_sheet}: Update vorbereitet für {vorschlag_key} ({range_str_vorschlag}): '{wert_vorschlag}'")
self.logger.debug(f" Konsolidierung Input: CRM_U(L)='{num_crm_umsatz}', Wiki_U(W)='{num_wiki_umsatz}' (aus {source_of_wiki_data_origin}), CRM_M(M)='{num_crm_ma}', Wiki_M(X)='{num_wiki_ma}' (aus {source_of_wiki_data_origin}), Parent_D='{parent_account_name_d}'")
if parent_account_name_d and parent_account_name_d.lower() != 'k.a.':
# Parent-Account (D) ist GEFÜLLT: Primär CRM-Daten der Tochter verwenden.
self.logger.info(f" -> Parent D ('{parent_account_name_d}') ist gesetzt. Konsolidiere primär mit CRM-Daten der Tochter für BD/BE.")
final_num_umsatz = num_crm_umsatz if num_crm_umsatz > 0 else num_wiki_umsatz # Wiki als Fallback, falls CRM der Tochter fehlt/0 ist
final_num_ma = num_crm_ma if num_crm_ma > 0 else num_wiki_ma # Wiki als Fallback
else:
self.logger.error(f"Zeile {row_num_in_sheet}: Schlüssel '{vorschlag_key}' nicht in COLUMN_MAP gefunden!")
# NEU: Konfidenz-Score speichern
konfidenz_key = "Chat Branche Konfidenz"
konfidenz_col_idx = COLUMN_MAP.get(konfidenz_key)
if konfidenz_col_idx is not None:
range_str_konfidenz = f'{self.sheet_handler._get_col_letter(konfidenz_col_idx + 1)}{row_num_in_sheet}'
wert_konfidenz = branch_result.get("confidence", "N/A CONF")
updates.append({'range': range_str_konfidenz, 'values': [[wert_konfidenz]]})
self.logger.debug(f"Zeile {row_num_in_sheet}: Update vorbereitet für {konfidenz_key} ({range_str_konfidenz}): '{wert_konfidenz}'")
else:
self.logger.error(f"Zeile {row_num_in_sheet}: Schlüssel '{konfidenz_key}' nicht in COLUMN_MAP gefunden!")
# Update für "Chat Konsistenz Branche"
konsistenz_key = "Chat Konsistenz Branche"
konsistenz_col_idx = COLUMN_MAP.get(konsistenz_key)
if konsistenz_col_idx is not None:
range_str_konsistenz = f'{self.sheet_handler._get_col_letter(konsistenz_col_idx + 1)}{row_num_in_sheet}'
wert_konsistenz = branch_result.get("consistency", "error CONS")
updates.append({'range': range_str_konsistenz, 'values': [[wert_konsistenz]]})
self.logger.debug(f"Zeile {row_num_in_sheet}: Update vorbereitet für {konsistenz_key} ({range_str_konsistenz}): '{wert_konsistenz}'")
else:
self.logger.error(f"Zeile {row_num_in_sheet}: Schlüssel '{konsistenz_key}' nicht in COLUMN_MAP gefunden!")
# Update für "Chat Begruendung Abweichung Branche"
begruendung_key = "Chat Begruendung Abweichung Branche"
begruendung_col_idx = COLUMN_MAP.get(begruendung_key)
if begruendung_col_idx is not None:
range_str_begruendung = f'{self.sheet_handler._get_col_letter(begruendung_col_idx + 1)}{row_num_in_sheet}'
wert_begruendung = branch_result.get("justification", "Keine Begruendung JUST")
updates.append({'range': range_str_begruendung, 'values': [[wert_begruendung]]})
self.logger.debug(f"Zeile {row_num_in_sheet}: Update vorbereitet für {begruendung_key} ({range_str_begruendung}): '{str(wert_begruendung)[:50]}...'")
else:
self.logger.error(f"Zeile {row_num_in_sheet}: Schlüssel '{begruendung_key}' nicht in COLUMN_MAP gefunden!")
except Exception as e_branch_eval:
self.logger.error(f"FEHLER bei Branchen-Einstufung ueber ChatGPT fuer Zeile {row_num_in_sheet}: {e_branch_eval}")
self.logger.debug(traceback.format_exc())
error_msg = f"Fehler Eval: {str(e_branch_eval)[:100]}..."
# Fehlerwerte für alle relevanten Spalten schreiben
if COLUMN_MAP.get("Chat Vorschlag Branche") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Vorschlag Branche"] + 1)}{row_num_in_sheet}', 'values': [['FEHLER']]})
if COLUMN_MAP.get("Chat Branche Konfidenz") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Branche Konfidenz"] + 1)}{row_num_in_sheet}', 'values': [['N/A (Eval Fehler)']]})
if COLUMN_MAP.get("Chat Konsistenz Branche") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Konsistenz Branche"] + 1)}{row_num_in_sheet}', 'values': [['error']]})
if COLUMN_MAP.get("Chat Begruendung Abweichung Branche") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Chat Begruendung Abweichung Branche"] + 1)}{row_num_in_sheet}', 'values': [[error_msg]]})
pass
# --- 3e. Konsolidierung Umsatz/Mitarbeiter (AY, AZ) ---
self.logger.debug(" -> Konsolidiere Umsatz (AY) und Mitarbeiter (AZ) (Wiki > CRM Logik)...")
final_umsatz_str_konsolidiert = "k.A." # Default
final_ma_str_konsolidiert = "k.A." # Default
try:
crm_umsatz_val = self._get_cell_value_safe(row_data, "CRM Umsatz")
wiki_umsatz_val = final_wiki_data.get('umsatz', 'k.A.')
crm_ma_val = self._get_cell_value_safe(row_data, "CRM Anzahl Mitarbeiter")
wiki_ma_val = final_wiki_data.get('mitarbeiter', 'k.A.')
# get_numeric_filter_value gibt 0.0 oder 0 zurück, wenn der Input nicht positiv numerisch ist oder leer/k.A.
num_crm_umsatz = get_numeric_filter_value(crm_umsatz_val, is_umsatz=True)
num_wiki_umsatz = get_numeric_filter_value(wiki_umsatz_val, is_umsatz=True)
num_crm_ma = get_numeric_filter_value(crm_ma_val, is_umsatz=False)
num_wiki_ma = get_numeric_filter_value(wiki_ma_val, is_umsatz=False)
# Logge die numerischen Werte VOR der Konsolidierung
self.logger.debug(f" Konsolidierung Input: num_crm_umsatz={num_crm_umsatz}, num_wiki_umsatz={num_wiki_umsatz}, num_crm_ma={num_crm_ma}, num_wiki_ma={num_wiki_ma}")
# Konsolidierung: Wiki > CRM. Wenn Wiki keinen positiven Wert hat, nimm CRM.
# Wenn beide keinen positiven Wert haben, bleibt es bei 0.
final_num_umsatz = num_wiki_umsatz if num_wiki_umsatz > 0 else num_crm_umsatz
final_num_ma = num_wiki_ma if num_wiki_ma > 0 else num_crm_ma
# Parent-Account (D) ist LEER: Standardlogik Wiki > CRM.
self.logger.debug(f" -> Parent D leer. Standardkonsolidierung Wiki > CRM für BD/BE.")
final_num_umsatz = num_wiki_umsatz if num_wiki_umsatz > 0 else num_crm_umsatz
final_num_ma = num_wiki_ma if num_wiki_ma > 0 else num_crm_ma
final_umsatz_str_konsolidiert = str(int(round(final_num_umsatz))) if final_num_umsatz > 0 else 'k.A.'
final_ma_str_konsolidiert = str(int(round(final_num_ma))) if final_num_ma > 0 else 'k.A.'
# String-Konvertierung: "0" bedeutet "unbekannt" -> "k.A."
# Nur echt positive Werte werden als Zahl geschrieben.
final_umsatz_str_konsolidiert = str(int(round(final_num_umsatz))) if final_num_umsatz > 0 else 'k.A.'
final_ma_str_konsolidiert = str(int(round(final_num_ma))) if final_num_ma > 0 else 'k.A.'
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Umsatz (Wiki>CRM)"] + 1)}{row_num_in_sheet}', 'values': [[final_umsatz_str_konsolidiert]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Mitarbeiter (Wiki>CRM)"] + 1)}{row_num_in_sheet}', 'values': [[final_ma_str_konsolidiert]]})
# Das Log hier sollte jetzt die String-Werte ausgeben, die auch geschrieben werden:
self.logger.debug(f" -> Konsolidiert (Sheet-Werte): Umsatz='{final_umsatz_str_konsolidiert}', MA='{final_ma_str_konsolidiert}'")
except Exception as e_consolidate:
self.logger.error(f"FEHLER bei Konsolidierung Umsatz/Mitarbeiter fuer Zeile {row_num_in_sheet}: {e_consolidate}")
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Umsatz (Wiki>CRM)"] + 1)}{row_num_in_sheet}', 'values': [['FEHLER KONSO']]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Mitarbeiter (Wiki>CRM)"] + 1)}{row_num_in_sheet}', 'values': [['FEHLER KONSO']]})
final_umsatz_str_konsolidiert = "FEHLER KONSO"
final_ma_str_konsolidiert = "FEHLER KONSO"
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Umsatz (Wiki>CRM)"] + 1)}{row_num_in_sheet}', 'values': [[final_umsatz_str_konsolidiert]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Mitarbeiter (Wiki>CRM)"] + 1)}{row_num_in_sheet}', 'values': [[final_ma_str_konsolidiert]]})
self.logger.debug(f" -> Konsolidiert für BD/BE (Sheet-Werte): Umsatz='{final_umsatz_str_konsolidiert}', MA='{final_ma_str_konsolidiert}'")
except Exception as e_consolidate_row_final:
self.logger.error(f"FEHLER bei Konsolidierung Umsatz/Mitarbeiter (BD/BE) für Zeile {row_num_in_sheet}: {e_consolidate_row_final}")
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Umsatz (Wiki>CRM)"] + 1)}{row_num_in_sheet}', 'values': [['FEHLER_KONSO']]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Mitarbeiter (Wiki>CRM)"] + 1)}{row_num_in_sheet}', 'values': [['FEHLER_KONSO']]})
final_umsatz_str_konsolidiert = "FEHLER_KONSO" # Für Plausi-Input
final_ma_str_konsolidiert = "FEHLER_KONSO"
# --- NEU: 3f. Plausibilitäts-Checks durchführen (BG-BM) ---
self.logger.debug(f" Zeile {row_num_in_sheet}: Führe Plausibilitäts-Checks durch...")
plausi_check_erfolgreich_durchgefuehrt = False # Flag
# --- 3f. Plausibilitäts-Checks durchführen (BG-BM) ---
# Der Aufruf von _check_financial_plausibility muss jetzt parent_account_name_d (Spalte D) übergeben bekommen.
self.logger.debug(f" Zeile {row_num_in_sheet}: Führe Plausibilitäts-Checks durch (Parent D: '{parent_account_name_d}')...")
try:
# ... (Erstellung von plausi_input_data wie zuvor) ...
plausi_input_data = {
"Finaler Umsatz (Wiki>CRM)": final_umsatz_str_konsolidiert,
"Finaler Mitarbeiter (Wiki>CRM)": final_ma_str_konsolidiert,
"CRM Umsatz": self._get_cell_value_safe(row_data, "CRM Umsatz"),
"Wiki Umsatz": self._get_cell_value_safe(row_data, "Wiki Umsatz"),
"CRM Anzahl Mitarbeiter": self._get_cell_value_safe(row_data, "CRM Anzahl Mitarbeiter"),
"Wiki Mitarbeiter": self._get_cell_value_safe(row_data, "Wiki Mitarbeiter")
"Finaler Umsatz (Wiki>CRM)": final_umsatz_str_konsolidiert, # Aus Schritt 3e
"Finaler Mitarbeiter (Wiki>CRM)": final_ma_str_konsolidiert, # Aus Schritt 3e
"CRM Umsatz": self._get_cell_value_safe(row_data, "CRM Umsatz"), # Spalte L
"Wiki Umsatz": final_wiki_data.get('umsatz', 'k.A.'), # Spalte W (kann von Parent sein)
"CRM Anzahl Mitarbeiter": self._get_cell_value_safe(row_data, "CRM Anzahl Mitarbeiter"), # Spalte M
"Wiki Mitarbeiter": final_wiki_data.get('mitarbeiter', 'k.A.'), # Spalte X (kann von Parent sein)
"Parent Account Name": parent_account_name_d # WICHTIG: Spalte D übergeben
}
plausi_results = self._check_financial_plausibility(plausi_input_data)
plausi_results = self._check_financial_plausibility(plausi_input_data) # Diese Methode muss D berücksichtigen
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_umsatz_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Mitarbeiter"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ma_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz/MA Ratio"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ratio_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung Umsatz CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_umsatz_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung MA CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_ma_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung Umsatz CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_umsatz_flag", "ERR_FLAG")]]}) # BJ
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung MA CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_ma_flag", "ERR_FLAG")]]}) # BK
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plausi_begruendung_final", "Fehler Begr.")]]})
plausi_check_erfolgreich_durchgefuehrt = True # Markieren, dass der Check zumindest versucht wurde
except Exception as e_plausi_in_single_row_chat:
self.logger.error(f"FEHLER bei Plausibilitäts-Checks in _process_single_row (Chat-Block) für Zeile {row_num_in_sheet}: {e_plausi_in_single_row_chat}")
# ... (Fehler-Flags für Plausi-Spalten setzen) ...
except Exception as e_plausi_in_single_row:
self.logger.error(f"FEHLER bei Plausibilitäts-Checks in _process_single_row für Zeile {row_num_in_sheet}: {e_plausi_in_single_row}")
# Fehler-Flags für Plausi-Spalten setzen
error_val_plausi = [['FEHLER_PLAUSI_CALL']]
for key_flag_plausi in ["Plausibilität Umsatz", "Plausibilität Mitarbeiter", "Plausibilität Umsatz/MA Ratio", "Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki"]:
if COLUMN_MAP.get(key_flag_plausi) is not None: # Sicherstellen, dass der Key existiert
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP[key_flag_plausi] + 1)}{row_num_in_sheet}', 'values': error_val_plausi})
if COLUMN_MAP.get("Plausibilität Begründung") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[f"Systemfehler Plausi-Call: {str(e_plausi_in_single_row)[:50]}"]]})
# Plausi-Timestamp setzen, wenn der Plausi-Block (3f) erreicht wurde
# (entweder erfolgreich oder mit Fehler im try-Block)
if COLUMN_MAP.get("Plausibilität Prüfdatum") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Prüfdatum"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
else:
self.logger.error("FEHLER: Schlüssel 'Plausibilität Prüfdatum' nicht in COLUMN_MAP. Kann Timestamp nicht setzen.")
# Setze den "Timestamp letzte Prüfung" (BO - für ChatGPT-Evaluationen), da dieser Block lief
if COLUMN_MAP.get("Timestamp letzte Prüfung") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Timestamp letzte Prüfung"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
else:
self.logger.error("FEHLER: Schlüssel 'Timestamp letzte Prüfung' nicht in COLUMN_MAP. Kann Haupt-Timestamp nicht setzen.")
# else if run_chat_step (aber nicht processing_needed):
# self.logger.debug(f"Zeile {row_num_in_sheet}: Ueberspringe CHATGPT Evaluationen und Plausi-Checks (Timestamp BI gesetzt, Wiki nicht aktualisiert und kein Re-Eval).")
# else if run_chat_step:
# Die Chat-Schritte waren angefordert, aber nicht noetig basierend auf Status/Re-Eval/Wiki-Update.
# Die lokalen Variablen final_wiki_data und website_summary behalten ihre initialen Werte (current_...).
# chat_eval_just_ran bleibt False.
# self.logger.debug(f"Zeile {row_num_in_sheet}: Ueberspringe CHATGPT Evaluationen (AO vorhanden, Wiki nicht aktualisiert und kein Re-Eval).") # Zu viel Laerm im Debug
# Plausi-Timestamp (BM) und Haupt-Timestamp (BN) setzen
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Prüfdatum"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Timestamp letzte Prüfung"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
# --- Der Code fuer den naechsten Verarbeitungsschritt (ML Prediction) folgt im naechsten Block ---