Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hs_api/.env.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
HUBSPOT_ACCESS_TOKEN=
HUBS_PIPELINE_ID=
HUBSPOT_TEST_PIPELINE_ID=

HUBSPOT_TEST_ACCESS_TOKEN=
HUBSPOT_TEST_PIPELINE_ID=
126 changes: 126 additions & 0 deletions hs_api/api/hubspot_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
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
Expand Down Expand Up @@ -65,13 +68,16 @@ 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
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
Expand Down Expand Up @@ -157,13 +163,67 @@ 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"}]

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
Expand Down Expand Up @@ -196,6 +256,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(
Expand Down Expand Up @@ -320,6 +390,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_contacts_in_list(self, contact_list_id: str) -> object:
"""
This function will return all contacts in a contact list.
Expand Down Expand Up @@ -510,6 +583,45 @@ 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(
Expand Down Expand Up @@ -537,6 +649,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
Expand Down
13 changes: 7 additions & 6 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -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

2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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*"]),
)