diff --git a/brancheneinstufung.py b/brancheneinstufung.py index e01c28bb..1cff150b 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -14,7 +14,7 @@ import csv # ==================== KONFIGURATION ==================== class Config: - VERSION = "v1.3.12" # v1.3.12: Neuer Modus 51 implementiert (nur Verifizierung bis Spalte Y) + VERSION = "v1.3.13" # v1.3.13: Neuer Modus 8 integriert (Batch-Token-Zählung in Spalte AQ) LANG = "de" CREDENTIALS_FILE = "service_account.json" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" @@ -200,7 +200,6 @@ def validate_article_with_chatgpt(crm_data, wiki_data): return "k.A." def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien): - # Dieser Prompt soll den Brancheneinordnungsprozess durchführen. prompt_text = ( "Du bist ein Experte im Field Service Management. Analysiere die folgenden Branchenangaben und ordne das Unternehmen " "einer der gültigen Branchen zu. Nutze ausschließlich die vorhandenen Informationen.\n\n" @@ -374,7 +373,7 @@ def search_linkedin_contact(company_name, website, position_query): except Exception as e: debug_print("Fehler beim Lesen des SerpAPI-Schlüssels: " + str(e)) return None - search_name = company_name # Hier kannst du auch die Kurzform verwenden, falls vorhanden. + search_name = company_name query = f'site:linkedin.com/in "{position_query}" "{search_name}"' debug_print(f"Erstelle LinkedIn-Query: {query}") params = { @@ -451,26 +450,15 @@ def count_linkedin_contacts(company_name, website, position_query): def process_verification_only(): debug_print("Starte Verifizierungs-Modus (Modus 51)...") processor = DataProcessor() - # Wir nutzen als Kriterium, dass in Spalte Y (Begründung Abweichung Branche) noch kein Wert steht. for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2): - if len(row) <= 24 or row[24].strip() == "": # Spalte Y ist Index 24 (0-basiert) + if len(row) <= 25 or row[24].strip() == "": processor._process_verification_row(i, row) debug_print("Verifizierungs-Modus abgeschlossen.") def _process_verification_row(self, row_num, row_data): - # In diesem Modus verarbeiten wir nur bis Spalte Y (Begründung Abweichung Branche) - # Spalte B: Firmenname, Spalte C: Kurzform, Spalte D: Website, Spalte E: Ort, Spalte F: Beschreibung, - # Spalte G: Aktuelle Branche, Spalte H: Beschreibung Branche extern, - # Spalte I: Anzahl Techniker CRM, J: Umsatz CRM, K: Anzahl Mitarbeiter CRM, - # Spalte L: Vorschlag Wiki URL, M: Wikipedia URL, N: Wikipedia Absatz, O: Wikipedia Branche, - # P: Wikipedia Umsatz, Q: Wikipedia Mitarbeiter, R: Wikipedia Kategorien, - # S: Konsistenzprüfung, T: Begründung bei Inkonsistenz, U: Vorschlag Wiki Artikel ChatGPT, - # V: Begründung bei Abweichung, W: Vorschlag neue Branche, X: Konsistenzprüfung Branche, - # Y: Begründung Abweichung Branche. company_name = row_data[1] if len(row_data) > 1 else "" website = row_data[3] if len(row_data) > 3 else "" current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - # Wikipedia-Teil: Spalte L bis R if len(row_data) > 11 and row_data[11].strip() not in ["", "k.A."]: wiki_url = row_data[11].strip() try: @@ -499,32 +487,67 @@ def _process_verification_row(self, row_num, row_data): wiki_data.get('mitarbeiter', 'k.A.'), wiki_data.get('categories', 'k.A.') ] - # Update Wikipedia-Spalten (L bis R) self.sheet_handler.sheet.update(values=[wiki_values], range_name=f"L{row_num}:R{row_num}") - # Brancheneinordnung: Verwende CRM-Branche (Spalte G) und Beschreibung Branche extern (Spalte H) crm_branche = row_data[6] if len(row_data) > 6 else "k.A." beschreibung = row_data[7] if len(row_data) > 7 else "k.A." wiki_branche = wiki_data.get('branche', 'k.A.') wiki_kategorien = wiki_data.get('categories', 'k.A.') branche_result = evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien) - # Update Brancheneinordnung in Spalten V, W, X (Beispielsweise) self.sheet_handler.sheet.update(values=[[branche_result["branch"]]], range_name=f"V{row_num}") self.sheet_handler.sheet.update(values=[[branche_result["consistency"]]], range_name=f"W{row_num}") self.sheet_handler.sheet.update(values=[[branche_result["justification"]]], range_name=f"X{row_num}") - # Verifizierungsstatus: Wir nutzen validate_article_with_chatgpt, um eine finale Aussage zu erhalten, in Spalte Y crm_data = ";".join(row_data[1:11]) wiki_data_str = ";".join(row_data[11:18]) valid_result = validate_article_with_chatgpt(crm_data, wiki_data_str) self.sheet_handler.sheet.update(values=[[valid_result]], range_name=f"Y{row_num}") - # Aktualisiere Timestamp und Version (z. B. in Spalte Z und AA) self.sheet_handler.sheet.update(values=[[current_dt]], range_name=f"Z{row_num}") self.sheet_handler.sheet.update(values=[[Config.VERSION]], range_name=f"AA{row_num}") debug_print(f"Zeile {row_num} verifiziert: URL: {wiki_data.get('url', 'k.A.')}, Branche: {wiki_data.get('branche', 'k.A.')}") time.sleep(Config.RETRY_DELAY) -# Füge _process_verification_row als Methode in DataProcessor hinzu DataProcessor._process_verification_row = _process_verification_row +# ==================== NEUER MODUS 8: BATCH-PROZESSING MIT TOKEN-ZÄHLUNG ==================== +def process_batch_token_count(batch_size=10): + import tiktoken + def count_tokens(text, model="gpt-3.5-turbo"): + encoding = tiktoken.encoding_for_model(model) + tokens = encoding.encode(text) + return len(tokens) + debug_print("Starte Batch-Token-Zählung (Modus 8)...") + gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name( + Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])) + sh = gc.open_by_url(Config.SHEET_URL) + main_sheet = sh.sheet1 + data = main_sheet.get_all_values() + # Gehe in Schritten von batch_size Zeilen durch den Datensatz + for i in range(2, len(data)+1, batch_size): + batch_rows = data[i-1:i-1+batch_size] + aggregated_prompt = "" + for row in batch_rows: + # Verwende Spalte B (Firmenname), C (Kurzform), D (Website), E (Ort), F (Beschreibung) und G (Aktuelle Branche) + info = [] + if len(row) > 1: + info.append(row[1]) + if len(row) > 2: + info.append(row[2]) + if len(row) > 3: + info.append(row[3]) + if len(row) > 4: + info.append(row[4]) + if len(row) > 5: + info.append(row[5]) + if len(row) > 6: + info.append(row[6]) + aggregated_prompt += "; ".join(info) + "\n" + token_count = count_tokens(aggregated_prompt) + debug_print(f"Batch beginnend in Zeile {i}: {token_count} Tokens") + # Aktualisiere Spalte AQ für alle Zeilen im Batch + for j in range(i, min(i+batch_size, len(data)+1)): + main_sheet.update(values=[[str(token_count)]], range_name=f"AQ{j}") + time.sleep(Config.RETRY_DELAY) + debug_print("Batch-Token-Zählung abgeschlossen.") + # ==================== NEUER MODUS: ALIGNMENT DEMO (für Hauptblatt und Contacts) ==================== def alignment_demo_full(): alignment_demo(GoogleSheetHandler().sheet) @@ -770,7 +793,7 @@ class GoogleSheetHandler: self.sheet = gspread.authorize(creds).open_by_url(Config.SHEET_URL).sheet1 self.sheet_values = self.sheet.get_all_values() def get_start_index(self): - # Wir verwenden Spalte AN (Index 39) als Wikipedia-Timestamp im regulären Modus + # Verwende Spalte AN (Index 39) als Wikipedia-Timestamp im regulären Modus filled_n = [row[39] if len(row) > 39 else '' for row in self.sheet_values[1:]] return next((i + 1 for i, v in enumerate(filled_n, start=1) if not str(v).strip()), len(filled_n) + 1) @@ -799,12 +822,12 @@ class DataProcessor: if len(row) <= 40 or row[40].strip() == "": processor._process_single_row(i, row, process_wiki=False, process_chatgpt=True) elif MODE == "51": - # Neuer Modus 51: Nur Verifizierung (Wikipedia + Brancheneinordnung) – bis Spalte Y bearbeiten. processor = DataProcessor() for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2): - # Hier prüfen wir, ob in Spalte Y (Index 24) noch kein Wert steht. if len(row) <= 25 or row[24].strip() == "": processor._process_verification_row(i, row) + elif MODE == "8": + process_batch_token_count() else: start_index = self.sheet_handler.get_start_index() print(f"Starte bei Zeile {start_index+1}") @@ -817,7 +840,6 @@ class DataProcessor: self._process_single_row(i, row) rows_processed += 1 def _process_single_row(self, row_num, row_data, force_all=False, process_wiki=True, process_chatgpt=True): - # Dies ist die vollständige Verarbeitung (wie in v1.3.11) company_name = row_data[1] if len(row_data) > 1 else "" website = row_data[3] if len(row_data) > 3 else "" wiki_update_range = f"L{row_num}:R{row_num}" @@ -964,11 +986,49 @@ def process_contacts(): else: debug_print("Keine Kontakte gefunden in der Haupttabelle.") +# ==================== NEUER MODUS 8: BATCH-PROZESSING MIT TOKEN-ZÄHLUNG ==================== +def process_batch_token_count(batch_size=10): + import tiktoken + def count_tokens(text, model="gpt-3.5-turbo"): + encoding = tiktoken.encoding_for_model(model) + tokens = encoding.encode(text) + return len(tokens) + debug_print("Starte Batch-Token-Zählung (Modus 8)...") + gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name( + Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])) + sh = gc.open_by_url(Config.SHEET_URL) + main_sheet = sh.sheet1 + data = main_sheet.get_all_values() + for i in range(2, len(data)+1, batch_size): + batch_rows = data[i-1:i-1+batch_size] + aggregated_prompt = "" + for row in batch_rows: + info = [] + if len(row) > 1: + info.append(row[1]) # Firmenname + if len(row) > 2: + info.append(row[2]) # Kurzform + if len(row) > 3: + info.append(row[3]) # Website + if len(row) > 4: + info.append(row[4]) # Ort + if len(row) > 5: + info.append(row[5]) # Beschreibung + if len(row) > 6: + info.append(row[6]) # Aktuelle Branche + aggregated_prompt += "; ".join(info) + "\n" + token_count = count_tokens(aggregated_prompt) + debug_print(f"Batch beginnend in Zeile {i}: {token_count} Tokens") + for j in range(i, min(i+batch_size, len(data)+1)): + main_sheet.update(values=[[str(token_count)]], range_name=f"AQ{j}") + time.sleep(Config.RETRY_DELAY) + debug_print("Batch-Token-Zählung abgeschlossen.") + # ==================== MAIN PROGRAMM ==================== if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() - parser.add_argument("--mode", type=str, default="1", help="Modus: 1, 2, 3, 4, 5, 6, 7 oder 51") + parser.add_argument("--mode", type=str, default="1", help="Modus: 1,2,3,4,5,6,7,51 oder 8") parser.add_argument("--num_rows", type=int, default=0, help="Anzahl der zu bearbeitenden Zeilen (nur für Modus 1)") args = parser.parse_args() @@ -990,10 +1050,12 @@ if __name__ == "__main__": for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2): if len(row) <= 40 or row[40].strip() == "": processor._process_single_row(i, row, process_wiki=False, process_chatgpt=True) + elif MODE == "51": + process_verification_only() elif MODE == "6": process_contact_research() elif MODE == "7": process_contacts() - elif MODE == "51": - process_verification_only() + elif MODE == "8": + process_batch_token_count() print(f"\n✅ Auswertung abgeschlossen ({Config.VERSION})")