ingestr 0.13.2__py3-none-any.whl → 0.14.104__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.
Files changed (146) hide show
  1. ingestr/conftest.py +72 -0
  2. ingestr/main.py +134 -87
  3. ingestr/src/adjust/__init__.py +4 -4
  4. ingestr/src/adjust/adjust_helpers.py +7 -3
  5. ingestr/src/airtable/__init__.py +3 -2
  6. ingestr/src/allium/__init__.py +128 -0
  7. ingestr/src/anthropic/__init__.py +277 -0
  8. ingestr/src/anthropic/helpers.py +525 -0
  9. ingestr/src/applovin/__init__.py +262 -0
  10. ingestr/src/applovin_max/__init__.py +117 -0
  11. ingestr/src/appsflyer/__init__.py +325 -0
  12. ingestr/src/appsflyer/client.py +49 -45
  13. ingestr/src/appstore/__init__.py +1 -0
  14. ingestr/src/arrow/__init__.py +9 -1
  15. ingestr/src/asana_source/__init__.py +1 -1
  16. ingestr/src/attio/__init__.py +102 -0
  17. ingestr/src/attio/helpers.py +65 -0
  18. ingestr/src/blob.py +38 -11
  19. ingestr/src/buildinfo.py +1 -0
  20. ingestr/src/chess/__init__.py +1 -1
  21. ingestr/src/clickup/__init__.py +85 -0
  22. ingestr/src/clickup/helpers.py +47 -0
  23. ingestr/src/collector/spinner.py +43 -0
  24. ingestr/src/couchbase_source/__init__.py +118 -0
  25. ingestr/src/couchbase_source/helpers.py +135 -0
  26. ingestr/src/cursor/__init__.py +83 -0
  27. ingestr/src/cursor/helpers.py +188 -0
  28. ingestr/src/destinations.py +520 -33
  29. ingestr/src/docebo/__init__.py +589 -0
  30. ingestr/src/docebo/client.py +435 -0
  31. ingestr/src/docebo/helpers.py +97 -0
  32. ingestr/src/elasticsearch/__init__.py +80 -0
  33. ingestr/src/elasticsearch/helpers.py +138 -0
  34. ingestr/src/errors.py +8 -0
  35. ingestr/src/facebook_ads/__init__.py +47 -28
  36. ingestr/src/facebook_ads/helpers.py +59 -37
  37. ingestr/src/facebook_ads/settings.py +2 -0
  38. ingestr/src/facebook_ads/utils.py +39 -0
  39. ingestr/src/factory.py +116 -2
  40. ingestr/src/filesystem/__init__.py +8 -3
  41. ingestr/src/filters.py +46 -3
  42. ingestr/src/fluxx/__init__.py +9906 -0
  43. ingestr/src/fluxx/helpers.py +209 -0
  44. ingestr/src/frankfurter/__init__.py +157 -0
  45. ingestr/src/frankfurter/helpers.py +48 -0
  46. ingestr/src/freshdesk/__init__.py +89 -0
  47. ingestr/src/freshdesk/freshdesk_client.py +137 -0
  48. ingestr/src/freshdesk/settings.py +9 -0
  49. ingestr/src/fundraiseup/__init__.py +95 -0
  50. ingestr/src/fundraiseup/client.py +81 -0
  51. ingestr/src/github/__init__.py +41 -6
  52. ingestr/src/github/helpers.py +5 -5
  53. ingestr/src/google_analytics/__init__.py +22 -4
  54. ingestr/src/google_analytics/helpers.py +124 -6
  55. ingestr/src/google_sheets/__init__.py +4 -4
  56. ingestr/src/google_sheets/helpers/data_processing.py +2 -2
  57. ingestr/src/hostaway/__init__.py +302 -0
  58. ingestr/src/hostaway/client.py +288 -0
  59. ingestr/src/http/__init__.py +35 -0
  60. ingestr/src/http/readers.py +114 -0
  61. ingestr/src/http_client.py +24 -0
  62. ingestr/src/hubspot/__init__.py +66 -23
  63. ingestr/src/hubspot/helpers.py +52 -22
  64. ingestr/src/hubspot/settings.py +14 -7
  65. ingestr/src/influxdb/__init__.py +46 -0
  66. ingestr/src/influxdb/client.py +34 -0
  67. ingestr/src/intercom/__init__.py +142 -0
  68. ingestr/src/intercom/helpers.py +674 -0
  69. ingestr/src/intercom/settings.py +279 -0
  70. ingestr/src/isoc_pulse/__init__.py +159 -0
  71. ingestr/src/jira_source/__init__.py +340 -0
  72. ingestr/src/jira_source/helpers.py +439 -0
  73. ingestr/src/jira_source/settings.py +170 -0
  74. ingestr/src/kafka/__init__.py +4 -1
  75. ingestr/src/kinesis/__init__.py +139 -0
  76. ingestr/src/kinesis/helpers.py +82 -0
  77. ingestr/src/klaviyo/{_init_.py → __init__.py} +5 -6
  78. ingestr/src/linear/__init__.py +634 -0
  79. ingestr/src/linear/helpers.py +111 -0
  80. ingestr/src/linkedin_ads/helpers.py +0 -1
  81. ingestr/src/loader.py +69 -0
  82. ingestr/src/mailchimp/__init__.py +126 -0
  83. ingestr/src/mailchimp/helpers.py +226 -0
  84. ingestr/src/mailchimp/settings.py +164 -0
  85. ingestr/src/masking.py +344 -0
  86. ingestr/src/mixpanel/__init__.py +62 -0
  87. ingestr/src/mixpanel/client.py +99 -0
  88. ingestr/src/monday/__init__.py +246 -0
  89. ingestr/src/monday/helpers.py +392 -0
  90. ingestr/src/monday/settings.py +328 -0
  91. ingestr/src/mongodb/__init__.py +72 -8
  92. ingestr/src/mongodb/helpers.py +915 -38
  93. ingestr/src/partition.py +32 -0
  94. ingestr/src/personio/__init__.py +331 -0
  95. ingestr/src/personio/helpers.py +86 -0
  96. ingestr/src/phantombuster/__init__.py +65 -0
  97. ingestr/src/phantombuster/client.py +87 -0
  98. ingestr/src/pinterest/__init__.py +82 -0
  99. ingestr/src/pipedrive/__init__.py +198 -0
  100. ingestr/src/pipedrive/helpers/__init__.py +23 -0
  101. ingestr/src/pipedrive/helpers/custom_fields_munger.py +102 -0
  102. ingestr/src/pipedrive/helpers/pages.py +115 -0
  103. ingestr/src/pipedrive/settings.py +27 -0
  104. ingestr/src/pipedrive/typing.py +3 -0
  105. ingestr/src/plusvibeai/__init__.py +335 -0
  106. ingestr/src/plusvibeai/helpers.py +544 -0
  107. ingestr/src/plusvibeai/settings.py +252 -0
  108. ingestr/src/quickbooks/__init__.py +117 -0
  109. ingestr/src/resource.py +40 -0
  110. ingestr/src/revenuecat/__init__.py +83 -0
  111. ingestr/src/revenuecat/helpers.py +237 -0
  112. ingestr/src/salesforce/__init__.py +156 -0
  113. ingestr/src/salesforce/helpers.py +64 -0
  114. ingestr/src/shopify/__init__.py +1 -17
  115. ingestr/src/smartsheets/__init__.py +82 -0
  116. ingestr/src/snapchat_ads/__init__.py +489 -0
  117. ingestr/src/snapchat_ads/client.py +72 -0
  118. ingestr/src/snapchat_ads/helpers.py +535 -0
  119. ingestr/src/socrata_source/__init__.py +83 -0
  120. ingestr/src/socrata_source/helpers.py +85 -0
  121. ingestr/src/socrata_source/settings.py +8 -0
  122. ingestr/src/solidgate/__init__.py +219 -0
  123. ingestr/src/solidgate/helpers.py +154 -0
  124. ingestr/src/sources.py +3132 -212
  125. ingestr/src/stripe_analytics/__init__.py +49 -21
  126. ingestr/src/stripe_analytics/helpers.py +286 -1
  127. ingestr/src/stripe_analytics/settings.py +62 -10
  128. ingestr/src/telemetry/event.py +10 -9
  129. ingestr/src/tiktok_ads/__init__.py +12 -6
  130. ingestr/src/tiktok_ads/tiktok_helpers.py +0 -1
  131. ingestr/src/trustpilot/__init__.py +48 -0
  132. ingestr/src/trustpilot/client.py +48 -0
  133. ingestr/src/version.py +6 -1
  134. ingestr/src/wise/__init__.py +68 -0
  135. ingestr/src/wise/client.py +63 -0
  136. ingestr/src/zoom/__init__.py +99 -0
  137. ingestr/src/zoom/helpers.py +102 -0
  138. ingestr/tests/unit/test_smartsheets.py +133 -0
  139. ingestr-0.14.104.dist-info/METADATA +563 -0
  140. ingestr-0.14.104.dist-info/RECORD +203 -0
  141. ingestr/src/appsflyer/_init_.py +0 -24
  142. ingestr-0.13.2.dist-info/METADATA +0 -302
  143. ingestr-0.13.2.dist-info/RECORD +0 -107
  144. {ingestr-0.13.2.dist-info → ingestr-0.14.104.dist-info}/WHEEL +0 -0
  145. {ingestr-0.13.2.dist-info → ingestr-0.14.104.dist-info}/entry_points.txt +0 -0
  146. {ingestr-0.13.2.dist-info → ingestr-0.14.104.dist-info}/licenses/LICENSE.md +0 -0
