debug
This commit is contained in:
@@ -578,126 +578,72 @@ def normalize_company_name(name):
|
|||||||
def process_wiki_updates_from_chatgpt(sheet_handler, data_processor):
|
def process_wiki_updates_from_chatgpt(sheet_handler, data_processor):
|
||||||
"""
|
"""
|
||||||
Identifiziert Zeilen, bei denen Wiki-Konsistenz (S) NICHT 'OK'/'X (Updated)' ist
|
Identifiziert Zeilen, bei denen Wiki-Konsistenz (S) NICHT 'OK'/'X (Updated)' ist
|
||||||
und ein alternativer Artikel in T vorgeschlagen wurde (URL, != M).
|
und ein alternativer Artikel in U vorgeschlagen wurde (URL, != M).
|
||||||
Kopiert neue URL nach M, führt Reparse (N-R) & Re-Branch (W-Y) durch,
|
Kopiert neue URL nach M, führt Reparse (N-R) & Re-Branch (W-Y) durch,
|
||||||
löscht Timestamps (AN, AX, AO, AP) und markiert S als 'X (Updated)'.
|
löscht Timestamps (AN, AX, AO, AP) und markiert S als 'X (Updated)'.
|
||||||
"""
|
"""
|
||||||
debug_print("Starte Modus: Wiki-Updates basierend auf ChatGPT-Vorschlägen...")
|
debug_print("Starte Modus: Wiki-Updates basierend auf ChatGPT-Vorschlägen...")
|
||||||
|
|
||||||
if not sheet_handler.load_data():
|
if not sheet_handler.load_data(): return
|
||||||
debug_print("FEHLER: Konnte Sheet-Daten nicht laden für Wiki-Updates.")
|
|
||||||
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:
|
if not all_data or len(all_data) <= 5: return
|
||||||
debug_print("FEHLER/WARNUNG: Keine Daten zum Verarbeiten in Wiki-Updates gefunden.")
|
|
||||||
return
|
|
||||||
header_rows = 5
|
header_rows = 5
|
||||||
data_rows = all_data[header_rows:]
|
data_rows = all_data[header_rows:]
|
||||||
|
|
||||||
# --- Indizes holen ---
|
# Indizes holen
|
||||||
required_keys = [
|
required_keys = ["Chat Wiki Konsistenzprüfung", "Chat Vorschlag Wiki Artikel", "Wiki URL", "Wiki Absatz", "Wiki Branche", "Wiki Umsatz", "Wiki Mitarbeiter", "Wiki Kategorien", "Chat Vorschlag Branche", "Chat Konsistenz Branche", "Chat Begründung Abweichung Branche", "Wikipedia Timestamp", "Wiki Verif. Timestamp", "Timestamp letzte Prüfung", "Version", "Website Zusammenfassung", "CRM Branche", "CRM Beschreibung"]
|
||||||
"Chat Wiki Konsistenzprüfung", "Chat Vorschlag Wiki Artikel", "Wiki URL",
|
col_indices = {}; all_keys_found = True
|
||||||
"Wiki Absatz", "Wiki Branche", "Wiki Umsatz", "Wiki Mitarbeiter", "Wiki Kategorien",
|
|
||||||
"Chat Vorschlag Branche", "Chat Konsistenz Branche", "Chat Begründung Abweichung Branche",
|
|
||||||
"Wikipedia Timestamp", "Wiki Verif. Timestamp", "Timestamp letzte Prüfung", "Version",
|
|
||||||
"Website Zusammenfassung", "CRM Branche", "CRM Beschreibung"
|
|
||||||
]
|
|
||||||
col_indices = {}
|
|
||||||
all_keys_found = True
|
|
||||||
# Überprüfe jeden Schlüssel und logge genau, welcher fehlt
|
|
||||||
for key in required_keys:
|
for key in required_keys:
|
||||||
idx = COLUMN_MAP.get(key)
|
idx = COLUMN_MAP.get(key)
|
||||||
if idx is None:
|
if idx is None: debug_print(f"FEHLER: Schlüssel '{key}' fehlt!"); all_keys_found = False
|
||||||
debug_print(f"FEHLER: Schlüssel '{key}' für Spaltenindex fehlt in COLUMN_MAP!")
|
col_indices[key] = idx
|
||||||
all_keys_found = False
|
if not all_keys_found: return debug_print("Breche Wiki-Updates ab.")
|
||||||
col_indices[key] = idx # Speichere auch None, um Fehler unten zu vermeiden
|
|
||||||
|
|
||||||
if not all_keys_found:
|
|
||||||
debug_print("Breche Wiki-Updates ab, da Spaltenindizes fehlen.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# --- Variablen für Verarbeitung ---
|
|
||||||
all_sheet_updates = []
|
all_sheet_updates = []
|
||||||
processed_rows_count = 0
|
processed_rows_count = 0
|
||||||
skipped_rows_count = 0
|
|
||||||
error_rows_count = 0
|
error_rows_count = 0
|
||||||
wiki_scraper = data_processor.wiki_scraper # Nutze Instanz
|
wiki_scraper = data_processor.wiki_scraper
|
||||||
|
|
||||||
# --- Hauptschleife über Datenzeilen ---
|
|
||||||
for idx, row in enumerate(data_rows):
|
for idx, row in enumerate(data_rows):
|
||||||
row_num_in_sheet = idx + header_rows + 1 # 1-basierte Zeilennummer
|
row_num_in_sheet = idx + header_rows + 1
|
||||||
|
|
||||||
# --- Werte sicher lesen ---
|
def get_value(key): # Sicherer Zugriff auf Spaltenindex
|
||||||
# Funktion zum sicheren Holen von Werten aus der Zeile
|
|
||||||
def get_value(key):
|
|
||||||
index = col_indices.get(key)
|
index = col_indices.get(key)
|
||||||
if index is not None and len(row) > index:
|
if index is not None and len(row) > index: return row[index]
|
||||||
return row[index]
|
return ""
|
||||||
return "" # Leerer String als Standard
|
|
||||||
|
|
||||||
konsistenz_s = get_value("Chat Wiki Konsistenzprüfung")
|
konsistenz_s = get_value("Chat Wiki Konsistenzprüfung")
|
||||||
vorschlag_t = get_value("Chat Vorschlag Wiki Artikel")
|
vorschlag_u = get_value("Chat Vorschlag Wiki Artikel") # Liest Spalte U
|
||||||
url_m = get_value("Wiki URL")
|
url_m = get_value("Wiki URL")
|
||||||
|
|
||||||
# --- Bedingung prüfen ---
|
# Bedingung prüfen
|
||||||
is_update_candidate = False
|
is_update_candidate = False; new_url = ""
|
||||||
new_url = ""
|
|
||||||
konsistenz_s_upper = konsistenz_s.strip().upper()
|
konsistenz_s_upper = konsistenz_s.strip().upper()
|
||||||
vorschlag_t_cleaned = vorschlag_t.strip()
|
vorschlag_u_cleaned = vorschlag_u.strip()
|
||||||
url_m_cleaned = url_m.strip()
|
url_m_cleaned = url_m.strip()
|
||||||
|
|
||||||
# Bedingung 1: Status in S ist nicht OK und nicht bereits als "Updated" markiert
|
|
||||||
condition1_status_nok = konsistenz_s_upper not in ["OK", "X (UPDATED)", ""]
|
condition1_status_nok = konsistenz_s_upper not in ["OK", "X (UPDATED)", ""]
|
||||||
|
condition2_u_is_url = vorschlag_u_cleaned.lower().startswith(("http://", "https://"))
|
||||||
# Bedingung 2: Vorschlag T ist eine valide URL
|
condition3_u_differs_m = False
|
||||||
condition2_t_is_url = vorschlag_t_cleaned.lower().startswith(("http://", "https://"))
|
if condition2_u_is_url: new_url = vorschlag_u_cleaned; condition3_u_differs_m = new_url != url_m_cleaned
|
||||||
|
is_update_candidate = condition1_status_nok and condition2_u_is_url and condition3_u_differs_m
|
||||||
# Bedingung 3: Vorgeschlagene URL T unterscheidet sich von aktueller URL M
|
|
||||||
condition3_t_differs_m = False
|
|
||||||
if condition2_t_is_url: # Prüfe nur, wenn T eine URL ist
|
|
||||||
new_url = vorschlag_t_cleaned # Setze new_url hier
|
|
||||||
if new_url != url_m_cleaned:
|
|
||||||
condition3_t_differs_m = True
|
|
||||||
|
|
||||||
# Finale Entscheidung
|
|
||||||
is_update_candidate = condition1_status_nok and condition2_t_is_url and condition3_t_differs_m
|
|
||||||
|
|
||||||
# Detailliertes Debugging für relevante Zeilen
|
# Detailliertes Debugging für relevante Zeilen
|
||||||
if row_num_in_sheet in [28, 40, 42, 388, 416, 478, 523, 527, 545, 571, 630] or idx < 5:
|
if row_num_in_sheet in [28, 40, 42, 388, 416, 478, 523, 527, 545, 571, 630] or idx < 2:
|
||||||
debug_print(f"\n--- DEBUG Zeile {row_num_in_sheet} ---")
|
debug_print(f"\n--- DEBUG Zeile {row_num_in_sheet} ---"); debug_print(f" S='{konsistenz_s}' -> NOK? {condition1_status_nok}"); debug_print(f" U='{vorschlag_u}' -> URL? {condition2_u_is_url}"); debug_print(f" M='{url_m}'"); debug_print(f" U!=M? {condition3_u_differs_m}"); debug_print(f" => Update? {is_update_candidate}"); debug_print(f"--- ENDE DEBUG ---\n")
|
||||||
debug_print(f" S='{konsistenz_s}' -> Status NOK? {condition1_status_nok} (Wert: '{konsistenz_s_upper}')")
|
|
||||||
debug_print(f" T='{vorschlag_t}' -> Ist URL? {condition2_t_is_url} (Wert: '{vorschlag_t_cleaned}')")
|
|
||||||
debug_print(f" M='{url_m}' -> (Wert Cleaned: '{url_m_cleaned}')")
|
|
||||||
debug_print(f" Bedingung (T != M)?: {condition3_t_differs_m}")
|
|
||||||
debug_print(f" => Update-Kandidat? {is_update_candidate}")
|
|
||||||
debug_print(f"--- ENDE DEBUG Zeile {row_num_in_sheet} ---\n")
|
|
||||||
|
|
||||||
# --- Verarbeitung des Kandidaten ---
|
|
||||||
if is_update_candidate:
|
if is_update_candidate:
|
||||||
debug_print(f"Zeile {row_num_in_sheet}: Verarbeite Update-Kandidat. Neue URL: {new_url}")
|
debug_print(f"Zeile {row_num_in_sheet}: Verarbeite Update-Kandidat. Neue URL: {new_url}")
|
||||||
try:
|
try:
|
||||||
# --- Wiki Reparse ---
|
# Wiki Reparse
|
||||||
debug_print(f" -> Reparsing Wiki-Daten für {new_url}...")
|
debug_print(f" -> Reparsing Wiki: {new_url}...")
|
||||||
new_wiki_data = wiki_scraper.extract_company_data(new_url)
|
new_wiki_data = wiki_scraper.extract_company_data(new_url); time.sleep(0.2)
|
||||||
time.sleep(0.2) # Kleinere Pause nach Scrape
|
# Branch Neuberechnung
|
||||||
|
crm_branche = get_value("CRM Branche"); crm_beschreibung = get_value("CRM Beschreibung"); website_summary = get_value("Website Zusammenfassung")
|
||||||
# --- Branch Neuberechnung ---
|
debug_print(f" -> Neuberechnung Branch...")
|
||||||
crm_branche = get_value("CRM Branche")
|
new_branch_result = evaluate_branche_chatgpt(crm_branche, crm_beschreibung, new_wiki_data.get('branche', 'k.A.'), new_wiki_data.get('categories', 'k.A.'), website_summary); time.sleep(0.2)
|
||||||
crm_beschreibung = get_value("CRM Beschreibung")
|
|
||||||
website_summary = get_value("Website Zusammenfassung")
|
|
||||||
debug_print(f" -> Neuberechnung der Branche...")
|
|
||||||
new_branch_result = evaluate_branche_chatgpt(
|
|
||||||
crm_branche, crm_beschreibung,
|
|
||||||
new_wiki_data.get('branche', 'k.A.'),
|
|
||||||
new_wiki_data.get('categories', 'k.A.'),
|
|
||||||
website_summary
|
|
||||||
)
|
|
||||||
time.sleep(0.2) # Kleine Pause nach ChatGPT
|
|
||||||
|
|
||||||
# --- Updates sammeln ---
|
|
||||||
# Spaltenbuchstaben (vereinfacht für Lesbarkeit)
|
|
||||||
m_l=sheet_handler._get_col_letter(col_indices["Wiki URL"]+1); n_l=sheet_handler._get_col_letter(col_indices["Wiki Absatz"]+1); o_l=sheet_handler._get_col_letter(col_indices["Wiki Branche"]+1); p_l=sheet_handler._get_col_letter(col_indices["Wiki Umsatz"]+1); q_l=sheet_handler._get_col_letter(col_indices["Wiki Mitarbeiter"]+1); r_l=sheet_handler._get_col_letter(col_indices["Wiki Kategorien"]+1); w_l=sheet_handler._get_col_letter(col_indices["Chat Vorschlag Branche"]+1); x_l=sheet_handler._get_col_letter(col_indices["Chat Konsistenz Branche"]+1); y_l=sheet_handler._get_col_letter(col_indices["Chat Begründung Abweichung Branche"]+1); an_l=sheet_handler._get_col_letter(col_indices["Wikipedia Timestamp"]+1); ax_l=sheet_handler._get_col_letter(col_indices["Wiki Verif. Timestamp"]+1); ao_l=sheet_handler._get_col_letter(col_indices["Timestamp letzte Prüfung"]+1); ap_l=sheet_handler._get_col_letter(col_indices["Version"]+1); t_l=sheet_handler._get_col_letter(col_indices["Chat Vorschlag Wiki Artikel"]+1); s_l=sheet_handler._get_col_letter(col_indices["Chat Wiki Konsistenzprüfung"]+1)
|
|
||||||
|
|
||||||
|
# Updates sammeln
|
||||||
|
m_l=sheet_handler._get_col_letter(col_indices["Wiki URL"]+1); n_l=sheet_handler._get_col_letter(col_indices["Wiki Absatz"]+1); o_l=sheet_handler._get_col_letter(col_indices["Wiki Branche"]+1); p_l=sheet_handler._get_col_letter(col_indices["Wiki Umsatz"]+1); q_l=sheet_handler._get_col_letter(col_indices["Wiki Mitarbeiter"]+1); r_l=sheet_handler._get_col_letter(col_indices["Wiki Kategorien"]+1); w_l=sheet_handler._get_col_letter(col_indices["Chat Vorschlag Branche"]+1); x_l=sheet_handler._get_col_letter(col_indices["Chat Konsistenz Branche"]+1); y_l=sheet_handler._get_col_letter(col_indices["Chat Begründung Abweichung Branche"]+1); an_l=sheet_handler._get_col_letter(col_indices["Wikipedia Timestamp"]+1); ax_l=sheet_handler._get_col_letter(col_indices["Wiki Verif. Timestamp"]+1); ao_l=sheet_handler._get_col_letter(col_indices["Timestamp letzte Prüfung"]+1); ap_l=sheet_handler._get_col_letter(col_indices["Version"]+1); vorschlag_u_letter=sheet_handler._get_col_letter(col_indices["Chat Vorschlag Wiki Artikel"]+1); konsistenz_s_letter=sheet_handler._get_col_letter(col_indices["Chat Wiki Konsistenzprüfung"]+1)
|
||||||
row_updates = [
|
row_updates = [
|
||||||
{'range': f'{m_l}{row_num_in_sheet}', 'values': [[new_url]]},
|
{'range': f'{m_l}{row_num_in_sheet}', 'values': [[new_url]]},
|
||||||
{'range': f'{n_l}{row_num_in_sheet}', 'values': [[new_wiki_data.get('first_paragraph', 'k.A.')]]},
|
{'range': f'{n_l}{row_num_in_sheet}', 'values': [[new_wiki_data.get('first_paragraph', 'k.A.')]]},
|
||||||
@@ -708,33 +654,43 @@ def process_wiki_updates_from_chatgpt(sheet_handler, data_processor):
|
|||||||
{'range': f'{w_l}{row_num_in_sheet}', 'values': [[new_branch_result.get("branch", "Fehler")]]},
|
{'range': f'{w_l}{row_num_in_sheet}', 'values': [[new_branch_result.get("branch", "Fehler")]]},
|
||||||
{'range': f'{x_l}{row_num_in_sheet}', 'values': [[new_branch_result.get("consistency", "Fehler")]]},
|
{'range': f'{x_l}{row_num_in_sheet}', 'values': [[new_branch_result.get("consistency", "Fehler")]]},
|
||||||
{'range': f'{y_l}{row_num_in_sheet}', 'values': [[new_branch_result.get("justification", "Fehler")]]},
|
{'range': f'{y_l}{row_num_in_sheet}', 'values': [[new_branch_result.get("justification", "Fehler")]]},
|
||||||
{'range': f'{an_l}{row_num_in_sheet}', 'values': [[""]]}, # Timestamps leeren
|
{'range': f'{an_l}{row_num_in_sheet}', 'values': [[""]]}, {'range': f'{ax_l}{row_num_in_sheet}', 'values': [[""]]},
|
||||||
{'range': f'{ax_l}{row_num_in_sheet}', 'values': [[""]]},
|
{'range': f'{ao_l}{row_num_in_sheet}', 'values': [[""]]}, {'range': f'{ap_l}{row_num_in_sheet}', 'values': [[""]]},
|
||||||
{'range': f'{ao_l}{row_num_in_sheet}', 'values': [[""]]},
|
{'range': f'{vorschlag_u_letter}{row_num_in_sheet}', 'values': [["Korrektur übernommen"]]},
|
||||||
{'range': f'{ap_l}{row_num_in_sheet}', 'values': [[""]]}, # Version leeren
|
{'range': f'{konsistenz_s_letter}{row_num_in_sheet}', 'values': [["X (Updated)"]]},
|
||||||
{'range': f'{t_l}{row_num_in_sheet}', 'values': [["Korrektur übernommen"]]},
|
|
||||||
{'range': f'{s_l}{row_num_in_sheet}', 'values': [["X (Updated)"]]}, # Status aktualisieren
|
|
||||||
]
|
]
|
||||||
all_sheet_updates.extend(row_updates)
|
all_sheet_updates.extend(row_updates)
|
||||||
processed_rows_count += 1 # Erfolgreich verarbeitet
|
processed_rows_count += 1
|
||||||
debug_print(f" -> Updates für Zeile {row_num_in_sheet} vorbereitet.")
|
debug_print(f" -> Updates für Zeile {row_num_in_sheet} vorbereitet.")
|
||||||
|
# Keine Pause mehr hier pro Zeile, nur am Ende des Batches
|
||||||
|
|
||||||
except Exception as e_row:
|
except Exception as e_row:
|
||||||
error_rows_count += 1
|
error_rows_count += 1
|
||||||
debug_print(f"FEHLER bei Verarbeitung von Update-Kandidat Zeile {row_num_in_sheet}: {e_row}")
|
debug_print(f"FEHLER Verarbeitung Zeile {row_num_in_sheet}: {e_row}")
|
||||||
# Optional Traceback loggen:
|
# import traceback; debug_print(traceback.format_exc()) # Bei Bedarf Traceback loggen
|
||||||
# import traceback; debug_print(traceback.format_exc())
|
|
||||||
|
|
||||||
# --- Batch Update am Ende ---
|
# --- Batch Update am Ende ---
|
||||||
if all_sheet_updates:
|
if all_sheet_updates:
|
||||||
debug_print(f"Sende Batch-Update für {processed_rows_count} korrigierte Wiki-Einträge ({len(all_sheet_updates)} Zellen)...")
|
debug_print(f"BEREIT ZUM SENDEN: Batch-Update für {processed_rows_count} korrigierte Wiki-Einträge ({len(all_sheet_updates)} Zellen)...")
|
||||||
success = sheet_handler.batch_update_cells(all_sheet_updates)
|
# Optional: Logge Beispiel-Updates
|
||||||
if success: debug_print(f"Sheet-Update für Wiki-Korrekturen erfolgreich.")
|
if len(all_sheet_updates) > 0: debug_print(f" -> Beispiel Update 1: {all_sheet_updates[0]}")
|
||||||
else: debug_print(f"FEHLER beim Sheet-Update für Wiki-Korrekturen.")
|
if len(all_sheet_updates) > 15: debug_print(f" -> Beispiel Update 15: {all_sheet_updates[14]}")
|
||||||
else:
|
|
||||||
debug_print("Keine Zeilen gefunden, die eine Wiki-URL-Korrektur benötigen/erfüllen.")
|
|
||||||
|
|
||||||
debug_print(f"Wiki-Updates basierend auf ChatGPT abgeschlossen. {processed_rows_count} Zeilen aktualisiert, {error_rows_count} Fehler bei Verarbeitung.")
|
success = False
|
||||||
|
try:
|
||||||
|
debug_print(" -> Rufe sheet_handler.batch_update_cells AUF...")
|
||||||
|
success = sheet_handler.batch_update_cells(all_sheet_updates)
|
||||||
|
debug_print(f" -> Aufruf von sheet_handler.batch_update_cells BEENDET. Erfolg? {success}")
|
||||||
|
except Exception as e_update:
|
||||||
|
debug_print(f" -> FEHLER direkt beim Aufruf von batch_update_cells in process_wiki_updates: {e_update}")
|
||||||
|
import traceback; debug_print(traceback.format_exc())
|
||||||
|
|
||||||
|
if success: debug_print(f"Sheet-Update Wiki-Korrekturen laut Rückgabewert erfolgreich.")
|
||||||
|
else: debug_print(f"FEHLER/Kein Erfolg Sheet-Update Wiki-Korrekturen.")
|
||||||
|
else:
|
||||||
|
debug_print("Keine Zeilen für Wiki-URL-Korrektur gefunden/verarbeitet.")
|
||||||
|
|
||||||
|
debug_print(f"Wiki-Updates basierend auf ChatGPT abgeschlossen. {processed_rows_count} Zeilen verarbeitet, {error_rows_count} Fehler bei Verarbeitung.")
|
||||||
|
|
||||||
def extract_numeric_value(raw_value, is_umsatz=False):
|
def extract_numeric_value(raw_value, is_umsatz=False):
|
||||||
"""Extrahiert und normalisiert Zahlenwerte (Umsatz in Mio, Mitarbeiter)."""
|
"""Extrahiert und normalisiert Zahlenwerte (Umsatz in Mio, Mitarbeiter)."""
|
||||||
@@ -1136,203 +1092,123 @@ def token_count(text):
|
|||||||
|
|
||||||
class GoogleSheetHandler:
|
class GoogleSheetHandler:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialisiert den Handler, verbindet und lädt initiale Daten."""
|
|
||||||
self.sheet = None
|
self.sheet = None
|
||||||
self.sheet_values = []
|
self.sheet_values = []
|
||||||
self.headers = [] # Speichert die erste Zeile als Header-Namen
|
self.headers = []
|
||||||
try:
|
try:
|
||||||
self._connect()
|
self._connect()
|
||||||
if self.sheet:
|
if self.sheet:
|
||||||
self.load_data() # Erste Datenladung bei Initialisierung
|
self.load_data()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"FATAL: Fehler bei Initialisierung von GoogleSheetHandler: {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}")
|
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):
|
||||||
"""Stellt Verbindung zum Google Sheet her."""
|
self.sheet = None
|
||||||
self.sheet = None # Sicherstellen, dass sheet vor try None ist
|
|
||||||
debug_print("Verbinde mit Google Sheets...")
|
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)
|
gc = gspread.authorize(creds)
|
||||||
sh = gc.open_by_url(Config.SHEET_URL)
|
sh = gc.open_by_url(Config.SHEET_URL)
|
||||||
self.sheet = sh.sheet1 # Greift auf das erste Blatt zu
|
self.sheet = sh.sheet1
|
||||||
debug_print("Verbindung zu Google Sheets erfolgreich.")
|
debug_print("Verbindung zu Google Sheets erfolgreich.")
|
||||||
except gspread.exceptions.APIError as e:
|
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]}")
|
debug_print(f"FEHLER bei Google API Verbindung: Status {e.response.status_code} - {e.response.text[:200]}")
|
||||||
raise e # Fehler weitergeben, damit retry greift
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Logge andere Verbindungsfehler
|
|
||||||
debug_print(f"FEHLER bei der Google Sheets Verbindung: {type(e).__name__} - {e}")
|
debug_print(f"FEHLER bei der Google Sheets Verbindung: {type(e).__name__} - {e}")
|
||||||
raise e # Fehler weitergeben
|
raise e
|
||||||
|
|
||||||
# retry_on_failure Decorator sollte hier angewendet werden
|
|
||||||
@retry_on_failure
|
@retry_on_failure
|
||||||
def load_data(self):
|
def load_data(self):
|
||||||
"""Lädt alle Daten aus dem Sheet und aktualisiert self.sheet_values und self.headers."""
|
|
||||||
if not self.sheet:
|
if not self.sheet:
|
||||||
debug_print("Fehler: Keine Sheet-Verbindung zum Laden der Daten.")
|
debug_print("Fehler: Keine Sheet-Verbindung zum Laden der Daten.")
|
||||||
self.sheet_values = []
|
self.sheet_values = []
|
||||||
self.headers = []
|
self.headers = []
|
||||||
return False # Signalisiert Fehler
|
return False
|
||||||
debug_print("Lade Daten aus Google Sheet...")
|
debug_print("Lade Daten aus Google Sheet...")
|
||||||
try:
|
try:
|
||||||
self.sheet_values = self.sheet.get_all_values() # Daten neu holen
|
self.sheet_values = self.sheet.get_all_values()
|
||||||
if not self.sheet_values:
|
if not self.sheet_values:
|
||||||
debug_print("Warnung: Google Sheet scheint leer zu sein oder keine Daten zurückgegeben.")
|
debug_print("Warnung: Google Sheet scheint leer zu sein oder keine Daten zurückgegeben.")
|
||||||
self.headers = []
|
self.headers = []
|
||||||
elif len(self.sheet_values) >= 1:
|
return True
|
||||||
self.headers = self.sheet_values[0] # Speichere die erste Zeile als Header
|
if len(self.sheet_values) >= 1: self.headers = self.sheet_values[0]
|
||||||
else:
|
else: self.headers = []
|
||||||
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 # Signalisiert Erfolg
|
return True
|
||||||
except gspread.exceptions.APIError as 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]}")
|
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
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"Allgemeiner Fehler beim Laden der Google Sheet Daten: {e}")
|
debug_print(f"Allgemeiner Fehler beim Laden der Google Sheet Daten: {e}")
|
||||||
raise e # Damit retry greift
|
raise e
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
"""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:
|
if not self.sheet_values or len(self.sheet_values) <= header_rows:
|
||||||
if self.sheet_values: # Logge nur, wenn Daten da, aber zu wenige
|
if self.sheet_values:
|
||||||
debug_print(f"Warnung in get_data: Nur {len(self.sheet_values)} Zeilen vorhanden, weniger als {header_rows} Header-Zeilen erwartet.")
|
debug_print(f"Warnung in get_data: Nur {len(self.sheet_values)} Zeilen vorhanden, weniger als {header_rows} Header-Zeilen erwartet.")
|
||||||
return []
|
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):
|
||||||
"""Gibt alle aktuell im Handler gespeicherten Daten inklusive Header zurück."""
|
|
||||||
if not self.sheet_values:
|
if not self.sheet_values:
|
||||||
debug_print("Warnung in get_all_data_with_headers: Keine Daten im Handler gespeichert.")
|
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?
|
return []
|
||||||
# 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):
|
||||||
""" 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
|
||||||
|
|
||||||
# --- ANGEPASST: Sucht jetzt nur noch nach EXAKT LEER ("") ---
|
|
||||||
def get_start_row_index(self, check_column_key, min_sheet_row=7):
|
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.
|
|
||||||
"""
|
|
||||||
# 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
|
|
||||||
|
|
||||||
header_rows = 5
|
header_rows = 5
|
||||||
data_rows = self.get_data() # Greift auf die neu geladenen Daten zu
|
data_rows = self.get_data()
|
||||||
|
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 # Fehlerindikator
|
return -1
|
||||||
|
|
||||||
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 EXAKT LEEREM Wert (=='') 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 vorherigen Zeilen scheinen gefüllt.")
|
debug_print(f"Start-Suchindex ({search_start_index_in_data}) >= Datenlänge ({len(data_rows)}).")
|
||||||
return len(data_rows) # Index nach der letzten Zeile
|
return len(data_rows)
|
||||||
|
|
||||||
# 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 = ""; is_exactly_empty = True
|
||||||
cell_value = "" # Standardwert, falls Spalte nicht existiert
|
|
||||||
is_exactly_empty = False
|
|
||||||
if len(row) > check_column_index:
|
if len(row) > check_column_index:
|
||||||
cell_value = row[check_column_index] # Hole den Rohwert
|
cell_value = row[check_column_index]
|
||||||
# Prüfe, ob der Wert EXAKT ein leerer String ist
|
if cell_value != "": is_exactly_empty = False
|
||||||
if cell_value == "":
|
log_debug = (i == search_start_index_in_data or i % 1000 == 0 or is_exactly_empty or i in range(10110, 10116))
|
||||||
is_exactly_empty = True
|
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}")
|
||||||
else:
|
|
||||||
# Spalte existiert nicht -> gilt als leer
|
|
||||||
is_exactly_empty = True
|
|
||||||
|
|
||||||
# Logge nur relevante Prüfungen
|
|
||||||
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
|
|
||||||
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:
|
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})")
|
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 # Gibt den 0-basierten Index *innerhalb der Datenliste* zurück
|
return i
|
||||||
|
|
||||||
# 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):
|
||||||
"""
|
if not self.sheet: debug_print("FEHLER: Keine Sheet-Verbindung für Batch-Update."); 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 gspread.exceptions.APIError as e: debug_print(f"Google API Fehler Batch-Update: Status {e.response.status_code} - {e.response.text[:500]}"); raise e
|
||||||
update_data (list): Eine Liste von Dictionaries, jedes mit 'range' und 'values'.
|
except Exception as e: debug_print(f"Allgemeiner Fehler Batch-Update: {type(e).__name__} - {e}"); raise e
|
||||||
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 ---
|
||||||
|
|||||||
Reference in New Issue
Block a user