google_sheet_handler.py aktualisiert
This commit is contained in:
@@ -5,86 +5,71 @@ import logging
|
||||
import gspread
|
||||
import pandas as pd
|
||||
from oauth2client.service_account import ServiceAccountCredentials
|
||||
from config import Config, COLUMN_MAP
|
||||
# KORRIGIERTER IMPORT
|
||||
from config import Config, COLUMN_MAP, CREDENTIALS_FILE
|
||||
from helpers import retry_on_failure, _get_col_letter
|
||||
|
||||
class GoogleSheetHandler:
|
||||
"""
|
||||
Kapselt alle Interaktionen mit dem Google Sheet.
|
||||
Finale, robuste Version v2.1.0
|
||||
Finale, robuste Version v2.1.1
|
||||
"""
|
||||
def __init__(self, sheet_url=None):
|
||||
"""
|
||||
Initialisiert den Handler. Die Verbindung wird bei Bedarf hergestellt ("lazy connect").
|
||||
"""
|
||||
self.logger = logging.getLogger(__name__ + ".GoogleSheetHandler")
|
||||
self.logger.info("Initialisiere GoogleSheetHandler...")
|
||||
|
||||
self.sheet_url = sheet_url or Config.SHEET_URL
|
||||
if "docs.google.com" not in self.sheet_url:
|
||||
raise ValueError(f"Ungültige Google Sheet URL in config.py: '{self.sheet_url}'")
|
||||
|
||||
# Attribute werden immer initialisiert, um AttributeErrors zu vermeiden.
|
||||
raise ValueError(f"Ungültige Google Sheet URL: '{self.sheet_url}'")
|
||||
self.client = None
|
||||
self.sheet = None # Haupt-Tabellenblatt ('Tabelle1')
|
||||
self.sheet = None
|
||||
self._all_data_with_headers = []
|
||||
self._header_rows = 5
|
||||
|
||||
@retry_on_failure
|
||||
def _connect(self):
|
||||
"""
|
||||
Stellt die Verbindung zu Google Sheets her, falls sie noch nicht besteht.
|
||||
"""
|
||||
if self.client:
|
||||
self.logger.debug("Verbindung zu Google Sheets besteht bereits.")
|
||||
return True
|
||||
|
||||
if self.client: return True
|
||||
self.logger.info("Stelle neue Verbindung mit Google Sheets her...")
|
||||
try:
|
||||
creds = ServiceAccountCredentials.from_json_keyfile_name(Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])
|
||||
if not os.path.exists(CREDENTIALS_FILE):
|
||||
raise FileNotFoundError(f"Credential-Datei nicht gefunden: {CREDENTIALS_FILE}")
|
||||
# KORRIGIERTER ZUGRIFF
|
||||
creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])
|
||||
self.client = gspread.authorize(creds)
|
||||
spreadsheet = self.client.open_by_url(self.sheet_url)
|
||||
self.sheet = spreadsheet.sheet1
|
||||
self.logger.info("Verbindung zu Google Sheets erfolgreich hergestellt.")
|
||||
self.logger.info("Verbindung erfolgreich.")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.error(f"FEHLER bei der Google Sheets Verbindung: {e}")
|
||||
self.client = None # Verbindung im Fehlerfall zurücksetzen
|
||||
self.logger.error(f"FEHLER bei Google Sheets Verbindung: {e}")
|
||||
self.client = None
|
||||
return False
|
||||
|
||||
# ... (Der Rest der Methoden bleibt exakt gleich wie in meiner letzten Nachricht)
|
||||
|
||||
@retry_on_failure
|
||||
def load_data(self):
|
||||
"""Lädt alle Daten aus dem Haupt-Sheet ('Tabelle1')."""
|
||||
if not self.client and not self._connect():
|
||||
return False
|
||||
|
||||
self.logger.info("Lade Daten aus dem Haupt-Google-Sheet...")
|
||||
if not self.client and not self._connect(): return False
|
||||
self.logger.info("Lade Daten aus dem Haupt-Sheet ('Tabelle1')...")
|
||||
try:
|
||||
self._all_data_with_headers = self.sheet.get_all_values()
|
||||
self.logger.info(f"Daten erfolgreich geladen: {len(self._all_data_with_headers)} Zeilen.")
|
||||
|
||||
# Dynamisch die Anzahl der Header-Zeilen finden
|
||||
self.logger.info(f"Daten geladen: {len(self._all_data_with_headers)} Zeilen.")
|
||||
for i, row in enumerate(self._all_data_with_headers):
|
||||
if "CRM Name" in row: # Annahme: "CRM Name" ist in der Haupt-Header-Zeile
|
||||
if "CRM Name" in row:
|
||||
self._header_rows = i + 1
|
||||
break
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.critical(f"Allgemeiner Fehler beim Laden der Google Sheet Daten: {e}")
|
||||
self.logger.critical(f"Fehler beim Laden der Sheet Daten: {e}")
|
||||
return False
|
||||
|
||||
def get_all_data_with_headers(self):
|
||||
"""Gibt alle aktuell im Handler gespeicherten Daten inklusive Header zurück."""
|
||||
return self._all_data_with_headers.copy()
|
||||
|
||||
def get_sheet_as_dataframe(self, sheet_name):
|
||||
"""Liest ein komplettes Tabellenblatt und gibt es als Pandas DataFrame zurück."""
|
||||
try:
|
||||
if not self.client and not self._connect(): return None
|
||||
|
||||
self.logger.debug(f"Lese Tabellenblatt '{sheet_name}' als DataFrame...")
|
||||
worksheet = self.client.open_by_url(self.sheet_url).worksheet(sheet_name)
|
||||
data = worksheet.get_all_records()
|
||||
data = worksheet.get_all_records(empty_value_render_option='EMPTY_STRING')
|
||||
df = pd.DataFrame(data)
|
||||
self.logger.info(f"{len(df)} Zeilen aus '{sheet_name}' als DataFrame geladen.")
|
||||
return df
|
||||
@@ -95,49 +80,35 @@ class GoogleSheetHandler:
|
||||
self.logger.error(f"Fehler beim Lesen des Sheets '{sheet_name}' als DataFrame: {e}")
|
||||
return None
|
||||
|
||||
def append_rows(self, sheet_name, values_to_append):
|
||||
"""Hängt eine Liste von Zeilen an ein Tabellenblatt an."""
|
||||
def append_rows(self, sheet_name, values):
|
||||
try:
|
||||
if not self.client and not self._connect(): return False
|
||||
|
||||
self.logger.debug(f"Hänge {len(values_to_append)} Zeilen an das Tabellenblatt '{sheet_name}' an...")
|
||||
worksheet = self.client.open_by_url(self.sheet_url).worksheet(sheet_name)
|
||||
worksheet.append_rows(values_to_append, value_input_option='USER_ENTERED')
|
||||
self.logger.info(f"{len(values_to_append)} Zeilen erfolgreich an '{sheet_name}' angehängt.")
|
||||
worksheet.append_rows(values, value_input_option='USER_ENTERED')
|
||||
self.logger.info(f"{len(values)} Zeilen erfolgreich an '{sheet_name}' angehängt.")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.error(f"Fehler beim Anhängen von Zeilen an das Sheet '{sheet_name}': {e}")
|
||||
return False
|
||||
|
||||
def clear_and_write_data(self, sheet_name, data):
|
||||
"""Leert das angegebene Tabellenblatt vollständig und schreibt neue Daten hinein."""
|
||||
try:
|
||||
if not self.client and not self._connect(): return False
|
||||
|
||||
self.logger.info(f"Greife auf Tabellenblatt '{sheet_name}' zu, um es zu leeren und neu zu beschreiben...")
|
||||
worksheet = self.client.open_by_url(self.sheet_url).worksheet(sheet_name)
|
||||
|
||||
worksheet.clear()
|
||||
|
||||
if not data:
|
||||
self.logger.warning("Keine Daten zum Schreiben in '{sheet_name}' vorhanden.")
|
||||
return True
|
||||
|
||||
end_col_letter = _get_col_letter(len(data[0]))
|
||||
range_to_update = f'A1:{end_col_letter}{len(data)}'
|
||||
worksheet.update(range_name=range_to_update, values=data)
|
||||
self.logger.info(f"Schreiben von {len(data)} Zeilen in '{sheet_name}' erfolgreich.")
|
||||
return True
|
||||
except gspread.exceptions.WorksheetNotFound:
|
||||
self.logger.error(f"FATAL: Das Tabellenblatt '{sheet_name}' wurde nicht gefunden.")
|
||||
return False
|
||||
except Exception as e:
|
||||
self.logger.error(f"FATAL: Unerwarteter Fehler bei clear_and_write_data für '{sheet_name}': {e}")
|
||||
self.logger.error(f"Fehler bei clear_and_write_data für '{sheet_name}': {e}")
|
||||
return False
|
||||
|
||||
@retry_on_failure
|
||||
|
||||
def batch_update_cells(self, update_data):
|
||||
"""Führt ein Batch-Update im Haupt-Sheet durch."""
|
||||
if not self.sheet and not self._connect():
|
||||
self.logger.error("FEHLER: Keine Sheet-Verbindung fuer Batch-Update.")
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user