ingestr 0.13.30__py3-none-any.whl → 0.13.32__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/main.py CHANGED
@@ -3,15 +3,9 @@ from enum import Enum
3
3
  from typing import Optional
4
4
 
5
5
  import typer
6
- from dlt.common.runtime.collector import Collector
7
6
  from rich.console import Console
8
- from rich.status import Status
9
7
  from typing_extensions import Annotated
10
8
 
11
- import ingestr.src.partition as partition
12
- import ingestr.src.resource as resource
13
- from ingestr.src.destinations import AthenaDestination
14
- from ingestr.src.filters import cast_set_to_list, handle_mysql_empty_dates
15
9
  from ingestr.src.telemetry.event import track
16
10
 
17
11
  app = typer.Typer(
@@ -46,45 +40,6 @@ PARQUET_SUPPORTED_DESTINATIONS = [
46
40
  JSON_RETURNING_SOURCES = ["notion"]
47
41
 
48
42
 
49
- class SpinnerCollector(Collector):
50
- status: Status
51
- current_step: str
52
- started: bool
53
-
54
- def __init__(self) -> None:
55
- self.status = Status("Ingesting data...", spinner="dots")
56
- self.started = False
57
-
58
- def update(
59
- self,
60
- name: str,
61
- inc: int = 1,
62
- total: Optional[int] = None,
63
- message: Optional[str] = None, # type: ignore
64
- label: str = "",
65
- **kwargs,
66
- ) -> None:
67
- self.status.update(self.current_step)
68
-
69
- def _start(self, step: str) -> None:
70
- self.current_step = self.__step_to_label(step)
71
- self.status.start()
72
-
73
- def __step_to_label(self, step: str) -> str:
74
- verb = step.split(" ")[0].lower()
75
- if verb.startswith("normalize"):
76
- return "Normalizing the data"
77
- elif verb.startswith("load"):
78
- return "Loading the data to the destination"
79
- elif verb.startswith("extract"):
80
- return "Extracting the data from the source"
81
-
82
- return f"{verb.capitalize()} the data"
83
-
84
- def _stop(self) -> None:
85
- self.status.stop()
86
-
87
-
88
43
  class IncrementalStrategy(str, Enum):
89
44
  create_replace = "replace"
90
45
  append = "append"
@@ -323,8 +278,12 @@ def ingest(
323
278
  from dlt.common.runtime.collector import Collector, LogCollector
324
279
  from dlt.common.schema.typing import TColumnSchema
325
280
 
281
+ import ingestr.src.partition as partition
282
+ import ingestr.src.resource as resource
283
+ from ingestr.src.collector.spinner import SpinnerCollector
284
+ from ingestr.src.destinations import AthenaDestination
326
285
  from ingestr.src.factory import SourceDestinationFactory
327
- from ingestr.src.telemetry.event import track
286
+ from ingestr.src.filters import cast_set_to_list, handle_mysql_empty_dates
328
287
 
329
288
  def report_errors(run_info: LoadInfo):
330
289
  for load_package in run_info.load_packages:
@@ -406,6 +365,7 @@ def ingest(
406
365
  dlt.config["data_writer.file_max_items"] = loader_file_size
407
366
  dlt.config["extract.workers"] = extract_parallelism
408
367
  dlt.config["extract.max_parallel_items"] = extract_parallelism
368
+ dlt.config["load.raise_on_max_retries"] = 15
409
369
  if schema_naming != SchemaNaming.default:
410
370
  dlt.config["schema.naming"] = schema_naming.value
411
371
 
@@ -36,7 +36,7 @@ class AdjustAPI:
36
36
  def __init__(self, api_key):
37
37
  self.api_key = api_key
38
38
  self.request_client = Client(
39
- request_timeout=8.0,
39
+ request_timeout=1000, # Adjust support recommends 1000 seconds of read timeout.
40
40
  raise_for_status=False,
41
41
  retry_condition=retry_on_limit,
42
42
  request_max_attempts=12,
@@ -68,7 +68,6 @@ def applovin_max_source(
68
68
 
69
69
  def create_client() -> requests.Session:
70
70
  return Client(
71
- request_timeout=10.0,
72
71
  raise_for_status=False,
73
72
  retry_condition=retry_on_limit,
74
73
  request_max_attempts=12,
@@ -46,7 +46,6 @@ class AppsflyerClient:
46
46
  )
47
47
 
48
48
  request_client = Client(
49
- request_timeout=10.0,
50
49
  raise_for_status=False,
51
50
  retry_condition=retry_on_limit,
52
51
  request_max_attempts=12,
ingestr/src/buildinfo.py CHANGED
@@ -1 +1 @@
1
- version = "v0.13.30"
1
+ version = "v0.13.32"
@@ -0,0 +1,43 @@
1
+ from typing import Optional
2
+
3
+ from dlt.common.runtime.collector import Collector
4
+ from rich.status import Status
5
+
6
+
7
+ class SpinnerCollector(Collector):
8
+ status: Status
9
+ current_step: str
10
+ started: bool
11
+
12
+ def __init__(self) -> None:
13
+ self.status = Status("Ingesting data...", spinner="dots")
14
+ self.started = False
15
+
16
+ def update(
17
+ self,
18
+ name: str,
19
+ inc: int = 1,
20
+ total: Optional[int] = None,
21
+ message: Optional[str] = None, # type: ignore
22
+ label: str = "",
23
+ **kwargs,
24
+ ) -> None:
25
+ self.status.update(self.current_step)
26
+
27
+ def _start(self, step: str) -> None:
28
+ self.current_step = self.__step_to_label(step)
29
+ self.status.start()
30
+
31
+ def __step_to_label(self, step: str) -> str:
32
+ verb = step.split(" ")[0].lower()
33
+ if verb.startswith("normalize"):
34
+ return "Normalizing the data"
35
+ elif verb.startswith("load"):
36
+ return "Loading the data to the destination"
37
+ elif verb.startswith("extract"):
38
+ return "Extracting the data from the source"
39
+
40
+ return f"{verb.capitalize()} the data"
41
+
42
+ def _stop(self) -> None:
43
+ self.status.stop()
@@ -223,34 +223,47 @@ class AthenaDestination:
223
223
  query_result_path = bucket
224
224
 
225
225
  access_key_id = source_params.get("access_key_id", [None])[0]
226
- if not access_key_id:
227
- raise ValueError("The AWS access_key_id is required to connect to Athena.")
228
-
229
226
  secret_access_key = source_params.get("secret_access_key", [None])[0]
230
- if not secret_access_key:
231
- raise ValueError("The AWS secret_access_key is required to connect Athena")
227
+ session_token = source_params.get("session_token", [None])[0]
228
+ profile_name = source_params.get("profile", ["default"])[0]
229
+ region_name = source_params.get("region_name", [None])[0]
232
230
 
233
- work_group = source_params.get("workgroup", [None])[0]
231
+ if not access_key_id and not secret_access_key:
232
+ import botocore.session # type: ignore
233
+
234
+ session = botocore.session.Session(profile=profile_name)
235
+ default = session.get_credentials()
236
+ if not profile_name:
237
+ raise ValueError(
238
+ "You have to either provide access_key_id and secret_access_key pair or a valid AWS profile name."
239
+ )
240
+ access_key_id = default.access_key
241
+ secret_access_key = default.secret_key
242
+ session_token = default.token
243
+ if region_name is None:
244
+ region_name = session.get_config_variable("region")
234
245
 
235
- region_name = source_params.get("region_name", [None])[0]
236
246
  if not region_name:
237
247
  raise ValueError("The region_name is required to connect to Athena.")
238
248
 
239
249
  os.environ["DESTINATION__BUCKET_URL"] = bucket
240
- os.environ["DESTINATION__CREDENTIALS__AWS_ACCESS_KEY_ID"] = access_key_id
241
- os.environ["DESTINATION__CREDENTIALS__AWS_SECRET_ACCESS_KEY"] = (
242
- secret_access_key
243
- )
250
+ if access_key_id and secret_access_key:
251
+ os.environ["DESTINATION__CREDENTIALS__AWS_ACCESS_KEY_ID"] = access_key_id
252
+ os.environ["DESTINATION__CREDENTIALS__AWS_SECRET_ACCESS_KEY"] = (
253
+ secret_access_key
254
+ )
255
+ if session_token:
256
+ os.environ["DESTINATION__CREDENTIALS__AWS_SESSION_TOKEN"] = session_token
244
257
 
245
- credentials = AwsCredentials(
246
- aws_access_key_id=access_key_id,
247
- aws_secret_access_key=secret_access_key,
248
- region_name=region_name,
249
- )
250
258
  return dlt.destinations.athena(
251
259
  query_result_bucket=query_result_path,
252
- athena_work_group=work_group,
253
- credentials=credentials,
260
+ athena_work_group=source_params.get("workgroup", [None])[0],
261
+ credentials=AwsCredentials(
262
+ aws_access_key_id=access_key_id, # type: ignore
263
+ aws_secret_access_key=secret_access_key, # type: ignore
264
+ aws_session_token=session_token,
265
+ region_name=region_name,
266
+ ),
254
267
  destination_name=bucket,
255
268
  )
256
269
 
@@ -32,7 +32,12 @@ from dlt.common import pendulum
32
32
  from dlt.common.typing import TDataItems
33
33
  from dlt.sources import DltResource
34
34
 
35
- from .helpers import _get_property_names, fetch_data, fetch_property_history
35
+ from .helpers import (
36
+ _get_property_names,
37
+ fetch_data,
38
+ fetch_data_raw,
39
+ fetch_property_history,
40
+ )
36
41
  from .settings import (
37
42
  ALL,
38
43
  CRM_OBJECT_ENDPOINTS,
@@ -192,12 +197,21 @@ def hubspot(
192
197
  api_key: str = api_key,
193
198
  custom_object_name: str = custom_object,
194
199
  ) -> Iterator[TDataItems]:
195
- get_custom_object = schemas(api_key)
200
+ get_custom_object = fetch_data_raw(CRM_SCHEMAS_ENDPOINT, api_key)
196
201
  object_type_id = None
197
- for custom_object in get_custom_object:
198
- if custom_object["name"] == custom_object_name.capitalize():
202
+
203
+ custom_object_lowercase = custom_object_name.lower()
204
+ for custom_object in get_custom_object["results"]:
205
+ if custom_object["name"].lower() == custom_object_lowercase:
199
206
  object_type_id = custom_object["objectTypeId"]
200
207
  break
208
+
209
+ # sometimes people use the plural name of the object type by accident, we should try to match that if we can
210
+ if "labels" in custom_object:
211
+ if custom_object_lowercase == custom_object["labels"]["plural"].lower():
212
+ object_type_id = custom_object["objectTypeId"]
213
+ break
214
+
201
215
  if object_type_id is None:
202
216
  raise ValueError(f"There is no such custom object as {custom_object_name}")
203
217
  custom_object_properties = f"crm/v3/properties/{object_type_id}"
@@ -130,6 +130,7 @@ def fetch_data(
130
130
  # Parse the API response and yield the properties of each result
131
131
  # Parse the response JSON data
132
132
  _data = r.json()
133
+
133
134
  # Yield the properties of each result in the API response
134
135
  while _data is not None:
135
136
  if "results" in _data:
@@ -156,6 +157,7 @@ def fetch_data(
156
157
  if "id" not in _obj and "id" in _result:
157
158
  # Move id from properties to top level
158
159
  _obj["id"] = _result["id"]
160
+
159
161
  if "associations" in _result:
160
162
  for association in _result["associations"]:
161
163
  __values = [
@@ -168,12 +170,13 @@ def fetch_data(
168
170
  ]
169
171
  ]
170
172
 
171
- # remove duplicates from list of dicts
172
- __values = [
173
- dict(t) for t in {tuple(d.items()) for d in __values}
174
- ]
173
+ # remove duplicates from list of dicts
174
+ __values = [
175
+ dict(t) for t in {tuple(d.items()) for d in __values}
176
+ ]
177
+
178
+ _obj[association] = __values
175
179
 
176
- _obj[association] = __values
177
180
  _objects.append(_obj)
178
181
  yield _objects
179
182
 
@@ -208,3 +211,12 @@ def _get_property_names(api_key: str, object_type: str) -> List[str]:
208
211
  properties.extend([prop["name"] for prop in page])
209
212
 
210
213
  return properties
214
+
215
+
216
+ def fetch_data_raw(
217
+ endpoint: str, api_key: str, params: Optional[Dict[str, Any]] = None
218
+ ) -> Iterator[List[Dict[str, Any]]]:
219
+ url = get_url(endpoint)
220
+ headers = _get_headers(api_key)
221
+ r = requests.get(url, headers=headers, params=params)
222
+ return r.json()
@@ -18,7 +18,6 @@ def retry_on_limit(response: requests.Response, exception: BaseException) -> boo
18
18
 
19
19
  def create_client() -> requests.Session:
20
20
  return Client(
21
- request_timeout=10.0,
22
21
  raise_for_status=False,
23
22
  retry_condition=retry_on_limit,
24
23
  request_max_attempts=12,
@@ -18,7 +18,6 @@ def retry_on_limit(
18
18
 
19
19
  def create_client() -> requests.Session:
20
20
  return Client(
21
- request_timeout=10.0,
22
21
  raise_for_status=False,
23
22
  retry_condition=retry_on_limit,
24
23
  request_max_attempts=12,
ingestr/src/sources.py CHANGED
@@ -18,9 +18,7 @@ from typing import (
18
18
  from urllib.parse import ParseResult, parse_qs, quote, urlencode, urlparse
19
19
 
20
20
  import dlt
21
- import gcsfs # type: ignore
22
21
  import pendulum
23
- import s3fs # type: ignore
24
22
  from dlt.common.configuration.specs import (
25
23
  AwsCredentials,
26
24
  )
@@ -42,22 +40,16 @@ from dlt.sources.sql_database.schema_types import (
42
40
  Table,
43
41
  TTypeAdapter,
44
42
  )
45
- from google.ads.googleads.client import GoogleAdsClient # type: ignore
46
43
  from sqlalchemy import Column
47
44
  from sqlalchemy import types as sa
48
45
 
49
46
  from ingestr.src import blob
50
47
  from ingestr.src.adjust import REQUIRED_CUSTOM_DIMENSIONS, adjust_source
51
48
  from ingestr.src.adjust.adjust_helpers import parse_filters
52
- from ingestr.src.airtable import airtable_source
53
49
  from ingestr.src.applovin import applovin_source
54
50
  from ingestr.src.applovin_max import applovin_max_source
55
- from ingestr.src.appstore import app_store
56
- from ingestr.src.appstore.client import AppStoreConnectClient
57
51
  from ingestr.src.arrow import memory_mapped_arrow
58
- from ingestr.src.asana_source import asana_source
59
52
  from ingestr.src.chess import source
60
- from ingestr.src.dynamodb import dynamodb
61
53
  from ingestr.src.errors import (
62
54
  InvalidBlobTableError,
63
55
  MissingValueError,
@@ -69,25 +61,18 @@ from ingestr.src.filters import table_adapter_exclude_columns
69
61
  from ingestr.src.frankfurter import frankfurter_source
70
62
  from ingestr.src.frankfurter.helpers import validate_dates
71
63
  from ingestr.src.github import github_reactions, github_repo_events, github_stargazers
72
- from ingestr.src.google_ads import google_ads
73
- from ingestr.src.google_analytics import google_analytics
74
- from ingestr.src.google_sheets import google_spreadsheet
75
64
  from ingestr.src.gorgias import gorgias_source
76
65
  from ingestr.src.hubspot import hubspot
77
66
  from ingestr.src.kafka import kafka_consumer
78
67
  from ingestr.src.kafka.helpers import KafkaCredentials
79
- from ingestr.src.kinesis import kinesis_stream
80
68
  from ingestr.src.klaviyo._init_ import klaviyo_source
81
69
  from ingestr.src.linkedin_ads import linked_in_ads_source
82
70
  from ingestr.src.linkedin_ads.dimension_time_enum import (
83
71
  Dimension,
84
72
  TimeGranularity,
85
73
  )
86
- from ingestr.src.mongodb import mongodb_collection
87
74
  from ingestr.src.notion import notion_databases
88
75
  from ingestr.src.personio import personio_source
89
- from ingestr.src.pipedrive import pipedrive_source
90
- from ingestr.src.salesforce import salesforce_source
91
76
  from ingestr.src.shopify import shopify_source
92
77
  from ingestr.src.slack import slack_source
93
78
  from ingestr.src.sql_database.callbacks import (
@@ -96,7 +81,6 @@ from ingestr.src.sql_database.callbacks import (
96
81
  limit_callback,
97
82
  type_adapter_callback,
98
83
  )
99
- from ingestr.src.stripe_analytics import stripe_source
100
84
  from ingestr.src.table_definition import TableDefinition, table_string_to_dataclass
101
85
  from ingestr.src.tiktok_ads import tiktok_source
102
86
  from ingestr.src.time import isotime
@@ -338,7 +322,12 @@ class ArrowMemoryMappedSource:
338
322
  class MongoDbSource:
339
323
  table_builder: Callable
340
324
 
341
- def __init__(self, table_builder=mongodb_collection) -> None:
325
+ def __init__(self, table_builder=None) -> None:
326
+ if table_builder is None:
327
+ from ingestr.src.mongodb import mongodb_collection
328
+
329
+ table_builder = mongodb_collection
330
+
342
331
  self.table_builder = table_builder
343
332
 
344
333
  def handles_incrementality(self) -> bool:
@@ -554,7 +543,12 @@ class GorgiasSource:
554
543
  class GoogleSheetsSource:
555
544
  table_builder: Callable
556
545
 
557
- def __init__(self, table_builder=google_spreadsheet) -> None:
546
+ def __init__(self, table_builder=None) -> None:
547
+ if table_builder is None:
548
+ from ingestr.src.google_sheets import google_spreadsheet
549
+
550
+ table_builder = google_spreadsheet
551
+
558
552
  self.table_builder = table_builder
559
553
 
560
554
  def handles_incrementality(self) -> bool:
@@ -685,6 +679,8 @@ class StripeAnalyticsSource:
685
679
  if kwargs.get("interval_end"):
686
680
  date_args["end_date"] = kwargs.get("interval_end")
687
681
 
682
+ from ingestr.src.stripe_analytics import stripe_source
683
+
688
684
  return stripe_source(
689
685
  endpoints=[
690
686
  endpoint,
@@ -860,6 +856,8 @@ class AirtableSource:
860
856
  "base_id and access_token in the URI are required to connect to Airtable"
861
857
  )
862
858
 
859
+ from ingestr.src.airtable import airtable_source
860
+
863
861
  return airtable_source(
864
862
  base_id=base_id[0], table_names=tables, access_token=access_token[0]
865
863
  )
@@ -1189,6 +1187,8 @@ class S3Source:
1189
1187
 
1190
1188
  bucket_url = f"s3://{bucket_name}/"
1191
1189
 
1190
+ import s3fs # type: ignore
1191
+
1192
1192
  fs = s3fs.S3FileSystem(
1193
1193
  key=access_key_id[0],
1194
1194
  secret=secret_access_key[0],
@@ -1349,6 +1349,8 @@ class AsanaSource:
1349
1349
  )
1350
1350
 
1351
1351
  dlt.secrets["sources.asana_source.access_token"] = access_token[0]
1352
+ from ingestr.src.asana_source import asana_source
1353
+
1352
1354
  src = asana_source()
1353
1355
  src.workspaces.add_filter(lambda w: w["gid"] == workspace)
1354
1356
  return src.with_resources(table)
@@ -1413,6 +1415,8 @@ class DynamoDBSource:
1413
1415
  range_start="closed",
1414
1416
  )
1415
1417
 
1418
+ from ingestr.src.dynamodb import dynamodb
1419
+
1416
1420
  # bug: we never validate table.
1417
1421
  return dynamodb(table, creds, incremental)
1418
1422
 
@@ -1474,6 +1478,8 @@ class GoogleAnalyticsSource:
1474
1478
  if kwargs.get("interval_end") is not None:
1475
1479
  end_date = pendulum.instance(kwargs.get("interval_end")) # type: ignore
1476
1480
 
1481
+ from ingestr.src.google_analytics import google_analytics
1482
+
1477
1483
  return google_analytics(
1478
1484
  property_id=property_id[0],
1479
1485
  start_date=start_date,
@@ -1543,6 +1549,8 @@ class AppleAppStoreSource:
1543
1549
  else:
1544
1550
  key = base64.b64decode(key_base64[0]).decode() # type: ignore
1545
1551
 
1552
+ from ingestr.src.appstore.client import AppStoreConnectClient
1553
+
1546
1554
  return AppStoreConnectClient(key.encode(), key_id, issuer_id)
1547
1555
 
1548
1556
  def dlt_source(self, uri: str, table: str, **kwargs):
@@ -1583,6 +1591,8 @@ class AppleAppStoreSource:
1583
1591
  if app_ids is None:
1584
1592
  raise MissingValueError("app_id", "App Store")
1585
1593
 
1594
+ from ingestr.src.appstore import app_store
1595
+
1586
1596
  src = app_store(
1587
1597
  client,
1588
1598
  app_ids,
@@ -1639,6 +1649,8 @@ class GCSSource:
1639
1649
  # (The RECOMMENDED way of passing service account credentials)
1640
1650
  # directly with gcsfs. As a workaround, we construct the GCSFileSystem
1641
1651
  # and pass it directly to filesystem.readers.
1652
+ import gcsfs # type: ignore
1653
+
1642
1654
  fs = gcsfs.GCSFileSystem(
1643
1655
  token=credentials,
1644
1656
  )
@@ -1662,7 +1674,9 @@ class GoogleAdsSource:
1662
1674
  def handles_incrementality(self) -> bool:
1663
1675
  return True
1664
1676
 
1665
- def init_client(self, params: Dict[str, List[str]]) -> GoogleAdsClient:
1677
+ def init_client(self, params: Dict[str, List[str]]):
1678
+ from google.ads.googleads.client import GoogleAdsClient # type: ignore
1679
+
1666
1680
  dev_token = params.get("dev_token")
1667
1681
  if dev_token is None or len(dev_token) == 0:
1668
1682
  raise MissingValueError("dev_token", "Google Ads")
@@ -1716,6 +1730,7 @@ class GoogleAdsSource:
1716
1730
  raise MissingValueError("customer_id", "Google Ads")
1717
1731
 
1718
1732
  params = parse_qs(parsed_uri.query)
1733
+
1719
1734
  client = self.init_client(params)
1720
1735
 
1721
1736
  start_date = kwargs.get("interval_start") or datetime.now(
@@ -1737,6 +1752,8 @@ class GoogleAdsSource:
1737
1752
  report_spec = table
1738
1753
  table = "daily_report"
1739
1754
 
1755
+ from ingestr.src.google_ads import google_ads
1756
+
1740
1757
  src = google_ads(
1741
1758
  client,
1742
1759
  customer_id,
@@ -1954,6 +1971,8 @@ class SalesforceSource:
1954
1971
  if v is None:
1955
1972
  raise MissingValueError(k, "Salesforce")
1956
1973
 
1974
+ from ingestr.src.salesforce import salesforce_source
1975
+
1957
1976
  src = salesforce_source(**creds) # type: ignore
1958
1977
 
1959
1978
  if table not in src.resources:
@@ -2040,6 +2059,9 @@ class KinesisSource:
2040
2059
  aws_secret_access_key=aws_secret_access_key[0],
2041
2060
  region_name=region_name[0],
2042
2061
  )
2062
+
2063
+ from ingestr.src.kinesis import kinesis_stream
2064
+
2043
2065
  return kinesis_stream(
2044
2066
  stream_name=table, credentials=credentials, initial_at_timestamp=start_date
2045
2067
  )
@@ -2073,6 +2095,8 @@ class PipedriveSource:
2073
2095
  ]:
2074
2096
  raise UnsupportedResourceError(table, "Pipedrive")
2075
2097
 
2098
+ from ingestr.src.pipedrive import pipedrive_source
2099
+
2076
2100
  return pipedrive_source(
2077
2101
  pipedrive_api_key=api_key, since_timestamp=start_date
2078
2102
  ).with_resources(table)
@@ -17,7 +17,6 @@ def retry_on_limit(
17
17
 
18
18
  def create_client() -> requests.Session:
19
19
  return Client(
20
- request_timeout=10.0,
21
20
  raise_for_status=False,
22
21
  retry_condition=retry_on_limit,
23
22
  request_max_attempts=12,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.30
3
+ Version: 0.13.32
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
@@ -74,7 +74,7 @@ Requires-Dist: google-cloud-storage==3.1.0
74
74
  Requires-Dist: google-crc32c==1.6.0
75
75
  Requires-Dist: google-resumable-media==2.7.2
76
76
  Requires-Dist: googleapis-common-protos==1.69.0
77
- Requires-Dist: greenlet==3.2.0
77
+ Requires-Dist: greenlet==3.2.1
78
78
  Requires-Dist: grpcio-status==1.62.3
79
79
  Requires-Dist: grpcio==1.70.0
80
80
  Requires-Dist: hdbcli==2.23.27
@@ -419,11 +419,6 @@ Pull requests are welcome. However, please open an issue first to discuss what y
419
419
  <td>Google Analytics</td>
420
420
  <td>✅</td>
421
421
  <td>-</td>
422
- </tr>
423
- <tr>
424
- <td>Intercom</td>
425
- <td>✅</td>
426
- <td>-</td>
427
422
  </tr>
428
423
  <tr>
429
424
  <td>Klaviyo</td>
@@ -1,26 +1,26 @@
1
1
  ingestr/conftest.py,sha256=Q03FIJIZpLBbpj55cfCHIKEjc1FCvWJhMF2cidUJKQU,1748
2
- ingestr/main.py,sha256=Vt5NFN59BUlkYrOwiF9xF5MCFYp9r-aVSnQ99f1ypRE,25964
2
+ ingestr/main.py,sha256=yPEn1FvfjCdWZe0PZqgKB79GQiwUy_qH3QelET13RCE,24870
3
3
  ingestr/src/.gitignore,sha256=8cX1AZTSI0TcdZFGTmS_oyBjpfCzhOEt0DdAo2dFIY8,203
4
4
  ingestr/src/blob.py,sha256=onMe5ZHxPXTdcB_s2oGNdMo-XQJ3ajwOsWE9eSTGFmc,1495
5
- ingestr/src/buildinfo.py,sha256=4uN8Z1CmXZ2T6X2YHEC3ZWFuS5uL_sTMUZiwgVyotHo,21
6
- ingestr/src/destinations.py,sha256=vrGij4qMPCdXTMIimROWBJFqzOqCM4DFmgyubgSHejA,11279
5
+ ingestr/src/buildinfo.py,sha256=mvCtlkSChRqpUCkS3LXuEwmkyyBr5oK4zK-YUERhKf8,21
6
+ ingestr/src/destinations.py,sha256=_jRB_0rc-HNb5uvy30ZuFSm_NpK_8I9OJHmwbt0jxCM,12057
7
7
  ingestr/src/errors.py,sha256=Ufs4_DfE77_E3vnA1fOQdi6cmuLVNm7_SbFLkL1XPGk,686
8
8
  ingestr/src/factory.py,sha256=659h_sVRBhtPv2dvtOK8tf3PtUhlK3KsWLrb20_iQKw,5333
9
9
  ingestr/src/filters.py,sha256=5LNpBgm8FJXdrFHGyM7dLVyphKykSpPk7yuQAZ8GML4,1133
10
10
  ingestr/src/loader.py,sha256=9NaWAyfkXdqAZSS-N72Iwo36Lbx4PyqIfaaH1dNdkFs,1712
11
11
  ingestr/src/partition.py,sha256=E0WHqh1FTheQAIVK_-jWUx0dgyYZCD1VxlAm362gao4,964
12
12
  ingestr/src/resource.py,sha256=XG-sbBapFVEM7OhHQFQRTdTLlh-mHB-N4V1t8F8Tsww,543
13
- ingestr/src/sources.py,sha256=oCqYQPGUrGVASJyZpDe7b4hz6SqwcaFtF6hXHxx4pzo,74650
13
+ ingestr/src/sources.py,sha256=gWWYlj3cyscernMrCePDR8Q_zJF5wXFwGM6x0LVRbV4,74921
14
14
  ingestr/src/table_definition.py,sha256=REbAbqdlmUMUuRh8nEQRreWjPVOQ5ZcfqGkScKdCrmk,390
15
15
  ingestr/src/time.py,sha256=H_Fk2J4ShXyUM-EMY7MqCLZQhlnZMZvO952bmZPc4yE,254
16
16
  ingestr/src/version.py,sha256=J_2xgZ0mKlvuHcjdKCx2nlioneLH0I47JiU_Slr_Nwc,189
17
17
  ingestr/src/adjust/__init__.py,sha256=ULjtJqrNS6XDvUyGl0tjl12-tLyXlCgeFe2icTbtu3Q,3255
18
- ingestr/src/adjust/adjust_helpers.py,sha256=G_EvNuvA7CsaOtbV3g249iAyggMDMZYbtWOzOAz_EjY,3742
18
+ ingestr/src/adjust/adjust_helpers.py,sha256=IHSS94A7enOWkZ8cP5iW3RdYt0Xl3qZGAmDc1Xy4qkI,3802
19
19
  ingestr/src/airtable/__init__.py,sha256=GHWYrjI2qhs_JihdNJysB0Ni3bzqT_MLXn_S9_Q5zRA,2775
20
20
  ingestr/src/applovin/__init__.py,sha256=X_YCLppPrnL8KXfYWICE_uDfMzHHH3JZ-DBGZ1RlaOI,6984
21
- ingestr/src/applovin_max/__init__.py,sha256=CBMADQ23gi0oxAsxe-RV67GGb8I4EFOX_It45Vv9dj4,3315
21
+ ingestr/src/applovin_max/__init__.py,sha256=ZrxOUSirGxkGDmM9wsQO3anwNVzqtoCwN_OuCXfPkXE,3285
22
22
  ingestr/src/appsflyer/__init__.py,sha256=QoK-B3cYYMD3bqzQaLWNH6FkJyjRbzRkBF2n6urxubs,8071
23
- ingestr/src/appsflyer/client.py,sha256=5lIbiVG_l1_n2uMZx6_rw3FyM0yZUM9-sOa20utNtYU,3649
23
+ ingestr/src/appsflyer/client.py,sha256=E6xPW4KlbBnQZ0K4eq2Xgb3AmGrtrzIX9bX8EnQr-D4,3615
24
24
  ingestr/src/appstore/__init__.py,sha256=3P4VZH2WJF477QjW19jMTwu6L8DXcLkYSdutnvp3AmM,4742
25
25
  ingestr/src/appstore/client.py,sha256=qY9nBZPNIAveR-Dn-pW141Mr9xi9LMOz2HHfnfueHvE,3975
26
26
  ingestr/src/appstore/errors.py,sha256=KVpPWth5qlv6_QWEm3aJAt3cdf6miPJs0UDzxknx2Ms,481
@@ -33,6 +33,7 @@ ingestr/src/asana_source/settings.py,sha256=-2tpdkwh04RvLKFvwQodnFLYn9MaxOO1hseb
33
33
  ingestr/src/chess/__init__.py,sha256=y0Q8aKBigeKf3N7wuB_gadMQjVJzBPUT8Jhp1ObEWjk,6812
34
34
  ingestr/src/chess/helpers.py,sha256=v1HTImOMjAF7AzZUPDIuHu00e7ut0o5y1kWcVYo4QZw,549
35
35
  ingestr/src/chess/settings.py,sha256=p0RlCGgtXUacPDEvZmwzSWmzX0Apj1riwfz-nrMK89k,158
36
+ ingestr/src/collector/spinner.py,sha256=_ZUqF5MI43hVIULdjF5s5mrAZbhEFXaiWirQmrv3Yk4,1201
36
37
  ingestr/src/dynamodb/__init__.py,sha256=swhxkeYBbJ35jn1IghCtvYWT2BM33KynVCh_oR4z28A,2264
37
38
  ingestr/src/facebook_ads/__init__.py,sha256=reEpSr4BaKA1wO3qVgCH51gW-TgWkbJ_g24UIhJWbac,9286
38
39
  ingestr/src/facebook_ads/exceptions.py,sha256=4Nlbc0Mv3i5g-9AoyT-n1PIa8IDi3VCTfEAzholx4Wc,115
@@ -61,19 +62,19 @@ ingestr/src/google_sheets/helpers/api_calls.py,sha256=RiVfdacbaneszhmuhYilkJnkc9
61
62
  ingestr/src/google_sheets/helpers/data_processing.py,sha256=RNt2MYfdJhk4bRahnQVezpNg2x9z0vx60YFq2ukZ8vI,11004
62
63
  ingestr/src/gorgias/__init__.py,sha256=_mFkMYwlY5OKEY0o_FK1OKol03A-8uk7bm1cKlmt5cs,21432
63
64
  ingestr/src/gorgias/helpers.py,sha256=DamuijnvhGY9hysQO4txrVMf4izkGbh5qfBKImdOINE,5427
64
- ingestr/src/hubspot/__init__.py,sha256=rSYmN8h6qqxhWCW6elD-pC7iqHXlIofb1F9wvTzziUE,10962
65
- ingestr/src/hubspot/helpers.py,sha256=fscilfO_K7HS2XQxzf7MeZwVXLTP0WdqnV-NhdeqQAA,7748
65
+ ingestr/src/hubspot/__init__.py,sha256=QheZb_F2TEBdzd29SEgkU3AMdIIF7Gpj-t27EXnSIZ4,11448
66
+ ingestr/src/hubspot/helpers.py,sha256=4aVOSzIsQV3RemqRJEJLu7BWMdcOuubwNvrjuMu87rg,8045
66
67
  ingestr/src/hubspot/settings.py,sha256=i73MkSiJfRLMFLfiJgYdhp-rhymHTfoqFzZ4uOJdFJM,2456
67
68
  ingestr/src/kafka/__init__.py,sha256=wMCXdiraeKd1Kssi9WcVCGZaNGm2tJEtnNyuB4aR5_k,3541
68
69
  ingestr/src/kafka/helpers.py,sha256=V9WcVn3PKnEpggArHda4vnAcaV8VDuh__dSmRviJb5Y,7502
69
70
  ingestr/src/kinesis/__init__.py,sha256=u5ThH1y8uObZKXgIo71em1UnX6MsVHWOjcf1jKqKbE8,6205
70
71
  ingestr/src/kinesis/helpers.py,sha256=aF0GCDKSectaaW8XPdERY_6bUs0ky19dcBs24ZFn-o0,2473
71
- ingestr/src/klaviyo/_init_.py,sha256=ucWHqBe8DQvXVpbmxKFAV5ljpCFb4ps_2QTD0OSiWxY,7905
72
+ ingestr/src/klaviyo/_init_.py,sha256=o_noUgbxLk36s4f9W56_ibPorF0n7kVapPUlV0p-jfA,7875
72
73
  ingestr/src/klaviyo/client.py,sha256=tPj79ia7AW0ZOJhzlKNPCliGbdojRNwUFp8HvB2ym5s,7434
73
74
  ingestr/src/klaviyo/helpers.py,sha256=_i-SHffhv25feLDcjy6Blj1UxYLISCwVCMgGtrlnYHk,496
74
75
  ingestr/src/linkedin_ads/__init__.py,sha256=CAPWFyV24loziiphbLmODxZUXZJwm4JxlFkr56q0jfo,1855
75
76
  ingestr/src/linkedin_ads/dimension_time_enum.py,sha256=EmHRdkFyTAfo4chGjThrwqffWJxmAadZMbpTvf0xkQc,198
76
- ingestr/src/linkedin_ads/helpers.py,sha256=6jSIp4DF0iUafJWU3Y7DbIJGKRH6hrx4S7zCTDOjNuE,4528
77
+ ingestr/src/linkedin_ads/helpers.py,sha256=eUWudRVlXl4kqIhfXQ1eVsUpZwJn7UFqKSpnbLfxzds,4498
77
78
  ingestr/src/mongodb/__init__.py,sha256=aMr1PFIDUMRv--ne61lR17HudsN-fsrzMeyxe9PqK2s,4335
78
79
  ingestr/src/mongodb/helpers.py,sha256=y9rYKR8eyIqam_eNsZmwSYevgi8mghh7Zp8qhTHl65s,5652
79
80
  ingestr/src/notion/__init__.py,sha256=36wUui8finbc85ObkRMq8boMraXMUehdABN_AMe_hzA,1834
@@ -106,7 +107,7 @@ ingestr/src/stripe_analytics/settings.py,sha256=rl9L5XumxO0pjkZf7MGesXHp4QLRgnz3
106
107
  ingestr/src/telemetry/event.py,sha256=MpWc5tt0lSJ1pWKe9HQ11BHrcPBxSH40l4wjZi9u0tI,924
107
108
  ingestr/src/testdata/fakebqcredentials.json,sha256=scc6TUc963KAbKTLZCfcmqVzbtzDCW1_8JNRnyAXyy8,628
108
109
  ingestr/src/tiktok_ads/__init__.py,sha256=aEqCl3dTH6_d43s1jgAeG1UasEls_SlorORulYMwIL8,4590
109
- ingestr/src/tiktok_ads/tiktok_helpers.py,sha256=cfdPflCeR_mCk5fxq0v4d7pzlvZDiAoz8bWQJYqKALM,3935
110
+ ingestr/src/tiktok_ads/tiktok_helpers.py,sha256=jmWHvZzN1Vt_PWrJkgq5a2wIwon-OBEzXoZx0jEy-74,3905
110
111
  ingestr/src/zendesk/__init__.py,sha256=tmJ_jdb6kpwmEKpcv6Im71-bOZI6h-Tcofe18OH4I24,17762
111
112
  ingestr/src/zendesk/settings.py,sha256=Vdj706nTJFQ-3KH4nO97iYCQuba3dV3E9gfnmLK6xwU,2294
112
113
  ingestr/src/zendesk/helpers/__init__.py,sha256=YTJejCiUjfIcsj9FrkY0l-JGYDI7RRte1Ydq5FDH_0c,888
@@ -121,8 +122,8 @@ ingestr/testdata/delete_insert_part2.csv,sha256=B_KUzpzbNdDY_n7wWop1mT2cz36TmayS
121
122
  ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ7ZqYN0,276
122
123
  ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
123
124
  ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
124
- ingestr-0.13.30.dist-info/METADATA,sha256=h7uzURTF300XZEFjInqhpEjO6b-RFeBHWgS5jf4qCM0,13659
125
- ingestr-0.13.30.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
126
- ingestr-0.13.30.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
127
- ingestr-0.13.30.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
128
- ingestr-0.13.30.dist-info/RECORD,,
125
+ ingestr-0.13.32.dist-info/METADATA,sha256=RK2X2A37bphtuSAtyffInwt0ev8kKgx7VeZMDILrwsM,13574
126
+ ingestr-0.13.32.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
127
+ ingestr-0.13.32.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
128
+ ingestr-0.13.32.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
129
+ ingestr-0.13.32.dist-info/RECORD,,