Refactor: Robuste Initialisierungs- und Konfigurationslogik in main()
- Umstrukturierung der `main()`-Funktion für eine saubere und robuste Initialisierungsreihenfolge: 1. Argumente parsen, 2. Logging einrichten, 3. Konfigurationsdaten laden, 4. Klassen initialisieren, 5. Modus ausführen. - Fehlerbehebung `NameError`: Die Funktion `parse_arguments()` wurde außerhalb von `main()` platziert und greift für Default-Werte nun korrekt auf die zuvor definierte `Config`-Klasse zu. Alle Aufrufe im Skript sind auf die zentrale `Config`-Klasse umgestellt. - Fehlerbehebung `TypeError`: Die Funktionen `load_target_schema()` und `load_branch_mapping()` geben jetzt in Fehlerfällen leere, aber typkorrekte Tupel/Dictionaries zurück, um Abstürze beim Entpacken der Rückgabewerte zu verhindern. - Verbessertes Logging: Das Logging wird nun zweistufig initialisiert, um auch die interaktive Modus- und Limit-Auswahl korrekt zu protokollieren. - Code-Konsolidierung: Die gesamte Logik zur Modus- und Parameter-Verarbeitung wurde innerhalb der neuen `main()`-Struktur bereinigt und zentralisiert.
This commit is contained in:
@@ -10314,680 +10314,191 @@ def parse_arguments():
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
"""
|
||||||
|
Haupteinstiegspunkt des Skripts.
|
||||||
|
Verarbeitet Kommandozeilen-Argumente, richtet Logging ein,
|
||||||
|
initialisiert Komponenten und dispatchet zu den passenden Modi.
|
||||||
|
"""
|
||||||
# 1. Argumente parsen
|
# 1. Argumente parsen
|
||||||
args = parse_arguments()
|
args = parse_arguments()
|
||||||
|
|
||||||
# 2. Logging konfigurieren
|
# 2. Temporäres Logging für interaktive Auswahl einrichten
|
||||||
global LOG_FILE
|
global LOG_FILE
|
||||||
log_level = logging.DEBUG if getattr(Config, 'DEBUG', False) else logging.INFO
|
log_level = logging.DEBUG if getattr(Config, 'DEBUG', False) else logging.INFO
|
||||||
log_format = '%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s'
|
log_format = '%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s'
|
||||||
logging.basicConfig(level=log_level, format=log_format, handlers=[logging.StreamHandler()])
|
|
||||||
|
temp_console_handler = logging.StreamHandler()
|
||||||
|
temp_console_handler.setFormatter(logging.Formatter(log_format))
|
||||||
|
root_logger = logging.getLogger('')
|
||||||
|
root_logger.setLevel(log_level)
|
||||||
|
root_logger.addHandler(temp_console_handler)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# 3. Globale Konfigurationsdaten laden
|
# 3. Modusauswahl (interaktiv, falls nicht per CLI gesetzt)
|
||||||
|
selected_mode = args.mode
|
||||||
|
if not selected_mode:
|
||||||
|
mode_categories = {
|
||||||
|
"Sequentielle Verarbeitung (Zeilenweise)": ["full_run"],
|
||||||
|
"Re-Evaluate Markierte Zeilen (Spalte A='x')": ["reeval"],
|
||||||
|
"Einzelne Dienstprogramme / Suchen": [
|
||||||
|
"find_wiki_serp", "website_lookup", "check_urls", "contacts", "update_wiki_suggestions",
|
||||||
|
"wiki_reextract_missing_an", "website_details", "train_technician_model", "alignment",
|
||||||
|
"reparatur_sitz", "plausi_check_data", "branch_eval", "suggest_parents", "analyze_ml_by_branch"
|
||||||
|
],
|
||||||
|
"Kombinierte Laeufe (Vordefiniert)": ["combined_all"]
|
||||||
|
}
|
||||||
|
valid_modes = [mode for modes in mode_categories.values() for mode in modes]
|
||||||
|
print("\nVerfügbare Betriebsmodi:")
|
||||||
|
mode_number = 1
|
||||||
|
mode_map = {}
|
||||||
|
for category, modes in mode_categories.items():
|
||||||
|
print(f"\n{category}:")
|
||||||
|
for mode in modes:
|
||||||
|
print(f" {mode_number}: {mode}")
|
||||||
|
mode_map[mode_number] = mode
|
||||||
|
mode_number += 1
|
||||||
|
print("\n 0: Abbrechen")
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
choice_str = input("\nGeben Sie den Modusnamen oder die Zahl ein: ").strip().lower()
|
||||||
|
if choice_str in ['0', 'abbrechen']:
|
||||||
|
print("Vorgang abgebrochen.")
|
||||||
|
return
|
||||||
|
if choice_str.isdigit() and int(choice_str) in mode_map:
|
||||||
|
selected_mode = mode_map[int(choice_str)]
|
||||||
|
break
|
||||||
|
elif choice_str in valid_modes:
|
||||||
|
selected_mode = choice_str
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print("Ungültige Eingabe. Bitte versuchen Sie es erneut.")
|
||||||
|
except (KeyboardInterrupt, EOFError):
|
||||||
|
print("\nVorgang abgebrochen.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 4. Finales Logging (mit Datei) einrichten
|
||||||
|
root_logger.removeHandler(temp_console_handler)
|
||||||
|
log_directory = Config.LOG_DIR
|
||||||
|
if not os.path.exists(log_directory): os.makedirs(log_directory)
|
||||||
|
timestamp = datetime.now().strftime("%d-%m-%Y_%H-%M")
|
||||||
|
log_filename = f"{timestamp}_v{Config.VERSION.replace('.', '')}_Modus{selected_mode}.txt"
|
||||||
|
LOG_FILE = os.path.join(log_directory, log_filename)
|
||||||
|
final_console_handler = logging.StreamHandler()
|
||||||
|
final_console_handler.setFormatter(logging.Formatter(log_format))
|
||||||
|
file_handler = logging.FileHandler(LOG_FILE, 'w', 'utf-8')
|
||||||
|
file_handler.setFormatter(logging.Formatter(log_format))
|
||||||
|
logging.basicConfig(level=log_level, format=log_format, handlers=[final_console_handler, file_handler])
|
||||||
|
|
||||||
|
logger.info(f"Logging wird jetzt auch in Datei geschrieben: {LOG_FILE}")
|
||||||
|
logger.info("===== Skript gestartet =====")
|
||||||
|
logger.info(f"Version: {Config.VERSION}")
|
||||||
|
logger.info(f"CLI Argumente: {args}")
|
||||||
|
|
||||||
|
# 5. Globale Konfigurationsdaten laden
|
||||||
logger.info("Lade Konfigurationsdateien...")
|
logger.info("Lade Konfigurationsdateien...")
|
||||||
Config.load_api_keys()
|
Config.load_api_keys()
|
||||||
ziel_schema, fokus_branchen = load_target_schema()
|
ziel_schema, fokus_branchen = load_target_schema()
|
||||||
branch_mapping_dict = load_branch_mapping()
|
branch_mapping_dict = load_branch_mapping()
|
||||||
|
|
||||||
if not ziel_schema or not branch_mapping_dict:
|
if not ziel_schema or not branch_mapping_dict:
|
||||||
logger.critical("Fehler beim Laden von Konfigurationsdateien (Schema oder Mapping). Skriptabbruch.")
|
logger.critical("Fehler beim Laden von Konfigurationsdateien. Skriptabbruch.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 4. Hauptkomponenten initialisieren
|
# 6. Hauptkomponenten initialisieren
|
||||||
logger.info("Initialisiere Hauptkomponenten...")
|
logger.info("Initialisiere Hauptkomponenten...")
|
||||||
sheet_handler = GoogleSheetHandler(Config.SHEET_ID, Config.SERVICE_ACCOUNT_FILE, Config.TOKEN_FILE)
|
sheet_handler = GoogleSheetHandler(Config.SHEET_ID, Config.SERVICE_ACCOUNT_FILE, Config.TOKEN_FILE)
|
||||||
wiki_scraper = WikipediaScraper()
|
wiki_scraper = WikipediaScraper()
|
||||||
data_processor = DataProcessor(sheet_handler, wiki_scraper, ziel_schema, fokus_branchen, branch_mapping_dict)
|
data_processor = DataProcessor(sheet_handler, wiki_scraper, ziel_schema, fokus_branchen, branch_mapping_dict)
|
||||||
|
|
||||||
logger.info("DataProcessor erfolgreich initialisiert.")
|
logger.info("DataProcessor erfolgreich initialisiert.")
|
||||||
|
|
||||||
# 5. Modus-Dispatching (der Rest Ihrer main-Funktion)
|
# 7. Modus-Dispatching
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
# Liste der gueltigen Modi - MUSS mit den elif-Zweigen unten uebereinstimmen!
|
|
||||||
# Kategorisiert fuer die Menue-Ausgabe
|
|
||||||
mode_categories = {
|
|
||||||
"Batch-Verarbeitung (Schritt-Optimiert)": [
|
|
||||||
"wiki_verify", # Uebereinstimmend mit process_verification_batch (Block 26)
|
|
||||||
"website_scraping", # Uebereinstimmend mit process_website_scraping_batch (Block 27)
|
|
||||||
"summarize_website", # Uebereinstimmend mit process_summarization_batch (Block 28)
|
|
||||||
"branch_eval", # Uebereinstimmend mit process_branch_batch (Block 29)
|
|
||||||
"suggest_parents",
|
|
||||||
],
|
|
||||||
"Sequentielle Verarbeitung (Zeilenweise)": [
|
|
||||||
"full_run", # Nutzt process_rows_sequentially (Block 24)
|
|
||||||
],
|
|
||||||
"Re-Evaluate Markierte Zeilen (Spalte A='x')": [
|
|
||||||
"reeval", # Nutzt process_reevaluation_rows (Block 25)
|
|
||||||
],
|
|
||||||
"Einzelne Dienstprogramme / Suchen": [
|
|
||||||
"find_wiki_serp", # Nutzt process_find_wiki_serp (Block 30)
|
|
||||||
"website_lookup", # Nutzt process_serp_website_lookup (Block 30)
|
|
||||||
"check_urls", # <<< NEUER MODUS HIER EINFÜGEN
|
|
||||||
"contacts", # Nutzt process_contact_search (Block 30)
|
|
||||||
"update_wiki_suggestions", # Nutzt process_wiki_updates_from_chatgpt (Block 32)
|
|
||||||
"wiki_reextract_missing_an", # Nutzt process_wiki_reextract_missing_an (Block 32)
|
|
||||||
"website_details", # EXPERIMENTELL - Nutzt process_website_details (Block 32)
|
|
||||||
"train_technician_model", # Nutzt train_technician_model (Block 31)
|
|
||||||
"alignment", # Nutzt globale alignment_demo (Block 14)
|
|
||||||
"reparatur_sitz",
|
|
||||||
"plausi_check_data" # NEUER MODUS HIER
|
|
||||||
],
|
|
||||||
"Kombinierte Laeufe (Vordefiniert)": [
|
|
||||||
"combined_all", # Definiert eine Sequenz von Batch-Modi
|
|
||||||
]
|
|
||||||
}
|
|
||||||
# Erstellen Sie eine flache Liste aller validen Modi fuer die Validierung
|
|
||||||
valid_modes = [mode for modes in mode_categories.values() for mode in modes]
|
|
||||||
|
|
||||||
|
|
||||||
# Dynamisch generieren des Help-Textes fuer den Modus
|
|
||||||
mode_help_text = "Betriebsmodus. Waehlen Sie einen der folgenden:\n"
|
|
||||||
for category, modes in mode_categories.items():
|
|
||||||
mode_help_text += f"\n{category}:\n"
|
|
||||||
for mode in modes:
|
|
||||||
mode_help_text += f" - {mode}\n"
|
|
||||||
|
|
||||||
parser.add_argument("--mode", type=str, help=mode_help_text)
|
|
||||||
# Hilfsargument fuer die CLI-basierte Modusauswahl (wenn --mode gesetzt ist)
|
|
||||||
parser.add_argument("-m", "--cli-mode", dest="mode", action="store_const", const=valid_modes[0] if valid_modes else None, help=argparse.SUPPRESS) # Unterdruecke in --help
|
|
||||||
|
|
||||||
parser.add_argument("--limit", type=int, help="Maximale Anzahl zu verarbeitender Zeilen in den meisten Modi (prueft Zeilen VOR Ueberspringung/Filterung).", default=None)
|
|
||||||
# start_sheet_row wird primaer fuer full_run verwendet, kann aber auch fuer Bereiche in Batch nuetzlich sein
|
|
||||||
parser.add_argument("--start_sheet_row", type=int, help="Startzeile im Sheet (1-basiert) fuer 'full_run' und einige Batch-Modi. Standard: Automatische Ermittlung basierend auf Timestamp.", default=None)
|
|
||||||
# end_sheet_row fuer Bereiche
|
|
||||||
parser.add_argument("--end_sheet_row", type=int, help="Endzeile im Sheet (1-basiert) fuer 'full_run' und einige Batch-Modi. Standard: Ende des Sheets.", default=None)
|
|
||||||
|
|
||||||
|
|
||||||
# Argument fuer den Re-Eval und Full-Run Modus zur Auswahl der Schritte
|
|
||||||
# Moegliche Werte fuer die Schritte: 'wiki', 'chat', 'web', 'ml_predict', etc. (entsprechend den step_type Schluesseln in _process_single_row Block 19)
|
|
||||||
# Default ist 'all' fuer alle Schritte, oder eine spezifische Liste
|
|
||||||
# Dies sind die Schluessel, die _process_single_row (Block 19) in steps_to_run Set erwartet.
|
|
||||||
valid_single_row_steps = ['wiki', 'chat', 'web', 'ml_predict'] # Fuegen Sie hier weitere Schritt-Schluessel hinzu, die _process_single_row versteht
|
|
||||||
single_row_steps_help = f"Komma-getrennte Liste der Schritte im 'reeval' und 'full_run' Modus (z.B. 'wiki,chat').\nMögliche Schritte: {', '.join(valid_single_row_steps)}.\nStandard: {'all' if valid_single_row_steps else 'keine'}" # Standard: alle verfuegbaren Schritte
|
|
||||||
|
|
||||||
# Standardwert fuer --steps: Alle gueltigen Single-Row Schritte, wenn es welche gibt
|
|
||||||
default_steps_arg = ','.join(valid_single_row_steps) if valid_single_row_steps else ''
|
|
||||||
parser.add_argument("--steps", type=str, help=single_row_steps_help, default=default_steps_arg)
|
|
||||||
|
|
||||||
|
|
||||||
# Argumente fuer find_wiki_serp (falls ueber CLI gesteuert)
|
|
||||||
parser.add_argument("--min_umsatz", type=float, help="Mindestumsatz in MIO € (CRM Spalte J) fuer find_wiki_serp Filter.", default=200.0) # Float fuer Konsistenz
|
|
||||||
parser.add_argument("--min_employees", type=int, help="Mindestmitarbeiterzahl (CRM Spalte K) fuer find_wiki_serp Filter.", default=500)
|
|
||||||
|
|
||||||
|
|
||||||
# Argumente fuer train_technician_model (Pfade fuer Output-Dateien)
|
|
||||||
parser.add_argument("--model_out", type=str, default=MODEL_FILE, help=f"Pfad fuer das trainierte Modell (.pkl). Standard: {MODEL_FILE}") # Block 1 Konstante
|
|
||||||
parser.add_argument("--imputer_out", type=str, default=IMPUTER_FILE, help=f"Pfad fuer den trainierten Imputer (.pkl). Standard: {IMPUTER_FILE}") # Block 1 Konstante
|
|
||||||
parser.add_argument("--patterns_out", type=str, default=PATTERNS_FILE_JSON, help=f"Pfad fuer die Feature-Spaltenliste (.json). Standard: {PATTERNS_FILE_JSON}") # Block 1 Konstante
|
|
||||||
|
|
||||||
# TODO: Fuegen Sie hier weitere CLI-Argumente hinzu, falls andere Modi Parameter benoetigen
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
# --- Konfiguration laden ---
|
|
||||||
Config.load_api_keys() # Nutzt jetzt logging intern (print am Anfang Block 1)
|
|
||||||
|
|
||||||
|
|
||||||
# --- Logdatei-Konfiguration abschliessen ---
|
|
||||||
# Bestimmen Sie den Log-Modus Namen basierend auf CLI oder Interaktion
|
|
||||||
# Wir nutzen den CLI Modus Namen, wenn --mode gesetzt ist, sonst "interactive".
|
|
||||||
log_mode_name = args.mode if args.mode else "interactive"
|
|
||||||
LOG_FILE = create_log_filename(log_mode_name) # Nutzt globale Funktion (Block 3)
|
|
||||||
|
|
||||||
# Wenn die Logdatei erfolgreich erstellt wurde
|
|
||||||
if LOG_FILE:
|
|
||||||
try:
|
|
||||||
# Erstellen Sie den FileHandler fuer die Logdatei
|
|
||||||
# mode='a' zum Anhaengen, encoding='utf-8' fuer Unicode
|
|
||||||
file_handler = logging.FileHandler(LOG_FILE, mode='a', encoding='utf-8')
|
|
||||||
file_handler.setLevel(log_level) # Nimm das globale Level
|
|
||||||
# Verwenden Sie denselben Formatter wie fuer den Console Handler
|
|
||||||
file_handler.setFormatter(logging.Formatter(log_format))
|
|
||||||
# Fuege FileHandler zum Root-Logger hinzu
|
|
||||||
# Pruefen, ob nicht schon ein File Handler mit demselben Pfad vorhanden ist (z.B. bei wiederholten Aufrufen in Tests)
|
|
||||||
if not any(isinstance(h, logging.FileHandler) and h.baseFilename == os.path.abspath(LOG_FILE) for h in logging.getLogger('').handlers):
|
|
||||||
logging.getLogger('').addHandler(file_handler)
|
|
||||||
logger.info(f"Logging wird jetzt auch in Datei geschrieben: {LOG_FILE}")
|
|
||||||
except Exception as e:
|
|
||||||
# Logge Fehler nur auf Konsole, da FileHandler fehlgeschlagen ist
|
|
||||||
# logger.exception loggt auch an die Konsole, wenn kein FileHandler da ist
|
|
||||||
logger.error(f"Konnte FileHandler fuer Logdatei '{LOG_FILE}' nicht erstellen: {e}")
|
|
||||||
# Optional: Entfernen Sie evtl. den fehlerhaften Handler aus der Liste
|
|
||||||
logging.getLogger('').handlers = [h for h in logging.getLogger('').handlers if not isinstance(h, logging.FileHandler) or h.baseFilename == os.path.abspath(LOG_FILE)] # Entferne nur den fehlerhaften Handler
|
|
||||||
|
|
||||||
|
|
||||||
# --- JETZT die Startmeldungen loggen (gehen jetzt in Konsole UND Datei) ---
|
|
||||||
logger.info(f"===== Skript gestartet =====")
|
|
||||||
logger.info(f"Version: {current_script_version}")
|
|
||||||
# Logge den tatsaechlichen Pfad der Logdatei oder die Fehlermeldung
|
|
||||||
logger.info(f"Logdatei: {LOG_FILE if LOG_FILE else 'FEHLER - Keine Logdatei erstellt'}")
|
|
||||||
# Loggen Sie relevante CLI Argumente zur Dokumentation des Laufs
|
|
||||||
logger.info(f"CLI Argumente: {args}")
|
|
||||||
|
|
||||||
|
|
||||||
# --- Vorbereitung (Schema, Handler etc.) ---
|
|
||||||
# Laden Sie das Ziel-Branchenschema (Block 6)
|
|
||||||
# load_target_schema ist mit retry_on_failure dekoriert (Block 2).
|
|
||||||
load_target_schema()
|
|
||||||
|
|
||||||
|
|
||||||
# Initialisiere GoogleSheetHandler (Block 14)
|
|
||||||
sheet_handler = None # Initialisiere Variable
|
|
||||||
try:
|
|
||||||
# Der GoogleSheetHandler Init (_init_ Methode) baut die Verbindung auf und laedt Daten.
|
|
||||||
# Fehler werden dort gefangen und als ConnectionError erneut geworfen.
|
|
||||||
sheet_handler = GoogleSheetHandler() #<- Zeile 11362
|
|
||||||
logger.info("GoogleSheetHandler erfolgreich initialisiert.")
|
|
||||||
except ConnectionError as e:
|
|
||||||
# Wenn die Initialisierung des SheetHandlers fehlschlaegt (Verbindungs-/Ladefehler)
|
|
||||||
logger.critical(f"FATAL: Initialisierung des GoogleSheetHandlers fehlgeschlagen: {e}")
|
|
||||||
logger.critical(f"Bitte ueberpruefen Sie Ihre Google Sheets URL, Credentials und Berechtigungen.")
|
|
||||||
logger.critical(f"Bitte Logdatei pruefen fuer Details: {LOG_FILE}")
|
|
||||||
return # Beende Skript, wenn Sheet nicht geladen werden kann
|
|
||||||
except Exception as e:
|
|
||||||
# Fangen Sie andere unerwartete Fehler bei der Initialisierung ab
|
|
||||||
logger.critical(f"FATAL: Unerwarteter Fehler bei Initialisierung von GoogleSheetHandler: {e}")
|
|
||||||
logger.debug(traceback.format_exc())
|
|
||||||
logger.critical(f"Bitte Logdatei pruefen fuer Details: {LOG_FILE}")
|
|
||||||
return # Beende Skript
|
|
||||||
|
|
||||||
|
|
||||||
# Initialisiere WikipediaScraper (Block 14)
|
|
||||||
wiki_scraper = None # Initialisiere Variable
|
|
||||||
try:
|
|
||||||
# Der WikipediaScraper Init (_init_ Methode) konfiguriert die Bibliothek und Requests.
|
|
||||||
# Fehler werden dort gefangen und erneut geworfen.
|
|
||||||
wiki_scraper = WikipediaScraper()
|
|
||||||
logger.info("WikipediaScraper erfolgreich initialisiert.")
|
|
||||||
except Exception as e:
|
|
||||||
# Wenn die Initialisierung des WikipediaScrapers fehlschlaegt
|
|
||||||
logger.critical(f"FATAL: Initialisierung des WikipediaScrapers fehlgeschlagen: {e}")
|
|
||||||
logger.debug(traceback.format_exc())
|
|
||||||
logger.critical(f"Bitte Logdatei pruefen fuer Details: {LOG_FILE}")
|
|
||||||
# Das Skript kann ohne Wiki Scraper viele Modi nicht sinnvoll laufen
|
|
||||||
return # Beende Skript
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Initialisieren Sie hier weitere Worker-Instanzen, falls Sie separate Klassen haben (z.B. OpenAIHandler, SerpAPIHandler)
|
|
||||||
# openai_handler = OpenAIHandler()
|
|
||||||
# serpapi_handler = SerpAPIHandler()
|
|
||||||
|
|
||||||
logger.info("===== Spalten-Alignment Check =====")
|
|
||||||
if sheet_handler and sheet_handler.sheet_values and len(sheet_handler.sheet_values) > 0:
|
|
||||||
sheet_headers = sheet_handler.sheet_values[0]
|
|
||||||
logger.info(f"Header aus Google Sheet (erste {len(sheet_headers)} Spalten): {sheet_headers}")
|
|
||||||
|
|
||||||
# Ausgabe der COLUMN_MAP für den Abgleich (gekürzt für Lesbarkeit im Log)
|
|
||||||
logger.info("Aktuelle COLUMN_MAP (Auszug):")
|
|
||||||
for i, (key, value) in enumerate(COLUMN_MAP.items()):
|
|
||||||
if i < 5 or i > len(COLUMN_MAP) - 6 : # Zeige erste 5 und letzte 5
|
|
||||||
logger.info(f" '{key}': {value}")
|
|
||||||
elif i == 5:
|
|
||||||
logger.info(" ...")
|
|
||||||
else:
|
|
||||||
logger.warning("Konnte Header nicht aus Google Sheet laden für Alignment Check.")
|
|
||||||
logger.info("===================================")
|
|
||||||
|
|
||||||
# Initialisiere DataProcessor Instanz (Block 15) mit Handlern
|
|
||||||
# Uebergeben Sie alle benoetigten Handler an den DataProcessor.
|
|
||||||
# Die __init__ Methode des DataProcessor (Block 15) prueft die Typen und wirft Value Error, wenn falsch.
|
|
||||||
try:
|
|
||||||
data_processor = DataProcessor(sheet_handler=sheet_handler, wiki_scraper=wiki_scraper)
|
|
||||||
logger.info("DataProcessor erfolgreich initialisiert.")
|
|
||||||
except Exception as e:
|
|
||||||
# Fangen Sie Fehler bei der DataProcessor Initialisierung ab.
|
|
||||||
logger.critical(f"FATAL: Initialisierung des DataProcessors fehlgeschlagen: {e}")
|
|
||||||
logger.debug(traceback.format_exc())
|
|
||||||
logger.critical(f"Bitte Logdatei pruefen fuer Details: {LOG_FILE}")
|
|
||||||
return # Beende Skript
|
|
||||||
|
|
||||||
|
|
||||||
# --- Modusauswahl und Ausfuehrung ---
|
|
||||||
start_process_time = time.time() # Zeitmessung fuer die Verarbeitung starten
|
|
||||||
logger.info(f"Starte Verarbeitung um {datetime.now().strftime('%H:%M:%S')}...")
|
logger.info(f"Starte Verarbeitung um {datetime.now().strftime('%H:%M:%S')}...")
|
||||||
|
|
||||||
|
|
||||||
selected_mode = None # Variable fuer den tatsaechlich auszufuehrenden Modus
|
|
||||||
|
|
||||||
# --- Ermitteln des zu fuehrenden Modus (CLI hat Prioritaet vor interaktiver Auswahl) ---
|
|
||||||
# Wenn das --mode Argument ueber die Kommandozeile gesetzt wurde
|
|
||||||
if args.mode:
|
|
||||||
selected_mode = args.mode.lower() # Konvertiere zu Kleinbuchstaben
|
|
||||||
# Pruefen Sie, ob der gewaehlte Modus in der Liste der validen Modi enthalten ist
|
|
||||||
if selected_mode not in valid_modes:
|
|
||||||
# Logge einen Fehler und beende das Skript, wenn der Modus ungueltig ist.
|
|
||||||
logger.error(f"Ungueltiger Modus '{args.mode}' ueber Kommandozeile angegeben. Gueltige Modi: {', '.join(valid_modes)}")
|
|
||||||
print(f"Fehler: Ungueltiger Modus '{args.mode}'. Bitte ueberpruefen Sie die Liste der gueltigen Modi (siehe --help).")
|
|
||||||
return # Skript beenden
|
|
||||||
logger.info(f"Betriebsmodus (CLI gewaehlt): {selected_mode}")
|
|
||||||
|
|
||||||
# Wenn das --mode Argument NICHT ueber die Kommandozeile gesetzt wurde
|
|
||||||
else:
|
|
||||||
# --- Interaktive Modusauswahl ueber die Konsole ---
|
|
||||||
print("\nBitte waehlen Sie den Betriebsmodus:")
|
|
||||||
# Zeigen Sie die Liste der validen Modi kategorisiert an, mit Nummern.
|
|
||||||
mode_options_map = {} # Dictionary zum Abbilden von Zahl/Name auf Modusname
|
|
||||||
option_counter = 1 # Zaehler fuer die numerischen Optionen
|
|
||||||
# Iteriere durch die Kategorien und Modi
|
|
||||||
for category, modes in mode_categories.items():
|
|
||||||
print(f"\n{category}:")
|
|
||||||
for mode in modes:
|
|
||||||
print(f" {option_counter}: {mode}")
|
|
||||||
mode_options_map[str(option_counter)] = mode # Bilde die numerische Option auf den Modusnamen ab
|
|
||||||
mode_options_map[mode] = mode # Bilde den Modusnamen (kleingeschrieben) auf sich selbst ab (fuer direkte Eingabe)
|
|
||||||
option_counter += 1 # Erhoehe den Zaehler
|
|
||||||
|
|
||||||
|
|
||||||
# Fuegen Sie eine Option zum Abbrechen hinzu
|
|
||||||
print(f"\n 0: Abbrechen")
|
|
||||||
mode_options_map['0'] = 'exit' # Bilde 0 auf den speziellen 'exit' Modus ab
|
|
||||||
|
|
||||||
|
|
||||||
# Schleife, bis ein gueltiger Modus gewaehlt wurde oder der Benutzer abbricht
|
|
||||||
while selected_mode is None:
|
|
||||||
try:
|
|
||||||
# Lesen Sie die Eingabe vom Benutzer
|
|
||||||
mode_input = input(f"Geben Sie den Modusnamen oder die Zahl ein: ").strip().lower()
|
|
||||||
|
|
||||||
# Pruefen Sie, ob die Eingabe einer Option in der Map entspricht
|
|
||||||
if mode_input in mode_options_map:
|
|
||||||
selected_mode = mode_options_map[mode_input] # Setzen Sie den gewaehlten Modusnamen
|
|
||||||
|
|
||||||
# Wenn der 'exit' Modus gewaehlt wurde
|
|
||||||
if selected_mode == 'exit':
|
|
||||||
logger.info("Modus 'exit' gewaehlt. Skript wird beendet.")
|
|
||||||
print("Abgebrochen durch Benutzer.")
|
|
||||||
return # Beende das Skript
|
|
||||||
|
|
||||||
# Logge den gewaehlten Modus
|
|
||||||
logger.info(f"Betriebsmodus (interaktiv gewaehlt): {selected_mode}")
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Wenn die Eingabe keinem gueltigen Modus entspricht
|
|
||||||
print("Ungueltige Eingabe. Bitte waehlen Sie eine gueltige Option aus der Liste.")
|
|
||||||
|
|
||||||
# Wenn selected_mode immer noch None ist, laeuft die Schleife weiter
|
|
||||||
|
|
||||||
|
|
||||||
except EOFError: # Benutzer hat Ctrl+D gedrueckt (End-of-File)
|
|
||||||
# Fangen Sie das EOFError ab und beenden Sie das Skript sauber.
|
|
||||||
logger.warning("Interaktive Modus-Eingabe abgebrochen (EOFError). Skript wird beendet.")
|
|
||||||
print("\nEingabe abgebrochen.")
|
|
||||||
return # Beende das Skript
|
|
||||||
except Exception as e:
|
|
||||||
# Fangen Sie andere unerwartete Fehler bei der Eingabe ab
|
|
||||||
logger.error(f"Fehler bei interaktiver Modus-Eingabe: {e}")
|
|
||||||
logger.debug(traceback.format_exc())
|
|
||||||
print(f"Ein Fehler ist bei der Modus-Eingabe aufgetreten ({e}). Bitte pruefen Sie die Logdatei.")
|
|
||||||
return # Beende das Skript bei unerwartetem Fehler
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# === NEUER BLOCK: Interaktive Limit-Abfrage ===
|
|
||||||
# ==============================================================================
|
|
||||||
limit_arg_cli = args.limit # ursprüngliches Limit vom CLI Argument
|
|
||||||
final_limit_to_use = limit_arg_cli # Standardmäßig das CLI-Limit verwenden
|
|
||||||
|
|
||||||
# Wenn kein Limit über CLI gesetzt wurde UND der Modus nicht einer der ist,
|
|
||||||
# bei denen ein Zeilenlimit typischerweise keinen Sinn macht.
|
|
||||||
# (Für 'alignment' und 'train_technician_model' ist ein Zeilen-Durchlauf-Limit meist nicht relevant)
|
|
||||||
# 'check_urls' und andere Batch-Modi können aber von einem Limit profitieren.
|
|
||||||
skippable_limit_modes = ['alignment', 'train_technician_model']
|
|
||||||
|
|
||||||
if final_limit_to_use is None and selected_mode not in skippable_limit_modes:
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
limit_input_str = input(f"Maximale Anzahl zu verarbeitender Zeilen für Modus '{selected_mode}' (Enter für Unbegrenzt, aktuell: Unbegrenzt): ").strip()
|
|
||||||
if not limit_input_str: # Benutzer drückt Enter
|
|
||||||
final_limit_to_use = None
|
|
||||||
logger.info("Kein Limit für Zeilenverarbeitung gesetzt (interaktiv).")
|
|
||||||
break
|
|
||||||
|
|
||||||
# Versuche, in Integer umzuwandeln
|
|
||||||
temp_limit = int(limit_input_str)
|
|
||||||
|
|
||||||
if temp_limit <= 0:
|
|
||||||
logger.warning(f"Ungültiges Limit '{temp_limit}' (<=0) eingegeben, wird als Unbegrenzt behandelt.")
|
|
||||||
final_limit_to_use = None
|
|
||||||
else:
|
|
||||||
final_limit_to_use = temp_limit
|
|
||||||
logger.info(f"Limit für Zeilenverarbeitung (interaktiv gesetzt): {final_limit_to_use}")
|
|
||||||
break # Gültige Eingabe oder Entscheidung für "Unbegrenzt"
|
|
||||||
except ValueError:
|
|
||||||
print("Ungültige Eingabe. Bitte eine ganze Zahl eingeben oder Enter für Unbegrenzt.")
|
|
||||||
except EOFError:
|
|
||||||
logger.warning("Interaktive Limit-Eingabe abgebrochen. Nutze kein Limit (oder CLI-Vorgabe, falls vorhanden).")
|
|
||||||
# final_limit_to_use behält den Wert von limit_arg_cli (also None, wenn hierher gekommen)
|
|
||||||
break
|
|
||||||
except Exception as e_limit_input:
|
|
||||||
logger.error(f"Unerwarteter Fehler bei interaktiver Limit-Eingabe: {e_limit_input}")
|
|
||||||
logger.debug(traceback.format_exc())
|
|
||||||
print("Ein Fehler ist bei der Limit-Eingabe aufgetreten. Nutze kein Limit.")
|
|
||||||
final_limit_to_use = None
|
|
||||||
break
|
|
||||||
elif final_limit_to_use is not None: # Wenn ein Limit via CLI gesetzt wurde
|
|
||||||
logger.info(f"Verwende Limit aus CLI-Argument: {final_limit_to_use}")
|
|
||||||
elif selected_mode in skippable_limit_modes and final_limit_to_use is None:
|
|
||||||
logger.info(f"Modus '{selected_mode}' benötigt typischerweise kein Zeilenlimit. Limit-Abfrage übersprungen.")
|
|
||||||
|
|
||||||
|
|
||||||
# Die Variable `final_limit_to_use` enthält nun das anzuwendende Limit (entweder von CLI, interaktiv oder None)
|
|
||||||
# ==============================================================================
|
|
||||||
# === ENDE NEUER BLOCK ===
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
# --- Ausfuehrung des gewaehlten Modus ---
|
|
||||||
try:
|
try:
|
||||||
# Holen Sie die CLI-Argumente fuer Start/End/Limit/Steps
|
logger.info(f"Betriebsmodus: {selected_mode}")
|
||||||
limit_arg = args.limit
|
final_limit_to_use = args.limit
|
||||||
start_row_arg = args.start_sheet_row
|
|
||||||
end_row_arg = args.end_sheet_row
|
|
||||||
|
|
||||||
# Sonderbehandlung fuer --steps Argument (relevant fuer reeval und full_run)
|
skippable_limit_modes = ['alignment', 'train_technician_model', 'analyze_ml_by_branch']
|
||||||
steps_to_run_set = set() # Initialisiere ein leeres Set
|
if final_limit_to_use is None and selected_mode not in skippable_limit_modes:
|
||||||
# Pruefen Sie, ob das --steps Argument gesetzt ist und nicht "all" (case-insensitive)
|
try:
|
||||||
if args.steps and isinstance(args.steps, str) and args.steps.strip().lower() != 'all':
|
limit_input = input(f"Maximale Anzahl zu verarbeitender Zeilen für Modus '{selected_mode}' (Enter für Unbegrenzt): ")
|
||||||
# Teilen Sie den String in Schritte auf und bereinigen Sie Leerzeichen
|
if limit_input.strip():
|
||||||
steps_list = [step.strip().lower() for step in args.steps.split(',') if step.strip()]
|
final_limit_to_use = int(limit_input)
|
||||||
# Filtern Sie nur erlaubte Schritte (die von _process_single_row verstanden werden Block 19)
|
logger.info(f"Limit für Zeilenverarbeitung (interaktiv gesetzt): {final_limit_to_use}")
|
||||||
steps_to_run_set = set(step for step in steps_list if step in valid_single_row_steps) # valid_single_row_steps wurde oben definiert
|
except (ValueError, KeyboardInterrupt, EOFError):
|
||||||
|
logger.warning("Kein gültiges Limit eingegeben, wird als Unbegrenzt behandelt.")
|
||||||
# Logge eine Warnung, wenn ungueltige Schritte angegeben wurden
|
final_limit_to_use = None
|
||||||
if len(steps_to_run_set) != len(steps_list):
|
|
||||||
invalid_steps = [step for step in steps_list if step not in valid_single_row_steps]
|
|
||||||
logger.warning(f"Ignoriere ungueltige Schritte im --steps Argument: {invalid_steps}. Fuehre nur {steps_to_run_set} aus.")
|
|
||||||
|
|
||||||
# Wenn nach der Filterung keine gueltigen Schritte uebrig sind
|
|
||||||
if not steps_to_run_set:
|
|
||||||
logger.error("Keine gueltigen Schritte im --steps Argument gefunden. Re-Eval/Full-Run kann nicht gestartet werden.")
|
|
||||||
print("Fehler: Keine gueltigen Schritte fuer den Modus ausgewaehlt. Bitte ueberpruefen Sie das --steps Argument.")
|
|
||||||
return # Skript beenden, wenn keine Schritte ausgewaehlt sind
|
|
||||||
|
|
||||||
# Wenn das --steps Argument 'all' ist oder nicht gesetzt
|
|
||||||
else:
|
|
||||||
# Fuhren Sie standardmaessig alle gueltigen Single-Row Schritte aus.
|
|
||||||
steps_to_run_set = set(valid_single_row_steps) # valid_single_row_steps wurde oben definiert
|
|
||||||
# Logge, welche Schritte ausgewaehlt wurden, wenn es der Standard ist
|
|
||||||
if default_steps_arg: # Wenn es ueberhaupt gueltige Schritte gibt
|
|
||||||
logger.debug(f"--steps Argument 'all' oder nicht gesetzt. Standard Schritte: {steps_to_run_set}.")
|
|
||||||
|
|
||||||
|
# --- Ausführung des gewählten Modus ---
|
||||||
|
valid_single_row_steps = ['wiki', 'chat', 'web', 'ml_predict']
|
||||||
|
steps_to_run_set = {step.strip().lower() for step in args.steps.split(',') if step.strip() and step.strip().lower() in valid_single_row_steps}
|
||||||
|
if not steps_to_run_set:
|
||||||
|
steps_to_run_set = set(valid_single_row_steps) # Default auf alle, wenn nichts Gültiges angegeben wurde
|
||||||
|
|
||||||
# Dispatching basierend auf dem gewaehlten Modus (selected_mode)
|
# Dispatching basierend auf dem gewaehlten Modus (selected_mode)
|
||||||
logger.info(f"Starte Ausfuehrung des Modus: {selected_mode}")
|
|
||||||
|
|
||||||
# ---- Kombinierte LÄUFE ----
|
|
||||||
if selected_mode == "combined_all":
|
if selected_mode == "combined_all":
|
||||||
# Führt die wichtigsten Batch-Modi nacheinander aus
|
data_processor.process_verification_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=args.limit)
|
||||||
logger.info("--- Start Kombinierter Modus: wiki_verify ---")
|
data_processor.process_website_scraping_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=args.limit)
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 26)
|
data_processor.process_summarization_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=args.limit)
|
||||||
data_processor.process_verification_batch(start_sheet_row=start_row_arg, end_sheet_row=end_row_arg, limit=limit_arg)
|
data_processor.process_branch_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=args.limit)
|
||||||
logger.info("--- Start Kombinierter Modus: website_scraping ---")
|
elif selected_mode == "wiki_verify":
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 27)
|
data_processor.process_verification_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=args.limit)
|
||||||
data_processor.process_website_scraping_batch(start_sheet_row=start_row_arg, end_sheet_row=end_row_arg, limit=limit_arg)
|
elif selected_mode == "website_scraping":
|
||||||
logger.info("--- Start Kombinierter Modus: summarize_website ---")
|
data_processor.process_website_scraping_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=args.limit)
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 28)
|
elif selected_mode == "summarize_website":
|
||||||
data_processor.process_summarization_batch(start_sheet_row=start_row_arg, end_sheet_row=end_row_arg, limit=limit_arg)
|
data_processor.process_summarization_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=args.limit)
|
||||||
logger.info("--- Start Kombinierter Modus: branch_eval ---")
|
elif selected_mode == "branch_eval":
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 29)
|
data_processor.process_branch_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=args.limit)
|
||||||
data_processor.process_branch_batch(start_sheet_row=start_row_arg, end_sheet_row=end_row_arg, limit=limit_arg)
|
elif selected_mode == "suggest_parents":
|
||||||
# TODO: Fuegen Sie hier weitere Batch-Modi hinzu, falls sie im kombinierten Lauf enthalten sein sollen
|
data_processor.process_parent_suggestion_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
logger.info("--- Kombinierter Modus abgeschlossen ---")
|
elif selected_mode == "full_run":
|
||||||
|
data_processor.process_rows_sequentially(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, num_to_process=final_limit_to_use, steps_to_run=steps_to_run_set)
|
||||||
|
elif selected_mode == "reeval":
|
||||||
# ---- Batch-VERARBEITUNG (Schritt-Optimiert) ----
|
data_processor.process_reevaluation_rows(row_limit=final_limit_to_use, steps_to_run=steps_to_run_set)
|
||||||
elif selected_mode == "wiki_verify": # Entspricht dem Batch-Modus Wiki Verifizierung (AX)
|
elif selected_mode == "find_wiki_serp":
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 26)
|
data_processor.process_find_wiki_serp(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use, min_employees=args.min_employees, min_umsatz=args.min_umsatz)
|
||||||
data_processor.process_verification_batch(start_sheet_row=start_row_arg, end_sheet_row=end_row_arg, limit=limit_arg)
|
elif selected_mode == "website_lookup":
|
||||||
|
data_processor.process_serp_website_lookup(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
elif selected_mode == "website_scraping": # Entspricht dem Batch-Modus Website Scraping (AR, AT, AP)
|
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 27)
|
|
||||||
data_processor.process_website_scraping_batch(start_sheet_row=start_row_arg, end_sheet_row=end_row_arg, limit=limit_arg)
|
|
||||||
|
|
||||||
elif selected_mode == "summarize_website": # Entspricht dem Batch-Modus Website Summarization (AS, AP)
|
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 28)
|
|
||||||
data_processor.process_summarization_batch(start_sheet_row=start_row_arg, end_sheet_row=end_row_arg, limit=limit_arg)
|
|
||||||
|
|
||||||
elif selected_mode == "branch_eval": # Entspricht dem Batch-Modus Branchen-Einstufung (W-Y, AO, AP)
|
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 29)
|
|
||||||
data_processor.process_branch_batch(start_sheet_row=start_row_arg, end_sheet_row=end_row_arg, limit=limit_arg)
|
|
||||||
|
|
||||||
|
|
||||||
# ---- Sequentielle VERARBEITUNG (Zeilenweise) ----
|
|
||||||
elif selected_mode == "full_run": # Nutzt process_rows_sequentially (Block 24)
|
|
||||||
# Full_run verarbeitet sequentiell einen Bereich.
|
|
||||||
# Startzeile wird vom CLI Argument oder automatisch ermittelt (erste leere AO).
|
|
||||||
# Endzeile vom CLI Argument oder bis Ende Sheet.
|
|
||||||
# Limit begrenzt die Anzahl der *verarbeiteten* Zeilen im Bereich.
|
|
||||||
|
|
||||||
calculated_start_sheet_row = start_row_arg # Beginne mit CLI Argument start_sheet_row
|
|
||||||
# Wenn start_sheet_row nicht ueber CLI gesetzt wurde
|
|
||||||
if calculated_start_sheet_row is None:
|
|
||||||
# Automatische Ermittlung der Startzeile (erste Zeile ohne AO)
|
|
||||||
logger.info("Automatische Ermittlung der Startzeile fuer sequenzielle Verarbeitung (erste Zeile ohne AO)...")
|
|
||||||
# get_start_row_index (Block 14) gibt 0-basierten Index in Daten (ohne Header) zurueck.
|
|
||||||
# Prueft auf leeren AO (Block 1 Column Map).
|
|
||||||
start_data_index_no_header = sheet_handler.get_start_row_index(check_column_key="Timestamp letzte Pruefung", min_sheet_row=7)
|
|
||||||
|
|
||||||
# Wenn get_start_row_index -1 zurueckgibt (Fehler)
|
|
||||||
if start_data_index_no_header == -1:
|
|
||||||
logger.error("FEHLER bei automatischer Ermittlung der Startzeile. Kann Full-Run nicht starten.")
|
|
||||||
return # Beende das Skript
|
|
||||||
|
|
||||||
# Berechne die 1-basierte Sheet-Startzeile aus dem 0-basierten Daten-Index
|
|
||||||
calculated_start_sheet_row = start_data_index_no_header + sheet_handler._header_rows + 1 # Block 14 SheetHandler Attribut
|
|
||||||
|
|
||||||
|
|
||||||
# Berechnen Sie die tatsaechliche Anzahl der zu verarbeitenden Zeilen im Bereich.
|
|
||||||
# (basierend auf Endzeile und Limit)
|
|
||||||
total_sheet_rows = len(sheet_handler.get_all_data_with_headers()) # Block 14 SheetHandler
|
|
||||||
calculated_end_sheet_row = end_row_arg if end_row_arg is not None else total_sheet_rows
|
|
||||||
# Stellen Sie sicher, dass die Endzeile nicht vor der Startzeile liegt
|
|
||||||
calculated_end_sheet_row = max(calculated_start_sheet_row - 1, calculated_end_sheet_row)
|
|
||||||
|
|
||||||
|
|
||||||
# Die Anzahl der Zeilen im betrachteten Bereich
|
|
||||||
rows_in_range = max(0, calculated_end_sheet_row - calculated_start_sheet_row + 1)
|
|
||||||
|
|
||||||
# num_to_process ist das Limit, angewendet auf die Zeilen im Bereich.
|
|
||||||
num_to_process_calc = rows_in_range # Standard: alle Zeilen im Bereich
|
|
||||||
# Wenn ein Limit ueber CLI gesetzt wurde und es gueltig ist
|
|
||||||
if limit_arg is not None and isinstance(limit_arg, int) and limit_arg >= 0:
|
|
||||||
num_to_process_calc = min(rows_in_range, limit_arg)
|
|
||||||
|
|
||||||
|
|
||||||
# Wenn es Zeilen zu verarbeiten gibt
|
|
||||||
if num_to_process_calc > 0:
|
|
||||||
logger.info(f"'full_run': Verarbeite {num_to_process_calc} Zeilen im Sheet-Bereich [{calculated_start_sheet_row}, {calculated_end_sheet_row}].")
|
|
||||||
# Rufe die sequentielle Verarbeitungsmethode auf (Block 24)
|
|
||||||
# _process_single_row (Block 19) wird intern aufgerufen.
|
|
||||||
data_processor.process_rows_sequentially(
|
|
||||||
start_sheet_row = calculated_start_sheet_row,
|
|
||||||
num_to_process = num_to_process_calc,
|
|
||||||
# Uebergeben Sie die aus dem --steps Argument ermittelten Flags (steps_to_run_set)
|
|
||||||
process_wiki_steps='wiki' in steps_to_run_set,
|
|
||||||
process_chatgpt_steps='chat' in steps_to_run_set,
|
|
||||||
process_website_steps='web' in steps_to_run_set,
|
|
||||||
process_ml_steps='ml_predict' in steps_to_run_set
|
|
||||||
# TODO: Weitere Schritt-Flags hier uebergeben
|
|
||||||
# force_reeval_in_single_row=False # Normalerweise kein Re-Eval im Full-Run
|
|
||||||
# clear_x_flag=False # Normalerweise kein X loeschen im Full-Run
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Wenn keine Zeilen zu verarbeiten sind
|
|
||||||
logger.info(f"Keine Zeilen fuer 'full_run' zu verarbeiten im Bereich [{calculated_start_sheet_row}, {calculated_end_sheet_row}] mit Limit {limit_arg}.")
|
|
||||||
|
|
||||||
|
|
||||||
# ---- Re-EVALUATE Markierte Zeilen ----
|
|
||||||
elif selected_mode == "reeval": # Nutzt process_reevaluation_rows (Block 25)
|
|
||||||
# reeval Modus nutzt immer force_reeval=True in _process_single_row.
|
|
||||||
# Das 'x'-Flag wird von _process_single_row (Block 21) geloescht, wenn clear_flag=True uebergeben wird.
|
|
||||||
# Das Limit wird direkt an process_reevaluation_rows uebergeben und dort gehandhabt.
|
|
||||||
if limit_arg is not None and isinstance(limit_arg, int) and limit_arg <= 0:
|
|
||||||
# Wenn ein Limit von 0 oder weniger angegeben wurde
|
|
||||||
logger.info(f"Limit {limit_arg} angegeben im Re-Eval Modus. Ueberspringe Verarbeitung.")
|
|
||||||
else:
|
|
||||||
# Rufe die Methode der DataProcessor Instanz auf (Block 25)
|
|
||||||
data_processor.process_reevaluation_rows(
|
|
||||||
row_limit=limit_arg, # Uebergibt das Limit (kann None sein)
|
|
||||||
clear_flag=True, # Standardmaessig das 'x'-Flag loeschen
|
|
||||||
# Uebergeben Sie die aus dem --steps Argument ermittelten Schritte (steps_to_run_set)
|
|
||||||
process_wiki_steps='wiki' in steps_to_run_set,
|
|
||||||
process_chatgpt_steps='chat' in steps_to_run_set,
|
|
||||||
process_website_steps='web' in steps_to_run_set,
|
|
||||||
process_ml_steps='ml_predict' in steps_to_run_set
|
|
||||||
# TODO: Weitere Schritt-Flags hier uebergeben
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ---- Einzelne DIENSTPROGRAMME / SUCHEN ----
|
|
||||||
elif selected_mode == "find_wiki_serp": # Nutzt process_find_wiki_serp (Block 30)
|
|
||||||
# find_wiki_serp sucht leere AY mit Groessenfilter. Nutzt limit, min_employees, min_umsatz.
|
|
||||||
# Start/Endzeile koennen manuell gesetzt werden oder werden automatisch ermittelt (erste leere AY).
|
|
||||||
data_processor.process_find_wiki_serp(
|
|
||||||
start_sheet_row=start_row_arg, # Kann manuell gesetzt werden
|
|
||||||
end_sheet_row=end_row_arg, # Kann manuell gesetzt werden
|
|
||||||
limit=limit_arg, # Kann manuell gesetzt werden
|
|
||||||
min_employees=args.min_employees, # Aus CLI Argument
|
|
||||||
min_umsatz=args.min_umsatz # Aus CLI Argument
|
|
||||||
)
|
|
||||||
|
|
||||||
elif selected_mode == "website_lookup": # Nutzt process_serp_website_lookup (Block 30)
|
|
||||||
# website_lookup sucht leere D. Nutzt limit. Start/Endzeile koennen manuell gesetzt werden.
|
|
||||||
data_processor.process_serp_website_lookup(
|
|
||||||
start_sheet_row=start_row_arg, # Kann manuell gesetzt werden
|
|
||||||
end_sheet_row=end_row_arg, # Kann manuell gesetzt werden
|
|
||||||
limit=limit_arg # Kann manuell gesetzt werden
|
|
||||||
)
|
|
||||||
|
|
||||||
elif selected_mode == "check_urls":
|
elif selected_mode == "check_urls":
|
||||||
data_processor.process_url_check(
|
data_processor.process_url_check(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
start_sheet_row=start_row_arg,
|
elif selected_mode == "contacts":
|
||||||
end_sheet_row=end_row_arg,
|
data_processor.process_contact_search(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
limit=limit_arg
|
elif selected_mode == "update_wiki_suggestions":
|
||||||
)
|
data_processor.process_wiki_updates_from_chatgpt(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
|
elif selected_mode == "wiki_reextract_missing_an":
|
||||||
elif selected_mode == "contacts": # Nutzt process_contact_search (Block 30)
|
data_processor.process_wiki_reextract_missing_an(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
# contacts sucht leere AM. Nutzt limit. Start/Endzeile koennen manuell gesetzt werden.
|
elif selected_mode == "website_details":
|
||||||
data_processor.process_contact_search(
|
data_processor.process_website_details(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
start_sheet_row=start_row_arg, # Kann manuell gesetzt werden
|
elif selected_mode == "train_technician_model":
|
||||||
end_sheet_row=end_row_arg, # Kann manuell gesetzt werden
|
data_processor.train_technician_model(model_out=args.model_out or Config.MODEL_FILE, imputer_out=args.imputer_out or Config.IMPUTER_FILE, patterns_out=args.patterns_out or Config.PATTERNS_FILE_JSON)
|
||||||
limit=limit_arg # Kann manuell gesetzt werden
|
elif selected_mode == "alignment":
|
||||||
)
|
alignment_demo(sheet_handler.sheet)
|
||||||
|
elif selected_mode == "reparatur_sitz":
|
||||||
elif selected_mode == "update_wiki_suggestions": # Nutzt process_wiki_updates_from_chatgpt (Block 32)
|
data_processor.process_repair_sitz_data(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
# update_wiki_suggestions prueft Status S. Nutzt limit. Start/Endzeile koennen manuell gesetzt werden.
|
|
||||||
data_processor.process_wiki_updates_from_chatgpt(
|
|
||||||
start_sheet_row=start_row_arg, # Kann manuell gesetzt werden
|
|
||||||
end_sheet_row=end_row_arg, # Kann manuell gesetzt werden
|
|
||||||
limit=limit_arg # Kann manuell gesetzt werden
|
|
||||||
)
|
|
||||||
|
|
||||||
elif selected_mode == "wiki_reextract_missing_an": # Nutzt process_wiki_reextract_missing_an (Block 32)
|
|
||||||
# wiki_reextract_missing_an sucht M gefuellt & AN leer. Nutzt limit. Start/Endzeile koennen manuell gesetzt werden.
|
|
||||||
# Ruft intern _process_single_row mit steps={'wiki'} und force_reeval=True auf.
|
|
||||||
data_processor.process_wiki_reextract_missing_an(
|
|
||||||
start_sheet_row=start_row_arg, # Kann manuell gesetzt werden
|
|
||||||
end_sheet_row=end_row_arg, # Kann manuell gesetzt werden
|
|
||||||
limit=limit_arg # Kann manuell gesetzt werden
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
elif selected_mode == "website_details": # EXPERIMENTELL - Nutzt process_website_details (Block 32)
|
|
||||||
# website_details sucht 'x' in A. Nutzt limit. Start/Endzeile koennen manuell gesetzt werden.
|
|
||||||
data_processor.process_website_details(
|
|
||||||
start_sheet_row=start_row_arg, # Kann manuell gesetzt werden
|
|
||||||
end_sheet_row=end_row_arg, # Kann manuell gesetzt werden
|
|
||||||
limit=limit_arg # Kann manuell gesetzt werden
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
elif selected_mode == "train_technician_model": # Nutzt train_technician_model (Block 31)
|
|
||||||
# training braucht keine Zeilenlimits im Sinne eines Bereichs oder der Anzahl zu verarbeitender Zeilen im Sheet.
|
|
||||||
# Es nutzt prepare_data_for_modeling (Block 31), die alle relevanten Zeilen filtert.
|
|
||||||
# Die output-Pfade werden aus CLI Argumenten genommen (args).
|
|
||||||
data_processor.train_technician_model(
|
|
||||||
model_out=args.model_out, # Aus CLI Argument
|
|
||||||
imputer_out=args.imputer_out, # Aus CLI Argument
|
|
||||||
patterns_out=args.patterns_out # Aus CLI Argument (JSON Datei)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif selected_mode == "alignment": # Nutzt globale alignment_demo (Block 14)
|
|
||||||
# alignment_demo ist eine globale Funktion, die das sheet Objekt braucht.
|
|
||||||
# Sie braucht keine Zeilenlimits oder Start/Ende.
|
|
||||||
if sheet_handler and sheet_handler.sheet:
|
|
||||||
alignment_demo(sheet_handler.sheet)
|
|
||||||
else:
|
|
||||||
logger.error("Sheet-Handler oder Sheet-Objekt nicht verfuegbar fuer Alignment-Demo.")
|
|
||||||
|
|
||||||
elif selected_mode == "reparatur_sitz": # NEUER BLOCK
|
|
||||||
# Hier können Sie Start, Ende und Limit aus args verwenden, falls Sie dafür CLI-Optionen hinzufügen möchten
|
|
||||||
# oder feste Werte / interaktive Abfragen für diesen Modus implementieren.
|
|
||||||
# Für den Anfang ein kompletter Durchlauf (ab Datenstart):
|
|
||||||
data_processor.process_repair_sitz_data(
|
|
||||||
start_sheet_row=None, # Beginnt nach den Headern
|
|
||||||
end_sheet_row=None, # Bis zum Ende des Sheets
|
|
||||||
limit=final_limit_to_use # Verwendet das global ermittelte Limit
|
|
||||||
)
|
|
||||||
|
|
||||||
elif selected_mode == "plausi_check_data":
|
elif selected_mode == "plausi_check_data":
|
||||||
data_processor.run_plausibility_checks_batch(
|
data_processor.run_plausibility_checks_batch(start_sheet_row=args.start_sheet_row, end_sheet_row=args.end_sheet_row, limit=final_limit_to_use)
|
||||||
start_sheet_row=args.start_sheet_row, # Nimmt CLI-Argumente für Bereich
|
|
||||||
end_sheet_row=args.end_sheet_row,
|
|
||||||
limit=final_limit_to_use # VERWENDE das ermittelte Limit
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
elif selected_mode == "suggest_parents": # <<< NEUER ELIF-BLOCK
|
|
||||||
data_processor.process_parent_suggestion_batch(
|
|
||||||
start_sheet_row=args.start_sheet_row,
|
|
||||||
end_sheet_row=args.end_sheet_row,
|
|
||||||
limit=final_limit_to_use, # Nutzt das ggf. interaktiv abgefragte Limit
|
|
||||||
re_evaluate_question_mark=True # Beispiel: Standardmäßig Fragezeichen neu bewerten
|
|
||||||
# Sie können hierfür auch ein CLI Argument hinzufügen
|
|
||||||
)
|
|
||||||
|
|
||||||
# ---- Modus nicht gefunden (sollte durch Validierung oben abgefangen werden) ----
|
|
||||||
else:
|
else:
|
||||||
# Dieser Zweig sollte aufgrund der Validierung am Anfang nie erreicht werden.
|
logger.error(f"Modus '{selected_mode}' ist definiert, hat aber keine Ausführungslogik in main().")
|
||||||
logger.error(f"Unerwarteter Modus '{selected_mode}' erreichte das Ausfuehrungsende des Dispatchers.")
|
|
||||||
print(f"Interner Fehler: Unbekannter Modus '{selected_mode}'.")
|
|
||||||
|
|
||||||
|
|
||||||
# --- Ausnahmebehandlung fuer den gesamten Ausfuehrungsblock ---
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# Wenn der Benutzer das Skript manuell unterbricht (Ctrl+C)
|
logger.warning("Skript durch Benutzer unterbrochen (KeyboardInterrupt).")
|
||||||
logger.warning("Skript durch Benutzer unterbrochen (KeyboardInterrupt).")
|
print("\n! Skript wurde manuell beendet.")
|
||||||
print("\n! Skript wurde manuell beendet.")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Dieser Block faengt alle unerwarteten Exceptions ab, die in den aufgerufenen
|
|
||||||
# Funktionen/Methoden passieren und nicht intern gefangen und behandelt werden.
|
|
||||||
logger.critical(f"FATAL: Unerwarteter Fehler waehrend der Ausfuehrung von Modus '{selected_mode}': {e}")
|
logger.critical(f"FATAL: Unerwarteter Fehler waehrend der Ausfuehrung von Modus '{selected_mode}': {e}")
|
||||||
# exception() loggt den Fehlertyp, die Nachricht und den vollständigen Traceback.
|
logger.error(f"Traceback des kritischen Fehlers:\n{traceback.format_exc()}")
|
||||||
logger.exception("Traceback des kritischen Fehlers:")
|
if LOG_FILE:
|
||||||
# Gebe eine Fehlermeldung an die Konsole aus, die auf das Log verweist.
|
print(f"\n! Ein kritischer Fehler ist aufgetreten: {type(e).__name__} - {e}. Bitte Logdatei pruefen: {LOG_FILE}")
|
||||||
print(f"\n! Ein kritischer Fehler ist aufgetreten: {type(e).__name__} - {e}")
|
else:
|
||||||
print(f"Bitte pruefen Sie die Logdatei fuer Details: {LOG_FILE}")
|
print(f"\n! Ein kritischer Fehler ist aufgetreten: {type(e).__name__} - {e}.")
|
||||||
|
finally:
|
||||||
|
end_time = time.time()
|
||||||
# --- Abschluss der Skriptausfuehrung ---
|
logger.info(f"Verarbeitung abgeschlossen um {datetime.now().strftime('%H:%M:%S')}.")
|
||||||
end_process_time = time.time() # Ende der Zeitmessung
|
logger.info(f"Gesamtdauer: {(end_time - start_time):.2f} Sekunden.")
|
||||||
duration = end_process_time - start_process_time # Berechne die Gesamtdauer
|
logger.info("===== Skript beendet =====")
|
||||||
logger.info(f"Verarbeitung abgeschlossen um {datetime.now().strftime('%H:%M:%S')}.")
|
logging.shutdown()
|
||||||
logger.info(f"Gesamtdauer: {duration:.2f} Sekunden.")
|
if LOG_FILE:
|
||||||
logger.info(f"===== Skript beendet =====")
|
print(f"\nVerarbeitung abgeschlossen. Logfile: {LOG_FILE}")
|
||||||
|
|
||||||
# Schliesse Logging Handler explizit
|
|
||||||
# Dies stellt sicher, dass alle gepufferten Logmeldungen in die Datei geschrieben werden.
|
|
||||||
logging.shutdown()
|
|
||||||
|
|
||||||
# Logfile Pfad fuer den Nutzer auf der Konsole ausgeben
|
|
||||||
if LOG_FILE:
|
|
||||||
print(f"\nVerarbeitung abgeschlossen. Logfile: {LOG_FILE}")
|
|
||||||
else:
|
|
||||||
print("\nVerarbeitung abgeschlossen. Es konnte keine Logdatei erstellt werden.")
|
|
||||||
|
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user