1.5.2: Ergänzung heuristischer E-Mail-Generierung und Spaltenanpassung im Contacts-Blatt

- Neue Funktion get_email_address zur Erzeugung der E-Mail-Adresse im Format vorname.nachname@domain.tld
- Anpassung von process_contact_research: E-Mail-Adresse in Spalte I eingetragen, LinkedIn-Link und Timestamp entsprechend verschoben
- Integration der E-Mail-Generierung in den bestehenden Kontaktverarbeitungs-Workflow, bestehende Funktionen weitgehend unverändert
This commit is contained in:
2025-04-10 06:11:28 +00:00
parent b4f5974877
commit e8b18a40d5

View File

@@ -1,16 +1,15 @@
#!/usr/bin/env python3
"""
Version: v1.5.1
Version: v1.5.2
Datum: {aktuelles Datum}
Git-Überschrift (max. 100 Zeichen):
1.5.1: Integrierter hybrider Geschlechtsdetektor & aktualisierte Kontakte-Spalten
1.5.2: Ergänzung heuristischer E-Mail-Generierung und Spaltenanpassung im Contacts-Blatt
Git-Änderungsbeschreibung:
- Umstellung der Geschlechtsbestimmung: Zuerst gender-guesser, Fallback zu Genderize API
- Geschlecht wird jetzt in Spalte D gespeichert (alle folgenden Felder rutschen um eine Spalte nach rechts)
- Aktualisierte Header und Kontaktzeilen im Contacts-Blatt, inklusive API-Key aus "genderize_API_Key.txt"
- Anpassung der Contact Research-Funktion zur Verarbeitung der geänderten Spalten
- Neue Funktion get_email_address zur Erzeugung der E-Mail-Adresse im Format vorname.nachname@domain.tld
- Anpassung von process_contact_research: E-Mail-Adresse in Spalte I eingetragen, LinkedIn-Link und Timestamp entsprechend verschoben
- Integration der E-Mail-Generierung in den bestehenden Kontaktverarbeitungs-Workflow, bestehende Funktionen weitgehend unverändert
"""
@@ -37,7 +36,7 @@ except ImportError:
# ==================== KONFIGURATION ====================
class Config:
VERSION = "v1.5.1"
VERSION = "v1.5.2"
LANG = "de"
CREDENTIALS_FILE = "service_account.json"
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
@@ -128,6 +127,25 @@ def get_gender(firstname):
else:
return result
def get_email_address(firstname, lastname, website):
"""
Generiert eine E-Mail-Adresse im Format vorname.nachname@domain.tld.
Dabei wird der Domainname aus der Website extrahiert.
"""
# Falls die Website keinen Protokoll-Präfix hat, hinzufügen, um das Parsen zu ermöglichen
url = website if website.startswith("http") else "http://" + website
parsed = urlparse(url)
domain = parsed.netloc
if domain.startswith("www."):
domain = domain[4:]
# Entferne Nicht-Alphanumerische Zeichen aus Vor- und Nachnamen und wandle in Kleinbuchstaben um
f = re.sub(r'\W+', '', firstname.lower())
l = re.sub(r'\W+', '', lastname.lower())
if f and l:
return f"{f}.{l}@{domain}"
else:
return ""
def is_valid_company_article(wiki_categories):
"""
Prüft, ob in den Wikipedia-Kategorien ein Hinweis auf einen Unternehmensartikel enthalten ist.
@@ -1707,9 +1725,9 @@ def count_linkedin_contacts(company_name, website, position_query, crm_kurzform)
def process_contact_research():
"""
Sucht mithilfe der SerpAPI Kontakte für bestimmte Positionen für jedes Unternehmen.
Es werden zunächst die CRM-Daten (insbesondere CRM Kurzform in Spalte C) sowie Firma und Website aus dem Hauptblatt genommen.
Die gefundenen Kontakte, welche den Filter (CRM Kurzform muss im Titel enthalten sein) erfüllen, werden in das Kontakte-Blatt eingetragen.
Es werden zunächst die CRM-Daten (insbesondere CRM Kurzform in Spalte C) sowie Firma und Website aus dem Hauptblatt gelesen.
Die gefundenen Kontakte, welche den Filter (CRM Kurzform muss im Titel enthalten sein) erfüllen, werden im Kontakte-Blatt eingetragen.
Zusätzlich werden die Trefferzahlen (als Summen pro Position) in das Hauptblatt in den Spalten AI, AJ, AK, AL geschrieben
und ein Timestamp in Spalte AM gesetzt.
@@ -1717,13 +1735,14 @@ def process_contact_research():
A: Firmenname
B: CRM Kurzform
C: Website
D: Geschlecht (ermittelt aus dem Vornamen)
D: Geschlecht
E: Vorname
F: Nachname
G: Position
H: Suchbegriffskategorie
I: LinkedIn-Link
J: Timestamp
I: E-Mail-Adresse
J: LinkedIn-Link
K: Timestamp
Detaillierte Debug-Ausgaben sorgen für Transparenz bei der Ausführung.
"""
@@ -1736,15 +1755,14 @@ def process_contact_research():
data = main_sheet.get_all_values()
# Ermittle die letzte Zeile in Spalte AM (Spalte 39), in der ein Timestamp eingetragen wurde
col_am = main_sheet.col_values(39) # Spalte AM = 39. Spalte
col_am = main_sheet.col_values(39) # Spalte AM hat den Index 39 (A=1, ..., AM=39)
last_filled_row = 1 # Header-Zeile
for idx, cell in enumerate(col_am):
if cell.strip() != "":
last_filled_row = idx + 1 # 0-basierter Index -> Zeilennummer
start_row = last_filled_row + 1 # Verarbeitung ab der nächsten Zeile
last_filled_row = idx + 1
start_row = last_filled_row + 1
debug_print(f"Letzter Timestamp in Spalte AM wurde in Zeile {last_filled_row} gefunden. Starte Verarbeitung ab Zeile {start_row}.")
# Falls start_row größer ist als die Anzahl der vorhandenen Zeilen, gibt es nichts zu verarbeiten.
if start_row > len(data):
debug_print("Keine neuen Zeilen zu verarbeiten, da Timestamp in Spalte AM bereits bis zum Ende vorhanden ist.")
return
@@ -1753,15 +1771,15 @@ def process_contact_research():
try:
contacts_sheet = sh.worksheet("Contacts")
except gspread.exceptions.WorksheetNotFound:
contacts_sheet = sh.add_worksheet(title="Contacts", rows="1000", cols="10")
contacts_sheet = sh.add_worksheet(title="Contacts", rows="1000", cols="12")
header = ["Firmenname", "CRM Kurzform", "Website", "Geschlecht", "Vorname", "Nachname", "Position",
"Suchbegriffskategorie", "LinkedIn-Link", "Timestamp"]
contacts_sheet.update(values=[header], range_name="A1:J1")
"Suchbegriffskategorie", "E-Mail-Adresse", "LinkedIn-Link", "Timestamp"]
contacts_sheet.update(values=[header], range_name="A1:K1")
debug_print("Neues Blatt 'Contacts' erstellt und Header eingetragen.")
# Verarbeite jede Zeile ab der ermittelten Startzeile
for i in range(start_row, len(data) + 1):
row = data[i - 1] # data ist 0-basiert
row = data[i - 1]
company_name = row[1] if len(row) > 1 else ""
crm_kurzform = row[2] if len(row) > 2 else ""
website = row[3] if len(row) > 3 else ""
@@ -1771,38 +1789,31 @@ def process_contact_research():
positions = ["Serviceleiter", "IT-Leiter", "Geschäftsführer", "Disponent"]
contact_counts = {}
# Für jeden Positionsbegriff: Kontakte zählen und exemplarisch einen Kontakt suchen
for pos in positions:
count = count_linkedin_contacts(crm_kurzform, website, pos, crm_kurzform)
contact_counts[pos] = count
contact = search_linkedin_contact(crm_kurzform, website, pos, crm_kurzform)
if contact:
# Ermittle das Geschlecht anhand des Vornamens
firstname = contact.get("Vorname", "")
lastname = contact.get("Nachname", "")
gender_value = get_gender(firstname) if firstname else "unknown"
# Erstelle den Kontaktzeile-Eintrag:
# Spalte A: Firmenname
# Spalte B: CRM Kurzform
# Spalte C: Website
# Spalte D: Geschlecht
# Spalte E: Vorname
# Spalte F: Nachname
# Spalte G: Position
# Spalte H: Suchbegriffskategorie
# Spalte I: LinkedIn-Link
# Spalte J: Timestamp
email = get_email_address(firstname, lastname, website)
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Neue Reihenfolge:
# A: Firmenname, B: CRM Kurzform, C: Website, D: Geschlecht, E: Vorname, F: Nachname,
# G: Position, H: Suchbegriffskategorie, I: E-Mail-Adresse, J: LinkedIn-Link, K: Timestamp
contact_row = [
company_name,
crm_kurzform,
website,
gender_value, # Geschlecht (Spalte D)
contact.get("Vorname", ""),
contact.get("Nachname", ""),
gender_value,
firstname,
lastname,
contact.get("Position", ""),
pos, # Suchbegriffskategorie (Spalte H)
pos,
email,
contact.get("LinkedInURL", ""),
timestamp # Timestamp (Spalte J)
timestamp
]
try:
contacts_sheet.append_row(contact_row)
@@ -1812,7 +1823,7 @@ def process_contact_research():
else:
debug_print(f"Zeile {i}: Kein passender Kontakt für Position '{pos}' gefunden.")
# Aktualisierung des Hauptblatts
# Aktualisiere Hauptblatt-Zeile mit Trefferzahlen und Timestamp
try:
main_sheet.update(values=[[str(contact_counts.get("Serviceleiter", 0))]], range_name=f"AI{i}")
main_sheet.update(values=[[str(contact_counts.get("IT-Leiter", 0))]], range_name=f"AJ{i}")
@@ -1826,7 +1837,6 @@ def process_contact_research():
time.sleep(Config.RETRY_DELAY)
debug_print("Contact Research abgeschlossen.")
# ----------------- DataProcessor-Klasse inklusive neuer SERP-API Website Lookup-Methode -----------------
class DataProcessor:
def __init__(self):