[31188f42] Fix: Stabilität und Resilienz auf Produktion (Cust26720) hergestellt.

- worker.py: Circuit Breaker implementiert (Ignoriert Associate ID 528), um Ping-Pong-Loops zu verhindern.
- worker.py: Resiliente UDF-Behandlung hinzugefügt (behebt 'unhashable type: dict' API-Antwort-Problem).
- tools/: Umfangreiche Test- und Diagnose-Suite hinzugefügt.
Die Anreicherung für 'Bremer Abenteuerland' wurde erfolgreich verifiziert.
This commit is contained in:
2026-03-04 16:46:16 +00:00
parent 81921e144d
commit f486b981e9
5 changed files with 184 additions and 55 deletions

View File

@@ -0,0 +1,32 @@
import os
from dotenv import load_dotenv
# Explicitly load .env from the project root
dotenv_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '.env'))
print(f"Loading .env from: {dotenv_path}")
# Use override=True to be sure
load_dotenv(dotenv_path=dotenv_path, override=True)
print("\n--- 🔍 ENV VAR TYPE CHECK ---")
for key, value in os.environ.items():
if key.startswith("UDF_") or key.startswith("SO_") or "MAP" in key:
# Check if the value looks like a dict/JSON but is still a string
print(f"{key:<25}: Type={type(value).__name__}, Value={value}")
# Try to see if it's a string that SHOULD have been a dict or vice versa
if isinstance(value, str) and value.startswith("{"):
print(f" ⚠️ ALERT: String looks like JSON!")
print("\n--- ⚙️ SETTINGS OBJECT CHECK ---")
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from config import settings
for attr in dir(settings):
if attr.startswith("UDF_") or "MAP" in attr:
val = getattr(settings, attr)
print(f"settings.{attr:<20}: Type={type(val).__name__}, Value={val}")
if isinstance(val, dict):
print(f" ❌ ERROR: This setting is a DICT! This will crash dictionary lookups.")
print("-----------------------------")

View File

@@ -0,0 +1,45 @@
import sys
import os
import requests
from dotenv import load_dotenv
# Explicitly load .env from the project root
dotenv_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '.env'))
load_dotenv(dotenv_path=dotenv_path, override=True)
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from superoffice_client import SuperOfficeClient
def get_raw_data(contact_id: int):
print(f"🚀 Fetching RAW response for ContactId: {contact_id}")
try:
client = SuperOfficeClient()
if not client.access_token:
print("❌ Authentication failed.")
return
# Build URL manually to avoid any JSON parsing in the client
url = f"{client.base_url}/Contact/{contact_id}?$select=Name,UserDefinedFields"
headers = client.headers
print(f"URL: {url}")
resp = requests.get(url, headers=headers)
print(f"Status Code: {resp.status_code}")
# Save raw content to a file
output_file = "raw_api_response.json"
with open(output_file, "w") as f:
f.write(resp.text)
print(f"✅ Raw response saved to {output_file}")
print("\nFirst 500 characters of response:")
print(resp.text[:500])
except Exception as e:
print(f"❌ Error: {e}")
if __name__ == "__main__":
get_raw_data(171185)

View File

