v1.7.5: Zahlenextraktion & Plausi-Logik verfeinert, Plausi-TS
- Zahlenextraktionsfunktionen (`get_numeric_filter_value`, `_get_numeric_value_for_plausi`) überarbeitet für robustere Behandlung von:
- Tausendertrennern (Punkt, Apostroph, Leerzeichen).
- Dezimaltrennzeichen (Komma, Punkt).
- Annahme, dass Umsatz-Rohwerte im Sheet bereits in Mio. € sind (außer bei expliziten Einheiten wie Mrd/Tsd).
- String-Input "0" wird in `_get_numeric_value_for_plausi` jetzt konsistent als "unbekannt" (NaN) behandelt.
- Methode `_check_financial_plausibility` angepasst:
- Verwendet die korrigierten numerischen Werte für absolute Plausi-Checks.
- Nutzt `get_numeric_filter_value` für den Vergleich von CRM- vs. Wiki-Umsatz (Mio.-Werte).
- Default-Flags auf "NICHT_PRUEFBAR" geändert, wenn zugrundeliegende Zahlen NaN sind.
- Logik für "FEHLER_FORMAT" präzisiert, um explizite "0"-Strings nicht fälschlicherweise als Formatfehler zu werten.
- Konsolidierungslogik in `_process_single_row` stellt sicher, dass `final_umsatz/ma_str_konsolidiert` korrekt "k.A." wird, wenn numerische Quellen 0 (im Sinne von unbekannt) sind.
- Plausibilitäts-Checks in `_process_single_row` verwenden nun die direkt zuvor neu konsolidierten Finanzwerte.
- Sicherstellung, dass der "Plausibilität Prüfdatum"-Timestamp (Spalte BM) auch bei Fehlern innerhalb des Plausi-Check-Blocks in `_process_single_row` gesetzt wird.
- Methode `run_plausibility_checks_batch` aktualisiert, um die korrigierte Konsolidierungs- und Plausi-Logik zu verwenden und den Plausi-Timestamp zu schreiben.
- Diverse Debug-Log-Ausgaben zur besseren Nachverfolgung der Zahlenverarbeitung und Plausi-Logik hinzugefügt/optimiert.
This commit is contained in:
@@ -808,14 +808,14 @@ def extract_numeric_value(raw_value, is_umsatz=False):
|
||||
# Extrahiert und normalisiert Zahlenwerte fuer Vergleichslogik.
|
||||
# Nutzt globale Helfer: clean_text, re.
|
||||
# Globale Funktion (ersetzen Sie Ihre bestehende Version)
|
||||
# Globale Funktion (ersetzen Sie Ihre bestehende Version)
|
||||
def get_numeric_filter_value(value_str, is_umsatz=False):
|
||||
logger = logging.getLogger(__name__)
|
||||
logger = logging.getLogger(__name__ + ".get_numeric_filter_value")
|
||||
if value_str is None or pd.isna(value_str) or str(value_str).strip() == '':
|
||||
return 0.0 if is_umsatz else 0
|
||||
|
||||
raw_value_str_original = str(value_str).strip()
|
||||
|
||||
# "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
|
||||
|
||||
@@ -828,108 +828,52 @@ def get_numeric_filter_value(value_str, is_umsatz=False):
|
||||
processed_value = re.sub(r'[€$£¥]', '', processed_value).strip()
|
||||
processed_value = re.split(r'\s*(-|–|bis)\s*', processed_value, 1)[0].strip()
|
||||
|
||||
# Tausendertrenner entfernen (Apostroph, Punkt wenn nicht Dezimal)
|
||||
# Komma zu Punkt für Dezimal machen
|
||||
num_extraction_str = processed_value.replace("'", "")
|
||||
num_extraction_str = processed_value.replace("'", "").replace(" ", "")
|
||||
if not num_extraction_str: return 0.0 if is_umsatz else 0
|
||||
|
||||
# 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
|
||||
has_dot = '.' in num_extraction_str
|
||||
has_comma = ',' in num_extraction_str
|
||||
|
||||
if has_dot and has_comma:
|
||||
if num_extraction_str.rfind('.') > num_extraction_str.rfind(','): # US: 1,234.56
|
||||
num_extraction_str = num_extraction_str.replace(',', '')
|
||||
else: # EU-Stil: 1.234,56 -> Punkte entfernen, Komma zu Punkt
|
||||
else: # EU: 1.234,56
|
||||
num_extraction_str = num_extraction_str.replace('.', '').replace(',', '.')
|
||||
# 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(',', '.')
|
||||
# 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):
|
||||
elif has_comma: # Nur Kommas
|
||||
if num_extraction_str.count(',') == 1 and re.search(r',\d{1,2}$', num_extraction_str) and not re.search(r',\d{3}', num_extraction_str):
|
||||
num_extraction_str = num_extraction_str.replace(',', '.') # Dezimalkomma
|
||||
else: # Tausenderkommas
|
||||
num_extraction_str = num_extraction_str.replace(',', '')
|
||||
elif has_dot: # Nur Punkte
|
||||
if num_extraction_str.count('.') == 1 and re.search(r'\.\d{1,2}$', num_extraction_str) and not re.search(r'\.\d{3}', num_extraction_str):
|
||||
pass # Dezimalpunkt, bleibt
|
||||
else: # Tausenderpunkte
|
||||
num_extraction_str = num_extraction_str.replace('.', '')
|
||||
# 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:
|
||||
# Finale Validierung mit Regex, ob es eine gültige Zahl ist
|
||||
if not re.fullmatch(r'-?\d+(\.\d+)?', num_extraction_str):
|
||||
logger.debug(f"Kein gültiger numerischer String nach Trennzeichenbehandlung: '{num_extraction_str}' (Original: '{raw_value_str_original}')")
|
||||
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)
|
||||
|
||||
num_as_float = float(num_extraction_str)
|
||||
|
||||
scaled_num = num_as_float
|
||||
original_lower = raw_value_str_original.lower()
|
||||
|
||||
if is_umsatz: # Zielwert soll in Millionen sein
|
||||
if is_umsatz:
|
||||
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
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
scaled_num = num_as_float / 1000.0
|
||||
else: # Mitarbeiter
|
||||
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:
|
||||
return 0.0 if is_umsatz else 0
|
||||
|
||||
num_str_for_float = match.group(1)
|
||||
|
||||
# Finale Prüfung des extrahierten Zahlenstrings
|
||||
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)
|
||||
|
||||
scaled_num = num_as_float
|
||||
original_lower = raw_value_str_original.lower()
|
||||
|
||||
if is_umsatz: # Wert soll in Mio. € sein
|
||||
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
|
||||
scaled_num = num_as_float * 1000.0 # von Mrd-Einheit zu Mio-Einheit
|
||||
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
scaled_num = num_as_float / 1000.0 # von Tsd-Einheit zu Mio-Einheit
|
||||
# Ansonsten wird angenommen, dass num_as_float bereits den Wert in Mio. € darstellt
|
||||
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]}...'")
|
||||
logger.debug(f"ValueError '{e}' bei Konvertierung (get_numeric_filter_value) von '{num_extraction_str if 'num_extraction_str' 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}")
|
||||
@@ -4836,37 +4780,53 @@ class DataProcessor:
|
||||
final_umsatz_str_konsolidiert = "FEHLER KONSO"
|
||||
final_ma_str_konsolidiert = "FEHLER KONSO"
|
||||
|
||||
# --- NEU: 3f. Plausibilitäts-Checks durchführen (BB-BG) ---
|
||||
self.logger.debug(f" -> Führe Plausibilitäts-Checks für Zeile {row_num_in_sheet} durch...")
|
||||
# --- NEU: 3f. Plausibilitäts-Checks durchführen (BG-BM) ---
|
||||
self.logger.debug(f" Zeile {row_num_in_sheet}: Führe Plausibilitäts-Checks durch...")
|
||||
plausi_check_erfolgreich_durchgefuehrt = False # Flag
|
||||
try:
|
||||
# Erstelle ein Dictionary mit den benötigten Werten für den Plausi-Check
|
||||
# ... (Erstellung von plausi_input_data wie zuvor) ...
|
||||
plausi_input_data = {
|
||||
"Finaler Umsatz (Wiki>CRM)": final_umsatz_str_konsolidiert,
|
||||
"Finaler Mitarbeiter (Wiki>CRM)": final_ma_str_konsolidiert,
|
||||
"CRM Umsatz": self._get_cell_value_safe(row_data, "CRM Umsatz"),
|
||||
"Wiki Umsatz": final_wiki_data.get('umsatz', 'k.A.'), # Nutze den Wiki-Wert, der für Konsolidierung verwendet wurde
|
||||
"Wiki Umsatz": self._get_cell_value_safe(row_data, "Wiki Umsatz"),
|
||||
"CRM Anzahl Mitarbeiter": self._get_cell_value_safe(row_data, "CRM Anzahl Mitarbeiter"),
|
||||
"Wiki Mitarbeiter": final_wiki_data.get('mitarbeiter', 'k.A.') # Nutze den Wiki-Wert, der für Konsolidierung verwendet wurde
|
||||
"Wiki Mitarbeiter": self._get_cell_value_safe(row_data, "Wiki Mitarbeiter")
|
||||
}
|
||||
plausi_results = self._check_financial_plausibility(plausi_input_data)
|
||||
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_umsatz_flag", "FEHLER")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Mitarbeiter"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ma_flag", "FEHLER")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz/MA Ratio"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ratio_flag", "FEHLER")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung Umsatz CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_umsatz_flag", "FEHLER")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung MA CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_ma_flag", "FEHLER")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plausi_begruendung_final", "Fehler bei Begründungserstellung")]]})
|
||||
self.logger.debug(f" -> Plausi-Ergebnisse: U:{plausi_results.get('plaus_umsatz_flag')} MA:{plausi_results.get('plaus_ma_flag')} Ratio:{plausi_results.get('plaus_ratio_flag')} AbwU:{plausi_results.get('abweichung_umsatz_flag')} AbwMA:{plausi_results.get('abweichung_ma_flag')}")
|
||||
except Exception as e_plausi:
|
||||
self.logger.error(f"FEHLER bei Plausibilitäts-Checks für Zeile {row_num_in_sheet}: {e_plausi}")
|
||||
# Setze Fehler-Flags, falls noch nicht geschehen
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_in_sheet}', 'values': [['FEHLER_CHECK']]})
|
||||
# ... (ähnlich für andere Plausi-Spalten) ...
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[f"Systemfehler Plausi-Check: {str(e_plausi)[:50]}"]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_umsatz_flag", "ERR_FLAG")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Mitarbeiter"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ma_flag", "ERR_FLAG")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz/MA Ratio"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ratio_flag", "ERR_FLAG")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung Umsatz CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_umsatz_flag", "ERR_FLAG")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung MA CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_ma_flag", "ERR_FLAG")]]})
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plausi_begruendung_final", "Fehler Begr.")]]})
|
||||
|
||||
plausi_check_erfolgreich_durchgefuehrt = True # Markieren, dass der Check zumindest versucht wurde
|
||||
|
||||
except Exception as e_plausi_in_single_row:
|
||||
self.logger.error(f"FEHLER bei Plausibilitäts-Checks in _process_single_row für Zeile {row_num_in_sheet}: {e_plausi_in_single_row}")
|
||||
# Fehler-Flags für Plausi-Spalten setzen
|
||||
error_val_plausi = [['FEHLER_PLAUSI_CALL']]
|
||||
for key_flag_plausi in ["Plausibilität Umsatz", "Plausibilität Mitarbeiter", "Plausibilität Umsatz/MA Ratio", "Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki"]:
|
||||
if COLUMN_MAP.get(key_flag_plausi) is not None: # Sicherstellen, dass der Key existiert
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP[key_flag_plausi] + 1)}{row_num_in_sheet}', 'values': error_val_plausi})
|
||||
if COLUMN_MAP.get("Plausibilität Begründung") is not None:
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[f"Systemfehler Plausi-Call: {str(e_plausi_in_single_row)[:50]}"]]})
|
||||
|
||||
# Plausi-Timestamp setzen, wenn der Plausi-Block (3f) erreicht wurde
|
||||
# (entweder erfolgreich oder mit Fehler im try-Block)
|
||||
if COLUMN_MAP.get("Plausibilität Prüfdatum") is not None:
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Prüfdatum"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
|
||||
else:
|
||||
self.logger.error("FEHLER: Schlüssel 'Plausibilität Prüfdatum' nicht in COLUMN_MAP. Kann Timestamp nicht setzen.")
|
||||
|
||||
|
||||
# Setze den Timestamp letzte Pruefung (BI - vorher BB), da die ChatGPT-Evaluationen (und jetzt Plausi) liefen
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Timestamp letzte Pruefung"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
|
||||
# Setze den "Timestamp letzte Prüfung" (BO - für ChatGPT-Evaluationen), da dieser Block lief
|
||||
if COLUMN_MAP.get("Timestamp letzte Prüfung") is not None:
|
||||
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Timestamp letzte Prüfung"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
|
||||
else:
|
||||
self.logger.error("FEHLER: Schlüssel 'Timestamp letzte Prüfung' nicht in COLUMN_MAP. Kann Haupt-Timestamp nicht setzen.")
|
||||
|
||||
# else if run_chat_step (aber nicht processing_needed):
|
||||
# self.logger.debug(f"Zeile {row_num_in_sheet}: Ueberspringe CHATGPT Evaluationen und Plausi-Checks (Timestamp BI gesetzt, Wiki nicht aktualisiert und kein Re-Eval).")
|
||||
@@ -7958,7 +7918,7 @@ class DataProcessor:
|
||||
|
||||
# Innerhalb der DataProcessor Klasse
|
||||
def _get_numeric_value_for_plausi(self, value_str, is_umsatz=False):
|
||||
logger = logging.getLogger(__name__ + "._get_numeric_value_for_plausi") # Eigener Logger
|
||||
logger = logging.getLogger(__name__ + "._get_numeric_value_for_plausi")
|
||||
if value_str is None or pd.isna(value_str): return np.nan
|
||||
raw_value_str_clean = str(value_str).strip()
|
||||
|
||||
@@ -7973,58 +7933,53 @@ class DataProcessor:
|
||||
temp_val = re.sub(r'[€$£¥]', '', temp_val).strip()
|
||||
temp_val = re.split(r'\s*(-|–|bis)\s*', temp_val, 1)[0].strip()
|
||||
|
||||
num_extraction_str = temp_val.replace("'", "")
|
||||
if '.' in num_extraction_str and ',' in num_extraction_str:
|
||||
num_extraction_str = temp_val.replace("'", "").replace(" ", "")
|
||||
if not num_extraction_str: return np.nan
|
||||
|
||||
has_dot = '.' in num_extraction_str
|
||||
has_comma = ',' in num_extraction_str
|
||||
|
||||
if has_dot and has_comma:
|
||||
if num_extraction_str.rfind('.') > num_extraction_str.rfind(','):
|
||||
num_extraction_str = num_extraction_str.replace(',', '')
|
||||
else:
|
||||
num_extraction_str = num_extraction_str.replace('.', '').replace(',', '.')
|
||||
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) and not re.search(r'\.\d{1,2}$', num_extraction_str):
|
||||
elif has_comma:
|
||||
if num_extraction_str.count(',') == 1 and re.search(r',\d{1,2}$', num_extraction_str) and not re.search(r',\d{3}', num_extraction_str):
|
||||
num_extraction_str = num_extraction_str.replace(',', '.')
|
||||
else:
|
||||
num_extraction_str = num_extraction_str.replace(',', '')
|
||||
elif has_dot:
|
||||
if num_extraction_str.count('.') == 1 and re.search(r'\.\d{1,2}$', num_extraction_str) and not re.search(r'\.\d{3}', num_extraction_str):
|
||||
pass
|
||||
else:
|
||||
num_extraction_str = num_extraction_str.replace('.', '')
|
||||
elif num_extraction_str.count('.') > 1:
|
||||
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:
|
||||
logger.debug(f"Kein numerischer Match in '{num_extraction_str}' (von '{raw_value_str_clean}') -> NaN.")
|
||||
if not re.fullmatch(r'-?\d+(\.\d+)?', num_extraction_str):
|
||||
logger.debug(f"Kein gültiger numerischer String nach Trennzeichenbehandlung: '{num_extraction_str}' (Original: '{raw_value_str_clean}')")
|
||||
return np.nan
|
||||
|
||||
num_str_for_float = match.group(1)
|
||||
try:
|
||||
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('-')):
|
||||
raise ValueError("Ungültiger Zahlenstring nach Regex")
|
||||
|
||||
num_val = float(num_str_for_float)
|
||||
|
||||
try:
|
||||
num_val = float(num_extraction_str)
|
||||
original_lower = raw_value_str_clean.lower()
|
||||
final_num_absolute = num_val
|
||||
|
||||
if is_umsatz: # Ziel ist der absolute Euro-Betrag
|
||||
if is_umsatz:
|
||||
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'\btsd\s*\b|\btausend\s*\b', original_lower):
|
||||
final_num_absolute = num_val * 1000.0
|
||||
else: # Annahme: num_val ist bereits in Mio (z.B. "173" aus CRM), konvertiere zu absolutem Euro
|
||||
else:
|
||||
final_num_absolute = num_val * 1000000.0
|
||||
else: # Mitarbeiter (absolute Zahl, außer bei expliziten Einheiten)
|
||||
else:
|
||||
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
|
||||
|
||||
# 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:
|
||||
logger.debug(f"ValueError '{e}' bei Konvertierung von '{num_str_for_float}' (von '{raw_value_str_clean}') -> NaN.")
|
||||
logger.debug(f"ValueError '{e}' bei Konvertierung von '{num_extraction_str}' (von '{raw_value_str_clean}') -> NaN.")
|
||||
return np.nan
|
||||
except Exception as e_general:
|
||||
logger.error(f"Unerwarteter Fehler in _get_numeric_value_for_plausi für '{raw_value_str_clean[:50]}...': {e_general}")
|
||||
|
||||
Reference in New Issue
Block a user