This commit is contained in:
2025-04-17 19:32:37 +00:00
parent faac14c91b
commit a45e1a3109

View File

@@ -3209,203 +3209,168 @@ class DataProcessor:
# @retry_on_failure # Vorsicht mit Retry auf dieser Ebene für die ganze Zeile
def _process_single_row(self, row_num_in_sheet, row_data, process_wiki=True, process_chatgpt=True, process_website=True):
"""
Verarbeitet die Daten für eine einzelne Zeile, prüft Timestamps für jeden Teilbereich.
Args:
row_num_in_sheet (int): Die 1-basierte Zeilennummer im Google Sheet.
row_data (list): Die List der Daten für diese Zeile.
process_wiki (bool): Ob der Wikipedia-Teil ausgeführt werden soll.
process_chatgpt (bool): Ob der ChatGPT-Evaluationsteil ausgeführt werden soll.
process_website (bool): Ob der Website-Teil ausgeführt werden soll.
Verarbeitet die Daten für eine einzelne Zeile, prüft Timestamps für jeden Teilbereich
und priorisiert die URL in Spalte M, wenn Wiki neu verarbeitet wird.
"""
debug_print(f"--- Starte Verarbeitung für Zeile {row_num_in_sheet} ---")
updates = [] # Sammle alle Updates für diese Zeile
updates = []
now_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
any_processing_done = False # Flag, ob überhaupt etwas getan wurde
any_processing_done = False
# --- Daten extrahieren (nutze COLUMN_MAP) ---
# Hilfsfunktion, um sicher auf Index zuzugreifen
def get_cell_value(col_key):
idx = COLUMN_MAP.get(col_key)
if idx is not None and len(row_data) > idx:
return row_data[idx]
return "" # Oder None oder einen anderen Standardwert
# Hilfsfunktion für sicheren Zugriff
def get_cell_value(key):
idx = COLUMN_MAP.get(key)
if idx is not None and len(row_data) > idx: return row_data[idx]
return ""
company_name = get_cell_value("CRM Name")
website_url = get_cell_value("CRM Website")
original_website = website_url # Merken für späteren Vergleich
original_website = website_url
crm_branche = get_cell_value("CRM Branche")
crm_beschreibung = get_cell_value("CRM Beschreibung")
crm_wiki_url = get_cell_value("CRM Vorschlag Wiki URL")
crm_wiki_url = get_cell_value("CRM Vorschlag Wiki URL") # Wird nur noch als Fallback genutzt
konsistenz_s = get_cell_value("Chat Wiki Konsistenzprüfung") # Für Re-Parse Trigger
# Lese aktuelle Werte für Website Rohtext/Zusammenfassung
# Lese aktuelle Werte, die potenziell überschrieben werden
website_raw = get_cell_value("Website Rohtext") or "k.A."
website_summary = get_cell_value("Website Zusammenfassung") or "k.A."
wiki_data = { # Lade aktuelle Wiki-Daten für den Fall, dass Wiki nicht neu geparst wird
'url': get_cell_value("Wiki URL") or 'k.A.',
'first_paragraph': get_cell_value("Wiki Absatz") or 'k.A.',
'branche': get_cell_value("Wiki Branche") or 'k.A.',
'umsatz': get_cell_value("Wiki Umsatz") or 'k.A.',
'mitarbeiter': get_cell_value("Wiki Mitarbeiter") or 'k.A.',
'categories': get_cell_value("Wiki Kategorien") or 'k.A.'
}
# --- 1. Website Handling (Lookup, Scrape, Summarize) ---
# Prüfe Timestamp AT (Index 45)
# --- 1. Website Handling (wie zuletzt -> prüft AT) ---
website_ts_needed = process_website and not get_cell_value("Website Scrape Timestamp").strip()
if website_ts_needed:
debug_print(f"Zeile {row_num_in_sheet}: Starte Website Verarbeitung (Timestamp AT fehlt)...")
any_processing_done = True
# Website Lookup, wenn leer
# --- Lookup ---
if not website_url or website_url.strip().lower() == "k.a.":
debug_print(f"Zeile {row_num_in_sheet}: CRM Website fehlt, starte SERP Lookup für '{company_name}'...")
new_website = serp_website_lookup(company_name)
if new_website != "k.A.":
website_url = new_website # Aktualisiere URL für weitere Schritte
debug_print(f"Zeile {row_num_in_sheet}: SERP Lookup erfolgreich: {website_url}")
if website_url != original_website:
updates.append({'range': f'D{row_num_in_sheet}', 'values': [[website_url]]})
else:
debug_print(f"Zeile {row_num_in_sheet}: SERP Lookup erfolglos.")
# Website Scraping, wenn URL vorhanden
new_website = serp_website_lookup(company_name)
if new_website != "k.A.": website_url = new_website; debug_print(f"Zeile {row_num_in_sheet}: SERP Lookup: {website_url}");
if website_url != original_website: updates.append({'range': f'D{row_num_in_sheet}', 'values': [[website_url]]})
else: debug_print(f"Zeile {row_num_in_sheet}: SERP Lookup erfolglos.")
# --- Scraping ---
if website_url and website_url.strip().lower() != "k.a.":
debug_print(f"Zeile {row_num_in_sheet}: Starte Website Scraping für {website_url}...")
new_website_raw = get_website_raw(website_url)
new_website_summary = summarize_website_content(new_website_raw)
# Füge Updates nur hinzu, wenn sich etwas geändert hat oder vorher k.A. war
if new_website_raw != website_raw:
updates.append({'range': f'AR{row_num_in_sheet}', 'values': [[new_website_raw]]})
website_raw = new_website_raw # Aktualisiere lokalen Wert für Chat-Teil
if new_website_summary != website_summary:
updates.append({'range': f'AS{row_num_in_sheet}', 'values': [[new_website_summary]]})
website_summary = new_website_summary # Aktualisiere lokalen Wert für Chat-Teil
if new_website_raw != website_raw: updates.append({'range': f'AR{row_num_in_sheet}', 'values': [[new_website_raw]]}); website_raw = new_website_raw
if new_website_summary != website_summary: updates.append({'range': f'AS{row_num_in_sheet}', 'values': [[new_website_summary]]}); website_summary = new_website_summary
else:
debug_print(f"Zeile {row_num_in_sheet}: Überspringe Website Scraping (keine gültige URL).")
# Setze Rohtext/Summary auf k.A., falls sie vorher was anderes waren?
if website_raw != "k.A.": updates.append({'range': f'AR{row_num_in_sheet}', 'values': [['k.A.']]})
if website_summary != "k.A.": updates.append({'range': f'AS{row_num_in_sheet}', 'values': [['k.A.']]})
website_raw, website_summary = "k.A.", "k.A." # Aktualisiere lokale Werte
# Setze Website Timestamp (AT)
website_raw, website_summary = "k.A.", "k.A."
updates.append({'range': f'AT{row_num_in_sheet}', 'values': [[now_timestamp]]})
# Version wird am Ende gesetzt
elif process_website:
debug_print(f"Zeile {row_num_in_sheet}: Überspringe Website Verarbeitung (Timestamp AT vorhanden).")
# Stelle sicher, dass lokale Variablen website_raw/summary aktuell sind
website_raw = get_cell_value("Website Rohtext") or "k.A."
website_summary = get_cell_value("Website Zusammenfassung") or "k.A."
# --- 2. Wikipedia Handling (ANGEPASSTE LOGIK) ---
# Trigger für Reparsing: AN fehlt ODER Status S ist "X (URL Copied)"
wiki_ts_an_missing = not get_cell_value("Wikipedia Timestamp").strip()
status_s_indicates_reparse = konsistenz_s.strip().upper() == "X (URL COPIED)"
reparse_wiki_needed = process_wiki and (wiki_ts_an_missing or status_s_indicates_reparse)
# --- 2. Wikipedia Handling ---
wiki_data = {} # Wird gefüllt, entweder durch Scraping oder aus Zeile gelesen
wiki_ts_needed = process_wiki and not get_cell_value("Wikipedia Timestamp").strip()
if wiki_ts_needed:
debug_print(f"Zeile {row_num_in_sheet}: Starte Wikipedia Verarbeitung (Timestamp AN fehlt)...")
if reparse_wiki_needed:
debug_print(f"Zeile {row_num_in_sheet}: Starte Wikipedia Verarbeitung (Grund: AN fehlt? {wiki_ts_an_missing}, S='X (URL Copied)'? {status_s_indicates_reparse})...")
any_processing_done = True
new_wiki_data_extracted = None # Wird mit den neuen Daten gefüllt
# Logik für Suche und Extraktion
valid_crm_wiki_url = crm_wiki_url if crm_wiki_url and crm_wiki_url.strip() not in ["", "k.A."] else None
article_page = None
if valid_crm_wiki_url:
debug_print(f"Zeile {row_num_in_sheet}: Prüfe CRM Wiki Vorschlag: {valid_crm_wiki_url}")
page = self.wiki_scraper._fetch_page_content(valid_crm_wiki_url.split('/')[-1])
# Überprüfe ob website_url hier aktuell ist (könnte durch Lookup geändert sein)
current_website_for_validation = website_url if website_url and website_url != 'k.A.' else original_website
if page and self.wiki_scraper._validate_article(page, company_name, current_website_for_validation):
article_page = page
else:
debug_print(f"Zeile {row_num_in_sheet}: CRM Wiki Vorschlag nicht validiert. Starte Suche...")
article_page = self.wiki_scraper.search_company_article(company_name, current_website_for_validation)
# --- Priorisiere URL aus Spalte M ---
url_to_parse = get_cell_value("Wiki URL").strip()
if url_to_parse and url_to_parse.lower() not in ["k.a.", "kein artikel gefunden"] and url_to_parse.lower().startswith("http"):
debug_print(f" -> Nutze vorhandene URL aus Spalte M: {url_to_parse}")
new_wiki_data_extracted = self.wiki_scraper.extract_company_data(url_to_parse)
else:
debug_print(f"Zeile {row_num_in_sheet}: Kein CRM Wiki Vorschlag. Starte Suche...")
current_website_for_validation = website_url if website_url and website_url != 'k.A.' else original_website
article_page = self.wiki_scraper.search_company_article(company_name, current_website_for_validation)
# --- Nur wenn M leer/ungültig ist, starte die Suche ---
debug_print(f" -> Spalte M ('{url_to_parse}') ungültig/leer. Starte Wiki-Suche...")
valid_crm_wiki_url = crm_wiki_url if crm_wiki_url and crm_wiki_url.strip() not in ["", "k.A."] else None
article_page = None
current_website_for_validation = website_url if website_url and website_url != 'k.A.' else original_website # Nutze aktuelle URL
if valid_crm_wiki_url:
debug_print(f" -> Prüfe CRM Vorschlag L: {valid_crm_wiki_url}")
page = self.wiki_scraper._fetch_page_content(valid_crm_wiki_url.split('/')[-1])
if page and self.wiki_scraper._validate_article(page, company_name, current_website_for_validation): article_page = page
else: debug_print(f" -> CRM Vorschlag L nicht validiert. Starte Suche..."); article_page = self.wiki_scraper.search_company_article(company_name, current_website_for_validation)
else:
debug_print(f" -> Kein CRM Vorschlag L. Starte Suche...")
article_page = self.wiki_scraper.search_company_article(company_name, current_website_for_validation)
if article_page:
debug_print(f"Zeile {row_num_in_sheet}: Extrahiere Daten aus Artikel: {article_page.url}")
wiki_data = self.wiki_scraper.extract_company_data(article_page.url)
else:
debug_print(f"Zeile {row_num_in_sheet}: Kein passender Wikipedia Artikel gefunden.")
# Setze Standard-k.A.-Werte für wiki_data
wiki_data = {'url': 'Kein Artikel gefunden', 'first_paragraph': 'k.A.', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.'}
if article_page:
debug_print(f" -> Artikel gefunden durch Suche: {article_page.url}")
new_wiki_data_extracted = self.wiki_scraper.extract_company_data(article_page.url)
else:
debug_print(f" -> Kein passender Wikipedia Artikel durch Suche gefunden.")
new_wiki_data_extracted = {'url': 'Kein Artikel gefunden', 'first_paragraph': 'k.A.', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.'}
# Füge Wiki-Daten zu Updates hinzu
updates.append({'range': f'M{row_num_in_sheet}', 'values': [[wiki_data.get('url', 'k.A.')]]})
updates.append({'range': f'N{row_num_in_sheet}', 'values': [[wiki_data.get('first_paragraph', 'k.A.')]]})
updates.append({'range': f'O{row_num_in_sheet}', 'values': [[wiki_data.get('branche', 'k.A.')]]})
updates.append({'range': f'P{row_num_in_sheet}', 'values': [[wiki_data.get('umsatz', 'k.A.')]]})
updates.append({'range': f'Q{row_num_in_sheet}', 'values': [[wiki_data.get('mitarbeiter', 'k.A.')]]})
updates.append({'range': f'R{row_num_in_sheet}', 'values': [[wiki_data.get('categories', 'k.A.')]]})
# Setze Wiki Timestamp (AN)
updates.append({'range': f'AN{row_num_in_sheet}', 'values': [[now_timestamp]]})
# Version wird am Ende gesetzt
# --- Updates für Wiki-Spalten vorbereiten ---
if new_wiki_data_extracted:
wiki_data = new_wiki_data_extracted # Überschreibe alte Daten für nachfolgende Schritte
updates.append({'range': f'M{row_num_in_sheet}', 'values': [[wiki_data.get('url', 'k.A.')]]})
updates.append({'range': f'N{row_num_in_sheet}', 'values': [[wiki_data.get('first_paragraph', 'k.A.')]]})
updates.append({'range': f'O{row_num_in_sheet}', 'values': [[wiki_data.get('branche', 'k.A.')]]})
updates.append({'range': f'P{row_num_in_sheet}', 'values': [[wiki_data.get('umsatz', 'k.A.')]]})
updates.append({'range': f'Q{row_num_in_sheet}', 'values': [[wiki_data.get('mitarbeiter', 'k.A.')]]})
updates.append({'range': f'R{row_num_in_sheet}', 'values': [[wiki_data.get('categories', 'k.A.')]]})
updates.append({'range': f'AN{row_num_in_sheet}', 'values': [[now_timestamp]]}) # Setze AN Timestamp neu
# Wenn der Trigger "X (URL Copied)" war, setze S zurück
if status_s_indicates_reparse:
konsistenz_s_idx = COLUMN_MAP.get("Chat Wiki Konsistenzprüfung")
if konsistenz_s_idx is not None:
s_col_letter = self.sheet_handler._get_col_letter(konsistenz_s_idx + 1)
updates.append({'range': f'{s_col_letter}{row_num_in_sheet}', 'values': [["?"]]}) # Setze auf "?" für erneute Prüfung
debug_print(f" -> Status S zurückgesetzt auf '?' für erneute Verifikation.")
elif process_wiki: # Wenn nicht benötigt, aber Modus aktiv ist
debug_print(f"Zeile {row_num_in_sheet}: Überspringe Wikipedia Verarbeitung (Timestamp AN vorhanden).")
# Lade vorhandene Wiki-Daten aus der Zeile, um sie für ChatGPT verfügbar zu machen
wiki_data['url'] = get_cell_value("Wiki URL") or 'k.A.'
wiki_data['first_paragraph'] = get_cell_value("Wiki Absatz") or 'k.A.'
wiki_data['branche'] = get_cell_value("Wiki Branche") or 'k.A.'
wiki_data['umsatz'] = get_cell_value("Wiki Umsatz") or 'k.A.'
wiki_data['mitarbeiter'] = get_cell_value("Wiki Mitarbeiter") or 'k.A.'
wiki_data['categories'] = get_cell_value("Wiki Kategorien") or 'k.A.'
else: # Wenn Modus inaktiv, setze leere Daten
wiki_data = {'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.'}
elif process_wiki:
debug_print(f"Zeile {row_num_in_sheet}: Überspringe Wikipedia Verarbeitung (Timestamp AN vorhanden UND S != 'X (URL Copied)').")
# else: Wiki-Verarbeitung nicht aktiv
# --- 3. ChatGPT Evaluationen (Branch etc.) ---
# Trigger: AO fehlt ODER Wiki wurde gerade neu geparsed (um Branch neu zu bewerten)
chat_ts_ao_missing = not get_cell_value("Timestamp letzte Prüfung").strip()
run_chat_eval = process_chatgpt and (chat_ts_ao_missing or reparse_wiki_needed) # reparse_wiki_needed ist True wenn Wiki neu gemacht wurde
# --- 3. ChatGPT Evaluationen ---
chat_ts_needed = process_chatgpt and not get_cell_value("Timestamp letzte Prüfung").strip()
if run_chat_eval:
debug_print(f"Zeile {row_num_in_sheet}: Starte ChatGPT Evaluationen (Grund: AO fehlt? {chat_ts_ao_missing}, Wiki neu geparsed? {reparse_wiki_needed})...")
any_processing_done = True # Markiere, dass etwas getan wurde
if chat_ts_needed:
debug_print(f"Zeile {row_num_in_sheet}: Starte ChatGPT Evaluationen (Timestamp AO fehlt)...")
any_processing_done = True
# 3.1 Branchenevaluierung (Nutzt aktuelle wiki_data und website_summary)
# 3.1 Branchenevaluierung (Nutzt die ggf. gerade aktualisierten wiki_data)
branch_result = evaluate_branche_chatgpt(
crm_branche,
crm_beschreibung,
wiki_data.get('branche', 'k.A.'),
wiki_data.get('categories', 'k.A.'),
website_summary # Nutzt den Wert, der ggf. oben aktualisiert wurde
crm_branche, crm_beschreibung,
wiki_data.get('branche', 'k.A.'), # Nimmt aktuelle wiki_data
wiki_data.get('categories', 'k.A.'),# Nimmt aktuelle wiki_data
website_summary # Nimmt aktuellen website_summary
)
updates.append({'range': f'W{row_num_in_sheet}', 'values': [[branch_result.get('branch', 'Fehler')]]})
updates.append({'range': f'X{row_num_in_sheet}', 'values': [[branch_result.get('consistency', 'Fehler')]]})
updates.append({'range': f'Y{row_num_in_sheet}', 'values': [[branch_result.get('justification', 'Fehler')]]})
# --- HIER weitere ChatGPT-basierte Evaluationen einfügen ---
# Beispiel: FSM-Eignung
# fsm_result = evaluate_fsm_suitability(company_name, wiki_data)
# if fsm_result:
# updates.append({'range': f'Z{row_num_in_sheet}', 'values': [[fsm_result.get('suitability', 'k.A.')]]})
# updates.append({'range': f'AA{row_num_in_sheet}', 'values': [[fsm_result.get('justification', 'k.A.')]]})
# Beispiel: Mitarbeiter-Schätzung etc.
# ...
# --- Hier weitere ChatGPT Evaluationen einfügen ---
# ... (FSM, Mitarbeiter etc.) ...
# Setze Timestamp letzte Prüfung (AO)
updates.append({'range': f'AO{row_num_in_sheet}', 'values': [[now_timestamp]]})
# Version wird am Ende gesetzt
elif process_chatgpt:
debug_print(f"Zeile {row_num_in_sheet}: Überspringe ChatGPT Evaluationen (Timestamp AO vorhanden).")
debug_print(f"Zeile {row_num_in_sheet}: Überspringe ChatGPT Evaluationen (Timestamp AO vorhanden UND Wiki nicht neu geparsed).")
# --- 4. Abschließende Updates ---
# Setze Version, wenn *irgendetwas* in dieser Zeile verarbeitet wurde
if any_processing_done:
updates.append({'range': f'AP{row_num_in_sheet}', 'values': [[Config.VERSION]]})
updates.append({'range': f'AP{row_num_in_sheet}', 'values': [[Config.VERSION]]}) # Setze Version nur wenn etwas getan wurde
# --- 5. Batch Update für diese Zeile durchführen ---
if updates:
# Führe Batch Update über den Handler aus
success = self.sheet_handler.batch_update_cells(updates)
if success:
debug_print(f"Zeile {row_num_in_sheet}: Batch-Update erfolgreich ({len(updates)} Zellen/Bereiche).")
else:
debug_print(f"Zeile {row_num_in_sheet}: FEHLER beim Batch-Update.")
if success: debug_print(f"Zeile {row_num_in_sheet}: Batch-Update erfolgreich ({len(updates)} Zellen/Bereiche).")
else: debug_print(f"Zeile {row_num_in_sheet}: FEHLER beim Batch-Update.")
else:
debug_print(f"Zeile {row_num_in_sheet}: Keine Updates zum Schreiben (alles übersprungen oder keine Änderungen).")
debug_print(f"Zeile {row_num_in_sheet}: Keine Updates zum Schreiben.")
debug_print(f"--- Verarbeitung für Zeile {row_num_in_sheet} abgeschlossen ---")
# Kurze Pause (optional, aber gut für APIs)
time.sleep(max(0.2, Config.RETRY_DELAY / 10))
time.sleep(max(0.1, Config.RETRY_DELAY / 20)) # Kürzere Pause, da Haupt-API Calls schon Pausen haben
def process_rows_sequentially(self, start_row_index, num_rows_to_process, process_wiki=True, process_chatgpt=True, process_website=True):