Files
Brancheneinstufung2/list_generator.py
2025-05-26 18:20:27 +00:00

208 lines
10 KiB
Python

import csv
from datetime import datetime
import collections
import os.path
# Für Service Account Authentifizierung
from google.oauth2 import service_account # Wichtig!
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# --- Konfiguration ---
CSV_FILENAME = 'Namensliste.csv'
GOOGLE_DOC_TITLE = f"Gruppenlisten Kinderhaus St. Martin Neuching (Service Acc) - {datetime.now().strftime('%Y-%m-%d')}"
EINRICHTUNG = "Kinderhaus St. Martin Neuching"
FOTODATUM = "02. - 05.06.2025"
GRUPPENNAME_SUFFIX = "gruppe"
FOTOGRAF_NAME = "Kinderfotos Erding"
FOTOGRAF_ADRESSE = "Gartenstr. 10 85445 Oberding"
FOTOGRAF_WEB = "www.kinderfotos-erding.de"
FOTOGRAF_TEL = "08122-8470867"
# Scopes für die Google Docs API
SCOPES = ['https://www.googleapis.com/auth/documents']
SERVICE_ACCOUNT_FILE = 'credentials.json' # Ihre Service Account Schlüsseldatei
# --- Ende Konfiguration ---
def get_docs_service_with_service_account():
"""Erstellt den API-Dienst mit einem Service Account."""
creds = None
try:
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
except Exception as e:
print(f"Fehler beim Laden der Service Account Credentials aus '{SERVICE_ACCOUNT_FILE}': {e}")
return None
try:
service = build('docs', 'v1', credentials=creds)
return service
except HttpError as err:
print(f"Ein Fehler beim Erstellen des Docs Service mit Service Account ist aufgetreten: {err}")
return None
except Exception as e:
print(f"Ein unerwarteter Fehler beim Erstellen des Docs Service: {e}")
return None
def create_google_doc_from_csv(service):
# ... (CSV-Verarbeitung bleibt gleich) ...
# 1. Neues Google Doc erstellen (bleibt gleich)
try:
doc_body = {'title': GOOGLE_DOC_TITLE}
doc = service.documents().create(body=doc_body).execute()
document_id = doc.get('documentId')
print(f"Google Doc erstellt mit ID: {document_id}")
print(f"Link: https://docs.google.com/document/d/{document_id}/edit")
except HttpError as err:
print(f"Fehler beim Erstellen des Google Dokuments: {err}")
return None
requests = []
current_index = 1
for i, gruppe_original in enumerate(sorted_gruppen_namen):
kinder_liste = kinder_nach_gruppen[gruppe_original]
anzahl_kinder = len(kinder_liste)
gruppe_display_name = gruppe_original + GRUPPENNAME_SUFFIX
# --- Seiten-Header ---
header_text_for_page = (
f"{EINRICHTUNG}\t\t\t{FOTOGRAF_NAME}\n"
f"{FOTODATUM}\n\n"
)
requests.append({'insertText': {'location': {'index': current_index}, 'text': header_text_for_page}})
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},
'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_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_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"
"Dies ist die Liste der bereits angemeldeten Kinder. Bitte die Eltern der noch fehlenden\n"
"Kinder an die Anmeldung erinnern.\n\n"
f"Stand {stand_zeit}\n\n"
f"{FOTOGRAF_NAME}\n{FOTOGRAF_ADRESSE}\n{FOTOGRAF_WEB}\n{FOTOGRAF_TEL}\n"
)
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
})
# 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.
# Batch-Update ausführen (bleibt gleich)
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."
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
if __name__ == '__main__':
print(f"Info: Verwendetes Fotodatum: {FOTODATUM}")
print(f"Info: Gruppennamen werden mit Suffix '{GRUPPENNAME_SUFFIX}' versehen.")
docs_service = get_docs_service_with_service_account()
if docs_service:
document_id = create_google_doc_from_csv(docs_service)
if document_id:
print("\n--- WICHTIG ---")
print(f"Das Dokument wurde vom Servicekonto erstellt: {SERVICE_ACCOUNT_FILE}")
print(f"Sie finden es unter: https://docs.google.com/document/d/{document_id}/edit")
print("Wenn Sie das Dokument in Ihrem Haupt-Google-Drive-Konto sehen und bearbeiten möchten,")
print("müssen Sie entweder:")
print("1. Das Servicekonto (die E-Mail-Adresse des Servicekontos, steht in der JSON-Datei)")
print(" als Bearbeiter zu dem Google Drive Ordner hinzufügen, in dem das Dokument erstellt werden soll (bevor das Skript läuft, oder das Dokument verschieben).")
print("2. Oder das Skript erweitern, um das Dokument nach der Erstellung explizit mit Ihrem")
print(" Benutzerkonto zu teilen (siehe auskommentierten Code-Teil mit `drive_service.permissions().create`).")
print(" Dafür benötigt das Servicekonto auch den Scope 'https://www.googleapis.com/auth/drive'.")