From 43c23b50d85e81a37ed0a2f4ea58431a4e14dc89 Mon Sep 17 00:00:00 2001 From: Floke Date: Wed, 2 Apr 2025 13:27:37 +0000 Subject: [PATCH] =?UTF-8?q?v1.3.4=20Erweiterung:=20FSM-Pr=C3=BCfung,=20Ser?= =?UTF-8?q?vicetechniker-Sch=C3=A4tzung,=20Wiki-Vorschlag=20&=203s=20Pause?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bevorzugter Wikipedia-Artikel aus Spalte K wird genutzt. Nach dem Schreiben der Wiki-Daten erfolgt eine 3-Sekunden-Pause. Neue Funktion zur FSM-Eignungsprüfung (Spalte Y/Z) integriert. Neue Servicetechniker-Schätzung (Spalte AD) und Vergleich mit interner Angabe (Spalte AE) hinzugefügt. Versionsnummer wurde auf v1.3.4 aktualisiert. --- brancheneinstufung.py | 140 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 16 deletions(-) diff --git a/brancheneinstufung.py b/brancheneinstufung.py index f49ff86f..c0696513 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -14,7 +14,7 @@ import csv # ==================== KONFIGURATION ==================== class Config: - VERSION = "v1.3.3" # v1.3.3: Branchenabgleich per ChatGPT integriert, Ziel-Branchenschema inkl. aller gültigen Branchen eingebunden. + VERSION = "v1.3.4" # v1.3.4: FSM-Eignungsprüfung, Servicetechniker-Schätzung, Wiki-Vorschlag und 3s Pause integriert. LANG = "de" CREDENTIALS_FILE = "service_account.json" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" @@ -193,6 +193,86 @@ def validate_article_with_chatgpt(crm_data, wiki_data): debug_print(f"Fehler beim Validierungs-API-Aufruf: {e}") return "k.A." +# ==================== NEUE FUNKTION: FSM-EIGNUNGSPRÜFUNG ==================== +def evaluate_fsm_suitability(company_name, 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: {e}") + return {"suitability": "k.A.", "justification": "k.A."} + openai.api_key = api_key + prompt = ( + 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 " + "Disponenten, die mit der Planung mobiler Ressourcen beschäftigt sind, als geeignet gilt. Nutze dabei vor allem verifizierte " + "Wikipedia-Daten und deine eigene Einschätzung. Antworte ausschließlich mit 'Ja' oder 'Nein' und gib eine kurze Begründung." + ) + 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"FSM-Eignungsantwort ChatGPT: '{result}'") + # Erwartetes Format: "Eignung: \nBegründung: <...>" + suitability = "k.A." + justification = "" + for line in result.split("\n"): + if line.lower().startswith("eignung:"): + suitability = line.split(":", 1)[1].strip() + elif line.lower().startswith("begründung:"): + justification = line.split(":", 1)[1].strip() + return {"suitability": suitability, "justification": justification} + except Exception as e: + debug_print(f"Fehler beim Aufruf der ChatGPT API für FSM-Eignungsprüfung: {e}") + return {"suitability": "k.A.", "justification": "k.A."} + +# ==================== NEUE FUNKTION: SCHÄTZUNG DER ANZAHL SERVICETECHNIKER ==================== +def evaluate_servicetechnicians_estimate(company_name, 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: {e}") + return "k.A." + openai.api_key = api_key + prompt = ( + f"Bitte schätze auf Basis öffentlich zugänglicher Informationen (insbesondere verifizierte Wikipedia-Daten) " + 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: " + "'<50 Techniker', '>100 Techniker', '>200 Techniker', '>500 Techniker'." + ) + 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"Schätzung Servicetechniker ChatGPT: '{result}'") + # Wir erwarten eine Antwort, die exakt einer der vier Kategorien entspricht. + return result + except Exception as e: + debug_print(f"Fehler beim Aufruf der ChatGPT API für Servicetechniker-Schätzung: {e}") + return "k.A." + +def map_internal_technicians(value): + try: + num = int(value) + except Exception: + return "k.A." + if num < 50: + return "<50 Techniker" + elif num < 100: + return ">100 Techniker" + elif num < 200: + return ">200 Techniker" + else: + return ">500 Techniker" + # ==================== BRANCHENABGLEICH PER CHATGPT ==================== def load_target_branches(): try: @@ -225,7 +305,6 @@ focus_branches = [ ] def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien): - # Lade das Ziel-Branchenschema target_branches = load_target_branches() target_branches_str = "\n".join(target_branches) focus_branches_str = "\n".join(focus_branches) @@ -243,7 +322,7 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg f"Branchenbeschreibung (Spalte G): {beschreibung}\n" f"Wikipedia-Branche (Spalte N): {wiki_branche}\n" f"Wikipedia-Kategorien (Spalte Q): {wiki_kategorien}\n\n" - "Das Ziel-Branchenschema umfasst ALLE gültigen Branchen, also sowohl Fokusbranchen als auch weitere Branchen (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" 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" @@ -266,7 +345,6 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg ) result = response.choices[0].message.content.strip() debug_print(f"Branchenabgleich ChatGPT Antwort: '{result}'") - # Parsing der Antwort branch = "k.A." consistency = "k.A." justification = "" @@ -504,6 +582,8 @@ class WikipediaScraper: } @retry_on_failure def search_company_article(self, company_name, website): + # 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) for term in search_terms: try: @@ -557,11 +637,26 @@ class DataProcessor: ver_range = f"AI{row_num}" print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Verarbeite Zeile {row_num}: {company_name}") - article = self.wiki_scraper.search_company_article(company_name, website) - if article: - company_data = self.wiki_scraper.extract_company_data(article.url) + # Prüfe: Ist in Spalte K (Index 10) bereits ein Wikipedia-Vorschlag hinterlegt? + if len(row_data) > 10 and row_data[10].strip() not in ["", "k.A."]: + wiki_url = row_data[10].strip() + try: + company_data = self.wiki_scraper.extract_company_data(wiki_url) + except Exception as e: + debug_print(f"Fehler beim Laden des vorgeschlagenen Wikipedia-Artikels: {e}") + article = self.wiki_scraper.search_company_article(company_name, website) + company_data = self.wiki_scraper.extract_company_data(article.url) if article else { + 'url': 'k.A.', + 'first_paragraph': 'k.A.', + 'branche': 'k.A.', + 'umsatz': 'k.A.', + 'mitarbeiter': 'k.A.', + 'categories': 'k.A.', + 'full_infobox': 'k.A.' + } else: - company_data = { + article = self.wiki_scraper.search_company_article(company_name, website) + company_data = self.wiki_scraper.extract_company_data(article.url) if article else { 'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.', @@ -572,7 +667,7 @@ class DataProcessor: } wiki_values = [ - "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.", # Vorschlag Wiki URL company_data.get('url', 'k.A.'), company_data.get('first_paragraph', 'k.A.'), company_data.get('branche', 'k.A.'), @@ -581,7 +676,7 @@ class DataProcessor: company_data.get('categories', 'k.A.') ] self.sheet_handler.sheet.update(values=[wiki_values], range_name=wiki_update_range) - time.sleep(1) + time.sleep(3) # 3 Sekunden warten, bevor Validierung durchgeführt wird. # Umsatz-Schätzung via ChatGPT (wie bisher) wiki_umsatz = company_data.get('umsatz', 'k.A.') @@ -608,11 +703,6 @@ class DataProcessor: wiki_branche = company_data.get('branche', 'k.A.') wiki_kategorien = company_data.get('categories', 'k.A.') branche_result = evaluate_branche_chatgpt(crm_branche, beschreibung_branche, wiki_branche, wiki_kategorien) - - # Update der Spalten: - # Spalte V: Vorschlag neue Branche - # Spalte W: Konsistenzprüfung Branche (ok, wenn Übereinstimmung, sonst X) - # Spalte X: Begründung bei Abweichung branche_v_range = f"V{row_num}" branche_w_range = f"W{row_num}" branche_x_range = f"X{row_num}" @@ -620,6 +710,23 @@ class DataProcessor: 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) + # Neue FSM-Eignungsprüfung: + 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["justification"]]], range_name=f"Z{row_num}") + + # Neue Servicetechniker-Schätzung (ohne Berücksichtigung interner Angaben in Spalte H) + st_estimate = evaluate_servicetechnicians_estimate(company_name, company_data) + 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_category = map_internal_technicians(internal_value) if internal_value != "k.A." else "k.A." + if internal_category != "k.A." and st_estimate != internal_category: + discrepancy = f"Interne Angabe: {internal_category} vs. ChatGPT: {st_estimate}" + else: + discrepancy = "ok" + self.sheet_handler.sheet.update(values=[[discrepancy]], range_name=f"AE{row_num}") + # Timestamp und Version schreiben current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.sheet_handler.sheet.update(values=[[current_dt]], range_name=dt_range) @@ -628,7 +735,8 @@ class DataProcessor: print(f"✅ Aktualisiert: URL: {company_data.get('url', 'k.A.')}, " f"Branche: {company_data.get('branche', 'k.A.')}, " 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}") time.sleep(Config.RETRY_DELAY) if __name__ == "__main__":