bugfix
This commit is contained in:
@@ -89,27 +89,9 @@ except ImportError:
|
|||||||
print("tiktoken nicht gefunden. Token-Zaehlung wird geschaetzt.") # Debugging-Ausgabe
|
print("tiktoken nicht gefunden. Token-Zaehlung wird geschaetzt.") # Debugging-Ausgabe
|
||||||
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# 2. GLOBALE KONSTANTEN UND KONFIGURATION
|
|
||||||
# (Logisch 'config.py')
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
# --- Dateipfade ---
|
|
||||||
CREDENTIALS_FILE = "service_account.json"
|
|
||||||
API_KEY_FILE = "api_key.txt" # OpenAI
|
|
||||||
SERP_API_KEY_FILE = "serpApiKey.txt"
|
|
||||||
GENDERIZE_API_KEY_FILE = "genderize_API_Key.txt"
|
|
||||||
BRANCH_MAPPING_FILE = "ziel_Branchenschema.csv" # Enthält Zielschema
|
|
||||||
LOG_DIR = "Log"
|
|
||||||
SHEET_ID = "1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" # <<< HINZUFÜGEN
|
|
||||||
|
|
||||||
# --- ML Modell Artefakte ---
|
def load_branch_mapping(file_path=Config.BRANCH_MAPPING_FILE):
|
||||||
MODEL_FILE = "technician_decision_tree_model.pkl"
|
|
||||||
IMPUTER_FILE = "median_imputer.pkl"
|
|
||||||
PATTERNS_FILE_TXT = "technician_patterns.txt" # Alt (Optional beibehalten)
|
|
||||||
PATTERNS_FILE_JSON = "technician_patterns.json" # Neu (Empfohlen)
|
|
||||||
|
|
||||||
def load_branch_mapping(file_path='Branchen.csv'):
|
|
||||||
"""
|
"""
|
||||||
Lädt das Mapping von Detail-Branche zu Branchen-Gruppe aus einer CSV-Datei.
|
Lädt das Mapping von Detail-Branche zu Branchen-Gruppe aus einer CSV-Datei.
|
||||||
Ist extrem robust gegen Kodierungs-, Spaltennamen- und Pfad-Fehler.
|
Ist extrem robust gegen Kodierungs-, Spaltennamen- und Pfad-Fehler.
|
||||||
@@ -164,39 +146,49 @@ def load_branch_mapping(file_path='Branchen.csv'):
|
|||||||
|
|
||||||
|
|
||||||
# --- Globale Konfiguration Klasse ---
|
# --- Globale Konfiguration Klasse ---
|
||||||
|
# ==============================================================================
|
||||||
|
# 2. GLOBALE KONSTANTEN UND KONFIGURATION (ZENTRALISIERT)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
"""Zentrale Konfigurationseinstellungen."""
|
"""Zentrale Konfigurationseinstellungen."""
|
||||||
VERSION = "v1.7.8"
|
# --- Grundkonfiguration ---
|
||||||
LANG = "de" # Sprache fuer Wikipedia etc.
|
VERSION = "v1.7.9" # Version auf 1.7.9 erhöht
|
||||||
# ACHTUNG: SHEET_URL ist hier ein Platzhalter. Ersetzen Sie ihn durch Ihre tatsaechliche URL.
|
LANG = "de"
|
||||||
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" # <<< ERSETZEN SIE DIES!
|
DEBUG = True
|
||||||
MAX_RETRIES = 5 # Anzahl der Versuche (nicht Wiederholungen nach dem ersten Fehler) fuer wiederholbare Fehler
|
HTML_PARSER = "html.parser"
|
||||||
RETRY_DELAY = 10 # Basiswartezeit (Sekunden) fuer Retries (exponentieller Backoff wird im Decorator angewendet)
|
USER_AGENT = 'Mozilla/5.0 (compatible; UnternehmenSkript/1.0; +https://www.example.com/bot)'
|
||||||
REQUEST_TIMEOUT = 20 # Timeout (Sekunden) fuer externe HTTP/API Anfragen (Requests)
|
|
||||||
SIMILARITY_THRESHOLD = 0.65 # Schwelle fuer Namensaaehnlichkeit bei Wikipedia Validierung
|
# --- Dateipfade & IDs ---
|
||||||
DEBUG = True # Detailliertes Logging aktivieren/deaktivieren
|
SHEET_ID = "1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
|
||||||
WIKIPEDIA_SEARCH_RESULTS = 5 # Anzahl Ergebnisse bei Wikipedia Suche ueber Bibliothek
|
SERVICE_ACCOUNT_FILE = "service_account.json"
|
||||||
HTML_PARSER = "html.parser" # Parser fuer BeautifulSoup ('lxml' ist schneller, erfordert aber Installation)
|
TOKEN_FILE = "token.json"
|
||||||
TOKEN_MODEL = "gpt-3.5-turbo" # OpenAI Modell fuer Token-Zaehlung/Chat (Standard fuer die meisten Calls)
|
API_KEY_FILE = "api_key.txt"
|
||||||
USER_AGENT = 'Mozilla/5.0 (compatible; UnternehmenSkript/1.0; +https://www.example.com/bot)' # User-Agent fuer Web Scraping/Requests (Beispiel URL anpassen)
|
SERP_API_KEY_FILE = "serpApiKey.txt"
|
||||||
BRANCH_MAPPING_FILE = "Branchen.csv" # << Sicherstellen, dass dies der korrekte Dateiname ist
|
GENDERIZE_API_KEY_FILE = "genderize_API_Key.txt"
|
||||||
SCHEMA_FILE = "ziel_Branchenschema.csv"
|
SCHEMA_FILE = "ziel_Branchenschema.csv"
|
||||||
MODEL_FILE = 'technician_decision_tree_model.pkl'
|
BRANCH_MAPPING_FILE = "Branchen.csv"
|
||||||
IMPUTER_FILE = 'median_imputer.pkl'
|
LOG_DIR = "Log"
|
||||||
PATTERNS_FILE_JSON = 'technician_patterns.json'
|
|
||||||
|
|
||||||
# --- Konfiguration fuer Batching & Parallelisierung ---
|
# --- ML Modell Artefakte ---
|
||||||
# Passen Sie diese Werte an die Leistung Ihres Systems und die API-Limits an.
|
MODEL_FILE = "technician_decision_tree_model.pkl"
|
||||||
PROCESSING_BATCH_SIZE = 20 # Anzahl Zeilen pro Verarbeitungs-Batch (fuer _process_single_row in Batches)
|
IMPUTER_FILE = "median_imputer.pkl"
|
||||||
OPENAI_BATCH_SIZE_LIMIT = 4 # Max. Texte pro OpenAI Call fuer Zusammenfassung (nur fuer summarize_batch_openai)
|
PATTERNS_FILE_JSON = "technician_patterns.json"
|
||||||
MAX_SCRAPING_WORKERS = 10 # Threads fuer paralleles Website-Scraping
|
|
||||||
UPDATE_BATCH_ROW_LIMIT = 50 # Zeilen sammeln fuer gebuendelte Sheet Updates (effizienter)
|
|
||||||
MAX_BRANCH_WORKERS = 10 # Threads fuer parallele Branchenbewertung
|
|
||||||
OPENAI_CONCURRENCY_LIMIT = 3 # Max. gleichzeitige OpenAI Calls (Semaphore fuer Branch Evaluation)
|
|
||||||
PROCESSING_BRANCH_BATCH_SIZE = 20 # Batch-Groesse fuer Branch-Evaluierung
|
|
||||||
SERPAPI_DELAY = 1.5 # Pause zwischen einzelnen SerpAPI-Aufrufen (Sekunden)
|
|
||||||
|
|
||||||
|
# --- OpenAI & API Konfiguration ---
|
||||||
|
TOKEN_MODEL = "gpt-3.5-turbo"
|
||||||
|
MAX_RETRIES = 5
|
||||||
|
RETRY_DELAY = 10
|
||||||
|
REQUEST_TIMEOUT = 20
|
||||||
|
SERPAPI_DELAY = 1.5
|
||||||
|
|
||||||
|
# --- Batching & Parallelisierung ---
|
||||||
|
MAX_SCRAPING_WORKERS = 10
|
||||||
|
MAX_BRANCH_WORKERS = 10
|
||||||
|
OPENAI_CONCURRENCY_LIMIT = 3
|
||||||
|
UPDATE_BATCH_ROW_LIMIT = 50
|
||||||
|
|
||||||
|
# --- Plausibilitäts-Schwellenwerte ---
|
||||||
PLAUSI_UMSATZ_MIN_WARNUNG = 50000
|
PLAUSI_UMSATZ_MIN_WARNUNG = 50000
|
||||||
PLAUSI_UMSATZ_MAX_WARNUNG = 200000000000
|
PLAUSI_UMSATZ_MAX_WARNUNG = 200000000000
|
||||||
PLAUSI_MA_MIN_WARNUNG_ABS = 1
|
PLAUSI_MA_MIN_WARNUNG_ABS = 1
|
||||||
@@ -207,53 +199,38 @@ class Config:
|
|||||||
PLAUSI_RATIO_UMSATZ_PRO_MA_MAX = 1500000
|
PLAUSI_RATIO_UMSATZ_PRO_MA_MAX = 1500000
|
||||||
PLAUSI_ABWEICHUNG_CRM_WIKI_PROZENT = 30
|
PLAUSI_ABWEICHUNG_CRM_WIKI_PROZENT = 30
|
||||||
|
|
||||||
|
# --- API Schluessel Speicherung (wird zur Laufzeit befüllt) ---
|
||||||
# --- API Schluessel Speicherung (werden in main() geladen) ---
|
|
||||||
API_KEYS = {}
|
API_KEYS = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_api_keys(cls):
|
def load_api_keys(cls):
|
||||||
"""Laedt API-Schluessel aus den definierten Dateien."""
|
"""Laedt API-Schluessel aus den definierten Dateien."""
|
||||||
print("Lade API-Schluessel...")
|
logger = logging.getLogger(cls.__name__) # Logger innerhalb der Methode holen
|
||||||
cls.API_KEYS['openai'] = cls._load_key_from_file(API_KEY_FILE)
|
logger.info("Lade API-Schluessel...")
|
||||||
cls.API_KEYS['serpapi'] = cls._load_key_from_file(SERP_API_KEY_FILE)
|
cls.API_KEYS['openai'] = cls._load_key_from_file(cls.API_KEY_FILE)
|
||||||
cls.API_KEYS['genderize'] = cls._load_key_from_file(GENDERIZE_API_KEY_FILE)
|
cls.API_KEYS['serpapi'] = cls._load_key_from_file(cls.SERP_API_KEY_FILE)
|
||||||
|
cls.API_KEYS['genderize'] = cls._load_key_from_file(cls.GENDERIZE_API_KEY_FILE)
|
||||||
# import openai # <--- DIESER IMPORT IST NUN ENTFERNT
|
|
||||||
|
|
||||||
if cls.API_KEYS.get('openai'):
|
if cls.API_KEYS.get('openai'):
|
||||||
# Stelle sicher, dass das 'openai' Modul hier im Scope ist,
|
|
||||||
# indem wir auf den globalen Import zugreifen.
|
|
||||||
# Da 'openai' schon global importiert wurde (ganz oben im Skript),
|
|
||||||
# ist es hier direkt verfügbar.
|
|
||||||
openai.api_key = cls.API_KEYS['openai']
|
openai.api_key = cls.API_KEYS['openai']
|
||||||
print("OpenAI API Key erfolgreich geladen.")
|
logger.info("OpenAI API Key erfolgreich geladen.")
|
||||||
else:
|
else:
|
||||||
print("WARNUNG: OpenAI API Key konnte nicht geladen werden (Datei fehlt oder ist leer?). OpenAI-Funktionen sind deaktiviert.")
|
logger.warning("WARNUNG: OpenAI API Key konnte nicht geladen werden.")
|
||||||
|
|
||||||
if not cls.API_KEYS.get('serpapi'):
|
|
||||||
print("WARNUNG: SerpAPI Key konnte nicht geladen werden (Datei fehlt oder ist leer?). Bestimmte Suchfunktionen sind deaktiviert.")
|
|
||||||
if not cls.API_KEYS.get('genderize'):
|
|
||||||
print("WARNUNG: Genderize API Key konnte nicht geladen werden (Datei fehlt oder ist leer?). Geschlechtserkennung ist eingeschraenkt.")
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _load_key_from_file(filepath):
|
def _load_key_from_file(filepath):
|
||||||
"""Hilfsfunktion zum Laden eines Schluessels aus einer Datei."""
|
"""Hilfsfunktion zum Laden eines Schluessels aus einer Datei."""
|
||||||
|
logger = logging.getLogger(Config.__name__)
|
||||||
try:
|
try:
|
||||||
# Verwenden Sie "r" fuer Textmodus und geben Sie das Encoding an
|
|
||||||
with open(filepath, "r", encoding="utf-8") as f:
|
with open(filepath, "r", encoding="utf-8") as f:
|
||||||
key = f.read().strip()
|
key = f.read().strip()
|
||||||
if key:
|
if key: return key
|
||||||
return key
|
|
||||||
else:
|
|
||||||
print(f"WARNUNG: Datei '{filepath}' ist leer.")
|
|
||||||
return None
|
return None
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"INFO: API-Schluesseldatei '{filepath}' nicht gefunden.")
|
logger.debug(f"INFO: API-Schluesseldatei '{filepath}' nicht gefunden.")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"FEHLER beim Lesen der Schluesseldatei '{filepath}': {e}")
|
logger.error(f"FEHLER beim Lesen der Schluesseldatei '{filepath}': {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -1212,7 +1189,7 @@ TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar." # String-Repraese
|
|||||||
ALLOWED_TARGET_BRANCHES = [] # Liste der erlaubten Kurzformen
|
ALLOWED_TARGET_BRANCHES = [] # Liste der erlaubten Kurzformen
|
||||||
|
|
||||||
|
|
||||||
def load_target_schema(csv_filepath=BRANCH_MAPPING_FILE):
|
def load_target_schema(csv_filepath=Config.SCHEMA_FILE):
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
global ALLOWED_TARGET_BRANCHES, FOCUS_TARGET_BRANCHES, TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART
|
global ALLOWED_TARGET_BRANCHES, FOCUS_TARGET_BRANCHES, TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART
|
||||||
|
|
||||||
@@ -9337,7 +9314,7 @@ class DataProcessor:
|
|||||||
# logger, pickle, json, os,
|
# logger, pickle, json, os,
|
||||||
# train_test_split, SimpleImputer, DecisionTreeClassifier,
|
# train_test_split, SimpleImputer, DecisionTreeClassifier,
|
||||||
# accuracy_score, classification_report, confusion_matrix, export_text (sklearn).
|
# accuracy_score, classification_report, confusion_matrix, export_text (sklearn).
|
||||||
def train_technician_model(self, model_out=MODEL_FILE, imputer_out=IMPUTER_FILE, patterns_out=PATTERNS_FILE_JSON):
|
def train_technician_model(self, model_out=Config.MODEL_FILE, imputer_out=Config.IMPUTER_FILE, patterns_out=Config.PATTERNS_FILE_JSON):
|
||||||
self.logger.info("Starte Training des Servicetechniker Decision Tree Modells...")
|
self.logger.info("Starte Training des Servicetechniker Decision Tree Modells...")
|
||||||
|
|
||||||
# 1. Daten vorbereiten
|
# 1. Daten vorbereiten
|
||||||
|
|||||||
Reference in New Issue
Block a user