diff --git a/brancheneinstufung.py b/brancheneinstufung.py index c9791783..9730221f 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -1,15 +1,17 @@ #!/usr/bin/env python3 """ -Version: v1.5.6 +Version: v1.5.8 Datum: {aktuelles Datum} Git-Überschrift (max. 100 Zeichen): -v1.5.6: Fallback-Mechanismus in evaluate_branche_chatgpt verbessert +v1.5.8: Externe Branchenzuordnung mittels Mapping verfeinert Git-Änderungsbeschreibung: -- evaluate_branche_chatgpt: Fallback auf CRM-Wert implementiert, wenn ChatGPT-Vorschlag nicht valide ist -- Helper-Funktionen is_valid_branch und branch_matches_target_schema zur Überprüfung der Branchenwerte hinzugefügt -- Fokusbranchen (service provider, hersteller / produzenten, sonstige) bleiben erhalten +- Mapping-Funktion load_branch_mapping() integriert, um aus der CSV "ziel_Branchenschema.csv" automatisch ein Mapping-Dictionary zu erstellen. +- Neue Funktion map_external_branch() implementiert, die den von ChatGPT gelieferten externen Branchenbegriff (nach Normalisierung) in das standardisierte Ziel-Branchenschema überführt. +- In evaluate_branche_chatgpt wird zuerst der ChatGPT-Vorschlag geparst, unerwünschte Präfixe entfernt und über map_external_branch() in den korrekten Standardwert transformiert. +- Optional wird der CRM-Präfix ergänzt, falls der Mapping-Wert kein hierarchisches Trennzeichen enthält. +- Damit wird der externe Input selbstbewusster übernommen, solange er durch das Mapping bestätigt wird. """ @@ -39,7 +41,7 @@ except ImportError: # ==================== KONFIGURATION ==================== class Config: - VERSION = "v1.5.7" + VERSION = "v1.5.8" LANG = "de" CREDENTIALS_FILE = "service_account.json" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" @@ -100,6 +102,47 @@ def simple_normalize_url(url): except Exception as e: return "k.A." +# --------------------------------------------------------------------- +# 1. Mapping-Funktion: Laden der Ziel-Branchenschema-Tabelle aus CSV +# --------------------------------------------------------------------- +def load_branch_mapping(file_path="ziel_Branchenschema.csv"): + """ + Lädt die Mapping-Tabelle mit zwei Spalten: + Spalte A: Externer (Wikipedia-)Brancheneintrag (z. B. "Getränkeabfüllung") + Spalte B: Ziel-Branchenschema (z. B. "Hersteller / Produzenten > Getränke") + Gibt ein Dictionary zurück, das alle Einträge (in Lowercase und normalisiert) enthält. + """ + mapping = {} + try: + with open(file_path, encoding="utf-8") as f: + reader = csv.reader(f) + for row in reader: + if len(row) >= 2: + key = row[0].strip().lower() + value = row[1].strip() + if key and value: + mapping[key] = value + except Exception as e: + debug_print("Fehler beim Laden des Branchen-Mappings: " + str(e)) + return mapping + +# Globales Mapping-Dictionary laden +BRANCH_MAPPING = load_branch_mapping() + + +def map_external_branch(external_branch): + """ + Normalisiert den externen Brancheneintrag und sucht im Mapping-Dictionary nach einer + entsprechenden Übersetzung in das Ziel-Branchenschema. + Falls kein exaktes Mapping gefunden wird, erfolgt eine Teilübereinstimmungsprüfung. + """ + norm = normalize_string(external_branch).lower() + if norm in BRANCH_MAPPING: + return BRANCH_MAPPING[norm] + for key in BRANCH_MAPPING: + if key in norm: + return BRANCH_MAPPING[key] + return norm def process_wiki_batch(main_sheet, data, start_row, end_row): @@ -1458,26 +1501,21 @@ class WikipediaScraper: # ==================== NEUE FUNKTION: Angepasste evaluate_branche_chatgpt ==================== def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien, website_summary): """ - Ordnet ein Unternehmen exakt einer Branche des Ziel-Branchenschemas zu. + Ordnet das Unternehmen anhand externer Quellen und interner Daten exakt einer Branche + des Ziel-Branchenschemas zu. Vorgehen: - 1. Es wird ein aggregierter Prompt mit folgenden Angaben erstellt: - - CRM-Branche - - Externe Beschreibung (z. B. aus der CRM-Beschreibung) - - Wikipedia-Branche - - Wikipedia-Kategorien - - Website-Zusammenfassung - 2. Der Prompt wird an ChatGPT übergeben. Erwartetes Antwortformat: - Branche: - Übereinstimmung: - Begründung: - 3. Nach dem Parsen erfolgt: - a) Falls der ChatGPT‑Vorschlag kein ">" enthält, wird er mit dem Präfix aus crm_branche ergänzt. - b) Anschließend wird mittels Fuzzy Matching die Ähnlichkeit des (extrahierten) Suffix - zwischen dem finalen Vorschlag und dem Suffix des CRM-Werts überprüft. - Liegt die Ähnlichkeit unter 0.75, erfolgt ein Fallback auf den CRM-Wert. - c) Abschließend wird überprüft, ob der Vorschlag den Fokusbranchen entspricht. - 4. Der finale Wert wird zurückgegeben – garantiert als gültiger, dem Branchenschema entsprechender String. + 1. Es wird ein aggregierter Prompt mit den Angaben (CRM-Branche, Beschreibung, Wikipedia-Branche, + Wikipedia-Kategorien, Website-Zusammenfassung) erstellt und an ChatGPT geschickt. + 2. Der von ChatGPT zurückgegebene externe Branchenvorschlag wird geparst. + 3. Unerwünschte Präfixe werden entfernt. + 4. Anschließend wird der externe Vorschlag mittels map_external_branch() in den + standardisierten Zielwert überführt. + 5. Falls der resultierende Standardwert noch kein hierarchisches Trennzeichen ">" enthält, + wird – sofern im CRM-Wert vorhanden – der fehlende Präfix ergänzt. + 6. Der so ermittelte externe Vorschlag wird dann übernommen, sodass der finale Rückgabewert + exakt dem Ziel-Branchenschema entspricht. + Falls kein sinnvoller externer Vorschlag vorliegt, erfolgt der Fallback auf den CRM-Wert. """ debug_print( f"Verwendete Angaben: CRM-Branche='{crm_branche}', externe Beschreibung='{beschreibung}', " @@ -1517,7 +1555,6 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg debug_print("Fehler bei der ChatGPT-Anfrage: " + str(e)) return {"branch": "k.A.", "consistency": "X", "justification": "ChatGPT-Anfrage fehlgeschlagen."} - # Parsen der Antwort suggested_branch = "" consistency = "" justification = "" @@ -1529,40 +1566,26 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg elif line.startswith("Begründung:"): justification = line.split(":", 1)[1].strip() - debug_print(f"Extrahiert: Branche='{suggested_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'") + debug_print(f"Ursprünglicher ChatGPT-Vorschlag: '{suggested_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'") - # Entferne unerwünschte Präfixe (z.B. "CRM-Branche:") falls vorhanden if suggested_branch.lower().startswith("crm-branche"): suggested_branch = suggested_branch.split(":", 1)[-1].strip() - norm_suggested = normalize_string(suggested_branch) - norm_crm = normalize_string(crm_branche) + # Externen Vorschlag über das Mapping in den Zielstandard überführen + mapped_branch = map_external_branch(suggested_branch) + debug_print(f"Nach Mapping erhalten: '{mapped_branch}'") - # Ergänze Hierarchie falls ">" fehlt - if ">" not in norm_suggested: - if crm_branche and crm_branche.lower() != "k.a.": - merged = merge_with_prefix(norm_suggested, norm_crm) - debug_print(f"Ergänzung der Hierarchie: Zusammenführen von CRM-Präfix mit ChatGPT-Vorschlag. Ergebnis: '{merged}'") - norm_suggested = merged + # Hierarchie ergänzen: Falls der resultierende Mappingwert kein ">" enthält, + # wird der Präfix aus dem CRM-Wert übernommen (sofern vorhanden und sinnvoll) + if ">" not in mapped_branch and crm_branche.lower() != "k.a.": + prefix = crm_branche.split(">")[0].strip() if ">" in crm_branche else "" + if prefix: + mapped_branch = prefix + " > " + mapped_branch + debug_print(f"Ergänzung der Hierarchie: Ergebnis: '{mapped_branch}'") - # Fuzzy Matching: Vergleiche den Suffix (also den Teil nach ">") des finalen Vorschlags mit dem CRM-Suffix - crm_suffix = extract_suffix(norm_crm) - suggestion_suffix = extract_suffix(norm_suggested) - similarity = fuzzy_similarity(suggestion_suffix, crm_suffix) - debug_print(f"Fuzzy Matching: Ähnlichkeit zwischen Suffix '{suggestion_suffix}' und '{crm_suffix}' = {similarity:.2f}") - - # Schwellenwert (z. B. 0.75); falls zu geringe Ähnlichkeit, Fallback auf CRM-Wert - if similarity < 0.75: - debug_print("Fuzzy Matching hat eine zu geringe Übereinstimmung ergeben. Fallback: CRM-Wert verwendet.") - return {"branch": crm_branche, "consistency": "ok", "justification": "Fallback: CRM-Wert verwendet."} - - # Überprüfe, ob der (ggf. hierarchisch ergänzte) Vorschlag den Fokusbranchen entspricht - if not branch_matches_target_schema(norm_suggested): - debug_print(f"Vorgeschlagene Branche '{suggested_branch}' (normiert: '{norm_suggested}') entspricht nicht dem Ziel-Branchenschema. Fallback: CRM-Wert verwendet.") - return {"branch": crm_branche, "consistency": "ok", "justification": "Fallback: CRM-Wert verwendet."} - - debug_print(f"Endergebnis Branchenbewertung: Branche='{suggested_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'") - return {"branch": norm_suggested, "consistency": consistency, "justification": justification} + final_branch = mapped_branch + debug_print(f"Endergebnis Branchenbewertung: Branche='{final_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'") + return {"branch": final_branch, "consistency": consistency, "justification": justification} def evaluate_servicetechnicians_estimate(company_name, company_data):