diff --git a/brancheneinstufung.py b/brancheneinstufung.py index dc9b4b7a..3f366e25 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -2465,13 +2465,16 @@ class DataProcessor: # ==================== MAIN FUNCTION ==================== # ==================== MAIN FUNCTION ==================== def main(): - global MODE, LOG_FILE # MODE wieder global, da es in älteren Teilen evtl. noch referenziert wird? Besser wäre es zu übergeben. + # global MODE, LOG_FILE # MODE wird nicht mehr global benötigt, aber LOG_FILE schon + # -> MODE wird unten neu gesetzt, kein global nötig. LOG_FILE wird global gesetzt. + global LOG_FILE # --- Initialisierung --- # Argument Parser parser = argparse.ArgumentParser(description="Firmen-Datenanreicherungs-Skript") parser.add_argument("--mode", type=str, help="Betriebsmodus (z.B. combined, wiki, website, branch, reeval, website_lookup, website_details, contacts, full_run)") - parser.add_argument("--limit", type=int, help="Maximale Anzahl zu verarbeitender Zeilen (für Batch/sequentielle Modi)", default=None) # Korrekter Name: --limit + # HIER KORRIGIERT: --limit statt --row_limit + parser.add_argument("--limit", type=int, help="Maximale Anzahl zu verarbeitender Zeilen (für Batch/sequentielle Modi)", default=None) args = parser.parse_args() # Lade API Keys @@ -2480,60 +2483,168 @@ def main(): # Betriebsmodus ermitteln valid_modes = ["combined", "wiki", "website", "branch", "reeval", "website_lookup", "website_details", "contacts", "full_run"] mode = None - # --> NEUE LOGIK: Priorisiere Kommandozeilenargumente + # Priorisiere Kommandozeilenargumente if args.mode and args.mode.lower() in valid_modes: mode = args.mode.lower() print(f"Betriebsmodus (aus Kommandozeile): {mode}") else: # Nur wenn KEIN Modus über die Kommandozeile kam, FRAGE interaktiv print("Bitte wählen Sie den Betriebsmodus:") - # ... (Liste der Modi ausgeben) ... - mode_input = input(f"Geben Sie den Modus ein ({', '.join(valid_modes)}): ").strip().lower() - if mode_input in valid_modes: - mode = mode_input - else: - print("Ungültige Eingabe. Standardmodus 'combined' wird verwendet.") - mode = "combined" # Standardmodus + print(" combined: Wiki-Verifizierung, Website-Scraping & Branch-Einschätzung (Batch, ab erster leerer Zeile)") + print(" wiki: Nur Wikipedia-Verifizierung (Batch, ab erster leerer Zeile)") + print(" website: Nur Website-Scraping & Zusammenfassung (Batch, ab erster leerer Zeile)") + print(" branch: Nur Branchen-Einschätzung (Batch, ab erster leerer Zeile)") + print(" reeval: Verarbeitet alle Zeilen mit 'x' in Spalte A (volle Verarbeitung)") + print(" website_lookup: Sucht fehlende Websites (Spalte D) via SERP API") + print(" website_details:Extrahiert Title/Desc/H-Tags für Zeilen mit 'x' in Spalte A") + print(" contacts: Sucht LinkedIn Kontakte via SERP API und schreibt in 'Contacts' Blatt") + print(" full_run: Verarbeitet alle Zeilen sequentiell ab der ersten ohne Zeitstempel (AO)") + try: + mode_input = input(f"Geben Sie den Modus ein ({', '.join(valid_modes)}): ").strip().lower() + if mode_input in valid_modes: + mode = mode_input + else: + print("Ungültige Eingabe. Standardmodus 'combined' wird verwendet.") + mode = "combined" # Standardmodus + # Füge eine Sicherheitsprüfung hinzu, falls input() in nohup aufgerufen wird + except OSError as e: + if e.errno == 9: # Bad file descriptor + print("Fehler: Interaktive Modus-Abfrage nicht möglich (läuft im Hintergrund?). Standardmodus 'combined' wird verwendet.") + mode = "combined" + else: + print(f"Unerwarteter OS-Fehler bei Modus-Abfrage: {e}") + print("Standardmodus 'combined' wird verwendet.") + mode = "combined" + except EOFError: # Kann auftreten, wenn stdin geschlossen ist (z.B. bei nohup) + print("Fehler: Interaktive Modus-Abfrage nicht möglich (EOF). Standardmodus 'combined' wird verwendet.") + mode = "combined" + # Zeilenlimit ermitteln row_limit = None - # --> NEUE LOGIK: Priorisiere Kommandozeilenargumente - if args.limit is not None: # Prüfe, ob --limit gesetzt wurde - row_limit = args.limit - print(f"Zeilenlimit (aus Kommandozeile): {row_limit}") + # Priorisiere Kommandozeilenargumente + if args.limit is not None: # Prüfe, ob --limit explizit gesetzt wurde (auch wenn 0) + # Erlaube auch 0 als Limit (obwohl es nichts tut, ist es eine gültige Angabe) + if args.limit >= 0: + row_limit = args.limit + print(f"Zeilenlimit (aus Kommandozeile): {row_limit}") + else: + print("Warnung: Negatives Zeilenlimit ignoriert. Kein Limit gesetzt.") + row_limit = None # Nur wenn KEIN Limit über die Kommandozeile kam UND es ein Modus ist, der ein Limit brauchen könnte, FRAGE interaktiv elif mode in ["combined", "wiki", "website", "branch", "full_run"]: try: limit_input = input("Wie viele Zeilen sollen maximal bearbeitet werden? (Enter für alle) ") if limit_input.strip(): - row_limit = int(limit_input) - print(f"Zeilenlimit: {row_limit}") + limit_val = int(limit_input) + if limit_val >= 0: + row_limit = limit_val + print(f"Zeilenlimit: {row_limit}") + else: + print("Warnung: Negatives Zeilenlimit ignoriert. Kein Limit gesetzt.") + row_limit = None else: row_limit = None # Alle verarbeiten print("Kein Zeilenlimit gesetzt.") except ValueError: - print("Ungültige Eingabe für Zeilenlimit. Es werden alle Zeilen verarbeitet.") + print("Ungültige Eingabe für Zeilenlimit. Kein Limit gesetzt.") row_limit = None - # Füge eine Sicherheitsprüfung hinzu, falls input() in nohup aufgerufen würde + # Füge eine Sicherheitsprüfung hinzu, falls input() in nohup aufgerufen wird except OSError as e: if e.errno == 9: # Bad file descriptor print("Warnung: Interaktive Abfrage des Limits nicht möglich (läuft im Hintergrund?). Kein Limit gesetzt.") row_limit = None else: - raise # Anderen OSError weiterwerfen + print(f"Unerwarteter OS-Fehler bei Limit-Abfrage: {e}") + print("Kein Limit gesetzt.") + row_limit = None + except EOFError: # Kann auftreten, wenn stdin geschlossen ist + print("Warnung: Interaktive Abfrage des Limits nicht möglich (EOF). Kein Limit gesetzt.") + row_limit = None - # --- Rest der main() Funktion bleibt gleich --- # Logfile initialisieren LOG_FILE = create_log_filename(mode) - # ... (Logging der Startparameter) ... - # ... (Schema laden) ... - # ... (Sheet Handler initialisieren) ... - # ... (DataProcessor initialisieren) ... - # ... (Modusausführung) ... - # ... (Abschluss) ... + debug_print(f"===== Skript gestartet =====") + debug_print(f"Version: {Config.VERSION}") + debug_print(f"Betriebsmodus: {mode}") + debug_print(f"Zeilenlimit: {row_limit if row_limit is not None else 'Unbegrenzt'}") + debug_print(f"Logdatei: {LOG_FILE}") -# (Der Rest der Datei bleibt unverändert) + # --- Vorbereitung --- + # Lade Branchenschema + load_target_schema() # Stellt sicher, dass globale Variablen gesetzt sind + # Initialisiere Google Sheet Handler + try: + sheet_handler = GoogleSheetHandler() + except Exception as e: + debug_print(f"FATAL: Konnte Google Sheet Handler nicht initialisieren: {e}") + return # Abbruch + + # Initialisiere DataProcessor + data_processor = DataProcessor(sheet_handler) + + # Optional: Alignment Demo für Hauptblatt ausführen? (Bleibt auskommentiert) + # run_alignment = input("Sollen die Header im Hauptblatt aktualisiert werden (Alignment Demo)? (j/N): ").lower() + # if run_alignment == 'j': + # alignment_demo(sheet_handler.sheet) + + # --- Modusausführung --- + start_time = time.time() + debug_print(f"Starte Verarbeitung um {datetime.now().strftime('%H:%M:%S')}...") + + try: + if mode in ["wiki", "website", "branch", "combined"]: + run_dispatcher(mode, sheet_handler, row_limit) + elif mode == "reeval": + data_processor.process_reevaluation_rows() + elif mode == "website_lookup": + data_processor.process_serp_website_lookup_for_empty() + elif mode == "website_details": + data_processor.process_website_details_for_marked_rows() + elif mode == "contacts": + process_contact_research(sheet_handler) # Übergib den Handler + elif mode == "full_run": + start_index = sheet_handler.get_start_row_index() # Index in Datenliste (0-basiert) + if start_index < len(sheet_handler.get_data()): # Nur starten, wenn Startindex valide ist + num_available = len(sheet_handler.get_data()) - start_index + # Berechne Anzahl zu verarbeitender Zeilen basierend auf Limit + if row_limit is not None and row_limit >= 0: + num_to_process = min(row_limit, num_available) + else: # Kein Limit oder negatives Limit -> alle verfügbaren + num_to_process = num_available + + if num_to_process > 0: + data_processor.process_rows_sequentially(start_index, num_to_process, process_wiki=True, process_chatgpt=True, process_website=True) + else: + debug_print("Keine Zeilen für 'full_run' zu verarbeiten (Limit 0 oder Startindex am Ende).") + else: + debug_print(f"Startindex {start_index} liegt hinter der letzten Datenzeile. Keine Verarbeitung für 'full_run'.") + else: + debug_print(f"Unbekannter Modus '{mode}' - keine Aktion ausgeführt.") + + except Exception as e: + debug_print(f"FATAL: Unerwarteter Fehler auf oberster Ebene während der Modusausführung: {e}") + import traceback + debug_print(traceback.format_exc()) # Detaillierten Stacktrace loggen + + # --- Abschluss --- + end_time = time.time() + duration = end_time - start_time + debug_print(f"Verarbeitung abgeschlossen um {datetime.now().strftime('%H:%M:%S')}.") + debug_print(f"Gesamtdauer: {duration:.2f} Sekunden.") + debug_print(f"===== Skript beendet =====") + # Sicherstellen, dass die letzte Log-Nachricht auch geschrieben wird + if LOG_FILE: + try: + with open(LOG_FILE, "a", encoding="utf-8") as f: + f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] ===== Skript wirklich beendet =====\n") + except: pass # Ignoriere Fehler beim letzten Schreiben + + print(f"Verarbeitung abgeschlossen. Logfile: {LOG_FILE}") + + +# Führt die main-Funktion aus, wenn das Skript direkt gestartet wird if __name__ == '__main__': main() \ No newline at end of file