Files
Brancheneinstufung2/list_generator.py
2025-05-26 18:23:25 +00:00

220 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 = 'service_account.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):
"""Liest CSV, verarbeitet Daten und erstellt/befüllt das Google Doc."""
kinder_nach_gruppen = collections.defaultdict(list)
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()
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): # 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
# --- 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 ---
num_rows_for_table = len(kinder_liste) + 1
num_cols_for_table = 3
requests.append({
'insertTable': {
'location': {'index': current_index},
'rows': num_rows_for_table,
'columns': num_cols_for_table
}
})
table_fill_start_index = current_index + 1
table_text_content = []
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"
requests.append({
'insertText': {
'location': {'index': table_fill_start_index},
'text': full_table_text
}
})
# --- Footer ---
# Korrektur: 'text' muss auf derselben Ebene wie 'endOfSegmentLocation' sein
footer_text_for_page = (
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': {
'endOfSegmentLocation': {},
'text': footer_text_for_page
}
})
# --- Seitenumbruch ---
if i < len(sorted_gruppen_namen) - 1:
requests.append({'insertPageBreak': {'endOfSegmentLocation': {}}})
# 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
if requests:
try:
print("Sende Batch Update an Google Docs API...")
print("Anzahl der Requests:", len(requests))
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 = "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}")
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'.")