This commit is contained in:
2025-04-22 06:12:55 +00:00
parent be6db61200
commit dcc2413f9b

View File

@@ -192,6 +192,7 @@ COLUMN_MAP = {
"Finaler Umsatz (Wiki>CRM)": 47,# AV
"Finaler Mitarbeiter (Wiki>CRM)": 48, # AW
"Wiki Verif. Timestamp": 49 # AX (NEU)
"SerpAPI Wiki Search Timestamp": 50 # AY (NEU)
}
# Hinweis: Index ist 0-basiert, Spaltenbuchstaben sind 1-basiert (A=1, AW=49)
@@ -319,11 +320,7 @@ def process_find_wiki_with_serp(sheet_handler, row_limit=None, min_employees=500
Sucht fehlende Wikipedia-URLs (Spalte M = k.A.) für Unternehmen mit > min_employees
über SerpAPI und trägt gefundene URLs in Spalte M ein. Setzt ReEval-Flag (A)
und löscht Timestamps (AN, AO) für gefundene Einträge.
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.
Merkt sich in Spalte AY, wann die Suche durchgeführt wurde.
"""
logging.info(f"Starte Modus 'find_wiki_serp': Suche fehlende Wiki-URLs für Firmen > {min_employees} MA...")
@@ -335,7 +332,7 @@ def process_find_wiki_with_serp(sheet_handler, row_limit=None, min_employees=500
header_rows = 5
data_rows = all_data[header_rows:]
# Benötigte Spaltenindizes holen
# Benötigte Spaltenindizes holen (inkl. neuer Spalte AY)
try:
col_indices = {
"A": COLUMN_MAP["ReEval Flag"],
@@ -343,7 +340,8 @@ def process_find_wiki_with_serp(sheet_handler, row_limit=None, min_employees=500
"M": COLUMN_MAP["Wiki URL"],
"B": COLUMN_MAP["CRM Name"],
"AN": COLUMN_MAP["Wikipedia Timestamp"],
"AO": COLUMN_MAP["Timestamp letzte Prüfung"]
"AO": COLUMN_MAP["Timestamp letzte Prüfung"],
"AY": COLUMN_MAP["SerpAPI Wiki Search Timestamp"] # <-- NEU
}
col_letters = {key: sheet_handler._get_col_letter(idx + 1) for key, idx in col_indices.items()}
except KeyError as e:
@@ -353,43 +351,49 @@ def process_find_wiki_with_serp(sheet_handler, row_limit=None, min_employees=500
logging.critical(f"FEHLER beim Holen der Spaltenbuchstaben: {e}")
return
all_sheet_updates = []
processed_rows = 0
found_urls = 0
processed_rows = 0 # Zählt Zeilen, für die eine Suche durchgeführt wurde
found_urls = 0 # Zählt Zeilen, wo eine URL gefunden wurde
skipped_timestamp_ay = 0 # Zählt Zeilen, die übersprungen wurden, weil AY gesetzt war
skipped_employee_count = 0
skipped_m_filled_count = 0
now_timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Einmaliger Timestamp für diesen Lauf
for idx, row in enumerate(data_rows):
row_num_in_sheet = idx + header_rows + 1
if row_limit is not None and processed_rows >= row_limit:
logging.info(f"Zeilenlimit ({row_limit}) erreicht.")
logging.info(f"Zeilenlimit ({row_limit}) für durchgeführte Suchen erreicht.")
break
# Prüfe, ob Zeile überhaupt verarbeitet werden soll
# --- NEUE PRÜFUNG: Wurde diese Zeile schon via SerpAPI geprüft? ---
ts_ay_val = row[col_indices["AY"]] if len(row) > col_indices["AY"] else ""
if ts_ay_val and ts_ay_val.strip():
skipped_timestamp_ay += 1
# Optional: Debug Log
# logging.debug(f"Zeile {row_num_in_sheet}: Übersprungen (SerpAPI Wiki Search Timestamp AY bereits gesetzt: '{ts_ay_val}').")
continue # Nächste Zeile, diese wurde schon geprüft
# --- ENDE NEUE PRÜFUNG ---
try:
# 1. Mitarbeiterzahl prüfen
ma_val_str = row[col_indices["K"]] if len(row) > col_indices["K"] else "0"
try:
# Versuche, die Zahl zu extrahieren (vereinfacht, ohne extract_numeric_value)
ma_val_str_cleaned = re.sub(r"[^\d]", "", ma_val_str) # Nur Ziffern
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 # Im Zweifel als 0 werten
except ValueError: ma_val = 0
if ma_val <= min_employees:
skipped_employee_count += 1
continue # Nächste Zeile
continue
# 2. Prüfen, ob Wiki URL (M) leer oder "k.A." ist
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 # Nächste Zeile
continue
# Wenn wir hier sind, ist die Zeile ein Kandidat
processed_rows += 1
# Wenn wir hier sind, ist die Zeile ein Kandidat FÜR DIESEN LAUF
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.")
@@ -397,42 +401,50 @@ def process_find_wiki_with_serp(sheet_handler, row_limit=None, min_employees=500
# --- SerpAPI Suche durchführen ---
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) # Annahme: nutzt logging
time.sleep(1.5) # Pause zwischen SerpAPI-Aufrufen
wiki_url_found = serp_wikipedia_lookup(company_name)
processed_rows += 1 # Zähle die durchgeführte Suche
time.sleep(1.5) # Pause
# --- Updates vorbereiten ---
# Timestamp AY IMMER setzen, um die Suche zu markieren
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.")
found_urls += 1
# Updates für diese Zeile sammeln
row_updates = [
{'range': f'{col_letters["M"]}{row_num_in_sheet}', 'values': [[wiki_url_found]]}, # URL in M schreiben
{'range': f'{col_letters["A"]}{row_num_in_sheet}', 'values': [['x']]}, # ReEval Flag setzen
{'range': f'{col_letters["AN"]}{row_num_in_sheet}', 'values': [['']]}, # AN löschen
{'range': f'{col_letters["AO"]}{row_num_in_sheet}', 'values': [['']]} # AO löschen
]
all_sheet_updates.extend(row_updates)
# Zusätzliche Updates nur, wenn URL gefunden wurde
row_updates.extend([
{'range': f'{col_letters["M"]}{row_num_in_sheet}', 'values': [[wiki_url_found]]},
{'range': f'{col_letters["A"]}{row_num_in_sheet}', 'values': [['x']]},
{'range': f'{col_letters["AN"]}{row_num_in_sheet}', 'values': [['']]},
{'range': f'{col_letters["AO"]}{row_num_in_sheet}', 'values': [['']]}
])
else:
logging.info(f" -> Keine Wiki-URL für '{company_name}' via SerpAPI gefunden.")
# Optional: Status in eine separate Spalte schreiben? Vorerst nicht.
# Optional: Marker in M schreiben? Vorerst nicht, AY reicht.
# row_updates.append({'range': f'{col_letters["M"]}{row_num_in_sheet}', 'values': [['k.A. (SerpAPI Searched)']]})
all_sheet_updates.extend(row_updates)
except Exception as e:
logging.exception(f"Unerwarteter Fehler bei Verarbeitung von Zeile {row_num_in_sheet}: {e}")
# Mache mit der nächsten Zeile weiter
# --- Batch Update am Ende ---
if all_sheet_updates:
logging.info(f"Sende Batch-Update für {found_urls} gefundene Wiki-URLs ({len(all_sheet_updates)} Zellen)...")
success = sheet_handler.batch_update_cells(all_sheet_updates)
if success:
logging.info(f"Sheet-Update für 'find_wiki_serp' erfolgreich.")
else:
logging.info("Keine neuen Wiki-URLs gefunden zum Eintragen.")
# --- 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)...")
success = sheet_handler.batch_update_cells(all_sheet_updates)
if success:
logging.info(f"Sheet-Update für 'find_wiki_serp' erfolgreich.")
# Fehler 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" Geprüfte Kandidaten (MA>{min_employees}, M leer): {processed_rows}")
logging.info(f" Gefundene & eingetragene URLs: {found_urls}")
logging.info(f" Übersprungen (MA <= {min_employees}): {skipped_employee_count}")
logging.info(f" Übersprungen (M bereits gefüllt): {skipped_m_filled_count}")
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" Übersprungen (M bereits gefüllt): {skipped_m_filled_count}")
def prepare_data_for_modeling(sheet_handler):
"""
@@ -3714,7 +3726,8 @@ def alignment_demo(sheet):
"Geschätzter Techniker Bucket", # AU
"Finaler Umsatz (Wiki>CRM)", # AV
"Finaler Mitarbeiter (Wiki>CRM)", # AW
"Wiki Verif. Timestamp" # AX (NEU)
"Wiki Verif. Timestamp", # AX
"SerpAPI Wiki Search Timestamp" # AY (NEU)
],
[ # Quelle der Daten (Zeile 2)
"CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", "System", "System", "System", "System", "System", "Web Scraper", "Chat GPT API",
@@ -3722,7 +3735,8 @@ def alignment_demo(sheet):
"ML Modell / Skript", # AU
"Skript (Wiki/CRM)", # AV
"Skript (Wiki/CRM)", # AW
"System" # AX (NEU) - Timestamp vom Wiki-Verifizierungs-Prozess
"System", # AX
"System" # AY (NEU)
],
[ # Feldkategorie (Zeile 3)
"Prozess", "Firmenname", "Firmenname", "Website", "Ort", "Beschreibung (Text)", "Branche", "Branche", "Anzahl Servicetechniker", "Umsatz", "Anzahl Mitarbeiter", "Wikipedia Artikel URL", "Wikipedia Artikel", "Beschreibung (Text)", "Branche", "Umsatz", "Anzahl Mitarbeiter", "Kategorien (Text)", "Verifizierung", "Begründung bei Abweichung", "Wikipedia Artikel", "Wikipedia Artikel", "Branche", "Branche", "Branche", "FSM Relevanz", "FSM Relevanz", "Anzahl Mitarbeiter", "Anzahl Mitarbeiter", "Anzahl Mitarbeiter", "Anzahl Servicetechniker", "Anzahl Servicetechniker", "Umsatz", "Umsatz", "Kontakte zur Firma", "Kontakte zur Firma", "Kontakte zur Firma", "Kontakte zur Firma", "Timestamp", "Timestamp", "Timestamp", "Version des Skripts die verwendet wurde", "ChatGPT Tokens", "Website-Content", "Website Zusammenfassung",
@@ -3730,7 +3744,8 @@ def alignment_demo(sheet):
"Anzahl Servicetechniker Bucket", # AU
"Umsatz", # AV
"Anzahl Mitarbeiter", # AW
"Timestamp" # AX (NEU)
"Timestamp", # AX
"Timestamp" # AY (NEU)
],
[ # Kurze Beschreibung (Zeile 4)
"Systemspalte...", "Enthält den Firmennamen...", "Manuell gepflegte Kurzform...", "Website des Unternehmens.", "Ort des Unternehmens.", "Kurze Beschreibung...", "Aktuelle Branchenzuweisung...", "Externe Branchenbeschreibung...", "Recherchierte Anzahl...", "Umsatz in Mio. € (CRM).", "Anzahl Mitarbeiter (CRM).", "Vorgeschlagene Wikipedia URL...", "Wikipedia URL...", "Erster Absatz...", "Wikipedia-Branche...", "Wikipedia-Umsatz...", "Wikipedia-Mitarbeiterzahl...", "Liste der Wikipedia-Kategorien.", "\"OK\" oder \"X\" Ergebnis...", "Begründung bei Inkonsistenz...", "Chat-Vorschlag Wiki Artikel...", "Nicht genutzt...", "Branchenvorschlag via ChatGPT...", "Vergleich: Übereinstimmung CRM vs. ...", "Begründung bei abweichender...", "FSM-Relevanz: Bewertung...", "Begründung zur FSM-Bewertung.", "Schätzung Anzahl Mitarbeiter...", "Vergleich CRM vs. Wiki vs. ...", "Begründung bei Mitarbeiterabweichung...", "Schätzung Servicetechniker...", "Begründung bei Abweichung...", "Schätzung Umsatz via ChatGPT.", "Begründung bei Umsatzabweichung.", "Anzahl Kontakte (Serviceleiter)...", "Anzahl Kontakte (IT-Leiter)...", "Anzahl Kontakte (Management)...", "Anzahl Kontakte (Disponent)...", "Timestamp der Kontaktsuche.", "Timestamp der Wikipedia-Suche/Extraktion.", "Timestamp der ChatGPT-Bewertung / Letzte Prüfung der Zeile.", "Ausgabe der Skriptversion...", "Token-Zählung...", "Roh extrahierter Text...", "Zusammenfassung des Webseiteninhalts...",
@@ -3738,7 +3753,8 @@ def alignment_demo(sheet):
"Geschätzter Bucket (1-7) für Servicetechniker...", # AU
"Konsolidierter Umsatz (Mio €) nach Priorität Wiki > CRM.", # AV
"Konsolidierte Mitarbeiterzahl nach Priorität Wiki > CRM.", # AW
"Timestamp der letzten Wiki-Verifikation (Spalten S-Y)." # AX (NEU)
"Timestamp der letzten Wiki-Verifikation (Spalten S-Y).", # AX
"Timestamp der letzten SerpAPI-Suche nach fehlender Wiki-URL (Modus find_wiki_serp)." # AY (NEU)
],
[ # Aufgabe / Funktion (Zeile 5) - Ergänzt um AX
"Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Datenquelle", "Wird durch Wikipedia Scraper bereitgestellt", "Wird zunächst nicht verwendet...", "Wird u.a. zur finalen Ermittlung...", "Wird u.a. mit CRM-Umsatz...", "Wird u.a. mit CRM-Anzahl...", "Wenn Website-Daten fehlen...", "\"Es soll durch ChatGPT geprüft werden...", "\"Liegt eine Inkonsistenz...", "\"Sollte durch die Wikipedia-Suche...", "XXX derzeit nicht verwendet...", "\"ChatGPT soll anhand der vorliegenden...", "Die in Spalte CRM festgelegte...", "Weicht die von ChatGPT ermittelte...", "ChatGPT soll anhand der vorliegenden Daten prüfen...", "Die in 'Chat Begründung für FSM Relevanz'...", "Nur wenn kein Wikipedia-Eintrag...", "Entspricht die durch ChatGPT ermittelte...", "Weicht die von ChatGPT geschätzte...", "ChatGPT soll auf Basis öffentlich...", "Weicht die von ChatGPT geschätzte...", "Nur wenn kein Wikipedia-Eintrag...", "ChatGPT soll signifikante Umsatzabweichungen...", "Über SerpAPI wird zusammen...", "Über SerpAPI wird zusammen...", "Über SerpAPI wird zusammen...", "Über SerpAPI wird zusammen...", "Wenn die Kontaktsuche gestartet wird...", "Wenn die Wikipedia-Suche gestartet wird...", "Wenn die ChatGPT-Bewertung gestartet wird...", "Wird durch das System befüllt", "Wird durch tiktoken berechnet", "Wird durch Web Scraper...", "Wird durch ChatGPT API...",
@@ -3746,23 +3762,23 @@ def alignment_demo(sheet):
"Ergebnis der Schätzung durch das trainierte ML-Modell.", # AU
"Vom Skript berechneter Wert, priorisiert Wiki > CRM...", # AV
"Vom Skript berechneter Wert, priorisiert Wiki > CRM...", # AW
"Timestamp wird gesetzt, wenn Wiki-Verifikation (S-Y) durchgeführt wurde." # AX (NEU)
"Timestamp wird gesetzt, wenn Wiki-Verifikation (S-Y) durchgeführt wurde.", # AX
"Timestamp wird gesetzt, nachdem versucht wurde, eine fehlende Wiki-URL via SerpAPI zu finden." # AY (NEU)
]
]
num_cols = len(new_headers[0])
def colnum_string(n):
string = ""
while n > 0: n, remainder = divmod(n - 1, 26); string = chr(65 + remainder) + string
return string
# ... (Rest der Funktion zum Schreiben der Header bleibt gleich, verwendet jetzt AY als Endspalte) ...
def colnum_string(n): # Hilfsfunktion bleibt
string = ""
while n > 0: n, remainder = divmod(n - 1, 26); string = chr(65 + remainder) + string
return string
end_col_letter = colnum_string(num_cols)
header_range = f"A1:{end_col_letter}{len(new_headers)}"
try:
sheet.update(values=new_headers, range_name=header_range)
print(f"Alignment-Demo abgeschlossen: Header in Bereich {header_range} geschrieben.")
debug_print(f"Alignment-Demo: Header in Bereich {header_range} geschrieben.")
logging.info(f"Alignment-Demo abgeschlossen: Header in Bereich {header_range} geschrieben.")
except Exception as e:
print(f"FEHLER beim Schreiben der Alignment-Demo Header: {e}")
debug_print(f"FEHLER beim Schreiben der Alignment-Demo Header: {e}")
logging.error(f"FEHLER beim Schreiben der Alignment-Demo Header: {e}")
# ==================== DATA PROCESSOR ====================
class DataProcessor: