v1.3.5 Erweiterung: FSM-Eignungsprüfung & Servicetechniker-Explanation, Sheet-Update-Check, automati

Wikipedia-Artikelvorschlag aus Spalte K wird bevorzugt genutzt.

Nach dem Schreiben der Wikipedia-Daten wird überprüft, ob das Update abgeschlossen ist (mit einer max. 5‑s‑Wartezeit) und es folgt eine 3‑s Pause.

Der FSM-Eignungsparser wurde angepasst, um auch freiere Antworten zu verarbeiten.

Bei Diskrepanzen in der Servicetechniker-Schätzung wird nun zusätzlich eine detaillierte Erklärung von ChatGPT angefordert.

Die Spalten AF und AG werden vorerst mit "XX" befüllt.

Alle Debug-Ausgaben werden automatisch in einer Log-Datei im Ordner "Log" mit Datum und Versionsnummer gespeichert.
This commit is contained in:
2025-04-02 13:58:23 +00:00
parent 43c23b50d8
commit e0ea9f8e4d

View File

@@ -14,7 +14,7 @@ import csv
# ==================== KONFIGURATION ==================== # ==================== KONFIGURATION ====================
class Config: class Config:
VERSION = "v1.3.4" # v1.3.4: FSM-Eignungsprüfung, Servicetechniker-Schätzung, Wiki-Vorschlag und 3s Pause integriert. VERSION = "v1.3.5" # v1.3.5: FSM-Prüfung mit flexiblem Parser, Servicetechniker-Explanation, Log-Datei, Warten bis Sheet-Update.
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"
@@ -26,21 +26,19 @@ class Config:
WIKIPEDIA_SEARCH_RESULTS = 5 WIKIPEDIA_SEARCH_RESULTS = 5
HTML_PARSER = "html.parser" HTML_PARSER = "html.parser"
# ==================== HELPER FUNCTIONS ==================== # Log-Datei vorbereiten
def retry_on_failure(func): if not os.path.exists("Log"):
def wrapper(*args, **kwargs): os.makedirs("Log")
for attempt in range(Config.MAX_RETRIES): LOG_FILE = os.path.join("Log", f"{datetime.now().strftime('%d-%m-%Y_%H-%M')}_{Config.VERSION.replace('.', '')}.txt")
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
def debug_print(message): def debug_print(message):
if Config.DEBUG: if Config.DEBUG:
print(f"[DEBUG] {message}") print(f"[DEBUG] {message}")
try:
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(f"[DEBUG] {message}\n")
except Exception as e:
print(f"[DEBUG] Log-Schreibfehler: {e}")
def clean_text(text): def clean_text(text):
if not text: if not text:
@@ -144,23 +142,18 @@ def evaluate_umsatz_chatgpt(company_name, wiki_umsatz):
temperature=0.0 temperature=0.0
) )
result = response.choices[0].message.content.strip() result = response.choices[0].message.content.strip()
debug_print(f"ChatGPT Antwort: '{result}'") debug_print(f"ChatGPT Umsatzschätzung: '{result}'")
try: try:
value = float(result.replace(',', '.')) value = float(result.replace(',', '.'))
return str(int(round(value))) return str(int(round(value)))
except Exception as conv_e: except Exception as conv_e:
debug_print(f"Fehler bei der Verarbeitung der ChatGPT-Antwort '{result}': {conv_e}") debug_print(f"Fehler bei der Verarbeitung der Umsatzschätzung '{result}': {conv_e}")
return result return result
except Exception as e: except Exception as e:
debug_print(f"Fehler beim Aufruf der ChatGPT API: {e}") debug_print(f"Fehler beim Aufruf der ChatGPT API für Umsatzschätzung: {e}")
return "k.A." return "k.A."
def validate_article_with_chatgpt(crm_data, wiki_data): def validate_article_with_chatgpt(crm_data, wiki_data):
"""
Aggregiert die CRM-Daten (Spalten B-J) und Wikipedia-Daten (Spalten L-Q)
als CSV-Text und übermittelt diesen an die ChatGPT-API, um zu validieren,
ob beide Datensätze zum selben Unternehmen gehören.
"""
crm_headers = "Firmenname;Website;Ort;Beschreibung;Aktuelle Branche;Beschreibung Branche extern;Anzahl Techniker;Umsatz (CRM);Anzahl Mitarbeiter (CRM)" crm_headers = "Firmenname;Website;Ort;Beschreibung;Aktuelle Branche;Beschreibung Branche extern;Anzahl Techniker;Umsatz (CRM);Anzahl Mitarbeiter (CRM)"
wiki_headers = "Wikipedia URL;Wikipedia Absatz;Wikipedia Branche;Wikipedia Umsatz;Wikipedia Mitarbeiter;Wikipedia Kategorien" wiki_headers = "Wikipedia URL;Wikipedia Absatz;Wikipedia Branche;Wikipedia Umsatz;Wikipedia Mitarbeiter;Wikipedia Kategorien"
prompt_text = ( prompt_text = (
@@ -199,13 +192,13 @@ def evaluate_fsm_suitability(company_name, company_data):
with open("api_key.txt", "r") as f: with open("api_key.txt", "r") as f:
api_key = f.read().strip() api_key = f.read().strip()
except Exception as e: except Exception as e:
debug_print(f"Fehler beim Lesen des API-Tokens: {e}") debug_print(f"Fehler beim Lesen des API-Tokens (FSM): {e}")
return {"suitability": "k.A.", "justification": "k.A."} return {"suitability": "k.A.", "justification": "k.A."}
openai.api_key = api_key openai.api_key = api_key
prompt = ( prompt = (
f"Bitte bewerte, ob das Unternehmen '{company_name}' für den Einsatz einer Field Service Management Lösung geeignet ist. " f"Bitte bewerte, ob das Unternehmen '{company_name}' für den Einsatz einer Field Service Management Lösung geeignet ist. "
"Berücksichtige, dass ein Unternehmen mit einem technischen Außendienst, idealerweise mit über 50 Technikern und " "Berücksichtige, dass ein Unternehmen mit einem technischen Außendienst, idealerweise mit über 50 Technikern und "
"Disponenten, die mit der Planung mobiler Ressourcen beschäftigt sind, als geeignet gilt. Nutze dabei vor allem verifizierte " "Disponenten, die mit der Planung mobiler Ressourcen beschäftigt sind, als geeignet gilt. Nutze dabei verifizierte "
"Wikipedia-Daten und deine eigene Einschätzung. Antworte ausschließlich mit 'Ja' oder 'Nein' und gib eine kurze Begründung." "Wikipedia-Daten und deine eigene Einschätzung. Antworte ausschließlich mit 'Ja' oder 'Nein' und gib eine kurze Begründung."
) )
try: try:
@@ -216,14 +209,24 @@ def evaluate_fsm_suitability(company_name, company_data):
) )
result = response.choices[0].message.content.strip() result = response.choices[0].message.content.strip()
debug_print(f"FSM-Eignungsantwort ChatGPT: '{result}'") debug_print(f"FSM-Eignungsantwort ChatGPT: '{result}'")
# Erwartetes Format: "Eignung: <Ja/Nein>\nBegründung: <...>" # Flexibler Parser: Falls keine Zeilen mit ":" vorhanden sind, nimm den ersten Satz.
suitability = "k.A." suitability = "k.A."
justification = "" justification = ""
for line in result.split("\n"): lines = result.split("\n")
if len(lines) == 1:
parts = result.split(" ", 1)
suitability = parts[0].strip()
justification = parts[1].strip() if len(parts) > 1 else ""
else:
for line in lines:
if line.lower().startswith("eignung:"): if line.lower().startswith("eignung:"):
suitability = line.split(":", 1)[1].strip() suitability = line.split(":", 1)[1].strip()
elif line.lower().startswith("begründung:"): elif line.lower().startswith("begründung:"):
justification = line.split(":", 1)[1].strip() justification = line.split(":", 1)[1].strip()
if suitability not in ["Ja", "Nein"]:
parts = result.split(" ", 1)
suitability = parts[0].strip()
justification = " ".join(result.split()[1:]).strip()
return {"suitability": suitability, "justification": justification} return {"suitability": suitability, "justification": justification}
except Exception as e: except Exception as e:
debug_print(f"Fehler beim Aufruf der ChatGPT API für FSM-Eignungsprüfung: {e}") debug_print(f"Fehler beim Aufruf der ChatGPT API für FSM-Eignungsprüfung: {e}")
@@ -235,13 +238,12 @@ def evaluate_servicetechnicians_estimate(company_name, company_data):
with open("api_key.txt", "r") as f: with open("api_key.txt", "r") as f:
api_key = f.read().strip() api_key = f.read().strip()
except Exception as e: except Exception as e:
debug_print(f"Fehler beim Lesen des API-Tokens: {e}") debug_print(f"Fehler beim Lesen des API-Tokens (Servicetechniker): {e}")
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 (insbesondere verifizierte Wikipedia-Daten) " f"Bitte schätze auf Basis öffentlich zugänglicher Informationen (vor allem verifizierte Wikipedia-Daten) "
f"die Anzahl der Servicetechniker des Unternehmens '{company_name}' ein. " f"die Anzahl der Servicetechniker des Unternehmens '{company_name}' ein. "
"Berücksichtige dabei Angaben zu Branche, Umsatz und Mitarbeiterzahl. "
"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'."
) )
@@ -253,12 +255,36 @@ def evaluate_servicetechnicians_estimate(company_name, company_data):
) )
result = response.choices[0].message.content.strip() result = response.choices[0].message.content.strip()
debug_print(f"Schätzung Servicetechniker ChatGPT: '{result}'") debug_print(f"Schätzung Servicetechniker ChatGPT: '{result}'")
# Wir erwarten eine Antwort, die exakt einer der vier Kategorien entspricht.
return result return result
except Exception as e: except Exception as e:
debug_print(f"Fehler beim Aufruf der ChatGPT API für Servicetechniker-Schätzung: {e}") debug_print(f"Fehler beim Aufruf der ChatGPT API für Servicetechniker-Schätzung: {e}")
return "k.A." return "k.A."
def evaluate_servicetechnicians_explanation(company_name, st_estimate, company_data):
try:
with open("api_key.txt", "r") as f:
api_key = f.read().strip()
except Exception as e:
debug_print(f"Fehler beim Lesen des API-Tokens (ST-Erklärung): {e}")
return "k.A."
openai.api_key = api_key
prompt = (
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."
)
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "system", "content": prompt}],
temperature=0.0
)
result = response.choices[0].message.content.strip()
debug_print(f"Servicetechniker-Erklärung ChatGPT: '{result}'")
return result
except Exception as e:
debug_print(f"Fehler beim Aufruf der ChatGPT API für Servicetechniker-Erklärung: {e}")
return "k.A."
def map_internal_technicians(value): def map_internal_technicians(value):
try: try:
num = int(value) num = int(value)
@@ -273,19 +299,30 @@ def map_internal_technicians(value):
else: else:
return ">500 Techniker" return ">500 Techniker"
# ==================== WARTEN BIS ZELLE AKTUALISIERT IST ====================
def wait_for_sheet_update(sheet, cell, expected_value, timeout=5):
start_time = time.time()
while time.time() - start_time < timeout:
try:
current_value = sheet.acell(cell).value
if current_value == expected_value:
return True
except Exception as e:
debug_print(f"Fehler beim Lesen von Zelle {cell}: {e}")
time.sleep(0.5)
return False
# ==================== BRANCHENABGLEICH PER CHATGPT ==================== # ==================== BRANCHENABGLEICH PER CHATGPT ====================
def load_target_branches(): def load_target_branches():
try: try:
with open("ziel_Branchenschema.csv", "r", encoding="utf-8") as csvfile: with open("ziel_Branchenschema.csv", "r", encoding="utf-8") as csvfile:
reader = csv.reader(csvfile) reader = csv.reader(csvfile)
# Annahme: Jede Zeile enthält in der ersten Spalte einen gültigen Branchen-Namen
branches = [row[0] for row in reader if row] branches = [row[0] for row in reader if row]
return branches return branches
except Exception as e: except Exception as e:
debug_print(f"Fehler beim Laden des Ziel-Branchenschemas: {e}") debug_print(f"Fehler beim Laden des Ziel-Branchenschemas: {e}")
return [] return []
# Fokusbranchen-Liste (wie definiert)
focus_branches = [ focus_branches = [
"Gutachter / Versicherungen > Baugutachter", "Gutachter / Versicherungen > Baugutachter",
"Gutachter / Versicherungen > Technische Gutachten", "Gutachter / Versicherungen > Technische Gutachten",
@@ -312,17 +349,16 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg
with open("api_key.txt", "r") as f: with open("api_key.txt", "r") as f:
api_key = f.read().strip() api_key = f.read().strip()
except Exception as e: except Exception as e:
debug_print(f"Fehler beim Lesen des API-Tokens: {e}") debug_print(f"Fehler beim Lesen des API-Tokens (Branche): {e}")
return {"branch": "k.A.", "consistency": "k.A.", "justification": "k.A."} return {"branch": "k.A.", "consistency": "k.A.", "justification": "k.A."}
openai.api_key = api_key openai.api_key = api_key
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"
f"CRM-Branche (Spalte F): {crm_branche}\n" f"CRM-Branche (Spalte F): {crm_branche}\n"
f"Branchenbeschreibung (Spalte G): {beschreibung}\n" f"Branchenbeschreibung (Spalte G): {beschreibung}\n"
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"
"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, also sowohl Fokusbranchen als auch weitere, z.B. 'Housing > Sozialbau Unternehmen'.\n"
"Das vollständige Ziel-Branchenschema lautet:\n" "Das vollständige Ziel-Branchenschema lautet:\n"
f"{target_branches_str}\n\n" f"{target_branches_str}\n\n"
"Falls das Unternehmen mehreren Branchen zugeordnet werden könnte, wähle bitte bevorzugt eine Branche aus der folgenden Fokusliste, sofern zutreffend:\n" "Falls das Unternehmen mehreren Branchen zugeordnet werden könnte, wähle bitte bevorzugt eine Branche aus der folgenden Fokusliste, sofern zutreffend:\n"
@@ -583,7 +619,6 @@ class WikipediaScraper:
@retry_on_failure @retry_on_failure
def search_company_article(self, company_name, website): def search_company_article(self, company_name, website):
# Zuerst prüfen: Gibt es in Spalte K bereits einen Wikipedia-Vorschlag? # Zuerst prüfen: Gibt es in Spalte K bereits einen Wikipedia-Vorschlag?
# (Dies wird im _process_single_row gehandhabt)
search_terms = self._generate_search_terms(company_name, website) search_terms = self._generate_search_terms(company_name, website)
for term in search_terms: for term in search_terms:
try: try:
@@ -625,19 +660,17 @@ class DataProcessor:
if i >= self.sheet_handler.get_start_index(): if i >= self.sheet_handler.get_start_index():
self._process_single_row(i, row) self._process_single_row(i, row)
def _process_single_row(self, row_num, row_data): def _process_single_row(self, row_num, row_data):
# CRM-Daten: Spalten B bis J (Indices 1 bis 9)
# Wikipedia-Daten: Spalten L bis Q (Indices 11 bis 16)
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}" wiki_update_range = f"K{row_num}:Q{row_num}"
chatgpt_range = f"AF{row_num}" chatgpt_range = f"AF{row_num}"
abgleich_range = f"AG{row_num}" abgleich_range = f"AG{row_num}"
valid_range = f"R{row_num}" # Konsistenzprüfung valid_range = f"R{row_num}"
dt_range = f"AH{row_num}" dt_range = f"AH{row_num}"
ver_range = f"AI{row_num}" ver_range = f"AI{row_num}"
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}")
# Prüfe: Ist in Spalte K (Index 10) bereits ein Wikipedia-Vorschlag hinterlegt? # Prüfen: Wikipedia-Vorschlag in Spalte K?
if len(row_data) > 10 and row_data[10].strip() not in ["", "k.A."]: if len(row_data) > 10 and row_data[10].strip() not in ["", "k.A."]:
wiki_url = row_data[10].strip() wiki_url = row_data[10].strip()
try: try:
@@ -646,28 +679,20 @@ class DataProcessor:
debug_print(f"Fehler beim Laden des vorgeschlagenen Wikipedia-Artikels: {e}") debug_print(f"Fehler beim Laden des vorgeschlagenen Wikipedia-Artikels: {e}")
article = self.wiki_scraper.search_company_article(company_name, website) article = self.wiki_scraper.search_company_article(company_name, website)
company_data = self.wiki_scraper.extract_company_data(article.url) if article else { company_data = self.wiki_scraper.extract_company_data(article.url) if article else {
'url': 'k.A.', 'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.',
'first_paragraph': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.',
'branche': 'k.A.',
'umsatz': 'k.A.',
'mitarbeiter': 'k.A.',
'categories': 'k.A.',
'full_infobox': 'k.A.' 'full_infobox': 'k.A.'
} }
else: else:
article = self.wiki_scraper.search_company_article(company_name, website) article = self.wiki_scraper.search_company_article(company_name, website)
company_data = self.wiki_scraper.extract_company_data(article.url) if article else { company_data = self.wiki_scraper.extract_company_data(article.url) if article else {
'url': 'k.A.', 'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.',
'first_paragraph': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'categories': 'k.A.',
'branche': 'k.A.',
'umsatz': 'k.A.',
'mitarbeiter': 'k.A.',
'categories': 'k.A.',
'full_infobox': 'k.A.' 'full_infobox': 'k.A.'
} }
wiki_values = [ wiki_values = [
row_data[10] if len(row_data) > 10 and row_data[10].strip() not in ["", "k.A."] else "k.A.", # Vorschlag Wiki URL row_data[10] if len(row_data) > 10 and row_data[10].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.'),
@@ -676,28 +701,25 @@ class DataProcessor:
company_data.get('categories', 'k.A.') company_data.get('categories', 'k.A.')
] ]
self.sheet_handler.sheet.update(values=[wiki_values], range_name=wiki_update_range) self.sheet_handler.sheet.update(values=[wiki_values], range_name=wiki_update_range)
time.sleep(3) # 3 Sekunden warten, bevor Validierung durchgeführt wird. # Warten, bis das Update im Sheet übernommen wurde (prüfe Zelle K{row_num})
wait_for_sheet_update(self.sheet_handler.sheet, f"K{row_num}", wiki_values[0])
time.sleep(3)
# Umsatz-Schätzung via ChatGPT (wie bisher) # Umsatz-Schätzung (Spalte AF soll "XX" erhalten)
wiki_umsatz = company_data.get('umsatz', 'k.A.') self.sheet_handler.sheet.update(values=[["XX"]], range_name=chatgpt_range)
if wiki_umsatz != "k.A.":
chatgpt_umsatz = evaluate_umsatz_chatgpt(company_name, wiki_umsatz)
else:
chatgpt_umsatz = "k.A."
self.sheet_handler.sheet.update(values=[[chatgpt_umsatz]], range_name=chatgpt_range)
# Umsatz-Abgleich (wie bisher) # Umsatz-Abgleich (Spalte AG)
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."
abgleich_result = compare_umsatz_values(crm_umsatz, company_data.get('umsatz', 'k.A.')) abgleich_result = compare_umsatz_values(crm_umsatz, company_data.get('umsatz', 'k.A.'))
self.sheet_handler.sheet.update(values=[[abgleich_result]], range_name=abgleich_range) self.sheet_handler.sheet.update(values=[[abgleich_result]], range_name=abgleich_range)
# Neuer Validierungsschritt (Firmenabgleich) unverändert # Validierung
crm_data = ";".join(row_data[1:10]) crm_data = ";".join(row_data[1:10])
wiki_data = ";".join(row_data[11:17]) wiki_data = ";".join(row_data[11:17])
valid_result = validate_article_with_chatgpt(crm_data, wiki_data) valid_result = validate_article_with_chatgpt(crm_data, wiki_data)
self.sheet_handler.sheet.update(values=[[valid_result]], range_name=valid_range) self.sheet_handler.sheet.update(values=[[valid_result]], range_name=valid_range)
# Neuer Branchenabgleich per ChatGPT: # Branchenabgleich
crm_branche = row_data[5] if len(row_data) > 5 else "k.A." crm_branche = row_data[5] if len(row_data) > 5 else "k.A."
beschreibung_branche = row_data[6] if len(row_data) > 6 else "k.A." beschreibung_branche = row_data[6] if len(row_data) > 6 else "k.A."
wiki_branche = company_data.get('branche', 'k.A.') wiki_branche = company_data.get('branche', 'k.A.')
@@ -710,31 +732,35 @@ class DataProcessor:
self.sheet_handler.sheet.update(values=[[branche_result["consistency"]]], range_name=branche_w_range) self.sheet_handler.sheet.update(values=[[branche_result["consistency"]]], range_name=branche_w_range)
self.sheet_handler.sheet.update(values=[[branche_result["justification"]]], range_name=branche_x_range) self.sheet_handler.sheet.update(values=[[branche_result["justification"]]], range_name=branche_x_range)
# Neue FSM-Eignungsprüfung: # FSM-Eignungsprüfung (Spalte Y/Z)
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}")
# Neue Servicetechniker-Schätzung (ohne Berücksichtigung interner Angaben in Spalte H) # Servicetechniker-Schätzung (Spalte AD) und Vergleich (Spalte AE)
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}")
# Vergleich mit interner Angabe (Spalte H)
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."
internal_category = map_internal_technicians(internal_value) if internal_value != "k.A." else "k.A." internal_category = map_internal_technicians(internal_value) if internal_value != "k.A." else "k.A."
if internal_category != "k.A." and st_estimate != internal_category: if internal_category != "k.A." and st_estimate != internal_category:
discrepancy = f"Interne Angabe: {internal_category} vs. ChatGPT: {st_estimate}" # Hole detaillierte Erklärung von ChatGPT, warum die Schätzung so ist.
explanation = evaluate_servicetechnicians_explanation(company_name, st_estimate, company_data)
discrepancy = explanation
else: else:
discrepancy = "ok" discrepancy = "ok"
self.sheet_handler.sheet.update(values=[[discrepancy]], range_name=f"AE{row_num}") self.sheet_handler.sheet.update(values=[[discrepancy]], range_name=f"AE{row_num}")
# Timestamp und Version schreiben # Spalten AF und AG sollen "XX" enthalten
self.sheet_handler.sheet.update(values=[["XX"]], range_name="AF" + str(row_num))
self.sheet_handler.sheet.update(values=[["XX"]], range_name="AG" + str(row_num))
# Timestamp und Version
current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.sheet_handler.sheet.update(values=[[current_dt]], range_name=dt_range) self.sheet_handler.sheet.update(values=[[current_dt]], range_name=dt_range)
self.sheet_handler.sheet.update(values=[[Config.VERSION]], range_name=ver_range) self.sheet_handler.sheet.update(values=[[Config.VERSION]], range_name=ver_range)
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.')}, " f"Branche: {company_data.get('branche', 'k.A.')}, Umsatz-Abgleich: {abgleich_result}, "
f"ChatGPT Umsatz: {chatgpt_umsatz}, Umsatz-Abgleich: {abgleich_result}, "
f"Validierung: {valid_result}, Branchenvorschlag: {branche_result['branch']}, " f"Validierung: {valid_result}, Branchenvorschlag: {branche_result['branch']}, "
f"FSM: {fsm_result['suitability']}, Servicetechniker-Schätzung: {st_estimate}") f"FSM: {fsm_result['suitability']}, Servicetechniker-Schätzung: {st_estimate}")
time.sleep(Config.RETRY_DELAY) time.sleep(Config.RETRY_DELAY)