diff --git a/brancheneinstufung.py b/brancheneinstufung.py index eb11c3a7..05ca0fc1 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -201,86 +201,86 @@ COLUMN_MAP = { # CRM-Daten Teil 1 (A-C) "ReEval Flag": 0, "CRM Name": 1, "CRM Kurzform": 2, - # Parent Account Info (D, neue Spalte) + # Parent Account Info (D) "Parent Account Name": 3, # D (NEU) - # CRM-Daten Teil 2 (E-P, alte D-M verschoben um +1) - "CRM Website": 4, # E (vorher D) - "CRM Ort": 5, # F (vorher E) - "CRM Land": 6, # G (vorher F) - "CRM Beschreibung": 7, # H (vorher G) - "CRM Branche": 8, # I (vorher H) - "CRM Beschreibung Branche extern": 9, # J (vorher I) - "CRM Anzahl Techniker": 10, # K (vorher J) - "CRM Umsatz": 11, # L (vorher K) - "CRM Anzahl Mitarbeiter": 12, # M (vorher L) - "CRM Vorschlag Wiki URL": 13, # N (vorher M) + # CRM-Daten Teil 2 (E-N), alte D-M verschoben um +1 + "CRM Website": 4, # E + "CRM Ort": 5, # F + "CRM Land": 6, # G + "CRM Beschreibung": 7, # H + "CRM Branche": 8, # I + "CRM Beschreibung Branche extern": 9, # J + "CRM Anzahl Techniker": 10, # K + "CRM Umsatz": 11, # L + "CRM Anzahl Mitarbeiter": 12, # M + "CRM Vorschlag Wiki URL": 13, # N - # System Vorschlag Parent & Status (O-P, neue Spalten) + # System Vorschlag Parent & Status & Timestamp (O-Q) "System Vorschlag Parent Account": 14, # O (NEU) "Parent Vorschlag Status": 15, # P (NEU) + "Parent Vorschlag Timestamp": 16, # Q (NEU) - # Wikipedia-Daten & -Status (Q-AF, alte N-AB verschoben um +2) - "Wiki URL": 16, # Q (vorher N) - "Wiki Sitz Stadt": 17, # R (vorher O) - "Wiki Sitz Land": 18, # S (vorher P) - "Wiki Absatz": 19, # T (vorher Q) - "Wiki Branche": 20, # U (vorher R) - "Wiki Umsatz": 21, # V (vorher S) - "Wiki Mitarbeiter": 22, # W (vorher T) - "Wiki Kategorien": 23, # X (vorher U) - "Wikipedia Timestamp": 24, # Y (vorher V) - "Wiki Verif. Timestamp": 25, # Z (vorher W) - "SerpAPI Wiki Search Timestamp": 26, # AA (vorher X) - "Chat Wiki Konsistenzpruefung": 27, # AB (vorher Y) - "Chat Begruendung Wiki Inkonsistenz": 28, # AC (vorher Z) - "Chat Vorschlag Wiki Artikel": 29, # AD (vorher AA) - "Begruendung bei Abweichung": 30, # AE (vorher AB) + # Wikipedia-Daten & -Status (R-AG), alte N-AB verschoben um +4 + "Wiki URL": 17, # R + "Wiki Sitz Stadt": 18, # S + "Wiki Sitz Land": 19, # T + "Wiki Absatz": 20, # U + "Wiki Branche": 21, # V + "Wiki Umsatz": 22, # W + "Wiki Mitarbeiter": 23, # X + "Wiki Kategorien": 24, # Y + "Wikipedia Timestamp": 25, # Z + "Wiki Verif. Timestamp": 26, # AA + "SerpAPI Wiki Search Timestamp": 27, # AB + "Chat Wiki Konsistenzpruefung": 28, # AC + "Chat Begruendung Wiki Inkonsistenz": 29, # AD + "Chat Vorschlag Wiki Artikel": 30, # AE + "Begruendung bei Abweichung": 31, # AF - # Website-Daten (AG-AK, alte AC-AG verschoben um +3) - "Website Rohtext": 31, # AF (vorher AC) - "Website Zusammenfassung": 32, # AG (vorher AD) - "Website Meta-Details": 33, # AH (vorher AE) - "Website Scrape Timestamp": 34, # AI (vorher AF) - "URL Prüfstatus": 35, # AJ (vorher AG) + # Website-Daten (AH-AL), alte AC-AG verschoben um +4 + "Website Rohtext": 32, # AG + "Website Zusammenfassung": 33, # AH + "Website Meta-Details": 34, # AI + "Website Scrape Timestamp": 35, # AJ + "URL Prüfstatus": 36, # AK - # ChatGPT Branchen- & weitere Schätzungen (AL-AX, alte AH-AT verschoben um +3) - "Chat Vorschlag Branche": 36, # AK (vorher AH) - "Chat Branche Konfidenz": 37, # AL (vorher AI) - "Chat Konsistenz Branche": 38, # AM (vorher AJ) - "Chat Begruendung Abweichung Branche": 39, # AN (vorher AK) - "Chat Pruefung FSM Relevanz": 40, # AO (vorher AL) - "Chat Begruendung für FSM Relevanz": 41, # AP (vorher AM) - "Chat Schaetzung Anzahl Mitarbeiter": 42, # AQ (vorher AN) - "Chat Konsistenzprüfung Mitarbeiterzahl": 43, # AR (vorher AO) - "Chat Begruendung Abweichung Mitarbeiterzahl": 44, # AS (vorher AP) - "Chat Einschätzung Anzahl Servicetechniker": 45, # AT (vorher AQ) - "Chat Begruendung Abweichung Anzahl Servicetechniker": 46, # AU (vorher AR) - "Chat Schätzung Umsatz": 47, # AV (vorher AS) - "Chat Begruendung Abweichung Umsatz": 48, # AW (vorher AT) + # ChatGPT Branchen- & weitere Schätzungen (AM-AY), alte AH-AT verschoben um +4 + "Chat Vorschlag Branche": 37, # AL + "Chat Branche Konfidenz": 38, # AM + "Chat Konsistenz Branche": 39, # AN + "Chat Begruendung Abweichung Branche": 40, # AO + "Chat Pruefung FSM Relevanz": 41, # AP + "Chat Begruendung für FSM Relevanz": 42, # AQ + "Chat Schaetzung Anzahl Mitarbeiter": 43, # AR + "Chat Konsistenzprüfung Mitarbeiterzahl": 44, # AS + "Chat Begruendung Abweichung Mitarbeiterzahl": 45, # AT + "Chat Einschätzung Anzahl Servicetechniker": 46, # AU + "Chat Begruendung Abweichung Anzahl Servicetechniker": 47, # AV + "Chat Schätzung Umsatz": 48, # AW + "Chat Begruendung Abweichung Umsatz": 49, # AX - # LinkedIn-Kontakte (AY-BB, alte AU-AX verschoben um +3) - "Linked Serviceleiter gefunden": 49, # AX (vorher AU) - "Linked It-Leiter gefunden": 50, # AY (vorher AV) - "Linked Management gefunden": 51, # AZ (vorher AW) - "Linked Disponent gefunden": 52, # BA (vorher AX) + # LinkedIn-Kontakte (AZ-BC), alte AU-AX verschoben um +4 + "Linked Serviceleiter gefunden": 50, # AY + "Linked It-Leiter gefunden": 51, # AZ + "Linked Management gefunden": 52, # BA + "Linked Disponent gefunden": 53, # BB - # Konsolidierte Werte, Timestamps, Plausi & System (BC-BO) - # Reihenfolge von "Contact Search Timestamp" nach Ihren letzten Wünschen - "Contact Search Timestamp": 53, # BB (war AY in der 64-Spalten-Version) - "Finaler Umsatz (Wiki>CRM)": 54, # BC (war AZ in der 64-Spalten-Version) - "Finaler Mitarbeiter (Wiki>CRM)": 55, # BD (war BA in der 64-Spalten-Version) - "Geschaetzter Techniker Bucket": 56, # BE (war BB in der 64-Spalten-Version) - "Plausibilität Umsatz": 57, # BF (war BC in der 64-Spalten-Version) - "Plausibilität Mitarbeiter": 58, # BG (war BD in der 64-Spalten-Version) - "Plausibilität Umsatz/MA Ratio": 59, # BH (war BE in der 64-Spalten-Version) - "Abweichung Umsatz CRM/Wiki": 60, # BI (war BF in der 64-Spalten-Version) - "Abweichung MA CRM/Wiki": 61, # BJ (war BG in der 64-Spalten-Version) - "Plausibilität Begründung": 62, # BK (war BH in der 64-Spalten-Version) - "Plausibilität Prüfdatum": 63, # BL (war BI in der 64-Spalten-Version) - "Timestamp letzte Pruefung": 64, # BM (ChatGPT Eval TS, war BJ) - "Version": 65, # BN (war BK) - "Tokens": 66, # BO (war BL) + # Timestamps, Konsolidierte Werte, ML & Plausi (BD-BP) + "Contact Search Timestamp": 54, # BC + "Finaler Umsatz (Wiki>CRM)": 55, # BD + "Finaler Mitarbeiter (Wiki>CRM)": 56, # BE + "Geschaetzter Techniker Bucket": 57, # BF + "Plausibilität Umsatz": 58, # BG + "Plausibilität Mitarbeiter": 59, # BH + "Plausibilität Umsatz/MA Ratio": 60, # BI + "Abweichung Umsatz CRM/Wiki": 61, # BJ + "Abweichung MA CRM/Wiki": 62, # BK + "Plausibilität Begründung": 63, # BL + "Plausibilität Prüfdatum": 64, # BM + "Timestamp letzte Pruefung": 65, # BN (ChatGPT Eval TS) + "Version": 66, # BO + "Tokens": 67, # BP } # --- Globale Variablen fuer Branch Mapping (werden von load_target_schema() befuellt) --- @@ -815,13 +815,8 @@ def get_numeric_filter_value(value_str, is_umsatz=False): raw_value_str_original = str(value_str).strip() - if raw_value_str_original.lower() in ['k.a.', 'n/a', '-']: - return 0.0 if is_umsatz else 0 - - # "0" als expliziter String wird hier für Filterzwecke wie 0 behandelt. - # Die Plausi-Logik wird "0" (falls es in die finale Spalte geschrieben wird) - # dann über _get_numeric_value_for_plausi als "unbekannt" interpretieren. - if raw_value_str_original in ['0', '0.0', '0,0', '0.00', '0,000', '0.000']: + # "0"-Strings und "k.A." für Filterlogik als 0 behandeln + if raw_value_str_original.lower() in ['k.a.', 'n/a', '-', '0', '0.0', '0,0', '0.00', '0,000', '0.000']: return 0.0 if is_umsatz else 0 try: @@ -835,24 +830,69 @@ def get_numeric_filter_value(value_str, is_umsatz=False): # Tausendertrenner entfernen (Apostroph, Punkt wenn nicht Dezimal) # Komma zu Punkt für Dezimal machen - num_extraction_str = processed_value.replace("'", "") # Apostrophe immer entfernen + num_extraction_str = processed_value.replace("'", "") - if '.' in num_extraction_str and ',' in num_extraction_str: # Enthält Punkt und Komma + # Fall 1: Enthält Punkt UND Komma (z.B. 1.234,56 oder 1,234.56) + if '.' in num_extraction_str and ',' in num_extraction_str: if num_extraction_str.rfind('.') > num_extraction_str.rfind(','): # US-Stil: 1,234.56 -> Kommas entfernen num_extraction_str = num_extraction_str.replace(',', '') else: # EU-Stil: 1.234,56 -> Punkte entfernen, Komma zu Punkt num_extraction_str = num_extraction_str.replace('.', '').replace(',', '.') - elif ',' in num_extraction_str: # Nur Kommas als Dezimaltrenner: 1234,56 -> 1234.56 + # Fall 2: Enthält nur Komma (z.B. 1234,56) -> Komma zu Punkt + elif ',' in num_extraction_str: num_extraction_str = num_extraction_str.replace(',', '.') - elif '.' in num_extraction_str: # Nur Punkte - # Wenn der String klar ein Tausenderformat hat (z.B. 1.234 oder 1.234.567), Punkte entfernen - if re.fullmatch(r'^\d{1,3}(\.\d{3})+$', num_extraction_str): + # Fall 3: Enthält nur Punkt(e) (z.B. 4.380 oder 123.45 oder 1.234.567) + elif '.' in num_extraction_str: + # Wenn der String klar ein Tausenderformat hat (Endet nicht mit .XX und hat Punkte) + if re.fullmatch(r'^\d{1,3}(\.\d{3})+$', num_extraction_str) and not re.search(r'\.\d{1,2}$', num_extraction_str): num_extraction_str = num_extraction_str.replace('.', '') - # Ansonsten ist ein einzelner Punkt ein Dezimalpunkt (z.B. "123.45") oder es ist ungültig - elif num_extraction_str.count('.') > 1: # Mehrere Punkte nicht im Tausenderformat sind ungültig + # Sonst ist ein einzelner Punkt ein Dezimalpunkt (z.B. "123.45") + # Bei mehreren Punkten, die nicht Tausenderformat sind (z.B. 1.2.3), wird es später als Fehler behandelt. + elif num_extraction_str.count('.') > 1: logger.debug(f"get_numeric_filter_value: Mehrere Punkte in '{num_extraction_str}' (nicht als Tausenderformat erkannt).") return 0.0 if is_umsatz else 0 + match = re.search(r'([\d.\-]+)', num_extraction_str) + if not match: + return 0.0 if is_umsatz else 0 + + num_str_for_float = match.group(1) + + if not num_str_for_float or num_str_for_float == '.' or num_str_for_float == '-' or \ + (num_str_for_float.count('.') > 1) or \ + (num_str_for_float.count('-') > 1) or \ + (num_str_for_float.count('-') == 1 and not num_str_for_float.startswith('-')): + return 0.0 if is_umsatz else 0 + + num_as_float = float(num_str_for_float) # Dies ist der Wert, wie er im String steht (sollte Mio. € sein für Umsatz, wenn keine Einheit) + + scaled_num = num_as_float + original_lower = raw_value_str_original.lower() + + if is_umsatz: # Zielwert soll in Millionen sein + if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower): + scaled_num = num_as_float * 1000.0 # z.B. "2 Mrd" -> num_as_float=2 -> 2000 Mio + elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower): + scaled_num = num_as_float / 1000.0 # z.B. "500 Tsd" -> num_as_float=500 -> 0.5 Mio + # Ansonsten wird angenommen, dass num_as_float bereits den Wert in Mio. € darstellt (z.B. "173" aus CRM) + else: # Mitarbeiter (absolute Zahl) + if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower): + scaled_num = num_as_float * 1000000000.0 + elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower): + scaled_num = num_as_float * 1000000.0 + elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower): + scaled_num = num_as_float * 1000.0 + + return scaled_num if scaled_num > 0 else (0.0 if is_umsatz else 0) + + except ValueError as e: + logger.debug(f"get_numeric_filter_value: ValueError '{e}' bei Konvertierung von '{num_str_for_float if 'num_str_for_float' in locals() else raw_value_str_original[:30]}...'") + return 0.0 if is_umsatz else 0 + except Exception as e_general: + logger.error(f"Unerwarteter Fehler in get_numeric_filter_value für '{raw_value_str_original[:50]}...': {e_general}") + logger.debug(traceback.format_exc()) + return 0.0 if is_umsatz else 0 + # Extrahiere die Zahl (kann jetzt einen Dezimalpunkt enthalten) match = re.search(r'([\d.\-]+)', num_extraction_str) if not match: @@ -2518,10 +2558,10 @@ def alignment_demo(sheet): logger.info("Starte Alignment Demo für das Hauptblatt...") new_headers = [ - [ # Zeile 1: Spaltenname (67 Spalten) - "ReEval Flag", "CRM Name", "CRM Kurzform", - "Parent Account Name", "CRM Website", "CRM Ort", "CRM Land", "CRM Beschreibung", "CRM Branche", "CRM Beschreibung Branche extern", "CRM Anzahl Techniker", "CRM Umsatz", "CRM Anzahl Mitarbeiter", "CRM Vorschlag Wiki URL", - "System Vorschlag Parent Account", "Parent Vorschlag Status", + [ # Zeile 1: Spaltenname (68 Spalten) + "ReEval Flag", "CRM Name", "CRM Kurzform", "Parent Account Name", + "CRM Website", "CRM Ort", "CRM Land", "CRM Beschreibung", "CRM Branche", "CRM Beschreibung Branche extern", "CRM Anzahl Techniker", "CRM Umsatz", "CRM Anzahl Mitarbeiter", "CRM Vorschlag Wiki URL", + "System Vorschlag Parent Account", "Parent Vorschlag Status", "Parent Vorschlag Timestamp", "Wiki URL", "Wiki Sitz Stadt", "Wiki Sitz Land", "Wiki Absatz", "Wiki Branche", "Wiki Umsatz", "Wiki Mitarbeiter", "Wiki Kategorien", "Wikipedia Timestamp", "Wiki Verif. Timestamp", "SerpAPI Wiki Search Timestamp", "Chat Wiki Konsistenzpruefung", "Chat Begründung Wiki Inkonsistenz", "Chat Vorschlag Wiki Artikel", "Begründung bei Abweichung", "Website Rohtext", "Website Zusammenfassung", "Website Meta-Details", "Website Scrape Timestamp", "URL Prüfstatus", "Chat Vorschlag Branche", "Chat Branche Konfidenz", "Chat Konsistenz Branche", "Chat Begruendung Abweichung Branche", "Chat Prüfung FSM Relevanz", "Chat Begründung für FSM Relevanz", "Chat Schätzung Anzahl Mitarbeiter", "Chat Konsistenzprüfung Mitarbeiterzahl", "Chat Begründung Abweichung Mitarbeiterzahl", "Chat Einschätzung Anzahl Servicetechniker", "Chat Begründung Abweichung Anzahl Servicetechniker", "Chat Schätzung Umsatz", "Chat Begründung Abweichung Umsatz", @@ -2531,42 +2571,38 @@ def alignment_demo(sheet): "Plausibilität Prüfdatum", "Timestamp letzte Prüfung", "Version", "Tokens" ], - [ # Zeile 2: Quelle der Daten (67 Spalten) - "CRM", "CRM", "CRM", - "CRM/Manuell", # D: Parent Account Name - "CRM", "CRM", "CRM/Manuell", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", # E-N (alt D-M) - "System", "Manuell/System", # O-P: System Vorschlag Parent, Status - "Wikipediascraper/SerpAPI/ChatGPT/Manuell", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "System", "System", "System", "ChatGPT API", "ChatGPT API", "ChatGPT API", "System/Manuell", # Q-AE - "Web Scraper", "ChatGPT API", "Web Scraper", "System", "System/Web Scraper", # AF-AJ - "ChatGPT API", "ChatGPT API", "System/ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", "System/ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", # AK-AW - "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", # AX-BA - "System", # BB: Contact Search Timestamp - "Skript (Wiki/CRM Logik)", "Skript (Wiki/CRM Logik)", "ML Modell / Skript", # BC-BE - "Skript (Plausi-Check)", "Skript (Plausi-Check)", "Skript (Plausi-Check)", "Skript (Plausi-Check)", "Skript (Plausi-Check)", "Skript (Plausi-Check)", # BF-BK - "System (Plausi-Check TS)", # BL - "System", "System", "System" # BM-BO + [ # Zeile 2: Quelle der Daten (68 Spalten) + "CRM", "CRM", "CRM", "CRM/Manuell", + "CRM", "CRM", "CRM/Manuell", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", + "System", "Manuell/System", "System", + "Wikipediascraper/SerpAPI/ChatGPT/Manuell", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "System", "System", "System", "ChatGPT API", "ChatGPT API", "ChatGPT API", "System/Manuell", + "Web Scraper", "ChatGPT API", "Web Scraper", "System", "System/Web Scraper", + "ChatGPT API", "ChatGPT API", "System/ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", "System/ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", "ChatGPT API", + "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", "LinkedIn (via SerpApi)", + "System", "Skript (Wiki/CRM Logik)", "Skript (Wiki/CRM Logik)", "ML Modell / Skript", + "Skript (Plausi-Check)", "Skript (Plausi-Check)", "Skript (Plausi-Check)", "Skript (Plausi-Check)", "Skript (Plausi-Check)", "Skript (Plausi-Check)", + "System (Plausi-Check TS)", + "System", "System", "System" ], - [ # Zeile 3: Feldkategorie (67 Spalten) - "Prozess", "Firmenname", "Firmenname", - "Konzernstruktur", # D - "Website", "Ort", "Land", "Beschreibung (Text)", "Branche", "Branche", "Anzahl Servicetechniker", "Umsatz", "Anzahl Mitarbeiter", "Wikipedia Artikel URL", # E-N - "Konzernstruktur (Vorschlag)", "Konzernstruktur (Status)", # O-P - "Wikipedia Artikel URL", "Ort", "Land", "Beschreibung (Text)", "Branche", "Umsatz", "Anzahl Mitarbeiter", "Kategorien (Text)", "Timestamp", "Timestamp", "Timestamp", "Verifizierung Wiki-Artikel", "Begründung Verifizierung", "Wikipedia Artikel URL (Vorschlag)", "Begründung URL-Abweichung", # Q-AE - "Website-Content", "Website-Content (Zusammenfassung)", "Website-Content (Meta)", "Timestamp", "Prozess-Status", # AF-AJ - "Branche (Vorschlag KI)", "Branche (Konfidenz KI)", "Branche (Konsistenz)", "Branche (Begründung KI)", "FSM Relevanz (KI)", "FSM Relevanz (Begründung KI)", "Anzahl Mitarbeiter (KI)", "Anzahl Mitarbeiter (Konsistenz KI)", "Anzahl Mitarbeiter (Begründung KI)", "Anzahl Servicetechniker (KI)", "Anzahl Servicetechniker (Begründung KI)", "Umsatz (KI)", "Umsatz (Begründung KI)", # AK-AW - "Kontakte (Anzahl)", "Kontakte (Anzahl)", "Kontakte (Anzahl)", "Kontakte (Anzahl)", # AX-BA - "Timestamp", # BB - "Umsatz (Konsolidiert)", "Anzahl Mitarbeiter (Konsolidiert)", "Anzahl Servicetechniker (Bucket ML)", # BC-BE - "Plausibilität", "Plausibilität", "Plausibilität", "Datenqualitäts-Indikator", "Datenqualitäts-Indikator", "Plausibilität (Text)", # BF-BK - "Timestamp (Plausi)", # BL - "Timestamp", "Skript Version", "API Tokens" # BM-BO + [ # Zeile 3: Feldkategorie (68 Spalten) + "Prozess", "Firmenname", "Firmenname", "Konzernstruktur", + "Website", "Ort", "Land", "Beschreibung (Text)", "Branche", "Branche", "Anzahl Servicetechniker", "Umsatz", "Anzahl Mitarbeiter", "Wikipedia Artikel URL", + "Konzernstruktur (Vorschlag)", "Konzernstruktur (Status)", "Timestamp", + "Wikipedia Artikel URL", "Ort", "Land", "Beschreibung (Text)", "Branche", "Umsatz", "Anzahl Mitarbeiter", "Kategorien (Text)", "Timestamp", "Timestamp", "Timestamp", "Verifizierung Wiki-Artikel", "Begründung Verifizierung", "Wikipedia Artikel URL (Vorschlag)", "Begründung URL-Abweichung", + "Website-Content", "Website-Content (Zusammenfassung)", "Website-Content (Meta)", "Timestamp", "Prozess-Status", + "Branche (Vorschlag KI)", "Branche (Konfidenz KI)", "Branche (Konsistenz)", "Branche (Begründung KI)", "FSM Relevanz (KI)", "FSM Relevanz (Begründung KI)", "Anzahl Mitarbeiter (KI)", "Anzahl Mitarbeiter (Konsistenz KI)", "Anzahl Mitarbeiter (Begründung KI)", "Anzahl Servicetechniker (KI)", "Anzahl Servicetechniker (Begründung KI)", "Umsatz (KI)", "Umsatz (Begründung KI)", + "Kontakte (Anzahl)", "Kontakte (Anzahl)", "Kontakte (Anzahl)", "Kontakte (Anzahl)", + "Timestamp", "Umsatz (Konsolidiert)", "Anzahl Mitarbeiter (Konsolidiert)", "Anzahl Servicetechniker (Bucket ML)", + "Plausibilität", "Plausibilität", "Plausibilität", "Datenqualitäts-Indikator", "Datenqualitäts-Indikator", "Plausibilität (Text)", + "Timestamp (Plausi)", + "Timestamp", "Skript Version", "API Tokens" ], - [ # Zeile 4: Kurze Beschreibung (67 Spalten - JETZT AUSFÜHRLICH) + [ # Zeile 4: Kurze Beschreibung (68 Spalten - JETZT AUSFÜHRLICH) "Systemspalte, irrelevant für den Prompt. Wird genutzt um die manuelle Neuprüfung dieses Accounts durchzuführen.", #A ReEval Flag "Enthält den Firmennamen nach bestem Gewissen. Firmennamen sind manchmal herausfordernd, insbesondere was unterschiedliche Schreibweisen, Firmierung, Tochter/Mutterfirmen etc. anbelangt. Zur besseren Trefferquote in der Wikipedia-Suche normalisieren wir den Firmennamen und entfernen sämtliche Firmenformen, wie z.B. AG, GmbH, SE etc.", #B CRM Name "Enthält eine manuell gepflegte (normalisierte) Kurzform des Firmennamens, wie auch ein Mensch die Firma nennen würde. Dies bedeutet insbesondere, dass die Firmenform wie z.B. GmbH oder AG aus dem Namen entfernt wird. Meist entspricht die Kurzform den ersten beiden Worten des Firmennamens. Manchmal sind auch Worte nötig, wenn die ersten beiden worte zu wenig Aussagekraft haben. Beispiele dafür sind beispielsweise Firmen wie 'Schmidt & Söhne', bei denen 'Schmidt &' wenig Sinn machen würde, oder 'Philip Morris Tabakwaren' - weil in diesem Fall 'Philip Morris' zu generisch wäre bzw. wenig eindeutig.", #C CRM Kurzform - "Name der direkten Muttergesellschaft / des Hauptkonzerns (falls zutreffend). Manuell gepflegt oder aus CRM. Beeinflusst Konsolidierung und Plausi-Checks.", #D Parent Account Name (NEU) - "Von uns ermittelte Website des Unternehmens, sofern verfügbar. Kann durch 'website_lookup' oder 'check_urls' aktualisiert werden.", #E CRM Website + "Name der direkten Muttergesellschaft / des Hauptkonzerns (falls zutreffend). Manuell gepflegt oder aus CRM. Beeinflusst Konsolidierung und Plausi-Checks.", #D Parent Account Name + "Von uns ermittelte Website des Unternehmens, sofern verfügbar.", #E CRM Website "von uns ermittelter Ort des Unternehmens", #F CRM Ort "Land des Unternehmenssitzes laut CRM oder manueller Recherche. Wichtig für regionale Analysen (z.B. DACH).", #G CRM Land "Kurze Beschreibung der Haupttätigkeit des Unternehmens aus dem CRM-System. Dient als Input für KI-Analysen.", #H CRM Beschreibung @@ -2575,62 +2611,63 @@ def alignment_demo(sheet): "Bekannte Anzahl der Servicetechniker des Unternehmens (aus CRM oder Recherche). Dient als Ground Truth für ML.", #K CRM Anzahl Techniker "Umsatz des Unternehmens in Millionen Euro laut CRM oder Recherche.", #L CRM Umsatz "Anzahl der Mitarbeiter des Unternehmens laut CRM oder Recherche.", #M CRM Anzahl Mitarbeiter - "Enthält aus einer alten Recherche Vorschläge für die Wikipedia URL zum Unternehmen. Dieser muss aber nicht stimmen. Sollte als Ausgangs- und Vergleichspunkt für die nachgelagerte Wikipedia-Suche dienen.", #N CRM Vorschlag Wiki URL - "Vom System heuristisch ermittelter Vorschlag für den Parent Account (basierend auf Namensähnlichkeiten, Wiki-Infos etc.).", #O System Vorschlag Parent Account (NEU) - "Status des System-Vorschlags für Parent Account (z.B. 'x' für akzeptiert, '-' für abgelehnt, '?' für unklar zur manuellen Prüfung).", #P Parent Vorschlag Status (NEU) - "Finale Wikipedia URL des Unternehmens. Quelle: Wikipedia-Scraper (Suche/Extraktion), SerpAPI (Neusuche), ChatGPT (Vorschlag AD nach Prüfung), oder manuelle Eingabe.", #Q Wiki URL - "Aus Wikipedia-Infobox extrahierte Stadt des Unternehmenssitzes.", #R Wiki Sitz Stadt - "Aus Wikipedia-Infobox extrahiertes Land des Unternehmenssitzes.", #S Wiki Sitz Land - "Erster aussagekräftiger Absatz des Wikipedia-Artikels (Q).", #T Wiki Absatz - "Aus der Wikipedia-Infobox extrahierte Branche(n).", #U Wiki Branche - "Aus der Wikipedia-Infobox extrahierter Umsatz (normalisiert in Mio. €).", #V Wiki Umsatz - "Aus der Wikipedia-Infobox extrahierte Mitarbeiterzahl (normalisiert).", #W Wiki Mitarbeiter - "Komma-separierte Liste der Kategorien, denen der Artikel in Wikipedia zugewiesen wurde. Hier ist auch häufig eine Branche enthalten, häufig auch noch weitere Informationen etwa zur Gründung, ob sie etwa im DAX gelistet ist etc. Guter Anhaltspunkt zur Differenzierung von Unternehmenseinträgen und Wikipedia-Seiten, die kein Unternehmen beschreiben und fälschlicherweise zugewiesen wurden. Bei jeder Unternehmensseite MUSS das Wort unternehmen in irgendeiner Art und Weise vorkommen. NEGATIVSIGNAL: EHEMALIGES UNTERNEHMEN -> Weist darauf hin, dass das Unternehmen nicht mehr besteht.", #X Wiki Kategorien - "Zeitstempel der letzten Wikipedia-Suche und Datenextraktion für diese Zeile (für Spalten Q-X).", #Y Wikipedia Timestamp - "Zeitstempel der letzten Wikipedia-Artikel-Verifizierung durch ChatGPT (Ergebnis in Spalten AB-AD).", #Z Wiki Verif. Timestamp - "Zeitstempel des letzten Versuchs, eine fehlende Wiki-URL (Q) über SerpAPI zu suchen.", #AA SerpAPI Wiki Search Timestamp - "Ergebnis der ChatGPT-Prüfung, ob der Wiki-Artikel (Q) zum Unternehmen passt. ('OK', 'X', '?', 'FEHLER', etc.)", #AB Chat Wiki Konsistenzpruefung - "Von ChatGPT generierte Begründung, falls der Wiki-Artikel (Q) als inkonsistent zum Unternehmen bewertet wurde.", #AC Chat Begründung Wiki Inkonsistenz - "Von ChatGPT vorgeschlagene alternative Wikipedia-URL, falls der Artikel in Q als unpassend bewertet wurde.", #AD Chat Vorschlag Wiki Artikel - "Wird derzeit primär geleert. Ursprünglich für Begründung bei Abweichung CRM Vorschlag Wiki URL (N alt) vs. gefundener Wiki-URL (Q).", #AE Begründung bei Abweichung - "Roh extrahierter Textinhalt der Website. Basis für Zusammenfassung. Kann auch Fehlermeldungen oder Marker wie 'URL_CHECK_NEEDED' enthalten.", #AF Website Rohtext - "KI-generierte Zusammenfassung des Website-Rohtextes (AF).", #AG Website Zusammenfassung - "Extrahierte Meta-Daten der Website: Title, Meta-Description, H1-H3 Überschriften etc.", #AH Website Meta-Details - "Zeitstempel des letzten Website-Scraping/Summarization-Versuchs (für AF, AG, AH).", #AI Website Scrape Timestamp - "Status der URL-Prüfung (z.B. 'URL_CHECK_NEEDED', 'URL_OK', 'FEHLER_SSL'). Wird von 'check_urls' Modus gesetzt/genutzt.", #AJ URL Prüfstatus - "Branchenvorschlag von ChatGPT basierend auf allen verfügbaren Informationen.", #AK Chat Vorschlag Branche - "Konfidenz des ChatGPT-Branchenvorschlags (AK), z.B. Hoch/Mittel/Niedrig.", #AL Chat Branche Konfidenz - "Konsistenzprüfung: 'ok', wenn ChatGPT-Vorschlag (AK) mit CRM-Branche (I) übereinstimmt (unter Berücksichtigung von Parent-Logik).", #AM Chat Konsistenz Branche - "Von ChatGPT generierte Begründung für den Branchenvorschlag (AK) oder bei Abweichung.", #AN Chat Begruendung Abweichung Branche - "Bewertung der FSM-Relevanz durch ChatGPT. ('OK' für relevant, 'X' für irrelevant) (Platzhalter)", #AO Chat Prüfung FSM Relevanz - "Von ChatGPT generierte Begründung für die Einschätzung der FSM-Relevanz. (Platzhalter)", #AP Chat Begründung für FSM Relevanz - "Von ChatGPT geschätzte Anzahl der Mitarbeiter. (Platzhalter)", #AQ Chat Schaetzung Anzahl Mitarbeiter - "Konsistenzprüfung der von ChatGPT geschätzten Mitarbeiterzahl (AQ). (Platzhalter)", #AR Chat Konsistenzprüfung Mitarbeiterzahl - "Von ChatGPT generierte Begründung bei signifikanter Abweichung der geschätzten Mitarbeiterzahl. (Platzhalter)", #AS Chat Begründung Abweichung Mitarbeiterzahl - "Anzahl der Servicetechniker geschätzt durch Chat GPT (z.B. 0, <50, >100). (Platzhalter)", #AT Chat Einschätzung Anzahl Servicetechniker - "Begründung für Abweichungen der ChatGPT-Technikerschätzung zur CRM-Angabe. (Platzhalter)", #AU Chat Begründung Abweichung Anzahl Servicetechniker - "Umsatz durch ChatGPT geschätzt. (Platzhalter)", #AV Chat Schätzung Umsatz - "Begründung für Abweichungen des ChatGPT-Umsatzes zu CRM/Wiki. (Platzhalter)", #AW Chat Begründung Abweichung Umsatz - "Anzahl der Kontakte die zur Suche 'Serviceleiter', 'Leiter Service', 'technischer Leiter', 'Service Manager', 'Leiter Kundendienst' gefunden wurden.", #AX Linked Serviceleiter gefunden - "Anzahl der Kontakte die zur Suche 'Leiter IT', 'IT Leiter', 'Head of IT', 'IT-Leiter', 'CIO' gefunden wurden.", #AY Linked It-Leiter gefunden - "Anzahl der Kontakte die zur Suche 'Geschäftsführer', 'Geschäftsführung', 'GF', 'CEO', 'Geschäftsführerin', 'Managing Director', 'Geschäftsführender Gesellschafter' gefunden wurden.", #AZ Linked Management gefunden - "Anzahl der Kontakte die zur Suche 'Disponent', 'Einsatzplaner' gefunden wurden.", #BA Linked Disponent gefunden - "Timestamp des Zeitpunkts zu dem die Kontaktsuche via SerpAPI für diese Zeile fertiggestellt wurde.", #BB Contact Search Timestamp - "Konsolidierter Umsatzwert in Millionen Euro. Priorisiert Wiki (V) > CRM (L). Berücksichtigt Parent-Account (D).", #BC Finaler Umsatz (Wiki>CRM) - "Konsolidierte Mitarbeiterzahl (absolut). Priorisiert Wiki (W) > CRM (M). Berücksichtigt Parent-Account (D).", #BD Finaler Mitarbeiter (Wiki>CRM) - "Ergebnis der Schätzung durch das trainierte Machine-Learning-Modell (Techniker-Bucket).", #BE Geschaetzter Techniker Bucket - "Plausibilitätsstatus für den finalen Umsatzwert (BC) (z.B. OK, WARNUNG_HOCH, FEHLER_FORMAT).", #BF Plausibilität Umsatz - "Plausibilitätsstatus für die finale Mitarbeiterzahl (BD) (z.B. OK, WARNUNG_NIEDRIG).", #BG Plausibilität Mitarbeiter - "Plausibilitätsstatus für die Umsatz-pro-Mitarbeiter-Ratio (BC/BD).", #BH Plausibilität Umsatz/MA Ratio - "Indikator für Abweichung (>30%) zwischen CRM-Umsatz (L) und Wiki-Umsatz (V). Berücksichtigt Parent-Logik.", #BI Abweichung Umsatz CRM/Wiki - "Indikator für Abweichung (>30%) zwischen CRM-MA (M) und Wiki-MA (W). Berücksichtigt Parent-Logik.", #BJ Abweichung MA CRM/Wiki - "Gesammelte Begründungen für Plausibilitätswarnungen oder -fehler aus den Spalten BF-BJ.", #BK Plausibilität Begründung - "Zeitstempel des letzten Laufs der Plausibilitäts-Checks für diese Zeile.", #BL Plausibilität Prüfdatum - "Timestamp des Zeitpunkts zu dem die übergreifende Validierung/Schätzung durch ChatGPT (AK-AW) durchgeführt wurde.", #BM Timestamp letzte Prüfung - "Systemspalte zur Ausgabe der Skriptversion die das Ergebnis generiert hat.", #BN Version - "Zeigt an, wie viele Tokens für die OpenAI API-Requests dieser Zeile ungefähr benötigt wurden." #BO Tokens + "Enthält aus einer alten Recherche Vorschläge für die Wikipedia URL zum Unternehmen. Dieser muss aber nicht stimmen. Sollte als Ausgangs- und Vergleichspunkt für die nachgelagerte Wikipedia-Suche dienen. Der Wert soll mit den üblichen Methoden geprüft werden z.B. kommt die normalisierte Website vor, Ähnlichkeitsprüfung des Firmennamens mit dem Artikelnamen von Wikipedia etc.", #N CRM Vorschlag Wiki URL + "Vom System heuristisch ermittelter Vorschlag für den Parent Account (basierend auf Namensähnlichkeiten, Wiki-Infos etc.).", #O System Vorschlag Parent Account + "Status des System-Vorschlags für Parent Account (z.B. 'x' für akzeptiert, '-' für abgelehnt, '?' für unklar zur manuellen Prüfung).", #P Parent Vorschlag Status + "Zeitstempel der letzten Generierung/Änderung des Parent-Vorschlags/-Status.", #Q Parent Vorschlag Timestamp + "Wikipedia URL aus der Recherche im laufenden Prozess", #R Wiki URL (alt M) + "Aus Wikipedia-Infobox extrahierte Stadt des Unternehmenssitzes.", #S Wiki Sitz Stadt (neu) + "Aus Wikipedia-Infobox extrahiertes Land des Unternehmenssitzes.", #T Wiki Sitz Land (neu) + "Erster Absatz des Wikipedia-Artikels", #U Wiki Absatz (alt N) + "Branche aus Wikipedia-Artikel soweit verfügbar", #V Wiki Branche (alt O) + "Umsatz aus Wikipediaartikel soweit verfügbar.", #W Wiki Umsatz (alt P) + "Anzahl Mitarbeiter laut Wikipedia sofern verfügbar.", #X Wiki Mitarbeiter (alt Q) + "Komma-separierte Liste der Kategorien, denen der Artikel in Wikipedia zugewiesen wurde. Hier ist auch häufig eine Branche enthalten, häufig auch noch weitere Informationen etwa zur Gründung, ob sie etwa im DAX gelistet ist etc. Guter Anhaltspunkt zur Differenzierung von Unternehmenseinträgen und Wikipedia-Seiten, die kein Unternehmen beschreiben und fälschlicherweise zugewiesen wurden. \nBei jeder Unternehmensseite MUSS das Wort unternehmen in irgendeiner Art und Weise vorkommen.\nNEGATIVSIGNAL: EHEMALIGES UNTERNEHMEN -> Weist darauf hin, dass das Unternehmen nicht mehr besteht.", #Y Wiki Kategorien (alt R) + "Zeitstempel der letzten Wikipedia-Suche und Datenextraktion für diese Zeile (jetzt für Spalten R-Y).", #Z Wikipedia Timestamp (alt AN) + "Zeitstempel der letzten Wikipedia-Artikel-Verifizierung durch ChatGPT (Ergebnis in Spalten AC-AE).", #AA Wiki Verif. Timestamp (alt AX) + "Zeitstempel des letzten Versuchs, eine fehlende Wiki-URL (R) über SerpAPI zu suchen.", #AB SerpAPI Wiki Search Timestamp (alt AY) + "\"OK\" wird bei Firmen eingetragen, wo Firma und Wikipedia-Eintrag zusammenpassen. \"X\" wird bei Firmen eingetragen, wo Firma und Wikipedia-Eintrag nicht zusammenpassen.", #AC Chat Wiki Konsistenzpruefung (alt S) + "Begründung welche Inkonsistenz aus den Daten hervorgeht.", #AD Chat Begründung Wiki Inkonsistenz (alt T) + "URL des durch ChatGPT recherchierten Wikipedia-Artikels", #AE Chat Vorschlag Wiki Artikel (alt U) + "XXX derzeit nicht verwendet, wird vermutlich gelöscht xxx", #AF Begründung bei Abweichung (alt V) + "Roh extrahierter Textinhalt der Firmenwebsite. Basis für Zusammenfassung und KI-Analysen.", #AG Website Rohtext (neu) + "KI-generierte Zusammenfassung des Website-Rohtextes (AG). Input für Branchenbewertung.", #AH Website Zusammenfassung (neu) + "Extrahierte Meta-Daten der Website (Title, Description, H-Tags). Für schnelle Analyse & Validierung.", #AI Website Meta-Details (neu) + "Zeitstempel des letzten Website-Scraping/Summarization-Versuchs (für AG-AI).", #AJ Website Scrape Timestamp (neu) + "Status der URL-Prüfung (z.B. 'URL_CHECK_NEEDED', 'URL_OK', 'FEHLER_SSL'). Wird von 'check_urls' Modus gesetzt/genutzt.", #AK URL Prüfstatus (neu) + "Durch ChatGPT ermittelte Branche des Unternehmens", #AL Chat Vorschlag Branche (alt W) + "Konfidenz des ChatGPT-Branchenvorschlags (AL), z.B. Hoch/Mittel/Niedrig.", #AM Chat Branche Konfidenz (neu) + "\"OK\" wird bei Firmen eingetragen, wo die Einschätzung zur Branche mit der CRM Branche übereinstimmt. \"X\" wird ausgegeben, wenn die Einschätzungen nicht zusammenpassen.", #AN Chat Konsistenz Branche (alt X) + "Begründung für Abweichung der Branche von CRM Branche", #AO Chat Begruendung Abweichung Branche (alt Y) + "\"OK\" wird bei Firmen eingetragen, für die FSM relevant ist, \"X\" für Firmen, für die FSM irrelevant ist.", #AP Chat Prüfung FSM Relevanz (alt Z) + "Begründung für die Beurteilung in Spalte Chat Begründung für FSM Relevanz", #AQ Chat Begründung für FSM Relevanz (alt AA) + "Anzahl der Mitarbeiter durch ChatGPT geschätzt.", #AR Chat Schaetzung Anzahl Mitarbeiter (alt AB) + "\"OK\" wird bei Firmen eingetragen, für die Anzahl der Mitarbeiter grob mit der aus Spalte CRM Anzahl Mitarbeiter bzw. der Spalte Wiki Mitarbeiter übereinstimmt. \"X\" für Firmen, bei denen dies nicht zutrifft.", #AS Chat Konsistenzprüfung Mitarbeiterzahl (alt AC) + "Begründung für Abweichende Mitarbeiterzahl", #AT Chat Begründung Abweichung Mitarbeiterzahl (alt AD) + "Anzahl der Servicetechniker geschätzt durch Chat GPT", #AU Chat Einschätzung Anzahl Servicetechniker (alt AE) + "Begründung für Abweichungen zur Anzahl der Techniker", #AV Chat Begründung Abweichung Anzahl Servicetechniker (alt AF) + "Umsatz durch ChatGTP geschätzt", #AW Chat Schätzung Umsatz (alt AG) + "Begründung für Abweichungen zum Umsatz", #AX Chat Begründung Abweichung Umsatz (alt AH) + "Anzahl der Kontakte die zur Suche 'Serviceleiter', 'Leiter Service', 'technischer Leiter', 'Service Manager', 'Leiter Kundendienst' gefunden wurden", #AY Linked Serviceleiter gefunden (alt AI) + "Anzahl der Kontakte die zur Suche 'Leiter IT', 'IT Leiter', 'Head of IT', 'IT-Leiter', 'CIO' gefunden wurden", #AZ Linked It-Leiter gefunden (alt AJ) + "Anzahl der Kontakte die zur Suche 'Geschäftsführer', 'Geschäftsführung', 'GF', 'CEO', 'Geschäftsführerin', 'Managing Director', 'Geschäftsführender Gesellschafter' gefunden wurden", #BA Linked Management gefunden (alt AK) + "Anzahl der Kontakte die zur Suche 'Disponent', 'Einsatzplaner' gefunden wurden", #BB Linked Disponent gefunden (alt AL) + "Timestamp des Zeitpunkts zu dem die Kontaktsuche fertiggestellt wurde", #BC Contact Search Timestamp (alt AM) + "Konsolidierter Umsatzwert in Millionen Euro. Priorisiert Wiki (W) > CRM (L). Berücksichtigt Parent-Account (D).", #BD Finaler Umsatz (Wiki>CRM) + "Konsolidierte Mitarbeiterzahl (absolut). Priorisiert Wiki (X) > CRM (M). Berücksichtigt Parent-Account (D).", #BE Finaler Mitarbeiter (Wiki>CRM) + "Ergebnis der Schätzung durch das trainierte Machine-Learning-Modell (Techniker-Bucket).", #BF Geschaetzter Techniker Bucket + "Plausibilitätsstatus für den finalen Umsatzwert (BD) (z.B. OK, WARNUNG_HOCH, FEHLER_FORMAT).", #BG Plausibilität Umsatz + "Plausibilitätsstatus für die finale Mitarbeiterzahl (BE) (z.B. OK, WARNUNG_NIEDRIG).", #BH Plausibilität Mitarbeiter + "Plausibilitätsstatus für die Umsatz-pro-Mitarbeiter-Ratio (BD/BE).", #BI Plausibilität Umsatz/MA Ratio + "Indikator für Abweichung (>30%) zwischen CRM-Umsatz (L) und Wiki-Umsatz (W). Berücksichtigt Parent-Logik.", #BJ Abweichung Umsatz CRM/Wiki + "Indikator für Abweichung (>30%) zwischen CRM-MA (M) und Wiki-MA (X). Berücksichtigt Parent-Logik.", #BK Abweichung MA CRM/Wiki + "Gesammelte Begründungen für Plausibilitätswarnungen oder -fehler aus den Spalten BG-BK.", #BL Plausibilität Begründung + "Zeitstempel des letzten Laufs der Plausibilitäts-Checks für diese Zeile.", #BM Plausibilität Prüfdatum + "Timestamp des Zeitpunkts zu dem die Validierung durch ChatGPT durchgeführt wurde", #BN Timestamp letzte Prüfung (alt AO) + "Systemspalte zur Ausgabe der Skriptversion die das Ergebnis generiert hat", #BO Version (alt AP) + "Zeigt an, wie viele Tokens für den Request benötigt wurden" #BP Tokens (alt AQ) ], - [ # Zeile 5: Aufgabe / Funktion (67 Spalten - JETZT AUSFÜHRLICH) + [ # Zeile 5: Aufgabe / Funktion (68 Spalten - JETZT AUSFÜHRLICH) "Datenquelle/Prozesssteuerung: 'x' markiert Zeile für Re-Evaluation im Modus 'reeval'.", #A "Datenquelle: Firmenname aus CRM.", #B "Datenquelle: Manuell gepflegte Kurzform des Firmennamens, primär für API-Suchen (LinkedIn, SerpAPI) und Matching genutzt.", #C @@ -2646,65 +2683,66 @@ def alignment_demo(sheet): "Datenquelle: Mitarbeiterzahl aus CRM (absolut). Input für Konsolidierung, Plausi-Checks und ML.", #M "Datenquelle: Alte/vorgeschlagene Wiki-URL aus CRM. Dient als initialer Input oder Vergleichswert für den Wiki-Prozess.", #N "Ziel/System: Vom Skript generierter Vorschlag für die Muttergesellschaft (basierend auf Heuristiken), zur manuellen Prüfung.", #O - "Prozesssteuerung/Manuell: Bestätigung ('x'), Ablehnung ('-') oder Unklarheit ('?') des System-Vorschlags für den Parent Account. Kann Übernahme in Spalte D steuern.", #P - "Ziel/Quelle: Finale, als relevant erachtete Wikipedia-URL. Quelle: Wikipedia-Scraper, SerpAPI, ChatGPT-Vorschlag (AD), oder manuelle Eingabe.", #Q - "Quelle: Aus Wikipedia-Infobox extrahierte Stadt des Unternehmenssitzes. Ziel: Geografische Analyse.", #R - "Quelle: Aus Wikipedia-Infobox extrahiertes Land des Unternehmenssitzes. Ziel: Geografische Analyse (DACH).", #S - "Quelle: Erster aussagekräftiger Absatz des Wikipedia-Artikels (Q). Input für KI-Analysen (Wiki-Verifizierung, Branchen).", #T - "Quelle: Aus der Wikipedia-Infobox extrahierte Branche(n). Input für KI-Branchenbewertung.", #U - "Quelle: Aus der Wikipedia-Infobox extrahierter Umsatz (in Mio. €). Input für Konsolidierung und Plausi-Checks.", #V - "Quelle: Aus der Wikipedia-Infobox extrahierte Mitarbeiterzahl (absolut). Input für Konsolidierung und Plausi-Checks.", #W - "Quelle: Wikipedia-Kategorien des Artikels (Q). Wichtiger Input für KI-Analysen (Wiki-Verifizierung, Branchenbewertung, Parent-Vorschlag).", #X - "System: Timestamp der letzten Wikipedia-Suche/Datenextraktion (für Spalten Q-X). Steuert Wiederholung.", #Y - "System: Timestamp der letzten ChatGPT-Verifizierung des Wiki-Artikels (Q). Steuert Wiederholung der Verifizierung.", #Z - "System: Timestamp des letzten Versuchs, eine fehlende Wiki-URL (Q) via SerpAPI zu suchen. Steuert Wiederholung.", #AA - "Ziel: Ergebnis der ChatGPT-Konsistenzprüfung für Wiki-Artikel (Q). Status '?' triggert erneute Prüfung.", #AB - "Ziel: Begründung von ChatGPT, falls Wiki-Artikel (Q) als inkonsistent bewertet wurde.", #AC - "Ziel: Von ChatGPT vorgeschlagene alternative Wikipedia-URL. Wird von Modus 'update_wiki_suggestions' verarbeitet.", #AD - "Ziel/Info: Begründung für manuelle oder systemische Abweichungen bei der finalen Wiki-URL-Wahl.", #AE - "Ziel: Rohtext der Website (AF). Input für Zusammenfassung (AG).", #AF - "Ziel: KI-generierte Zusammenfassung des Website-Rohtextes (AF). Input für Branchenbewertung.", #AG - "Ziel: Strukturierte Meta-Daten der Website (Title, Description, H-Tags). Für schnelle Analyse & Validierung.", #AH - "System: Timestamp des letzten Website-Scraping/Summarization-Versuchs (für AF-AH). Steuert Wiederholung.", #AI - "System/Ziel: Status der URL-Prüfung. 'URL_CHECK_NEEDED' triggert Neusuche im Modus 'check_urls'.", #AJ - "Ziel: Von ChatGPT final vorgeschlagene Branche gemäß Zielschema. Hauptziel der Branchenklassifizierung.", #AK - "Ziel: Von ChatGPT eingeschätzte Konfidenz (Hoch/Mittel/Niedrig) für den Branchenvorschlag (AK).", #AL - "Ziel: Ergebnis des Abgleichs ('ok'/'X') zwischen KI-Branchenvorschlag (AK) und CRM-Branche (I).", #AM - "Ziel: Von ChatGPT generierte Begründung für den Branchenvorschlag (AK) und/oder die Konsistenz (AM).", #AN - "Ziel: Ergebnis der FSM-Relevanzprüfung durch ChatGPT. (Platzhalter)", #AO - "Ziel: Begründung von ChatGPT für die FSM-Relevanz. (Platzhalter)", #AP - "Ziel: Von ChatGPT geschätzte Mitarbeiterzahl. (Platzhalter)", #AQ - "Ziel: Konsistenzprüfung der KI-Mitarbeiterschätzung. (Platzhalter)", #AR - "Ziel: Begründung von ChatGPT bei Abweichung der Mitarbeiterschätzung. (Platzhalter)", #AS - "Ziel: Von ChatGPT geschätzte Anzahl Servicetechniker (Buckets). (Platzhalter)", #AT - "Ziel: Begründung von ChatGPT bei Abweichung der Technikerzahl-Schätzung. (Platzhalter)", #AU - "Ziel: Von ChatGPT geschätzter Umsatz. (Platzhalter)", #AV - "Ziel: Begründung von ChatGPT bei Abweichung der Umsatzschätzung. (Platzhalter)", #AW - "Ziel: Anzahl gefundener LinkedIn-Kontakte (Serviceleiter) via SerpAPI.", #AX - "Ziel: Anzahl gefundener LinkedIn-Kontakte (IT-Leiter) via SerpAPI.", #AY - "Ziel: Anzahl gefundener LinkedIn-Kontakte (Management) via SerpAPI.", #AZ - "Ziel: Anzahl gefundener LinkedIn-Kontakte (Disponent) via SerpAPI.", #BA - "System: Timestamp der letzten Kontaktsuche (Modus 'contacts'). Steuert Wiederholung.", #BB - "Ziel: Konsolidierter Umsatz (Wiki > CRM), berücksichtigt Parent-Struktur. Input für ML und Plausi.", #BC - "Ziel: Konsolidierte Mitarbeiterzahl (Wiki > CRM), berücksichtigt Parent-Struktur. Input für ML und Plausi.", #BD - "Ziel: Vom ML-Modell vorhergesagter Bucket für die Anzahl der Servicetechniker.", #BE - "Ziel: Kennzeichnung der Plausibilität des finalen Umsatzwertes (BC).", #BF - "Ziel: Kennzeichnung der Plausibilität der finalen Mitarbeiterzahl (BD).", #BG - "Ziel: Kennzeichnung der Plausibilität der Umsatz-pro-Mitarbeiter-Verhältniszahl.", #BH - "Ziel: Indikator zur Datenqualität bezüglich Umsatz CRM (L) vs. Wiki (V), berücksichtigt Parent.", #BI - "Ziel: Indikator zur Datenqualität bezüglich Mitarbeiter CRM (M) vs. Wiki (W), berücksichtigt Parent.", #BJ - "Ziel: Zusammenfassung der Gründe für erkannte Plausibilitätsprobleme (aus BF-BJ).", #BK - "System: Timestamp des letzten Plausibilitäts-Checks. Steuert Wiederholung des Plausi-Modus.", #BL - "System: Timestamp der letzten übergreifenden ChatGPT-Evaluation (AK-AW). Steuert Wiederholung.", #BM - "System: Skriptversion, die die Zeile zuletzt signifikant bearbeitet hat.", #BN - "System: Verbrauchte Tokens für OpenAI API-Aufrufe für diese Zeile (kumuliert)." #BO + "Prozesssteuerung/Manuell: Bestätigung ('x'), Ablehnung ('-') oder Unklarheit ('?') des System-Vorschlags. Kann Übernahme in Spalte D steuern.", #P + "System: Zeitstempel für die Bearbeitung von Spalte O und P (Parent Vorschlag).", #Q + "Wird durch Wikipedia Scraper bereitgestellt", #R Wiki URL (alt M) + "Quelle: Aus Wikipedia-Infobox extrahierte Stadt des Unternehmenssitzes. Ziel: Geografische Analyse.", #S Wiki Sitz Stadt + "Quelle: Aus Wikipedia-Infobox extrahiertes Land des Unternehmenssitzes. Ziel: Geografische Analyse (DACH).", #T Wiki Sitz Land + "Wird zunächst nicht verwendet, kann möglicherweise in einem späteren Schritt z.B. zum Vergleich mit der CRM Beschreibung genutzt werden, um auf Textähnlichkeit / Übereinstimmende Worte geprüft zu werden und damit eine Validierung des Artikels zum Account sicherzustellen.", #U Wiki Absatz (alt N) + "Wird u.a. zur finalen Ermittlung der Branche im Ziel-Branchenschema genutzt und mit der CRM Branche bzw. CRM Beschreibung Branche Extern verglichen. Stimmen alle drei Einstufungen grob überein, bestärkt dies die urpsrüngliche Einstufung. Laufen diese Branchen weit auseinander, soll, sofern der Wikipedia-Artikel verifiziert ist, die Branche von Wikipedia als zuverlässigste Quelle bewertet werden, danach folgen die CRM Beschreibung Branche Extern und die CRM Branche an dritter Stelle.", #V Wiki Branche (alt O) + "Wird u.a. mit CRM Umsatz zur Validierung des Unternehmens verglichen bzw. zur Bewertung der größe / Einschätzung Anzahl der Techniker bzw. Bewertung der Relevanz für FSM genutzt.", #W Wiki Umsatz (alt P) + "Wird u.a. mit CRM Anzahl Mitarbeiter zur Validierung des Unternehmens verglichen bzw. zur Bewertung der größe / Einschätzung Anzahl der Techniker bzw. Bewertung der Relevanz für FSM genutzt.", #X Wiki Mitarbeiter (alt Q) + "Wenn Wiki Branche nicht gepflegt ist, wird dieses Feld zur finalen Ermittlung der Branche im Ziel-Branchenschema genutzt und mit der CRM Branche bzw. CRM Beschreibung Branche Extern verglichen. Dabei muss aus dem Feld natürlich die Branche ermittelt werden, die auch hier nicht zwingend eingetragen ist. Stimmen alle drei Einstufungen grob überein, bestärkt dies die urpsrüngliche Einstufung. Laufen diese Branchen weit auseinander, soll, sofern der Wikipedia-Artikel verifiziert ist, die Branche von Wikipedia als zuverlässigste Quelle bewertet werden, danach folgen die CRM Beschreibung Branche Extern und die CRM Branche an dritter Stelle.", #Y Wiki Kategorien (alt R) + "System: Timestamp der letzten Wikipedia-Suche/Datenextraktion (für Spalten R-Y). Steuert Wiederholung.", #Z Wikipedia Timestamp + "System: Timestamp der letzten ChatGPT-Verifizierung des Wiki-Artikels (R). Steuert Wiederholung.", #AA Wiki Verif. Timestamp + "System: Timestamp des letzten Versuchs, eine fehlende Wiki-URL (R) via SerpAPI zu suchen. Steuert Wiederholung.", #AB SerpAPI Wiki Search Timestamp + "\"Es soll durch ChatGPT geprüft werden, ob anhand der vorliegenden Daten bestätigt werden kann, dass der Wikipedia-Eintrag sicher das Unternehmen beschreibt. Hierzu können sämtliche Daten miteinander verglichen werden. u.a. stimmt die Website überein, ist der Umsatz in einer ähnlichen größenordnung, passt die mitarbeiterzahl etc. Bei allen daten darf eine gewisse Unschärfe zum Vergleich (+-30%) gelten.\n Es muss teilweise etwas großzügig bewertet werden, insbesondere bei Konzernstrukturen, wo oft Töchter keinen eigenen Wikipedia-Artikel haben, aber quasi am Umsatz der Mutter hängen und damit prinzipiell die gleichen Daten zur Bewertung herangezogen werden können wie für die Mutter.\nAbweichungen sollen in der Spalte Chat Begründung Wiki Inkonsistenz begründet werden.\"", #AC Chat Wiki Konsistenzpruefung (alt S) + "\"Liegt eine Inkonsistenz zwischen gefundenem Wikipedia-Artikel und dem Unternehmen vor, soll dies hier kurz begründet werden.\nWurde der Artikel als unpassend identifiziert, soll Chat GPT selbst einen passenden wikipedia-Artikel zum Unternehmen finden und diesen in Spalte Chat Vorschlag Wiki Artikel ausgeben.\"", #AD Chat Begründung Wiki Inkonsistenz (alt T) + "\"Sollte durch die Wikipedia-Suche kein Artikel gefunden werden, oder der Artikel von Chat GPT als nicht zum Unternehmen passend identifiziert werden, soll Chat GPT eigenständig nach einem Wikipedia-Artikel recherchieren. Auch dieser soll die gleichen Stufen zur Qualitätsprüfung durchlaufen wie bei der Wikipedia-Suche üblich.\nDer von ChatGPT gefundene Artikel muss vom als falsch bewerteten Artikel abweichen. Wurde kein passender Artikel gefunden, soll 'kein Artikel verfügbar' ausgegeben werden\"", #AE Chat Vorschlag Wiki Artikel (alt U) + "XXX derzeit nicht verwendet, wird vermutlich gelöscht xxx", #AF Begründung bei Abweichung (alt V) + "Ziel: Rohtext der Website. Input für Zusammenfassung (AH).", #AG Website Rohtext + "Ziel: KI-generierte Zusammenfassung des Website-Rohtextes (AG). Input für Branchenbewertung.", #AH Website Zusammenfassung + "Ziel: Strukturierte Meta-Daten der Website. Für schnelle Analyse & Validierung.", #AI Website Meta-Details + "System: Timestamp des letzten Website-Scraping/Summarization-Versuchs (für AG-AI).", #AJ Website Scrape Timestamp + "System/Ziel: Status der URL-Prüfung. 'URL_CHECK_NEEDED' triggert Neusuche.", #AK URL Prüfstatus + "\"Chat GPT soll anhand der vorliegenden Informationen prüfen, welcher Branche des Ziel-Branchenschemas das Unternehmen am ehesten zugeordnet werden kann. Das Ziel-Branchenschema darf nicht verändert oder erweitert werden, sondern die Vorschläge müssen genau dem Ziel-Branchenschema entsprechen.\nDie Bewertung soll möglichst ohne Abgleich mit der CRM Branche bewertet werden, da diese falsch sein könnte. ChatGPT soll auch die Firmenwebsite und ähnliche Quellen zur Bewertung des Unernehmens heranziehen.\"", #AL Chat Vorschlag Branche (alt W) + "Ziel: Von ChatGPT eingeschätzte Konfidenz (Hoch/Mittel/Niedrig) für den Branchenvorschlag (AL).", #AM Chat Branche Konfidenz + "Die durch uns festgelegte Branche in Spalte CRM Branche soll mit der von ChatGPT ermittelten Branche in Spalte Chat Vorschlag Branche verglichen werden.", #AN Chat Konsistenz Branche (alt X) + "Weicht die Branche von unserer Eisntufung in Spalte CRM Branche ab, soll ChatGPT die Abweichung kurz begründen.", #AO Chat Begruendung Abweichung Branche (alt Y) + "Chat GPT soll anhand der vorliegenden Informationen sowie eigener Recherche prüfen, ob für das Unternehmen der Einsatz einer Fieldservice Management Lösung vorteilhaft ist. Sprich hat das Unternehmen mutmaßlich einen technischen Außendienst bzw. Disponenten die mit der Planung mobiler Resourcen beschäftigt sind.", #AP Chat Prüfung FSM Relevanz (alt Z) + "Die in Spalte Chat Begründung für FSM Relevanz soll begründet werden.", #AQ Chat Begründung für FSM Relevanz (alt AA) + "Nur wenn kein Wikipedia-Eintrag vorliegt (Wiki URL = \"\") soll ChatGPT auf Basis öffentlich verfügbarer Informationen z.B. durch Auswertung der Firmen-Website herausfinden oder schätzen, wieviele Mitarbeiter das Unternehmen hat. Wenn keine Schätzung möglich ist, soll \"keine Schätzung möglich\" ausgegeben werden.", #AR Chat Schaetzung Anzahl Mitarbeiter (alt AB) + "Entspricht die durch ChatGPT ermittelte Mitarbeiterzahl der von uns ermittelten (Spalte CRM Anzahl Mitarbeiter) bzw. der durch Wikipedia ermittelten Mitarbeiterzahl (Spalte Wiki Mitarbeiter). Begründung bei Abweichung über +-30% in Spalte Chat Begründung Abweichung Mitarbeiter", #AS Chat Konsistenzprüfung Mitarbeiterzahl (alt AC) + "Weicht die durch Chat GPT ermittelte Mitarbeiterzahl erheblich von der Anzahl der Mitarbeiter aus dem CRM (Spalte CRM Anzahl Mitarbeiter) bzw. der von Wikipedia ermittelten Anzahl (Spalte Wiki Mitarbeiter) ab, soll dies kurz begründet werden.", #AT Chat Begründung Abweichung Mitarbeiterzahl (alt AD) + "Chat GPT soll auf basis öffentlich zugänglicher Information eine Schätzung abgeben, wieviele Servicetechniker das Unternehmen hat. Hierzu können auch Querverbindungen zwischen Anzahl der Mitarbeiter, Umsatz, Branche hergestellt werden, um eine möglichst solide Schätzung abgeben zu können. Die Schätzung soll in den Abstufungen 0, <50 , >100, >200, >500 Techniker abgegeben werden. In Entwicklung ist eine Aggegierung von branchenspezifischen Merkmalen (z.B. Umsatz, Mitarbeiterzahl) die für jede Gruppe typisch ist. In weiterer Zukunft kann hierzu möglicherweise auf ein RAG-System (Retrieval-Augmented Generation) zurückgegriffen werden. Abweichungen der Einschätzung von der durch uns ermittelten Anzahl Servicetechniker (die relativ zuverlässig ist) sollen in Spalte Chat Begründung Abweichung Anzahl Servicetechniker ausgegeben werden. Wenn die Einschätzung zu einem ähnlichen Ergebnis kommt, soll \"OK\" ausgegeben werden.", #AU Chat Einschätzung Anzahl Servicetechniker (alt AE) + "Weicht die Einschätzung in Spalte Chat Einschätzung Anzahl Servicetechniker von den durch uns recherchierten Ergebnissen in Spalte CRM Anzahl Techniker ab, soll dies begründet werden.", #AV Chat Begründung Abweichung Anzahl Servicetechniker (alt AF) + "Nur wenn kein wikipedia-Eintrag vorliegt (Wiki URL = \"\") soll ChatGPT den Umsatz auf Basis seiner Daten oder der Unternehmenswebsite ermitteln. Wenn keine Schätzung möglich ist, soll \"keine Schätzung möglich\" ausgegeben werden.", #AW Chat Schätzung Umsatz (alt AG) + "ChatGPT soll erhebliche Abweichungen beim Umsatz zwischen Chat Schätzung Umsatz, Wiki Umsatz und CRM Umsatz begründen. Sind alle Umsätze einigermaßen deckungsgleich (+-30%) soll \"OK\" ausgegeben werden.", #AX Chat Begründung Abweichung Umsatz (alt AH) + "über SerpAPI wird gemeinsam mit der Kurzform des Unternehmensnamens (Spalte CRM Kurzform) und der folgenden Liste per ODER verknüpfung gesucht.\n- 'Serviceleiter'\n- 'Leiter Service'\n- 'technischer Leiter'\n- 'Service Manager'\n- 'Leiter Kundendienst'", #AY Linked Serviceleiter gefunden (alt AI) + "über SerpAPI wird gemeinsam mit der Kurzform des Unternehmensnamens (Spalte CRM Kurzform) und der folgenden Liste per ODER verknüpfung gesucht.\n- 'Leiter IT'\n- 'IT Leiter'\n- 'Head of IT'\n- 'IT-Leiter'\n- 'CIO'", #AZ Linked It-Leiter gefunden (alt AJ) + "über SerpAPI wird gemeinsam mit der Kurzform des Unternehmensnamens (Spalte CRM Kurzform) und der folgenden Liste per ODER verknüpfung gesucht.\n- 'Geschäftsführer'\n- 'Geschäftsführung'\n- 'GF'\n- 'CEO'\n- 'Geschäftsführerin'\n- 'Managing Director'\n- 'Geschäftsführender Gesellschafter'", #BA Linked Management gefunden (alt AK) + "über SerpAPI wird gemeinsam mit der Kurzform des Unternehmensnamens (Spalte CRM Kurzform) und der folgenden Liste per ODER verknüpfung gesucht.\n- 'Disponent'\n- 'Einsatzplaner'", #BB Linked Disponent gefunden (alt AL) + "System: Timestamp der letzten Kontaktsuche. Steuert Wiederholung.", #BC Contact Search Timestamp (alt AM) + "Ziel: Konsolidierter Umsatz (Mio. €), Wiki > CRM, berücksichtigt Parent. Input für ML/Plausi.", #BD Finaler Umsatz (Wiki>CRM) + "Ziel: Konsolidierte Mitarbeiterzahl, Wiki > CRM, berücksichtigt Parent. Input für ML/Plausi.", #BE Finaler Mitarbeiter (Wiki>CRM) + "Ziel: Vom ML-Modell vorhergesagter Techniker-Bucket.", #BF Geschaetzter Techniker Bucket + "Ziel: Plausi-Flag für finalen Umsatz (BD).", #BG Plausibilität Umsatz + "Ziel: Plausi-Flag für finale Mitarbeiterzahl (BE).", #BH Plausibilität Mitarbeiter + "Ziel: Plausi-Flag für Umsatz/MA-Ratio.", #BI Plausibilität Umsatz/MA Ratio + "Ziel: Datenqualitäts-Indikator Umsatz CRM vs. Wiki, berücksichtigt Parent.", #BJ Abweichung Umsatz CRM/Wiki + "Ziel: Datenqualitäts-Indikator MA CRM vs. Wiki, berücksichtigt Parent.", #BK Abweichung MA CRM/Wiki + "Ziel: Zusammenfassung Plausi-Probleme.", #BL Plausibilität Begründung + "System: Timestamp letzter Plausi-Check. Steuert Wiederholung.", #BM Plausibilität Prüfdatum + "Wenn die ChatGPT Bewertung gestartet wird, wird der erste Eintrag ohne Zeitstempel in dieser Spalte gesucht und die Bearbeitung ab hier forgesetzt. Zeilen die bereits einen Zeitstempel haben werden bei der Bearbeitung übersprungen", #BN Timestamp letzte Prüfung (alt AO) + "Wird durch das System befüllt", #BO Version (alt AP) + "Wird durch Ticktokens berechnet" #BP Tokens (alt AQ) ] ] num_cols = len(new_headers[0]) if not all(len(row) == num_cols for row in new_headers): logger.critical(f"FEHLER in alignment_demo: Die Anzahl der Spalten in den Header-Zeilen ist nicht konsistent! Erwartet {num_cols} Spalten pro Zeile, aber Längen sind: {[len(row) for row in new_headers]}.") - num_cols = max(len(row) for row in new_headers) if new_headers and any(row for row in new_headers) else 0 # Sicherstellen, dass new_headers nicht komplett leer ist + num_cols = max(len(row) for row in new_headers) if new_headers and any(row for row in new_headers) else 0 if num_cols == 0: logger.error("FEHLER: Konnte keine Spaltenanzahl für Alignment-Demo Header bestimmen.") return @@ -2716,7 +2754,7 @@ def alignment_demo(sheet): string = chr(65 + remainder) + string return string - end_col_letter = colnum_string(num_cols) # Sollte "BO" für 67 Spalten sein + end_col_letter = colnum_string(num_cols) # Sollte "BQ" für 68 Spalten sein header_range = f"A1:{end_col_letter}{len(new_headers)}" logger.info(f"Schreibe Alignment-Demo Header in Bereich {header_range}...") @@ -7920,18 +7958,14 @@ class DataProcessor: # Innerhalb der DataProcessor Klasse def _get_numeric_value_for_plausi(self, value_str, is_umsatz=False): - # Diese Funktion ist jetzt sehr ähnlich zu get_numeric_filter_value, - # nur dass sie np.nan für "unbekannt" zurückgibt und den Umsatz immer in Euro. - + logger = logging.getLogger(__name__ + "._get_numeric_value_for_plausi") # Eigener Logger if value_str is None or pd.isna(value_str): return np.nan raw_value_str_clean = str(value_str).strip() - # Explizit "0" und andere "unbekannt" Strings als NaN - if raw_value_str_clean.lower() in ['', 'k.a.', 'n/a', '-', '0', '0.0', '0,00', '0,000', '0.00']: # '.00' hinzugefügt - self.logger.debug(f"PlausiParse: Input '{raw_value_str_clean}' -> NaN (als 'unbekannt' interpretiert).") + if raw_value_str_clean.lower() in ['', 'k.a.', 'n/a', '-', '0', '0.0', '0,00', '0.000', '0.00']: + logger.debug(f"Input '{raw_value_str_clean}' als 'unbekannt' (NaN) interpretiert.") return np.nan - # Bereinigungslogik temp_val = clean_text(raw_value_str_clean) if temp_val.lower() in ['k.a.', 'n/a', '-']: return np.nan @@ -7939,7 +7973,6 @@ class DataProcessor: temp_val = re.sub(r'[€$£¥]', '', temp_val).strip() temp_val = re.split(r'\s*(-|–|bis)\s*', temp_val, 1)[0].strip() - # Tausendertrenner und Dezimalzeichen normalisieren num_extraction_str = temp_val.replace("'", "") if '.' in num_extraction_str and ',' in num_extraction_str: if num_extraction_str.rfind('.') > num_extraction_str.rfind(','): @@ -7949,15 +7982,15 @@ class DataProcessor: elif ',' in num_extraction_str: num_extraction_str = num_extraction_str.replace(',', '.') elif '.' in num_extraction_str: - if re.fullmatch(r'^\d{1,3}(\.\d{3})+$', num_extraction_str): + if re.fullmatch(r'^\d{1,3}(\.\d{3})+$', num_extraction_str) and not re.search(r'\.\d{1,2}$', num_extraction_str): num_extraction_str = num_extraction_str.replace('.', '') elif num_extraction_str.count('.') > 1: - self.logger.debug(f"PlausiParse: Mehrere Punkte in '{num_extraction_str}' (nicht als Tausenderformat erkannt).") + logger.debug(f"Mehrere Punkte in '{num_extraction_str}' (nicht als Tausenderformat erkannt).") return np.nan match = re.search(r'([\d.\-]+)', num_extraction_str) if not match: - self.logger.debug(f"PlausiParse: Kein numerischer Match in '{num_extraction_str}' (von '{raw_value_str_clean}') -> NaN.") + logger.debug(f"Kein numerischer Match in '{num_extraction_str}' (von '{raw_value_str_clean}') -> NaN.") return np.nan num_str_for_float = match.group(1) @@ -7968,37 +8001,34 @@ class DataProcessor: (num_str_for_float.count('-') == 1 and not num_str_for_float.startswith('-')): raise ValueError("Ungültiger Zahlenstring nach Regex") - num_val = float(num_str_for_float) # Der reine Zahlenwert aus dem String + num_val = float(num_str_for_float) - # Einheiten-Skalierung zum absoluten Wert original_lower = raw_value_str_clean.lower() - final_num_absolute = num_val + final_num_absolute = num_val - if is_umsatz: - # Annahme: Input-String (value_str) ist bereits in Mio, wenn keine andere Einheit explizit genannt ist. - # Wir wollen hier den absoluten Euro-Betrag. + if is_umsatz: # Ziel ist der absolute Euro-Betrag if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower): - final_num_absolute = num_val * 1000000000.0 # z.B. String "2 Mrd" -> num_val=2 -> 2 Mrd € + final_num_absolute = num_val * 1000000000.0 elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower): - final_num_absolute = num_val * 1000.0 # z.B. String "500 Tsd" -> num_val=500 -> 500 Tsd € - else: # Keine explizite andere Einheit, oder "Mio" steht da - final_num_absolute = num_val * 1000000.0 # z.B. String "173" -> num_val=173 -> 173 Mio € - else: # Mitarbeiter (absolute Zahl erwartet, außer bei expliziten Einheiten) + final_num_absolute = num_val * 1000.0 + else: # Annahme: num_val ist bereits in Mio (z.B. "173" aus CRM), konvertiere zu absolutem Euro + final_num_absolute = num_val * 1000000.0 + else: # Mitarbeiter (absolute Zahl, außer bei expliziten Einheiten) if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower): final_num_absolute = num_val * 1000000000.0 elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower): final_num_absolute = num_val * 1000000.0 elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower): final_num_absolute = num_val * 1000.0 - # Nach der Skalierung: Wenn das Ergebnis 0 ist, aber der Originalstring nicht explizit "0" war, ist 0 ein valider Wert. - # Explizite "0"-Strings wurden ganz am Anfang schon zu NaN. - # Diese Funktion soll den *berechneten* numerischen Wert zurückgeben. + # Explizite "0"-Strings wurden oben bereits zu NaN. + # Wenn final_num_absolute hier 0.0 ist, dann kam es von einer Berechnung (z.B. "0 Tsd"). + # Die Plausi-Checks können dann mit diesem numerischen Wert 0.0 arbeiten. return final_num_absolute except ValueError as e: - self.logger.debug(f"PlausiParse: ValueError '{e}' bei Konvertierung von '{num_str_for_float}' (von '{raw_value_str_clean}') -> NaN.") + logger.debug(f"ValueError '{e}' bei Konvertierung von '{num_str_for_float}' (von '{raw_value_str_clean}') -> NaN.") return np.nan except Exception as e_general: - self.logger.error(f"Unerwarteter Fehler in _get_numeric_value_for_plausi für '{raw_value_str_clean[:50]}...': {e_general}") - self.logger.debug(traceback.format_exc()) + logger.error(f"Unerwarteter Fehler in _get_numeric_value_for_plausi für '{raw_value_str_clean[:50]}...': {e_general}") + logger.debug(traceback.format_exc()) return np.nan