From 3433986f3efdc8914657b51bea1cd97e94f55dc6 Mon Sep 17 00:00:00 2001 From: Brian Seel Date: Fri, 20 Jan 2023 12:43:22 -0500 Subject: [PATCH 1/3] Fixing a couple issues with the pipeline variable names --- README.md | 2 +- hs_api/.env.template | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a785b7c..ee08dd0 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ pipeline_id = 'my_pipeline_id' client = HubSpotClient( access_token=access_token, - PIPELINE_ID=pipeline_id, + pipeline_id=pipeline_id, ) ``` diff --git a/hs_api/.env.template b/hs_api/.env.template index 42d945b..0087396 100644 --- a/hs_api/.env.template +++ b/hs_api/.env.template @@ -1,5 +1,5 @@ HUBSPOT_ACCESS_TOKEN= -HUBS_PIPELINE_ID= +HUBSPOT_TEST_PIPELINE_ID= HUBSPOT_TEST_ACCESS_TOKEN= -HUBS_TEST_PIPELINE_ID= +HUBSPOT_TEST_PIPELINE_ID= From 9550ecc5d17526bc5801c583c5b26380f736b85f Mon Sep 17 00:00:00 2001 From: Brian Seel Date: Fri, 3 Mar 2023 15:03:03 -0500 Subject: [PATCH 2/3] Wrapping additional API calls. --- hs_api/api/hubspot_api.py | 110 ++++++++++++++++++++++++++++++++++++++ requirements.txt | 13 ++--- setup.py | 2 +- 3 files changed, 118 insertions(+), 7 deletions(-) diff --git a/hs_api/api/hubspot_api.py b/hs_api/api/hubspot_api.py index a795a78..5f8978f 100644 --- a/hs_api/api/hubspot_api.py +++ b/hs_api/api/hubspot_api.py @@ -1,3 +1,4 @@ +from datetime import datetime import time import requests @@ -11,6 +12,8 @@ SimplePublicObjectInput, ) from requests.exceptions import HTTPError +from typing import Dict, Optional +from collections.abc import Generator from hs_api.settings.settings import HUBSPOT_ACCESS_TOKEN, HUBSPOT_PIPELINE_ID @@ -64,6 +67,8 @@ def create_lookup(self): "contact": self._client.crm.contacts.basic_api.create, "company": self._client.crm.companies.basic_api.create, "deal": self._client.crm.deals.basic_api.create, + "ticket": self._client.crm.tickets.basic_api.create, + "email": self._client.crm.objects.emails.basic_api.create, } @property @@ -71,6 +76,7 @@ def search_lookup(self): return { "contact": self._client.crm.contacts.search_api.do_search, "company": self._client.crm.companies.search_api.do_search, + "email": self._client.crm.objects.emails.search_api.do_search, } @property @@ -156,6 +162,29 @@ def find_contact(self, property_name, value): response = self._find("contact", property_name, value, sort) return response.results + def find_contact_iter(self, property_name: str, value: str, limit: int = 20) -> Generator[Dict, None, None]: + """ + Searches for a contact in Hubspot and returns results as a generator + + :param property_name: The field name from Hubspot + :param value: The value to search in the field property_name + :param limit: The number of results to return per iteration + :return: Dictionary of results + """ + sort = [{"propertyName": "hs_object_id", "direction": "ASCENDING"}] + after = 0 + + while True: + response = self._find("contact", property_name, value, sort, limit=limit, after=after) + if not response.results: + break + + yield response.results + + if not response.paging: + break + after = response.paging.next.after + def find_company(self, property_name, value): sort = [{"propertyName": "hs_lastmodifieddate", "direction": "DESCENDING"}] @@ -163,6 +192,29 @@ def find_company(self, property_name, value): response = self._find("company", property_name, value, sort) return response.results + def find_company_iter(self, property_name: str, value: str, limit: int = 20) -> Generator[Dict, None, None]: + """ + Searches for a company in Hubspot and returns results as a generator + + :param property_name: The field name from Hubspot + :param value: The value to search in the field property_name + :param limit: The number of results to return per iteration + :return: Dictionary of results + """ + sort = [{"propertyName": "hs_lastmodifieddate", "direction": "DESCENDING"}] + after = 0 + + while True: + response = self._find("company", property_name, value, sort, limit=limit, after=after) + if not response.results: + break + + yield response.results + + if not response.paging: + break + after = response.paging.next.after + def find_deal(self, property_name, value): pipeline_filter = Filter( property_name="pipeline", operator="EQ", value=self.pipeline_id @@ -195,6 +247,16 @@ def _find_owner_by_id(self, owner_id): response = self._client.crm.owners.owners_api.get_by_id(owner_id=owner_id) return response + def find_all_owners(self): + after = None + while True: + response = self._client.crm.owners.owners_api.get_page(after=after) + yield response + + if not response.paging: + break + after = response.paging.next.after + def find_owner(self, property_name, value): if property_name not in ("id", "email"): raise NameError( @@ -319,6 +381,9 @@ def find_all_tickets( else: after = None + def find_ticket(self, ticket_id): + return self._client.crm.tickets.basic_api.get_by_id(ticket_id) + def find_all_deals( self, filter_name=None, @@ -428,6 +493,37 @@ def create_deal( ) return response + def create_ticket(self, subject, **properties): + properties = dict(subject=subject, **properties) + response = self._create("ticket", properties) + return response + + def create_email(self, hs_timestamp: Optional[datetime] = None, hs_email_direction: Optional[str] = 'EMAIL', + **properties): + """ + See documentation at https://developers.hubspot.com/docs/api/crm/email + + :param hs_timestamp: This field marks the email's time of creation and determines where the email sits on the + record timeline. You can use either a Unix timestamp in milliseconds or UTC format. If not provided, then the + current time is used. + :param hs_email_direction: The direction the email was sent in. Possible values include: + + EMAIL: the email was sent from the CRM or sent and logged to the CRM with the BCC address. + INCOMING_EMAIL: the email was a reply to a logged outgoing email. + + FORWARDED_EMAIL: the email was forwarded to the CRM. + :param properties: Dictionary of properties as documented on hubspot + :return: + """ + if not hs_timestamp: + hs_timestamp = int(datetime.now().timestamp()) + else: + hs_timestamp = int(hs_timestamp.timestamp()) + + properties = dict(hs_timestamp=hs_timestamp, hs_email_direction=hs_email_direction, **properties) + response = self._create("email", properties) + return response + def delete_contact(self, value, property_name=None): try: public_gdpr_delete_input = PublicGdprDeleteInput( @@ -455,6 +551,20 @@ def delete_deal(self, deal_id): except ApiException as e: print(f"Exception when deleting deal: {e}\n") + def delete_ticket(self, ticket_id): + try: + api_response = self._client.crm.tickets.basic_api.archive(ticket_id) + return api_response + except ApiException as e: + print(f"Exception when deleting ticket: {e}\n") + + def delete_email(self, email_id): + try: + api_response = self._client.crm.objects.emails.basic_api.archive(email_id) + return api_response + except ApiException as e: + print(f"Exception when deleting email: {e}\n") + def update_company(self, object_id, **properties): response = self._update("company", object_id, properties) return response diff --git a/requirements.txt b/requirements.txt index e9f6f99..be78bd4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ requests -isort==5.10.1 -flake8==4.0.1 -black==22.3.0 -pytest==6.2.5 -python-dotenv==0.19.2 -hubspot-api-client==5.0.1 +isort==5.12.0 +flake8==6.0.0 +black==23.1.0 +pytest==7.2.2 +python-dotenv==1.0.0 +hubspot-api-client==7.5.0 +tenacity==8.2.2 diff --git a/setup.py b/setup.py index 4bd2ece..3e230a1 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,6 @@ description="Superscript Hubspot API", author="Superscript", author_email="paul.lucas@gosuperscript.com", - install_requires=["requests", "python-dotenv==0.19.2", "hubspot-api-client==5.0.1"], + install_requires=["requests", "python-dotenv==0.19.2", "hubspot-api-client==7.5.0"], packages=find_packages(include=["hs_api*"]), ) From e66dc33168e1d432aad8342c46a0ee4c1de686d9 Mon Sep 17 00:00:00 2001 From: awornast Date: Wed, 5 Apr 2023 17:07:03 +0100 Subject: [PATCH 3/3] lintz --- hs_api/api/hubspot_api.py | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/hs_api/api/hubspot_api.py b/hs_api/api/hubspot_api.py index 871de8e..3ee9a2e 100644 --- a/hs_api/api/hubspot_api.py +++ b/hs_api/api/hubspot_api.py @@ -1,6 +1,8 @@ -from datetime import datetime import time +from collections.abc import Generator +from datetime import datetime from math import ceil +from typing import Dict, Optional import requests from hubspot import HubSpot @@ -13,8 +15,6 @@ SimplePublicObjectInput, ) from requests.exceptions import HTTPError -from typing import Dict, Optional -from collections.abc import Generator from hs_api.settings.settings import HUBSPOT_ACCESS_TOKEN, HUBSPOT_PIPELINE_ID @@ -163,7 +163,9 @@ def find_contact(self, property_name, value): response = self._find("contact", property_name, value, sort) return response.results - def find_contact_iter(self, property_name: str, value: str, limit: int = 20) -> Generator[Dict, None, None]: + def find_contact_iter( + self, property_name: str, value: str, limit: int = 20 + ) -> Generator[Dict, None, None]: """ Searches for a contact in Hubspot and returns results as a generator @@ -176,7 +178,9 @@ def find_contact_iter(self, property_name: str, value: str, limit: int = 20) -> after = 0 while True: - response = self._find("contact", property_name, value, sort, limit=limit, after=after) + response = self._find( + "contact", property_name, value, sort, limit=limit, after=after + ) if not response.results: break @@ -193,7 +197,9 @@ def find_company(self, property_name, value): response = self._find("company", property_name, value, sort) return response.results - def find_company_iter(self, property_name: str, value: str, limit: int = 20) -> Generator[Dict, None, None]: + def find_company_iter( + self, property_name: str, value: str, limit: int = 20 + ) -> Generator[Dict, None, None]: """ Searches for a company in Hubspot and returns results as a generator @@ -206,7 +212,9 @@ def find_company_iter(self, property_name: str, value: str, limit: int = 20) -> after = 0 while True: - response = self._find("company", property_name, value, sort, limit=limit, after=after) + response = self._find( + "company", property_name, value, sort, limit=limit, after=after + ) if not response.results: break @@ -382,7 +390,6 @@ def find_all_tickets( else: after = None - def find_ticket(self, ticket_id): return self._client.crm.tickets.basic_api.get_by_id(ticket_id) @@ -581,8 +588,12 @@ def create_ticket(self, subject, **properties): response = self._create("ticket", properties) return response - def create_email(self, hs_timestamp: Optional[datetime] = None, hs_email_direction: Optional[str] = 'EMAIL', - **properties): + def create_email( + self, + hs_timestamp: Optional[datetime] = None, + hs_email_direction: Optional[str] = "EMAIL", + **properties, + ): """ See documentation at https://developers.hubspot.com/docs/api/crm/email @@ -603,7 +614,11 @@ def create_email(self, hs_timestamp: Optional[datetime] = None, hs_email_directi else: hs_timestamp = int(hs_timestamp.timestamp()) - properties = dict(hs_timestamp=hs_timestamp, hs_email_direction=hs_email_direction, **properties) + properties = dict( + hs_timestamp=hs_timestamp, + hs_email_direction=hs_email_direction, + **properties, + ) response = self._create("email", properties) return response