diff --git a/brancheneinstufung.py b/brancheneinstufung.py index 0f84e3c3..2bfde7a2 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -1,14 +1,13 @@ #!/usr/bin/env python3 """ -v1.6.2: Verfeinere Timestamp-Logik & integriere ML-Datenvorbereitung +v1.6.3: Beschleunige Website-Scraping durch gebündelte Sheet-Updates Git-Änderungsbeschreibung: -- Passe Dispatcher (`run_dispatcher`) und `GoogleSheetHandler.get_start_row_index` an, um den Startpunkt basierend auf dem Website Scrape Timestamp (Spalte AT) zu bestimmen. -- Implementiere individuelle Timestamp-Prüfungen in den Batch-Funktionen (`process_verification_only` (AN), `process_website_batch` (AT), `process_branch_batch` (AO)), um das erneute Verarbeiten abgeschlossener Zeilen zu verhindern. -- Überarbeite `_process_single_row` (`full_run`, `reeval`), um für jeden Teilbereich (Wiki, Website, Chat) den zugehörigen Timestamp zu prüfen und nur bei Bedarf auszuführen. -- Passe `_process_batch` an, sodass es nur noch Ergebnisspalten (S-Y) schreibt; Timestamps werden jetzt von der aufrufenden Funktion gesetzt. -- Füge neue Spalten (AT: Website TS, AU: Gesch. Techniker Bucket, AV: Finaler Umsatz, AW: Finaler MA) zur `alignment_demo` und `COLUMN_MAP` hinzu. -- Integriere die Funktion `prepare_data_for_modeling` als Methode in die `DataProcessor`-Klasse (wird noch nicht aktiv in einem Modus aufgerufen). +- Überarbeite `process_website_batch` zur Leistungssteigerung. +- Implementiere das Sammeln von Zell-Updates (`AR`, `AS`, `AT`, `AP`) für mehrere Zeilen in einer Liste (`all_sheet_updates`). +- Sende die gesammelten Updates gebündelt über einen einzigen `batch_update_cells`-Aufruf an Google Sheets, wenn ein Limit (`update_batch_row_limit`) erreicht ist oder die Schleife endet. +- Ziel: Reduzierung der Anzahl von Google Sheets API-Aufrufen und Beschleunigung des Website-Scraping-Prozesses. +- Stelle sicher, dass auch ein letzter, unvollständiger Batch nach der Hauptschleife gesendet wird. """ import os @@ -52,7 +51,7 @@ LOG_DIR = "Log" # ==================== KONFIGURATION ==================== class Config: - VERSION = "v1.6.2" + VERSION = "v1.6.3" LANG = "de" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" MAX_RETRIES = 3 @@ -2207,11 +2206,12 @@ def _process_batch(sheet, batches, row_numbers): # time.sleep(Config.RETRY_DELAY) # Entfernt # Komplette Funktion process_website_batch (prüft jetzt Timestamp AT mit erzwungenem Debugging) +# Komplette Funktion process_website_batch (MIT Batched Google Sheet Updates) def process_website_batch(sheet_handler, start_row_index_in_sheet, end_row_index_in_sheet): """ Batch-Prozess für Website-Scraping. Lädt Daten neu, prüft für jede Zeile im Bereich, ob Timestamp AT bereits gesetzt ist und überspringt diese ggf. - Setzt AT + AP für bearbeitete Zeilen. + Setzt AT + AP für bearbeitete Zeilen. Sendet Updates in Batches an Google Sheets. """ debug_print(f"Starte Website-Scraping (Batch) für Zeilen {start_row_index_in_sheet} bis {end_row_index_in_sheet}...") @@ -2223,7 +2223,7 @@ def process_website_batch(sheet_handler, start_row_index_in_sheet, end_row_index debug_print("FEHLER/WARNUNG: Keine Daten zum Verarbeiten in process_website_batch gefunden.") return - sheet = sheet_handler.sheet + # Hole Indizes und Spaltenbuchstaben (wie zuvor) timestamp_col_key = "Website Scrape Timestamp" timestamp_col_index = COLUMN_MAP.get(timestamp_col_key) website_col_idx = COLUMN_MAP.get("CRM Website") @@ -2240,71 +2240,88 @@ def process_website_batch(sheet_handler, start_row_index_in_sheet, end_row_index summary_col_letter = sheet_handler._get_col_letter(summary_col_idx + 1) version_col_letter = sheet_handler._get_col_letter(version_col_idx + 1) + # --- NEU: Liste für gesammelte Updates --- + all_sheet_updates = [] + rows_in_current_batch = 0 + update_batch_size = Config.BATCH_SIZE * 4 # Da wir 4 Zellen pro Zeile updaten, Batch entsprechend anpassen? Oder einfach 50? + # Nehmen wir eine feste Anzahl von Zeilen, deren Updates wir sammeln + update_batch_row_limit = 50 # Sammle Updates für 50 Zeilen, bevor gesendet wird + processed_count = 0 skipped_count = 0 skipped_url_count = 0 for i in range(start_row_index_in_sheet, end_row_index_in_sheet + 1): row_index_in_list = i - 1 - if row_index_in_list >= len(all_data): - debug_print(f"Warnung (Website): Zeilenindex {row_index_in_list} außerhalb des Datenbereichs ({len(all_data)} Zeilen).") - continue - + if row_index_in_list >= len(all_data): continue row = all_data[row_index_in_list] - # --- Timestamp-Prüfung für jede Zeile (AT) --- - ts_value_at = "INDEX_FEHLER" + # --- Timestamp-Prüfung (AT) --- should_skip = False if len(row) > timestamp_col_index: - ts_value_at = row[timestamp_col_index] - if str(ts_value_at).strip(): + if str(row[timestamp_col_index]).strip(): should_skip = True + # Optional: Debugging der Prüfung reduzieren + # if i % 100 == 0: debug_print(f"Zeile {i} (Website Check): Prüfe TS {ts_col_letter}. Überspringen? -> {should_skip}") - # Debug Log - log_debug = (i < start_row_index_in_sheet + 5 or i > end_row_index_in_sheet - 5 or i % 500 == 0 or i in range(2122, 2132)) - if log_debug: - debug_print(f"Zeile {i} (Website Check): Prüfe Timestamp {ts_col_letter}. Rohwert='{ts_value_at}'. Überspringen? -> {should_skip}") - - # --- ABSOLUT KRITISCH: Korrekte IF/ELSE Struktur --- if should_skip: - # Wenn should_skip True ist, wird NUR das hier ausgeführt - # debug_print(f"Zeile {i}: *** WIRD ÜBERSPRUNGEN (Timestamp AT vorhanden) ***") # Jetzt wirklich übersprungen skipped_count += 1 - continue # Springt DIREKT zur nächsten Iteration der for-Schleife - + continue else: - # --- Verarbeitung NUR, wenn should_skip False war --- - debug_print(f"Zeile {i}: Timestamp AT nicht vorhanden oder leer. Verarbeitung wird gestartet.") - + # --- Verarbeitung (wenn nicht übersprungen) --- website_url = row[website_col_idx] if len(row) > website_col_idx else "" if not website_url or website_url.strip().lower() == "k.a.": skipped_url_count += 1 - # Wichtig: Auch hier continue, um leere Updates zu vermeiden continue - debug_print(f"Zeile {i}: Verarbeite Website {website_url}...") + # Scrapen und Zusammenfassen raw_text = get_website_raw(website_url) summary = summarize_website_content(raw_text) processed_count += 1 - updates = [] + # Einzelne Updates für diese Zeile vorbereiten current_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_version = Config.VERSION + row_updates = [ + {'range': f'{rohtext_col_letter}{i}', 'values': [[raw_text]]}, + {'range': f'{summary_col_letter}{i}', 'values': [[summary]]}, + {'range': f'{ts_col_letter}{i}', 'values': [[current_timestamp]]}, # AT Timestamp + {'range': f'{version_col_letter}{i}', 'values': [[current_version]]} # AP Version + ] - updates.append({'range': f'{rohtext_col_letter}{i}', 'values': [[raw_text]]}) - updates.append({'range': f'{summary_col_letter}{i}', 'values': [[summary]]}) - updates.append({'range': f'{ts_col_letter}{i}', 'values': [[current_timestamp]]}) # AT Timestamp - updates.append({'range': f'{version_col_letter}{i}', 'values': [[current_version]]}) # AP Version + # --- NEU: Updates sammeln --- + all_sheet_updates.extend(row_updates) + rows_in_current_batch += 1 - if updates: - success = sheet_handler.batch_update_cells(updates) - if success: - debug_print(f"Zeile {i}: Website-Daten erfolgreich aktualisiert.") - else: - debug_print(f"FEHLER beim Schreiben der Website-Updates für Zeile {i}.") + # --- NEU: Batch senden, wenn Limit erreicht oder letzte Zeile --- + # Prüfe, ob die Anzahl der *Zeilen*, deren Updates gesammelt wurden, das Limit erreicht + if rows_in_current_batch >= update_batch_row_limit or i == end_row_index_in_sheet: + if all_sheet_updates: # Nur senden, wenn Updates vorhanden sind + debug_print(f"Sende Batch-Update für {rows_in_current_batch} Zeilen (insgesamt {len(all_sheet_updates)} Zellen)...") + success = sheet_handler.batch_update_cells(all_sheet_updates) + if success: + debug_print(f"Sheet-Update für Zeilen bis {i} erfolgreich.") + else: + debug_print(f"FEHLER beim Sheet-Update für Zeilen bis {i}.") + # Optional: Hier überlegen, ob man abbricht oder weitermacht + # Liste und Zähler zurücksetzen + all_sheet_updates = [] + rows_in_current_batch = 0 + + # Kurze Pause nach jeder Website-Verarbeitung (oder nur nach Batch-Update?) + # Besser hier lassen, um APIs nicht zu überlasten time.sleep(Config.RETRY_DELAY) - # --- Ende des ELSE-Blocks --- + # --- Ende ELSE-Block --- + + # --- NEU: Ggf. letzte gesammelte Updates senden --- + if all_sheet_updates: + debug_print(f"Sende letztes Batch-Update für {rows_in_current_batch} Zeilen ({len(all_sheet_updates)} Zellen)...") + success = sheet_handler.batch_update_cells(all_sheet_updates) + if success: + debug_print(f"Letztes Sheet-Update erfolgreich.") + else: + debug_print(f"FEHLER beim letzten Sheet-Update.") debug_print(f"Website-Scraping (Batch) abgeschlossen. {processed_count} Websites gescraped, {skipped_count} Zeilen wg. Timestamp übersprungen, {skipped_url_count} Zeilen ohne URL übersprungen.")