This commit is contained in:
2025-05-26 18:04:01 +00:00
parent c8d14b7672
commit 5427394ff7

View File

@@ -1,43 +1,61 @@
import csv import csv
from datetime import datetime from datetime import datetime
import collections import collections
import os # For path handling, though not strictly needed if files are in same dir 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 --- # --- Konfiguration ---
CSV_FILENAME = 'Namensliste.csv' CSV_FILENAME = 'Namensliste.csv'
OUTPUT_FILENAME = 'Gruppenlisten_Output.txt' GOOGLE_DOC_TITLE = f"Gruppenlisten Kinderhaus St. Martin Neuching (Service Acc) - {datetime.now().strftime('%Y-%m-%d')}"
EINRICHTUNG = "Kinderhaus St. Martin Neuching" EINRICHTUNG = "Kinderhaus St. Martin Neuching"
# Bitte das Datum anpassen, falls die Annahme nicht korrekt war: FOTODATUM = "02. - 05.06.2025"
# Mögliche Optionen: GRUPPENNAME_SUFFIX = "gruppe"
# FOTODATUM = "02.06. & 05.06.2025" # Für zwei spezifische Tage
# FOTODATUM = "02.06. + 05.06.2025" # Alternative für zwei Tage
FOTODATUM = "02. - 05.06.2025" # Für einen Zeitraum
GRUPPENNAME_SUFFIX = "gruppe" # Wird an den Gruppennamen aus der CSV angehängt
FOTOGRAF_NAME = "Kinderfotos Erding" FOTOGRAF_NAME = "Kinderfotos Erding"
FOTOGRAF_ADRESSE = "Gartenstr. 10 85445 Oberding" FOTOGRAF_ADRESSE = "Gartenstr. 10 85445 Oberding"
FOTOGRAF_WEB = "www.kinderfotos-erding.de" FOTOGRAF_WEB = "www.kinderfotos-erding.de"
FOTOGRAF_TEL = "08122-8470867" 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 --- # --- Ende Konfiguration ---
def generate_group_lists(): def get_docs_service_with_service_account():
""" """Erstellt den API-Dienst mit einem Service Account."""
Liest die CSV-Datei, verarbeitet die Daten und generiert die gruppierten Listen. 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."""
# (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) kinder_nach_gruppen = collections.defaultdict(list)
try: try:
# 'utf-8-sig' handhabt das BOM (Byte Order Mark), falls vorhanden
with open(CSV_FILENAME, mode='r', encoding='utf-8-sig', newline='') as csvfile: with open(CSV_FILENAME, mode='r', encoding='utf-8-sig', newline='') as csvfile:
# Korrektur für das BOM-Zeichen, falls es manuell am Anfang der CSV-Daten steht
# und nicht durch utf-8-sig entfernt wird (bei String-Input nötig, bei Datei seltener)
# content = csvfile.read()
# if content.startswith('\ufeff'):
# content = content[1:]
# reader = csv.DictReader(content.splitlines(), delimiter=';') # Für String-Input
reader = csv.DictReader(csvfile, delimiter=';') reader = csv.DictReader(csvfile, delimiter=';')
for row in reader: for row in reader:
vorname = row.get('Vorname', '').strip() vorname = row.get('Vorname', '').strip()
@@ -47,92 +65,136 @@ def generate_group_lists():
if not vorname or not nachname or not gruppe_original: if not vorname or not nachname or not gruppe_original:
print(f"Warnung: Zeile übersprungen wegen fehlender Daten: {row}") print(f"Warnung: Zeile übersprungen wegen fehlender Daten: {row}")
continue continue
kinder_nach_gruppen[gruppe_original].append({ kinder_nach_gruppen[gruppe_original].append({
'Nachname': nachname, 'Nachname': nachname,
'Vorname': vorname 'Vorname': vorname
}) })
except FileNotFoundError: except FileNotFoundError:
print(f"FEHLER: Die Datei '{CSV_FILENAME}' wurde nicht gefunden.") print(f"FEHLER: Die Datei '{CSV_FILENAME}' wurde nicht gefunden.")
return return None
except Exception as e: except Exception as e:
print(f"FEHLER beim Lesen der CSV-Datei: {e}") print(f"FEHLER beim Lesen der CSV-Datei: {e}")
return return None
if not kinder_nach_gruppen: if not kinder_nach_gruppen:
print("Keine Daten aus der CSV-Datei geladen.") print("Keine Daten aus der CSV-Datei geladen.")
return return None
# Kinder innerhalb jeder Gruppe sortieren
for gruppe in kinder_nach_gruppen: for gruppe in kinder_nach_gruppen:
kinder_nach_gruppen[gruppe].sort(key=lambda x: (x['Nachname'].lower(), x['Vorname'].lower())) kinder_nach_gruppen[gruppe].sort(key=lambda x: (x['Nachname'].lower(), x['Vorname'].lower()))
# Alphabetisch sortierte Gruppenliste für die Ausgabe
sorted_gruppen_namen = sorted(kinder_nach_gruppen.keys()) sorted_gruppen_namen = sorted(kinder_nach_gruppen.keys())
gesamtdokument_teile = []
stand_zeit = datetime.now().strftime("%d.%m.%Y %H:%M Uhr") stand_zeit = datetime.now().strftime("%d.%m.%Y %H:%M Uhr")
for gruppe_original in sorted_gruppen_namen: # 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")
# 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:
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] kinder_liste = kinder_nach_gruppen[gruppe_original]
anzahl_kinder = len(kinder_liste) anzahl_kinder = len(kinder_liste)
# Gruppennamen für die Anzeige anpassen
gruppe_display_name = gruppe_original + GRUPPENNAME_SUFFIX gruppe_display_name = gruppe_original + GRUPPENNAME_SUFFIX
seiteninhalt = [] header_text = (
f"{EINRICHTUNG}\t\t\t{FOTOGRAF_NAME}\n"
f"{FOTODATUM}\n\n"
)
requests.append({'insertText': {'location': {'index': current_index}, 'text': header_text}})
current_index += len(header_text)
# Header num_rows = len(kinder_liste) + 1
# Für die Spaltenausrichtung in Google Docs ist es besser, Tabulatoren zu verwenden num_cols = 3
# und dann in Docs "Text in Tabelle umwandeln" zu nutzen oder manuell zu formatieren.
seiteninhalt.append(f"{EINRICHTUNG}\t\t\t{FOTOGRAF_NAME}")
seiteninhalt.append(FOTODATUM)
seiteninhalt.append("") # Leerzeile
# Tabellenkopf requests.append({'insertTable': {'location': {'index': current_index}, 'rows': num_rows, 'columns': num_cols}})
# Die Spaltenbreiten hier sind nur für die Textausgabe gedacht.
# Feste Breiten sind besser als nur Tabs für die reine Textansicht. table_content_start_index = current_index + 2
seiteninhalt.append(f"{'Nachname':<25}{'Vorname':<25}{'Gruppe'}")
seiteninhalt.append("-" * (25 + 25 + len(gruppe_display_name) + 2)) # Linie unter Kopf 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)
# Tabellenzeilen
for kind in kinder_liste: for kind in kinder_liste:
seiteninhalt.append(f"{kind['Nachname']:<25}{kind['Vorname']:<25}{gruppe_display_name}") 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)
seiteninhalt.append("") # Leerzeile current_index = table_content_start_index
# Footer footer_text = (
seiteninhalt.append(f"{anzahl_kinder} angemeldete Kinder") f"\n{anzahl_kinder} angemeldete Kinder\n\n"
seiteninhalt.append("") "Dies ist die Liste der bereits angemeldeten Kinder. Bitte die Eltern der noch fehlenden\n"
seiteninhalt.append("Dies ist die Liste der bereits angemeldeten Kinder. Bitte die Eltern der noch fehlenden") "Kinder an die Anmeldung erinnern.\n\n"
seiteninhalt.append("Kinder an die Anmeldung erinnern.") f"Stand {stand_zeit}\n\n"
seiteninhalt.append("") f"{FOTOGRAF_NAME}\n{FOTOGRAF_ADRESSE}\n{FOTOGRAF_WEB}\n{FOTOGRAF_TEL}\n"
seiteninhalt.append(f"Stand {stand_zeit}") )
seiteninhalt.append("\n") # Eine zusätzliche Leerzeile vor Fotografen-Block requests.append({'insertText': {'location': {'index': current_index}, 'text': footer_text}})
seiteninhalt.append(FOTOGRAF_NAME) current_index += len(footer_text)
seiteninhalt.append(FOTOGRAF_ADRESSE)
seiteninhalt.append(FOTOGRAF_WEB)
seiteninhalt.append(FOTOGRAF_TEL)
gesamtdokument_teile.append("\n".join(seiteninhalt)) if i < len(sorted_gruppen_namen) - 1:
requests.append({'insertPageBreak': {'endOfSegmentLocation': {}}})
# current_index += 1 # Für Page Break, aber mit endOfSegmentLocation nicht mehr so relevant für Index
# Seiten mit Trenner zusammenfügen if requests:
final_output_text = "\n\n--- SEITENWECHSEL / PAGE BREAK ---\n\n".join(gesamtdokument_teile) try:
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}")
print("Details zum Fehler:", err.resp.status, err._get_reason())
print("Gesendete Requests (Auszug):", requests[:5])
return document_id
try: if __name__ == '__main__':
with open(OUTPUT_FILENAME, mode='w', encoding='utf-8') as outfile:
outfile.write(final_output_text)
print(f"Erfolg! Die Listen wurden in '{OUTPUT_FILENAME}' gespeichert.")
except Exception as e:
print(f"FEHLER beim Schreiben der Ausgabedatei: {e}")
if __name__ == "__main__":
# Hier können Sie das Datum ggf. noch dynamischer setzen oder abfragen
# Beispiel: aktuelles_jahr = datetime.now().year
# FOTODATUM = f"02. - 05.06.{aktuelles_jahr}"
# Überprüfen Sie Ihre Datumseinstellung:
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.")
generate_group_lists() 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'.")