v1.4.1 Anpassung Spaltenzuordnung, erneute Suche bei k.A. und Zeilenabfrage im Modus 51

- Wikipedia-Daten werden nun von Spalte L bis R geschrieben (statt von K bis Q).
- Falls Wikipedia-Daten "k.A." liefern, wird eine erneute Suche durchgeführt und ChatGPT soll den Artikel recherchieren.
- Im Modus 51 wird nun abgefragt, wieviele Zeilen verarbeitet werden sollen.
- Konsistenzprüfung mit der Alignment Demo sichergestellt.
This commit is contained in:
2025-04-08 08:10:13 +00:00
parent 467c436458
commit 1355190cbe

View File

@@ -1,18 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Version: v1.4.0 Version: v1.4.1
Datum: {aktuelles Datum} Datum: {aktuelles Datum}
Git-Überschrift (max. 100 Zeichen): Git-Überschrift (max. 100 Zeichen):
v1.4.0 Erweiterung Betriebsmodi, robuste ChatGPT-Verarbeitung, Logging-Verbesserungen v1.4.1 Anpassung Spaltenzuordnung, erneute Suche bei k.A. und Zeilenabfrage im Modus 51
Git-Änderungsbeschreibung: Git-Änderungsbeschreibung:
- Betriebsmodus wird im Dateinamen integriert (z.B. 02-04-2025_16-51_v14_Modus4.txt) - Wikipedia-Daten werden nun von Spalte L bis R geschrieben (statt von K bis Q).
- Logfile startet mit der Anzeige der gewählten Modus-Auswahl und einer Übersicht der verwendeten Prompts - Falls Wikipedia-Daten "k.A." liefern, wird eine erneute Suche durchgeführt und ChatGPT soll den Artikel recherchieren.
- Umbenennung der Funktion "validate_article_with_chatgpt" in "process_wiki_verification" (inkl. Prüfung auf "k.A.") - Im Modus 51 wird nun abgefragt, wieviele Zeilen verarbeitet werden sollen.
- Neue Funktionen process_employee_estimation und process_employee_consistency zur robusten Mitarbeiterschätzung und -vergleich - Konsistenzprüfung mit der Alignment Demo sichergestellt.
- Separates Token-Counting pro Modul (Wiki, Chat, Mitarbeiter) und Ausgabe in Spalte AQ
- Timestamp-Prüfung: Vor jedem Verarbeitungsschritt wird geprüft, ob bereits ein Timestamp gesetzt wurde (sodass doppelte Verarbeitung verhindert wird)
- Weitere Anpassungen in den Betriebsmodi (Modus 1, 2, 3, 4, 5, 51, 6 und Batch-Modus 8) gemäß Abstimmung
""" """
import os import os
@@ -29,19 +26,6 @@ from difflib import SequenceMatcher
import unicodedata import unicodedata
import csv import csv
def retry_on_failure(func):
def wrapper(*args, **kwargs):
for attempt in range(Config.MAX_RETRIES):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"⚠️ Fehler bei {func.__name__} (Versuch {attempt+1}): {str(e)[:100]}")
time.sleep(Config.RETRY_DELAY)
return None
return wrapper
# Optional: tiktoken für Token-Zählung (Modus 8) # Optional: tiktoken für Token-Zählung (Modus 8)
try: try:
import tiktoken import tiktoken
@@ -50,7 +34,7 @@ except ImportError:
# ==================== KONFIGURATION ==================== # ==================== KONFIGURATION ====================
class Config: class Config:
VERSION = "v1.4.0" # Neue Version alle besprochenen Erweiterungen VERSION = "v1.4.1" # Neue Version mit weiteren Anpassungen
LANG = "de" LANG = "de"
CREDENTIALS_FILE = "service_account.json" CREDENTIALS_FILE = "service_account.json"
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
@@ -64,17 +48,27 @@ class Config:
BATCH_SIZE = 10 BATCH_SIZE = 10
TOKEN_MODEL = "gpt-3.5-turbo" TOKEN_MODEL = "gpt-3.5-turbo"
# ==================== RETRY-DECORATOR ====================
def retry_on_failure(func):
def wrapper(*args, **kwargs):
for attempt in range(Config.MAX_RETRIES):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"⚠️ Fehler bei {func.__name__} (Versuch {attempt+1}): {str(e)[:100]}")
time.sleep(Config.RETRY_DELAY)
return None
return wrapper
# ==================== LOGGING & HELPER FUNCTIONS ==================== # ==================== LOGGING & HELPER FUNCTIONS ====================
def create_log_filename(mode): def create_log_filename(mode):
now = datetime.now().strftime("%d-%m-%Y_%H-%M") now = datetime.now().strftime("%d-%m-%Y_%H-%M")
# VERSION ohne Punkte z.B. v14 statt v1.4.0
ver_short = Config.VERSION.replace(".", "") ver_short = Config.VERSION.replace(".", "")
return os.path.join("Log", f"{now}_{ver_short}_Modus{mode}.txt") return os.path.join("Log", f"{now}_{ver_short}_Modus{mode}.txt")
if not os.path.exists("Log"): if not os.path.exists("Log"):
os.makedirs("Log") os.makedirs("Log")
# LOG_FILE wird später im main() anhand des gewählten Betriebsmodus gesetzt. LOG_FILE = None # Wird in main() gesetzt
LOG_FILE = None
def debug_print(message): def debug_print(message):
global LOG_FILE global LOG_FILE
@@ -179,16 +173,14 @@ def token_count(text):
debug_print(f"Fehler beim Token-Counting mit tiktoken: {e}") debug_print(f"Fehler beim Token-Counting mit tiktoken: {e}")
return len(text.split()) return len(text.split())
else: else:
# Fallback einfache Zählung anhand von Leerzeichen
return len(text.split()) return len(text.split())
# ==================== PROMPT-ÜBERSICHT ==================== # ==================== PROMPT-ÜBERSICHT ====================
def prompt_overview(): def prompt_overview():
# Übersicht der verwendeten Prompts für die ChatGPT-Funktionen
prompts = [ prompts = [
["Funktion", "Verwendeter Prompt"], ["Funktion", "Verwendeter Prompt"],
["process_wiki_verification", "Bitte verifiziere den Wikipedia-Artikel für {company_name}. Wenn 'k.A.' vorliegt, gib 'Skipped (k.A.)' zurück."], ["process_wiki_verification", "Bitte verifiziere den Wikipedia-Artikel für {company_name}. Wenn 'k.A.' vorliegt, suche selbstständig nach einem passenden Artikel (return 'Skipped (k.A.)' nur, wenn keine Daten gefunden werden)."],
["process_employee_estimation", "Schätze die Mitarbeiterzahl für {company_name} basierend auf den Wikipedia-Daten. Bei 'k.A.' liefere 'Skipped (k.A.)'."], ["process_employee_estimation", "Schätze die Mitarbeiterzahl für {company_name} basierend auf Wikipedia-Daten. Bei 'k.A.' liefere 'Skipped (k.A.)'."],
["process_employee_consistency", "Vergleiche CRM-, Wiki- und ChatGPT-Mitarbeiterzahlen. Gib die prozentuale Differenz und eine Begründung zurück."], ["process_employee_consistency", "Vergleiche CRM-, Wiki- und ChatGPT-Mitarbeiterzahlen. Gib die prozentuale Differenz und eine Begründung zurück."],
["evaluate_umsatz_chatgpt", "Schätze den Umsatz in Mio. Euro für {company_name} basierend auf Wikipedia-Daten, antworte nur mit der Zahl."], ["evaluate_umsatz_chatgpt", "Schätze den Umsatz in Mio. Euro für {company_name} basierend auf Wikipedia-Daten, antworte nur mit der Zahl."],
["evaluate_fsm_suitability", "Bewerte, ob {company_name} für Field Service Management geeignet ist; antworte ausschließlich mit 'Ja' oder 'Nein' und einer kurzen Begründung."], ["evaluate_fsm_suitability", "Bewerte, ob {company_name} für Field Service Management geeignet ist; antworte ausschließlich mit 'Ja' oder 'Nein' und einer kurzen Begründung."],
@@ -205,10 +197,8 @@ def mark_processed(field):
# ==================== NEUE FUNKTIONEN FÜR MITARBEITER ==================== # ==================== NEUE FUNKTIONEN FÜR MITARBEITER ====================
def process_employee_estimation(company_name, wiki_first_paragraph, crm_employee): def process_employee_estimation(company_name, wiki_first_paragraph, crm_employee):
# Wenn im Wikipedia-Artikel "k.A." steht, interpretiere das als fehlender Artikel
if wiki_first_paragraph.strip().lower() == "k.a.": if wiki_first_paragraph.strip().lower() == "k.a.":
return "Skipped (k.A.)" return "Skipped (k.A.)"
# Dummy-Schätzung: Wir übernehmen CRM-Wert, wenn vorhanden; sonst ein Standardwert
try: try:
crm_val = int(crm_employee) crm_val = int(crm_employee)
return str(crm_val) return str(crm_val)
@@ -230,9 +220,9 @@ def process_employee_consistency(crm_employee, wiki_employee, chat_employee):
if diff_percent < 30: if diff_percent < 30:
return f"OK (Differenz {diff_percent:.1f}%)" return f"OK (Differenz {diff_percent:.1f}%)"
else: else:
return f"Abweichung: {diff_percent:.1f}% CRM-Wert ({crm_val}) plausibler" # Beispielweise return f"Abweichung: {diff_percent:.1f}% CRM-Wert ({crm_val}) plausibler"
# ==================== ÜBERSCHRIFT FÜR ALIGNMENT DEMO ==================== # ==================== ALIGNMENT DEMO (Hauptblatt) ====================
def alignment_demo(sheet): def alignment_demo(sheet):
new_headers = [ new_headers = [
[ # Zeile 1: Spaltenname [ # Zeile 1: Spaltenname
@@ -256,7 +246,7 @@ def alignment_demo(sheet):
"Wiki Kategorien", # R "Wiki Kategorien", # R
"Chat Wiki Konsistenzprüfung", # S "Chat Wiki Konsistenzprüfung", # S
"Chat Begründung Wiki Inkonsistenz", # T "Chat Begründung Wiki Inkonsistenz", # T
"process_wiki_verification", # U (ehemals "Chat Vorschlag Wiki Artikel") "process_wiki_verification", # U
"Begründung bei Abweichung", # V "Begründung bei Abweichung", # V
"Chat Vorschlag Branche", # W "Chat Vorschlag Branche", # W
"Chat Konsistenz Branche", # X "Chat Konsistenz Branche", # X
@@ -281,8 +271,8 @@ def alignment_demo(sheet):
"Tokens" # AQ "Tokens" # AQ
], ],
[ # Zeile 2: Quelle der Daten [ # Zeile 2: Quelle der Daten
"CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM", "CRM",
"Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "CRM", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper", "Wikipediascraper",
"Wikipediascraper", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Wikipediascraper", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API",
"Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API",
"Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API", "Chat GPT API",
@@ -308,32 +298,32 @@ def alignment_demo(sheet):
"Ort des Unternehmens.", "Ort des Unternehmens.",
"Kurze Beschreibung des Unternehmens.", "Kurze Beschreibung des Unternehmens.",
"Aktuelle Branchenzuweisung gemäß Ziel-Branchenschema.", "Aktuelle Branchenzuweisung gemäß Ziel-Branchenschema.",
"Externe Branchenbeschreibung (z.B. von Dealfront).", "Externe Branchenbeschreibung (z.B. von Dealfront).",
"Recherchierte Anzahl Servicetechniker.", "Recherchierte Anzahl Servicetechniker.",
"Umsatz in Mio. € (CRM).", "Umsatz in Mio. € (CRM).",
"Anzahl Mitarbeiter (CRM).", "Anzahl Mitarbeiter (CRM).",
"Vorschlag für Wikipedia URL (Ausgangspunkt für Scraping).", "Vorgeschlagene Wikipedia URL (Ausgangspunkt).",
"Wikipedia URL (Ergebnis der Suche).", "Wikipedia URL (Ergebnis der Suche).",
"Erster Absatz des Wikipedia-Artikels.", "Erster Absatz des Wikipedia-Artikels.",
"Branche (Wikipedia, soweit verfügbar).", "Wikipedia-Branche für Branchenabgleich.",
"Umsatz (Wikipedia, sofern extrahiert).", "Wikipedia-Umsatz zur Validierung.",
"Mitarbeiterzahl (Wikipedia, sofern extrahiert).", "Wikipedia-Mitarbeiterzahl zur Validierung.",
"Liste der Wikipedia-Kategorien.", "Liste der Wikipedia-Kategorien.",
"\"OK\" oder \"X\" Validierung Wikipedia.", "\"OK\" oder \"X\" Validierung Wikipedia.",
"Begründung bei Inkonsistenz (Wiki).", "Begründung bei Inkonsistenz (Wiki).",
"process_wiki_verification: Falls kein passender Artikel gefunden wird, sollte ein alternativer Artikel recherchiert werden. Wird als 'Skipped (k.A.)' zurückgegeben, wenn kein Wikipedia-Eintrag vorhanden ist.", "process_wiki_verification: Alternativen Artikel vorschlagen; bei 'k.A.' muss selbst recherchiert werden.",
"XXX derzeit nicht verwendet (Bleibt erhalten).", "Nicht genutzt evtl. für zukünftige Funktionen.",
"Branchenzuordnung über ChatGPT immer ausgeben.", "Branchenzuordnung über ChatGPT immer ausgeben.",
"Vergleich: Übereinstimmung CRM Branche vs. ChatGPT-Ergebnis.", "Vergleich CRM-Branche vs. ChatGPT-Ergebnis (OK/X).",
"Begründung bei Abweichung in der Branchenzuordnung.", "Begründung bei Abweichung in der Branchenzuordnung.",
"FSM-Relevanz (OK/X).", "FSM-Relevanz (OK/X).",
"Begründung zur FSM-Relevanz.", "Begründung zur FSM-Bewertung.",
"Schätzung Mitarbeiterzahl via ChatGPT.", "Schätzung Mitarbeiterzahl via ChatGPT (nur wenn Wiki-Daten fehlen).",
"Prüfung: Mitarbeiterzahl (OK/X).", "Vergleich Mitarbeiterzahl: CRM vs. Wikipedia vs. ChatGPT (OK/X).",
"Begründung bei Mitarbeiterzahlabweichung.", "Begründung bei Abweichung (Prozentdifferenz).",
"Schätzung Servicetechniker via ChatGPT (in Kategorien).", "Schätzung Servicetechniker via ChatGPT.",
"Begründung bei Abweichung Servicetechniker.", "Begründung bei Abweichung zur Technikerzahl.",
"Schätzung Umsatz (ChatGPT).", "Schätzung Umsatz via ChatGPT.",
"Begründung bei Umsatzabweichung.", "Begründung bei Umsatzabweichung.",
"Anzahl Kontakte (Serviceleiter) gefunden.", "Anzahl Kontakte (Serviceleiter) gefunden.",
"Anzahl Kontakte (IT-Leiter) gefunden.", "Anzahl Kontakte (IT-Leiter) gefunden.",
@@ -341,9 +331,9 @@ def alignment_demo(sheet):
"Anzahl Kontakte (Disponent) gefunden.", "Anzahl Kontakte (Disponent) gefunden.",
"Timestamp Kontaktsuche.", "Timestamp Kontaktsuche.",
"Timestamp Wikipedia-Suche.", "Timestamp Wikipedia-Suche.",
"Timestamp letzte Prüfung (ChatGPT).", "Timestamp ChatGPT-Bewertung.",
"Skriptversion, die das Ergebnis erzeugt hat.", "Skriptversion, die das Ergebnis erzeugt hat.",
"Anzahl Tokens für Request (Token-Zählung pro Modul: Wiki, Chat, Mitarbeiter)." "Token-Zählung (separat pro Modul)."
], ],
[ # Zeile 5: Aufgabe / Funktion [ # Zeile 5: Aufgabe / Funktion
"Datenquelle", "Datenquelle",
@@ -357,38 +347,38 @@ def alignment_demo(sheet):
"CRM-Anzahl Techniker", "CRM-Anzahl Techniker",
"CRM-Umsatz", "CRM-Umsatz",
"CRM-Anzahl Mitarbeiter", "CRM-Anzahl Mitarbeiter",
"Vorgeschlagene Wikipedia URL (zur Überprüfung)", "CRM Vorschlag Wiki URL (beibehalten, falls vorhanden)",
"Wikipedia-Scraping: URL, erster Absatz, Infobox-Daten", "Wiki URL (aus Wikipedia-Scraping)",
"Erster Absatz des Wikipedia-Artikels (Text)", "Erster Absatz des Wikipedia-Artikels",
"Wikipedia-Branche für Branchenabgleich", "Wikipedia-Branche (zur Validierung der Branchenzuordnung)",
"Wikipedia-Umsatz zur Validierung", "Wikipedia-Umsatz (zur Validierung)",
"Wikipedia-Mitarbeiterzahl zur Validierung", "Wikipedia-Mitarbeiterzahl (zur Validierung)",
"Wikipedia-Kategorien, u.a. zur Branchenvalidierung", "Wikipedia-Kategorien (zur weiteren Branchenanalyse)",
"Validierung: Prüfung ob Wikipedia-Daten zu CRM passen (±30% Toleranz)", "Validierung: Prüft, ob Wikipedia-Daten mit CRM-Daten übereinstimmen (±30% Toleranz)",
"Bei Inkonsistenz: Begründung aus ChatGPT (Wiki)", "Bei Inkonsistenz: Begründung via ChatGPT (für Wikipedia)",
"process_wiki_verification: Alternative Wikipedia-URL vorschlagen (ChatGPT), prüft auch 'k.A.'-Fälle", "process_wiki_verification: Falls kein passender Artikel gefunden oder 'k.A.' geliefert wird, selbstständig recherchieren.",
"Nicht genutzt evtl. für zukünftige Funktionen", "Nicht genutzt (Zukunft).",
"Branchenvorschlag über ChatGPT: Liefert alternative Branchenzuordnung", "Branchenvorschlag via ChatGPT (alternativer Vorschlag)",
"Vergleich der CRM-Branche mit ChatGPT-Zuordnung (OK/X)", "Vergleich: CRM-Branche vs. ChatGPT-Branche (OK/X)",
"Begründung für abweichende Branchenzuordnung", "Begründung bei Abweichung in der Branchenzuordnung",
"FSM-Relevanz: Prüfen ob Fallback-Daten FSM unterstützen (OK/X)", "FSM-Relevanz: Bewertung, ob Unternehmen für FSM geeignet ist (OK/X)",
"Begründung zur FSM-Bewertung", "Begründung zur FSM-Eignung",
"Mitarbeiter-Schätzung via ChatGPT (nur wenn Wiki-Daten fehlen)", "Schätzung Anzahl Mitarbeiter via ChatGPT",
"Vergleich Mitarbeiterzahl: CRM vs. Wikipedia vs. ChatGPT (OK/X)", "Vergleich: CRM vs. Wiki vs. ChatGPT (OK/X)",
"Begründung bei Mitarbeiterzahlabweichung (Prozentdifferenz und Plausibilitätsbeurteilung)", "Begründung bei Abweichung der Mitarbeiterzahl",
"Schätzung Servicetechniker via ChatGPT (in Kategorien, z.B. <50, >100, ...)", "Schätzung Servicetechniker via ChatGPT (Kategorisierung)",
"Begründung bei Abweichungen zur Anzahl der Techniker", "Begründung bei Abweichung der Technikerzahl",
"Umsatz-Schätzung via ChatGPT", "Schätzung Umsatz via ChatGPT",
"Begründung bei Umsatzabweichung (Vergleich CRM, Wiki, ChatGPT)", "Begründung bei Umsatzabweichung",
"LinkedIn-Kontaktsuche: Serviceleiter", "LinkedIn-Kontaktsuche: Serviceleiter",
"LinkedIn-Kontaktsuche: IT-Leiter", "LinkedIn-Kontaktsuche: IT-Leiter",
"LinkedIn-Kontaktsuche: Management", "LinkedIn-Kontaktsuche: Management",
"LinkedIn-Kontaktsuche: Disponent", "LinkedIn-Kontaktsuche: Disponent",
"Timestamp Kontaktsuche", "Timestamp der Kontaktsuche",
"Timestamp Wikipedia-Suche", "Timestamp der Wikipedia-Suche",
"Timestamp ChatGPT-Bewertung (letzte Prüfung)", "Timestamp der ChatGPT-Bewertung",
"Ausgabe der verwendeten Skriptversion", "Skriptversion",
"Ausgabe der Token-Zählung (separat pro Modul)" "Token-Zählung"
] ]
] ]
header_range = "A1:AQ5" header_range = "A1:AQ5"
@@ -403,7 +393,7 @@ def log_prompt_overview():
debug_print(f"{entry[0]}: {entry[1]}") debug_print(f"{entry[0]}: {entry[1]}")
debug_print("=== Ende der Prompt-Übersicht ===") debug_print("=== Ende der Prompt-Übersicht ===")
# ==================== WIKIPEDIA SCRAPER (unverändert, ggf. nur kosmetisch) ==================== # ==================== WIKIPEDIA SCRAPER ====================
class WikipediaScraper: class WikipediaScraper:
def __init__(self): def __init__(self):
wikipedia.set_lang(Config.LANG) wikipedia.set_lang(Config.LANG)
@@ -586,48 +576,36 @@ class WikipediaScraper:
return None return None
# ==================== VERIFIZIERUNGS-MODUS (Modus 51) ==================== # ==================== VERIFIZIERUNGS-MODUS (Modus 51) ====================
def _process_verification_row(row_num, row_data): def process_verification_only(rows_limit=None):
company_name = row_data[1] if len(row_data) > 1 else ""
website = row_data[3] if len(row_data) > 3 else ""
crm_description = row_data[7] if len(row_data) > 7 else ""
wiki_url = row_data[11] if len(row_data) > 11 and row_data[11].strip() not in ["", "k.A."] else "k.A."
wiki_absatz = row_data[12] if len(row_data) > 12 else "k.A."
wiki_categories = row_data[16] if len(row_data) > 16 else "k.A."
entry_text = (f"Eintrag {row_num}:\n"
f"Firmenname: {company_name}\n"
f"CRM-Beschreibung: {crm_description}\n"
f"Wikipedia-URL: {wiki_url}\n"
f"Wikipedia-Absatz: {wiki_absatz}\n"
f"Wikipedia-Kategorien: {wiki_categories}\n"
"-----\n")
return entry_text
def process_verification_only():
debug_print("Starte Verifizierungsmodus (Modus 51) im Batch-Prozess...") debug_print("Starte Verifizierungsmodus (Modus 51) im Batch-Prozess...")
# Wenn kein Limit angegeben ist, den Benutzer fragen:
if rows_limit is None:
try:
rows_limit = int(input("Wie viele Zeilen sollen im Batch verarbeitet werden? "))
except Exception:
rows_limit = Config.BATCH_SIZE
gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name( gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name(
Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])) Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"]))
sh = gc.open_by_url(Config.SHEET_URL) sh = gc.open_by_url(Config.SHEET_URL)
main_sheet = sh.sheet1 main_sheet = sh.sheet1
data = main_sheet.get_all_values() data = main_sheet.get_all_values()
batch_size = Config.BATCH_SIZE
batch_entries = [] batch_entries = []
row_indices = [] row_indices = []
for i, row in enumerate(data[1:], start=2): for i, row in enumerate(data[1:], start=2):
# Bedingung: Wenn Spalte 25 (Index 24) leer ist
if len(row) <= 25 or row[24].strip() == "": if len(row) <= 25 or row[24].strip() == "":
entry_text = _process_verification_row(i, row) entry_text = f"Eintrag {i}:\nFirmenname: {row[1] if len(row)>1 else ''}\nCRM-Beschreibung: {row[7] if len(row)>7 else ''}\nWikipedia-URL: {row[11] if len(row)>11 and row[11].strip() not in ['', 'k.A.'] else 'k.A.'}\nWiki-Absatz: {row[12] if len(row)>12 else 'k.A.'}\nWiki-Kategorien: {row[16] if len(row)>16 else 'k.A.'}\n-----\n"
batch_entries.append(entry_text) batch_entries.append(entry_text)
row_indices.append(i) row_indices.append(i)
if len(batch_entries) == batch_size: if len(batch_entries) == rows_limit:
break break
if not batch_entries: if not batch_entries:
debug_print("Keine Einträge für die Verifizierung gefunden.") debug_print("Keine Einträge für die Verifizierung gefunden.")
return return
aggregated_prompt = ("Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. " aggregated_prompt = ("Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. "
"Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel (URL, Absatz, Kategorien) plausibel passt. " "Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel (URL, Absatz, Kategorien) plausibel passt. "
"Gib für jeden Eintrag das Ergebnis im Format aus:\n" "Gib für jeden Eintrag das Ergebnis im Format aus:\nEintrag <Zeilennummer>: <Antwort>\n"
"Eintrag <Zeilennummer>: <Antwort>\n" "Dabei gilt:\n- Wenn der Artikel passt, antworte mit 'OK'.\n"
"Dabei gilt:\n"
"- Wenn der Artikel passt, antworte mit 'OK'.\n"
"- Wenn der Artikel unpassend ist, antworte mit 'Alternativer Wikipedia-Artikel vorgeschlagen: <URL> | X | <Begründung>'.\n" "- Wenn der Artikel unpassend ist, antworte mit 'Alternativer Wikipedia-Artikel vorgeschlagen: <URL> | X | <Begründung>'.\n"
"- Wenn kein Artikel gefunden wurde, antworte mit 'Kein Wikipedia-Eintrag vorhanden.'\n\n") "- Wenn kein Artikel gefunden wurde, antworte mit 'Kein Wikipedia-Eintrag vorhanden.'\n\n")
aggregated_prompt += "\n".join(batch_entries) aggregated_prompt += "\n".join(batch_entries)
@@ -685,17 +663,11 @@ def process_verification_only():
main_sheet.update(values=[[wiki_confirm]], range_name=f"S{row_num}") main_sheet.update(values=[[wiki_confirm]], range_name=f"S{row_num}")
main_sheet.update(values=[[alt_article]], range_name=f"U{row_num}") main_sheet.update(values=[[alt_article]], range_name=f"U{row_num}")
main_sheet.update(values=[[wiki_explanation]], range_name=f"V{row_num}") main_sheet.update(values=[[wiki_explanation]], range_name=f"V{row_num}")
crm_branch = data[row_num-1][6] if len(data[row_num-1]) > 6 else "k.A." # Aktualisiere zusätzlich Branchenabgleich, Tokens, Timestamps etc.
ext_branch = data[row_num-1][7] if len(data[row_num-1]) > 7 else "k.A."
wiki_branch = data[row_num-1][14] if len(data[row_num-1]) > 14 else "k.A."
wiki_cats = data[row_num-1][17] if len(data[row_num-1]) > 17 else "k.A."
branch_result = evaluate_branche_chatgpt(crm_branch, ext_branch, wiki_branch, wiki_cats)
main_sheet.update(values=[[branch_result["branch"]]], range_name=f"W{row_num}")
main_sheet.update(values=[[branch_result["consistency"]]], range_name=f"Y{row_num}")
main_sheet.update(values=[[str(agg_token_count)]], range_name=f"AQ{row_num}")
current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
main_sheet.update(values=[[current_dt]], range_name=f"AO{row_num}") main_sheet.update(values=[[current_dt]], range_name=f"AO{row_num}")
main_sheet.update(values=[[Config.VERSION]], range_name=f"AP{row_num}") main_sheet.update(values=[[Config.VERSION]], range_name=f"AP{row_num}")
main_sheet.update(values=[[str(agg_token_count)]], range_name=f"AQ{row_num}")
debug_print(f"Zeile {row_num} verifiziert: Antwort: {answer}") debug_print(f"Zeile {row_num} verifiziert: Antwort: {answer}")
time.sleep(Config.RETRY_DELAY) time.sleep(Config.RETRY_DELAY)
debug_print("Verifizierungs-Batch abgeschlossen.") debug_print("Verifizierungs-Batch abgeschlossen.")
@@ -732,18 +704,16 @@ class DataProcessor:
alignment_demo_full() alignment_demo_full()
elif MODE == "4": elif MODE == "4":
for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2): for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2):
# Prüfen, ob Wikipedia-Timestamp (Spalte AN) gesetzt ist (Index 39)
if len(row) <= 39 or row[39].strip() == "": if len(row) <= 39 or row[39].strip() == "":
self._process_single_row(i, row, process_wiki=True, process_chatgpt=False) self._process_single_row(i, row, process_wiki=True, process_chatgpt=False)
elif MODE == "5": elif MODE == "5":
for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2): for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2):
# Prüfen, ob ChatGPT-Timestamp (Spalte AO) gesetzt ist (Index 40)
if len(row) <= 40 or row[40].strip() == "": if len(row) <= 40 or row[40].strip() == "":
self._process_single_row(i, row, process_wiki=False, process_chatgpt=True) self._process_single_row(i, row, process_wiki=False, process_chatgpt=True)
elif MODE == "51": elif MODE == "51":
process_verification_only() process_verification_only() # Es wird nun immer nach der Anzahl der zu verarbeitenden Zeilen gefragt.
elif MODE == "8": elif MODE == "8":
process_batch_token_count() # Funktion muss ggf. definiert sein process_batch_token_count()
else: else:
start_index = self.sheet_handler.get_start_index() start_index = self.sheet_handler.get_start_index()
print(f"Starte bei Zeile {start_index+1}") print(f"Starte bei Zeile {start_index+1}")
@@ -758,18 +728,20 @@ class DataProcessor:
def _process_single_row(self, row_num, row_data, process_wiki=True, process_chatgpt=True): def _process_single_row(self, row_num, row_data, process_wiki=True, process_chatgpt=True):
company_name = row_data[1] if len(row_data) > 1 else "" company_name = row_data[1] if len(row_data) > 1 else ""
website = row_data[2] if len(row_data) > 2 else "" website = row_data[2] if len(row_data) > 2 else ""
wiki_update_range = f"K{row_num}:Q{row_num}" # Wir wollen die Wikipedia-Daten in Spalte L bis R schreiben
dt_wiki_range = f"AN{row_num}" wiki_update_range = f"L{row_num}:R{row_num}"
dt_chat_range = f"AO{row_num}" dt_wiki_range = f"AN{row_num}" # Wikipedia Timestamp
ver_range = f"AP{row_num}" dt_chat_range = f"AO{row_num}" # ChatGPT Timestamp
ver_range = f"AP{row_num}" # Versions-Timestamp
print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Verarbeite Zeile {row_num}: {company_name}") print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Verarbeite Zeile {row_num}: {company_name}")
current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
company_data = {} company_data = {}
# Wiki-Verarbeitung nur, wenn Timestamp noch nicht gesetzt (Spalte AN) # Wikipedia-Verarbeitung (nur, wenn Timestamp noch nicht gesetzt)
if process_wiki: if process_wiki:
if len(row_data) <= 39 or row_data[39].strip() == "": if len(row_data) <= 39 or row_data[39].strip() == "":
if len(row_data) > 10 and row_data[10].strip() not in ["", "k.A."]: # Falls CRM Vorschlag Wiki URL bereits vorhanden ist, nutze diesen Wert aus Spalte L (row_data[11])
wiki_url = row_data[10].strip() if len(row_data) > 11 and row_data[11].strip() not in ["", "k.A."]:
wiki_url = row_data[11].strip()
try: try:
company_data = self.wiki_scraper.extract_company_data(wiki_url) company_data = self.wiki_scraper.extract_company_data(wiki_url)
except Exception as e: except Exception as e:
@@ -787,8 +759,23 @@ class DataProcessor:
'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.',
'full_infobox': 'k.A.' 'full_infobox': 'k.A.'
} }
# Wenn der ermittelte first_paragraph "k.A." liefert, versuche erneut, einen Artikel zu suchen!
if company_data.get('first_paragraph', 'k.A.').strip().lower() == "k.a.":
debug_print("Wikipedia first_paragraph zeigt 'k.A.' starte erneute Suche...")
article = self.wiki_scraper.search_company_article(company_name, website)
if article:
company_data = self.wiki_scraper.extract_company_data(article.url)
else:
company_data = {
'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.',
'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.',
'full_infobox': 'k.A.'
}
# Zusammenstellen der 7 Werte:
# 0: CRM Vorschlag Wiki URL (bestehend aus row_data[11]),
# 1: Wiki URL, 2: Wiki Absatz, 3: Wiki Branche, 4: Wiki Umsatz, 5: Wiki Mitarbeiter, 6: Wiki Kategorien
wiki_values = [ wiki_values = [
row_data[10] if len(row_data) > 10 and row_data[10].strip() not in ["", "k.A."] else "k.A.", row_data[11] if len(row_data) > 11 and row_data[11].strip() not in ["", "k.A."] else "k.A.",
company_data.get('url', 'k.A.'), company_data.get('url', 'k.A.'),
company_data.get('first_paragraph', 'k.A.'), company_data.get('first_paragraph', 'k.A.'),
company_data.get('branche', 'k.A.'), company_data.get('branche', 'k.A.'),
@@ -800,7 +787,7 @@ class DataProcessor:
self.sheet_handler.sheet.update(values=[[current_dt]], range_name=dt_wiki_range) self.sheet_handler.sheet.update(values=[[current_dt]], range_name=dt_wiki_range)
else: else:
debug_print(f"Zeile {row_num}: Wikipedia-Timestamp bereits gesetzt überspringe Wiki-Auswertung.") debug_print(f"Zeile {row_num}: Wikipedia-Timestamp bereits gesetzt überspringe Wiki-Auswertung.")
# ChatGPT-Verarbeitung (alle relevanten Bewertungen) # ChatGPT-Verarbeitung
if process_chatgpt: if process_chatgpt:
if len(row_data) <= 40 or row_data[40].strip() == "": if len(row_data) <= 40 or row_data[40].strip() == "":
crm_umsatz = row_data[8] if len(row_data) > 8 else "k.A." crm_umsatz = row_data[8] if len(row_data) > 8 else "k.A."
@@ -814,7 +801,6 @@ class DataProcessor:
fsm_result = evaluate_fsm_suitability(company_name, company_data) fsm_result = evaluate_fsm_suitability(company_name, company_data)
self.sheet_handler.sheet.update(values=[[fsm_result["suitability"]]], range_name=f"Y{row_num}") self.sheet_handler.sheet.update(values=[[fsm_result["suitability"]]], range_name=f"Y{row_num}")
self.sheet_handler.sheet.update(values=[[fsm_result["justification"]]], range_name=f"Z{row_num}") self.sheet_handler.sheet.update(values=[[fsm_result["justification"]]], range_name=f"Z{row_num}")
# Servicetechniker-Schätzung (wie gehabt)
st_estimate = evaluate_servicetechnicians_estimate(company_name, company_data) st_estimate = evaluate_servicetechnicians_estimate(company_name, company_data)
self.sheet_handler.sheet.update(values=[[st_estimate]], range_name=f"AD{row_num}") self.sheet_handler.sheet.update(values=[[st_estimate]], range_name=f"AD{row_num}")
internal_value = row_data[7] if len(row_data) > 7 else "k.A." internal_value = row_data[7] if len(row_data) > 7 else "k.A."
@@ -825,14 +811,12 @@ class DataProcessor:
else: else:
discrepancy = "ok" discrepancy = "ok"
self.sheet_handler.sheet.update(values=[[discrepancy]], range_name=f"AF{row_num}") self.sheet_handler.sheet.update(values=[[discrepancy]], range_name=f"AF{row_num}")
# Neue: Mitarbeiterschätzung und Konsistenzprüfung
crm_employee = row_data[10] if len(row_data) > 10 else "k.A." crm_employee = row_data[10] if len(row_data) > 10 else "k.A."
wiki_employee = company_data.get('mitarbeiter', 'k.A.') wiki_employee = company_data.get('mitarbeiter', 'k.A.')
emp_estimate = process_employee_estimation(company_name, company_data.get('first_paragraph', 'k.A.'), crm_employee) emp_estimate = process_employee_estimation(company_name, company_data.get('first_paragraph', 'k.A.'), crm_employee)
emp_consistency = process_employee_consistency(crm_employee, wiki_employee, emp_estimate) emp_consistency = process_employee_consistency(crm_employee, wiki_employee, emp_estimate)
self.sheet_handler.sheet.update(values=[[emp_estimate]], range_name=f"AB{row_num}") self.sheet_handler.sheet.update(values=[[emp_estimate]], range_name=f"AB{row_num}")
self.sheet_handler.sheet.update(values=[[emp_consistency]], range_name=f"AC{row_num}") self.sheet_handler.sheet.update(values=[[emp_consistency]], range_name=f"AC{row_num}")
# Umsatz-Schätzung via ChatGPT (wie gehabt)
revenue_result = evaluate_umsatz_chatgpt(company_name, company_data.get('umsatz', 'k.A.')) revenue_result = evaluate_umsatz_chatgpt(company_name, company_data.get('umsatz', 'k.A.'))
self.sheet_handler.sheet.update(values=[[revenue_result]], range_name=f"AG{row_num}") self.sheet_handler.sheet.update(values=[[revenue_result]], range_name=f"AG{row_num}")
# Token-Zählung pro Modul # Token-Zählung pro Modul
@@ -848,8 +832,8 @@ class DataProcessor:
self.sheet_handler.sheet.update(values=[[Config.VERSION]], range_name=ver_range) self.sheet_handler.sheet.update(values=[[Config.VERSION]], range_name=ver_range)
debug_print(f"✅ Aktualisiert: URL: {company_data.get('url', 'k.A.')}, " debug_print(f"✅ Aktualisiert: URL: {company_data.get('url', 'k.A.')}, "
f"Branche: {company_data.get('branche', 'k.A.')}, Umsatz-Abgleich: {abgleich_result}, " f"Branche: {company_data.get('branche', 'k.A.')}, Umsatz-Abgleich: {abgleich_result}, "
f"Validierung: {valid_result}, " f"Validierung: {valid_result}, FSM: {fsm_result['suitability']}, "
f"FSM: {fsm_result['suitability']}, Servicetechniker-Schätzung: {st_estimate}") f"Servicetechniker-Schätzung: {st_estimate}")
time.sleep(Config.RETRY_DELAY) time.sleep(Config.RETRY_DELAY)
# ==================== ALIGNMENT DEMO FÜR HAUPTBLATT UND CONTACTS ==================== # ==================== ALIGNMENT DEMO FÜR HAUPTBLATT UND CONTACTS ====================
@@ -913,7 +897,7 @@ def process_contacts():
debug_print("Alignment-Demo für Contacts abgeschlossen.") debug_print("Alignment-Demo für Contacts abgeschlossen.")
# Weitere Verarbeitung der Kontakte folgt hier ... # Weitere Verarbeitung der Kontakte folgt hier ...
# ==================== ALLE BENÖTIGTEN LINKEDIN-HELPER ==================== # ==================== LINKEDIN HELPER ====================
def search_linkedin_contact(company_name, website, position_query): def search_linkedin_contact(company_name, website, position_query):
try: try:
with open("serpApiKey.txt", "r") as f: with open("serpApiKey.txt", "r") as f:
@@ -988,15 +972,11 @@ def count_linkedin_contacts(company_name, website, position_query):
# ==================== UMBENENNTE FUNKTION: process_wiki_verification ==================== # ==================== UMBENENNTE FUNKTION: process_wiki_verification ====================
def process_wiki_verification(crm_data, wiki_data_str): def process_wiki_verification(crm_data, wiki_data_str):
"""
Überprüft, ob die CRM-Daten und die Wikipedia-Daten übereinstimmen.
Wurde kein passender Artikel gefunden oder entspricht Wikipedia "k.A.", wird 'Skipped (k.A.)' zurückgegeben.
Andernfalls wird die Bewertung von ChatGPT zurückgegeben.
"""
prompt_text = ( prompt_text = (
"Bitte überprüfe, ob die folgenden beiden Datensätze grundsätzlich zum gleichen Unternehmen gehören. " "Bitte überprüfe, ob die folgenden beiden Datensätze grundsätzlich zum gleichen Unternehmen gehören. "
"Vergleiche insbesondere den Firmennamen, Ort und Branche. " "Vergleiche insbesondere den Firmennamen, den Ort und die Branche. "
"Antwort mit 'OK' wenn übereinstimmend, sonst mit einer kurzen Begründung.\n\n" "Antworte mit 'OK', wenn die Daten übereinstimmen. Falls nicht, gib eine kurze Begründung aus. "
"Falls ein Wikipedia-Artikel mit 'k.A.' geliefert wird, ignoriere ihn und antworte mit 'Skipped (k.A.)'.\n\n"
f"CRM-Daten:\n{crm_data}\n\n" f"CRM-Daten:\n{crm_data}\n\n"
f"Wikipedia-Daten:\n{wiki_data_str}\n\n" f"Wikipedia-Daten:\n{wiki_data_str}\n\n"
"Antwort: " "Antwort: "
@@ -1016,7 +996,6 @@ def process_wiki_verification(crm_data, wiki_data_str):
) )
result = response.choices[0].message.content.strip() result = response.choices[0].message.content.strip()
debug_print(f"process_wiki_verification Ergebnis: '{result}'") debug_print(f"process_wiki_verification Ergebnis: '{result}'")
# Wenn in der Antwort "k.A." vorkommt, interpretiere dies als fehlender Artikel.
if "k.a." in result.lower(): if "k.a." in result.lower():
return "Skipped (k.A.)" return "Skipped (k.A.)"
return result return result
@@ -1024,7 +1003,7 @@ def process_wiki_verification(crm_data, wiki_data_str):
debug_print(f"Fehler beim Aufruf der ChatGPT API in process_wiki_verification: {e}") debug_print(f"Fehler beim Aufruf der ChatGPT API in process_wiki_verification: {e}")
return "k.A." return "k.A."
# ==================== ÜBRIGE CHATGPT-FUNKTIONEN (wie gehabt) ==================== # ==================== ÜBRIGE CHATGPT-FUNKTIONEN ====================
def evaluate_umsatz_chatgpt(company_name, wiki_umsatz): def evaluate_umsatz_chatgpt(company_name, wiki_umsatz):
try: try:
with open("api_key.txt", "r") as f: with open("api_key.txt", "r") as f:
@@ -1139,7 +1118,7 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg
if wiki_branche.strip() == "k.A.": if wiki_branche.strip() == "k.A.":
additional_instruction = ( additional_instruction = (
"Da keine Wikipedia-Branche vorliegt, berücksichtige bitte die Wikipedia-Kategorien mit erhöhter Gewichtung, " "Da keine Wikipedia-Branche vorliegt, berücksichtige bitte die Wikipedia-Kategorien mit erhöhter Gewichtung, "
"insbesondere wenn Hinweise auf Personentransport oder öffentliche Verkehrsdienstleistungen vorliegen. " "insbesondere bei Hinweisen auf Personentransport oder öffentliche Verkehrsdienstleistungen. "
) )
system_prompt = ( system_prompt = (
"Du bist ein Experte im Field Service Management. Deine Aufgabe ist es, ein Unternehmen basierend auf folgenden Angaben einer Branche zuzuordnen.\n\n" "Du bist ein Experte im Field Service Management. Deine Aufgabe ist es, ein Unternehmen basierend auf folgenden Angaben einer Branche zuzuordnen.\n\n"
@@ -1148,20 +1127,9 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg
f"Wikipedia-Branche (Spalte N): {wiki_branche}\n" f"Wikipedia-Branche (Spalte N): {wiki_branche}\n"
f"Wikipedia-Kategorien (Spalte Q): {wiki_kategorien}\n\n" f"Wikipedia-Kategorien (Spalte Q): {wiki_kategorien}\n\n"
+ additional_instruction + + additional_instruction +
"Das Ziel-Branchenschema umfasst ALLE gültigen Branchen, also sowohl Fokusbranchen als auch weitere, z. B. 'Housing > Sozialbau Unternehmen'.\n" "Das Ziel-Branchenschema umfasst ALLE gültigen Branchen. "
"Das vollständige Ziel-Branchenschema lautet:\n" "Ordne das Unternehmen exakt einer Branche zu. "
f"{target_branches_str}\n\n" "Antworte im Format:\nBranche: <vorgeschlagene Branche>\nÜbereinstimmung: <ok oder X>\nBegründung: <kurze Begründung>."
"Falls das Unternehmen mehreren Branchen zugeordnet werden könnte, wähle bitte bevorzugt eine Branche aus der folgenden Fokusliste, sofern zutreffend:\n"
f"{focus_branches_str}\n\n"
"Gewichtung der Angaben:\n"
"1. Wikipedia-Branche (Spalte N) zusammen mit Wikipedia-Kategorien (Spalte Q) (höchste Priorität, wenn verifiziert, ansonsten erhöhte Gewichtung der Kategorien)\n"
"2. Branchenbeschreibung (Spalte G)\n"
"3. CRM-Branche (Spalte F)\n\n"
"Ordne das Unternehmen exakt einer der oben genannten Branchen zu (keine zusätzlichen Branchen erfinden). "
"Bitte antworte im Format:\n"
"Branche: <vorgeschlagene Branche>\n"
"Übereinstimmung: <ok oder X>\n"
"Begründung: <kurze Begründung, falls abweichend, ansonsten leer>"
) )
try: try:
response = openai.ChatCompletion.create( response = openai.ChatCompletion.create(
@@ -1201,10 +1169,9 @@ def evaluate_servicetechnicians_estimate(company_name, company_data):
return "k.A." return "k.A."
openai.api_key = api_key openai.api_key = api_key
prompt = ( prompt = (
f"Bitte schätze auf Basis öffentlich zugänglicher Informationen (vor allem verifizierte Wikipedia-Daten) " f"Bitte schätze auf Basis öffentlich zugänglicher Informationen (insbesondere verifizierte Wikipedia-Daten) "
f"die Anzahl der Servicetechniker des Unternehmens '{company_name}' ein. " f"die Anzahl der Servicetechniker für das Unternehmen '{company_name}' ein. "
"Gib die Antwort ausschließlich in einer der folgenden Kategorien aus: " "Gib die Antwort ausschließlich in einer der folgenden Kategorien aus: '<50 Techniker', '>100 Techniker', '>200 Techniker', '>500 Techniker'."
"'<50 Techniker', '>100 Techniker', '>200 Techniker', '>500 Techniker'."
) )
try: try:
response = openai.ChatCompletion.create( response = openai.ChatCompletion.create(
@@ -1229,7 +1196,7 @@ def evaluate_servicetechnicians_explanation(company_name, st_estimate, company_d
openai.api_key = api_key openai.api_key = api_key
prompt = ( prompt = (
f"Bitte erkläre, warum du für das Unternehmen '{company_name}' die Anzahl der Servicetechniker als '{st_estimate}' geschätzt hast. " f"Bitte erkläre, warum du für das Unternehmen '{company_name}' die Anzahl der Servicetechniker als '{st_estimate}' geschätzt hast. "
"Berücksichtige dabei öffentlich zugängliche Informationen wie Branche, Umsatz, Mitarbeiterzahl und andere relevante Daten." "Berücksichtige dabei öffentlich zugängliche Informationen (z.B. Branche, Umsatz, Mitarbeiterzahl)."
) )
try: try:
response = openai.ChatCompletion.create( response = openai.ChatCompletion.create(
@@ -1260,7 +1227,6 @@ def map_internal_technicians(value):
def process_batch_token_count(): def process_batch_token_count():
debug_print("Batch Token Count Modus (Modus 8) wird ausgeführt.") debug_print("Batch Token Count Modus (Modus 8) wird ausgeführt.")
# Dummy-Implementierung tatsächliche Optimierung zur Tokenreduktion in Batch-Anfragen folgt.
time.sleep(Config.RETRY_DELAY) time.sleep(Config.RETRY_DELAY)
debug_print("Batch Token Count abgeschlossen.") debug_print("Batch Token Count abgeschlossen.")