diff --git a/brancheneinstufung.py b/brancheneinstufung.py index c32e1b97..2b03c01b 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -533,33 +533,38 @@ class DataProcessor: # ==================== NEUE FUNKTION: process_verification_only ==================== def process_verification_only(): """ - Überarbeiteter Batch‑Prozess (Version 1.5.8, Modus 51): - - Ermittelt zunächst die letzte Zeile, in der in Spalte AO (Index 40) ein Zeitstempel steht. - - Beginnt die Verarbeitung ab der nächsten Zeile. - - Verarbeitet alle Zeilen ab diesem Startpunkt in Paketen der Größe Config.BATCH_SIZE (z. B. 10 Zeilen). - - Für jedes Batch wird ein aggregierter Prompt erstellt, an ChatGPT gesendet und die Antwort - zeilenweise geparst. + Überarbeiteter Batch‑Prozess (Version 1.5.9, Modus 51): + - Startet die Verarbeitung ab Zeile 7 und sucht ab dort die erste Zeile, in der Spalte AO (Index 41) leer ist. + - Ab dieser Zeile werden alle Zeilen in Paketen der Größe Config.BATCH_SIZE (z. B. 10 Zeilen) verarbeitet. + - Für jedes Batch wird ein aggregierter Prompt erstellt, an ChatGPT gesendet und die Antwort zeilenweise geparst. - Die Ergebnisse werden in den Spalten S bis Y geschrieben: S: Wiki-Validierung ("OK" oder "X") T: Alternativer Wiki-Artikel (URL oder "Kein Wikipedia-Eintrag vorhanden.") U: Wiki-Erklärung / Begründung V–Y: Platzhalter (leer) - - Umfassende Log-Ausgaben unterstützen die Fehlerdiagnose. + - Umfangreiche Log-Ausgaben unterstützen die Fehlerdiagnose. """ - debug_print("Starte Verifizierungsmodus (Modus 51) im Batch-Prozess (Version 1.5.8)...") + debug_print("Starte Verifizierungsmodus (Modus 51) im Batch-Prozess (Version 1.5.9)...") - # Bestimme, in welcher Zeile in Spalte AO zuletzt ein Zeitstempel vorhanden ist - try: - col_ao = main_sheet.col_values(41) # Spalte AO (1-basierter Index) - except Exception as e: - debug_print(f"Fehler beim Auslesen von Spalte AO: {e}") - col_ao = [] - last_filled_row = 1 # Header wird angenommen in Zeile 1 - for idx, cell in enumerate(col_ao, start=1): - if cell.strip() != "": - last_filled_row = idx - start_row = last_filled_row + 1 - debug_print(f"Letzter Zeitstempel in Spalte AO in Zeile {last_filled_row}. Verarbeitung startet ab Zeile {start_row}.") + # Bestimme ab Zeile 7 die erste Zeile, in der Spalte AO (Index 41) leer ist. + gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name( + Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])) + sh = gc.open_by_url(Config.SHEET_URL) + main_sheet = sh.sheet1 + data = main_sheet.get_all_values() + + # Setze den Startindex: Beginne mindestens bei Zeile 7. + start_row = None + for i in range(7, len(data) + 1): + row = data[i - 1] + # Prüfe, ob die Zeile weniger als 41 Spalten hat oder Spalte AO leer ist. + if len(row) < 41 or row[40].strip() == "": + start_row = i + break + if start_row is None: + debug_print("Keine Zeile ohne Zeitstempel in Spalte AO gefunden. Es wird nichts verarbeitet.") + return + debug_print(f"Verarbeitung startet ab Zeile {start_row} (erste Zeile ohne Zeitstempel in Spalte AO).") # Abfrage: Wie viele Zeilen sollen insgesamt verarbeitet werden? try: @@ -568,31 +573,24 @@ def process_verification_only(): debug_print(f"Fehler bei der Eingabe der Zeilenanzahl: {e}. Es werden alle verfügbaren Zeilen verarbeitet.") total_rows = None - gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name( - Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])) - sh = gc.open_by_url(Config.SHEET_URL) - main_sheet = sh.sheet1 - data = main_sheet.get_all_values() available_total = len(data) - 1 # ohne Header - # Begrenze die zu verarbeitenden Zeilen, falls total_rows vorgegeben ist if total_rows is not None: available_rows = min(total_rows, available_total) else: available_rows = available_total if start_row > available_rows + 1: - debug_print("Es gibt keine Zeilen ohne Zeitstempel, daher wird nichts verarbeitet.") + debug_print("Es gibt keine Zeilen ohne Zeitstempel ab dem Startpunkt. Es wird nichts verarbeitet.") return - batch_size = Config.BATCH_SIZE # z. B. 10, einstellbar in Config + batch_size = Config.BATCH_SIZE # z. B. 10 batches = [] row_numbers = [] - - # Iteriere ab start_row bis available_rows - for i, row in enumerate(data[start_row - 1:], start=start_row): - if i > available_rows + 1: - break - # Hier wird davon ausgegangen, dass ab start_row keine Zeitstempel in AO vorliegen. + + # Verarbeitung ab start_row bis available_rows + for i in range(start_row, available_rows + 2): # +1 wegen 1-basierter Index, +1 um bis available_rows einzubeziehen + row = data[i - 1] + # Da wir ab start_row starten, gehen wir davon aus, dass bisher keine Zeitstempel in AO vorhanden sind. entry_text = ( f"Eintrag {i}:\n" f"Firmenname: {row[1] if len(row) > 1 else ''}\n" @@ -605,146 +603,92 @@ def process_verification_only(): batches.append(entry_text) row_numbers.append(i) if len(batches) == batch_size: - aggregated_prompt = ( - "Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. " - "Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel (URL, Absatz, Kategorien) plausibel passt. " - "Gib das Ergebnis für jeden Eintrag im Format aus:\n" - "Eintrag : \n" - "Regeln:\n" - "- Bei Übereinstimmung: 'OK'\n" - "- Bei Nichtübereinstimmung: 'Alternativer Wikipedia-Artikel vorgeschlagen: | X | '\n" - "- Falls kein Artikel gefunden wurde: 'Kein Wikipedia-Eintrag vorhanden.'\n\n" - ) - aggregated_prompt += "\n".join(batches) - debug_print(f"Verarbeite Batch für Zeilen {row_numbers[0]} bis {row_numbers[-1]}.") - if tiktoken: - try: - enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL) - debug_print(f"Token-Zahl für aktuellen Batch: {len(enc.encode(aggregated_prompt))}") - except Exception as e: - debug_print(f"Fehler beim Token-Counting: {e}") - try: - with open("api_key.txt", "r") as f: - api_key = f.read().strip() - except Exception as e: - debug_print(f"Fehler beim Lesen des API-Tokens für Verifizierung: {e}") - return - openai.api_key = api_key - try: - response = openai.ChatCompletion.create( - model=Config.TOKEN_MODEL, - messages=[{"role": "user", "content": aggregated_prompt}], - temperature=0.0 - ) - result = response.choices[0].message.content.strip() - debug_print(f"Aggregierte Antwort für Batch {row_numbers[0]}-{row_numbers[-1]}: {result}") - except Exception as e: - debug_print(f"Fehler bei der ChatGPT-Anfrage für Batch {row_numbers[0]}-{row_numbers[-1]}: {e}") - result = "" - answers = result.split("\n") - for current_row in row_numbers: - answer = "k.A." - for line in answers: - if line.strip().startswith(f"Eintrag {current_row}:"): - answer = line.split(":", 1)[1].strip() - break - if answer.upper() == "OK": - wiki_confirm = "OK" - alt_article = "" - wiki_explanation = "" - elif answer.upper() == "KEIN WIKIPEDIA-EINTRAG VORHANDEN.": - wiki_confirm = "" - alt_article = "Kein Wikipedia-Eintrag vorhanden." - wiki_explanation = "" - elif answer.startswith("Alternativer Wikipedia-Artikel vorgeschlagen:"): - parts = answer.split(":", 1)[1].split("|") - alt_article = parts[0].strip() if len(parts) > 0 else "k.A." - wiki_explanation = parts[2].strip() if len(parts) > 2 else "" - wiki_confirm = "X" - else: - wiki_confirm = "" - alt_article = answer - wiki_explanation = answer - try: - main_sheet.update(values=[[wiki_confirm]], range_name=f"S{current_row}") - main_sheet.update(values=[[alt_article]], range_name=f"T{current_row}") - main_sheet.update(values=[[wiki_explanation]], range_name=f"U{current_row}") - main_sheet.update(values=[["", "", "", ""]], range_name=f"V{current_row}:Y{current_row}") - debug_print(f"Zeile {current_row} verifiziert: Antwort: {answer}") - except Exception as e: - debug_print(f"Fehler beim Updaten der Zeile {current_row}: {e}") - time.sleep(Config.RETRY_DELAY) + _process_batch(main_sheet, batches, row_numbers) batches = [] row_numbers = [] if batches: - aggregated_prompt = ( - "Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. " - "Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel plausibel passt. " - "Gib das Ergebnis im Format aus:\n" - "Eintrag : \n\n" - ) - aggregated_prompt += "\n".join(batches) - debug_print(f"Verarbeite letztes Batch für Zeilen {row_numbers[0]} bis {row_numbers[-1]}.") - if tiktoken: - try: - enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL) - debug_print(f"Token-Zahl für letztes Batch: {len(enc.encode(aggregated_prompt))}") - except Exception as e: - debug_print(f"Fehler beim Token-Counting im letzten Batch: {e}") - try: - with open("api_key.txt", "r") as f: - api_key = f.read().strip() - except Exception as e: - debug_print(f"Fehler beim Lesen des API-Tokens (Letztes Batch): {e}") - return - openai.api_key = api_key - try: - response = openai.ChatCompletion.create( - model=Config.TOKEN_MODEL, - messages=[{"role": "user", "content": aggregated_prompt}], - temperature=0.0 - ) - result = response.choices[0].message.content.strip() - debug_print(f"Aggregierte Antwort für letztes Batch: {result}") - except Exception as e: - debug_print(f"Fehler bei der ChatGPT-Anfrage für letztes Batch: {e}") - result = "" - answers = result.split("\n") - for current_row in row_numbers: - answer = "k.A." - for line in answers: - if line.strip().startswith(f"Eintrag {current_row}:"): - answer = line.split(":", 1)[1].strip() - break - if answer.upper() == "OK": - wiki_confirm = "OK" - alt_article = "" - wiki_explanation = "" - elif answer.upper() == "KEIN WIKIPEDIA-EINTRAG VORHANDEN.": - wiki_confirm = "" - alt_article = "Kein Wikipedia-Eintrag vorhanden." - wiki_explanation = "" - elif answer.startswith("Alternativer Wikipedia-Artikel vorgeschlagen:"): - parts = answer.split(":", 1)[1].split("|") - alt_article = parts[0].strip() if len(parts) > 0 else "k.A." - wiki_explanation = parts[2].strip() if len(parts) > 2 else "" - wiki_confirm = "X" - else: - wiki_confirm = "" - alt_article = answer - wiki_explanation = answer - try: - main_sheet.update(values=[[wiki_confirm]], range_name=f"S{current_row}") - main_sheet.update(values=[[alt_article]], range_name=f"T{current_row}") - main_sheet.update(values=[[wiki_explanation]], range_name=f"U{current_row}") - main_sheet.update(values=[["", "", "", ""]], range_name=f"V{current_row}:Y{current_row}") - debug_print(f"Zeile {current_row} verifiziert: Antwort: {answer}") - except Exception as e: - debug_print(f"Fehler beim Updaten der Zeile {current_row}: {e}") - time.sleep(Config.RETRY_DELAY) + _process_batch(main_sheet, batches, row_numbers) debug_print("Verifizierungs-Batch abgeschlossen.") +def _process_batch(main_sheet, batches, row_numbers): + """ + Hilfsfunktion: Bearbeitet einen Batch, indem ein aggregierter Prompt erstellt und + die aggregierte Antwort zeilenweise den entsprechenden Zeilennummern zugeordnet wird. + Die Ergebnisse werden in Spalten S bis Y geschrieben. + """ + aggregated_prompt = ( + "Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. " + "Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel (URL, Absatz, Kategorien) plausibel passt. " + "Gib das Ergebnis für jeden Eintrag im Format aus:\n" + "Eintrag : \n" + "Regeln:\n" + "- Bei Übereinstimmung: 'OK'\n" + "- Bei Nichtübereinstimmung: 'Alternativer Wikipedia-Artikel vorgeschlagen: | X | '\n" + "- Falls kein Artikel gefunden wurde: 'Kein Wikipedia-Eintrag vorhanden.'\n\n" + ) + aggregated_prompt += "\n".join(batches) + debug_print(f"Verarbeite Batch für Zeilen {row_numbers[0]} bis {row_numbers[-1]}.") + if tiktoken: + try: + enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL) + debug_print(f"Token-Zahl für aktuellen Batch: {len(enc.encode(aggregated_prompt))}") + except Exception as e: + debug_print(f"Fehler beim Token-Counting: {e}") + try: + with open("api_key.txt", "r") as f: + api_key = f.read().strip() + except Exception as e: + debug_print(f"Fehler beim Lesen des API-Tokens für Verifizierung: {e}") + return + openai.api_key = api_key + try: + response = openai.ChatCompletion.create( + model=Config.TOKEN_MODEL, + messages=[{"role": "user", "content": aggregated_prompt}], + temperature=0.0 + ) + result = response.choices[0].message.content.strip() + debug_print(f"Aggregierte Antwort für Batch {row_numbers[0]}-{row_numbers[-1]}: {result}") + except Exception as e: + debug_print(f"Fehler bei der ChatGPT-Anfrage für Batch {row_numbers[0]}-{row_numbers[-1]}: {e}") + result = "" + answers = result.split("\n") + for current_row in row_numbers: + answer = "k.A." + for line in answers: + if line.strip().startswith(f"Eintrag {current_row}:"): + answer = line.split(":", 1)[1].strip() + break + if answer.upper() == "OK": + wiki_confirm = "OK" + alt_article = "" + wiki_explanation = "" + elif answer.upper() == "KEIN WIKIPEDIA-EINTRAG VORHANDEN.": + wiki_confirm = "" + alt_article = "Kein Wikipedia-Eintrag vorhanden." + wiki_explanation = "" + elif answer.startswith("Alternativer Wikipedia-Artikel vorgeschlagen:"): + parts = answer.split(":", 1)[1].split("|") + alt_article = parts[0].strip() if len(parts) > 0 else "k.A." + wiki_explanation = parts[2].strip() if len(parts) > 2 else "" + wiki_confirm = "X" + else: + wiki_confirm = "" + alt_article = answer + wiki_explanation = answer + try: + main_sheet.update(values=[[wiki_confirm]], range_name=f"S{current_row}") + main_sheet.update(values=[[alt_article]], range_name=f"T{current_row}") + main_sheet.update(values=[[wiki_explanation]], range_name=f"U{current_row}") + main_sheet.update(values=[["", "", "", ""]], range_name=f"V{current_row}:Y{current_row}") + debug_print(f"Zeile {current_row} verifiziert: Antwort: {answer}") + except Exception as e: + debug_print(f"Fehler beim Updaten der Zeile {current_row}: {e}") + time.sleep(Config.RETRY_DELAY) + + + # ==================== List Metatitel, Description und Überschriften aus Websiten aus ==================== def scrape_website_details(url):