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 ====================
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"
CREDENTIALS_FILE = "service_account.json"
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}")
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):
try:
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
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 verifizierte "
"Wikipedia-Daten und deine eigene Einschätzung. Antworte ausschließlich mit 'Ja' oder 'Nein' und gib eine kurze Begründung."
"Antworte ausschließlich mit 'Ja' oder 'Nein' und gib eine kurze Begründung."
)
try:
response = openai.ChatCompletion.create(
@@ -258,9 +300,7 @@ def evaluate_servicetechnicians_estimate(company_name, company_data):
return "k.A."
openai.api_key = api_key
prompt = (
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. "
"Gib die Antwort ausschließlich in einer der folgenden Kategorien aus: "
f"Bitte schätze die Anzahl der Servicetechniker des Unternehmens '{company_name}' in einer der folgenden Kategorien: "
"'<50 Techniker', '>100 Techniker', '>200 Techniker', '>500 Techniker'."
)
try:
@@ -285,8 +325,7 @@ def evaluate_servicetechnicians_explanation(company_name, st_estimate, company_d
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."
f"Bitte erkläre, warum du für das Unternehmen '{company_name}' die Anzahl der Servicetechniker als '{st_estimate}' geschätzt hast."
)
try:
response = openai.ChatCompletion.create(
@@ -335,10 +374,7 @@ def search_linkedin_contact(company_name, website, position_query):
except Exception as e:
debug_print("Fehler beim Lesen des SerpAPI-Schlüssels: " + str(e))
return None
# Nutze hier die Kurzform, falls vorhanden (Spalte C, Index 2); ansonsten Firmenname (Index 1)
search_name = company_name
if company_name == "" and website != "":
search_name = website
search_name = company_name # Hier kannst du auch die Kurzform verwenden, falls vorhanden.
query = f'site:linkedin.com/in "{position_query}" "{search_name}"'
debug_print(f"Erstelle LinkedIn-Query: {query}")
params = {
@@ -382,7 +418,6 @@ def search_linkedin_contact(company_name, website, position_query):
debug_print(f"Fehler bei der SerpAPI-Suche: {e}")
return None
# ==================== NEUE FUNKTION: ZÄHLEN DER LINKEDIN-KONTAKTE ====================
def count_linkedin_contacts(company_name, website, position_query):
try:
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}")
return 0
# ==================== NEUER MODUS 6: CONTACT RESEARCH (via SerpAPI) ====================
def process_contact_research():
debug_print("Starte Contact Research (Modus 6)...")
gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name(
Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"]))
sh = gc.open_by_url(Config.SHEET_URL)
main_sheet = sh.sheet1
data = main_sheet.get_all_values()
# Website ist nun in Spalte D (Index 3), Firmenname in Spalte B (Index 1)
for i, row in enumerate(data[1:], start=2):
company_name = row[1] if len(row) > 1 else ""
# Verwende die Kurzform (Spalte C, Index 2) für die Suche, wenn vorhanden, ansonsten Firmenname
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 ""
if not company_name or not website:
continue
count_service = count_linkedin_contacts(search_name, website, "Serviceleiter")
count_it = count_linkedin_contacts(search_name, website, "IT-Leiter")
count_management = count_linkedin_contacts(search_name, website, "Geschäftsführer")
count_disponent = count_linkedin_contacts(search_name, website, "Disponent")
current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
main_sheet.update(values=[[str(count_service)]], range_name=f"AH{i}")
main_sheet.update(values=[[str(count_it)]], range_name=f"AI{i}")
main_sheet.update(values=[[str(count_management)]], range_name=f"AJ{i}")
main_sheet.update(values=[[str(count_disponent)]], range_name=f"AK{i}")
main_sheet.update(values=[[current_dt]], range_name=f"AL{i}")
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)
debug_print("Contact Research abgeschlossen.")
# ==================== NEUER MODUS 51: VERIFIZIERUNG (Nur Wikipedia + Brancheneinordnung) ====================
def process_verification_only():
debug_print("Starte Verifizierungs-Modus (Modus 51)...")
processor = DataProcessor()
# Wir nutzen als Kriterium, dass in Spalte Y (Begründung Abweichung Branche) noch kein Wert steht.
for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2):
if len(row) <= 24 or row[24].strip() == "": # Spalte Y ist Index 24 (0-basiert)
processor._process_verification_row(i, row)
debug_print("Verifizierungs-Modus abgeschlossen.")
def _process_verification_row(self, row_num, row_data):
# In diesem Modus verarbeiten wir nur bis Spalte Y (Begründung Abweichung Branche)
# Spalte B: Firmenname, Spalte C: Kurzform, Spalte D: Website, Spalte E: Ort, Spalte F: Beschreibung,
# Spalte G: Aktuelle Branche, Spalte H: Beschreibung Branche extern,
# Spalte I: Anzahl Techniker CRM, J: Umsatz CRM, K: Anzahl Mitarbeiter CRM,
# Spalte L: Vorschlag Wiki URL, M: Wikipedia URL, N: Wikipedia Absatz, O: Wikipedia Branche,
# P: Wikipedia Umsatz, Q: Wikipedia Mitarbeiter, R: Wikipedia Kategorien,
# S: Konsistenzprüfung, T: Begründung bei Inkonsistenz, U: Vorschlag Wiki Artikel ChatGPT,
# V: Begründung bei Abweichung, W: Vorschlag neue Branche, X: Konsistenzprüfung Branche,
# Y: Begründung Abweichung Branche.
company_name = row_data[1] if len(row_data) > 1 else ""
website = row_data[3] if len(row_data) > 3 else ""
current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Wikipedia-Teil: Spalte L bis R
if len(row_data) > 11 and row_data[11].strip() not in ["", "k.A."]:
wiki_url = row_data[11].strip()
try:
wiki_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)
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) ====================
def alignment_demo_full():
@@ -486,25 +569,10 @@ def alignment_demo(sheet):
"Spalte W (Vorschlag neue Branche)",
"Spalte X (Konsistenzprüfung Branche)",
"Spalte Y (Begründung Abweichung Branche)",
"Spalte Z (FSM Relevanz Ja / Nein)",
"Spalte AA (Begründung für FSM Relevanz)",
"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)"
"Spalte Z (Timestamp Verifizierung)",
"Spalte AA (Version)"
]
header_range = "A11200:AP11200"
header_range = "A11200:AA11200"
sheet.update(values=[new_headers], range_name=header_range)
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_values = self.sheet.get_all_values()
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:]]
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":
processor = DataProcessor()
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() == "":
processor._process_single_row(i, row, process_wiki=True, process_chatgpt=False)
elif MODE == "5":
processor = DataProcessor()
# Nur Zeilen ohne ChatGPT-Timestamp (Spalte AO, Index 40)
for i, row in enumerate(processor.sheet_handler.sheet_values[1:], start=2):
if len(row) <= 40 or row[40].strip() == "":
processor._process_single_row(i, row, process_wiki=False, process_chatgpt=True)
elif MODE == "51":
# 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:
start_index = self.sheet_handler.get_start_index()
print(f"Starte bei Zeile {start_index+1}")
@@ -744,17 +817,15 @@ class DataProcessor:
self._process_single_row(i, row)
rows_processed += 1
def _process_single_row(self, row_num, row_data, force_all=False, process_wiki=True, process_chatgpt=True):
# 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 ""
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)
dt_wiki_range = f"AN{row_num}" # Wikipedia Timestamp (Spalte AN)
dt_chat_range = f"AO{row_num}" # ChatGPT Timestamp (Spalte AO)
ver_range = f"AP{row_num}" # Version (Spalte AP)
wiki_update_range = f"L{row_num}:R{row_num}"
dt_wiki_range = f"AN{row_num}"
dt_chat_range = f"AO{row_num}"
ver_range = f"AP{row_num}"
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")
# Wikipedia-Teil
if force_all or process_wiki:
if len(row_data) <= 39 or row_data[39].strip() == "":
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)
else:
debug_print(f"Zeile {row_num}: Wikipedia-Timestamp bereits gesetzt überspringe Wiki-Auswertung.")
# ChatGPT-Teil
if force_all or process_chatgpt:
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."
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}")
# CRM-Daten: von Spalte B bis K (Indices 1 bis 10)
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])
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}")
@@ -808,7 +874,7 @@ class DataProcessor:
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 {})
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."
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 {})
@@ -819,10 +885,8 @@ class DataProcessor:
self.sheet_handler.sheet.update(values=[[current_dt]], range_name=dt_chat_range)
else:
debug_print(f"Zeile {row_num}: ChatGPT-Timestamp bereits gesetzt überspringe ChatGPT-Auswertung.")
# Aktualisiere letzten Timestamp und Version (Spalte AP)
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}")
self.sheet_handler.sheet.update(values=[[current_dt]], range_name=ver_range)
self.sheet_handler.sheet.update(values=[[Config.VERSION]], range_name=ver_range)
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"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"Servicetechniker-Schätzung: {st_estimate if 'st_estimate' in locals() else 'k.A.'}")
time.sleep(Config.RETRY_DELAY)
# ==================== NEUER MODUS 6: CONTACT RESEARCH (via SerpAPI) ====================
def process_contact_research():
debug_print("Starte Contact Research (Modus 6)...")
@@ -839,10 +903,8 @@ def process_contact_research():
sh = gc.open_by_url(Config.SHEET_URL)
main_sheet = sh.sheet1
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):
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
website = row[3] if len(row) > 3 else ""
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_disponent = count_linkedin_contacts(search_name, website, "Disponent")
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_it)]], range_name=f"AJ{i}") # IT-Leiter gefunden in Spalte AJ
main_sheet.update(values=[[str(count_management)]], range_name=f"AK{i}") # Management gefunden in Spalte AK
main_sheet.update(values=[[str(count_disponent)]], range_name=f"AL{i}") # Disponent gefunden in Spalte AL
main_sheet.update(values=[[current_dt]], range_name=f"AM{i}") # Contact Search Timestamp in Spalte AM
main_sheet.update(values=[[str(count_service)]], range_name=f"AI{i}")
main_sheet.update(values=[[str(count_it)]], range_name=f"AJ{i}")
main_sheet.update(values=[[str(count_management)]], range_name=f"AK{i}")
main_sheet.update(values=[[str(count_disponent)]], range_name=f"AL{i}")
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.")
time.sleep(Config.RETRY_DELAY * 1.5)
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) ====================
def process_contacts():
debug_print("Starte LinkedIn-Kontaktsuche...")
@@ -895,7 +941,6 @@ def process_contacts():
positions = ["Serviceleiter", "IT-Leiter", "Leiter After Sales", "Leiter Einsatzplanung"]
new_rows = []
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 ""
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 ""
@@ -923,7 +968,7 @@ def process_contacts():
if __name__ == "__main__":
import argparse
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)")
args = parser.parse_args()
@@ -949,4 +994,6 @@ if __name__ == "__main__":
process_contact_research()
elif MODE == "7":
process_contacts()
print(f"\n✅ Auswertung abgeschlossen ({Config.VERSION})")
elif MODE == "51":
process_verification_only()
print(f"\n✅ Auswertung abgeschlossen ({Config.VERSION})")