bugfix
This commit is contained in:
@@ -47,72 +47,15 @@ def get_docs_service_with_service_account():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def create_google_doc_from_csv(service):
|
def create_google_doc_from_csv(service):
|
||||||
"""Liest CSV, verarbeitet Daten und erstellt/befüllt das Google Doc."""
|
# ... (CSV-Verarbeitung bleibt gleich) ...
|
||||||
# (Dieser Funktionsteil bleibt identisch mit dem aus der vorherigen Antwort,
|
|
||||||
# die die Google Docs API verwendet, um das Dokument zu befüllen.
|
|
||||||
# Sie beginnt mit dem Einlesen der CSV und endet mit dem batchUpdate.)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
for gruppe in kinder_nach_gruppen:
|
|
||||||
kinder_nach_gruppen[gruppe].sort(key=lambda x: (x['Nachname'].lower(), x['Vorname'].lower()))
|
|
||||||
|
|
||||||
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:
|
try:
|
||||||
doc_body = {'title': GOOGLE_DOC_TITLE}
|
doc_body = {'title': GOOGLE_DOC_TITLE}
|
||||||
doc = service.documents().create(body=doc_body).execute()
|
doc = service.documents().create(body=doc_body).execute()
|
||||||
document_id = doc.get('documentId')
|
document_id = doc.get('documentId')
|
||||||
print(f"Google Doc erstellt mit ID: {document_id}")
|
print(f"Google Doc erstellt mit ID: {document_id}")
|
||||||
print(f"Link: https://docs.google.com/document/d/{document_id}/edit")
|
print(f"Link: https://docs.google.com/document/d/{document_id}/edit")
|
||||||
|
|
||||||
# WICHTIG: Teilen Sie das Dokument mit Ihrem Hauptkonto, wenn Sie es dort sehen möchten
|
|
||||||
# Holen Sie sich dazu den Drive Service (benötigt 'https://www.googleapis.com/auth/drive' Scope)
|
|
||||||
# drive_service = build('drive', 'v3', credentials=creds) # Benötigt ggf. Drive Scope im Service Account
|
|
||||||
# user_permission = {
|
|
||||||
# 'type': 'user',
|
|
||||||
# 'role': 'writer', # oder 'owner', 'reader'
|
|
||||||
# 'emailAddress': 'ihre-email@example.com' # IHRE E-MAIL-ADRESSE
|
|
||||||
# }
|
|
||||||
# try:
|
|
||||||
# drive_service.permissions().create(
|
|
||||||
# fileId=document_id,
|
|
||||||
# body=user_permission,
|
|
||||||
# fields='id'
|
|
||||||
# ).execute()
|
|
||||||
# print(f"Dokument wurde mit 'ihre-email@example.com' geteilt.")
|
|
||||||
# except HttpError as error:
|
|
||||||
# print(f"Fehler beim Teilen des Dokuments: {error}")
|
|
||||||
# Dieses Teilen ist optional und erfordert, dass der Service Account auch Drive-Berechtigungen hat.
|
|
||||||
|
|
||||||
except HttpError as err:
|
except HttpError as err:
|
||||||
print(f"Fehler beim Erstellen des Google Dokuments: {err}")
|
print(f"Fehler beim Erstellen des Google Dokuments: {err}")
|
||||||
return None
|
return None
|
||||||
@@ -125,61 +68,126 @@ def create_google_doc_from_csv(service):
|
|||||||
anzahl_kinder = len(kinder_liste)
|
anzahl_kinder = len(kinder_liste)
|
||||||
gruppe_display_name = gruppe_original + GRUPPENNAME_SUFFIX
|
gruppe_display_name = gruppe_original + GRUPPENNAME_SUFFIX
|
||||||
|
|
||||||
header_text = (
|
# --- Seiten-Header ---
|
||||||
|
header_text_for_page = (
|
||||||
f"{EINRICHTUNG}\t\t\t{FOTOGRAF_NAME}\n"
|
f"{EINRICHTUNG}\t\t\t{FOTOGRAF_NAME}\n"
|
||||||
f"{FOTODATUM}\n\n"
|
f"{FOTODATUM}\n\n"
|
||||||
)
|
)
|
||||||
requests.append({'insertText': {'location': {'index': current_index}, 'text': header_text}})
|
requests.append({'insertText': {'location': {'index': current_index}, 'text': header_text_for_page}})
|
||||||
current_index += len(header_text)
|
current_index += len(header_text_for_page)
|
||||||
|
|
||||||
num_rows = len(kinder_liste) + 1
|
# --- Tabelle ---
|
||||||
num_cols = 3
|
# Tabelle erstellen
|
||||||
|
num_rows_for_table = len(kinder_liste) + 1
|
||||||
|
num_cols_for_table = 3
|
||||||
|
|
||||||
requests.append({'insertTable': {'location': {'index': current_index}, 'rows': num_rows, 'columns': num_cols}})
|
# Füge die Tabellenstruktur ein
|
||||||
|
requests.append({
|
||||||
|
'insertTable': {
|
||||||
|
'location': {'index': current_index},
|
||||||
|
'rows': num_rows_for_table,
|
||||||
|
'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_content_start_index = current_index + 2
|
table_fill_start_index = current_index + 1 # Index, an dem der Text für die Zellen beginnt
|
||||||
|
|
||||||
kopf_zellen = ["Nachname", "Vorname", "Gruppe"]
|
|
||||||
for z, kopf_text in enumerate(kopf_zellen):
|
|
||||||
text_to_insert = kopf_text + ('\t' if z < len(kopf_zellen) -1 else '')
|
|
||||||
requests.append({'insertText': {'location': {'index': table_content_start_index }, 'text': text_to_insert}})
|
|
||||||
table_content_start_index += len(text_to_insert)
|
|
||||||
|
|
||||||
for kind in kinder_liste:
|
|
||||||
row_data = [kind['Nachname'], kind['Vorname'], gruppe_display_name]
|
|
||||||
for z, cell_text in enumerate(row_data):
|
|
||||||
text_to_insert = cell_text + ('\t' if z < len(row_data) -1 else '\n')
|
|
||||||
requests.append({'insertText': {'location': {'index': table_content_start_index}, 'text': text_to_insert}})
|
|
||||||
table_content_start_index += len(text_to_insert)
|
|
||||||
|
|
||||||
current_index = table_content_start_index
|
|
||||||
|
|
||||||
footer_text = (
|
# Baue den gesamten Textinhalt für die Tabelle
|
||||||
|
table_text_content = []
|
||||||
|
# Kopfzeile
|
||||||
|
table_text_content.append("Nachname\tVorname\tGruppe") # Tabs für Spalten
|
||||||
|
# Datenzeilen
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
'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 ---
|
||||||
|
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"
|
f"\n{anzahl_kinder} angemeldete Kinder\n\n"
|
||||||
"Dies ist die Liste der bereits angemeldeten Kinder. Bitte die Eltern der noch fehlenden\n"
|
"Dies ist die Liste der bereits angemeldeten Kinder. Bitte die Eltern der noch fehlenden\n"
|
||||||
"Kinder an die Anmeldung erinnern.\n\n"
|
"Kinder an die Anmeldung erinnern.\n\n"
|
||||||
f"Stand {stand_zeit}\n\n"
|
f"Stand {stand_zeit}\n\n"
|
||||||
f"{FOTOGRAF_NAME}\n{FOTOGRAF_ADRESSE}\n{FOTOGRAF_WEB}\n{FOTOGRAF_TEL}\n"
|
f"{FOTOGRAF_NAME}\n{FOTOGRAF_ADRESSE}\n{FOTOGRAF_WEB}\n{FOTOGRAF_TEL}\n"
|
||||||
)
|
)
|
||||||
requests.append({'insertText': {'location': {'index': current_index}, 'text': footer_text}})
|
requests.append({
|
||||||
current_index += len(footer_text)
|
'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
|
||||||
|
})
|
||||||
|
# Wenn endOfSegmentLocation verwendet wird, ist die manuelle Indexverfolgung hier schwierig.
|
||||||
|
|
||||||
|
# --- Seitenumbruch ---
|
||||||
if i < len(sorted_gruppen_namen) - 1:
|
if i < len(sorted_gruppen_namen) - 1:
|
||||||
requests.append({'insertPageBreak': {'endOfSegmentLocation': {}}})
|
requests.append({'insertPageBreak': {'endOfSegmentLocation': {}}})
|
||||||
# current_index += 1 # Für Page Break, aber mit endOfSegmentLocation nicht mehr so relevant für Index
|
# 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.
|
||||||
|
|
||||||
|
# Batch-Update ausführen (bleibt gleich)
|
||||||
if requests:
|
if requests:
|
||||||
try:
|
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(
|
service.documents().batchUpdate(
|
||||||
documentId=document_id, body={'requests': requests}
|
documentId=document_id, body={'requests': requests}
|
||||||
).execute()
|
).execute()
|
||||||
print("Dokument erfolgreich befüllt.")
|
print("Dokument erfolgreich befüllt.")
|
||||||
except HttpError as err:
|
except HttpError as err:
|
||||||
print(f"Fehler beim Befüllen des Google Dokuments: {err}")
|
print(f"Fehler beim Befüllen des Google Dokuments: {err}")
|
||||||
print("Details zum Fehler:", err.resp.status, err._get_reason())
|
error_details = err.content.decode('utf-8') if err.content else "Keine Details verfügbar."
|
||||||
print("Gesendete Requests (Auszug):", requests[:5])
|
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
|
return document_id
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print(f"Info: Verwendetes Fotodatum: {FOTODATUM}")
|
print(f"Info: Verwendetes Fotodatum: {FOTODATUM}")
|
||||||
print(f"Info: Gruppennamen werden mit Suffix '{GRUPPENNAME_SUFFIX}' versehen.")
|
print(f"Info: Gruppennamen werden mit Suffix '{GRUPPENNAME_SUFFIX}' versehen.")
|
||||||
|
|||||||
Reference in New Issue
Block a user