v1.6.7: Behebt strukturelle/Syntax-Fehler; passt Filter für Wiki-Suche via SerpAPI an
- 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).
This commit is contained in:
@@ -1,22 +1,14 @@
|
|||||||
#!/usr/bin/env python3
|
#!/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:
|
Git-Änderungsbeschreibung:
|
||||||
- Füge neuen Betriebsmodus `--mode find_wiki_serp` hinzu.
|
- Inkrementiere Versionsnummer auf v1.6.7.
|
||||||
- Implementiere neue Funktion `serp_wikipedia_lookup`, die SerpAPI nutzt, um gezielt nach Wikipedia-Artikeln für einen Firmennamen zu suchen.
|
- 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.
|
||||||
- Implementiere neue Funktion `process_find_wiki_with_serp`:
|
- 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.
|
||||||
- Lädt aktuelle Sheet-Daten.
|
- 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.
|
||||||
- Filtert Zeilen, bei denen Spalte M (Wiki URL) leer/'k.A.' ist UND Spalte K (CRM Mitarbeiter) einen Schwellenwert (Standard: 500) überschreitet.
|
- Stelle sicher, dass SerpAPI Wiki Search Timestamp (AY) immer nach einem Suchversuch im Modus 'find_wiki_serp' gesetzt wird, unabhängig vom Ergebnis.
|
||||||
- Ruft `serp_wikipedia_lookup` für gefilterte Zeilen auf.
|
- Diverse Logging-Anpassungen für Klarheit und Debugging (z.B. im Wiki-Verarbeitungsschritt).
|
||||||
- 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.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -73,7 +65,7 @@ PATTERNS_FILE_JSON = "technician_patterns.json" # Optional
|
|||||||
# ==================== KONFIGURATION ====================
|
# ==================== KONFIGURATION ====================
|
||||||
class Config:
|
class Config:
|
||||||
# ... (Alle deine bisherigen Config-Einstellungen) ...
|
# ... (Alle deine bisherigen Config-Einstellungen) ...
|
||||||
VERSION = "v1.6.6" # Versionsnummer erhöhen
|
VERSION = "v1.6.7" # Versionsnummer erhöhen
|
||||||
LANG = "de"
|
LANG = "de"
|
||||||
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
|
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
|
||||||
MAX_RETRIES = 3
|
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
|
return None # Bei unerwarteten Fehlern None zurückgeben
|
||||||
|
|
||||||
# Kann als eigenständige Funktion oder Methode in DataProcessor implementiert werden
|
# 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)
|
ü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).
|
und löscht abhängige Wiki-Spalten (N-V, AN, AO, AP, AX).
|
||||||
Merkt sich in Spalte AY, wann die Suche durchgeführt wurde.
|
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:
|
Args:
|
||||||
sheet_handler (GoogleSheetHandler): Initialisierte Instanz.
|
sheet_handler (GoogleSheetHandler): Initialisierte Instanz.
|
||||||
row_limit (int, optional): Maximale Anzahl zu prüfender Zeilen. Defaults to None.
|
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
|
if not sheet_handler.load_data(): return
|
||||||
all_data = sheet_handler.get_all_data_with_headers()
|
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.")
|
logging.warning("Keine oder zu wenige Daten im Sheet für 'find_wiki_serp' gefunden.")
|
||||||
return
|
return
|
||||||
header_rows = 5
|
data_rows = all_data[header_rows:] # Daten ab Zeile 6
|
||||||
data_rows = all_data[header_rows:]
|
|
||||||
|
|
||||||
# Benötigte Spaltenindizes holen (inkl. aller zu löschenden Spalten)
|
# Benötigte Spaltenindizes holen (inkl. aller zu löschenden Spalten)
|
||||||
try:
|
# Verwenden Sie hier das COLUMN_MAP robust
|
||||||
col_indices = {
|
col_indices = {}
|
||||||
"A": COLUMN_MAP["ReEval Flag"],
|
required_keys = [
|
||||||
"K": COLUMN_MAP["CRM Anzahl Mitarbeiter"],
|
"ReEval Flag", "CRM Anzahl Mitarbeiter", "CRM Umsatz", "Wiki URL", "CRM Name",
|
||||||
"M": COLUMN_MAP["Wiki URL"],
|
"Wiki Absatz", "Wiki Branche", "Wiki Umsatz", "Wiki Mitarbeiter", "Wiki Kategorien",
|
||||||
"B": COLUMN_MAP["CRM Name"],
|
"Chat Wiki Konsistenzprüfung", "Chat Begründung Wiki Inkonsistenz", "Chat Vorschlag Wiki Artikel",
|
||||||
"N": COLUMN_MAP["Wiki Absatz"], # NEU zum Löschen
|
"Begründung bei Abweichung", "Wikipedia Timestamp", "Timestamp letzte Prüfung",
|
||||||
"O": COLUMN_MAP["Wiki Branche"], # NEU zum Löschen
|
"Version", "Wiki Verif. Timestamp", "SerpAPI Wiki Search Timestamp"
|
||||||
"P": COLUMN_MAP["Wiki Umsatz"], # NEU zum Löschen
|
]
|
||||||
"Q": COLUMN_MAP["Wiki Mitarbeiter"], # NEU zum Löschen
|
all_keys_found = True
|
||||||
"R": COLUMN_MAP["Wiki Kategorien"], # NEU zum Löschen
|
for key in required_keys:
|
||||||
"S": COLUMN_MAP["Chat Wiki Konsistenzprüfung"], # NEU zum Löschen
|
idx = COLUMN_MAP.get(key)
|
||||||
"T": COLUMN_MAP["Chat Begründung Wiki Inkonsistenz"], # NEU zum Löschen
|
col_indices[key] = idx
|
||||||
"U": COLUMN_MAP["Chat Vorschlag Wiki Artikel"], # NEU zum Löschen
|
if idx is None:
|
||||||
"V": COLUMN_MAP["Begründung bei Abweichung"], # NEU zum Löschen
|
logging.critical(f"FEHLER: Benötigter Spaltenschlüssel '{key}' nicht in COLUMN_MAP gefunden! Modus abgebrochen.")
|
||||||
"AN": COLUMN_MAP["Wikipedia Timestamp"],
|
all_keys_found = False
|
||||||
"AO": COLUMN_MAP["Timestamp letzte Prüfung"],
|
|
||||||
"AP": COLUMN_MAP["Version"], # NEU zum Löschen
|
if not all_keys_found:
|
||||||
"AX": COLUMN_MAP["Wiki Verif. Timestamp"], # NEU zum Löschen
|
return # Abbruch, da Spalten fehlen
|
||||||
"AY": COLUMN_MAP["SerpAPI Wiki Search Timestamp"]
|
|
||||||
}
|
# Hilfsfunktion zur Konvertierung Spaltenindex -> Buchstabe
|
||||||
col_letters = {key: sheet_handler._get_col_letter(idx + 1) for key, idx in col_indices.items()}
|
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
|
|
||||||
|
|
||||||
all_sheet_updates = []
|
all_sheet_updates = []
|
||||||
processed_rows = 0
|
processed_rows_count = 0 # Zählt Zeilen, für die SerpAPI versucht wurde
|
||||||
found_urls = 0
|
found_urls_count = 0 # Zählt Zeilen, wo eine URL gefunden wurde
|
||||||
skipped_timestamp_ay = 0
|
skipped_timestamp_ay_count = 0
|
||||||
skipped_employee_count = 0
|
skipped_size_count = 0
|
||||||
skipped_m_filled_count = 0
|
skipped_m_filled_count = 0
|
||||||
|
|
||||||
now_timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
now_timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
for idx, row in enumerate(data_rows):
|
# --- Hilfsfunktion zur sicheren numerischen Extraktion (adaptiert von prepare_data_for_modeling) ---
|
||||||
row_num_in_sheet = idx + header_rows + 1
|
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.")
|
logging.info(f"Zeilenlimit ({row_limit}) für durchgeführte Suchen erreicht.")
|
||||||
break
|
break
|
||||||
|
|
||||||
# Prüfe AY Timestamp
|
# Sicherstellen, dass die Zeile lang genug für alle benötigten Spalten ist
|
||||||
ts_ay_val = row[col_indices["AY"]] if len(row) > col_indices["AY"] else ""
|
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():
|
if ts_ay_val and ts_ay_val.strip():
|
||||||
skipped_timestamp_ay += 1
|
skipped_timestamp_ay_count += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
# Prüfe Wiki URL (M): Überspringe, wenn bereits gefüllt (nicht k.A. oder leer)
|
||||||
# Mitarbeiterzahl prüfen
|
m_value = row[col_indices["Wiki URL"]]
|
||||||
ma_val_str = row[col_indices["K"]] if len(row) > col_indices["K"] else "0"
|
if m_value and str(m_value).strip().lower() != "k.a.": # Jetzt auch 'leer' abfangen? Nein, '' ist schon abgedeckt
|
||||||
try:
|
skipped_m_filled_count += 1
|
||||||
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}")
|
|
||||||
continue
|
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 ---
|
# --- Batch Update am Ende ---
|
||||||
if all_sheet_updates:
|
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)
|
success = sheet_handler.batch_update_cells(all_sheet_updates)
|
||||||
if success:
|
if success:
|
||||||
logging.info(f"Sheet-Update für 'find_wiki_serp' erfolgreich.")
|
logging.info(f"Sheet-Update für 'find_wiki_serp' erfolgreich.")
|
||||||
|
# Der else-Fall wird von batch_update_cells geloggt
|
||||||
else:
|
else:
|
||||||
logging.info("Keine Zeilen gefunden, für die eine SerpAPI Wiki-Suche durchgeführt werden musste/konnte.")
|
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"Modus 'find_wiki_serp' abgeschlossen.")
|
||||||
logging.info(f" Durchgeführte Suchen in diesem Lauf: {processed_rows}")
|
logging.info(f" Durchgeführte Suchen in diesem Lauf: {processed_rows_count}")
|
||||||
logging.info(f" Gefundene & eingetragene URLs: {found_urls}")
|
logging.info(f" Gefundene & eingetragene URLs: {found_urls_count}")
|
||||||
logging.info(f" Übersprungen (AY bereits gesetzt): {skipped_timestamp_ay}")
|
logging.info(f" Übersprungen (AY bereits gesetzt): {skipped_timestamp_ay_count}")
|
||||||
logging.info(f" Übersprungen (MA <= {min_employees}): {skipped_employee_count}")
|
logging.info(f" Übersprungen (Größe nicht ausreichend): {skipped_size_count}")
|
||||||
logging.info(f" Übersprungen (M bereits gefüllt): {skipped_m_filled_count}")
|
logging.info(f" Übersprungen (M bereits gefüllt): {skipped_m_filled_count}")
|
||||||
|
|
||||||
def prepare_data_for_modeling(sheet_handler):
|
def prepare_data_for_modeling(sheet_handler):
|
||||||
|
|||||||
Reference in New Issue
Block a user