[2ff88f42] Finalize Pains & Gains Phase 2 + Matrix Engine v3.2 (Ops Secondary Logic)
This commit is contained in:
@@ -1 +1 @@
|
|||||||
{"task_id": "2ff88f42-8544-8050-8245-c3bb852058f4", "token": "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", "session_start_time": "2026-02-23T07:38:02.815686"}
|
{"task_id": "2ff88f42-8544-8050-8245-c3bb852058f4", "token": "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", "session_start_time": "2026-02-23T09:56:54.647321"}
|
||||||
@@ -128,7 +128,7 @@ PERSÖNLICHE HERAUSFORDERUNGEN DES ANSPRECHPARTNERS (PAIN POINTS):
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
--- FORMAT ---
|
--- FORMAT ---
|
||||||
Antworte NUR mit einem validen JSON-Objekt.
|
Antworte NUR mit einem validen JSON-Objekt. Keine Markdown-Blöcke (```json), kein erklärender Text.
|
||||||
Format:
|
Format:
|
||||||
{{
|
{{
|
||||||
"subject": "...",
|
"subject": "...",
|
||||||
@@ -231,9 +231,22 @@ def run_matrix_generation(dry_run: bool = True, force: bool = False, specific_in
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
result = real_gemini_call(prompt)
|
result = real_gemini_call(prompt)
|
||||||
# Basic Validation
|
|
||||||
if not result.get("subject") or not result.get("intro"):
|
# Normalize Keys (Case-Insensitive)
|
||||||
print(" -> Invalid result structure. Skipping.")
|
normalized_result = {}
|
||||||
|
for k, v in result.items():
|
||||||
|
normalized_result[k.lower()] = v
|
||||||
|
|
||||||
|
# Map known variations to standardized keys
|
||||||
|
if "introduction_textonly" in normalized_result:
|
||||||
|
normalized_result["intro"] = normalized_result["introduction_textonly"]
|
||||||
|
if "industry_references_textonly" in normalized_result:
|
||||||
|
normalized_result["social_proof"] = normalized_result["industry_references_textonly"]
|
||||||
|
|
||||||
|
# Validation using normalized keys
|
||||||
|
if not normalized_result.get("subject") or not normalized_result.get("intro"):
|
||||||
|
print(f" -> Invalid result structure. Keys found: {list(result.keys())}")
|
||||||
|
print(f" -> Raw Result: {json.dumps(result, indent=2)}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -246,16 +259,16 @@ def run_matrix_generation(dry_run: bool = True, force: bool = False, specific_in
|
|||||||
new_entry = MarketingMatrix(
|
new_entry = MarketingMatrix(
|
||||||
industry_id=ind.id,
|
industry_id=ind.id,
|
||||||
persona_id=pers.id,
|
persona_id=pers.id,
|
||||||
subject=result.get("subject"),
|
subject=normalized_result.get("subject"),
|
||||||
intro=result.get("intro"),
|
intro=normalized_result.get("intro"),
|
||||||
social_proof=result.get("social_proof")
|
social_proof=normalized_result.get("social_proof")
|
||||||
)
|
)
|
||||||
db.add(new_entry)
|
db.add(new_entry)
|
||||||
print(f" -> Created new entry.")
|
print(f" -> Created new entry.")
|
||||||
else:
|
else:
|
||||||
existing.subject = result.get("subject")
|
existing.subject = normalized_result.get("subject")
|
||||||
existing.intro = result.get("intro")
|
existing.intro = normalized_result.get("intro")
|
||||||
existing.social_proof = result.get("social_proof")
|
existing.social_proof = normalized_result.get("social_proof")
|
||||||
print(f" -> Updated entry.")
|
print(f" -> Updated entry.")
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|||||||
@@ -66,43 +66,52 @@ class SuperOfficeClient:
|
|||||||
logger.error(f"❌ Connection Error during token refresh: {e}")
|
logger.error(f"❌ Connection Error during token refresh: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get(self, endpoint):
|
def _request_with_retry(self, method, endpoint, payload=None, retry=True):
|
||||||
"""Generic GET request."""
|
"""Helper to handle 401 Unauthorized with auto-refresh."""
|
||||||
if not self.access_token: return None
|
if not self.access_token:
|
||||||
try:
|
if not self._refresh_access_token():
|
||||||
resp = requests.get(f"{self.base_url}/{endpoint}", headers=self.headers)
|
return None
|
||||||
resp.raise_for_status()
|
|
||||||
return resp.json()
|
|
||||||
except requests.exceptions.HTTPError as e:
|
|
||||||
logger.error(f"❌ API GET Error for {endpoint} (Status: {e.response.status_code}): {e.response.text}")
|
|
||||||
logger.debug(f"Response Headers: {e.response.headers}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _put(self, endpoint, payload):
|
url = f"{self.base_url}/{endpoint}"
|
||||||
"""Generic PUT request."""
|
|
||||||
if not self.access_token: return None
|
|
||||||
try:
|
try:
|
||||||
resp = requests.put(f"{self.base_url}/{endpoint}", headers=self.headers, json=payload)
|
if method == "GET":
|
||||||
resp.raise_for_status()
|
resp = requests.get(url, headers=self.headers)
|
||||||
return resp.json()
|
elif method == "POST":
|
||||||
except requests.exceptions.HTTPError as e:
|
resp = requests.post(url, headers=self.headers, json=payload)
|
||||||
logger.error(f"❌ API PUT Error for {endpoint}: {e.response.text}")
|
elif method == "PUT":
|
||||||
return None
|
resp = requests.put(url, headers=self.headers, json=payload)
|
||||||
|
|
||||||
|
# 401 Handling
|
||||||
|
if resp.status_code == 401 and retry:
|
||||||
|
logger.warning(f"⚠️ 401 Unauthorized for {endpoint}. Attempting Token Refresh...")
|
||||||
|
new_token = self._refresh_access_token()
|
||||||
|
if new_token:
|
||||||
|
self.access_token = new_token
|
||||||
|
self.headers["Authorization"] = f"Bearer {self.access_token}"
|
||||||
|
return self._request_with_retry(method, endpoint, payload, retry=False)
|
||||||
|
else:
|
||||||
|
logger.error("❌ Token Refresh failed during retry.")
|
||||||
|
return None
|
||||||
|
|
||||||
def _post(self, endpoint, payload):
|
|
||||||
"""Generic POST request."""
|
|
||||||
if not self.access_token: return None
|
|
||||||
try:
|
|
||||||
resp = requests.post(f"{self.base_url}/{endpoint}", headers=self.headers, json=payload)
|
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
logger.error(f"❌ API POST Error for {endpoint} (Status: {e.response.status_code}): {e.response.text}")
|
logger.error(f"❌ API {method} Error for {endpoint} (Status: {e.response.status_code}): {e.response.text}")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ Connection Error during POST for {endpoint}: {e}")
|
logger.error(f"❌ Connection Error during {method} for {endpoint}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _get(self, endpoint):
|
||||||
|
return self._request_with_retry("GET", endpoint)
|
||||||
|
|
||||||
|
def _put(self, endpoint, payload):
|
||||||
|
return self._request_with_retry("PUT", endpoint, payload)
|
||||||
|
|
||||||
|
def _post(self, endpoint, payload):
|
||||||
|
return self._request_with_retry("POST", endpoint, payload)
|
||||||
|
|
||||||
# --- Convenience Wrappers ---
|
# --- Convenience Wrappers ---
|
||||||
|
|
||||||
def get_person(self, person_id):
|
def get_person(self, person_id):
|
||||||
|
|||||||
@@ -115,32 +115,37 @@ def process_job(job, so_client: SuperOfficeClient):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
contact_details = so_client.get_contact(contact_id)
|
contact_details = so_client.get_contact(contact_id)
|
||||||
if contact_details:
|
if not contact_details:
|
||||||
crm_name = contact_details.get("Name")
|
raise ValueError(f"Contact {contact_id} not found (API returned None)")
|
||||||
crm_website = contact_details.get("UrlAddress")
|
|
||||||
if not crm_website and "Urls" in contact_details and contact_details["Urls"]:
|
|
||||||
crm_website = contact_details["Urls"][0].get("Value")
|
|
||||||
|
|
||||||
if settings.UDF_VERTICAL:
|
crm_name = contact_details.get("Name")
|
||||||
udfs = contact_details.get("UserDefinedFields", {})
|
crm_website = contact_details.get("UrlAddress")
|
||||||
so_vertical_val = udfs.get(settings.UDF_VERTICAL)
|
if not crm_website and "Urls" in contact_details and contact_details["Urls"]:
|
||||||
|
crm_website = contact_details["Urls"][0].get("Value")
|
||||||
|
|
||||||
if so_vertical_val:
|
if settings.UDF_VERTICAL:
|
||||||
val_str = str(so_vertical_val)
|
udfs = contact_details.get("UserDefinedFields", {})
|
||||||
if val_str.startswith("[I:"):
|
so_vertical_val = udfs.get(settings.UDF_VERTICAL)
|
||||||
val_str = val_str.split(":")[1].strip("]")
|
|
||||||
|
|
||||||
try:
|
if so_vertical_val:
|
||||||
vertical_map = json.loads(settings.VERTICAL_MAP_JSON)
|
val_str = str(so_vertical_val)
|
||||||
vertical_map_rev = {str(v): k for k, v in vertical_map.items()}
|
if val_str.startswith("[I:"):
|
||||||
if val_str in vertical_map_rev:
|
val_str = val_str.split(":")[1].strip("]")
|
||||||
crm_industry_name = vertical_map_rev[val_str]
|
|
||||||
logger.info(f"Detected CRM Vertical Override: {so_vertical_val} -> {crm_industry_name}")
|
try:
|
||||||
except Exception as ex:
|
vertical_map = json.loads(settings.VERTICAL_MAP_JSON)
|
||||||
logger.error(f"Error mapping vertical ID {val_str}: {ex}")
|
vertical_map_rev = {str(v): k for k, v in vertical_map.items()}
|
||||||
|
if val_str in vertical_map_rev:
|
||||||
|
crm_industry_name = vertical_map_rev[val_str]
|
||||||
|
logger.info(f"Detected CRM Vertical Override: {so_vertical_val} -> {crm_industry_name}")
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error(f"Error mapping vertical ID {val_str}: {ex}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Failed to fetch contact details for {contact_id}: {e}")
|
logger.error(f"Failed to fetch contact details for {contact_id}: {e}")
|
||||||
|
# Critical failure: Without contact details, we cannot provision correctly.
|
||||||
|
# Raising exception triggers a retry.
|
||||||
|
raise Exception(f"SuperOffice API Failure: {e}")
|
||||||
|
|
||||||
# --- 3. Company Explorer Provisioning ---
|
# --- 3. Company Explorer Provisioning ---
|
||||||
ce_url = f"{settings.COMPANY_EXPLORER_URL}/api/provision/superoffice-contact"
|
ce_url = f"{settings.COMPANY_EXPLORER_URL}/api/provision/superoffice-contact"
|
||||||
|
|||||||
@@ -182,9 +182,16 @@ http {
|
|||||||
# Trailing Slash STRIPS the /connector/ prefix!
|
# Trailing Slash STRIPS the /connector/ prefix!
|
||||||
# So /connector/dashboard -> /dashboard
|
# So /connector/dashboard -> /dashboard
|
||||||
proxy_pass http://connector-superoffice:8000/;
|
proxy_pass http://connector-superoffice:8000/;
|
||||||
|
|
||||||
|
# Standard Proxy Headers
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# Websocket Support (just in case)
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user