bugfix
This commit is contained in:
@@ -9389,7 +9389,7 @@ class DataProcessor:
|
||||
# Stellen Sie sicher, dass die Header-Zeile auch die erwartete Mindestlaenge hat,
|
||||
# um die Spa
|
||||
|
||||
# ==========================================================================
|
||||
# ==========================================================================
|
||||
# === Utility Methods (Other Specific Tasks) ===============================
|
||||
# ==========================================================================
|
||||
|
||||
@@ -9472,7 +9472,7 @@ class DataProcessor:
|
||||
if details_col_idx is None:
|
||||
# Fallback auf 'Website Rohtext' (AR)
|
||||
details_col_idx = COLUMN_MAP.get("Website Rohtext") # Block 1 Column Map
|
||||
details_col_key_for_logging = "Website Rohtext" # Name fuer Logging
|
||||
details_col_key_for_logging = "Website Rohtext"
|
||||
# Pruefen Sie, ob der Fallback-Schluessel gefunden wurde
|
||||
if details_col_idx is None:
|
||||
self.logger.critical("FEHLER: Weder 'Website Details' noch 'Website Rohtext' Spaltenindex in COLUMN_MAP gefunden.")
|
||||
@@ -9545,7 +9545,7 @@ class DataProcessor:
|
||||
log_check = (i < start_sheet_row + 5) or (i % 100 == 0) or (processing_needed_for_row)
|
||||
if log_check:
|
||||
company_name = self._get_cell_value_safe(row, "CRM Name").strip() # Block 1 Column Map
|
||||
self.logger.debug(f"Zeile {i} ({company_name[:50]}... Website Details Check): A='x'? {is_marked_for_processing}, D gueltig? {website_url_is_valid_looking}. Benötigt Verarbeitung? {processing_needed_for_row}") # Gekuerzt loggen
|
||||
self.logger.debug(f"Zeile {i} ({company_name[:50]}... Website Details Check): A='x'? {is_marked_for_processing}, D gueltig? {website_url_is_valid_looking}. Benoetigt Verarbeitung? {processing_needed_for_row}") # Gekuerzt loggen
|
||||
|
||||
|
||||
# Wenn die Verarbeitung fuer diese Zeile nicht noetig ist (trotz 'x' fehlte die URL)
|
||||
@@ -9566,10 +9566,10 @@ class DataProcessor:
|
||||
break # Brich die Schleife ab
|
||||
|
||||
|
||||
self.logger.info(f"Zeile {i}: Extrahiere Website Details von {website_url[:100]}...") # Logge Start (gekuerzt)
|
||||
selflogger.info(f"Zeile {i}: Extrahiere Website Details von {website_url[:100]}...") # Logge Start (gekuerzt)
|
||||
|
||||
|
||||
details = "FEHLER: Funktion 'scrape_website_details' nicht verfuegbar" # Default Fehler, falls die Funktion nicht existiert (Sollte nicht passieren, wenn Sektion 3 korrekt ist)
|
||||
details = "FEHLER: Funktion 'scrape_website_details' nicht verfuegbar" # Default Fehler, falls die Funktion nicht existiert (Sollte nicht passieren, wenn Block 13 korrekt ist)
|
||||
|
||||
try:
|
||||
# Rufe die globale Funktion scrape_website_details auf (Block 13).
|
||||
@@ -9589,8 +9589,7 @@ class DataProcessor:
|
||||
|
||||
|
||||
except NameError:
|
||||
# Dieser Fehler sollte nicht auftreten, wenn scrape_website_details in Sektion 3/7 ist.
|
||||
# Wenn doch, deutet es auf ein schwerwiegendes Problem bei der Code-Organisation hin.
|
||||
# Dieser Fehler sollte nicht auftreten, wenn scrape_website_details in Block 13 ist.
|
||||
self.logger.critical("FEHLER: Funktion 'scrape_website_details' ist nicht definiert! Kann Details nicht extrahieren.")
|
||||
# Logge den Traceback.
|
||||
self.logger.debug(traceback.format_exc())
|
||||
@@ -9598,7 +9597,7 @@ class DataProcessor:
|
||||
|
||||
except Exception as e_detail:
|
||||
# Fange andere unerwartete Fehler ab, die nicht von scrape_website_details behandelt wurden.
|
||||
self.logger.exception(f"Unerwarteter Fehler bei scrape_website_details fuer {website_url[:100]}...: {e_detail}") # Logge Fehler (gekuerzt) und Traceback
|
||||
self.logger.exception(f"Unerwarteter Fehler bei scrape_website_details fuer {website_url[:100]}...: {type(e_detail).__name__} - {e_detail}") # Logge Fehler (gekuerzt) und Traceback
|
||||
details = f"k.A. (Unerwarteter Fehler: {str(e_detail)[:100]}...)" # Signalisiert Fehler (gekuerzt)
|
||||
|
||||
|
||||
@@ -9628,6 +9627,7 @@ class DataProcessor:
|
||||
# Leere die gesammelten Updates nach dem Senden.
|
||||
all_sheet_updates = []
|
||||
|
||||
|
||||
# Kleine Pause nach jeder Extraktion (nutzt Config Block 1).
|
||||
# Dieser Modus macht API calls (ueber scrape_website_details und dessen Helfer), also Pause einbauen.
|
||||
pause_duration = getattr(Config, 'RETRY_DELAY', 5) * 0.2
|
||||
@@ -9651,6 +9651,512 @@ class DataProcessor:
|
||||
# Keine Pause nach diesem Modus noetig, da die naechste Aktion im Dispatcher (Block 34) folgt.
|
||||
|
||||
|
||||
# --- Methode zum Verarbeiten von Wiki-Updates basierend auf ChatGPT Vorschlaegen ---
|
||||
# Diese Methode verarbeitet Zeilen, in denen S gesetzt ist (nicht in Endzustand),
|
||||
# prueft ob U eine valide und andere Wiki-URL ist und fuehrt entsprechende Updates durch.
|
||||
# Basierend auf process_wiki_updates_from_chatgpt aus Teil 4.
|
||||
# Nutzt interne Helfer: _get_cell_value_safe, _get_col_letter.
|
||||
# Nutzt globale Helfer: COLUMN_MAP (Block 1), logger, Config (Block 1), time,
|
||||
# is_valid_wikipedia_article_url (Block 12).
|
||||
# Nutzt die uebergeordnete sheet_handler Instanz (Block 14).
|
||||
def process_wiki_updates_from_chatgpt(self, start_sheet_row=None, end_sheet_row=None, limit=None):
|
||||
"""
|
||||
Identifiziert Zeilen, in denen Status S gesetzt ist, aber NICHT auf einem Endzustand
|
||||
(OK, X (UPDATED/COPIED/INVALID)), prueft ob U eine *valide* und *andere* Wiki-URL ist.
|
||||
- Wenn ja: Kopiert U->M, markiert S='X (URL Copied)', U='URL uebernommen', loescht
|
||||
abhaengige Wiki-Spalten (N-V, AN, AO, AP, AX), setzt ReEval-Flag A='x'.
|
||||
- Wenn nein (U keine URL, U==M, oder U ungueltig): LOESCHT den Inhalt von U und
|
||||
markiert S als 'X (Invalid Suggestion)'.
|
||||
Verarbeitet maximal limit Zeilen.
|
||||
|
||||
Args:
|
||||
start_sheet_row (int, optional): Die 1-basierte Startzeile im Sheet. Defaults to None (ab Zeile 7).
|
||||
end_sheet_row (int, optional): Die 1-basierte Endzeile im Sheet. Defaults to None (bis Ende Sheet).
|
||||
limit (int, optional): Maximale Anzahl ZU PRUEFENDER Zeilen. Defaults to None (Unbegrenzt).
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
# Logge die Konfiguration des Modus
|
||||
self.logger.info(f"Starte Modus 'wiki_updates_from_chatgpt' (S, U, M, N-V, AN, AO, AX, AP, A). Bereich: {start_sheet_row if start_sheet_row is not None else 'Start'}-{end_sheet_row if end_sheet_row is not None else 'Ende'}, Limit: {limit if limit is not None else 'Unbegrenzt'}...")
|
||||
|
||||
|
||||
# --- Daten laden ---
|
||||
# Laden Sie Daten neu. Kein automatischer Startindex-Check noetig hier,
|
||||
# da wir nach Status S suchen.
|
||||
# Der load_data Aufruf ist mit retry_on_failure dekoriert (Block 2).
|
||||
if not self.sheet_handler.load_data():
|
||||
self.logger.error("Fehler beim Laden der Daten fuer Wiki Updates.")
|
||||
return # Beende die Methode, wenn das Laden fehlschlaegt
|
||||
|
||||
|
||||
# Holen Sie die gesamte Datenliste (inklusive Header) aus dem SheetHandler.
|
||||
all_data = self.sheet_handler.get_all_data_with_headers()
|
||||
# Annahme: header_rows ist als Attribut im SheetHandler verfuegbar (Block 14).
|
||||
header_rows = self.sheet_handler._header_rows
|
||||
total_sheet_rows = len(all_data) # Gesamtzahl der Zeilen im Sheet
|
||||
|
||||
|
||||
# Standard Startzeile, wenn nicht manuell gesetzt
|
||||
if start_sheet_row is None: start_sheet_row = header_rows + 1 # Standardmaessig ab erster Datenzeile (Zeile nach Headern)
|
||||
|
||||
# Berechne Endzeile, wenn nicht manuell gesetzt
|
||||
if end_sheet_row is None: end_sheet_row = total_sheet_rows # Bis zur letzten Zeile
|
||||
|
||||
|
||||
# Logge den Suchbereich fuer Status S
|
||||
self.logger.info(f"Suchbereich fuer Status S: Sheet-Zeilen {start_sheet_row} bis {end_sheet_row}. Gesamtzeilen im Sheet: {total_sheet_rows}")
|
||||
|
||||
# Pruefe, ob der Bereich gueltig ist
|
||||
if start_sheet_row > end_sheet_row or start_sheet_row > total_sheet_rows:
|
||||
self.logger.info("Berechneter Start liegt nach dem Ende des Bereichs oder Sheets. Keine Zeilen zu verarbeiten.")
|
||||
return # Beende die Methode, wenn der Bereich leer ist
|
||||
|
||||
|
||||
# --- Indizes und Buchstaben ---
|
||||
# Stellen Sie sicher, dass alle benoetigten Spalten in COLUMN_MAP (Block 1) vorhanden sind
|
||||
required_keys = [
|
||||
"Chat Wiki Konsistenzpruefung", "Chat Vorschlag Wiki Artikel", "Wiki URL", # S, U, M (Pruefkriterien / Daten)
|
||||
"Wikipedia Timestamp", "Wiki Verif. Timestamp", "Timestamp letzte Pruefung", "Version", # AN, AX, AO, AP (Spalten zum Loeschen)
|
||||
"ReEval Flag", # A (ReEval Flag setzen)
|
||||
"Wiki Absatz", "Wiki Branche", "Wiki Umsatz", "Wiki Mitarbeiter", "Wiki Kategorien", # N-R (Spalten zum Loeschen)
|
||||
"Chat Begruendung Wiki Inkonsistenz", "Begruendung bei Abweichung", # T, V (Spalten zum Loeschen)
|
||||
# AY (SerpAPI Wiki Search Timestamp) wird ebenfalls geleert, da abhaengig von M.
|
||||
"SerpAPI Wiki Search Timestamp" # AY (Spalte zum Leeren)
|
||||
]
|
||||
# Erstellen Sie ein Dictionary mit Schluesseln und Indizes
|
||||
col_indices = {key: COLUMN_MAP.get(key) for key in required_keys}
|
||||
|
||||
# Pruefen Sie, ob alle benoetigten Schluessel in COLUMN_MAP gefunden wurden
|
||||
if None in col_indices.values():
|
||||
missing = [k for k, v in col_indices.items() if v is None]
|
||||
self.logger.critical(f"FEHLER: Benoetigte Spaltenschluessel fehlen in COLUMN_MAP fuer process_wiki_updates_from_chatgpt: {missing}. Breche ab.")
|
||||
return # Beende die Methode bei kritischem Fehler
|
||||
|
||||
|
||||
# Ermitteln Sie die Spaltenbuchstaben fuer Updates/Leerung (nutzt interne Helfer _get_col_letter Block 14)
|
||||
s_letter = self.sheet_handler._get_col_letter(col_indices["Chat Wiki Konsistenzpruefung"] + 1) # Status S
|
||||
u_letter = self.sheet_handler._get_col_letter(col_indices["Chat Vorschlag Wiki Artikel"] + 1) # Vorschlag U
|
||||
m_letter = self.sheet_handler._get_col_letter(col_indices["Wiki URL"] + 1) # Wiki URL M
|
||||
a_letter = self.sheet_handler._get_col_letter(col_indices["ReEval Flag"] + 1) # ReEval Flag A
|
||||
|
||||
# Spalten N-V leeren.
|
||||
# N ist Wiki Absatz, V ist Begruendung bei Abweichung.
|
||||
n_idx = col_indices["Wiki Absatz"]
|
||||
v_idx = col_indices["Begruendung bei Abweichung"]
|
||||
# Erstellen Sie den Bereichsnamen (z.B. "N:V")
|
||||
n_letter = self.sheet_handler._get_col_letter(n_idx + 1)
|
||||
v_letter = self.sheet_handler._get_col_letter(v_idx + 1)
|
||||
nv_range_letter = f'{n_letter}:{v_letter}' # z.B. N:V
|
||||
# Erstellen Sie eine Liste von leeren Strings fuer diesen Bereich
|
||||
empty_nv_values = [''] * (v_idx - n_idx + 1) # Anzahl der Spalten = V_Index - N_Index + 1
|
||||
|
||||
|
||||
# Timestamps AN, AO, AP, AX, AY leeren.
|
||||
# Diese werden von anderen Schritten gesetzt und sollen hier zurueckgesetzt werden.
|
||||
an_letter = self.sheet_handler._get_col_letter(col_indices["Wikipedia Timestamp"] + 1) # AN (Wiki Extraction TS)
|
||||
ao_letter = self.sheet_handler._get_col_letter(col_indices["Timestamp letzte Pruefung"] + 1) # AO (Chat Evaluation TS)
|
||||
ap_letter = self.sheet_handler._get_col_letter(col_indices["Version"] + 1) # AP (Version)
|
||||
ax_letter = self.sheet_handler._get_col_letter(col_indices["Wiki Verif. Timestamp"] + 1) # AX (Wiki Verif. TS)
|
||||
ay_letter = self.sheet_handler._get_col_letter(col_indices["SerpAPI Wiki Search Timestamp"] + 1) # AY (SerpAPI Wiki TS)
|
||||
|
||||
|
||||
# --- Verarbeitung ---
|
||||
# Holen Sie die Batch-Groesse fuer Sheet-Updates aus Config (Block 1).
|
||||
update_batch_row_limit = getattr(Config, 'UPDATE_BATCH_ROW_LIMIT', 50)
|
||||
|
||||
|
||||
all_sheet_updates = [] # Gesammelte Updates fuer Batch-Schreiben ins Sheet (Liste von Dicts)
|
||||
|
||||
|
||||
processed_rows_count = 0 # Zaehlt Zeilen, die geprueft werden (im Rahmen des Limits zaehlen).
|
||||
skipped_count = 0 # Zaehlt Zeilen, die uebersprungen werden (Status S im Endzustand etc.).
|
||||
updated_url_count = 0 # Zaehlt Zeilen, wo U -> M kopiert wurde.
|
||||
cleared_suggestion_count = 0 # Zaehlt Zeilen, wo Vorschlag U geloescht wurde.
|
||||
|
||||
|
||||
# Iteriere durch die Datenzeilen im definierten Bereich (1-basierte Sheet-Zeilennummer)
|
||||
for i in range(start_sheet_row, end_sheet_row + 1):
|
||||
row_index_in_list = i - 1 # 0-basierter Index in der all_data Liste
|
||||
# Pruefen Sie, ob das Ende des Sheets erreicht wurde
|
||||
if row_index_in_list >= total_sheet_rows: break # Ende des Sheets erreicht
|
||||
|
||||
|
||||
row = all_data[row_index_in_list] # Die Rohdaten fuer diese Zeile
|
||||
|
||||
|
||||
# Stellen Sie sicher, dass die Zeile nicht leer ist
|
||||
if not any(cell and isinstance(cell, str) and cell.strip() for cell in row):
|
||||
#self.logger.debug(f"Zeile {i}: Uebersprungen (Leere Zeile).") # Zu viel Laerm im Debug
|
||||
skipped_count += 1 # Zaehlen als uebersprungene Zeile
|
||||
continue # Springe zur naechsten Zeile
|
||||
|
||||
|
||||
# --- Pruefung, ob Verarbeitung fuer diese Zeile noetig ist ---
|
||||
# Kriterium: Status S ist gesetzt (nicht leer) UND NICHT einer der Endzustaende.
|
||||
# Endzustaende: "OK", "X (UPDATED)", "X (URL COPIED)", "X (INVALID SUGGESTION)"
|
||||
|
||||
# Holen Sie den Wert aus Spalte S (Chat Wiki Konsistenzpruefung) (nutzt interne Helfer _get_cell_value_safe)
|
||||
s_value = self._get_cell_value_safe(row, "Chat Wiki Konsistenzpruefung").strip() # Block 1 Column Map
|
||||
s_value_upper = s_value.upper()
|
||||
|
||||
# Definieren Sie die Endzustaende (Grossbuchstaben)
|
||||
s_end_states = ["OK", "X (UPDATED)", "X (URL COPIED)", "X (INVALID SUGGESTION)"]
|
||||
|
||||
# Verarbeitung ist noetig, wenn S nicht leer ist UND S NICHT im Endzustand ist.
|
||||
processing_needed_for_row = s_value and s_value_upper not in s_end_states
|
||||
|
||||
|
||||
# Loggen der Pruefergebnisse fuer diese Zeile auf Debug-Level
|
||||
log_check = (i < start_sheet_row + 5) or (i % 100 == 0) or (processing_needed_for_row)
|
||||
if log_check:
|
||||
self.logger.debug(f"Zeile {i} (Wiki Update Check): Status S='{s_value}'. Benoetigt Verarbeitung? {processing_needed_for_row}")
|
||||
|
||||
|
||||
# Wenn die Verarbeitung fuer diese Zeile nicht noetig ist
|
||||
if not processing_needed_for_row:
|
||||
skipped_count += 1 # Zaehlen als uebersprungene Zeile
|
||||
continue # Springe zur naechsten Zeile
|
||||
|
||||
|
||||
# --- Wenn Verarbeitung noetig: Pruefe Vorschlag U und handle ---
|
||||
processed_rows_count += 1 # Zaehle die Zeile, die geprueft wird (im Rahmen des Limits zaehlen).
|
||||
|
||||
# Pruefe das Limit fuer verarbeitete Zeilen
|
||||
if limit is not None and isinstance(limit, int) and limit > 0 and processed_rows_count > limit:
|
||||
# Wenn das Limit erreicht ist und es ein positives Limit gibt
|
||||
self.logger.info(f"Verarbeitungslimit ({limit}) fuer process_wiki_updates_from_chatgpt erreicht. Breche weitere Zeilenpruefung ab.")
|
||||
break # Brich die Schleife ab
|
||||
|
||||
|
||||
# Holen Sie die Werte aus Spalte U (Chat Vorschlag Wiki Artikel) und M (Wiki URL) (nutzt interne Helfer _get_cell_value_safe)
|
||||
vorschlag_u = self._get_cell_value_safe(row, "Chat Vorschlag Wiki Artikel").strip() # Block 1 Column Map
|
||||
url_m = self._get_cell_value_safe(row, "Wiki URL").strip() # Block 1 Column Map
|
||||
|
||||
|
||||
self.logger.info(f"Zeile {i}: Pruefe Wiki-Vorschlag U='{vorschlag_u[:100]}...' (aktuell M='{url_m[:100]}...')...") # Gekuerzt loggen
|
||||
|
||||
is_update_candidate = False # Flag, ob U eine gueltige, neue URL ist, die uebernommen werden soll.
|
||||
new_url = "" # Die URL, die ggf. in M kopiert wird.
|
||||
|
||||
|
||||
# Kriterium 1: Ist Vorschlag U ueberhaupt ein String und sieht nach Wikipedia aus?
|
||||
condition1_u_is_wiki_url = vorschlag_u and isinstance(vorschlag_u, str) and "wikipedia.org/wiki/" in vorschlag_u.lower() and vorschlag_u.lower().startswith(("http://", "https://")) # Check auf Schema hinzugefuegt
|
||||
|
||||
|
||||
# Wenn der Vorschlag U wie eine Wikipedia-URL aussieht
|
||||
if condition1_u_is_wiki_url:
|
||||
new_url = vorschlag_u # Nehme den Vorschlag als potenzielle neue URL
|
||||
# Kriterium 2: Unterscheidet sich der Vorschlag U von der aktuellen URL in M?
|
||||
# Pruefe, ob die neue URL nicht identisch mit der aktuellen M-URL ist.
|
||||
condition2_u_differs_m = new_url != url_m
|
||||
|
||||
# Wenn sich der Vorschlag U von der aktuellen M-URL unterscheidet
|
||||
if condition2_u_differs_m:
|
||||
self.logger.debug(f" -> Vorschlag U ({new_url[:100]}...) unterscheidet sich von M ({url_m[:100]}). Pruefe Validitaet...") # Gekuerzt loggen
|
||||
# Kriterium 3: Ist die vorgeschlagene URL ein valider Wikipedia-Artikel (nicht Weiterleitung, Begriffsklaerung, Fehler)?
|
||||
# Nutzt globale Funktion is_valid_wikipedia_article_url (Block 12) mit Retry Decorator (Block 2).
|
||||
# is_valid_wikipedia_article_url wirft Exception bei endgueltigem Fehler.
|
||||
try:
|
||||
condition3_u_is_valid = is_valid_wikipedia_article_url(new_url) # Nutzt globalen Helfer (Block 12)
|
||||
# Wenn die vorgeschlagene URL ein valider Artikel ist
|
||||
if condition3_u_is_valid:
|
||||
is_update_candidate = True # Alle Kriterien erfuellt! Der Vorschlag kann uebernommen werden.
|
||||
self.logger.debug(f" -> URL '{new_url[:100]}...' ist ein VALIDER Artikel laut API Check.") # Gekuerzt loggen
|
||||
else:
|
||||
# Wenn die vorgeschlagene URL nicht valide ist
|
||||
self.logger.debug(f" -> URL '{new_url[:100]}...' ist KEIN valider Artikel laut API Check.") # Gekuerzt loggen
|
||||
|
||||
except Exception as e_validity_check:
|
||||
# Wenn is_valid_wikipedia_article_url eine Exception wirft (nach Retries)
|
||||
# Der Fehler wird bereits vom retry_on_failure Decorator geloggt.
|
||||
self.logger.error(f"FEHLER bei Validitaetspruefung von Vorschlag U '{new_url[:100]}...': {e_validity_check}") # Gekuerzt loggen
|
||||
# Bei Fehler bleibt is_update_candidate False.
|
||||
pass # Faert fort
|
||||
|
||||
|
||||
else:
|
||||
# Wenn der Vorschlag U identisch mit der aktuellen M-URL ist
|
||||
self.logger.debug(f" -> Vorschlag U ist identisch mit URL M. Wird nicht uebernommen.")
|
||||
|
||||
else:
|
||||
# Wenn der Vorschlag U nicht wie eine Wikipedia-URL aussieht
|
||||
self.logger.debug(f" -> Vorschlag U ('{vorschlag_u[:100]}...') ist keine Wikipedia URL. Wird nicht uebernommen.") # Gekuerzt loggen
|
||||
|
||||
|
||||
# --- Verarbeitung des Kandidaten ODER Loeschen des ungueltigen Vorschlags ---
|
||||
updates_for_row = [] # Lokale Liste fuer Updates DIESER Zeile
|
||||
|
||||
if is_update_candidate:
|
||||
# Fall 1: Gueltiges Update durchfuehren (Vorschlag U wird in M kopiert)
|
||||
self.logger.info(f"Zeile {i}: Update-Kandidat VALIDIERUNG ERFOLGREICH. Kopiere U->M, setze ReEval-Flag 'x', loesche abhaengige Spalten.")
|
||||
updated_url_count += 1 # Zaehle die uebernommene URL
|
||||
|
||||
# Updates sammeln (M, S, U, N-V, AN, AO, AP, AX, AY, A) (nutzt interne Helfer _get_col_letter Block 14)
|
||||
updates_for_row.append({'range': f'{m_letter}{i}', 'values': [[new_url]]}) # Setze die neue URL in Spalte M (Block 1 Column Map)
|
||||
updates_for_row.append({'range': f'{s_letter}{i}', 'values': [["X (URL Copied)"]]}) # Setze Status S auf "X (URL Copied)" (Block 1 Column Map)
|
||||
updates_for_row.append({'range': f'{u_letter}{i}', 'values': [["URL uebernommen"]]}) # Schreibe Info in Spalte U (Block 1 Column Map)
|
||||
updates_for_row.append({'range': f'{a_letter}{i}', 'values': [["x"]]}) # Setze ReEval Flag (A) auf 'x' (Block 1 Column Map)
|
||||
|
||||
# Leere Spalten N-V.
|
||||
# Fuege das Update zum Leeren des Bereichs V-Y hinzu, falls der Bereichsname ermittelt werden konnte.
|
||||
if nv_range_letter: # Pruefe, ob der Bereichsname ermittelt werden konnte.
|
||||
updates_for_row.append({'range': f'{n_letter}{i}:{v_letter}{i}', 'values': [empty_nv_values]}) # Block 1 Column Map, lokale Variable
|
||||
else:
|
||||
self.logger.warning(f"Konnte Spaltenbereich N-V ({n_letter}:{v_letter}) fuer Leerung nicht ermitteln fuer Zeile {i}. Leerung uebersprungen.")
|
||||
|
||||
|
||||
# Leere Timestamps AN, AO, AP, AX, AY.
|
||||
# Dies setzt die Zeile zurueck, damit andere Schritte sie spaeter bearbeiten.
|
||||
updates_for_row.append({'range': f'{an_letter}{i}', 'values': [['']]}) # AN (Wiki Extraction TS) Block 1 Column Map
|
||||
updates_for_row.append({'range': f'{ao_letter}{i}', 'values': [['']]}) # AO (Chat Evaluation TS) Block 1 Column Map
|
||||
updates_for_row.append({'range': f'{ap_letter}{i}', 'values': [['']]}) # AP (Version) Block 1 Column Map
|
||||
updates_for_row.append({'range': f'{ax_letter}{i}', 'values': [['']]}) # AX (Wiki Verif. TS) Block 1 Column Map
|
||||
updates_for_row.append({'range': f'{ay_letter}{i}', 'values': [['']]}) # AY (SerpAPI Wiki TS) Block 1 Column Map
|
||||
|
||||
|
||||
else:
|
||||
# Fall 2: Ungueltigen Vorschlag loeschen/markieren
|
||||
# Wenn der Vorschlag U nicht uebernommen wird (weil ungueltig oder identisch mit M).
|
||||
self.logger.info(f"Zeile {i}: Vorschlag U ('{vorschlag_u[:100]}...') ist ungueltig/identisch. Loesche U und setze Status S auf 'X (Invalid Suggestion)'.") # Gekuerzt loggen
|
||||
cleared_suggestion_count += 1 # Zaehle den bereinigten Vorschlag
|
||||
|
||||
# Updates sammeln (S, U) (nutzt interne Helfer _get_col_letter Block 14)
|
||||
updates_for_row.append({'range': f'{s_letter}{i}', 'values': [["X (Invalid Suggestion)"]]}) # Setze Status S auf "X (Invalid Suggestion)" (Block 1 Column Map)
|
||||
updates_for_row.append({'range': f'{u_letter}{i}', 'values': [[""]]}) # Loesche den Vorschlag in Spalte U (Block 1 Column Map)
|
||||
# KEIN ReEval-Flag (A) setzen in diesem Fall.
|
||||
|
||||
|
||||
# Sammle die Updates fuer diese Zeile in der globalen Liste all_sheet_updates.
|
||||
all_sheet_updates.extend(updates_for_row)
|
||||
|
||||
|
||||
# Sende gesammelte Sheet Updates wenn das Update-Batch-Limit erreicht ist.
|
||||
# update_batch_row_limit wird aus Config geholt (Block 1).
|
||||
# Die Anzahl der Updates pro Zeile variiert stark (ca. 2 bei ungueltigem Vorschlag, ca. 10+ bei gueltigem).
|
||||
# Pruefen Sie einfach die Laenge der gesammelten Liste.
|
||||
if len(all_sheet_updates) >= update_batch_row_limit * 5: # Grobe Schaetzung: im Schnitt 5 Updates pro Zeile
|
||||
self.logger.debug(f" Sende gesammelte Sheet-Updates ({len(all_sheet_updates)} Zellen)...")
|
||||
# Nutzt die batch_update_cells Methode des Sheet Handlers (Block 14) mit Retry.
|
||||
# Wenn es fehlschlaegt, wird es intern geloggt.
|
||||
success = self.sheet_handler.batch_update_cells(all_sheet_updates)
|
||||
if success:
|
||||
self.logger.info(f" Sheet-Update fuer {len(all_sheet_updates)} Zellen erfolgreich.")
|
||||
# Der Fehlerfall wird von batch_update_cells geloggt
|
||||
|
||||
# Leere die gesammelten Updates nach dem Senden.
|
||||
all_sheet_updates = []
|
||||
|
||||
|
||||
# Kleine Pause nach jeder geprueften Zeile (nutzt Config Block 1).
|
||||
# Dieser Modus macht API calls (ueber is_valid_wikipedia_article_url Block 12), also Pause einbauen.
|
||||
pause_duration = getattr(Config, 'RETRY_DELAY', 5) * 0.2
|
||||
#self.logger.debug(f"Warte {pause_duration:.2f}s nach Pruefung...") # Zu viel Laerm im Debug
|
||||
time.sleep(pause_duration)
|
||||
|
||||
|
||||
# --- Finale Sheet Updates senden ---
|
||||
# Sende alle verbleibenden gesammelten Updates in einem letzten Batch-Update.
|
||||
if all_sheet_updates:
|
||||
self.logger.info(f"Sende FINALE gesammelte Sheet-Updates ({len(all_sheet_updates)} Zellen)...")
|
||||
# Nutzt die batch_update_cells Methode des Sheet Handlers (Block 14) mit Retry.
|
||||
success = self.sheet_handler.batch_update_cells(all_sheet_updates)
|
||||
if success:
|
||||
self.logger.info(f"FINALES Sheet-Update erfolgreich.")
|
||||
# Der Fehlerfall wird von batch_update_cells geloggt
|
||||
|
||||
|
||||
# Logge den Abschluss des Modus
|
||||
self.logger.info(f"Modus 'wiki_updates_from_chatgpt' abgeschlossen. {processed_rows_count} Zeilen geprueft, {updated_url_count} URLs kopiert & fuer ReEval markiert, {cleared_suggestion_count} ungueltige Vorschlaege geloescht/markiert, {skipped_count} Zeilen uebersprungen.")
|
||||
# Keine Pause nach diesem Modus noetig, da die naechste Aktion im Dispatcher (Block 34) folgt.
|
||||
|
||||
|
||||
# --- Methode zur Re-Extraktion von Wiki-Daten bei fehlendem Timestamp AN ---
|
||||
# Diese Methode identifiziert Zeilen mit M gefuellt und AN leer und fuehrt _process_single_row (Block 19) fuer diese aus.
|
||||
# Nutzt interne Helfer: _get_cell_value_safe, _process_single_row.
|
||||
# Nutzt globale Helfer: COLUMN_MAP (Block 1), logger.
|
||||
# Nutzt die uebergeordnete sheet_handler Instanz (Block 14).
|
||||
def process_wiki_reextract_missing_an(self, start_sheet_row=None, end_sheet_row=None, limit=None):
|
||||
"""
|
||||
Identifiziert Zeilen, bei denen eine Wiki URL (M) vorhanden ist, aber der
|
||||
Wikipedia Timestamp (AN) fehlt. Fuehrt _process_single_row fuer diese Zeilen aus,
|
||||
beschraenkt auf den 'wiki'-Schritt und mit force_reeval=True, um die Extraktion
|
||||
erneut zu versuchen.
|
||||
|
||||
Args:
|
||||
start_sheet_row (int, optional): Die 1-basierte Startzeile im Sheet. Defaults to None (automatische Ermittlung basierend auf leeren AN).
|
||||
end_sheet_row (int, optional): Die 1-basierte Endzeile im Sheet. Defaults to None (bis Ende Sheet).
|
||||
limit (int, optional): Maximale Anzahl ZU VERARBEITENDER Zeilen. Defaults to None (Unbegrenzt).
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
# Logge die Konfiguration des Modus
|
||||
self.logger.info(f"Starte Modus 'wiki_reextract_missing_an' (M gefuellt & AN leer). Bereich: {start_sheet_row if start_sheet_row is not None else 'Start'}-{end_sheet_row if end_sheet_row is not None else 'Ende'}, Limit: {limit if limit is not None else 'Unbegrenzt'}...")
|
||||
|
||||
|
||||
# --- Daten laden und Startzeile ermitteln ---
|
||||
# Automatische Ermittlung der Startzeile, wenn nicht manuell gesetzt.
|
||||
# Dieser Modus sucht nach leeren AN mit gefuelltem M. Die automatische Startzeile
|
||||
# basierend auf leeren AN ist ein guter Startpunkt.
|
||||
if start_sheet_row is None:
|
||||
self.logger.info("Automatische Ermittlung der Startzeile basierend auf leeren AN...")
|
||||
# Nutzt get_start_row_index des Sheet Handlers (Block 14). Prueft auf leeren AN (Block 1 Column Map).
|
||||
# Standardmaessig ab Zeile 7
|
||||
start_data_index_no_header = self.sheet_handler.get_start_row_index(check_column_key="Wikipedia Timestamp", min_sheet_row=7)
|
||||
|
||||
# Wenn get_start_row_index -1 zurueckgibt (Fehler)
|
||||
if start_data_index_no_header == -1:
|
||||
self.logger.error("FEHLER bei automatischer Ermittlung der Startzeile. Breche Modus ab.")
|
||||
return # Beende die Methode
|
||||
|
||||
# Berechne die 1-basierte Sheet-Startzeile aus dem 0-basierten Daten-Index
|
||||
start_sheet_row = start_data_index_no_header + self.sheet_handler._header_rows + 1 # Block 14 SheetHandler Attribut
|
||||
self.logger.info(f"Automatisch ermittelte Startzeile (erste leere AN Zelle): {start_sheet_row}")
|
||||
else:
|
||||
# Wenn start_sheet_row manuell gesetzt wurde, laden Sie die Daten trotzdem neu, um aktuell zu sein.
|
||||
# Der load_data Aufruf ist mit retry_on_failure dekoriert (Block 2).
|
||||
if not self.sheet_handler.load_data():
|
||||
self.logger.error("Fehler beim Laden der Daten fuer wiki_reextract_missing_an.")
|
||||
return # Beende die Methode, wenn das Laden fehlschlaegt
|
||||
|
||||
|
||||
# Holen Sie die gesamte Datenliste (inklusive Header) aus dem SheetHandler.
|
||||
all_data = self.sheet_handler.get_all_data_with_headers();
|
||||
# Annahme: header_rows ist als Attribut im SheetHandler verfuegbar (Block 14).
|
||||
header_rows = self.sheet_handler._header_rows;
|
||||
total_sheet_rows = len(all_data) # Gesamtzahl der Zeilen im Sheet
|
||||
|
||||
|
||||
# Berechne Endzeile, wenn nicht manuell gesetzt
|
||||
if end_sheet_row is None:
|
||||
end_sheet_row = total_sheet_rows # Bis zur letzten Zeile
|
||||
|
||||
|
||||
# Logge den verarbeitungsbereich
|
||||
self.logger.info(f"Suchbereich fuer M gefuellt & AN leer: Sheet-Zeilen {start_sheet_row} bis {end_sheet_row}. Gesamtzeilen im Sheet: {total_sheet_rows}")
|
||||
|
||||
# Pruefe, ob der Bereich gueltig ist (Start <= Ende und Start nicht ueber Gesamtzeilen)
|
||||
if start_sheet_row > end_sheet_row or start_sheet_row > total_sheet_rows:
|
||||
self.logger.info("Berechneter Start liegt nach dem Ende des Bereichs oder Sheets. Keine Zeilen zu verarbeiten.")
|
||||
return # Beende die Methode, wenn der Bereich leer ist
|
||||
|
||||
|
||||
# --- Indizes ---
|
||||
# Stellen Sie sicher, dass alle benoetigten Spalten in COLUMN_MAP (Block 1) vorhanden sind
|
||||
required_keys = ["Wiki URL", "Wikipedia Timestamp"] # M, AN (Pruefkriterien)
|
||||
# Erstellen Sie ein Dictionary mit Schluesseln und Indizes
|
||||
col_indices = {key: COLUMN_MAP.get(key) for key in required_keys}
|
||||
|
||||
# Pruefen Sie, ob alle benoetigten Schluessel in COLUMN_MAP gefunden wurden
|
||||
if None in col_indices.values():
|
||||
missing = [k for k, v in col_indices.items() if v is None]
|
||||
self.logger.critical(f"FEHLER: Benoetigte Spaltenschluessel fehlen in COLUMN_MAP fuer wiki_reextract_missing_an: {missing}. Breche ab.")
|
||||
return # Beende die Methode bei kritischem Fehler
|
||||
|
||||
# Ermitteln Sie die Indizes
|
||||
m_col_idx = col_indices["Wiki URL"]
|
||||
an_col_idx = col_indices["Wikipedia Timestamp"]
|
||||
|
||||
|
||||
# --- Verarbeitung ---
|
||||
processed_count = 0 # Zaehlt Zeilen, die an _process_single_row uebergeben wurden (im Rahmen des Limits zaehlen).
|
||||
skipped_count = 0 # Zaehlt Zeilen, die uebersprungen wurden.
|
||||
|
||||
|
||||
# Iteriere durch die Datenzeilen im definierten Bereich (1-basierte Sheet-Zeilennummer)
|
||||
for i in range(start_sheet_row, end_sheet_row + 1):
|
||||
row_index_in_list = i - 1 # 0-basierter Index in der all_data Liste
|
||||
# Pruefen Sie, ob das Ende des Sheets erreicht wurde
|
||||
if row_index_in_list >= total_sheet_rows: break # Ende des Sheets erreicht
|
||||
|
||||
|
||||
row = all_data[row_index_in_list] # Die Rohdaten fuer diese Zeile
|
||||
|
||||
|
||||
# Stellen Sie sicher, dass die Zeile nicht leer ist
|
||||
if not any(cell and isinstance(cell, str) and cell.strip() for cell in row):
|
||||
#self.logger.debug(f"Zeile {i}: Uebersprungen (Leere Zeile).") # Zu viel Laerm im Debug
|
||||
skipped_count += 1 # Zaehlen als uebersprungene Zeile
|
||||
continue # Springe zur naechsten Zeile
|
||||
|
||||
|
||||
# --- Pruefung, ob Verarbeitung fuer diese Zeile noetig ist ---
|
||||
# Kriterium: Wiki URL (M) ist vorhanden und gueltig aussehend.
|
||||
# UND Wikipedia Timestamp (AN) ist leer.
|
||||
|
||||
# Holen Sie die Werte aus den entsprechenden Spalten (nutzt interne Helfer _get_cell_value_safe)
|
||||
m_value = self._get_cell_value_safe(row, "Wiki URL").strip() # Block 1 Column Map
|
||||
an_value = self._get_cell_value_safe(row, "Wikipedia Timestamp").strip() # Block 1 Column Map
|
||||
|
||||
# Pruefen Sie, ob M gefuellt und gueltig aussieht.
|
||||
is_m_valid_looking = m_value and isinstance(m_value, str) and "wikipedia.org/wiki/" in m_value.lower() and m_value.lower() not in ["k.a.", "kein artikel gefunden", "fehler bei suche", "http:"] # Fuege "http:" hinzu basierend auf Log
|
||||
|
||||
# Pruefen Sie, ob AN leer ist.
|
||||
is_an_empty = not an_value
|
||||
|
||||
# Verarbeitung ist noetig, wenn M gueltig aussieht UND AN leer ist.
|
||||
processing_needed_for_row = is_m_valid_looking and is_an_empty
|
||||
|
||||
|
||||
# Loggen der Pruefergebnisse fuer diese Zeile auf Debug-Level
|
||||
log_check = (i < start_sheet_row + 5) or (i % 100 == 0) or (processing_needed_for_row)
|
||||
if log_check:
|
||||
company_name = self._get_cell_value_safe(row, "CRM Name").strip() # Block 1 Column Map
|
||||
self.logger.debug(f"Zeile {i} ({company_name[:50]}... Wiki Re-extract Check): M ('{m_value[:50]}...') gueltig? {is_m_valid_looking}, AN leer? {is_an_empty}. Benoetigt Verarbeitung? {processing_needed_for_row}") # Gekuerzt loggen
|
||||
|
||||
|
||||
# Wenn die Verarbeitung fuer diese Zeile nicht noetig ist
|
||||
if not processing_needed_for_row:
|
||||
skipped_count += 1 # Zaehlen als uebersprungene Zeile
|
||||
continue # Springe zur naechsten Zeile
|
||||
|
||||
|
||||
# --- Wenn Verarbeitung noetig: Rufe _process_single_row auf ---
|
||||
processed_count += 1 # Zaehle die Zeile, die an _process_single_row uebergeben wird (im Rahmen des Limits zaehlen)
|
||||
|
||||
# Pruefe das Limit fuer verarbeitete Zeilen
|
||||
if limit is not None and isinstance(limit, int) and limit > 0 and processed_count > limit:
|
||||
# Wenn das Limit erreicht ist und es ein positives Limit gibt
|
||||
self.logger.info(f"Verarbeitungslimit ({limit}) fuer wiki_reextract_missing_an erreicht. Breche weitere Zeilenpruefung ab.")
|
||||
break # Brich die Schleife ab
|
||||
|
||||
|
||||
self.logger.info(f"Zeile {i}: M gefuellt & AN leer. Versuche Wiki-Re-Extraktion ueber _process_single_row...")
|
||||
|
||||
try:
|
||||
# RUFE _process_single_row AUF (Block 19).
|
||||
# Mit steps_to_run={'wiki'} und force_reeval=True,
|
||||
# damit nur der Wiki-Schritt ausgefuehrt wird und Timestamps ignoriert werden.
|
||||
# Im Re-Extract Modus loeschen wir das 'x'-Flag NICHT automatisch.
|
||||
self._process_single_row(
|
||||
row_num_in_sheet = i,
|
||||
row_data = row, # Uebergibt die aktuellen Rohdaten der Zeile
|
||||
steps_to_run = {'wiki'}, # <<< NUR der Wiki-Schritt soll laufen
|
||||
force_reeval = True, # <<< Erzwingt die Ausfuehrung des 'wiki' Schritts (ignoriert AN, S).
|
||||
clear_x_flag = False # <<< 'x'-Flag wird in diesem Modus NICHT geloescht
|
||||
)
|
||||
# _process_single_row (Block 19) loggt intern den Abschluss und fuehrt das Sheet-Update durch.
|
||||
|
||||
except Exception as e_proc:
|
||||
# Wenn _process_single_row einen Fehler wirft (nachdem interne Retries aufgaben),
|
||||
# fangen wir ihn hier, loggen ihn und fahren mit der naechsten Zeile fort.
|
||||
self.logger.exception(f"FEHLER bei Verarbeitung von Zeile {i} in wiki_reextract_missing_an: {e_proc}")
|
||||
# Hier koennen Sie z.B. einen Fehlerindikator in eine spezielle Spalte im Sheet schreiben lassen.
|
||||
# Dieses Update muesste dann separat oder im naechsten Lauf behandelt werden.
|
||||
|
||||
# _process_single_row beinhaltet bereits eine kleine Pause am Ende.
|
||||
# Hier ist keine zusaetzliche Pause noetig, wenn _process_single_row erfolgreich war.
|
||||
# Wenn _process_single_row eine Exception wirft, kann hier eine kurze Pause sinnvoll sein.
|
||||
# time.sleep(0.1) # Optional: Kurze Pause bei Fehler nach Exception
|
||||
|
||||
|
||||
# Logge den Abschluss des Modus
|
||||
self.logger.info(f"Modus 'wiki_reextract_missing_an' abgeschlossen. {processed_count} Zeilen an _process_single_row uebergeben, {skipped_count} Zeilen uebersprungen.")
|
||||
# Keine Pause nach diesem Modus noetig, da die naechste Aktion im Dispatcher (Block 34) folgt.
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Ende DataProcessor Klasse Utility: Other Specific Tasks Block
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
# --- Methode zum Verarbeiten von Wiki-Updates basierend auf ChatGPT Vorschlaegen ---
|
||||
# Diese Methode verarbeitet Zeilen, in denen S gesetzt ist (nicht in Endzustand),
|
||||
# prueft ob U eine valide und andere Wiki-URL ist und fuehrt entsprechende Updates durch.
|
||||
|
||||
Reference in New Issue
Block a user