Skip to content

Commit d5aee76

Browse files
Daisychain connector
1 parent cc42389 commit d5aee76

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

parsons/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
("parsons.controlshift.controlshift", "Controlshift"),
5151
("parsons.copper.copper", "Copper"),
5252
("parsons.crowdtangle.crowdtangle", "CrowdTangle"),
53+
("parsons.daisychain.daisychain", "Daisychain"),
5354
("parsons.databases.database_connector", "DatabaseConnector"),
5455
("parsons.databases.discover_database", "discover_database"),
5556
("parsons.databases.db_sync", "DBSync"),

parsons/daisychain/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from parsons.daisychain.daisychain import Daisychain
2+
3+
__all__ = ["Daisychain"]

parsons/daisychain/daisychain.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
from parsons.utilities.api_connector import APIConnector
2+
from typing import Union, Optional
3+
4+
5+
class Daisychain:
6+
def __init__(self, api_token: str):
7+
self.connection = APIConnector(
8+
"https://go.daisychain.app/api/v1/", headers={"X-API-Token": api_token}
9+
)
10+
11+
def request(
12+
self,
13+
endpoint: str,
14+
method: str,
15+
data_key: str,
16+
json: Optional[Union[list, dict]] = None,
17+
) -> list[dict]:
18+
"""Get request with pagination."""
19+
results = []
20+
response = self.connection.request(endpoint, method, json=json)
21+
self.connection.validate_response(response)
22+
response_data = response.json()
23+
results.extend(response_data.get(data_key, []))
24+
while response_data.get("meta", {}).get("next_page"):
25+
response = self.connection.request(
26+
url=endpoint,
27+
req_type=method,
28+
json=json,
29+
params={"page": response["meta"]["next_page"]},
30+
)
31+
self.connection.validate_response(response)
32+
response_data = response.json()
33+
results.extend(response_data.get(data_key, []))
34+
return results
35+
36+
def find_person(
37+
self, email_address: str | None = None, phone_number: str | None = None
38+
) -> list[dict]:
39+
"""
40+
Find a person by email address and/or phone number.
41+
42+
If multiple parameters are provided, they will be
43+
combined with AND logic. All parameters are optional,
44+
but at least one must be provided.
45+
46+
Parameters:
47+
email_address (string):
48+
Email address of the person to match. This is a case
49+
insensitive match. In Daisychain it is possible for
50+
multiple people records to have the same email address.
51+
phone_number (string):
52+
Phone number of the person to match. In Daisychain
53+
it is possible for multiple people records to have the
54+
same phone number. We will do our best to parse any
55+
string provided, but E.164 format is preferred
56+
57+
Returns:
58+
a list of person dictionaries
59+
"""
60+
assert (
61+
email_address or phone_number
62+
), "At least one of email address or phone number must be provided."
63+
payload: dict[str, dict[str, str]] = {"person": {}}
64+
if email_address:
65+
payload["person"]["email"] = email_address
66+
if phone_number:
67+
payload["person"]["phone_number"] = phone_number
68+
69+
result = self.request("people/match", "post", data_key="people", json=payload)
70+
71+
return result
72+
73+
def post_action(
74+
self,
75+
email_address: str | None = None,
76+
phone_number: str | None = None,
77+
first_name: str | None = None,
78+
last_name: str | None = None,
79+
addresses: list[dict] | None = None,
80+
email_opt_in: bool = False,
81+
sms_opt_in: bool = False,
82+
action_data: dict | None = None,
83+
) -> str:
84+
"""Record an action on a person in Daisychain.
85+
86+
Actions are events that are associated with People. They're
87+
things that people have done or things that have happened
88+
to them.
89+
90+
Actions of this kind have no defined schema
91+
and can contain any data that you want, represented as
92+
json. The actions will appear in the timeline view of the
93+
person they are associated with and can be used to trigger
94+
automations.
95+
96+
If used as the trigger for an automation it is possible to
97+
write a jmespath expression in the Daisychain automation
98+
builder to match against the data in the action. This can
99+
be used to filter automation executions based on the data
100+
you or another system provides. For example, you could
101+
create an action with a action_data field of {"type":
102+
"donation"} and then use the jmespath expression
103+
action.type == 'donation' to match against it while
104+
authoring an automation. This feature can be used to
105+
create powerful automations that can be triggered by any
106+
external system that can make an API call.
107+
108+
Person Creation and Match
109+
110+
The action creation endpoint is designed to allow other
111+
systems to create actions for people in Daisychain without
112+
knowing in advance whether or not that person already
113+
exists.
114+
115+
It will create a person if one does not exist with the
116+
provided email or phone number. If a person does exist
117+
with the provided email or phone number, the action will
118+
be associated with that person. Daisychain matches on
119+
email and phone number in that order of priority. If
120+
different people exist with the provided email and phone
121+
number, the action will be associated with the person with
122+
the matching email.
123+
124+
Parameters:
125+
126+
Returns:
127+
person id (string)
128+
129+
"""
130+
assert (
131+
email_address or phone_number
132+
), "At least one of email address or phone number must be provided."
133+
if not action_data:
134+
action_data = {}
135+
payload = {
136+
"person": {
137+
"first_name": first_name,
138+
"last_name": last_name,
139+
"addresses": addresses,
140+
"phones": [{"value": phone_number}],
141+
"emails": [{"value": email_address}],
142+
"email_opt_in": email_opt_in,
143+
"phone_opt_in": sms_opt_in,
144+
},
145+
"action_data": action_data,
146+
}
147+
response = self.connection.post_request("actions", json=payload)
148+
person_id = response["person"]["id"]
149+
return person_id

0 commit comments

Comments
 (0)