From 3d337a39df12ea5e9e30e319f0384246632ca5c4 Mon Sep 17 00:00:00 2001 From: Floke Date: Wed, 23 Apr 2025 05:18:30 +0000 Subject: [PATCH] =?UTF-8?q?v1.6.7:=20Behebt=20strukturelle/Syntax-Fehler;?= =?UTF-8?q?=20passt=20Filter=20f=C3=BCr=20Wiki-Suche=20via=20SerpAPI=20an?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Inkrementiere Versionsnummer auf v1.6.7. - Behebe kritischen AttributeError: Korrigiere die Einrückung für mehrere Verarbeitungsmethoden (_process_single_row, process_reevaluation_rows, process_serp_website_lookup_for_empty, process_website_details_for_marked_rows, prepare_data_for_modeling, process_rows_sequentially, process_find_wiki_with_serp), sodass diese korrekt als Methoden innerhalb der Klasse DataProcessor definiert sind. - Behebe SyntaxError: Löse das Problem mit komplexen f-Strings in _process_single_row und potenziell anderen Stellen, indem die String-Konstruktion von Ausdrücken innerhalb der f-String-Syntax getrennt wird. - Passe Filterlogik für Modus 'find_wiki_serp' an: Die SerpAPI-Suche nach fehlenden Wiki-URLs (M=k.A./leer) wird nun ausgelöst, wenn (CRM Umsatz (J) > 200 Mio ODER CRM Anzahl Mitarbeiter (K) > 500). Implementiere robuste numerische Extraktion für J und K innerhalb der Filterlogik. - Stelle sicher, dass SerpAPI Wiki Search Timestamp (AY) immer nach einem Suchversuch im Modus 'find_wiki_serp' gesetzt wird, unabhängig vom Ergebnis. - Diverse Logging-Anpassungen für Klarheit und Debugging (z.B. im Wiki-Verarbeitungsschritt). --- brancheneinstufung.py | 329 +++++++++++++++++++++++++----------------- 1 file changed, 199 insertions(+), 130 deletions(-) diff --git a/brancheneinstufung.py b/brancheneinstufung.py index 2d5aff28..a513a17d 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -1,22 +1,14 @@ #!/usr/bin/env python3 """ -v1.6.6: Füge SerpAPI-Suche für fehlende Wiki-URLs großer Firmen hinzu +v1.6.7: Behebt strukturelle/Syntax-Fehler; passt Filter für Wiki-Suche via SerpAPI an Git-Änderungsbeschreibung: -- Füge neuen Betriebsmodus `--mode find_wiki_serp` hinzu. -- Implementiere neue Funktion `serp_wikipedia_lookup`, die SerpAPI nutzt, um gezielt nach Wikipedia-Artikeln für einen Firmennamen zu suchen. -- Implementiere neue Funktion `process_find_wiki_with_serp`: - - Lädt aktuelle Sheet-Daten. - - Filtert Zeilen, bei denen Spalte M (Wiki URL) leer/'k.A.' ist UND Spalte K (CRM Mitarbeiter) einen Schwellenwert (Standard: 500) überschreitet. - - Ruft `serp_wikipedia_lookup` für gefilterte Zeilen auf. - - Bei erfolgreicher URL-Findung: - - Schreibt die gefundene URL in Spalte M. - - Setzt Flag 'x' in Spalte A (ReEval Flag). - - Löscht Timestamps in Spalten AN (Wikipedia Timestamp) und AO (Timestamp letzte Prüfung). - - Führt gebündelte Sheet-Updates am Ende durch. -- Integriere den neuen Modus `find_wiki_serp` in die Argumentenverarbeitung und Ausführungslogik der `main`-Funktion. -- Füge notwendige Imports hinzu und stelle sicher, dass die neuen Funktionen Logging verwenden. -- Aktualisiere Versionsnummer in `Config.VERSION` auf v1.6.6. +- Inkrementiere Versionsnummer auf v1.6.7. +- Behebe kritischen AttributeError: Korrigiere die Einrückung für mehrere Verarbeitungsmethoden (_process_single_row, process_reevaluation_rows, process_serp_website_lookup_for_empty, process_website_details_for_marked_rows, prepare_data_for_modeling, process_rows_sequentially, process_find_wiki_with_serp), sodass diese korrekt als Methoden innerhalb der Klasse DataProcessor definiert sind. +- Behebe SyntaxError: Löse das Problem mit komplexen f-Strings in _process_single_row und potenziell anderen Stellen, indem die String-Konstruktion von Ausdrücken innerhalb der f-String-Syntax getrennt wird. +- Passe Filterlogik für Modus 'find_wiki_serp' an: Die SerpAPI-Suche nach fehlenden Wiki-URLs (M=k.A./leer) wird nun ausgelöst, wenn (CRM Umsatz (J) > 200 Mio ODER CRM Anzahl Mitarbeiter (K) > 500). Implementiere robuste numerische Extraktion für J und K innerhalb der Filterlogik. +- Stelle sicher, dass SerpAPI Wiki Search Timestamp (AY) immer nach einem Suchversuch im Modus 'find_wiki_serp' gesetzt wird, unabhängig vom Ergebnis. +- Diverse Logging-Anpassungen für Klarheit und Debugging (z.B. im Wiki-Verarbeitungsschritt). """ import os @@ -73,7 +65,7 @@ PATTERNS_FILE_JSON = "technician_patterns.json" # Optional # ==================== KONFIGURATION ==================== class Config: # ... (Alle deine bisherigen Config-Einstellungen) ... - VERSION = "v1.6.6" # Versionsnummer erhöhen + VERSION = "v1.6.7" # Versionsnummer erhöhen LANG = "de" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" MAX_RETRIES = 3 @@ -368,9 +360,17 @@ def serp_wikipedia_lookup(company_name, website=None, min_score=0.4): return None # Bei unerwarteten Fehlern None zurückgeben # Kann als eigenständige Funktion oder Methode in DataProcessor implementiert werden -def process_find_wiki_with_serp(sheet_handler, row_limit=None, min_employees=500): +# Annahme: COLUMN_MAP ist global definiert und enthält mindestens: +# "CRM Name" (B), "CRM Anzahl Mitarbeiter" (K), "CRM Umsatz" (J), +# "Wiki URL" (M), "ReEval Flag" (A), "Wiki Absatz" (N), ..., "Wiki Verif. Timestamp" (AX), "SerpAPI Wiki Search Timestamp" (AY) +# Annahme: serp_wikipedia_lookup und simple_normalize_url sind definiert und nutzen logging/retry +# Annahme: clean_text und normalize_company_name sind definiert +# Annahme: get_valid_numeric (oder ähnliche Logik zur numerischen Extraktion) ist verfügbar/implementiert. + +def process_find_wiki_with_serp(sheet_handler, row_limit=None, min_employees=500, min_umsatz=200): """ - Sucht fehlende Wikipedia-URLs (Spalte M = k.A.) für Unternehmen mit > min_employees + Sucht fehlende Wikipedia-URLs (Spalte M = k.A.) für Unternehmen mit + (Umsatz CRM > min_umsatz ODER Mitarbeiter CRM > min_employees) über SerpAPI und trägt gefundene URLs in Spalte M ein. Setzt ReEval-Flag (A) und löscht abhängige Wiki-Spalten (N-V, AN, AO, AP, AX). Merkt sich in Spalte AY, wann die Suche durchgeführt wurde. @@ -378,149 +378,218 @@ def process_find_wiki_with_serp(sheet_handler, row_limit=None, min_employees=500 Args: sheet_handler (GoogleSheetHandler): Initialisierte Instanz. row_limit (int, optional): Maximale Anzahl zu prüfender Zeilen. Defaults to None. - min_employees (int, optional): Mindestanzahl Mitarbeiter (Spalte K) als Filter. Defaults to 500. + min_employees (int, optional): Mindestanzahl Mitarbeiter (Spalte K) als Teilfilter. Defaults to 500. + min_umsatz (int, optional): Mindestumsatz (Spalte J) als Teilfilter. Defaults to 200. """ - logging.info(f"Starte Modus 'find_wiki_serp': Suche fehlende Wiki-URLs für Firmen > {min_employees} MA...") + logging.info(f"Starte Modus 'find_wiki_serp': Suche fehlende Wiki-URLs für Firmen mit (Umsatz CRM > {min_umsatz} ODER Mitarbeiter CRM > {min_employees})...") if not sheet_handler.load_data(): return all_data = sheet_handler.get_all_data_with_headers() - if not all_data or len(all_data) <= 5: + # Annahme: 5 Header-Zeilen + header_rows = 5 + if not all_data or len(all_data) <= header_rows: logging.warning("Keine oder zu wenige Daten im Sheet für 'find_wiki_serp' gefunden.") return - header_rows = 5 - data_rows = all_data[header_rows:] + data_rows = all_data[header_rows:] # Daten ab Zeile 6 # Benötigte Spaltenindizes holen (inkl. aller zu löschenden Spalten) - try: - col_indices = { - "A": COLUMN_MAP["ReEval Flag"], - "K": COLUMN_MAP["CRM Anzahl Mitarbeiter"], - "M": COLUMN_MAP["Wiki URL"], - "B": COLUMN_MAP["CRM Name"], - "N": COLUMN_MAP["Wiki Absatz"], # NEU zum Löschen - "O": COLUMN_MAP["Wiki Branche"], # NEU zum Löschen - "P": COLUMN_MAP["Wiki Umsatz"], # NEU zum Löschen - "Q": COLUMN_MAP["Wiki Mitarbeiter"], # NEU zum Löschen - "R": COLUMN_MAP["Wiki Kategorien"], # NEU zum Löschen - "S": COLUMN_MAP["Chat Wiki Konsistenzprüfung"], # NEU zum Löschen - "T": COLUMN_MAP["Chat Begründung Wiki Inkonsistenz"], # NEU zum Löschen - "U": COLUMN_MAP["Chat Vorschlag Wiki Artikel"], # NEU zum Löschen - "V": COLUMN_MAP["Begründung bei Abweichung"], # NEU zum Löschen - "AN": COLUMN_MAP["Wikipedia Timestamp"], - "AO": COLUMN_MAP["Timestamp letzte Prüfung"], - "AP": COLUMN_MAP["Version"], # NEU zum Löschen - "AX": COLUMN_MAP["Wiki Verif. Timestamp"], # NEU zum Löschen - "AY": COLUMN_MAP["SerpAPI Wiki Search Timestamp"] - } - col_letters = {key: sheet_handler._get_col_letter(idx + 1) for key, idx in col_indices.items()} - except KeyError as e: - logging.critical(f"FEHLER: Benötigter Spaltenschlüssel '{e}' nicht in COLUMN_MAP gefunden! Modus abgebrochen.") - return - except Exception as e: - logging.critical(f"FEHLER beim Holen der Spaltenbuchstaben: {e}") - return + # Verwenden Sie hier das COLUMN_MAP robust + col_indices = {} + required_keys = [ + "ReEval Flag", "CRM Anzahl Mitarbeiter", "CRM Umsatz", "Wiki URL", "CRM Name", + "Wiki Absatz", "Wiki Branche", "Wiki Umsatz", "Wiki Mitarbeiter", "Wiki Kategorien", + "Chat Wiki Konsistenzprüfung", "Chat Begründung Wiki Inkonsistenz", "Chat Vorschlag Wiki Artikel", + "Begründung bei Abweichung", "Wikipedia Timestamp", "Timestamp letzte Prüfung", + "Version", "Wiki Verif. Timestamp", "SerpAPI Wiki Search Timestamp" + ] + all_keys_found = True + for key in required_keys: + idx = COLUMN_MAP.get(key) + col_indices[key] = idx + if idx is None: + logging.critical(f"FEHLER: Benötigter Spaltenschlüssel '{key}' nicht in COLUMN_MAP gefunden! Modus abgebrochen.") + all_keys_found = False + + if not all_keys_found: + return # Abbruch, da Spalten fehlen + + # Hilfsfunktion zur Konvertierung Spaltenindex -> Buchstabe + col_letters = {key: sheet_handler._get_col_letter(idx + 1) for key, idx in col_indices.items()} + all_sheet_updates = [] - processed_rows = 0 - found_urls = 0 - skipped_timestamp_ay = 0 - skipped_employee_count = 0 + processed_rows_count = 0 # Zählt Zeilen, für die SerpAPI versucht wurde + found_urls_count = 0 # Zählt Zeilen, wo eine URL gefunden wurde + skipped_timestamp_ay_count = 0 + skipped_size_count = 0 skipped_m_filled_count = 0 + now_timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - for idx, row in enumerate(data_rows): - row_num_in_sheet = idx + header_rows + 1 + # --- Hilfsfunktion zur sicheren numerischen Extraktion (adaptiert von prepare_data_for_modeling) --- + def safe_numeric_extract(value_str): + if value_str is None or pd.isna(value_str) or str(value_str).strip() == '': return 0 # Return 0, nicht NaN, für die Vergleichslogik + try: + # Annahme: clean_text existiert + processed_value = clean_text(str(value_str)) + if processed_value == "k.A.": return 0 - if row_limit is not None and processed_rows >= row_limit: + processed_value = re.sub(r'(?i)^\s*(ca\.?|circa|rund|etwa|über|unter|mehr als|weniger als|bis zu)\s+', '', processed_value) + processed_value = re.sub(r'[€$£¥]', '', processed_value).strip() + processed_value = re.split(r'\s*(-|–|bis)\s*', processed_value, 1)[0].strip() + # Entferne Punkte UND Apostrophe als Tausendertrenner, ersetze Komma durch Punkt + processed_value = processed_value.replace('.', '').replace("'", "").replace(',', '.') + + match = re.search(r'([\d.]+)', processed_value) + if not match: return 0 # Keine numerischen Zeichen gefunden + + num_str = match.group(1) + if not num_str or num_str == '.': return 0 + + num = float(num_str) + + # Einheiten-Multiplikatoren (Mrd, Mio, Tsd) - Wichtig für Umsatz + multiplier = 1.0 + original_lower = str(value_str).lower() # Nutze den Originalstring für Einheiten + if "mrd" in original_lower or "milliarden" in original_lower or "billion" in original_lower: multiplier = 1000000000.0 + elif "mio" in original_lower or "millionen" in original_lower or "mill." in original_lower: multiplier = 1000000.0 + elif "tsd" in original_lower or "tausend" in original_lower: multiplier = 1000.0 + + num = num * multiplier + + # Für den Vergleich (Umsatz in Mio, Mitarbeiter int) + # Umsatz soll > 200 MIO sein, also num direkt mit 200,000,000 vergleichen + # Mitarbeiter > 500, num direkt mit 500 vergleichen + # Geben Sie den Rohwert nach Multiplikator zurück + return num if num > 0 else 0 # Nur positive Werte zählen + + except Exception as e: + logging.debug(f"Fehler in safe_numeric_extract für Wert '{str(value_str)[:50]}...': {e}") + return 0 + # --- Ende Hilfsfunktion --- + + + # Iteriere durch die Datenzeilen + for idx, row in enumerate(data_rows): + row_num_in_sheet = idx + header_rows + 1 # 1-basierte Sheet-Zeilennummer + + # Limit-Prüfung + if row_limit is not None and processed_rows_count >= row_limit: logging.info(f"Zeilenlimit ({row_limit}) für durchgeführte Suchen erreicht.") break - # Prüfe AY Timestamp - ts_ay_val = row[col_indices["AY"]] if len(row) > col_indices["AY"] else "" + # Sicherstellen, dass die Zeile lang genug für alle benötigten Spalten ist + max_needed_idx = max(col_indices.values()) + if len(row) <= max_needed_idx: + logging.debug(f"Zeile {row_num_in_sheet}: Übersprungen (Zeile zu kurz für benötigte Spalten, erwartet > {max_needed_idx}, hat {len(row)}).") + continue + + + # Prüfe AY Timestamp: Überspringe, wenn SerpAPI Suche für diese Zeile schon versucht wurde + ts_ay_val = row[col_indices["SerpAPI Wiki Search Timestamp"]] if ts_ay_val and ts_ay_val.strip(): - skipped_timestamp_ay += 1 + skipped_timestamp_ay_count += 1 continue - try: - # Mitarbeiterzahl prüfen - ma_val_str = row[col_indices["K"]] if len(row) > col_indices["K"] else "0" - try: - ma_val_str_cleaned = re.sub(r"[^\d]", "", ma_val_str) - ma_val = int(ma_val_str_cleaned) if ma_val_str_cleaned else 0 - except ValueError: ma_val = 0 - - if ma_val <= min_employees: - skipped_employee_count += 1 - continue - - # Wiki URL (M) prüfen - m_value = row[col_indices["M"]] if len(row) > col_indices["M"] else "" - if m_value and m_value.strip().lower() != "k.a.": - skipped_m_filled_count += 1 - continue - - # Kandidat gefunden - company_name = row[col_indices["B"]] if len(row) > col_indices["B"] else "" - if not company_name: - logging.warning(f"Zeile {row_num_in_sheet}: Übersprungen, kein Firmenname für Suche vorhanden.") - continue - - # SerpAPI Suche - logging.info(f"Zeile {row_num_in_sheet}: Suche Wiki-URL für '{company_name}' (MA: {ma_val})...") - wiki_url_found = serp_wikipedia_lookup(company_name) - processed_rows += 1 - time.sleep(1.5) - - # Updates vorbereiten - # Timestamp AY IMMER setzen - row_updates = [{'range': f'{col_letters["AY"]}{row_num_in_sheet}', 'values': [[now_timestamp_str]]}] - - if wiki_url_found: - logging.info(f" -> URL gefunden: {wiki_url_found}. Bereite Update vor (Setze M, A; Lösche N-V, AN, AO, AP, AX).") - found_urls += 1 - # Zusätzliche Updates für gefundene URL - row_updates.extend([ - {'range': f'{col_letters["M"]}{row_num_in_sheet}', 'values': [[wiki_url_found]]}, # URL setzen - {'range': f'{col_letters["A"]}{row_num_in_sheet}', 'values': [['x']]}, # ReEval Flag - # --- Spalten leeren --- - {'range': f'{col_letters["N"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["O"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["P"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["Q"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["R"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["S"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["T"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["U"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["V"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["AN"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["AO"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["AP"]}{row_num_in_sheet}', 'values': [['']]}, - {'range': f'{col_letters["AX"]}{row_num_in_sheet}', 'values': [['']]} - ]) - else: - logging.info(f" -> Keine Wiki-URL für '{company_name}' via SerpAPI gefunden.") - # Nur AY Timestamp wird geschrieben - - all_sheet_updates.extend(row_updates) - - except Exception as e: - logging.exception(f"Unerwarteter Fehler bei Verarbeitung von Zeile {row_num_in_sheet}: {e}") + # Prüfe Wiki URL (M): Überspringe, wenn bereits gefüllt (nicht k.A. oder leer) + m_value = row[col_indices["Wiki URL"]] + if m_value and str(m_value).strip().lower() != "k.a.": # Jetzt auch 'leer' abfangen? Nein, '' ist schon abgedeckt + skipped_m_filled_count += 1 continue + # --- Prüfe Unternehmensgröße (J Umsatz ODER K Mitarbeiter) --- + umsatz_val_str = row[col_indices["CRM Umsatz"]] + ma_val_str = row[col_indices["CRM Anzahl Mitarbeiter"]] + + umsatz_val_num = safe_numeric_extract(umsatz_val_str) + ma_val_num = safe_numeric_extract(ma_val_str) + + # Filterlogik: Umsatz > min_umsatz ODER Mitarbeiter > min_employees + # Wenn *nicht* (Umsatz > min_umsatz ODER Mitarbeiter > min_employees), dann überspringe + if not (umsatz_val_num > min_umsatz * 1000000 or ma_val_num > min_employees): + logging.debug(f"Zeile {row_num_in_sheet}: Übersprungen (Größe nicht ausreichend. Umsatz: {umsatz_val_num:.0f}, MA: {ma_val_num}).") + skipped_size_count += 1 + continue + # --- Ende Größenprüfung --- + + + # Kandidat gefunden: M leer/k.A., AY leer, und Größe passt + company_name = row[col_indices["CRM Name"]] if len(row) > col_indices["CRM Name"] else "" + if not company_name or str(company_name).strip() == "": + logging.warning(f"Zeile {row_num_in_sheet}: Übersprungen, kein Firmenname für Suche vorhanden.") + # Setze AY Timestamp, damit wir nicht immer wieder versuchen + ay_col_letter = col_letters["SerpAPI Wiki Search Timestamp"] + all_sheet_updates.append({'range': f'{ay_col_letter}{row_num_in_sheet}', 'values': [[now_timestamp_str]]}) + continue + + # SerpAPI Suche + logging.info(f"Zeile {row_num_in_sheet}: Suche Wiki-URL für '{company_name}' (Umsatz: {umsatz_val_num:.0f}, MA: {ma_val_num})...") + processed_rows_count += 1 # Zähle VOR dem Call, dass ein Versuch gestartet wird + + # Annahme: serp_wikipedia_lookup existiert und nutzt logging/retry + # website_url wird nicht direkt im SerpAPI Lookup verwendet, kann aber als Kontext hilfreich sein + website_url = row[COLUMN_MAP.get("CRM Website", -1)] if COLUMN_MAP.get("CRM Website", -1) != -1 and len(row) > COLUMN_MAP.get("CRM Website", -1) else None + wiki_url_found = serp_wikipedia_lookup(company_name, website=website_url) + + # Updates vorbereiten + # Timestamp AY IMMER setzen, nachdem der Versuch gemacht wurde + ay_col_letter = col_letters["SerpAPI Wiki Search Timestamp"] + all_sheet_updates.append({'range': f'{ay_col_letter}{row_num_in_sheet}', 'values': [[now_timestamp_str]]}) + + + if wiki_url_found and wiki_url_found.strip() and wiki_url_found != "k.A.": + logging.info(f" -> URL gefunden: {wiki_url_found}. Bereite Update vor (Setze M, A; Lösche N-V, AN, AO, AP, AX).") + found_urls_count += 1 + + # Zusätzliche Updates für gefundene URL + m_l = col_letters["Wiki URL"] + a_l = col_letters["ReEval Flag"] + # Spalten N-V leeren + n_l = col_letters["Wiki Absatz"] + v_l = col_letters["Begründung bei Abweichung"] + # Timestamps AN, AO, AX, Version AP leeren + an_l = col_letters["Wikipedia Timestamp"] + ao_l = col_letters["Timestamp letzte Prüfung"] + ap_l = col_letters["Version"] + ax_l = col_letters["Wiki Verif. Timestamp"] + + + all_sheet_updates.extend([ + {'range': f'{m_l}{row_num_in_sheet}', 'values': [[wiki_url_found]]}, # URL setzen in M + {'range': f'{a_l}{row_num_in_sheet}', 'values': [['x']]}, # ReEval Flag setzen in A + # --- Spalten leeren, damit sie neu befüllt werden --- + # Range N:V leeren + {'range': f'{n_l}{row_num_in_sheet}:{v_l}{row_num_in_sheet}', 'values': [[''] * (col_indices["Begründung bei Abweichung"] - col_indices["Wiki Absatz"] + 1)]}, + {'range': f'{an_l}{row_num_in_sheet}', 'values': [['']]}, # AN leeren + {'range': f'{ao_l}{row_num_in_sheet}', 'values': [['']]}, # AO leeren + {'range': f'{ap_l}{row_num_in_sheet}', 'values': [['']]}, # AP leeren + {'range': f'{ax_l}{row_num_in_sheet}', 'values': [['']]} # AX leeren + ]) + else: + logging.info(f" -> Keine Wiki-URL für '{company_name}' via SerpAPI gefunden.") + # Nur AY Timestamp wird gesetzt, was bereits oben passiert ist. + + # Kleiner Sleep nach jeder SerpAPI-Suche, auch wenn es ein Retry gibt + # Der Decorator kümmert sich um Retries mit Backoff, dies ist nur eine globale Rate-Limit-Vorsorge. + time.sleep(getattr(Config, 'RETRY_DELAY', 5) * 0.3) + + # --- Batch Update am Ende --- if all_sheet_updates: - logging.info(f"Sende Batch-Update für {processed_rows} geprüfte Zeilen ({found_urls} URLs gefunden, {len(all_sheet_updates)} Zellen)...") + logging.info(f"Sende Batch-Update für {processed_rows_count} geprüfte Zeilen ({found_urls_count} URLs gefunden, {len(all_sheet_updates)} Zellen)...") + # Annahme: sheet_handler.batch_update_cells existiert und nutzt logging/retry success = sheet_handler.batch_update_cells(all_sheet_updates) if success: logging.info(f"Sheet-Update für 'find_wiki_serp' erfolgreich.") + # Der else-Fall wird von batch_update_cells geloggt else: logging.info("Keine Zeilen gefunden, für die eine SerpAPI Wiki-Suche durchgeführt werden musste/konnte.") logging.info(f"Modus 'find_wiki_serp' abgeschlossen.") - logging.info(f" Durchgeführte Suchen in diesem Lauf: {processed_rows}") - logging.info(f" Gefundene & eingetragene URLs: {found_urls}") - logging.info(f" Übersprungen (AY bereits gesetzt): {skipped_timestamp_ay}") - logging.info(f" Übersprungen (MA <= {min_employees}): {skipped_employee_count}") + logging.info(f" Durchgeführte Suchen in diesem Lauf: {processed_rows_count}") + logging.info(f" Gefundene & eingetragene URLs: {found_urls_count}") + logging.info(f" Übersprungen (AY bereits gesetzt): {skipped_timestamp_ay_count}") + logging.info(f" Übersprungen (Größe nicht ausreichend): {skipped_size_count}") logging.info(f" Übersprungen (M bereits gefüllt): {skipped_m_filled_count}") def prepare_data_for_modeling(sheet_handler):