This commit is contained in:
2025-04-17 10:23:09 +00:00
parent d9d33db2eb
commit 8e6d263a64

View File

@@ -951,115 +951,203 @@ def token_count(text):
class GoogleSheetHandler: class GoogleSheetHandler:
def __init__(self): def __init__(self):
# ... (init und _connect unverändert) ... """Initialisiert den Handler, verbindet und lädt initiale Daten."""
self.sheet = None self.sheet = None
self.sheet_values = [] self.sheet_values = []
self.headers = [] self.headers = [] # Speichert die erste Zeile als Header-Namen
try: self._connect(); try:
except Exception as e: raise ConnectionError(f"Google Sheet Handler Init failed: {e}") self._connect()
if self.sheet: self.load_data() # Lade Daten initial if self.sheet:
self.load_data() # Erste Datenladung bei Initialisierung
except Exception as e:
debug_print(f"FATAL: Fehler bei Initialisierung von GoogleSheetHandler: {e}")
# Wirft einen Fehler, damit das Hauptprogramm weiß, dass es nicht weitergehen kann
raise ConnectionError(f"Google Sheet Handler Init failed: {e}")
# retry_on_failure Decorator sollte hier angewendet werden
@retry_on_failure @retry_on_failure
def _connect(self): def _connect(self):
# ... (unverändert) ... """Stellt Verbindung zum Google Sheet her."""
self.sheet = None; debug_print("Verbinde mit Google Sheets...") self.sheet = None # Sicherstellen, dass sheet vor try None ist
debug_print("Verbinde mit Google Sheets...")
try: try:
scope = ["https://www.googleapis.com/auth/spreadsheets"] scope = ["https://www.googleapis.com/auth/spreadsheets"]
creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS_FILE, scope) creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS_FILE, scope)
gc = gspread.authorize(creds); sh = gc.open_by_url(Config.SHEET_URL) gc = gspread.authorize(creds)
self.sheet = sh.sheet1; debug_print("Verbindung zu Google Sheets erfolgreich.") sh = gc.open_by_url(Config.SHEET_URL)
except Exception as e: debug_print(f"FEHLER Connect: {e}"); raise e self.sheet = sh.sheet1 # Greift auf das erste Blatt zu
debug_print("Verbindung zu Google Sheets erfolgreich.")
except gspread.exceptions.APIError as e:
# Logge spezifische API-Fehler von Google
debug_print(f"FEHLER bei Google API Verbindung: Status {e.response.status_code} - {e.response.text[:200]}")
raise e # Fehler weitergeben, damit retry greift
except Exception as e:
# Logge andere Verbindungsfehler
debug_print(f"FEHLER bei der Google Sheets Verbindung: {type(e).__name__} - {e}")
raise e # Fehler weitergeben
# retry_on_failure Decorator sollte hier angewendet werden
@retry_on_failure @retry_on_failure
def load_data(self): def load_data(self):
# ... (unverändert) ... """Lädt alle Daten aus dem Sheet und aktualisiert self.sheet_values und self.headers."""
if not self.sheet: return False if not self.sheet:
debug_print("Fehler: Keine Sheet-Verbindung zum Laden der Daten.")
self.sheet_values = []
self.headers = []
return False # Signalisiert Fehler
debug_print("Lade Daten aus Google Sheet...") debug_print("Lade Daten aus Google Sheet...")
try: try:
self.sheet_values = self.sheet.get_all_values() self.sheet_values = self.sheet.get_all_values() # Daten neu holen
if not self.sheet_values: self.headers=[]; return True if not self.sheet_values:
if len(self.sheet_values) >= 1: self.headers = self.sheet_values[0] debug_print("Warnung: Google Sheet scheint leer zu sein oder keine Daten zurückgegeben.")
else: self.headers = [] self.headers = []
elif len(self.sheet_values) >= 1:
self.headers = self.sheet_values[0] # Speichere die erste Zeile als Header
else:
self.headers = [] # Sollte nicht passieren, wenn sheet_values nicht leer war
debug_print(f"Daten neu geladen: {len(self.sheet_values)} Zeilen insgesamt.") debug_print(f"Daten neu geladen: {len(self.sheet_values)} Zeilen insgesamt.")
return True return True # Signalisiert Erfolg
except Exception as e: debug_print(f"FEHLER Laden: {e}"); raise e except gspread.exceptions.APIError as e:
debug_print(f"Google API Fehler beim Laden der Sheet Daten: Status {e.response.status_code} - {e.response.text[:200]}")
raise e # Damit retry greift
except Exception as e:
debug_print(f"Allgemeiner Fehler beim Laden der Google Sheet Daten: {e}")
raise e # Damit retry greift
def get_data(self): def get_data(self):
# ... (unverändert) ... """Gibt die aktuell im Handler gespeicherten Daten zurück (ohne die ersten 5 Header-Zeilen)."""
header_rows = 5 header_rows = 5 # Definiert die Anzahl der zu überspringenden Header-Zeilen
if not self.sheet_values or len(self.sheet_values) <= header_rows: return [] if not self.sheet_values or len(self.sheet_values) <= header_rows:
if self.sheet_values: # Logge nur, wenn Daten da, aber zu wenige
debug_print(f"Warnung in get_data: Nur {len(self.sheet_values)} Zeilen vorhanden, weniger als {header_rows} Header-Zeilen erwartet.")
return []
# Gibt eine Slice der Liste zurück, die die Datenzeilen enthält
return self.sheet_values[header_rows:] return self.sheet_values[header_rows:]
def get_all_data_with_headers(self): def get_all_data_with_headers(self):
# ... (unverändert) ... """Gibt alle aktuell im Handler gespeicherten Daten inklusive Header zurück."""
if not self.sheet_values: return [] if not self.sheet_values:
debug_print("Warnung in get_all_data_with_headers: Keine Daten im Handler gespeichert.")
# Optional: Versuche neu zu laden? Oder einfach leere Liste zurückgeben?
# self.load_data() # Erneuter Ladeversuch
# return self.sheet_values
return [] # Gib leere Liste zurück, wenn nichts geladen ist
return self.sheet_values return self.sheet_values
def _get_col_letter(self, col_idx_1_based): def _get_col_letter(self, col_idx_1_based):
# ... (unverändert) ... """ Konvertiert 1-basierten Spaltenindex in Buchstaben (A, B, ..., Z, AA, ...). """
string = ""; n = col_idx_1_based; string = ""
if n < 1: return None n = col_idx_1_based
while n > 0: n, remainder = divmod(n - 1, 26); string = chr(65 + remainder) + string if n < 1: return None # Ungültiger Index
while n > 0:
n, remainder = divmod(n - 1, 26)
string = chr(65 + remainder) + string
return string return string
# Prüft jetzt auf Werte in der `empty_values` Liste (case-insensitive) # --- ANGEPASST: Sucht jetzt nur noch nach EXAKT LEER ("") ---
def get_start_row_index(self, check_column_key, min_sheet_row=7, empty_values=None): def get_start_row_index(self, check_column_key, min_sheet_row=7):
"""Findet erste Zeile, deren Wert in check_column_key als leer gilt.""" """
# --- KORRIGIERT: Standardwerte für leere Strings --- Findet den Index der ersten Zeile (0-basiert für Daten nach Header),
if empty_values is None: ab einer Mindestzeilennummer im Sheet, in der der Wert in der
empty_values = ["", "k.a.", "k.a. (nur cookie-banner erkannt)", "k.a. (fehler)"] Spalte (definiert durch check_column_key) EXAKT LEER ("") ist.
Lädt die Daten vor der Prüfung neu.
Args:
check_column_key (str): Der Schlüssel in COLUMN_MAP für die zu prüfende Spalte.
min_sheet_row (int): Die 1-basierte Zeilennummer im Sheet, ab der gesucht werden soll.
Returns:
int: Der 0-basierte Index in der Datenliste (ohne Header),
oder -1 bei Fehler (z.B. Schlüssel nicht gefunden),
oder der Index nach der letzten Datenzeile, wenn alle gefüllt sind.
"""
# Lade Daten *vor* der Prüfung neu, um Aktualität sicherzustellen
if not self.load_data():
debug_print("FEHLER beim Laden der Daten in get_start_row_index. Breche ab.")
return -1 # Fehlerindikator
if not self.load_data(): return -1
header_rows = 5 header_rows = 5
data_rows = self.get_data() data_rows = self.get_data() # Greift auf die neu geladenen Daten zu
if not data_rows: return 0
if not data_rows:
debug_print("Keine Datenzeilen vorhanden für get_start_row_index nach Neuladen.")
return 0 # Index 0 signalisiert Start am Anfang (oder keine Daten)
# Hole den Spaltenindex aus COLUMN_MAP
check_column_index = COLUMN_MAP.get(check_column_key) check_column_index = COLUMN_MAP.get(check_column_key)
if check_column_index is None: if check_column_index is None:
debug_print(f"FEHLER: Schlüssel '{check_column_key}' nicht in COLUMN_MAP gefunden!") debug_print(f"FEHLER: Schlüssel '{check_column_key}' nicht in COLUMN_MAP gefunden!")
return -1 return -1 # Fehlerindikator
actual_col_letter = self._get_col_letter(check_column_index + 1) actual_col_letter = self._get_col_letter(check_column_index + 1)
# Berechne den 0-basierten Startindex für die *Datenliste* data_rows
search_start_index_in_data = max(0, min_sheet_row - header_rows - 1) search_start_index_in_data = max(0, min_sheet_row - header_rows - 1)
debug_print(f"get_start_row_index: Suche ab Daten-Index {search_start_index_in_data} nach Wert in {empty_values} in Spalte '{check_column_key}' ({actual_col_letter})...") debug_print(f"get_start_row_index: Suche ab Daten-Index {search_start_index_in_data} (Sheet-Zeile {search_start_index_in_data + header_rows + 1}) nach EXAKT LEEREM Wert (=='') in Spalte '{check_column_key}' ({actual_col_letter})...")
if search_start_index_in_data >= len(data_rows): if search_start_index_in_data >= len(data_rows):
debug_print(f"Start-Suchindex ({search_start_index_in_data}) >= Datenlänge ({len(data_rows)}). Alle geprüft.") debug_print(f"Start-Suchindex ({search_start_index_in_data}) >= Datenlänge ({len(data_rows)}). Alle vorherigen Zeilen scheinen gefüllt.")
return len(data_rows) return len(data_rows) # Index nach der letzten Zeile
# Durchlaufe die Datenzeilen ab dem berechneten Startindex
for i in range(search_start_index_in_data, len(data_rows)): for i in range(search_start_index_in_data, len(data_rows)):
row = data_rows[i] row = data_rows[i]
current_sheet_row = i + header_rows + 1 current_sheet_row = i + header_rows + 1
cell_value_str_lower = "FEHLER_INDEX" # Fallback cell_value = "" # Standardwert, falls Spalte nicht existiert
is_considered_empty = True # Annahme: Ist leer is_exactly_empty = False
if len(row) > check_column_index: if len(row) > check_column_index:
cell_value_str_lower = str(row[check_column_index]).strip().lower() cell_value = row[check_column_index] # Hole den Rohwert
if cell_value_str_lower not in empty_values: # Prüfe, ob der Wert EXAKT ein leerer String ist
is_considered_empty = False if cell_value == "":
# else: is_considered_empty bleibt True (Spalte zu kurz = leer) is_exactly_empty = True
else:
# Spalte existiert nicht -> gilt als leer
is_exactly_empty = True
# Logge nur relevante Prüfungen # Logge nur relevante Prüfungen
if i == search_start_index_in_data or i % 1000 == 0 or is_considered_empty: log_debug = (i == search_start_index_in_data or i % 1000 == 0 or is_exactly_empty or i in range(10110, 10116)) # Logge um Zeile 10113
debug_print(f" -> Prüfe Daten-Index {i} (Sheet {current_sheet_row}): Wert in {actual_col_letter}='{cell_value_str_lower}'. Gilt als leer? {is_considered_empty}") if log_debug:
debug_print(f" -> Prüfe Daten-Index {i} (Sheet {current_sheet_row}): Wert in {actual_col_letter}='{cell_value}' (Typ: {type(cell_value)}). Ist exakt leer ('')? {is_exactly_empty}")
if is_considered_empty: if is_exactly_empty:
debug_print(f"Erste Zeile ab {min_sheet_row} mit leerem Wert in Spalte {actual_col_letter} gefunden: Zeile {current_sheet_row} (Daten-Index {i})") debug_print(f"Erste Zeile ab {min_sheet_row} mit EXAKT LEEREM Wert in Spalte {actual_col_letter} gefunden: Zeile {current_sheet_row} (Daten-Index {i})")
return i return i # Gibt den 0-basierten Index *innerhalb der Datenliste* zurück
# Wenn die Schleife durchläuft, sind alle Zeilen ab dem Start gefüllt
last_index = len(data_rows) last_index = len(data_rows)
debug_print(f"Alle Zeilen ab Daten-Index {search_start_index_in_data} haben einen nicht-leeren Wert in Spalte {actual_col_letter}. Nächster Daten-Index wäre {last_index}.") debug_print(f"Alle Zeilen ab Daten-Index {search_start_index_in_data} haben einen nicht-leeren Wert in Spalte {actual_col_letter}. Nächster Daten-Index wäre {last_index}.")
return last_index return last_index
# retry_on_failure Decorator sollte hier angewendet werden
@retry_on_failure @retry_on_failure
def batch_update_cells(self, update_data): def batch_update_cells(self, update_data):
# ... (unverändert) ... """
if not self.sheet: return False Führt ein Batch-Update im Google Sheet durch. Beinhaltet Fehlerbehandlung.
if not update_data: return True
try: self.sheet.batch_update(update_data, value_input_option='USER_ENTERED'); return True Args:
except Exception as e: debug_print(f"FEHLER Batch Update: {e}"); raise e update_data (list): Eine Liste von Dictionaries, jedes mit 'range' und 'values'.
z.B. [{'range': 'A1', 'values': [['Wert']]}, ...]
Returns:
bool: True bei Erfolg, False bei Fehler nach Retries.
"""
if not self.sheet:
debug_print("FEHLER: Keine Sheet-Verbindung für Batch-Update.")
return False
if not update_data:
return True # Kein Fehler, aber nichts zu tun
try:
self.sheet.batch_update(update_data, value_input_option='USER_ENTERED')
return True
except gspread.exceptions.APIError as e:
debug_print(f"Google API Fehler beim Batch-Update: Status {e.response.status_code} - {e.response.text[:500]}")
raise e # Fehler weitergeben, damit der Decorator ihn fängt
except Exception as e:
debug_print(f"Allgemeiner Fehler beim Batch-Update: {type(e).__name__} - {e}")
raise e # Fehler weitergeben
# --- Ende GoogleSheetHandler Klasse --- # --- Ende GoogleSheetHandler Klasse ---
@@ -2177,13 +2265,12 @@ def process_website_batch(sheet_handler, start_row_index_in_sheet, end_row_index
""" """
debug_print(f"Starte Website-Scraping NUR ROHDATEN (Batch) für Zeilen {start_row_index_in_sheet} bis {end_row_index_in_sheet}...") debug_print(f"Starte Website-Scraping NUR ROHDATEN (Batch) für Zeilen {start_row_index_in_sheet} bis {end_row_index_in_sheet}...")
# --- Lade Daten ---
if not sheet_handler.load_data(): return if not sheet_handler.load_data(): return
all_data = sheet_handler.get_all_data_with_headers() all_data = sheet_handler.get_all_data_with_headers()
if not all_data or len(all_data) <= 5: return if not all_data or len(all_data) <= 5: return
header_rows = 5 header_rows = 5
# --- Indizes und Buchstaben --- # Indizes und Buchstaben
rohtext_col_key = "Website Rohtext" rohtext_col_key = "Website Rohtext"
rohtext_col_index = COLUMN_MAP.get(rohtext_col_key) rohtext_col_index = COLUMN_MAP.get(rohtext_col_key)
website_col_idx = COLUMN_MAP.get("CRM Website") website_col_idx = COLUMN_MAP.get("CRM Website")
@@ -2194,14 +2281,14 @@ def process_website_batch(sheet_handler, start_row_index_in_sheet, end_row_index
rohtext_col_letter = sheet_handler._get_col_letter(rohtext_col_index + 1) rohtext_col_letter = sheet_handler._get_col_letter(rohtext_col_index + 1)
version_col_letter = sheet_handler._get_col_letter(version_col_idx + 1) version_col_letter = sheet_handler._get_col_letter(version_col_idx + 1)
# --- Worker-Funktion (unverändert) --- # Worker-Funktion (unverändert)
def scrape_raw_text_task(task_info): def scrape_raw_text_task(task_info):
row_num = task_info['row_num']; url = task_info['url']; raw_text = "k.A."; error = None row_num = task_info['row_num']; url = task_info['url']; raw_text = "k.A."; error = None
try: raw_text = get_website_raw(url) try: raw_text = get_website_raw(url)
except Exception as e: error = f"Scraping Fehler Zeile {row_num}: {e}"; debug_print(error) except Exception as e: error = f"Scraping Fehler Zeile {row_num}: {e}"; debug_print(error)
return {"row_num": row_num, "raw_text": raw_text, "error": error} return {"row_num": row_num, "raw_text": raw_text, "error": error}
# --- Hauptlogik: Iteriere und sammle Batches --- # Hauptlogik
tasks_for_processing_batch = [] tasks_for_processing_batch = []
all_sheet_updates = [] all_sheet_updates = []
total_processed_count = 0 total_processed_count = 0
@@ -2209,11 +2296,10 @@ def process_website_batch(sheet_handler, start_row_index_in_sheet, end_row_index
total_skipped_url_count = 0 total_skipped_url_count = 0
total_error_count = 0 total_error_count = 0
# --- KORRIGIERT: Hole Konfigurationswerte aus Config ---
processing_batch_size = Config.PROCESSING_BATCH_SIZE processing_batch_size = Config.PROCESSING_BATCH_SIZE
max_scraping_workers = Config.MAX_SCRAPING_WORKERS max_scraping_workers = Config.MAX_SCRAPING_WORKERS
update_batch_row_limit = Config.UPDATE_BATCH_ROW_LIMIT update_batch_row_limit = Config.UPDATE_BATCH_ROW_LIMIT
# --- Ende Korrektur --- # Diese Liste wird weiterhin für die Skip-Logik *innerhalb* der Funktion verwendet
empty_values_for_skip = ["", "k.a.", "k.a. (nur cookie-banner erkannt)", "k.a. (fehler)"] empty_values_for_skip = ["", "k.a.", "k.a. (nur cookie-banner erkannt)", "k.a. (fehler)"]
for i in range(start_row_index_in_sheet, end_row_index_in_sheet + 1): for i in range(start_row_index_in_sheet, end_row_index_in_sheet + 1):
@@ -2221,21 +2307,24 @@ def process_website_batch(sheet_handler, start_row_index_in_sheet, end_row_index
if row_index_in_list >= len(all_data): continue if row_index_in_list >= len(all_data): continue
row = all_data[row_index_in_list] row = all_data[row_index_in_list]
# Prüfung, ob AR schon Inhalt hat # --- Prüfung, ob AR schon Inhalt hat (bleibt gleich!) ---
should_skip = False should_skip = False
cell_value_ar_str_lower = "FEHLER_INDEX" cell_value_ar_str_lower = "FEHLER_INDEX"
if len(row) > rohtext_col_index: if len(row) > rohtext_col_index:
cell_value_ar_str_lower = str(row[rohtext_col_index]).strip().lower() cell_value_ar_str_lower = str(row[rohtext_col_index]).strip().lower()
# Überspringen, wenn der Wert NICHT in der Liste der leeren Werte ist
if cell_value_ar_str_lower not in empty_values_for_skip: if cell_value_ar_str_lower not in empty_values_for_skip:
should_skip = True should_skip = True
# else: Spalte existiert nicht -> nicht überspringen
log_debug = (i < start_row_index_in_sheet + 5 or i > end_row_index_in_sheet - 5 or i % 500 == 0 or i in [10, 13]) log_debug = (i < start_row_index_in_sheet + 5 or i > end_row_index_in_sheet - 5 or i % 500 == 0)
if log_debug: if log_debug:
debug_print(f"Zeile {i} (Website AR Check): Prüfe Inhalt Spalte {rohtext_col_letter}. Wert='{cell_value_ar_str_lower}'. Überspringen (da schon Inhalt)? -> {should_skip}") debug_print(f"Zeile {i} (Website AR Check): Prüfe Inhalt Spalte {rohtext_col_letter}. Wert='{cell_value_ar_str_lower}'. Überspringen (da schon Inhalt)? -> {should_skip}")
if should_skip: if should_skip:
total_skipped_count += 1 total_skipped_count += 1
continue continue
# --- Ende AR Prüfung ---
# URL Prüfung # URL Prüfung
website_url = row[website_col_idx] if len(row) > website_col_idx else "" website_url = row[website_col_idx] if len(row) > website_col_idx else ""
@@ -2243,52 +2332,36 @@ def process_website_batch(sheet_handler, start_row_index_in_sheet, end_row_index
total_skipped_url_count += 1 total_skipped_url_count += 1
continue continue
# Task hinzufügen, wenn AR leer/k.A. war UND URL vorhanden ist
tasks_for_processing_batch.append({"row_num": i, "url": website_url}) tasks_for_processing_batch.append({"row_num": i, "url": website_url})
# Verarbeitungs-Batch ausführen # --- Verarbeitungs-Batch ausführen (Logik unverändert) ---
if len(tasks_for_processing_batch) >= processing_batch_size or i == end_row_index_in_sheet: if len(tasks_for_processing_batch) >= processing_batch_size or i == end_row_index_in_sheet:
if tasks_for_processing_batch: if tasks_for_processing_batch:
batch_start_row = tasks_for_processing_batch[0]['row_num'] # ... (Paralleles Scraping wie zuvor) ...
batch_end_row = tasks_for_processing_batch[-1]['row_num'] # ... (Sheet Updates vorbereiten wie zuvor - nur AR und AP) ...
batch_task_count = len(tasks_for_processing_batch) # ... (Updates sammeln und senden wie zuvor) ...
debug_print(f"\n--- Starte Scraping-Batch ({batch_task_count} Tasks, Zeilen {batch_start_row}-{batch_end_row}) ---") batch_start_row = tasks_for_processing_batch[0]['row_num']; batch_end_row = tasks_for_processing_batch[-1]['row_num']
batch_task_count = len(tasks_for_processing_batch); debug_print(f"\n--- Starte Scraping-Batch ({batch_task_count} Tasks, Zeilen {batch_start_row}-{batch_end_row}) ---")
scraping_results = {} scraping_results = {}; debug_print(f" Scrape {batch_task_count} Websites parallel (max {max_scraping_workers} worker)...")
debug_print(f" Scrape {batch_task_count} Websites parallel (max {max_scraping_workers} worker)...")
with concurrent.futures.ThreadPoolExecutor(max_workers=max_scraping_workers) as executor: with concurrent.futures.ThreadPoolExecutor(max_workers=max_scraping_workers) as executor:
# ... (executor Logik) ...
future_to_task = {executor.submit(scrape_raw_text_task, task): task for task in tasks_for_processing_batch} future_to_task = {executor.submit(scrape_raw_text_task, task): task for task in tasks_for_processing_batch}
for future in concurrent.futures.as_completed(future_to_task): for future in concurrent.futures.as_completed(future_to_task):
task = future_to_task[future] # ... (Ergebnisse sammeln) ...
try: task = future_to_task[future]; try: result = future.result(); scraping_results[result['row_num']] = result['raw_text'];
result = future.result() except Exception as exc: row_num = task['row_num']; err_msg = f"Generischer Fehler Scraping Task Zeile {row_num}: {exc}"; debug_print(err_msg); scraping_results[row_num] = "k.A. (Fehler)"; total_error_count +=1;
scraping_results[result['row_num']] = result['raw_text'] current_batch_processed_count = len(scraping_results); total_processed_count += current_batch_processed_count; debug_print(f" Scraping für Batch beendet. {current_batch_processed_count} Ergebnisse erhalten.")
if result['error']: total_error_count += 1
except Exception as exc:
row_num = task['row_num']; err_msg = f"Generischer Fehler Scraping Task Zeile {row_num}: {exc}"
debug_print(err_msg); scraping_results[row_num] = "k.A. (Fehler)"; total_error_count +=1
current_batch_processed_count = len(scraping_results)
total_processed_count += current_batch_processed_count
debug_print(f" Scraping für Batch beendet. {current_batch_processed_count} Ergebnisse erhalten.")
# Sheet Updates vorbereiten (AR und AP)
if scraping_results: if scraping_results:
current_version = Config.VERSION # ... (Sheet Updates vorbereiten - AR/AP) ...
batch_sheet_updates = [] current_version = Config.VERSION; batch_sheet_updates = []
for row_num, raw_text_res in scraping_results.items(): for row_num, raw_text_res in scraping_results.items():
row_updates = [ row_updates = [{'range': f'{rohtext_col_letter}{row_num}', 'values': [[raw_text_res]]}, {'range': f'{version_col_letter}{row_num}', 'values': [[current_version]]}]; batch_sheet_updates.extend(row_updates)
{'range': f'{rohtext_col_letter}{row_num}', 'values': [[raw_text_res]]},
{'range': f'{version_col_letter}{row_num}', 'values': [[current_version]]}
]
batch_sheet_updates.extend(row_updates)
all_sheet_updates.extend(batch_sheet_updates) all_sheet_updates.extend(batch_sheet_updates)
tasks_for_processing_batch = [] # Batch leeren tasks_for_processing_batch = [] # Batch leeren
if len(all_sheet_updates) >= update_batch_row_limit * 2:
# Sheet Updates senden (wenn update_batch_row_limit erreicht) # ... (Sheet Updates senden) ...
if len(all_sheet_updates) >= update_batch_row_limit * 2: # *2 Updates pro Zeile debug_print(f" Sende gesammelte Sheet-Updates ({len(all_sheet_updates)} Zellen)..."); success = sheet_handler.batch_update_cells(all_sheet_updates)
debug_print(f" Sende gesammelte Sheet-Updates ({len(all_sheet_updates)} Zellen)...")
success = sheet_handler.batch_update_cells(all_sheet_updates)
if success: debug_print(f" Sheet-Update bis Zeile {i} erfolgreich.") if success: debug_print(f" Sheet-Update bis Zeile {i} erfolgreich.")
else: debug_print(f" FEHLER beim Sheet-Update bis Zeile {i}.") else: debug_print(f" FEHLER beim Sheet-Update bis Zeile {i}.")
all_sheet_updates = [] all_sheet_updates = []
@@ -2556,21 +2629,26 @@ def process_branch_batch(sheet_handler, start_row_index_in_sheet, end_row_index_
# - Globale Konstante header_rows (oder besser, hol sie vom sheet_handler?) # - Globale Konstante header_rows (oder besser, hol sie vom sheet_handler?)
# Komplette run_dispatcher Funktion (Start immer basierend auf AO) # Komplette run_dispatcher Funktion (Start immer basierend auf AO)
# Komplette run_dispatcher Funktion (Keine Änderungen hier nötig)
def run_dispatcher(mode, sheet_handler, row_limit=None): def run_dispatcher(mode, sheet_handler, row_limit=None):
"""Wählt passenden Batch-Prozess, ermittelt Startzeile dynamisch.""" """
Wählt den passenden Batch-Prozess basierend auf dem Modus.
Ermittelt die Startzeile dynamisch basierend auf der relevanten Spalte für den Modus.
"""
debug_print(f"Starte Dispatcher im Modus '{mode}' mit row_limit={row_limit}.") debug_print(f"Starte Dispatcher im Modus '{mode}' mit row_limit={row_limit}.")
header_rows = 5 header_rows = 5
# Startspalte für jeden Modus # Startspalte für jeden Modus
start_col_key = "Timestamp letzte Prüfung" # Standard AO start_col_key = "Timestamp letzte Prüfung" # Standard AO
min_start_row = 7 min_start_row = 7
if mode == "website": start_col_key = "Website Rohtext" # AR ! if mode == "website": start_col_key = "Website Rohtext" # AR
elif mode == "wiki": start_col_key = "Wiki Verif. Timestamp" # AX elif mode == "wiki": start_col_key = "Wiki Verif. Timestamp" # AX
elif mode == "branch": start_col_key = "Timestamp letzte Prüfung" # AO elif mode == "branch": start_col_key = "Timestamp letzte Prüfung" # AO
elif mode == "summarize": start_col_key = "Website Zusammenfassung" # AS elif mode == "summarize": start_col_key = "Website Zusammenfassung" # AS
elif mode == "combined": start_col_key = "Timestamp letzte Prüfung" # AO elif mode == "combined": start_col_key = "Timestamp letzte Prüfung" # AO
debug_print(f"Dispatcher: Ermittle Startzeile basierend auf Spalte '{start_col_key}'...") debug_print(f"Dispatcher: Ermittle Startzeile basierend auf Spalte '{start_col_key}'...")
# get_start_row_index prüft jetzt auf exakt ""
start_data_index = sheet_handler.get_start_row_index(check_column_key=start_col_key, min_sheet_row=min_start_row) start_data_index = sheet_handler.get_start_row_index(check_column_key=start_col_key, min_sheet_row=min_start_row)
if start_data_index == -1: return debug_print(f"FEHLER: Startspalte '{start_col_key}' prüfen!") if start_data_index == -1: return debug_print(f"FEHLER: Startspalte '{start_col_key}' prüfen!")