From 0306b43584b36d604f8570b06d5058f8363ad299 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Wed, 8 Jul 2026 16:02:33 +0000 Subject: [PATCH 1/2] logging for debug purposes --- etl/hubspot/hubspot_deal_differ.py | 61 ++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/etl/hubspot/hubspot_deal_differ.py b/etl/hubspot/hubspot_deal_differ.py index def629947..ac68bf642 100644 --- a/etl/hubspot/hubspot_deal_differ.py +++ b/etl/hubspot/hubspot_deal_differ.py @@ -5,6 +5,9 @@ from backend.app.db.models.hubspot_deal_data import HubspotDealData from domain.abri.models import UNSUCCESSFUL_OUTCOMES from etl.hubspot.project_data import ProjectData from etl.hubspot.utils import parse_hs_bool, parse_hs_date, parse_hs_int +from utilities.logger import setup_logger + +logger = setup_logger() class HubspotDealDiffer: @@ -192,6 +195,13 @@ class HubspotDealDiffer: new_listing: Optional[Dict[str, str]], old_deal: HubspotDealData, ) -> Optional[Dict[str, Any]]: + logger.info( + "Evaluating Abri triggers for HubSpot deal %s " + "(project_code=%r, associated_project_id=%s)", + hubspot_deal_id, + new_deal.get("project_code"), + new_project["project_id"] if new_project is not None else None, + ) flows: List[str] = [] if HubspotDealDiffer.check_for_abri_job_amendment( new_deal=new_deal, new_project=new_project, old_deal=old_deal @@ -211,8 +221,15 @@ class HubspotDealDiffer: flows.append("abandon_job") if not flows: + logger.info( + "No Abri flows triggered for HubSpot deal %s", hubspot_deal_id + ) return None + logger.info( + "Abri flows triggered for HubSpot deal %s: %s", hubspot_deal_id, flows + ) + confirmed_survey_date = parse_hs_date(new_deal.get("confirmed_survey_date")) last_submission_date = parse_hs_date(new_deal.get("last_submission_date")) listing = new_listing or {} @@ -248,10 +265,19 @@ class HubspotDealDiffer: if not HubspotDealDiffer._is_abri_condition_deal(new_deal, new_project): return False + new_survey_date = parse_hs_date(new_deal.get("confirmed_survey_date")) + logger.info( + "Abri job-logging check: old_confirmed_survey_date=%s " + "new_confirmed_survey_date=%s", + old_deal.confirmed_survey_date, + new_survey_date, + ) + + # Only a first-time survey date (previously empty) logs a new job. if old_deal.confirmed_survey_date is not None: return False - return parse_hs_date(new_deal.get("confirmed_survey_date")) is not None + return new_survey_date is not None @staticmethod def check_for_abri_job_amendment( @@ -285,10 +311,21 @@ class HubspotDealDiffer: if not HubspotDealDiffer._is_abri_condition_deal(new_deal, new_project): return False + new_commencement_date = parse_hs_date( + new_deal.get("expected_commencement_date") + ) + logger.info( + "Abri tenant-data-fetch check: old_commencement_date=%s " + "new_commencement_date=%s", + old_deal.expected_commencement_date, + new_commencement_date, + ) + + # Only a first-time commencement date (previously empty) fires the fetch. if old_deal.expected_commencement_date is not None: return False - return parse_hs_date(new_deal.get("expected_commencement_date")) is not None + return new_commencement_date is not None @staticmethod def check_for_abri_deal_abandonment( @@ -334,10 +371,18 @@ class HubspotDealDiffer: new_deal: Dict[str, str], new_project: Optional[ProjectData] ) -> bool: if new_project is not None: - return ( + is_match = ( new_project["project_id"] == HubspotDealDiffer.ABRI_CONDITION_ASSOCIATED_PROJECT_ID ) + logger.info( + "Abri condition-deal check via associated project: " + "project_id=%s expected=%s match=%s", + new_project["project_id"], + HubspotDealDiffer.ABRI_CONDITION_ASSOCIATED_PROJECT_ID, + is_match, + ) + return is_match # TEST_ABRI_PROJECT_CODE_OVERRIDE lets a non-production deploy fire on a # sandbox deal's own project code; unset (the production default) falls @@ -347,7 +392,15 @@ class HubspotDealDiffer: or HubspotDealDiffer.ABRI_CONDITION_PROJECT_CODE ) new_project_code = (new_deal.get("project_code") or "").lower() - return new_project_code == expected_code.lower() + is_match = new_project_code == expected_code.lower() + logger.info( + "Abri condition-deal check via project_code: " + "new_project_code=%r expected=%r match=%s", + new_project_code, + expected_code.lower(), + is_match, + ) + return is_match @staticmethod def _has_valid_pashub_link(new_pashub_link: str) -> bool: From a065857e48f099512a54aa0a1abcaf30e3fdea41 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Wed, 8 Jul 2026 16:06:41 +0000 Subject: [PATCH 2/2] more detailed logging --- etl/hubspot/hubspot_deal_differ.py | 92 ++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 16 deletions(-) diff --git a/etl/hubspot/hubspot_deal_differ.py b/etl/hubspot/hubspot_deal_differ.py index ac68bf642..5107fb8a6 100644 --- a/etl/hubspot/hubspot_deal_differ.py +++ b/etl/hubspot/hubspot_deal_differ.py @@ -202,22 +202,36 @@ class HubspotDealDiffer: new_deal.get("project_code"), new_project["project_id"] if new_project is not None else None, ) + amend_job = HubspotDealDiffer.check_for_abri_job_amendment( + new_deal=new_deal, new_project=new_project, old_deal=old_deal + ) + log_job = HubspotDealDiffer.check_for_abri_job_logging( + new_deal=new_deal, new_project=new_project, old_deal=old_deal + ) + sync_tenant_data = HubspotDealDiffer.check_for_abri_tenant_data_fetch( + new_deal=new_deal, new_project=new_project, old_deal=old_deal + ) + abandon_job = HubspotDealDiffer.check_for_abri_deal_abandonment( + new_deal=new_deal, new_project=new_project, old_deal=old_deal + ) + logger.info( + "Abri trigger results for HubSpot deal %s: " + "amend_job=%s log_job=%s sync_tenant_data=%s abandon_job=%s", + hubspot_deal_id, + amend_job, + log_job, + sync_tenant_data, + abandon_job, + ) + flows: List[str] = [] - if HubspotDealDiffer.check_for_abri_job_amendment( - new_deal=new_deal, new_project=new_project, old_deal=old_deal - ): + if amend_job: flows.append("amend_job") - if HubspotDealDiffer.check_for_abri_job_logging( - new_deal=new_deal, new_project=new_project, old_deal=old_deal - ): + if log_job: flows.append("log_job") - if HubspotDealDiffer.check_for_abri_tenant_data_fetch( - new_deal=new_deal, new_project=new_project, old_deal=old_deal - ): + if sync_tenant_data: flows.append("sync_tenant_data") - if HubspotDealDiffer.check_for_abri_deal_abandonment( - new_deal=new_deal, new_project=new_project, old_deal=old_deal - ): + if abandon_job: flows.append("abandon_job") if not flows: @@ -233,7 +247,7 @@ class HubspotDealDiffer: confirmed_survey_date = parse_hs_date(new_deal.get("confirmed_survey_date")) last_submission_date = parse_hs_date(new_deal.get("last_submission_date")) listing = new_listing or {} - return { + message: Dict[str, Any] = { "hubspot_deal_id": hubspot_deal_id, "flows": flows, "place_ref": listing.get("owner_property_id"), @@ -255,6 +269,12 @@ class HubspotDealDiffer: # the other flows, which ignore it. "outcome": new_deal.get("outcome"), } + logger.info( + "Constructed Abri message for HubSpot deal %s: %s", + hubspot_deal_id, + message, + ) + return message @staticmethod def check_for_abri_job_logging( @@ -288,6 +308,18 @@ class HubspotDealDiffer: if not HubspotDealDiffer._is_abri_condition_deal(new_deal, new_project): return False + new_survey_date = parse_hs_date(new_deal.get("confirmed_survey_date")) + new_survey_time = new_deal.get("confirmed_survey_time") + logger.info( + "Abri job-amendment check: " + "old_confirmed_survey_date=%s new_confirmed_survey_date=%s " + "old_confirmed_survey_time=%s new_confirmed_survey_time=%s", + old_deal.confirmed_survey_date, + new_survey_date, + old_deal.confirmed_survey_time, + new_survey_time, + ) + # A first-time survey date logs a new job, not an amendment. if old_deal.confirmed_survey_date is None: return False @@ -296,11 +328,10 @@ class HubspotDealDiffer: # check, but the amendoptiappt route cannot express a cancellation. # Cancellation semantics (the "delete" action / canceljob route) need # a deliberate decision before this trigger is wired to a consumer. - new_survey_date = parse_hs_date(new_deal.get("confirmed_survey_date")) if old_deal.confirmed_survey_date != new_survey_date: return True - return old_deal.confirmed_survey_time != new_deal.get("confirmed_survey_time") + return old_deal.confirmed_survey_time != new_survey_time @staticmethod def check_for_abri_tenant_data_fetch( @@ -336,6 +367,19 @@ class HubspotDealDiffer: if not HubspotDealDiffer._is_abri_condition_deal(new_deal, new_project): return False + logger.info( + "Abri deal-abandonment check: " + "old_number_of_attempts=%s old_outcome=%r " + "new_number_of_attempts=%s new_outcome=%r " + "attempts_threshold=%s", + old_deal.number_of_attempts, + old_deal.outcome, + new_deal.get("number_of_attempts"), + new_deal.get("outcome"), + HubspotDealDiffer.ABANDONMENT_ATTEMPTS_THRESHOLD, + ) + + # Already abandoned before this change — nothing new to fire. if HubspotDealDiffer._is_abandoned( old_deal.number_of_attempts, old_deal.outcome ): @@ -362,9 +406,25 @@ class HubspotDealDiffer: attempts is None or attempts < HubspotDealDiffer.ABANDONMENT_ATTEMPTS_THRESHOLD ): + logger.info( + "Abri _is_abandoned=False (attempts gate): " + "raw_attempts=%r parsed_attempts=%s threshold=%s", + number_of_attempts, + attempts, + HubspotDealDiffer.ABANDONMENT_ATTEMPTS_THRESHOLD, + ) return False - return (outcome or "").lower() in UNSUCCESSFUL_OUTCOMES + outcome_is_unsuccessful = (outcome or "").lower() in UNSUCCESSFUL_OUTCOMES + logger.info( + "Abri _is_abandoned=%s (outcome gate): " + "parsed_attempts=%s outcome=%r outcome_is_unsuccessful=%s", + outcome_is_unsuccessful, + attempts, + outcome, + outcome_is_unsuccessful, + ) + return outcome_is_unsuccessful @staticmethod def _is_abri_condition_deal(