Compare commits
3 Commits
088f440d52
...
d710abd35b
| Author | SHA1 | Date | |
|---|---|---|---|
| d710abd35b | |||
| 64f730fc1a | |||
| 65d4e78257 |
@@ -1 +1 @@
|
||||
{"task_id": "2ea88f42-8544-8005-9344-e85c9b2ef5ec", "token": "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", "readme_path": "MIGRATION_PLAN.md", "session_start_time": "2026-03-04T15:14:09.546338"}
|
||||
{"task_id": "31188f42-8544-8074-bad3-d3e1b9b4051f", "token": "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", "readme_path": "connector-superoffice/README.md", "session_start_time": "2026-03-04T16:32:31.904656"}
|
||||
@@ -9,7 +9,7 @@ class Settings:
|
||||
# --- SuperOffice API Credentials ---
|
||||
# Fallback for empty string in env var
|
||||
env_val = os.getenv("SO_ENVIRONMENT")
|
||||
self.SO_ENVIRONMENT = env_val if env_val else "sod"
|
||||
self.SO_ENVIRONMENT = env_val if env_val else "online3"
|
||||
|
||||
self.SO_CLIENT_ID = os.getenv("SO_CLIENT_ID", "")
|
||||
self.SO_CLIENT_SECRET = os.getenv("SO_CLIENT_SECRET", "")
|
||||
|
||||
BIN
connector-superoffice/connector_queue.db-journal
Normal file
BIN
connector-superoffice/connector_queue.db-journal
Normal file
Binary file not shown.
56
connector-superoffice/tools/create_company.py
Normal file
56
connector-superoffice/tools/create_company.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import sys
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Explicitly load .env from the parent directory
|
||||
dotenv_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '.env'))
|
||||
print(f"Loading .env from: {dotenv_path}")
|
||||
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 create_test_company():
|
||||
"""
|
||||
Creates a new company in SuperOffice for E2E testing.
|
||||
"""
|
||||
company_name = "Bremer Abenteuerland"
|
||||
# Provide a real-world, scrapable website to test enrichment
|
||||
website = "https://www.belantis.de/"
|
||||
print(f"🚀 Attempting to create company: '{company_name}'")
|
||||
try:
|
||||
client = SuperOfficeClient()
|
||||
if not client.access_token:
|
||||
print("❌ Authentication failed. Check your .env file.")
|
||||
return
|
||||
# Check if company already exists
|
||||
existing = client.search(f"Contact?$select=contactId,name&$filter=name eq '{company_name}'")
|
||||
if existing:
|
||||
contact_id = existing[0]['ContactId']
|
||||
print(f"⚠️ Company '{company_name}' already exists with ContactId: {contact_id}.")
|
||||
print("Skipping creation.")
|
||||
return contact_id
|
||||
payload = {
|
||||
"Name": company_name,
|
||||
"Urls": [
|
||||
{
|
||||
"Value": website,
|
||||
"Description": "Main Website"
|
||||
}
|
||||
],
|
||||
"Country": {
|
||||
"CountryId": 68 # Germany
|
||||
}
|
||||
}
|
||||
new_company = client._post("Contact", payload)
|
||||
if new_company and "ContactId" in new_company:
|
||||
contact_id = new_company["ContactId"]
|
||||
print(f"✅ SUCCESS! Created company '{company_name}' with ContactId: {contact_id}")
|
||||
return contact_id
|
||||
else:
|
||||
print(f"❌ Failed to create company. Response: {new_company}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
return None
|
||||
if __name__ == "__main__":
|
||||
create_test_company()
|
||||
40
connector-superoffice/tools/debug_config_types.py
Normal file
40
connector-superoffice/tools/debug_config_types.py
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
import sys
|
||||
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}")
|
||||
load_dotenv(dotenv_path=dotenv_path, override=True)
|
||||
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
from config import settings
|
||||
|
||||
print("\n--- DEBUGGING CONFIG TYPES ---")
|
||||
try:
|
||||
print(f"UDF_VERTICAL: {settings.UDF_VERTICAL} (Type: {type(settings.UDF_VERTICAL)})")
|
||||
print(f"UDF_SUMMARY: {settings.UDF_SUMMARY} (Type: {type(settings.UDF_SUMMARY)})")
|
||||
print(f"UDF_OPENER: {settings.UDF_OPENER} (Type: {type(settings.UDF_OPENER)})")
|
||||
print(f"UDF_OPENER_SECONDARY: {settings.UDF_OPENER_SECONDARY} (Type: {type(settings.UDF_OPENER_SECONDARY)})")
|
||||
print(f"UDF_LAST_UPDATE: {settings.UDF_LAST_UPDATE} (Type: {type(settings.UDF_LAST_UPDATE)})")
|
||||
print(f"UDF_LAST_OUTREACH: {settings.UDF_LAST_OUTREACH} (Type: {type(settings.UDF_LAST_OUTREACH)})")
|
||||
|
||||
# Test dictionary creation to force the error if a key is a dict
|
||||
print("\nAttempting to create dictionary with these keys...")
|
||||
test_dict = {
|
||||
settings.UDF_VERTICAL: "Vertical",
|
||||
settings.UDF_SUMMARY: "Summary",
|
||||
settings.UDF_OPENER: "Opener",
|
||||
settings.UDF_OPENER_SECONDARY: "Opener 2",
|
||||
settings.UDF_LAST_UPDATE: "Last Update",
|
||||
settings.UDF_LAST_OUTREACH: "Last Outreach"
|
||||
}
|
||||
print("✅ Dictionary creation SUCCESSFUL.")
|
||||
|
||||
except TypeError as e:
|
||||
print(f"\n❌ TypeError CAUGHT: {e}")
|
||||
print("One of the settings above is likely a dictionary or unhashable type!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Unknown Error: {e}")
|
||||
84
connector-superoffice/tools/get_enriched_company_data.py
Normal file
84
connector-superoffice/tools/get_enriched_company_data.py
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import traceback
|
||||
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
|
||||
from config import settings
|
||||
|
||||
def get_enriched_data(contact_id: int):
|
||||
print(f"🚀 [DEBUG] Starting fetch for ContactId: {contact_id}")
|
||||
try:
|
||||
client = SuperOfficeClient()
|
||||
if not client.access_token:
|
||||
print("❌ Authentication failed.")
|
||||
return
|
||||
|
||||
print("✅ [DEBUG] Client authenticated.")
|
||||
|
||||
try:
|
||||
contact_data = client.get_contact(
|
||||
contact_id,
|
||||
select=[
|
||||
"Name", "UrlAddress", "OrgNr", "UserDefinedFields"
|
||||
]
|
||||
)
|
||||
print(f"✅ [DEBUG] API Call successful. Data type: {type(contact_data)}")
|
||||
except Exception as e:
|
||||
print(f"❌ [DEBUG] API Call failed: {e}")
|
||||
traceback.print_exc()
|
||||
return
|
||||
|
||||
if not contact_data:
|
||||
print("❌ [DEBUG] No data returned.")
|
||||
return
|
||||
|
||||
print(f"✅ [DEBUG] Name: {contact_data.get('Name')}")
|
||||
|
||||
try:
|
||||
udfs = contact_data.get("UserDefinedFields", {})
|
||||
print(f"✅ [DEBUG] UDFs extracted. Type: {type(udfs)}")
|
||||
except Exception as e:
|
||||
print(f"❌ [DEBUG] Failed to extract UDFs: {e}")
|
||||
traceback.print_exc()
|
||||
return
|
||||
|
||||
if isinstance(udfs, dict):
|
||||
print(f"✅ [DEBUG] UDFs is a dict with {len(udfs)} keys.")
|
||||
try:
|
||||
# Iterate keys safely
|
||||
print("--- UDF KEYS SAMPLE ---")
|
||||
for k in list(udfs.keys())[:5]:
|
||||
print(f"Key: {k} (Type: {type(k)})")
|
||||
except Exception as e:
|
||||
print(f"❌ [DEBUG] Failed to iterate keys: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
# Try to access specific key
|
||||
target_key = settings.UDF_VERTICAL
|
||||
print(f"✅ [DEBUG] Attempting access with key: '{target_key}' (Type: {type(target_key)})")
|
||||
|
||||
try:
|
||||
if target_key in udfs:
|
||||
val = udfs[target_key]
|
||||
print(f"✅ [DEBUG] Value found: {val}")
|
||||
else:
|
||||
print(f"ℹ️ [DEBUG] Key not found in UDFs.")
|
||||
except Exception as e:
|
||||
print(f"❌ [DEBUG] Failed to access dictionary with key: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ [DEBUG] Global Error: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
target_contact_id = 171185
|
||||
get_enriched_data(target_contact_id)
|
||||
69
connector-superoffice/tools/verify_enrichment.py
Normal file
69
connector-superoffice/tools/verify_enrichment.py
Normal file
@@ -0,0 +1,69 @@
|
||||
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
|
||||
from config import settings
|
||||
|
||||
def verify_enrichment(contact_id: int):
|
||||
print(f"🚀 Verifying enrichment for ContactId: {contact_id}")
|
||||
try:
|
||||
client = SuperOfficeClient()
|
||||
if not client.access_token:
|
||||
print("❌ Authentication failed.")
|
||||
return
|
||||
|
||||
contact_data = client.get_contact(
|
||||
contact_id,
|
||||
select=[
|
||||
"Name", "UrlAddress", "Urls", "OrgNr", "Address", "UserDefinedFields"
|
||||
]
|
||||
)
|
||||
|
||||
if not contact_data:
|
||||
print(f"❌ Contact {contact_id} not found.")
|
||||
return
|
||||
|
||||
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})")
|
||||
|
||||
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.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error during verification: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
target_contact_id = 171185
|
||||
verify_enrichment(target_contact_id)
|
||||
@@ -12,7 +12,7 @@ logger = logging.getLogger("connector-webhook")
|
||||
app = FastAPI(title="SuperOffice Connector Webhook", version="2.0")
|
||||
queue = JobQueue()
|
||||
|
||||
WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET", "changeme")
|
||||
WEBHOOK_TOKEN = os.getenv("WEBHOOK_TOKEN", "changeme")
|
||||
|
||||
@app.post("/webhook")
|
||||
async def receive_webhook(request: Request, background_tasks: BackgroundTasks):
|
||||
@@ -21,11 +21,10 @@ async def receive_webhook(request: Request, background_tasks: BackgroundTasks):
|
||||
"""
|
||||
# 1. Verify Secret (Basic Security)
|
||||
# SuperOffice puts signature in headers, but for custom webhook we might just use query param or header
|
||||
# Let's assume for now a shared secret in header 'X-SuperOffice-Signature' or similar
|
||||
# Or simply a secret in the URL: /webhook?token=...
|
||||
|
||||
token = request.query_params.get("token")
|
||||
if token != WEBHOOK_SECRET:
|
||||
if token != WEBHOOK_TOKEN:
|
||||
logger.warning(f"Invalid webhook token attempt: {token}")
|
||||
raise HTTPException(403, "Invalid Token")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user