From defe025c9daefcf4a3990716b11e390c47ef1eab Mon Sep 17 00:00:00 2001 From: Floke Date: Mon, 26 May 2025 09:22:56 +0000 Subject: [PATCH] bugfix --- brancheneinstufung.py | 84 +++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 55 deletions(-) diff --git a/brancheneinstufung.py b/brancheneinstufung.py index 4e6c3cf0..b86ca465 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -726,20 +726,16 @@ def extract_numeric_value(raw_value, is_umsatz=False): if not text_to_parse or text_to_parse.lower() in ['k.a.', 'n/a', '-']: return "k.A." - # Vereinheitliche typografische Apostrophe zu geraden text_to_parse = text_to_parse.replace("’", "'").replace("‘", "'") - # Vor dem Parsen prüfen, ob der Input-String selbst "0" oder eine Variation davon ist. - test_val_for_zero = text_to_parse.replace(',', '.').replace(' ', '') # Leerzeichen hier entfernen für den Test + test_val_for_zero = text_to_parse.replace(',', '.').replace(' ', '') if test_val_for_zero in ['0', '0.0', '0.00', '0.000']: logger.debug(f"Input '{raw_value_str_original_for_debug}' direkt als '0' interpretiert.") return "0" try: - # Schritt 1: Erweiterte Vorreinigung für Präfixe, Suffixe und Währungssymbole text_processed = text_to_parse - # Präfixe entfernen (Groß-/Kleinschreibung ignorieren) prefixes_to_remove = [ r'ca\.?\s*', r'circa\s*', r'rund\s*', r'etwa\s*', r'über\s*', r'unter\s*', r'mehr als\s*', r'weniger als\s*', r'bis zu\s*', r'about\s*', r'over\s*', @@ -748,8 +744,6 @@ def extract_numeric_value(raw_value, is_umsatz=False): for prefix_pattern in prefixes_to_remove: text_processed = re.sub(f'(?i)^{prefix_pattern}', '', text_processed).strip() - # Währungssymbole und -codes entfernen (umfassender) - # Reihenfolge ist wichtig: spezifischere (z.B. "US$") vor allgemeineren ($) currency_patterns = [ r'(?:US|USD)\$\s*', r'US\$\s*', r'EUR\s*€?\s*', r'€\s*', r'CHF\s*', r'GBP\s*£?\s*', r'£\s*', r'JPY\s*¥?\s*', r'¥\s*', r'₹\s*', r'[Cc][Hh][Ff]\s*' @@ -757,33 +751,21 @@ def extract_numeric_value(raw_value, is_umsatz=False): for curr_pattern in currency_patterns: text_processed = re.sub(curr_pattern, '', text_processed, flags=re.IGNORECASE).strip() - # Klammern und eckige Klammern und ihren Inhalt entfernen text_processed = re.sub(r'\(.*?\)|\[.*?\]', '', text_processed).strip() - # Bereiche ("X - Y" oder "X bis Y") - nur den ersten Teil nehmen text_processed = re.split(r'\s*(-|–|bis)\s*', text_processed, 1)[0].strip() if not text_processed: logger.debug(f"Text nach erweiterter Vorreinigung leer für '{raw_value_str_original_for_debug}'") return "k.A." - # Der globale bereinigte Text für die Einheitensuche (ohne Zahl) text_cleaned_for_units = clean_text(text_processed).lower() - - # Schritt 2: Zahl und direkt folgende Einheit suchen (flexibler) - # Suche nach der ERSTEN Zahlenfolge im String. - # Erlaubt optionalen Text davor, dann die Zahl, dann optionalen Text danach (für Einheit) - # Diese Regex versucht, die Zahl so gut wie möglich zu isolieren. - # \D*? am Anfang, um nicht-numerischen Text vor der Zahl zu überspringen (non-greedy) - # ([\d.,\'\s]+) fängt die Zahl selbst - # (.*) fängt den Rest als potentielle Einheit - num_match = re.search(r'([\d.,\'\s]+)', text_processed) # Finde die erste Zahlenfolge + num_match = re.search(r'([\d.,\'\s]+)', text_processed) num_str_candidate = "" - unit_part_str = "" # Einheit, die direkt NACH der Zahl steht + unit_part_str = "" if num_match: num_str_candidate = num_match.group(1).strip() - # Was nach der Zahl kommt, könnte eine Einheit sein potential_unit_start_index = num_match.end() unit_part_str = text_processed[potential_unit_start_index:].strip() else: @@ -794,41 +776,40 @@ def extract_numeric_value(raw_value, is_umsatz=False): logger.debug(f"Zahlenkandidat war leer oder nur Whitespace für '{raw_value_str_original_for_debug}' nach match in '{text_processed}'") return "k.A." - # Schritt 3: Bereinige den extrahierten Zahlen-String - cleaned_num_str = num_str_candidate.replace("'", "").replace(" ", "") # Apostrophe und alle Leerzeichen entfernen + cleaned_num_str = num_str_candidate.replace("'", "").replace(" ", "") if not cleaned_num_str: - logger.debug(f"Zahlenkandidat '{num_str_candidate}' wurde zu leerem String nach Bereinigung.") + logger.debug(f"Zahlenkandidat '{num_str_candidate}' wurde zu leerem String nach Entfernung von ' und Leerraum.") return "k.A." has_dot = '.' in cleaned_num_str has_comma = ',' in cleaned_num_str - # Verbesserte Trennzeichenlogik if has_dot and has_comma: last_dot_pos = cleaned_num_str.rfind('.') last_comma_pos = cleaned_num_str.rfind(',') - if last_dot_pos > last_comma_pos: # US: 1,234.56 - cleaned_num_str = cleaned_num_str.replace(',', '') # Komma ist Tausender - else: # EU: 1.234,56 - cleaned_num_str = cleaned_num_str.replace('.', '') # Punkt ist Tausender - cleaned_num_str = cleaned_num_str.replace(',', '.') # Komma wird Dezimal + if last_dot_pos > last_comma_pos: + cleaned_num_str = cleaned_num_str.replace(',', '') + else: + cleaned_num_str = cleaned_num_str.replace('.', '') + cleaned_num_str = cleaned_num_str.replace(',', '.') elif has_comma: - # Wenn es nur Kommas gibt: - # "1,23" -> "1.23" (letztes Komma, 1-2 Ziffern danach) - # "1,234" -> "1.234" (letztes Komma, 3 Ziffern danach, aber es ist das einzige Komma) - # "1,234,567" -> "1234567" (mehrere Kommas -> Tausender) - if cleaned_num_str.count(',') == 1 and re.search(r',\d+$', cleaned_num_str): - # Einziges Komma, gefolgt von Ziffern -> Dezimaltrennzeichen - cleaned_num_str = cleaned_num_str.replace(',', '.', 1) - else: - # Mehrere Kommas oder Format passt nicht zu Dezimal -> Tausendertrenner - cleaned_num_str = cleaned_num_str.replace(',', '') - elif has_dot: - # Analoge Logik für Punkte - if cleaned_num_str.count('.') == 1 and re.search(r'\.\d+$', cleaned_num_str): - cleaned_num_str = cleaned_num_str.replace('.', '.', 1) # Ist schon ein Punkt - else: - cleaned_num_str = cleaned_num_str.replace('.', '') + if is_umsatz: # Für Umsatz kann Komma Dezimal sein + if re.search(r',\d{1,2}$', cleaned_num_str): # Letztes Komma, 1-2 Ziffern danach + parts = cleaned_num_str.rsplit(',', 1) + integer_part = parts[0].replace(',', '') + cleaned_num_str = f"{integer_part}.{parts[1]}" + else: # Alle Kommas sind Tausendertrenner + cleaned_num_str = cleaned_num_str.replace(',', '') + else: # Für Mitarbeiter ist Komma IMMER Tausendertrenner + cleaned_num_str = cleaned_num_str.replace(',', '') + elif has_dot: + # Für Umsatz und Mitarbeiter kann Punkt Dezimal sein + if re.search(r'\.\d{1,2}$', cleaned_num_str): # Letzter Punkt, 1-2 Ziffern danach + parts = cleaned_num_str.rsplit('.', 1) + integer_part = parts[0].replace('.', '') + cleaned_num_str = f"{integer_part}.{parts[1]}" + else: # Alle Punkte sind Tausendertrenner + cleaned_num_str = cleaned_num_str.replace('.', '') if not re.fullmatch(r'-?\d+(\.\d+)?', cleaned_num_str): logger.debug(f"Kein gültiger numerischer String nach Trennzeichenbehandlung: '{cleaned_num_str}' (Num-Kandidat: '{num_str_candidate}', Original: '{raw_value_str_original_for_debug}')") @@ -836,11 +817,7 @@ def extract_numeric_value(raw_value, is_umsatz=False): num_as_float = float(cleaned_num_str) - # Schritt 4: Einheiten-Skalierung scaled_num = num_as_float - - # Priorisiere die Einheit, die direkt nach der Zahl kommt. - # Falls diese leer ist, verwende den globaleren, bereinigten Text. string_for_unit_search = unit_part_str.lower() if unit_part_str else text_cleaned_for_units logger.debug(f"String für Einheitensuche: '{string_for_unit_search}' (num_as_float: {num_as_float})") @@ -850,7 +827,7 @@ def extract_numeric_value(raw_value, is_umsatz=False): if re.search(r'\b(mrd\.?|milliarden|billion|mia\.?)\b', string_for_unit_search): multiplikator = 1000.0 einheit_gefunden = True - elif re.search(r'\bcrore\b', string_for_unit_search): # 1 crore = 10 Mio + elif re.search(r'\bcrore\b', string_for_unit_search): multiplikator = 10.0 einheit_gefunden = True elif re.search(r'\b(mio\.?|mill\.?|millionen|mn)\b', string_for_unit_search): @@ -859,12 +836,9 @@ def extract_numeric_value(raw_value, is_umsatz=False): elif re.search(r'\b(tsd\.?|tausend|k\b(?!\w))\b', string_for_unit_search): multiplikator = 0.001 einheit_gefunden = True - - # Wenn keine explizite Einheit (Mrd, Mio, Tsd, Crore) gefunden wurde, - # wird die Zahl als bereits in Millionen interpretiert (multiplikator bleibt 1.0). logger.debug(f"Umsatz: num_as_float={num_as_float}, gefundene Einheit? {einheit_gefunden}, Multiplikator={multiplikator}") scaled_num = num_as_float * multiplikator - else: # Mitarbeiter (absolute Zahl) + else: multiplikator = 1.0 if re.search(r'\b(mrd\.?|milliarden|billion|mia\.?)\b', string_for_unit_search): multiplikator = 1000000000.0