This commit is contained in:
2025-05-26 18:23:25 +00:00
parent 757df6afe9
commit 7304d08149

View File

@@ -47,9 +47,44 @@ def get_docs_service_with_service_account():
return None
def create_google_doc_from_csv(service):
# ... (CSV-Verarbeitung bleibt gleich) ...
"""Liest CSV, verarbeitet Daten und erstellt/befüllt das Google Doc."""
kinder_nach_gruppen = collections.defaultdict(list)
# 1. Neues Google Doc erstellen (bleibt gleich)
try:
with open(CSV_FILENAME, mode='r', encoding='utf-8-sig', newline='') as csvfile:
reader = csv.DictReader(csvfile, delimiter=';')
for row in reader:
vorname = row.get('Vorname', '').strip()
nachname = row.get('Nachname', '').strip()
gruppe_original = row.get('Gruppe', '').strip()
if not vorname or not nachname or not gruppe_original:
print(f"Warnung: Zeile übersprungen wegen fehlender Daten: {row}")
continue
kinder_nach_gruppen[gruppe_original].append({
'Nachname': nachname,
'Vorname': vorname
})
except FileNotFoundError:
print(f"FEHLER: Die Datei '{CSV_FILENAME}' wurde nicht gefunden.")
return None
except Exception as e:
print(f"FEHLER beim Lesen der CSV-Datei: {e}")
return None
if not kinder_nach_gruppen:
print("Keine Daten aus der CSV-Datei geladen.")
return None
# Kinder innerhalb jeder Gruppe sortieren
for gruppe_key in kinder_nach_gruppen: # Geändert von 'gruppe' zu 'gruppe_key' um Verwechslung zu vermeiden
kinder_nach_gruppen[gruppe_key].sort(key=lambda x: (x['Nachname'].lower(), x['Vorname'].lower()))
# ***** KORREKTUR HIER: sorted_gruppen_namen definieren *****
sorted_gruppen_namen = sorted(kinder_nach_gruppen.keys())
stand_zeit = datetime.now().strftime("%d.%m.%Y %H:%M Uhr")
# 1. Neues Google Doc erstellen
try:
doc_body = {'title': GOOGLE_DOC_TITLE}
doc = service.documents().create(body=doc_body).execute()
@@ -63,7 +98,7 @@ def create_google_doc_from_csv(service):
requests = []
current_index = 1
for i, gruppe_original in enumerate(sorted_gruppen_namen):
for i, gruppe_original in enumerate(sorted_gruppen_namen): # Jetzt ist sorted_gruppen_namen definiert
kinder_liste = kinder_nach_gruppen[gruppe_original]
anzahl_kinder = len(kinder_liste)
gruppe_display_name = gruppe_original + GRUPPENNAME_SUFFIX
@@ -77,11 +112,9 @@ def create_google_doc_from_csv(service):
current_index += len(header_text_for_page)
# --- Tabelle ---
# Tabelle erstellen
num_rows_for_table = len(kinder_liste) + 1
num_cols_for_table = 3
# Füge die Tabellenstruktur ein
requests.append({
'insertTable': {
'location': {'index': current_index},
@@ -89,60 +122,26 @@ def create_google_doc_from_csv(service):
'columns': num_cols_for_table
}
})
# WICHTIG: Nach insertTable befindet sich der Cursor (und somit der nächste Einfügepunkt
# für Text, den die API in die Zellen füllen soll) typischerweise direkt
# AM ANFANG der ersten Zelle. Der Index für diesen Punkt ist current_index + 1
# (da insertTable selbst eine bestimmte Länge im JSON-Request hat, aber der
# Dokumentenindex sich auf den Punkt bezieht, an dem die *Tabelle* beginnt).
table_fill_start_index = current_index + 1 # Index, an dem der Text für die Zellen beginnt
# Baue den gesamten Textinhalt für die Tabelle
table_fill_start_index = current_index + 1
table_text_content = []
# Kopfzeile
table_text_content.append("Nachname\tVorname\tGruppe") # Tabs für Spalten
# Datenzeilen
table_text_content.append("Nachname\tVorname\tGruppe")
for kind in kinder_liste:
table_text_content.append(f"{kind['Nachname']}\t{kind['Vorname']}\t{gruppe_display_name}")
full_table_text = "\n".join(table_text_content) + "\n" # Newlines für Zeilen, plus ein extra Newline am Ende
full_table_text = "\n".join(table_text_content) + "\n"
# Füge den gesamten Tabelleninhalt in einem Rutsch ein.
# Die API sollte dies in die Zellen der zuvor eingefügten Tabelle füllen.
requests.append({
'insertText': {
'location': {'index': table_fill_start_index}, # Am Anfang der ersten Zelle
'location': {'index': table_fill_start_index},
'text': full_table_text
}
})
# Aktualisiere den current_index um die Länge des eingefügten Tabellentextes
# PLUS die Strukturzeichen der Tabelle selbst. Dies ist schwer genau zu bestimmen.
# Eine sicherere Methode ist, nach diesem Batch-Update den aktuellen Dokumenten-Endindex
# abzufragen, aber für eine einfache Sequenz versuchen wir es mit einer Annahme.
# Die Länge der Struktur ist ungefähr: rows * cols * (1 für cell end) + rows * (1 für row end) + 1 (für table end)
# Das ist zu komplex. Einfacher: Der current_index wird auf den Start der Tabelle gesetzt,
# plus die Länge des reinen Textes, der eingefügt wurde. Die API fügt den Text *in* die Tabelle ein.
# Der nächste Text wird *nach* der Tabelle eingefügt.
# current_index bleibt current_index (Start der Tabelle), der nächste Text wird DANACH eingefügt.
# Also: current_index für den Footer muss *nach* der Tabelle sein.
# Die Tabelle selbst belegt `1` (für `insertTable`) im `requests` Array.
# Der Text der Tabelle belegt `len(full_table_text)`.
# Der Cursor ist nach dem Einfügen des Textes am Ende des Textes *innerhalb* der letzten Zelle.
# Um *nach* der Tabelle zu sein, brauchen wir `endOfSegmentLocation` oder eine bessere Indexlogik.
# NEUER ANSATZ für Index nach Tabelle:
# Der `current_index` zeigt auf den Beginn der Tabelle.
# Der nächste Text (Footer) muss nach der Tabelle eingefügt werden.
# Wir setzen den Index für den Footer auf current_index + 1 (symbolisch für "nach der Tabelle").
# Die API wird versuchen, dies nach der gerade bearbeiteten Tabellenstruktur einzufügen.
current_index_for_footer = current_index + 1 # Symbolisch "nach der Tabelle"
# --- Footer ---
# Korrektur: 'text' muss auf derselben Ebene wie 'endOfSegmentLocation' sein
footer_text_for_page = (
# Ein Newline am Anfang, um sicherzustellen, dass wir aus der Tabelle heraus sind
# Dies ist oft nicht nötig, wenn die API den Cursor korrekt nach der Tabelle platziert.
# Wir lassen es erstmal weg, da full_table_text mit \n endet.
f"\n{anzahl_kinder} angemeldete Kinder\n\n"
"Dies ist die Liste der bereits angemeldeten Kinder. Bitte die Eltern der noch fehlenden\n"
"Kinder an die Anmeldung erinnern.\n\n"
@@ -151,40 +150,53 @@ def create_google_doc_from_csv(service):
)
requests.append({
'insertText': {
# 'location': {'index': current_index_for_footer}, # VERSUCH 1
'endOfSegmentLocation': {} # VERSUCH 2: Sicherer, fügt am Ende des aktuellen Haupttextkörpers ein
# Nachdem die Tabelle und ihr Inhalt verarbeitet wurden.
},
'text': footer_text_for_page # Text muss auf derselben Ebene wie location sein
'endOfSegmentLocation': {},
'text': footer_text_for_page
}
})
# Wenn endOfSegmentLocation verwendet wird, ist die manuelle Indexverfolgung hier schwierig.
# --- Seitenumbruch ---
if i < len(sorted_gruppen_namen) - 1:
requests.append({'insertPageBreak': {'endOfSegmentLocation': {}}})
# current_index wird für die nächste Seite wieder auf 1 gesetzt,
# da ein PageBreak einen neuen Abschnitt startet, in dem die Indizes neu beginnen.
# Dies ist eine Vereinfachung. Technisch gesehen addiert sich der Index weiter,
# aber für `endOfSegmentLocation` ist es nicht so kritisch.
# Für den nächsten Header setzen wir current_index NICHT zurück, da endOfSegmentLocation verwendet wird.
# Wir nehmen an, dass `endOfSegmentLocation` den Cursor richtig für den nächsten Block positioniert.
# Wenn `location` verwendet wird, müssten wir den `current_index` genau weiterführen.
# Reset current_index for the new page if not using endOfSegmentLocation for header
# Da wir endOfSegmentLocation für den nächsten Header nicht explizit verwenden (er basiert auf current_index),
# müssen wir sicherstellen, dass current_index für die nächste Seite korrekt ist.
# Mit endOfSegmentLocation für Footer und PageBreak ist die manuelle Index-Verfolgung
# für den *nächsten* Header schwierig.
# Eine sicherere Methode wäre, den Header der nächsten Seite auch mit endOfSegmentLocation zu beginnen.
# Für jetzt lassen wir current_index einfach weiterlaufen und hoffen, dass
# endOfSegmentLocation für Footer/PageBreak den Cursor korrekt für den *nächsten* Block
# (der wieder 'location' mit 'current_index' verwendet) positioniert.
# Dies könnte ein potenzielles Problem für die nächste Iteration sein, wenn der Footer/PageBreak
# den `current_index` nicht so verschiebt, wie es für den nächsten Header-Insert benötigt wird.
# EINFACHERE LÖSUNG: current_index nach endOfSegmentLocation nicht mehr verwenden.
# Wenn endOfSegmentLocation verwendet wird, sollte der nächste Insert auch endOfSegmentLocation verwenden oder
# man muss den Index explizit auf 1 für eine neue Seite setzen (was aber nicht korrekt ist, wenn das Dokument
# als ein langer Stream behandelt wird).
# Für jetzt: Da der Header der nächsten Seite `current_index` verwendet, müssen wir ihn aktualisieren.
# Ein Page Break fügt 1 zum Index hinzu. Der Footer hat `len(footer_text_for_page)`.
current_index += len(footer_text_for_page) + 1 # +1 für den PageBreak
# Batch-Update ausführen (bleibt gleich)
# Batch-Update ausführen
if requests:
try:
print("Sende Batch Update an Google Docs API...")
print("Anzahl der Requests:", len(requests))
# print("Requests (Auszug):", requests[:5]) # Für Debugging ggf. komplett ausgeben
service.documents().batchUpdate(
documentId=document_id, body={'requests': requests}
).execute()
print("Dokument erfolgreich befüllt.")
except HttpError as err:
print(f"Fehler beim Befüllen des Google Dokuments: {err}")
error_details = err.content.decode('utf-8') if err.content else "Keine Details verfügbar."
error_details = "Keine Fehlerdetails im Content."
if err.content:
try:
error_details = err.content.decode('utf-8')
except Exception as e_decode:
error_details = f"Fehler beim Dekodieren der Fehlerdetails: {e_decode}"
print(f"Details zum Fehler ({err.resp.status} {err._get_reason()}): {error_details}")
# print("Gesendete Requests:", requests) # Alle Requests ausgeben für genaues Debugging
return document_id