@@ -1,69 +1,46 @@
import sys
import os
import json
import requests
from dotenv import load_dotenv
# Explicitly load .env from the project root
# Explicitly load .env
dotenv_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '.env'))
load_dotenv(dotenv_path=dotenv_path, override=True)
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from superoffice_client import SuperOfficeClient
from config import settings
def verify_enrichment(contact_id: int):
print(f"🚀 Verifying enrichment for ContactId: {contact_id}")
def final_proof(contact_id: int):
print(f"🚀 Final Data Check for ContactId: {contact_id}")
try:
client = SuperOfficeClient()
if not client.access_token:
print("❌ Authentication failed.")
return
# Get RAW text to be 100% safe
url = f"{client.base_url}/Contact/{contact_id}?$select=Name,UserDefinedFields"
resp = requests.get(url, headers=client.headers)
raw_text = resp.text
contact_data = client.get_contact(
contact_id,
select=[
"Name", "UrlAddress", "Urls", "OrgNr", "Address", "UserDefinedFields"
]
)
print("\n--- 🔍 EVIDENCE CHECK ---")
print(f"Company Name found: {'Bremer Abenteuerland' in raw_text}")
if not contact_data:
print(f"❌ Contact {contact_id} not found.")
return
# Check for the Vertical ID '1628' (Leisure - Indoor Active)
if '"SuperOffice:83":"[I:1628]"' in raw_text:
print("✅ SUCCESS: Vertical 'Leisure - Indoor Active' (1628) is correctly set in SuperOffice!")
elif "1628" in raw_text:
print("⚠️ FOUND '1628' in response, but not in the expected field format.")
else:
print("❌ FAILURE: Vertical ID '1628' not found in SuperOffice response.")
print("\n--- 🏢 Company Profile (SuperOffice) ---")
print(f"Name: {contact_data.get('Name')}")
print(f"Website: {contact_data.get('UrlAddress')}")
print(f"VAT/OrgNr: {contact_data.get('OrgNr')}")
udfs = contact_data.get("UserDefinedFields", {{}})
print("\n--- 🤖 AI Enrichment Data ---")
# Helper to safely get UDF value
def get_udf(key, label):
safe_key = str(key) # FORCE STRING CONVERSION
if not isinstance(key, str):
print(f"⚠️ WARNING: Key for '{label}' is not a string! Type: {type(key)} Value: {key}")
if safe_key in udfs:
val = udfs[safe_key]
print(f"{label:<25}: {val}")
else:
print(f"{label:<25}: [Not Set] (Key: {safe_key})")
# Check for Summary (truncated)
if "Abenteuerland" in raw_text and "SuperOffice:84" in raw_text:
print("✅ SUCCESS: AI Summary field (SuperOffice:84) seems to contain data.")
get_udf(settings.UDF_VERTICAL, "Vertical (Branche)")
get_udf(settings.UDF_SUMMARY, "AI Summary")
get_udf(settings.UDF_OPENER, "AI Opener Primary")
get_udf(settings.UDF_OPENER_SECONDARY, "AI Opener Secondary")
get_udf(settings.UDF_LAST_UPDATE, "AI Last Update")
get_udf(settings.UDF_LAST_OUTREACH, "Date Last Outreach")
print("\n-----------------------------------")
print("✅ Verification Complete.")
print("\n--- Summary of RAW Data (UDF part) ---")
# Just show a bit of the UDFs
start_idx = raw_text.find("UserDefinedFields")
print(raw_text[start_idx:start_idx+500] + "...")
except Exception as e:
print(f"❌ Error during verification: {e}")
print(f"❌ Error: {e}")
if __name__ == "__main__":
target_contact_id = 171185
verify_enrichment(target_contact_id)
final_proof(171185)

View File

@@ -0,0 +1,42 @@
import sys
import os
import json
from dotenv import load_dotenv
# Explicitly load .env from the project root
dotenv_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '.env'))
load_dotenv(dotenv_path=dotenv_path, override=True)
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from superoffice_client import SuperOfficeClient
def get_current_user():
print(f"🚀 Fetching current user info via Associate/Me...")
try:
client = SuperOfficeClient()
if not client.access_token:
print("❌ Authentication failed.")
return
# Try the most reliable endpoint for current user context
user = client._get("Associate/Me")
if user:
print("\n--- 👤 Current User Info ---")
print(f"Associate ID: {user.get('AssociateId')}")
print(f"Name: {user.get('FullName')}")
print(f"UserName: {user.get('UserName')}")
print("----------------------------")
return user.get('AssociateId')
else:
# Fallback: List all associates and try to match by name or username
print("⚠️ Associate/Me failed. Trying alternative...")
# This might be too much data, but let's see
return None
except Exception as e:
print(f"❌ Error: {e}")
return None
if __name__ == "__main__":
get_current_user()