bugfix
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Automatisiertes Unternehmensbewertungs-Skript - Refactoring v1.7.1
|
||||
Automatisiertes Unternehmensbewertungs-Skript - Refactoring v1.7.0
|
||||
Basierend auf v1.6.x - Umstrukturierung in modulare Klassen und flexibles UI.
|
||||
|
||||
Dieses Skript dient der automatisierten Anreicherung, Validierung und Standardisierung
|
||||
@@ -8,12 +8,14 @@ von Unternehmensdaten, primär aus einem Google Sheet, ergänzt durch Web Scrapi
|
||||
Wikipedia, OpenAI (ChatGPT) und SerpAPI (Google Search, LinkedIn).
|
||||
|
||||
Autor: [Ihr Name/Pseudonym]
|
||||
Version: v1.7.1
|
||||
Version: v1.7.0
|
||||
|
||||
Hinweis zur Struktur:
|
||||
Dieser Code wird in 4 logischen Bloecken uebermittelt. Fuegen Sie die Bloecke
|
||||
Dieser Code wird in logischen Bloecken uebermittelt. Fuegen Sie die Bloecke
|
||||
nacheinander in diese einzige Datei ein, achten Sie sorgfaeltig auf die
|
||||
Einrueckung. Jeder Block muss auf oberster Ebene eingefuegt werden (keine Einrueckung).
|
||||
Die Kommentare wie '# =================================================='
|
||||
markieren den Beginn neuer logischer Sektionen oder Klassen.
|
||||
"""
|
||||
|
||||
# ==============================================================================
|
||||
@@ -28,7 +30,7 @@ import json
|
||||
import pickle
|
||||
import threading
|
||||
import traceback
|
||||
import logging
|
||||
import logging # logging Modul importieren
|
||||
import argparse
|
||||
import random # Fuer Jitter im Retry Decorator
|
||||
from datetime import datetime
|
||||
@@ -36,7 +38,8 @@ from urllib.parse import urlparse, urlencode, unquote
|
||||
|
||||
# Externe Bibliotheken
|
||||
import gspread
|
||||
# Stellen Sie sicher, dass gspread >= 5.0.0 installiert ist.
|
||||
# Stellen Sie sicher, dass gspread >= 5.0.0 installiert ist, da APIError anders behandelt wird
|
||||
# (Unser Code sollte mit den neueren Versionen kompatibel sein)
|
||||
import wikipedia
|
||||
# Stellen Sie sicher, dass wikipedia-api nicht gleichzeitig installiert ist (Konflikt).
|
||||
import requests # Fuer HTTP-Anfragen
|
||||
@@ -137,17 +140,14 @@ class Config:
|
||||
@classmethod
|
||||
def load_api_keys(cls):
|
||||
"""Laedt API-Schluessel aus den definierten Dateien."""
|
||||
# Der Logger ist hier noch nicht vollstaendig konfiguriert, verwenden Sie print
|
||||
# logging.info wird nach Konfiguration des File Handlers korrekt funktionieren
|
||||
# Verwenden Sie print, da der Logger hier noch nicht vollstaendig konfiguriert ist.
|
||||
print("Lade API-Schluessel...")
|
||||
cls.API_KEYS['openai'] = cls._load_key_from_file(API_KEY_FILE)
|
||||
cls.API_KEYS['serpapi'] = cls._load_key_from_file(SERP_API_KEY_FILE)
|
||||
cls.API_KEYS['genderize'] = cls._load_key_from_file(GENDERIZE_API_KEY_FILE)
|
||||
|
||||
# Stelle sicher, dass das 'openai' Modul im Scope dieser Methode bekannt ist
|
||||
# Dies sollte eigentlich nicht noetig sein, wenn es global importiert wurde,
|
||||
# aber als explizite Massnahme:
|
||||
import openai # <<< FÜGEN SIE DIESE IMPORT-ZEILE HIER HINZU
|
||||
import openai # Importiere hier, um sicherzustellen, dass es verfuegbar ist
|
||||
|
||||
if cls.API_KEYS.get('openai'):
|
||||
# Setze den OpenAI API Key global fuer die Bibliothek
|
||||
@@ -170,90 +170,30 @@ class Config:
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
key = f.read().strip()
|
||||
if key:
|
||||
# print(f"Schluessel aus '{filepath}' erfolgreich geladen.") # Zu viel Laerm im Debug
|
||||
return key
|
||||
else:
|
||||
# Logge auf Warning, wenn die Datei leer ist
|
||||
print(f"WARNUNG: Datei '{filepath}' ist leer.")
|
||||
return None
|
||||
except FileNotFoundError:
|
||||
# Logge auf Info, da das Fehlen eines Keys nicht immer ein Fehler sein muss
|
||||
print(f"INFO: API-Schluesseldatei '{filepath}' nicht gefunden.")
|
||||
return None
|
||||
except Exception as e:
|
||||
# Logge auf Error, wenn beim Lesen ein anderer Fehler auftritt
|
||||
print(f"FEHLER beim Lesen der Schluesseldatei '{filepath}': {e}")
|
||||
return None
|
||||
|
||||
|
||||
# --- Globale Spalten-Mapping (WICHTIG: MUSS ZU IHREM SHEET PASSEN!) ---
|
||||
# Index ist 0-basiert, Spaltenbuchstaben sind 1-basiert (A=1, AW=49)
|
||||
# Dies sollte die Mapping-Definition aus Ihrem Code (Teil 1) sein, vervollstaendigt.
|
||||
# UEBERPRUEFEN SIE DIESES MAPPING SORGFÄLTIG GEGEN IHR SHEET!
|
||||
COLUMN_MAP = {
|
||||
"ReEval Flag": 0, # A - Markierungsspalte fuer manuelle Re-Evaluation
|
||||
"CRM Name": 1, # B - Unternehmensname aus CRM
|
||||
"CRM Kurzform": 2, # C - Manuell gepflegte Kurzform
|
||||
"CRM Website": 3, # D - Website URL aus CRM (kann durch Skript ergaenzt werden)
|
||||
"CRM Ort": 4, # E - Ort aus CRM
|
||||
"CRM Beschreibung": 5, # F - Beschreibung aus CRM
|
||||
"CRM Branche": 6, # G - Branche aus CRM
|
||||
"CRM Beschreibung Branche extern": 7, # H - Externe Branchenbeschreibung (falls vorhanden)
|
||||
"CRM Anzahl Techniker": 8, # I - Bekannte Anzahl Servicetechniker aus CRM (Zielvariable fuer ML)
|
||||
"CRM Umsatz": 9, # J - Umsatz aus CRM
|
||||
"CRM Anzahl Mitarbeiter": 10, # K - Anzahl Mitarbeiter aus CRM
|
||||
"CRM Vorschlag Wiki URL": 11, # L - Vorschlag fuer Wiki URL (kann manuell gepflegt werden)
|
||||
"Wiki URL": 12, # M - Gefundene oder validierte Wikipedia URL
|
||||
"Wiki Absatz": 13, # N - Erster Absatz des Wikipedia Artikels
|
||||
"Wiki Branche": 14, # O - Branche aus Wikipedia Infobox
|
||||
"Wiki Umsatz": 15, # P - Umsatz aus Wikipedia Infobox
|
||||
"Wiki Mitarbeiter": 16, # Q - Mitarbeiterzahl aus Wikipedia Infobox
|
||||
"Wiki Kategorien": 17, # R - Wikipedia Kategorien
|
||||
"Chat Wiki Konsistenzpruefung": 18, # S - ChatGPT Check: Passt Wiki Artikel zum Unternehmen? ('OK', 'X', '?')
|
||||
"Chat Begruendung Wiki Inkonsistenz": 19, # T - Begruendung, wenn S='X'
|
||||
"Chat Vorschlag Wiki Artikel": 20, # U - ChatGPT Vorschlag fuer alternativen Wiki Artikel (falls S='X')
|
||||
"Begruendung bei Abweichung": 21, # V - Nicht mehr primaer genutzt (Begruendung CRM vs Wiki URL)
|
||||
"Chat Vorschlag Branche": 22, # W - ChatGPT Vorschlag fuer Branche (Zielschema)
|
||||
"Chat Konsistenz Branche": 23, # X - Vergleich W vs. G ('ok', 'X', 'fallback_...')
|
||||
"Chat Begruendung Abweichung Branche": 24, # Y - Begruendung fuer W
|
||||
"Chat Pruefung FSM Relevanz": 25, # Z - ChatGPT Check: Ist das Unternehmen fuer FSM relevant?
|
||||
"Chat Begruendung fuer FSM Relevanz": 26, # AA - Begruendung fuer Z
|
||||
"Chat Schaetzung Anzahl Mitarbeiter": 27, # AB - ChatGPT Schaetzung Mitarbeiter
|
||||
"Chat Konsistenzpruefung Mitarbeiterzahl": 28, # AC - Vergleich AB vs. K/Q
|
||||
"Chat Begruendung Abweichung Mitarbeiterzahl": 29, # AD - Begruendung fuer AB/AC
|
||||
"Chat Einschaetzung Anzahl Servicetechniker": 30, # AE - ChatGPT Schaetzung Servicetechniker
|
||||
"Chat Begruendung Abweichung Anzahl Servicetechniker": 31, # AF - Begruendung fuer AE
|
||||
"Chat Schaetzung Umsatz": 32, # AG - ChatGPT Schaetzung Umsatz
|
||||
"Chat Begruendung Abweichung Umsatz": 33, # AH - Begruendung fuer AG
|
||||
"Linked Serviceleiter gefunden": 34, # AI - Anzahl gefundener Kontakte (Serviceleiter)
|
||||
"Linked It-Leiter gefunden": 35, # AJ - Anzahl gefundener Kontakte (IT-Leiter)
|
||||
"Linked Management gefunden": 36, # AK - Anzahl gefundener Kontakte (Management)
|
||||
"Linked Disponent gefunden": 37, # AL - Anzahl gefundener Kontakte (Disponent)
|
||||
"Contact Search Timestamp": 38, # AM - Timestamp der letzten LinkedIn Suche
|
||||
"Wikipedia Timestamp": 39, # AN - Timestamp der letzten erfolgreichen Wiki Extraktion (M-R befuellt)
|
||||
"Timestamp letzte Pruefung": 40, # AO - Timestamp der letzten ChatGPT Evaluationen (W-Y, Z-AD, AE-AH, AG-AH befuellt)
|
||||
"Version": 41, # AP - Skriptversion, die die Zeile zuletzt bearbeitet hat
|
||||
"Tokens": 42, # AQ - Anzahl Tokens des letzten OpenAI Calls fuer diese Zeile (ggf. aggregiert)
|
||||
"Website Rohtext": 43, # AR - Roh extrahierter Text von der Website
|
||||
"Website Zusammenfassung": 44, # AS - ChatGPT Zusammenfassung von AR
|
||||
"Website Scrape Timestamp": 45, # AT - Timestamp des letzten erfolgreichen Website Scrapings (AR, AS befuellt)
|
||||
"Geschaetzter Techniker Bucket": 46, # AU - Ergebnis des ML-Modells (Bucket)
|
||||
"Finaler Umsatz (Wiki>CRM)": 47,# AV - Konsolidierter Umsatz (Wiki > CRM)
|
||||
"Finaler Mitarbeiter (Wiki>CRM)": 48, # AW - Konsolidierte Mitarbeiterzahl (Wiki > CRM)
|
||||
"Wiki Verif. Timestamp": 49, # AX - Timestamp der letzten Wiki-Verifikation (S-U befuellt)
|
||||
"SerpAPI Wiki Search Timestamp": 50 # AY - Timestamp der letzten SerpAPI-Suche nach fehlender Wiki-URL (Modus find_wiki_serp)
|
||||
"ReEval Flag": 0, "CRM Name": 1, "CRM Kurzform": 2, "CRM Website": 3, "CRM Ort": 4, "CRM Beschreibung": 5, "CRM Branche": 6, "CRM Beschreibung Branche extern": 7, "CRM Anzahl Techniker": 8, "CRM Umsatz": 9, "CRM Anzahl Mitarbeiter": 10, "CRM Vorschlag Wiki URL": 11, "Wiki URL": 12, "Wiki Absatz": 13, "Wiki Branche": 14, "Wiki Umsatz": 15, "Wiki Mitarbeiter": 16, "Wiki Kategorien": 17, "Chat Wiki Konsistenzpruefung": 18, "Chat Begruendung Wiki Inkonsistenz": 19, "Chat Vorschlag Wiki Artikel": 20, "Begruendung bei Abweichung": 21, "Chat Vorschlag Branche": 22, "Chat Konsistenz Branche": 23, "Chat Begruendung Abweichung Branche": 24, "Chat Pruefung FSM Relevanz": 25, "Chat Begruendung fuer FSM Relevanz": 26, "Chat Schaetzung Anzahl Mitarbeiter": 27, "Chat Konsistenzpruefung Mitarbeiterzahl": 28, "Chat Begruendung Abweichung Mitarbeiterzahl": 29, "Chat Einschaetzung Anzahl Servicetechniker": 30, "Chat Begruendung Abweichung Anzahl Servicetechniker": 31, "Chat Schaetzung Umsatz": 32, "Chat Begruendung Abweichung Umsatz": 33, "Linked Serviceleiter gefunden": 34, "Linked It-Leiter gefunden": 35, "Linked Management gefunden": 36, "Linked Disponent gefunden": 37, "Contact Search Timestamp": 38, "Wikipedia Timestamp": 39, "Timestamp letzte Pruefung": 40, "Version": 41, "Tokens": 42, "Website Rohtext": 43, "Website Zusammenfassung": 44, "Website Scrape Timestamp": 45, "Geschaetzter Techniker Bucket": 46, "Finaler Umsatz (Wiki>CRM)": 47, "Finaler Mitarbeiter (Wiki>CRM)": 48, "Wiki Verif. Timestamp": 49, "SerpAPI Wiki Search Timestamp": 50
|
||||
}
|
||||
# Bestaetigen Sie, dass dies Ihre tatsaechlichen Spalten sind!
|
||||
|
||||
# --- Globale Variablen fuer Branch Mapping (werden von load_target_schema() befuellt) ---
|
||||
# BRANCH_MAPPING wird derzeit nicht verwendet, kann aber beibehalten werden.
|
||||
BRANCH_MAPPING = {}
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar." # String-Repraesentation des Schemas fuer Prompts
|
||||
ALLOWED_TARGET_BRANCHES = [] # Liste der erlaubten Kurzformen
|
||||
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar."
|
||||
ALLOWED_TARGET_BRANCHES = []
|
||||
|
||||
# ==============================================================================
|
||||
# Ende Basis-Setup & Globale Helfer Block
|
||||
# Ende Basis-Setup Block
|
||||
# ==============================================================================
|
||||
|
||||
# ==============================================================================
|
||||
@@ -374,82 +314,54 @@ def retry_on_failure(func):
|
||||
# ==============================================================================
|
||||
|
||||
# ==============================================================================
|
||||
# 3. GLOBALE HELPER FUNCTIONS (PART 2: Logging & Token Count)
|
||||
# (Teil von logisch 'utils.py')
|
||||
# GLOBALE HELPER FUNCTIONS (PART 2: Logging & Token Count)
|
||||
# ==============================================================================
|
||||
|
||||
# --- Token Count Funktion ---
|
||||
# Zaehlt Tokens via tiktoken oder schaetzt ueber Leerzeichen.
|
||||
# Der retry_on_failure Decorator ist hier nicht sinnvoll, da es eine lokale Berechnung ist.
|
||||
def token_count(text, model=None):
|
||||
"""Zaehlt Tokens via tiktoken oder schaetzt ueber Leerzeichen."""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist (im Root Logger)
|
||||
logger = logging.getLogger(__name__) # Logger-Instanz holen
|
||||
if not text or not isinstance(text, str): return 0
|
||||
|
||||
# ... (Rest der token_count Funktion wie in Block 3/36 gesendet) ...
|
||||
current_model = model if model else getattr(Config, 'TOKEN_MODEL', 'gpt-3.5-turbo')
|
||||
|
||||
if tiktoken:
|
||||
try:
|
||||
# Cache encoding object per model
|
||||
if not hasattr(token_count, 'enc_cache'):
|
||||
token_count.enc_cache = {}
|
||||
if current_model not in token_count.enc_cache:
|
||||
token_count.enc_cache[current_model] = tiktoken.encoding_for_model(current_model)
|
||||
if not hasattr(token_count, 'enc_cache'): token_count.enc_cache = {}
|
||||
if current_model not in token_count.enc_cache: token_count.enc_cache[current_model] = tiktoken.encoding_for_model(current_model)
|
||||
enc = token_count.enc_cache[current_model]
|
||||
return len(enc.encode(text))
|
||||
except Exception as e:
|
||||
# Logge Fehler auf Debug, da dies ein Fallback ist
|
||||
logger.debug(f"Fehler beim Token-Counting mit tiktoken fuer Modell '{current_model}': {e} - Fallback zur Schaetzung.")
|
||||
# Fallback zur Schaetzung
|
||||
return len(str(text).split()) # Sicherstellen, dass text ein String ist
|
||||
return len(str(text).split())
|
||||
else:
|
||||
# Fallback Schaetzung
|
||||
return len(str(text).split()) # Sicherstellen, dass text ein String ist
|
||||
return len(str(text).split())
|
||||
|
||||
|
||||
# --- Logging Helpers ---
|
||||
# Erstellt den Logdateinamen.
|
||||
# LOG_FILE ist global definiert und wird in main() gesetzt (Block 34)
|
||||
LOG_FILE = None # Initialisierung, falls noch nicht geschehen
|
||||
LOG_FILE = None # Initialisierung
|
||||
|
||||
def create_log_filename(mode):
|
||||
"""Erstellt einen zeitgestempelten Logdateinamen im LOG_DIR."""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist (print am Anfang von main)
|
||||
log_dir_path = LOG_DIR # Nutzt die globale Konstante (Block 1)
|
||||
|
||||
# Erstelle das Log-Verzeichnis, falls es nicht existiert
|
||||
logger = logging.getLogger(__name__) # Logger-Instanz holen
|
||||
log_dir_path = LOG_DIR
|
||||
# ... (Rest der create_log_filename Funktion wie in Block 3/36 gesendet) ...
|
||||
if not os.path.exists(log_dir_path):
|
||||
try:
|
||||
# exist_ok=True verhindert Fehler, wenn das Verzeichnis bereits existiert
|
||||
os.makedirs(log_dir_path, exist_ok=True)
|
||||
# Logge die Erstellung des Verzeichnisses
|
||||
logger.info(f"Log-Verzeichnis '{log_dir_path}' erstellt.")
|
||||
except Exception as e:
|
||||
# Logge Fehler auf Error-Level
|
||||
logger.error(f"FEHLER: Konnte Log-Verzeichnis '{log_dir_path}' nicht erstellen: {e}")
|
||||
# Versuche, die Datei im aktuellen Verzeichnis zu erstellen, wenn LOG_DIR fehlschlaegt
|
||||
log_dir_path = "." # Fallback Verzeichnis
|
||||
log_dir_path = "."
|
||||
logger.warning(f"Versuche, Logdatei im aktuellen Verzeichnis '{log_dir_path}' zu erstellen.")
|
||||
|
||||
# Erstelle den Dateinamen mit Zeitstempel und Version
|
||||
try:
|
||||
now = datetime.now().strftime("%d-%m-%Y_%H-%M")
|
||||
# Sicherstellen, dass Config.VERSION verfuegbar ist (Block 1), Fallback falls nicht
|
||||
ver_short = getattr(Config, 'VERSION', 'unknown').replace(".", "")
|
||||
filename = f"{now}_{ver_short}_Modus{mode}.txt"
|
||||
# Gebe den vollstaendigen Pfad zurueck
|
||||
return os.path.join(log_dir_path, filename)
|
||||
except Exception as e_fallback:
|
||||
# Logge Fehler bei der Dateinamen-Erstellung
|
||||
logger.error(f"FEHLER: Konnte Logdateinamen auch im Fallback-Verzeichnis '{log_dir_path}' nicht erstellen: {e_fallback}")
|
||||
# Signalisiere Fehler durch Rueckgabe von None
|
||||
return None
|
||||
|
||||
|
||||
# debug_print ist nicht mehr notwendig, da wir das Standard-Logging nutzen.
|
||||
# Alle bisherigen Aufrufe von debug_print werden durch logger.debug, logger.info, logger.warning, logger.error, logger.critical ersetzt.
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Ende Grundlegende Helfer Block
|
||||
# ==============================================================================
|
||||
@@ -464,6 +376,7 @@ def create_log_filename(mode):
|
||||
def simple_normalize_url(url):
|
||||
"""Normalisiert URL zu domain.tld oder k.A. (ohne www, ohne Pfad)."""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if not url or not isinstance(url, str): return "k.A."
|
||||
url = url.strip()
|
||||
# Pruefe auf Kleinbuchstaben "k.A." und leere Strings nach dem Strippen
|
||||
@@ -531,6 +444,7 @@ def simple_normalize_url(url):
|
||||
def normalize_string(s):
|
||||
"""Normalisiert Umlaute und Sonderzeichen nach einer definierten Liste."""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if not s or not isinstance(s, str): return ""
|
||||
# Ersetzungen fuer gaengige deutsche Umlaute und Sonderzeichen
|
||||
replacements = { 'Ä': 'Ae', 'Ö': 'Oe', 'Ü': 'Ue', 'ß': 'ss', 'ä': 'ae', 'ö': 'oe', 'ü': 'ue',
|
||||
@@ -565,6 +479,7 @@ def clean_text(text):
|
||||
Entfernt gaengige unerwuenschte Muster wie [1], [Bearbeiten].
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if text is None: return "k.A." # Behandle None explizit
|
||||
try:
|
||||
text = str(text) # Sicherstellen, dass es ein String ist
|
||||
@@ -684,6 +599,7 @@ def extract_numeric_value(raw_value, is_umsatz=False):
|
||||
und gaengige Praefixe/Suffixe. Gibt "k.A." zurueck, wenn nicht extrahierbar oder <= 0.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if not raw_value: return "k.A."
|
||||
raw_value_str = str(raw_value).strip()
|
||||
# Pruefe auf bekannte "keine Angabe" Strings oder 0 als Text
|
||||
@@ -776,6 +692,7 @@ def get_numeric_filter_value(value_str, is_umsatz=False):
|
||||
Beachtet Einheiten (Tsd, Mio, Mrd) fuer Umsatz.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if value_str is None or pd.isna(value_str) or str(value_str).strip() == '':
|
||||
# Gibt 0 (int/float) zurueck, nicht "k.A." fuer Filterlogik
|
||||
return 0.0 if is_umsatz else 0
|
||||
@@ -891,6 +808,7 @@ except Exception as e:
|
||||
def get_gender(firstname):
|
||||
"""Ermittelt Geschlecht via gender-guesser und Fallback Genderize API."""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if not firstname or not isinstance(firstname, str): return "unknown"
|
||||
# Nehmen Sie nur den ersten Teil des Vornamens und bereinigen Sie ihn
|
||||
firstname_clean = str(firstname).strip().split(" ")[0]
|
||||
@@ -970,6 +888,7 @@ def get_email_address(firstname, lastname, website):
|
||||
Normalisiert Namen und extrahiert die Domain aus der Website-URL.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if not all([firstname, lastname, website]) or not all(isinstance(x, str) and x.strip() for x in [firstname, lastname, website]): # Pruefen Sie auf nicht-leere Strings
|
||||
logger.debug("get_email_address skipped: Fehlende oder ungueltige Eingabe (Name, Website).")
|
||||
return "" # Gebe leeren String bei fehlenden/ungueltigen Eingaben zurueck
|
||||
@@ -1040,7 +959,7 @@ def load_target_schema(csv_filepath=BRANCH_MAPPING_FILE):
|
||||
csv_filepath (str, optional): Pfad zur CSV-Datei mit dem Branchenschema.
|
||||
Defaults to the global BRANCH_MAPPING_FILE.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Zugriff auf die globalen Variablen
|
||||
global BRANCH_MAPPING, TARGET_SCHEMA_STRING, ALLOWED_TARGET_BRANCHES
|
||||
|
||||
@@ -1050,7 +969,7 @@ def load_target_schema(csv_filepath=BRANCH_MAPPING_FILE):
|
||||
allowed_branches_set = set() # Nutzt ein Set, um Duplikate automatisch zu behandeln
|
||||
line_count = 0
|
||||
|
||||
logger.info(f"Lade Ziel-Schema (Kurzformen) aus '{csv_filepath}' Spalte A...")
|
||||
logger.info(f"Lade Ziel-Schema (Kurzformen) aus '{csv_filepath}' Spalte A...") # Jetzt sollte logger definiert sein
|
||||
|
||||
try:
|
||||
# Versuche, die Datei mit UTF-8-BOM-Signatur oder normalem UTF-8 zu oeffnen
|
||||
@@ -1153,6 +1072,7 @@ def call_openai_chat(prompt, temperature=0.3, model=None):
|
||||
Wirft Exception bei API-Fehlern nach Retries.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Pruefen Sie, ob der API Key konfiguriert ist
|
||||
if not Config.API_KEYS.get('openai'):
|
||||
logger.error("Fehler: OpenAI API Key nicht konfiguriert.")
|
||||
@@ -1260,6 +1180,7 @@ def summarize_website_content(raw_text):
|
||||
Wirft Exception bei API-Fehlern nach Retries (von call_openai_chat).
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Pruefe, ob gueltiger Rohtext vorhanden ist (nicht leer oder Standard-Fehlerwerte)
|
||||
if not raw_text or str(raw_text).strip() == "" or str(raw_text).strip().lower() in ["k.a.", "k.a. (nur cookie-banner erkannt)", "k.a. (fehler)"]:
|
||||
logger.debug("summarize_website_content skipped: No valid raw text provided.")
|
||||
@@ -1326,6 +1247,7 @@ def summarize_batch_openai(tasks_data):
|
||||
Wirft Exception bei endgueltigen API-Fehlern nach Retries.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
if not tasks_data: return {} # Gebe leeres Dictionary zurueck, wenn keine Tasks da sind
|
||||
|
||||
# Filtere Tasks, die gueltigen Text haben (nicht leer oder Standard-Fehlerwerte).
|
||||
@@ -1481,6 +1403,7 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg
|
||||
Wirft Exception bei API-Fehlern nach Retries (von call_openai_chat).
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Zugriff auf globale Variablen (befuellt von load_target_schema im Block 6)
|
||||
global ALLOWED_TARGET_BRANCHES, TARGET_SCHEMA_STRING
|
||||
|
||||
@@ -1690,6 +1613,7 @@ def serp_wikipedia_lookup(company_name, website=None, min_score=0.4):
|
||||
Wirft Exception bei API-Fehlern nach Retries.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
serp_key = Config.API_KEYS.get('serpapi')
|
||||
if not serp_key:
|
||||
logger.error("Fehler: SerpAPI Key nicht verfuegbar fuer Wikipedia Lookup.")
|
||||
@@ -1842,6 +1766,7 @@ def serp_website_lookup(company_name):
|
||||
Wirft Exception bei API-Fehlern nach Retries.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
serp_key = Config.API_KEYS.get('serpapi')
|
||||
if not serp_key:
|
||||
logger.error("Fehler: SerpAPI Key nicht verfuegbar fuer Website Lookup.")
|
||||
@@ -1974,6 +1899,7 @@ def search_linkedin_contacts(company_name, website, position_query, crm_kurzform
|
||||
Wirft Exception bei API-Fehlern nach Retries.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
serp_key = Config.API_KEYS.get('serpapi')
|
||||
if not serp_key:
|
||||
logger.error("Fehler: SerpAPI Key nicht verfuegbar fuer LinkedIn Suche.")
|
||||
@@ -2154,6 +2080,7 @@ def get_website_raw(url, max_length=20000, verify_cert=True): # Längeres Defaul
|
||||
Gibt den Rohtext zurück oder einen Fehlerwert ("k.A.", "k.A. (Fehler)", etc.).
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Pruefen Sie auf ungueltige oder leere URLs, inklusive spezifischer Fehlerwerte wie "http:"
|
||||
if not url or not isinstance(url, str) or url.strip().lower() in ["k.a.", "kein artikel gefunden", "fehler bei suche", "http:"]:
|
||||
logger.debug(f"get_website_raw skipped: Ungueltige oder leere URL '{url}'.")
|
||||
@@ -2320,6 +2247,7 @@ def scrape_website_details(url):
|
||||
str: Extrahierte Details als String oder Fehler/k.A.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Pruefen Sie auf ungueltige oder leere URLs
|
||||
if not url or not isinstance(url, str) or url.strip().lower() in ["k.a.", "kein artikel gefunden", "fehler bei suche", "http:"]:
|
||||
logger.debug(f"scrape_website_details skipped: Ungueltige oder leere URL '{url}'.")
|
||||
@@ -2411,6 +2339,7 @@ def alignment_demo(sheet):
|
||||
sheet (gspread.Worksheet): Das Worksheet-Objekt zum Schreiben der Header.
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Stellen Sie sicher, dass COLUMN_MAP die hoechstbenuetigte Spalte AY (Index 50) enthaelt.
|
||||
# Diese Funktion nimmt an, dass COLUMN_MAP komplett und korrekt ist.
|
||||
# Die Listen unten muessen exakt die gleiche Laenge haben wie COLUMN_MAP.
|
||||
|
||||
Reference in New Issue
Block a user