v1.3.12: Neuer Modus 51 – Nur Verifizierung bis Spalte Y, Spalten um +1 verschoben

- Neuer Modus 51 implementiert, der ausschließlich die Wikipedia-Daten extrahiert und die Brancheneinordnung (bis Spalte Y) vornimmt.
- FSM- und Servicetechniker-Bewertungen werden in diesem Modus übersprungen.
- Alle Spalten wurden um +1 verschoben; Kurzform des Firmennamens ist nun in Spalte C.
- Update-Aufrufe wurden entsprechend angepasst.
This commit is contained in:
2025-04-04 08:46:48 +00:00
parent 86635c020f
commit 2f046f5af0

View File

@@ -14,7 +14,7 @@ import csv
# ==================== KONFIGURATION ==================== # ==================== KONFIGURATION ====================
class Config: class Config:
VERSION = "v1.3.11" # v1.3.11: Spalten um +1 verschoben, Kurzform in Spalte C; alle Referenzen angepasst. VERSION = "v1.3.12" # v1.3.12: Neuer Modus 51 implementiert (nur Verifizierung bis Spalte Y)
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"
@@ -199,6 +199,50 @@ def validate_article_with_chatgpt(crm_data, wiki_data):
debug_print(f"Fehler beim Validierungs-API-Aufruf: {e}") debug_print(f"Fehler beim Validierungs-API-Aufruf: {e}")
return "k.A." 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"
f"CRM-Branche: {crm_branche}\n"
f"Beschreibung Branche extern: {beschreibung}\n"
f"Wikipedia-Branche: {wiki_branche}\n"
f"Wikipedia-Kategorien: {wiki_kategorien}\n\n"
"Ordne das Unternehmen exakt einer der gültigen Branchen zu und gib aus:\n"
"Branche: <vorgeschlagene Branche>\n"
"Übereinstimmung: <ok oder X>\n"
"Begründung: <kurze Begründung, falls abweichend, ansonsten leer>"
)
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 (Branche): {e}")
return {"branch": "k.A.", "consistency": "k.A.", "justification": "k.A."}
openai.api_key = api_key
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "system", "content": prompt_text}],
temperature=0.0
)
result = response.choices[0].message.content.strip()
debug_print(f"Branchenabgleich ChatGPT Antwort: '{result}'")
branch = "k.A."
consistency = "k.A."
justification = ""
for line in result.split("\n"):
if line.lower().startswith("branche:"):
branch = line.split(":", 1)[1].strip()
elif line.lower().startswith("übereinstimmung:"):
consistency = line.split(":", 1)[1].strip()
elif line.lower().startswith("begründung:"):
justification = line.split(":", 1)[1].strip()
return {"branch": branch, "consistency": consistency, "justification": justification}
except Exception as e:
debug_print(f"Fehler beim Aufruf der ChatGPT API für Branchenabgleich: {e}")
return {"branch": "k.A.", "consistency": "k.A.", "justification": "k.A."}
def evaluate_fsm_suitability(company_name, company_data): def evaluate_fsm_suitability(company_name, company_data):
try: try:
with open("api_key.txt", "r") as f: with open("api_key.txt", "r") as f:
@@ -209,9 +253,7 @@ def evaluate_fsm_suitability(company_name, company_data):
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 " "Antworte ausschließlich mit 'Ja' oder 'Nein' und gib eine kurze Begründung."
"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."
) )
try: try:
response = openai.ChatCompletion.create( response = openai.ChatCompletion.create(
@@ -258,9 +300,7 @@ 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 die Anzahl der Servicetechniker des Unternehmens '{company_name}' in einer der folgenden Kategorien: "
f"die Anzahl der Servicetechniker des Unternehmens '{company_name}' ein. "
"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:
@@ -285,8 +325,7 @@ def evaluate_servicetechnicians_explanation(company_name, st_estimate, company_d
return "k.A." return "k.A."
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."
) )
try: try:
response = openai.ChatCompletion.create( response = openai.ChatCompletion.create(
@@ -335,10 +374,7 @@ def search_linkedin_contact(company_name, website, position_query):
except Exception as e: except Exception as e:
debug_print("Fehler beim Lesen des SerpAPI-Schlüssels: " + str(e)) debug_print("Fehler beim Lesen des SerpAPI-Schlüssels: " + str(e))
return None return None
# Nutze hier die Kurzform, falls vorhanden (Spalte C, Index 2); ansonsten Firmenname (Index 1) search_name = company_name # Hier kannst du auch die Kurzform verwenden, falls vorhanden.
search_name = company_name
if company_name == "" and website != "":
search_name = website
query = f'site:linkedin.com/in "{position_query}" "{search_name}"' query = f'site:linkedin.com/in "{position_query}" "{search_name}"'
debug_print(f"Erstelle LinkedIn-Query: {query}") debug_print(f"Erstelle LinkedIn-Query: {query}")
params = { params = {
@@ -382,7 +418,6 @@ def search_linkedin_contact(company_name, website, position_query):
debug_print(f"Fehler bei der SerpAPI-Suche: {e}") debug_print(f"Fehler bei der SerpAPI-Suche: {e}")
return None return None
# ==================== NEUE FUNKTION: ZÄHLEN DER LINKEDIN-KONTAKTE ====================
def count_linkedin_contacts(company_name, website, position_query): def count_linkedin_contacts(company_name, website, position_query):
try: try:
with open("serpApiKey.txt", "r") as f: with open("serpApiKey.txt", "r") as f:
@@ -412,35 +447,83 @@ def count_linkedin_contacts(company_name, website, position_query):
debug_print(f"Fehler bei der SerpAPI-Suche (Count): {e}") debug_print(f"Fehler bei der SerpAPI-Suche (Count): {e}")
return 0 return 0
# ==================== NEUER MODUS 6: CONTACT RESEARCH (via SerpAPI) ==================== # ==================== NEUER MODUS 51: VERIFIZIERUNG (Nur Wikipedia + Brancheneinordnung) ====================
def process_contact_research(): def process_verification_only():
debug_print("Starte Contact Research (Modus 6)...") debug_print("Starte Verifizierungs-Modus (Modus 51)...")
gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name( processor = DataProcessor()
Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])) # Wir nutzen als Kriterium, dass in Spalte Y (Begründung Abweichung Branche) noch kein Wert steht.
sh = gc.open_by_url(Config.SHEET_URL) for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2):
main_sheet = sh.sheet1 if len(row) <= 24 or row[24].strip() == "": # Spalte Y ist Index 24 (0-basiert)
data = main_sheet.get_all_values() processor._process_verification_row(i, row)
# Website ist nun in Spalte D (Index 3), Firmenname in Spalte B (Index 1) debug_print("Verifizierungs-Modus abgeschlossen.")
for i, row in enumerate(data[1:], start=2):
company_name = row[1] if len(row) > 1 else "" def _process_verification_row(self, row_num, row_data):
# Verwende die Kurzform (Spalte C, Index 2) für die Suche, wenn vorhanden, ansonsten Firmenname # In diesem Modus verarbeiten wir nur bis Spalte Y (Begründung Abweichung Branche)
search_name = row[2].strip() if len(row) > 2 and row[2].strip() not in ["", "k.A."] else company_name # Spalte B: Firmenname, Spalte C: Kurzform, Spalte D: Website, Spalte E: Ort, Spalte F: Beschreibung,
website = row[3] if len(row) > 3 else "" # Spalte G: Aktuelle Branche, Spalte H: Beschreibung Branche extern,
if not company_name or not website: # Spalte I: Anzahl Techniker CRM, J: Umsatz CRM, K: Anzahl Mitarbeiter CRM,
continue # Spalte L: Vorschlag Wiki URL, M: Wikipedia URL, N: Wikipedia Absatz, O: Wikipedia Branche,
count_service = count_linkedin_contacts(search_name, website, "Serviceleiter") # P: Wikipedia Umsatz, Q: Wikipedia Mitarbeiter, R: Wikipedia Kategorien,
count_it = count_linkedin_contacts(search_name, website, "IT-Leiter") # S: Konsistenzprüfung, T: Begründung bei Inkonsistenz, U: Vorschlag Wiki Artikel ChatGPT,
count_management = count_linkedin_contacts(search_name, website, "Geschäftsführer") # V: Begründung bei Abweichung, W: Vorschlag neue Branche, X: Konsistenzprüfung Branche,
count_disponent = count_linkedin_contacts(search_name, website, "Disponent") # Y: Begründung Abweichung Branche.
current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") company_name = row_data[1] if len(row_data) > 1 else ""
main_sheet.update(values=[[str(count_service)]], range_name=f"AH{i}") website = row_data[3] if len(row_data) > 3 else ""
main_sheet.update(values=[[str(count_it)]], range_name=f"AI{i}") current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
main_sheet.update(values=[[str(count_management)]], range_name=f"AJ{i}") # Wikipedia-Teil: Spalte L bis R
main_sheet.update(values=[[str(count_disponent)]], range_name=f"AK{i}") if len(row_data) > 11 and row_data[11].strip() not in ["", "k.A."]:
main_sheet.update(values=[[current_dt]], range_name=f"AL{i}") wiki_url = row_data[11].strip()
debug_print(f"Zeile {i}: Serviceleiter {count_service}, IT-Leiter {count_it}, Management {count_management}, Disponent {count_disponent} Contact Search Timestamp gesetzt.") try:
time.sleep(Config.RETRY_DELAY * 1.5) wiki_data = self.wiki_scraper.extract_company_data(wiki_url)
debug_print("Contact Research abgeschlossen.") 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)
wiki_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:
article = self.wiki_scraper.search_company_article(company_name, website)
wiki_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.'
}
wiki_values = [
row_data[11] if len(row_data) > 11 and row_data[11].strip() not in ["", "k.A."] else "k.A.",
wiki_data.get('url', 'k.A.'),
wiki_data.get('first_paragraph', 'k.A.'),
wiki_data.get('branche', 'k.A.'),
wiki_data.get('umsatz', 'k.A.'),
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: ALIGNMENT DEMO (für Hauptblatt und Contacts) ==================== # ==================== NEUER MODUS: ALIGNMENT DEMO (für Hauptblatt und Contacts) ====================
def alignment_demo_full(): def alignment_demo_full():
@@ -486,25 +569,10 @@ def alignment_demo(sheet):
"Spalte W (Vorschlag neue Branche)", "Spalte W (Vorschlag neue Branche)",
"Spalte X (Konsistenzprüfung Branche)", "Spalte X (Konsistenzprüfung Branche)",
"Spalte Y (Begründung Abweichung Branche)", "Spalte Y (Begründung Abweichung Branche)",
"Spalte Z (FSM Relevanz Ja / Nein)", "Spalte Z (Timestamp Verifizierung)",
"Spalte AA (Begründung für FSM Relevanz)", "Spalte AA (Version)"
"Spalte AB (Schätzung Anzahl Mitarbeiter)",
"Spalte AC (Konsistenzprüfung Mitarbeiterzahl)",
"Spalte AD (Begründung für Abweichung Mitarbeiterzahl)",
"Spalte AE (Einschätzung Anzahl Servicetechniker)",
"Spalte AF (Begründung bei Abweichung Anzahl Servicetechniker)",
"Spalte AG (Schätzung Umsatz ChatGPT)",
"Spalte AH (Begründung für Abweichung Umsatz)",
"Spalte AI (Serviceleiter gefunden)",
"Spalte AJ (IT-Leiter gefunden)",
"Spalte AK (Management gefunden)",
"Spalte AL (Disponent gefunden)",
"Spalte AM (Contact Search Timestamp)",
"Spalte AN (Wikipedia Timestamp)",
"Spalte AO (ChatGPT Timestamp)",
"Spalte AP (Version)"
] ]
header_range = "A11200:AP11200" header_range = "A11200:AA11200"
sheet.update(values=[new_headers], range_name=header_range) sheet.update(values=[new_headers], range_name=header_range)
print("Alignment-Demo abgeschlossen: Neue Spaltenüberschriften in Zeile 11200 geschrieben.") print("Alignment-Demo abgeschlossen: Neue Spaltenüberschriften in Zeile 11200 geschrieben.")
@@ -702,7 +770,7 @@ class GoogleSheetHandler:
self.sheet = gspread.authorize(creds).open_by_url(Config.SHEET_URL).sheet1 self.sheet = gspread.authorize(creds).open_by_url(Config.SHEET_URL).sheet1
self.sheet_values = self.sheet.get_all_values() self.sheet_values = self.sheet.get_all_values()
def get_start_index(self): def get_start_index(self):
# Wikipedia Timestamp ist jetzt in Spalte AN (Index 39) # Wir verwenden 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:]] 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) return next((i + 1 for i, v in enumerate(filled_n, start=1) if not str(v).strip()), len(filled_n) + 1)
@@ -723,15 +791,20 @@ class DataProcessor:
elif MODE == "4": elif MODE == "4":
processor = DataProcessor() processor = DataProcessor()
for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2): for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2):
# Nur Zeilen ohne Wikipedia-Timestamp (Spalte AN, Index 39)
if len(row) <= 39 or row[39].strip() == "": if len(row) <= 39 or row[39].strip() == "":
processor._process_single_row(i, row, process_wiki=True, process_chatgpt=False) processor._process_single_row(i, row, process_wiki=True, process_chatgpt=False)
elif MODE == "5": elif MODE == "5":
processor = DataProcessor() processor = DataProcessor()
# Nur Zeilen ohne ChatGPT-Timestamp (Spalte AO, Index 40)
for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2): for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2):
if len(row) <= 40 or row[40].strip() == "": if len(row) <= 40 or row[40].strip() == "":
processor._process_single_row(i, row, process_wiki=False, process_chatgpt=True) 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)
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}")
@@ -744,17 +817,15 @@ class DataProcessor:
self._process_single_row(i, row) self._process_single_row(i, row)
rows_processed += 1 rows_processed += 1
def _process_single_row(self, row_num, row_data, force_all=False, process_wiki=True, process_chatgpt=True): def _process_single_row(self, row_num, row_data, force_all=False, process_wiki=True, process_chatgpt=True):
# Spalte B: Firmenname, Spalte C: Kurzform, Spalte D: Website # Dies ist die vollständige Verarbeitung (wie in v1.3.11)
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[3] if len(row_data) > 3 else "" website = row_data[3] if len(row_data) > 3 else ""
wiki_update_range = f"L{row_num}:R{row_num}" # Vorschlag Wiki URL bis Wikipedia Kategorien (Spalte L bis R) wiki_update_range = f"L{row_num}:R{row_num}"
dt_wiki_range = f"AN{row_num}" # Wikipedia Timestamp (Spalte AN) dt_wiki_range = f"AN{row_num}"
dt_chat_range = f"AO{row_num}" # ChatGPT Timestamp (Spalte AO) dt_chat_range = f"AO{row_num}"
ver_range = f"AP{row_num}" # Version (Spalte AP) ver_range = f"AP{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}")
current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Wikipedia-Teil
if force_all or process_wiki: if force_all or 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) > 11 and row_data[11].strip() not in ["", "k.A."]: if len(row_data) > 11 and row_data[11].strip() not in ["", "k.A."]:
@@ -789,17 +860,12 @@ 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-Teil
if force_all or process_chatgpt: if force_all or process_chatgpt:
if len(row_data) <= 40 or row_data[40].strip() == "": if len(row_data) <= 40 or row_data[40].strip() == "":
# Umsatz CRM ist nun in Spalte J (Index 9), Anzahl Mitarbeiter in Spalte K (Index 10)
crm_umsatz = row_data[9] if len(row_data) > 9 else "k.A." crm_umsatz = row_data[9] if len(row_data) > 9 else "k.A."
abgleich_result = compare_umsatz_values(crm_umsatz, wiki_data.get('umsatz', 'k.A.') if 'wiki_data' in locals() else "k.A.") abgleich_result = compare_umsatz_values(crm_umsatz, wiki_data.get('umsatz', 'k.A.') if 'wiki_data' in locals() else "k.A.")
self.sheet_handler.sheet.update(values=[[abgleich_result]], range_name=f"AG{row_num}") self.sheet_handler.sheet.update(values=[[abgleich_result]], range_name=f"AG{row_num}")
# CRM-Daten: von Spalte B bis K (Indices 1 bis 10)
crm_data = ";".join(row_data[1:11]) crm_data = ";".join(row_data[1:11])
# Wiki-Daten: von Spalte L bis R (Indices 11 bis 18)
wiki_data_str = ";".join(row_data[11:18]) wiki_data_str = ";".join(row_data[11:18])
valid_result = validate_article_with_chatgpt(crm_data, wiki_data_str) valid_result = validate_article_with_chatgpt(crm_data, wiki_data_str)
self.sheet_handler.sheet.update(values=[[valid_result]], range_name=f"R{row_num}") self.sheet_handler.sheet.update(values=[[valid_result]], range_name=f"R{row_num}")
@@ -808,7 +874,7 @@ class DataProcessor:
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}")
st_estimate = evaluate_servicetechnicians_estimate(company_name, wiki_data if 'wiki_data' in locals() else {}) st_estimate = evaluate_servicetechnicians_estimate(company_name, wiki_data if 'wiki_data' in locals() else {})
self.sheet_handler.sheet.update(values=[[st_estimate]], range_name=f"AE{row_num}") self.sheet_handler.sheet.update(values=[[st_estimate]], range_name=f"AE{row_num}")
internal_value = row_data[8] if len(row_data) > 8 else "k.A." # Anzahl Techniker CRM in Spalte I (Index 8) internal_value = row_data[8] if len(row_data) > 8 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:
explanation = evaluate_servicetechnicians_explanation(company_name, st_estimate, wiki_data if 'wiki_data' in locals() else {}) explanation = evaluate_servicetechnicians_explanation(company_name, st_estimate, wiki_data if 'wiki_data' in locals() else {})
@@ -819,10 +885,8 @@ class DataProcessor:
self.sheet_handler.sheet.update(values=[[current_dt]], range_name=dt_chat_range) self.sheet_handler.sheet.update(values=[[current_dt]], range_name=dt_chat_range)
else: else:
debug_print(f"Zeile {row_num}: ChatGPT-Timestamp bereits gesetzt überspringe ChatGPT-Auswertung.") debug_print(f"Zeile {row_num}: ChatGPT-Timestamp bereits gesetzt überspringe ChatGPT-Auswertung.")
self.sheet_handler.sheet.update(values=[[current_dt]], range_name=ver_range)
# Aktualisiere letzten Timestamp und Version (Spalte AP) self.sheet_handler.sheet.update(values=[[Config.VERSION]], range_name=ver_range)
self.sheet_handler.sheet.update(values=[[current_dt]], range_name=f"AP{row_num}")
self.sheet_handler.sheet.update(values=[[Config.VERSION]], range_name=f"AP{row_num}")
debug_print(f"✅ Aktualisiert: URL: {(wiki_data.get('url', 'k.A.') if 'wiki_data' in locals() else 'k.A.')}, " debug_print(f"✅ Aktualisiert: URL: {(wiki_data.get('url', 'k.A.') if 'wiki_data' in locals() else 'k.A.')}, "
f"Branche: {(wiki_data.get('branche', 'k.A.') if 'wiki_data' in locals() else 'k.A.')}, " f"Branche: {(wiki_data.get('branche', 'k.A.') if 'wiki_data' in locals() else 'k.A.')}, "
f"Umsatz-Abgleich: {abgleich_result if 'abgleich_result' in locals() else 'k.A.'}, " f"Umsatz-Abgleich: {abgleich_result if 'abgleich_result' in locals() else 'k.A.'}, "
@@ -830,7 +894,7 @@ class DataProcessor:
f"FSM: {fsm_result['suitability'] if 'fsm_result' in locals() else 'k.A.'}, " f"FSM: {fsm_result['suitability'] if 'fsm_result' in locals() else 'k.A.'}, "
f"Servicetechniker-Schätzung: {st_estimate if 'st_estimate' in locals() else 'k.A.'}") f"Servicetechniker-Schätzung: {st_estimate if 'st_estimate' in locals() else 'k.A.'}")
time.sleep(Config.RETRY_DELAY) time.sleep(Config.RETRY_DELAY)
# ==================== NEUER MODUS 6: CONTACT RESEARCH (via SerpAPI) ==================== # ==================== NEUER MODUS 6: CONTACT RESEARCH (via SerpAPI) ====================
def process_contact_research(): def process_contact_research():
debug_print("Starte Contact Research (Modus 6)...") debug_print("Starte Contact Research (Modus 6)...")
@@ -839,10 +903,8 @@ def process_contact_research():
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()
# Website ist nun in Spalte D (Index 3); Firmenname in Spalte B; Kurzform in Spalte C
for i, row in enumerate(data[1:], start=2): for i, row in enumerate(data[1:], start=2):
company_name = row[1] if len(row) > 1 else "" company_name = row[1] if len(row) > 1 else ""
# Verwende Kurzform (Spalte C, Index 2) falls vorhanden, sonst Firmenname
search_name = row[2].strip() if len(row) > 2 and row[2].strip() not in ["", "k.A."] else company_name search_name = row[2].strip() if len(row) > 2 and row[2].strip() not in ["", "k.A."] else company_name
website = row[3] if len(row) > 3 else "" website = row[3] if len(row) > 3 else ""
if not company_name or not website: if not company_name or not website:
@@ -852,31 +914,15 @@ def process_contact_research():
count_management = count_linkedin_contacts(search_name, website, "Geschäftsführer") count_management = count_linkedin_contacts(search_name, website, "Geschäftsführer")
count_disponent = count_linkedin_contacts(search_name, website, "Disponent") count_disponent = count_linkedin_contacts(search_name, website, "Disponent")
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=[[str(count_service)]], range_name=f"AI{i}") # Neu: Spalte AI (Serviceleiter gefunden) vorher AH -> jetzt AI main_sheet.update(values=[[str(count_service)]], range_name=f"AI{i}")
main_sheet.update(values=[[str(count_it)]], range_name=f"AJ{i}") # IT-Leiter gefunden in Spalte AJ main_sheet.update(values=[[str(count_it)]], range_name=f"AJ{i}")
main_sheet.update(values=[[str(count_management)]], range_name=f"AK{i}") # Management gefunden in Spalte AK main_sheet.update(values=[[str(count_management)]], range_name=f"AK{i}")
main_sheet.update(values=[[str(count_disponent)]], range_name=f"AL{i}") # Disponent gefunden in Spalte AL main_sheet.update(values=[[str(count_disponent)]], range_name=f"AL{i}")
main_sheet.update(values=[[current_dt]], range_name=f"AM{i}") # Contact Search Timestamp in Spalte AM main_sheet.update(values=[[current_dt]], range_name=f"AM{i}")
debug_print(f"Zeile {i}: Serviceleiter {count_service}, IT-Leiter {count_it}, Management {count_management}, Disponent {count_disponent} Contact Search Timestamp gesetzt.") debug_print(f"Zeile {i}: Serviceleiter {count_service}, IT-Leiter {count_it}, Management {count_management}, Disponent {count_disponent} Contact Search Timestamp gesetzt.")
time.sleep(Config.RETRY_DELAY * 1.5) time.sleep(Config.RETRY_DELAY * 1.5)
debug_print("Contact Research abgeschlossen.") debug_print("Contact Research abgeschlossen.")
# ==================== NEUER MODUS: ALIGNMENT DEMO (für Hauptblatt und Contacts) ====================
def alignment_demo_full():
alignment_demo(GoogleSheetHandler().sheet)
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)
try:
contacts_sheet = sh.worksheet("Contacts")
except gspread.exceptions.WorksheetNotFound:
contacts_sheet = sh.add_worksheet(title="Contacts", rows="1000", cols="10")
header = ["Firmenname", "Website", "Kurzform", "Vorname", "Nachname", "Position", "Anrede", "E-Mail"]
contacts_sheet.update(values=[header], range_name="A1:H1")
debug_print("Neues Blatt 'Contacts' erstellt und Header eingetragen.")
alignment_demo(contacts_sheet)
debug_print("Alignment-Demo für Hauptblatt und Contacts abgeschlossen.")
# ==================== NEUER MODUS: CONTACTS (LinkedIn) ==================== # ==================== NEUER MODUS: CONTACTS (LinkedIn) ====================
def process_contacts(): def process_contacts():
debug_print("Starte LinkedIn-Kontaktsuche...") debug_print("Starte LinkedIn-Kontaktsuche...")
@@ -895,7 +941,6 @@ def process_contacts():
positions = ["Serviceleiter", "IT-Leiter", "Leiter After Sales", "Leiter Einsatzplanung"] positions = ["Serviceleiter", "IT-Leiter", "Leiter After Sales", "Leiter Einsatzplanung"]
new_rows = [] new_rows = []
for idx, row in enumerate(data[1:], start=2): for idx, row in enumerate(data[1:], start=2):
# Firmenname in Spalte B (Index 1), Kurzform in Spalte C (Index 2), Website in Spalte D (Index 3)
company_name = row[1] if len(row) > 1 else "" company_name = row[1] if len(row) > 1 else ""
search_name = row[2].strip() if len(row) > 2 and row[2].strip() not in ["", "k.A."] else company_name search_name = row[2].strip() if len(row) > 2 and row[2].strip() not in ["", "k.A."] else company_name
website = row[3] if len(row) > 3 else "" website = row[3] if len(row) > 3 else ""
@@ -923,7 +968,7 @@ def process_contacts():
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--mode", type=str, default="1", help="Modus: 1, 2, 3, 4, 5, 6 oder 7") parser.add_argument("--mode", type=str, default="1", help="Modus: 1, 2, 3, 4, 5, 6, 7 oder 51")
parser.add_argument("--num_rows", type=int, default=0, help="Anzahl der zu bearbeitenden Zeilen (nur für Modus 1)") parser.add_argument("--num_rows", type=int, default=0, help="Anzahl der zu bearbeitenden Zeilen (nur für Modus 1)")
args = parser.parse_args() args = parser.parse_args()
@@ -949,4 +994,6 @@ if __name__ == "__main__":
process_contact_research() process_contact_research()
elif MODE == "7": elif MODE == "7":
process_contacts() process_contacts()
print(f"\n✅ Auswertung abgeschlossen ({Config.VERSION})") elif MODE == "51":
process_verification_only()
print(f"\n✅ Auswertung abgeschlossen ({Config.VERSION})")