bugfix
This commit is contained in:
@@ -1092,82 +1092,116 @@ def token_count(text):
|
||||
|
||||
class GoogleSheetHandler:
|
||||
def __init__(self):
|
||||
"""Initialisiert den Handler, verbindet und lädt initiale Daten."""
|
||||
self.sheet = None
|
||||
self.sheet_values = []
|
||||
self.headers = []
|
||||
self.headers = [] # Speichert die erste Zeile als Header-Namen
|
||||
try:
|
||||
self._connect()
|
||||
if self.sheet:
|
||||
self.load_data()
|
||||
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
|
||||
def _connect(self):
|
||||
self.sheet = None
|
||||
"""Stellt Verbindung zum Google Sheet her."""
|
||||
self.sheet = None # Sicherstellen, dass sheet vor try None ist
|
||||
debug_print("Verbinde mit Google Sheets...")
|
||||
try:
|
||||
scope = ["https://www.googleapis.com/auth/spreadsheets"]
|
||||
creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS_FILE, scope)
|
||||
gc = gspread.authorize(creds)
|
||||
sh = gc.open_by_url(Config.SHEET_URL)
|
||||
self.sheet = sh.sheet1
|
||||
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
|
||||
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
|
||||
raise e # Fehler weitergeben
|
||||
|
||||
# retry_on_failure Decorator sollte hier angewendet werden
|
||||
@retry_on_failure
|
||||
def load_data(self):
|
||||
"""Lädt alle Daten aus dem Sheet und aktualisiert self.sheet_values und self.headers."""
|
||||
if not self.sheet:
|
||||
debug_print("Fehler: Keine Sheet-Verbindung zum Laden der Daten.")
|
||||
self.sheet_values = []
|
||||
self.headers = []
|
||||
return False
|
||||
return False # Signalisiert Fehler
|
||||
debug_print("Lade Daten aus Google Sheet...")
|
||||
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:
|
||||
debug_print("Warnung: Google Sheet scheint leer zu sein oder keine Daten zurückgegeben.")
|
||||
self.headers = []
|
||||
return True
|
||||
if len(self.sheet_values) >= 1: self.headers = self.sheet_values[0]
|
||||
else: self.headers = []
|
||||
return True # Kein Fehler beim Laden, aber keine Daten
|
||||
if 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.")
|
||||
return True
|
||||
return True # Signalisiert Erfolg
|
||||
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
|
||||
raise e # Damit retry greift
|
||||
except Exception as e:
|
||||
debug_print(f"Allgemeiner Fehler beim Laden der Google Sheet Daten: {e}")
|
||||
raise e
|
||||
raise e # Damit retry greift
|
||||
|
||||
def get_data(self):
|
||||
header_rows = 5
|
||||
"""Gibt die aktuell im Handler gespeicherten Daten zurück (ohne die ersten 5 Header-Zeilen)."""
|
||||
header_rows = 5 # Definiert die Anzahl der zu überspringenden Header-Zeilen
|
||||
if not self.sheet_values or len(self.sheet_values) <= header_rows:
|
||||
if self.sheet_values:
|
||||
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:]
|
||||
|
||||
def get_all_data_with_headers(self):
|
||||
"""Gibt alle aktuell im Handler gespeicherten Daten inklusive Header zurück."""
|
||||
if not self.sheet_values:
|
||||
debug_print("Warnung in get_all_data_with_headers: Keine Daten im Handler gespeichert.")
|
||||
return []
|
||||
return self.sheet_values
|
||||
|
||||
def _get_col_letter(self, col_idx_1_based):
|
||||
string = ""; n = col_idx_1_based
|
||||
if n < 1: return None
|
||||
while n > 0: n, remainder = divmod(n - 1, 26); string = chr(65 + remainder) + string
|
||||
""" Konvertiert 1-basierten Spaltenindex in Buchstaben (A, B, ..., Z, AA, ...). """
|
||||
string = ""
|
||||
n = col_idx_1_based
|
||||
if n < 1: return None # Ungültiger Index
|
||||
while n > 0:
|
||||
n, remainder = divmod(n - 1, 26)
|
||||
string = chr(65 + remainder) + string
|
||||
return string
|
||||
|
||||
# Angepasst: Sucht nur noch nach EXAKT LEER ("")
|
||||
def get_start_row_index(self, check_column_key, min_sheet_row=7):
|
||||
if not self.load_data(): return -1
|
||||
"""
|
||||
Findet den Index der ersten Zeile (0-basiert für Daten nach Header),
|
||||
ab einer Mindestzeilennummer im Sheet, in der der Wert in der
|
||||
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.
|
||||
"""
|
||||
if not self.load_data(): return -1 # Fehlerindikator
|
||||
header_rows = 5
|
||||
data_rows = self.get_data()
|
||||
if not data_rows: return 0
|
||||
@@ -1179,6 +1213,7 @@ class GoogleSheetHandler:
|
||||
|
||||
actual_col_letter = self._get_col_letter(check_column_index + 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 EXAKT LEEREM Wert (=='') in Spalte '{check_column_key}' ({actual_col_letter})...")
|
||||
|
||||
if search_start_index_in_data >= len(data_rows):
|
||||
@@ -1192,7 +1227,7 @@ class GoogleSheetHandler:
|
||||
if len(row) > check_column_index:
|
||||
cell_value = row[check_column_index]
|
||||
if cell_value != "": is_exactly_empty = False
|
||||
log_debug = (i == search_start_index_in_data or i % 1000 == 0 or is_exactly_empty or i in range(10110, 10116))
|
||||
log_debug = (i == search_start_index_in_data or i % 1000 == 0 or is_exactly_empty or i in range(10110, 10116)) # Angepasste Log-Punkte
|
||||
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_exactly_empty:
|
||||
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})")
|
||||
@@ -1202,13 +1237,58 @@ class GoogleSheetHandler:
|
||||
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
|
||||
|
||||
# --- ÜBERARBEITETE METHODE mit besserem Error Handling ---
|
||||
@retry_on_failure
|
||||
def batch_update_cells(self, update_data):
|
||||
if not self.sheet: debug_print("FEHLER: Keine Sheet-Verbindung für Batch-Update."); return False
|
||||
if not update_data: return True
|
||||
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 Batch-Update: Status {e.response.status_code} - {e.response.text[:500]}"); raise e
|
||||
except Exception as e: debug_print(f"Allgemeiner Fehler Batch-Update: {type(e).__name__} - {e}"); raise e
|
||||
"""
|
||||
Führt ein Batch-Update im Google Sheet durch. Beinhaltet robustere
|
||||
Fehlerbehandlung und gibt nur True bei echtem Erfolg zurück.
|
||||
|
||||
Args:
|
||||
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:
|
||||
# debug_print("Keine Daten für Batch-Update vorhanden.") # Weniger Lärm
|
||||
return True # Nichts zu tun ist technisch ein Erfolg
|
||||
|
||||
success = False # Standard: Nicht erfolgreich
|
||||
try:
|
||||
debug_print(f" -> Versuche sheet.batch_update mit {len(update_data)} Operationen...")
|
||||
self.sheet.batch_update(update_data, value_input_option='USER_ENTERED')
|
||||
# Wenn keine Exception aufgetreten ist, war es erfolgreich
|
||||
success = True
|
||||
# Logge Erfolg nicht mehr hier, sondern in der aufrufenden Funktion
|
||||
# debug_print(f" -> sheet.batch_update erfolgreich abgeschlossen.")
|
||||
|
||||
except gspread.exceptions.APIError as e:
|
||||
# Spezifische Fehler loggen
|
||||
debug_print(f" -> FEHLER (Google API Error) beim Batch-Update: Status {e.response.status_code}")
|
||||
# Logge die ersten 500 Zeichen der Fehlermeldung von Google
|
||||
try:
|
||||
error_details = e.response.json() # Versuche JSON zu parsen
|
||||
debug_print(f" -> Details: {str(error_details)[:500]}")
|
||||
except: # Falls die Antwort kein JSON ist
|
||||
debug_print(f" -> Raw Response Text: {e.response.text[:500]}")
|
||||
# WICHTIG: Fehler weitergeben, damit retry_on_failure greifen kann
|
||||
raise e
|
||||
|
||||
except Exception as e:
|
||||
# Andere Fehler loggen
|
||||
debug_print(f" -> FEHLER (Allgemein) beim Batch-Update: {type(e).__name__} - {e}")
|
||||
import traceback
|
||||
debug_print(traceback.format_exc()) # Gib den vollen Traceback aus
|
||||
# Fehler weitergeben, damit retry_on_failure greifen kann
|
||||
raise e # Oder return False, wenn Retries nicht helfen sollen? Besser weitergeben.
|
||||
|
||||
# Gib den Erfolgsstatus zurück
|
||||
return success
|
||||
|
||||
|
||||
# --- Ende GoogleSheetHandler Klasse ---
|
||||
|
||||
Reference in New Issue
Block a user