Files
Brancheneinstufung2/list_generator.py
2025-05-27 05:47:10 +00:00

183 lines
11 KiB
Python

import csv
from datetime import datetime
import collections
import os.path
from google.oauth2 import service_account
from googleapiclient.discovery import build # discovery wird für das resource-Objekt der Lib benötigt
from googleapiclient.errors import HttpError
# Importiere die neue Bibliothek - KORRIGIERTER IMPORT und KLASSENNAME
from gdoctableapppy.gdoctableapp import gdoctableapp # Klasse ist klein geschrieben
# --- Konfiguration ---
CSV_FILENAME = 'Namensliste.csv'
GOOGLE_DOC_TITLE = f"Gruppenlisten_{datetime.now().strftime('%Y-%m-%d_%H-%M')}"
TARGET_FOLDER_ID = "18DNQaH9zbcBzwhckJI-4Uah-WXTXg6bg"
# Diese werden nur für den einmaligen Infoblock verwendet
EINRICHTUNG_INFO = "Kinderhaus St. Martin Neuching"
FOTODATUM_INFO = "02. - 05.06.2025"
GRUPPENNAME_SUFFIX = "gruppe"
SCOPES = [
'https://www.googleapis.com/auth/documents',
'https://www.googleapis.com/auth/drive.file'
]
SERVICE_ACCOUNT_FILE = 'service_account.json'
# --- Ende Konfiguration ---
def get_services_with_service_account():
creds = None; docs_service = None; drive_service = None
try: creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
except Exception as e: print(f"Fehler Credentials: {e}"); return None, None
try: docs_service = build('docs', 'v1', credentials=creds); print("Docs API Service erstellt.")
except Exception as e: print(f"Fehler Docs Service: {e}")
try:
if any(s in SCOPES for s in ['https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/drive']):
drive_service = build('drive', 'v3', credentials=creds); print("Drive API Service erstellt.")
else: print("WARNUNG: Kein Drive Scope für Drive Service.")
except Exception as e: print(f"Fehler Drive Service: {e}")
return docs_service, drive_service
def generate_tables_with_gdoctableapp(docs_api_service, document_id_to_fill):
# ... (CSV-Lesen, sorted_gruppen_namen, stand_zeit bleiben gleich) ...
kinder_nach_gruppen = collections.defaultdict(list) # Muss hier definiert werden
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: continue
kinder_nach_gruppen[gruppe_original].append({'Nachname': nachname, 'Vorname': vorname})
except Exception as e: print(f"FEHLER CSV-Lesen: {e}"); return False
if not kinder_nach_gruppen: print("Keine CSV-Daten."); return False
for gk_key in kinder_nach_gruppen:
kinder_nach_gruppen[gk_key].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")
try:
resource_for_lib = {"docsService": docs_api_service, "documentId": document_id_to_fill}
gtable_manager = gdoctableapp(resource_for_lib)
except Exception as e_dta_init:
print(f"Fehler bei der Initialisierung von gdoctableapp: {e_dta_init}"); return False
additional_requests_fallback = []
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
table_values = []
table_values.append(["Nachname", "Vorname", "Gruppe"])
for kind in kinder_liste:
table_values.append([kind['Nachname'], kind['Vorname'], gruppe_display_name])
try:
print(f"Versuche Tabelle für Gruppe '{gruppe_original}' mit gdoctableapp einzufügen...")
# KORREKTER METHODENNAME: create_table
res_table_obj = gtable_manager.create_table(values=table_values, index=None)
# Die Doku für create_table sagt:
# "When this is used, the table is created to the last of body. And, this method returns the object including the created table object and the start and end indexes of the table."
# Das zurückgegebene Objekt sollte also `{"table_object": {...}, "startIndex": X, "endIndex": Y}` sein.
# Und table_object enthält `created_table` mit `startIndex` (was wir als `tableStartIndex` brauchen).
# Laut der Doku zu `set_text_style_to_range` ist der Key im table_range Objekt `tableStartIndex` (großes S, kleines i).
if res_table_obj and "table_object" in res_table_obj and "created_table" in res_table_obj["table_object"]:
created_table_info = res_table_obj["table_object"]["created_table"]
table_start_idx_for_style = created_table_info.get("startIndex") # Dies ist der StartIndex des Tabellenobjekts selbst
if table_start_idx_for_style is not None:
header_range_for_style = {
"tableStartIndex": table_start_idx_for_style, # Korrekter Schlüsselname für die Methode
"rowIndex": 0,
"columnIndex": 0,
"rowSpan": 1,
"colSpan": 3
}
text_style_bold = {"bold": True}; fields_bold = "bold"
# KORREKTER METHODENNAME: set_text_style_to_range
gtable_manager.set_text_style_to_range(text_style_bold, fields_bold, table_range=header_range_for_style)
print(f" Kopfzeile für Tabelle '{gruppe_original}' versucht fett zu formatieren.")
else: print(f" Konnte startIndex für Tabelle '{gruppe_original}' nicht erhalten für Formatierung.")
else: print(f" Unerwartete Antwort von create_table für Formatierung: {res_table_obj}")
footer_lines_for_group = ["", f"{anzahl_kinder} angemeldete Kinder", "", "Dies ist die Liste der bereits angemeldeten Kinder. Bitte die Eltern der noch fehlenden", "Kinder an die Anmeldung erinnern.", "", f"Stand {stand_zeit}", ""]
full_footer_text_for_group = "\n".join(footer_lines_for_group) + "\n"
if full_footer_text_for_group.strip():
# KORREKTER METHODENNAME: insert_text
gtable_manager.insert_text(text=full_footer_text_for_group, index=None)
except Exception as e_table:
print(f"Fehler bei Tabelle/Footer für Gruppe '{gruppe_original}': {e_table}")
additional_requests_fallback.append({'insertText': {'endOfSegmentLocation': {}, 'text': f"\nFEHLER BEI TABELLE {gruppe_original}\n"}})
if i < len(sorted_gruppen_namen) - 1:
try:
# KORREKTER METHODENNAME: insert_page_break
gtable_manager.insert_page_break(index=None)
except Exception as e_pb:
print(f"Fehler beim Einfügen des PageBreak nach {gruppe_original}: {e_pb}")
additional_requests_fallback.append({'insertPageBreak': {'endOfSegmentLocation': {}}})
if additional_requests_fallback and docs_api_service:
try:
print(f"Sende zusätzliche Fallback-Requests für Doc ID '{document_id_to_fill}'...")
docs_api_service.documents().batchUpdate(documentId=document_id_to_fill, body={'requests': additional_requests_fallback}).execute()
except HttpError as err: print(f"Fehler bei zusätzlichen Fallback-Requests: {err}")
return True
# --- Main execution block ---
# (Bleibt weitgehend gleich, ruft generate_tables_with_gdoctableapp auf)
if __name__ == "__main__":
print(f"Info: Ordner-ID für Dokument: '{TARGET_FOLDER_ID}'")
# Wichtig: discovery.build wird für das resource Objekt der Lib gebraucht,
# unser get_services_with_service_account gibt aber das schon gebaute service Objekt zurück.
# Wir sollten `creds` zurückgeben und den Service ggf. neu bauen oder das `resource` Objekt anpassen.
# Einfacher: `docs_api_service` ist das, was die Lib als `docsService` braucht.
docs_api_service, drive_api_service = get_services_with_service_account()
if not docs_api_service:
print("Konnte Google Docs API Service nicht initialisieren. Skript wird beendet.")
else:
document_id = None
# 1. Dokument erstellen (wie zuvor)
# ... (Code für Dokumenterstellung bleibt gleich) ...
if drive_api_service and TARGET_FOLDER_ID:
# ... (Drive API Erstellung) ...
file_metadata = {'name': GOOGLE_DOC_TITLE, 'mimeType': 'application/vnd.google-apps.document', 'parents': [TARGET_FOLDER_ID]}
try:
created_file = drive_api_service.files().create(body=file_metadata, fields='id').execute()
document_id = created_file.get('id')
print(f"Neues leeres Doc via Drive API in Ordner '{TARGET_FOLDER_ID}' erstellt, ID: {document_id}")
except Exception as e: print(f"Fehler Drive API Erstellung: {e}\n Versuche Fallback...")
if not document_id:
try:
doc = docs_api_service.documents().create(body={'title': GOOGLE_DOC_TITLE}).execute()
document_id = doc.get('documentId')
print(f"Neues leeres Doc via Docs API (Root SA) erstellt, ID: {document_id}")
if TARGET_FOLDER_ID: print(f" BITTE manuell in Ordner '{TARGET_FOLDER_ID}' verschieben.")
except Exception as e: print(f"Konnte leeres Doc auch nicht mit Docs API erstellen: {e}")
if document_id:
# 2. Einmalige Info am Anfang
# ... (Code für initial_info bleibt gleich, wird mit docs_api_service.batchUpdate eingefügt) ...
initial_info_lines = ["Info zum Kopieren für Ihre manuelle Kopfzeile:", EINRICHTUNG_INFO, FOTODATUM_INFO, "\n" + "="*70 + "\n" ]
initial_text = "\n".join(initial_info_lines) + "\n"; initial_requests = [{'insertText': {'location': {'index': 1}, 'text': initial_text}}]
try: docs_api_service.documents().batchUpdate(documentId=document_id, body={'requests': initial_requests}).execute(); print("Einmalige Info eingefügt.")
except HttpError as err: print(f"Fehler bei einmaliger Info: {err}")
# 3. Dokument mit Tabellen befüllen
success_filling = generate_tables_with_gdoctableapp(docs_api_service, document_id)
if success_filling:
print(f"\n--- SKRIPT BEENDET ---"); print(f"Dokument-ID: {document_id}"); print(f"Link: https://docs.google.com/document/d/{document_id}/edit")
print("HINWEIS: Es wurde versucht, echte Tabellen mit fetter Kopfzeile via gdoctableapppy zu erstellen.")
else: print("\n--- FEHLER BEIM BEFÜLLEN MIT TABELLEN ---")
else: print("\n--- FEHLGESCHLAGEN: Kein Dokument erstellt ---")