@@ -0,0 +1,8 @@
1
+ """Socrata API settings and constants"""
2
+
3
+ # Request timeout in seconds
4
+ REQUEST_TIMEOUT = 30
5
+
6
+ # Maximum number of records to fetch per page
7
+ # Socrata API supports up to 50000 records per request
8
+ DEFAULT_PAGE_SIZE = 50000
@@ -0,0 +1,219 @@
1
+ from typing import Iterable, Iterator
2
+
3
+ import dlt
4
+ import pendulum
5
+ from dlt.sources import DltResource
6
+
7
+ from .helpers import SolidgateClient
8
+
9
+ COLUMN_HINTS = {
10
+ "subscriptions": {
11
+ "id": {"data_type": "text", "nullable": False, "primary_key": True},
12
+ "created_at": {"data_type": "timestamp", "partition": True},
13
+ "status": {"data_type": "text"},
14
+ "started_at": {"data_type": "timestamp"},
15
+ "updated_at": {"data_type": "timestamp"},
16
+ "expired_at": {"data_type": "timestamp"},
17
+ "next_charge_at": {"data_type": "timestamp"},
18
+ "payment_type": {"data_type": "text"},
19
+ "trial": {"data_type": "bool"},
20
+ "cancelled_at": {"data_type": "timestamp"},
21
+ "cancellation_requested_at": {"data_type": "timestamp"},
22
+ "cancel_code": {"data_type": "text"},
23
+ "cancel_message": {"data_type": "text"},
24
+ "customer": {"data_type": "json"},
25
+ "product": {"data_type": "json"},
26
+ "invoices": {"data_type": "json"},
27
+ },
28
+ "apm_orders": {
29
+ "order_id": {"data_type": "text", "nullable": False, "primary_key": True},
30
+ "created_at": {"data_type": "timestamp", "partition": True},
31
+ "updated_at": {"data_type": "timestamp"},
32
+ "order_description": {"data_type": "text"},
33
+ "method": {"data_type": "text"},
34
+ "amount": {"data_type": "bigint"},
35
+ "currency": {"data_type": "text"},
36
+ "processing_amount": {"data_type": "bigint"},
37
+ "processing_currency": {"data_type": "text"},
38
+ "status": {"data_type": "text"},
39
+ "customer_account_id": {"data_type": "text"},
40
+ "customer_email": {"data_type": "text"},
41
+ "ip_address": {"data_type": "text"},
42
+ "geo_country": {"data_type": "text"},
43
+ "error_code": {"data_type": "text"},
44
+ "transactions": {"data_type": "json"},
45
+ "order_metadata": {"data_type": "json"},
46
+ },
47
+ "card_orders": {
48
+ "order_id": {"data_type": "text", "nullable": False, "primary_key": True},
49
+ "created_at": {"data_type": "timestamp", "partition": True},
50
+ "updated_at": {"data_type": "timestamp"},
51
+ "order_description": {"data_type": "text"},
52
+ "psp_order_id": {"data_type": "text"},
53
+ "provider_payment_id": {"data_type": "text"},
54
+ "amount": {"data_type": "bigint"},
55
+ "currency": {"data_type": "text"},
56
+ "processing_amount": {"data_type": "bigint"},
57
+ "processing_currency": {"data_type": "text"},
58
+ "status": {"data_type": "text"},
59
+ "payment_type": {"data_type": "text"},
60
+ "type": {"data_type": "text"},
61
+ "is_secured": {"data_type": "bool"},
62
+ "routing": {"data_type": "json"},
63
+ "customer_account_id": {"data_type": "text"},
64
+ "customer_email": {"data_type": "text"},
65
+ "customer_first_name": {"data_type": "text"},
66
+ "customer_last_name": {"data_type": "text"},
67
+ "ip_address": {"data_type": "text"},
68
+ "mid": {"data_type": "text"},
69
+ "traffic_source": {"data_type": "text"},
70
+ "platform": {"data_type": "text"},
71
+ "geo_country": {"data_type": "text"},
72
+ "error_code": {"data_type": "text"},
73
+ "transactions": {"data_type": "json"},
74
+ "order_metadata": {"data_type": "json"},
75
+ "fraudulent": {"data_type": "bool"},
76
+ },
77
+ "financial_entries": {
78
+ "id": {
79
+ "data_type": "text",
80
+ "nullable": False,
81
+ "primary_key": True,
82
+ },
83
+ "order_id": {"data_type": "text"},
84
+ "external_psp_order_id": {"data_type": "text"},
85
+ "created_at": {"data_type": "timestamp", "partition": True},
86
+ "transaction_datetime_provider": {"data_type": "timestamp"},
87
+ "transaction_datetime_utc": {"data_type": "timestamp"},
88
+ "accounting_date": {"data_type": "date"},
89
+ "amount": {"data_type": "double"},
90
+ "amount_in_major_units": {"data_type": "double"},
91
+ "currency": {"data_type": "text"},
92
+ "currency_minor_units": {"data_type": "bigint"},
93
+ "payout_amount": {"data_type": "double"},
94
+ "payout_amount_in_major_units": {"data_type": "double"},
95
+ "payout_currency": {"data_type": "text"},
96
+ "payout_currency_minor_units": {"data_type": "bigint"},
97
+ "record_type_key": {"data_type": "text"},
98
+ "provider": {"data_type": "text"},
99
+ "payment_method": {"data_type": "text"},
100
+ "card_brand": {"data_type": "text"},
101
+ "geo_country": {"data_type": "text"},
102
+ "issuing_country": {"data_type": "text"},
103
+ "transaction_id": {"data_type": "text"},
104
+ "chargeback_id": {"data_type": "text"},
105
+ "legal_entity": {"data_type": "text"},
106
+ },
107
+ }
108
+
109
+
110
+ @dlt.source(max_table_nesting=0)
111
+ def solidgate_source(
112
+ start_date: pendulum.DateTime,
113
+ end_date: pendulum.DateTime | None,
114
+ public_key: str,
115
+ secret_key: str,
116
+ ) -> Iterable[DltResource]:
117
+ solidgate_client = SolidgateClient(public_key, secret_key)
118
+
119
+ @dlt.resource(
120
+ name="subscriptions",
121
+ write_disposition="merge",
122
+ primary_key="id",
123
+ columns=COLUMN_HINTS["subscriptions"], # type: ignore
124
+ )
125
+ def fetch_all_subscriptions(
126
+ dateTime=dlt.sources.incremental(
127
+ "updated_at",
128
+ initial_value=start_date,
129
+ end_value=end_date,
130
+ range_start="closed",
131
+ range_end="closed",
132
+ ),
133
+ ) -> Iterator[dict]:
134
+ path = "subscriptions"
135
+ if dateTime.end_value is None:
136
+ end_dt = pendulum.now(tz="UTC")
137
+ else:
138
+ end_dt = dateTime.end_value
139
+
140
+ start_dt = dateTime.last_value
141
+ yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
142
+
143
+ @dlt.resource(
144
+ name="apm_orders",
145
+ write_disposition="merge",
146
+ primary_key="order_id",
147
+ columns=COLUMN_HINTS["apm_orders"], # type: ignore
148
+ )
149
+ def fetch_apm_orders(
150
+ dateTime=dlt.sources.incremental(
151
+ "updated_at",
152
+ initial_value=start_date,
153
+ end_value=end_date,
154
+ range_start="closed",
155
+ range_end="closed",
156
+ ),
157
+ ) -> Iterator[dict]:
158
+ path = "apm-orders"
159
+ if dateTime.end_value is None:
160
+ end_dt = pendulum.now(tz="UTC")
161
+ else:
162
+ end_dt = dateTime.end_value
163
+
164
+ start_dt = dateTime.last_value
165
+ yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
166
+
167
+ @dlt.resource(
168
+ name="card_orders",
169
+ write_disposition="merge",
170
+ primary_key="order_id",
171
+ columns=COLUMN_HINTS["card_orders"], # type: ignore
172
+ )
173
+ def fetch_card_orders(
174
+ dateTime=dlt.sources.incremental(
175
+ "updated_at",
176
+ initial_value=start_date,
177
+ end_value=end_date,
178
+ range_start="closed",
179
+ range_end="closed",
180
+ ),
181
+ ) -> Iterator[dict]:
182
+ path = "card-orders"
183
+ if dateTime.end_value is None:
184
+ end_dt = pendulum.now(tz="UTC")
185
+ else:
186
+ end_dt = dateTime.end_value
187
+
188
+ start_dt = dateTime.last_value
189
+ yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
190
+
191
+ @dlt.resource(
192
+ name="financial_entries",
193
+ write_disposition="merge",
194
+ primary_key="id",
195
+ columns=COLUMN_HINTS["financial_entries"], # type: ignore
196
+ )
197
+ def fetch_financial_entries(
198
+ dateTime=dlt.sources.incremental(
199
+ "created_at",
200
+ initial_value=start_date,
201
+ end_value=end_date,
202
+ range_start="closed",
203
+ range_end="closed",
204
+ ),
205
+ ):
206
+ if dateTime.end_value is None:
207
+ end_date = pendulum.now(tz="UTC")
208
+ else:
209
+ end_date = dateTime.end_value
210
+
211
+ start_date = dateTime.last_value
212
+ yield solidgate_client.fetch_financial_entry_data(start_date, end_date)
213
+
214
+ return (
215
+ fetch_all_subscriptions,
216
+ fetch_apm_orders,
217
+ fetch_card_orders,
218
+ fetch_financial_entries,
219
+ )
@@ -0,0 +1,154 @@
1
+ import base64
2
+ import csv
3
+ import hashlib
4
+ import hmac
5
+ import json
6
+ import time
7
+ from io import StringIO
8
+
9
+ import pendulum
10
+
11
+ from ingestr.src.http_client import create_client
12
+
13
+
14
+ class SolidgateClient:
15
+ def __init__(self, public_key, secret_key):
16
+ self.base_url = "https://reports.solidgate.com/api/v1"
17
+ self.public_key = public_key
18
+ self.secret_key = secret_key
19
+ self.client = create_client(retry_status_codes=[204])
20
+
21
+ def fetch_data(
22
+ self,
23
+ path: str,
24
+ date_from: pendulum.DateTime,
25
+ date_to: pendulum.DateTime,
26
+ ):
27
+ request_payload = {
28
+ "date_from": date_from.format("YYYY-MM-DD HH:mm:ss"),
29
+ "date_to": date_to.format("YYYY-MM-DD HH:mm:ss"),
30
+ }
31
+
32
+ json_string = json.dumps(request_payload)
33
+ signature = self.generateSignature(json_string)
34
+ headers = {
35
+ "merchant": self.public_key,
36
+ "Signature": signature,
37
+ "Content-Type": "application/json",
38
+ }
39
+
40
+ next_page_iterator = None
41
+ url = f"{self.base_url}/{path}"
42
+
43
+ while True:
44
+ payload = request_payload.copy()
45
+ if next_page_iterator:
46
+ payload["page_iterator"] = next_page_iterator
47
+
48
+ response = self.client.post(url, headers=headers, json=payload)
49
+ response.raise_for_status()
50
+ response_json = response.json()
51
+
52
+ if path == "subscriptions":
53
+ data = response_json["subscriptions"]
54
+ for _, value in data.items():
55
+ if "updated_at" in value:
56
+ value["updated_at"] = pendulum.parse(value["updated_at"])
57
+ yield value
58
+
59
+ else:
60
+ data = response_json["orders"]
61
+ for value in data:
62
+ if "updated_at" in value:
63
+ value["updated_at"] = pendulum.parse(value["updated_at"])
64
+ yield value
65
+
66
+ next_page_iterator = response_json.get("metadata", {}).get(
67
+ "next_page_iterator"
68
+ )
69
+ if not next_page_iterator or next_page_iterator == "None":
70
+ break
71
+
72
+ def fetch_financial_entry_data(
73
+ self, date_from: pendulum.DateTime, date_to: pendulum.DateTime
74
+ ):
75
+ request_payload = {
76
+ "date_from": date_from.format("YYYY-MM-DD HH:mm:ss"),
77
+ "date_to": date_to.format("YYYY-MM-DD HH:mm:ss"),
78
+ }
79
+
80
+ json_string = json.dumps(request_payload)
81
+ signature = self.generateSignature(json_string)
82
+ headers = {
83
+ "merchant": self.public_key,
84
+ "Signature": signature,
85
+ "Content-Type": "application/json",
86
+ }
87
+
88
+ url = f"{self.base_url}/finance/financial_entries"
89
+ post_response = self.client.post(url, headers=headers, json=request_payload)
90
+ post_response.raise_for_status()
91
+ report_url = post_response.json().get("report_url")
92
+ if not report_url:
93
+ return f"Report URL not found in the response: {post_response.json()}", 400
94
+
95
+ data = self.public_key + self.public_key
96
+ hmac_hash = hmac.new(
97
+ self.secret_key.encode("utf-8"), data.encode("utf-8"), hashlib.sha512
98
+ ).digest()
99
+ signature_get = base64.b64encode(hmac_hash.hex().encode("utf-8")).decode(
100
+ "utf-8"
101
+ )
102
+
103
+ headers_get = {
104
+ "merchant": self.public_key,
105
+ "Signature": signature_get,
106
+ "Content-Type": "application/json",
107
+ }
108
+
109
+ # Retry getting the report for up to 10 minutes (600 seconds) with 5-second intervals
110
+ max_retries = 120 # 10 minutes / 5 seconds = 120 attempts
111
+ retry_count = 0
112
+
113
+ while retry_count < max_retries:
114
+ get_response = self.client.get(report_url, headers=headers_get)
115
+
116
+ if get_response.status_code == 200:
117
+ try:
118
+ response_json = json.loads(get_response.content)
119
+ if "error" in response_json:
120
+ raise Exception(
121
+ f"API Error: {response_json['error']['messages']}"
122
+ )
123
+ except json.JSONDecodeError:
124
+ try:
125
+ csv_data = get_response.content.decode("utf-8")
126
+ reader = csv.DictReader(StringIO(csv_data))
127
+ rows = []
128
+ for row in reader:
129
+ if row["created_at"]:
130
+ row["created_at"] = pendulum.parse(row["created_at"])
131
+ else:
132
+ row["created_at"] = None
133
+
134
+ row2 = {k: v for k, v in row.items() if v != ""}
135
+ rows.append(row2)
136
+
137
+ return rows
138
+ except Exception as e:
139
+ raise Exception(f"Error reading CSV: {e}")
140
+ else:
141
+ # Report might not be ready yet, wait and retry
142
+ retry_count += 1
143
+ if retry_count >= max_retries:
144
+ raise Exception(
145
+ f"Failed to get report after {max_retries} attempts. Status code: {get_response.status_code}"
146
+ )
147
+ time.sleep(5) # Wait 5 seconds before retrying
148
+
149
+ def generateSignature(self, json_string):
150
+ data = self.public_key + json_string + self.public_key
151
+ hmac_hash = hmac.new(
152
+ self.secret_key.encode("utf-8"), data.encode("utf-8"), hashlib.sha512
153
+ ).digest()
154
+ return base64.b64encode(hmac_hash.hex().encode("utf-8")).decode("utf-8")