ingestr 0.14.9__py3-none-any.whl → 0.14.91__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ingestr might be problematic. Click here for more details.
- ingestr/src/buildinfo.py +1 -1
- ingestr/src/factory.py +2 -0
- ingestr/src/mailchimp/__init__.py +126 -0
- ingestr/src/mailchimp/helpers.py +226 -0
- ingestr/src/mailchimp/settings.py +164 -0
- ingestr/src/sources.py +26 -0
- {ingestr-0.14.9.dist-info → ingestr-0.14.91.dist-info}/METADATA +1 -1
- {ingestr-0.14.9.dist-info → ingestr-0.14.91.dist-info}/RECORD +11 -8
- {ingestr-0.14.9.dist-info → ingestr-0.14.91.dist-info}/WHEEL +0 -0
- {ingestr-0.14.9.dist-info → ingestr-0.14.91.dist-info}/entry_points.txt +0 -0
- {ingestr-0.14.9.dist-info → ingestr-0.14.91.dist-info}/licenses/LICENSE.md +0 -0
ingestr/src/buildinfo.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version = "v0.14.
|
|
1
|
+
version = "v0.14.91"
|
ingestr/src/factory.py
CHANGED
|
@@ -64,6 +64,7 @@ from ingestr.src.sources import (
|
|
|
64
64
|
LinearSource,
|
|
65
65
|
LinkedInAdsSource,
|
|
66
66
|
LocalCsvSource,
|
|
67
|
+
MailchimpSource,
|
|
67
68
|
MixpanelSource,
|
|
68
69
|
MondaySource,
|
|
69
70
|
MongoDbSource,
|
|
@@ -219,6 +220,7 @@ class SourceDestinationFactory:
|
|
|
219
220
|
"wise": WiseSource,
|
|
220
221
|
"plusvibeai": PlusVibeAISource,
|
|
221
222
|
"monday": MondaySource,
|
|
223
|
+
"mailchimp": MailchimpSource,
|
|
222
224
|
}
|
|
223
225
|
destinations: Dict[str, Type[DestinationProtocol]] = {
|
|
224
226
|
"bigquery": BigQueryDestination,
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mailchimp source for data extraction via REST API.
|
|
3
|
+
|
|
4
|
+
This source provides access to Mailchimp account data.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Iterable, Iterator
|
|
8
|
+
|
|
9
|
+
import dlt
|
|
10
|
+
from dlt.sources import DltResource
|
|
11
|
+
|
|
12
|
+
from ingestr.src.http_client import create_client
|
|
13
|
+
from ingestr.src.mailchimp.helpers import (
|
|
14
|
+
create_merge_resource,
|
|
15
|
+
create_nested_resource,
|
|
16
|
+
create_replace_resource,
|
|
17
|
+
)
|
|
18
|
+
from ingestr.src.mailchimp.settings import (
|
|
19
|
+
MERGE_ENDPOINTS,
|
|
20
|
+
NESTED_ENDPOINTS,
|
|
21
|
+
REPLACE_ENDPOINTS,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dlt.source(max_table_nesting=0, name="mailchimp_source")
|
|
26
|
+
def mailchimp_source(
|
|
27
|
+
api_key: str,
|
|
28
|
+
server: str,
|
|
29
|
+
) -> Iterable[DltResource]:
|
|
30
|
+
"""
|
|
31
|
+
Mailchimp data source.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
api_key: Mailchimp API key for authentication
|
|
35
|
+
server: Server prefix (e.g., 'us10')
|
|
36
|
+
|
|
37
|
+
Yields:
|
|
38
|
+
DltResource: Data resources for Mailchimp data
|
|
39
|
+
"""
|
|
40
|
+
base_url = f"https://{server}.api.mailchimp.com/3.0"
|
|
41
|
+
session = create_client()
|
|
42
|
+
auth = ("anystring", api_key)
|
|
43
|
+
|
|
44
|
+
@dlt.resource(
|
|
45
|
+
name="account",
|
|
46
|
+
write_disposition="replace",
|
|
47
|
+
)
|
|
48
|
+
def fetch_account() -> Iterator[dict[str, Any]]:
|
|
49
|
+
"""
|
|
50
|
+
Fetch account information from Mailchimp.
|
|
51
|
+
|
|
52
|
+
Table format: account (no parameters needed)
|
|
53
|
+
"""
|
|
54
|
+
response = session.get(f"{base_url}/", auth=auth)
|
|
55
|
+
response.raise_for_status()
|
|
56
|
+
data = response.json()
|
|
57
|
+
yield data
|
|
58
|
+
|
|
59
|
+
# Create resources dynamically
|
|
60
|
+
resources = [fetch_account]
|
|
61
|
+
|
|
62
|
+
# Create merge resources (with incremental loading)
|
|
63
|
+
for (
|
|
64
|
+
resource_name,
|
|
65
|
+
endpoint_path,
|
|
66
|
+
data_key,
|
|
67
|
+
primary_key,
|
|
68
|
+
incremental_key,
|
|
69
|
+
) in MERGE_ENDPOINTS:
|
|
70
|
+
resources.append(
|
|
71
|
+
create_merge_resource(
|
|
72
|
+
base_url,
|
|
73
|
+
session,
|
|
74
|
+
auth,
|
|
75
|
+
resource_name,
|
|
76
|
+
endpoint_path,
|
|
77
|
+
data_key,
|
|
78
|
+
primary_key,
|
|
79
|
+
incremental_key,
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Create replace resources (without incremental loading)
|
|
84
|
+
for replace_endpoint in REPLACE_ENDPOINTS:
|
|
85
|
+
resource_name, endpoint_path, data_key, pk = replace_endpoint
|
|
86
|
+
resources.append(
|
|
87
|
+
create_replace_resource(
|
|
88
|
+
base_url,
|
|
89
|
+
session,
|
|
90
|
+
auth,
|
|
91
|
+
resource_name,
|
|
92
|
+
endpoint_path,
|
|
93
|
+
data_key,
|
|
94
|
+
pk,
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Create nested resources (depend on parent resources)
|
|
99
|
+
for nested_endpoint in NESTED_ENDPOINTS:
|
|
100
|
+
(
|
|
101
|
+
parent_name,
|
|
102
|
+
parent_path,
|
|
103
|
+
parent_key,
|
|
104
|
+
parent_id_field,
|
|
105
|
+
nested_name,
|
|
106
|
+
nested_path,
|
|
107
|
+
nested_key,
|
|
108
|
+
pk,
|
|
109
|
+
) = nested_endpoint
|
|
110
|
+
resources.append(
|
|
111
|
+
create_nested_resource(
|
|
112
|
+
base_url,
|
|
113
|
+
session,
|
|
114
|
+
auth,
|
|
115
|
+
parent_name,
|
|
116
|
+
parent_path,
|
|
117
|
+
parent_key,
|
|
118
|
+
parent_id_field,
|
|
119
|
+
nested_name,
|
|
120
|
+
nested_path,
|
|
121
|
+
nested_key,
|
|
122
|
+
pk,
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return tuple(resources)
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Helper functions for Mailchimp source.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Iterator
|
|
6
|
+
|
|
7
|
+
import dlt
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def fetch_paginated(
|
|
11
|
+
session,
|
|
12
|
+
url: str,
|
|
13
|
+
auth: tuple,
|
|
14
|
+
data_key: str | None = None,
|
|
15
|
+
) -> Iterator[dict[str, Any]]:
|
|
16
|
+
"""
|
|
17
|
+
Helper function to fetch paginated data from Mailchimp API.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
session: HTTP session
|
|
21
|
+
url: API endpoint URL
|
|
22
|
+
auth: Authentication tuple
|
|
23
|
+
data_key: Key in response containing the data array (if None, return whole response)
|
|
24
|
+
|
|
25
|
+
Yields:
|
|
26
|
+
Individual items from the paginated response
|
|
27
|
+
"""
|
|
28
|
+
offset = 0
|
|
29
|
+
count = 1000 # Maximum allowed by Mailchimp
|
|
30
|
+
|
|
31
|
+
while True:
|
|
32
|
+
params = {"count": count, "offset": offset}
|
|
33
|
+
response = session.get(url, auth=auth, params=params)
|
|
34
|
+
response.raise_for_status()
|
|
35
|
+
data = response.json()
|
|
36
|
+
|
|
37
|
+
# Extract items from response
|
|
38
|
+
if data_key and data_key in data:
|
|
39
|
+
items = data[data_key]
|
|
40
|
+
elif isinstance(data, list):
|
|
41
|
+
items = data
|
|
42
|
+
else:
|
|
43
|
+
# If no data_key specified and response is dict, yield the whole response
|
|
44
|
+
yield data
|
|
45
|
+
break
|
|
46
|
+
|
|
47
|
+
if not items:
|
|
48
|
+
break
|
|
49
|
+
|
|
50
|
+
yield from items
|
|
51
|
+
|
|
52
|
+
# Check if we've received fewer items than requested (last page)
|
|
53
|
+
if len(items) < count:
|
|
54
|
+
break
|
|
55
|
+
|
|
56
|
+
offset += count
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def create_merge_resource(
|
|
60
|
+
base_url: str,
|
|
61
|
+
session,
|
|
62
|
+
auth: tuple,
|
|
63
|
+
name: str,
|
|
64
|
+
path: str,
|
|
65
|
+
key: str,
|
|
66
|
+
pk: str,
|
|
67
|
+
ik: str,
|
|
68
|
+
):
|
|
69
|
+
"""
|
|
70
|
+
Create a DLT resource with merge disposition for incremental loading.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
base_url: Base API URL
|
|
74
|
+
session: HTTP session
|
|
75
|
+
auth: Authentication tuple
|
|
76
|
+
name: Resource name
|
|
77
|
+
path: API endpoint path
|
|
78
|
+
key: Data key in response
|
|
79
|
+
pk: Primary key field
|
|
80
|
+
ik: Incremental key field
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
DLT resource function
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
@dlt.resource(
|
|
87
|
+
name=name,
|
|
88
|
+
write_disposition="merge",
|
|
89
|
+
primary_key=pk,
|
|
90
|
+
)
|
|
91
|
+
def fetch_data(
|
|
92
|
+
updated_at: dlt.sources.incremental[str] = dlt.sources.incremental(
|
|
93
|
+
ik, initial_value=None
|
|
94
|
+
),
|
|
95
|
+
) -> Iterator[dict[str, Any]]:
|
|
96
|
+
url = f"{base_url}/{path}"
|
|
97
|
+
yield from fetch_paginated(session, url, auth, data_key=key)
|
|
98
|
+
|
|
99
|
+
return fetch_data
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def create_replace_resource(
|
|
103
|
+
base_url: str,
|
|
104
|
+
session,
|
|
105
|
+
auth: tuple,
|
|
106
|
+
name: str,
|
|
107
|
+
path: str,
|
|
108
|
+
key: str,
|
|
109
|
+
pk: str | None,
|
|
110
|
+
):
|
|
111
|
+
"""
|
|
112
|
+
Create a DLT resource with replace disposition.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
base_url: Base API URL
|
|
116
|
+
session: HTTP session
|
|
117
|
+
auth: Authentication tuple
|
|
118
|
+
name: Resource name
|
|
119
|
+
path: API endpoint path
|
|
120
|
+
key: Data key in response
|
|
121
|
+
pk: Primary key field (optional)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
DLT resource function
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def fetch_data() -> Iterator[dict[str, Any]]:
|
|
128
|
+
url = f"{base_url}/{path}"
|
|
129
|
+
yield from fetch_paginated(session, url, auth, data_key=key)
|
|
130
|
+
|
|
131
|
+
# Apply the resource decorator with conditional primary_key
|
|
132
|
+
if pk is not None:
|
|
133
|
+
return dlt.resource(
|
|
134
|
+
fetch_data,
|
|
135
|
+
name=name,
|
|
136
|
+
write_disposition="replace",
|
|
137
|
+
primary_key=pk,
|
|
138
|
+
)
|
|
139
|
+
else:
|
|
140
|
+
return dlt.resource(
|
|
141
|
+
fetch_data,
|
|
142
|
+
name=name,
|
|
143
|
+
write_disposition="replace",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def create_nested_resource(
|
|
148
|
+
base_url: str,
|
|
149
|
+
session,
|
|
150
|
+
auth: tuple,
|
|
151
|
+
parent_resource_name: str,
|
|
152
|
+
parent_path: str,
|
|
153
|
+
parent_key: str,
|
|
154
|
+
parent_id_field: str,
|
|
155
|
+
nested_name: str,
|
|
156
|
+
nested_path: str,
|
|
157
|
+
nested_key: str | None,
|
|
158
|
+
pk: str | None,
|
|
159
|
+
):
|
|
160
|
+
"""
|
|
161
|
+
Create a nested DLT resource that depends on a parent resource.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
base_url: Base API URL
|
|
165
|
+
session: HTTP session
|
|
166
|
+
auth: Authentication tuple
|
|
167
|
+
parent_resource_name: Name of the parent resource
|
|
168
|
+
parent_path: Parent API endpoint path
|
|
169
|
+
parent_key: Data key in parent response
|
|
170
|
+
parent_id_field: Field name for parent ID
|
|
171
|
+
nested_name: Nested resource name
|
|
172
|
+
nested_path: Nested API endpoint path (with {id} placeholder)
|
|
173
|
+
nested_key: Data key in nested response (None to return whole response)
|
|
174
|
+
pk: Primary key field (optional)
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
DLT resource function
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
def fetch_nested_data() -> Iterator[dict[str, Any]]:
|
|
181
|
+
# First, fetch parent items
|
|
182
|
+
parent_url = f"{base_url}/{parent_path}"
|
|
183
|
+
parent_items = fetch_paginated(session, parent_url, auth, data_key=parent_key)
|
|
184
|
+
|
|
185
|
+
# For each parent item, fetch nested data
|
|
186
|
+
for parent_item in parent_items:
|
|
187
|
+
parent_id = parent_item.get(parent_id_field)
|
|
188
|
+
if parent_id:
|
|
189
|
+
# Build nested URL with parent ID
|
|
190
|
+
nested_url = f"{base_url}/{nested_path.format(id=parent_id)}"
|
|
191
|
+
|
|
192
|
+
# Fetch nested data
|
|
193
|
+
response = session.get(nested_url, auth=auth)
|
|
194
|
+
response.raise_for_status()
|
|
195
|
+
data = response.json()
|
|
196
|
+
|
|
197
|
+
# Extract nested items or return whole response
|
|
198
|
+
if nested_key and nested_key in data:
|
|
199
|
+
items = data[nested_key]
|
|
200
|
+
if isinstance(items, list):
|
|
201
|
+
for item in items:
|
|
202
|
+
# Add parent reference
|
|
203
|
+
item[f"{parent_resource_name}_id"] = parent_id
|
|
204
|
+
yield item
|
|
205
|
+
else:
|
|
206
|
+
items[f"{parent_resource_name}_id"] = parent_id
|
|
207
|
+
yield items
|
|
208
|
+
else:
|
|
209
|
+
# Return whole response with parent reference
|
|
210
|
+
data[f"{parent_resource_name}_id"] = parent_id
|
|
211
|
+
yield data
|
|
212
|
+
|
|
213
|
+
# Apply the resource decorator with conditional primary_key
|
|
214
|
+
if pk is not None:
|
|
215
|
+
return dlt.resource(
|
|
216
|
+
fetch_nested_data,
|
|
217
|
+
name=nested_name,
|
|
218
|
+
write_disposition="replace",
|
|
219
|
+
primary_key=pk,
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
return dlt.resource(
|
|
223
|
+
fetch_nested_data,
|
|
224
|
+
name=nested_name,
|
|
225
|
+
write_disposition="replace",
|
|
226
|
+
)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mailchimp API endpoint configurations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Endpoints with merge disposition (have both primary_key and incremental_key)
|
|
6
|
+
# Format: (resource_name, endpoint_path, data_key, primary_key, incremental_key)
|
|
7
|
+
MERGE_ENDPOINTS = [
|
|
8
|
+
("audiences", "lists", "lists", "id", "date_created"),
|
|
9
|
+
("automations", "automations", "automations", "id", "create_time"),
|
|
10
|
+
("campaigns", "campaigns", "campaigns", "id", "create_time"),
|
|
11
|
+
("connected_sites", "connected-sites", "sites", "id", "updated_at"),
|
|
12
|
+
("conversations", "conversations", "conversations", "id", "last_message.timestamp"),
|
|
13
|
+
("ecommerce_stores", "ecommerce/stores", "stores", "id", "updated_at"),
|
|
14
|
+
("facebook_ads", "facebook-ads", "facebook_ads", "id", "updated_at"),
|
|
15
|
+
("landing_pages", "landing-pages", "landing_pages", "id", "updated_at"),
|
|
16
|
+
("reports", "reports", "reports", "id", "send_time"),
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# Endpoints with replace disposition
|
|
20
|
+
# Format: (resource_name, endpoint_path, data_key, primary_key)
|
|
21
|
+
REPLACE_ENDPOINTS: list[tuple[str, str, str, str | None]] = [
|
|
22
|
+
("account_exports", "account-exports", "exports", None),
|
|
23
|
+
("authorized_apps", "authorized-apps", "apps", "id"),
|
|
24
|
+
("batches", "batches", "batches", None),
|
|
25
|
+
("campaign_folders", "campaign-folders", "folders", "id"),
|
|
26
|
+
("chimp_chatter", "activity-feed/chimp-chatter", "chimp_chatter", None),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Nested endpoints (depend on parent resources)
|
|
30
|
+
# Format: (parent_name, parent_path, parent_key, parent_id_field, nested_name, nested_path, nested_key, pk)
|
|
31
|
+
NESTED_ENDPOINTS: list[tuple[str, str, str, str, str, str, str | None, str | None]] = [
|
|
32
|
+
# Reports nested endpoints
|
|
33
|
+
(
|
|
34
|
+
"reports",
|
|
35
|
+
"reports",
|
|
36
|
+
"reports",
|
|
37
|
+
"id",
|
|
38
|
+
"reports_advice",
|
|
39
|
+
"reports/{id}/advice",
|
|
40
|
+
None,
|
|
41
|
+
None,
|
|
42
|
+
),
|
|
43
|
+
(
|
|
44
|
+
"reports",
|
|
45
|
+
"reports",
|
|
46
|
+
"reports",
|
|
47
|
+
"id",
|
|
48
|
+
"reports_domain_performance",
|
|
49
|
+
"reports/{id}/domain-performance",
|
|
50
|
+
"domains",
|
|
51
|
+
None,
|
|
52
|
+
),
|
|
53
|
+
(
|
|
54
|
+
"reports",
|
|
55
|
+
"reports",
|
|
56
|
+
"reports",
|
|
57
|
+
"id",
|
|
58
|
+
"reports_locations",
|
|
59
|
+
"reports/{id}/locations",
|
|
60
|
+
"locations",
|
|
61
|
+
None,
|
|
62
|
+
),
|
|
63
|
+
(
|
|
64
|
+
"reports",
|
|
65
|
+
"reports",
|
|
66
|
+
"reports",
|
|
67
|
+
"id",
|
|
68
|
+
"reports_sent_to",
|
|
69
|
+
"reports/{id}/sent-to",
|
|
70
|
+
"sent_to",
|
|
71
|
+
None,
|
|
72
|
+
),
|
|
73
|
+
(
|
|
74
|
+
"reports",
|
|
75
|
+
"reports",
|
|
76
|
+
"reports",
|
|
77
|
+
"id",
|
|
78
|
+
"reports_sub_reports",
|
|
79
|
+
"reports/{id}/sub-reports",
|
|
80
|
+
None,
|
|
81
|
+
None,
|
|
82
|
+
),
|
|
83
|
+
(
|
|
84
|
+
"reports",
|
|
85
|
+
"reports",
|
|
86
|
+
"reports",
|
|
87
|
+
"id",
|
|
88
|
+
"reports_unsubscribed",
|
|
89
|
+
"reports/{id}/unsubscribed",
|
|
90
|
+
"unsubscribes",
|
|
91
|
+
None,
|
|
92
|
+
),
|
|
93
|
+
# Lists/Audiences nested endpoints
|
|
94
|
+
(
|
|
95
|
+
"audiences",
|
|
96
|
+
"lists",
|
|
97
|
+
"lists",
|
|
98
|
+
"id",
|
|
99
|
+
"lists_activity",
|
|
100
|
+
"lists/{id}/activity",
|
|
101
|
+
"activity",
|
|
102
|
+
None,
|
|
103
|
+
),
|
|
104
|
+
(
|
|
105
|
+
"audiences",
|
|
106
|
+
"lists",
|
|
107
|
+
"lists",
|
|
108
|
+
"id",
|
|
109
|
+
"lists_clients",
|
|
110
|
+
"lists/{id}/clients",
|
|
111
|
+
"clients",
|
|
112
|
+
None,
|
|
113
|
+
),
|
|
114
|
+
(
|
|
115
|
+
"audiences",
|
|
116
|
+
"lists",
|
|
117
|
+
"lists",
|
|
118
|
+
"id",
|
|
119
|
+
"lists_growth_history",
|
|
120
|
+
"lists/{id}/growth-history",
|
|
121
|
+
"history",
|
|
122
|
+
None,
|
|
123
|
+
),
|
|
124
|
+
(
|
|
125
|
+
"audiences",
|
|
126
|
+
"lists",
|
|
127
|
+
"lists",
|
|
128
|
+
"id",
|
|
129
|
+
"lists_interest_categories",
|
|
130
|
+
"lists/{id}/interest-categories",
|
|
131
|
+
"categories",
|
|
132
|
+
None,
|
|
133
|
+
),
|
|
134
|
+
(
|
|
135
|
+
"audiences",
|
|
136
|
+
"lists",
|
|
137
|
+
"lists",
|
|
138
|
+
"id",
|
|
139
|
+
"lists_locations",
|
|
140
|
+
"lists/{id}/locations",
|
|
141
|
+
"locations",
|
|
142
|
+
None,
|
|
143
|
+
),
|
|
144
|
+
(
|
|
145
|
+
"audiences",
|
|
146
|
+
"lists",
|
|
147
|
+
"lists",
|
|
148
|
+
"id",
|
|
149
|
+
"lists_merge_fields",
|
|
150
|
+
"lists/{id}/merge-fields",
|
|
151
|
+
"merge_fields",
|
|
152
|
+
None,
|
|
153
|
+
),
|
|
154
|
+
(
|
|
155
|
+
"audiences",
|
|
156
|
+
"lists",
|
|
157
|
+
"lists",
|
|
158
|
+
"id",
|
|
159
|
+
"lists_segments",
|
|
160
|
+
"lists/{id}/segments",
|
|
161
|
+
"segments",
|
|
162
|
+
None,
|
|
163
|
+
),
|
|
164
|
+
]
|
ingestr/src/sources.py
CHANGED
|
@@ -3971,3 +3971,29 @@ class MondaySource:
|
|
|
3971
3971
|
).with_resources(table_name)
|
|
3972
3972
|
except ResourcesNotFoundError:
|
|
3973
3973
|
raise UnsupportedResourceError(table_name, "Monday")
|
|
3974
|
+
|
|
3975
|
+
|
|
3976
|
+
class MailchimpSource:
|
|
3977
|
+
def handles_incrementality(self) -> bool:
|
|
3978
|
+
return False
|
|
3979
|
+
|
|
3980
|
+
def dlt_source(self, uri: str, table: str, **kwargs):
|
|
3981
|
+
parsed_uri = urlparse(uri)
|
|
3982
|
+
query_params = parse_qs(parsed_uri.query)
|
|
3983
|
+
api_key = query_params.get("api_key")
|
|
3984
|
+
server = query_params.get("server")
|
|
3985
|
+
|
|
3986
|
+
if api_key is None:
|
|
3987
|
+
raise MissingValueError("api_key", "Mailchimp")
|
|
3988
|
+
if server is None:
|
|
3989
|
+
raise MissingValueError("server", "Mailchimp")
|
|
3990
|
+
|
|
3991
|
+
from ingestr.src.mailchimp import mailchimp_source
|
|
3992
|
+
|
|
3993
|
+
try:
|
|
3994
|
+
return mailchimp_source(
|
|
3995
|
+
api_key=api_key[0],
|
|
3996
|
+
server=server[0],
|
|
3997
|
+
).with_resources(table)
|
|
3998
|
+
except ResourcesNotFoundError:
|
|
3999
|
+
raise UnsupportedResourceError(table, "Mailchimp")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ingestr
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.91
|
|
4
4
|
Summary: ingestr is a command-line application that ingests data from various sources and stores them in any database.
|
|
5
5
|
Project-URL: Homepage, https://github.com/bruin-data/ingestr
|
|
6
6
|
Project-URL: Issues, https://github.com/bruin-data/ingestr/issues
|
|
@@ -2,17 +2,17 @@ ingestr/conftest.py,sha256=OE2yxeTCosS9CUFVuqNypm-2ftYvVBeeq7egm3878cI,1981
|
|
|
2
2
|
ingestr/main.py,sha256=qo0g3wCFl8a_1jUwXagX8L1Q8PKKQlTF7md9pfnzW0Y,27155
|
|
3
3
|
ingestr/src/.gitignore,sha256=8cX1AZTSI0TcdZFGTmS_oyBjpfCzhOEt0DdAo2dFIY8,203
|
|
4
4
|
ingestr/src/blob.py,sha256=UUWMjHUuoR9xP1XZQ6UANQmnMVyDx3d0X4-2FQC271I,2138
|
|
5
|
-
ingestr/src/buildinfo.py,sha256=
|
|
5
|
+
ingestr/src/buildinfo.py,sha256=S1bddOwYtoZWcUt0-yZ-Xo-uAYE96jHPwDOFnYbIpyg,21
|
|
6
6
|
ingestr/src/destinations.py,sha256=QtjE0AGs0WkPHaI2snWPHJ8HHi4lwXUBYLJPklz8Mvk,27772
|
|
7
7
|
ingestr/src/errors.py,sha256=fhJ2BxOqOsBfOxuTDKfZblvawBrPG3x_1VikIxMZBRI,874
|
|
8
|
-
ingestr/src/factory.py,sha256=
|
|
8
|
+
ingestr/src/factory.py,sha256=z6lcaGwK8o7AcaeshX7TP4WfB_jsIi0TvarcquGYnK0,7720
|
|
9
9
|
ingestr/src/filters.py,sha256=0n0sNAVG_f-B_1r7lW5iNtw9z_G1bxWzPaiL1i6tnbU,1665
|
|
10
10
|
ingestr/src/http_client.py,sha256=bxqsk6nJNXCo-79gW04B53DQO-yr25vaSsqP0AKtjx4,732
|
|
11
11
|
ingestr/src/loader.py,sha256=9NaWAyfkXdqAZSS-N72Iwo36Lbx4PyqIfaaH1dNdkFs,1712
|
|
12
12
|
ingestr/src/masking.py,sha256=VN0LdfvExhQ1bZMRylGtaBUIoH-vjuIUmRnYKwo3yiY,11358
|
|
13
13
|
ingestr/src/partition.py,sha256=BrIP6wFJvyR7Nus_3ElnfxknUXeCipK_E_bB8kZowfc,969
|
|
14
14
|
ingestr/src/resource.py,sha256=ZqmZxFQVGlF8rFPhBiUB08HES0yoTj8sZ--jKfaaVps,1164
|
|
15
|
-
ingestr/src/sources.py,sha256=
|
|
15
|
+
ingestr/src/sources.py,sha256=DSJAiwfVV0czMpHX1d93gQ7xIk_RyGPubQ31KfT5N8I,139639
|
|
16
16
|
ingestr/src/table_definition.py,sha256=REbAbqdlmUMUuRh8nEQRreWjPVOQ5ZcfqGkScKdCrmk,390
|
|
17
17
|
ingestr/src/time.py,sha256=H_Fk2J4ShXyUM-EMY7MqCLZQhlnZMZvO952bmZPc4yE,254
|
|
18
18
|
ingestr/src/version.py,sha256=J_2xgZ0mKlvuHcjdKCx2nlioneLH0I47JiU_Slr_Nwc,189
|
|
@@ -109,6 +109,9 @@ ingestr/src/linear/helpers.py,sha256=J9lTuu8rHHM3YTA082_wfvByW6Teie4_44eYaVmDBhQ
|
|
|
109
109
|
ingestr/src/linkedin_ads/__init__.py,sha256=CAPWFyV24loziiphbLmODxZUXZJwm4JxlFkr56q0jfo,1855
|
|
110
110
|
ingestr/src/linkedin_ads/dimension_time_enum.py,sha256=EmHRdkFyTAfo4chGjThrwqffWJxmAadZMbpTvf0xkQc,198
|
|
111
111
|
ingestr/src/linkedin_ads/helpers.py,sha256=eUWudRVlXl4kqIhfXQ1eVsUpZwJn7UFqKSpnbLfxzds,4498
|
|
112
|
+
ingestr/src/mailchimp/__init__.py,sha256=-V4IacEJ8wvngHQsSuGu_Z4xAt1XtQIHigvOoWcndw0,3175
|
|
113
|
+
ingestr/src/mailchimp/helpers.py,sha256=KXchokX5ApLqpjIAHzKWkI23ETGmQMlUrbOC5rm7q6k,6166
|
|
114
|
+
ingestr/src/mailchimp/settings.py,sha256=0x9q-8tvieuDZb0H8GNdNWnRFmVDo2jK68IoBVdKE6k,4014
|
|
112
115
|
ingestr/src/mixpanel/__init__.py,sha256=s1QtqMP0BTGW6YtdCabJFWj7lEn7KujzELwGpBOQgfs,1796
|
|
113
116
|
ingestr/src/mixpanel/client.py,sha256=c_reouegOVYBOwHLfgYFwpmkba0Sxro1Zkml07NCYf0,3602
|
|
114
117
|
ingestr/src/monday/__init__.py,sha256=ZNdGCC_1CEYlgxAef-5QO56Drm9IMP82-rZpEvbD8aY,6918
|
|
@@ -180,8 +183,8 @@ ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ
|
|
|
180
183
|
ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
|
|
181
184
|
ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
|
|
182
185
|
ingestr/tests/unit/test_smartsheets.py,sha256=zf3DXT29Y4TH2lNPBFphdjlaelUUyPJcsW2UO68RzDs,4862
|
|
183
|
-
ingestr-0.14.
|
|
184
|
-
ingestr-0.14.
|
|
185
|
-
ingestr-0.14.
|
|
186
|
-
ingestr-0.14.
|
|
187
|
-
ingestr-0.14.
|
|
186
|
+
ingestr-0.14.91.dist-info/METADATA,sha256=S_ZjXBHtmd92ZS7l6u_Xg6W0TpXynfhXp7AA4WggEI4,15266
|
|
187
|
+
ingestr-0.14.91.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
188
|
+
ingestr-0.14.91.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
|
|
189
|
+
ingestr-0.14.91.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
|
|
190
|
+
ingestr-0.14.91.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|