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 # @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): 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. 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.
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.
""" """
debug_print(f"--- Starte Verarbeitung für Zeile {row_num_in_sheet} ---") 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") 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 für sicheren Zugriff
# Hilfsfunktion, um sicher auf Index zuzugreifen def get_cell_value(key):
def get_cell_value(col_key): idx = COLUMN_MAP.get(key)
idx = COLUMN_MAP.get(col_key) if idx is not None and len(row_data) > idx: return row_data[idx]
if idx is not None and len(row_data) > idx: return ""
return row_data[idx]
return "" # Oder None oder einen anderen Standardwert
company_name = get_cell_value("CRM Name") company_name = get_cell_value("CRM Name")
website_url = get_cell_value("CRM Website") 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_branche = get_cell_value("CRM Branche")
crm_beschreibung = get_cell_value("CRM Beschreibung") 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_raw = get_cell_value("Website Rohtext") or "k.A."
website_summary = get_cell_value("Website Zusammenfassung") 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) --- # --- 1. Website Handling (wie zuletzt -> prüft AT) ---
# Prüfe Timestamp AT (Index 45)
website_ts_needed = process_website and not get_cell_value("Website Scrape Timestamp").strip() website_ts_needed = process_website and not get_cell_value("Website Scrape Timestamp").strip()
if website_ts_needed: if website_ts_needed:
debug_print(f"Zeile {row_num_in_sheet}: Starte Website Verarbeitung (Timestamp AT fehlt)...") debug_print(f"Zeile {row_num_in_sheet}: Starte Website Verarbeitung (Timestamp AT fehlt)...")
any_processing_done = True any_processing_done = True
# --- Lookup ---
# Website Lookup, wenn leer
if not website_url or website_url.strip().lower() == "k.a.": 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) new_website = serp_website_lookup(company_name)
if new_website != "k.A.": if new_website != "k.A.": website_url = new_website; debug_print(f"Zeile {row_num_in_sheet}: SERP Lookup: {website_url}");
website_url = new_website # Aktualisiere URL für weitere Schritte if website_url != original_website: updates.append({'range': f'D{row_num_in_sheet}', 'values': [[website_url]]})
debug_print(f"Zeile {row_num_in_sheet}: SERP Lookup erfolgreich: {website_url}") else: debug_print(f"Zeile {row_num_in_sheet}: SERP Lookup erfolglos.")
if website_url != original_website: # --- Scraping ---
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
if website_url and website_url.strip().lower() != "k.a.": 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_raw = get_website_raw(website_url)
new_website_summary = summarize_website_content(new_website_raw) new_website_summary = summarize_website_content(new_website_raw)
if new_website_raw != website_raw: updates.append({'range': f'AR{row_num_in_sheet}', 'values': [[new_website_raw]]}); website_raw = new_website_raw
# Füge Updates nur hinzu, wenn sich etwas geändert hat oder vorher k.A. war if new_website_summary != website_summary: updates.append({'range': f'AS{row_num_in_sheet}', 'values': [[new_website_summary]]}); website_summary = new_website_summary
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
else: 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_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.']]}) 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 website_raw, website_summary = "k.A.", "k.A."
# Setze Website Timestamp (AT)
updates.append({'range': f'AT{row_num_in_sheet}', 'values': [[now_timestamp]]}) updates.append({'range': f'AT{row_num_in_sheet}', 'values': [[now_timestamp]]})
# Version wird am Ende gesetzt
elif process_website: elif process_website:
debug_print(f"Zeile {row_num_in_sheet}: Überspringe Website Verarbeitung (Timestamp AT vorhanden).") 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 --- if reparse_wiki_needed:
wiki_data = {} # Wird gefüllt, entweder durch Scraping oder aus Zeile gelesen 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})...")
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)...")
any_processing_done = True any_processing_done = True
new_wiki_data_extracted = None # Wird mit den neuen Daten gefüllt
# Logik für Suche und Extraktion # --- 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:
# --- 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 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 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: if valid_crm_wiki_url:
debug_print(f"Zeile {row_num_in_sheet}: Prüfe CRM Wiki Vorschlag: {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]) 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) if page and self.wiki_scraper._validate_article(page, company_name, current_website_for_validation): article_page = page
current_website_for_validation = website_url if website_url and website_url != 'k.A.' else original_website 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)
if page and self.wiki_scraper._validate_article(page, company_name, current_website_for_validation):
article_page = page
else: else:
debug_print(f"Zeile {row_num_in_sheet}: CRM Wiki Vorschlag nicht validiert. Starte Suche...") debug_print(f" -> Kein CRM Vorschlag L. Starte Suche...")
article_page = self.wiki_scraper.search_company_article(company_name, current_website_for_validation)
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) article_page = self.wiki_scraper.search_company_article(company_name, current_website_for_validation)
if article_page: if article_page:
debug_print(f"Zeile {row_num_in_sheet}: Extrahiere Daten aus Artikel: {article_page.url}") debug_print(f" -> Artikel gefunden durch Suche: {article_page.url}")
wiki_data = self.wiki_scraper.extract_company_data(article_page.url) new_wiki_data_extracted = self.wiki_scraper.extract_company_data(article_page.url)
else: else:
debug_print(f"Zeile {row_num_in_sheet}: Kein passender Wikipedia Artikel gefunden.") debug_print(f" -> Kein passender Wikipedia Artikel durch Suche gefunden.")
# Setze Standard-k.A.-Werte für wiki_data new_wiki_data_extracted = {'url': 'Kein Artikel gefunden', 'first_paragraph': 'k.A.', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.'}
wiki_data = {'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 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'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'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'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'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'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'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]]}) # Setze AN Timestamp neu
updates.append({'range': f'AN{row_num_in_sheet}', 'values': [[now_timestamp]]}) # Wenn der Trigger "X (URL Copied)" war, setze S zurück
# Version wird am Ende gesetzt 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 elif process_wiki:
debug_print(f"Zeile {row_num_in_sheet}: Überspringe Wikipedia Verarbeitung (Timestamp AN vorhanden).") debug_print(f"Zeile {row_num_in_sheet}: Überspringe Wikipedia Verarbeitung (Timestamp AN vorhanden UND S != 'X (URL Copied)').")
# Lade vorhandene Wiki-Daten aus der Zeile, um sie für ChatGPT verfügbar zu machen # else: Wiki-Verarbeitung nicht aktiv
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.'}
# --- 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 --- if run_chat_eval:
chat_ts_needed = process_chatgpt and not get_cell_value("Timestamp letzte Prüfung").strip() 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: # 3.1 Branchenevaluierung (Nutzt die ggf. gerade aktualisierten wiki_data)
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)
branch_result = evaluate_branche_chatgpt( branch_result = evaluate_branche_chatgpt(
crm_branche, crm_branche, crm_beschreibung,
crm_beschreibung, wiki_data.get('branche', 'k.A.'), # Nimmt aktuelle wiki_data
wiki_data.get('branche', 'k.A.'), wiki_data.get('categories', 'k.A.'),# Nimmt aktuelle wiki_data
wiki_data.get('categories', 'k.A.'), website_summary # Nimmt aktuellen website_summary
website_summary # Nutzt den Wert, der ggf. oben aktualisiert wurde
) )
updates.append({'range': f'W{row_num_in_sheet}', 'values': [[branch_result.get('branch', 'Fehler')]]}) 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'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')]]}) updates.append({'range': f'Y{row_num_in_sheet}', 'values': [[branch_result.get('justification', 'Fehler')]]})
# --- HIER weitere ChatGPT-basierte Evaluationen einfügen --- # --- Hier weitere ChatGPT Evaluationen einfügen ---
# Beispiel: FSM-Eignung # ... (FSM, Mitarbeiter etc.) ...
# 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.
# ...
# Setze Timestamp letzte Prüfung (AO) # Setze Timestamp letzte Prüfung (AO)
updates.append({'range': f'AO{row_num_in_sheet}', 'values': [[now_timestamp]]}) updates.append({'range': f'AO{row_num_in_sheet}', 'values': [[now_timestamp]]})
# Version wird am Ende gesetzt
elif process_chatgpt: 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 --- # --- 4. Abschließende Updates ---
# Setze Version, wenn *irgendetwas* in dieser Zeile verarbeitet wurde
if any_processing_done: 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 --- # --- 5. Batch Update für diese Zeile durchführen ---
if updates: if updates:
# Führe Batch Update über den Handler aus
success = self.sheet_handler.batch_update_cells(updates) success = self.sheet_handler.batch_update_cells(updates)
if success: if success: debug_print(f"Zeile {row_num_in_sheet}: Batch-Update erfolgreich ({len(updates)} Zellen/Bereiche).")
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: else:
debug_print(f"Zeile {row_num_in_sheet}: FEHLER beim Batch-Update.") debug_print(f"Zeile {row_num_in_sheet}: Keine Updates zum Schreiben.")
else:
debug_print(f"Zeile {row_num_in_sheet}: Keine Updates zum Schreiben (alles übersprungen oder keine Änderungen).")
debug_print(f"--- Verarbeitung für Zeile {row_num_in_sheet} abgeschlossen ---") debug_print(f"--- Verarbeitung für Zeile {row_num_in_sheet} abgeschlossen ---")
# Kurze Pause (optional, aber gut für APIs) time.sleep(max(0.1, Config.RETRY_DELAY / 20)) # Kürzere Pause, da Haupt-API Calls schon Pausen haben
time.sleep(max(0.2, Config.RETRY_DELAY / 10))
def process_rows_sequentially(self, start_row_index, num_rows_to_process, process_wiki=True, process_chatgpt=True, process_website=True): def process_rows_sequentially(self, start_row_index, num_rows_to_process, process_wiki=True, process_chatgpt=True, process_website=True):