ingestr 0.13.82__py3-none-any.whl → 0.13.84__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 CHANGED
@@ -1 +1 @@
1
- version = "v0.13.82"
1
+ version = "v0.13.84"
ingestr/src/factory.py CHANGED
@@ -73,6 +73,7 @@ from ingestr.src.sources import (
73
73
  StripeAnalyticsSource,
74
74
  TikTokSource,
75
75
  TrustpilotSource,
76
+ WiseSource,
76
77
  ZendeskSource,
77
78
  ZoomSource,
78
79
  )
@@ -192,6 +193,7 @@ class SourceDestinationFactory:
192
193
  "zoom": ZoomSource,
193
194
  "clickup": ClickupSource,
194
195
  "influxdb": InfluxDBSource,
196
+ "wise": WiseSource,
195
197
  }
196
198
  destinations: Dict[str, Type[DestinationProtocol]] = {
197
199
  "bigquery": BigQueryDestination,
@@ -485,9 +485,32 @@ query Projects($cursor: String) {
485
485
  description
486
486
  createdAt
487
487
  updatedAt
488
+ archivedAt
489
+ completedAt
490
+ canceledAt
491
+ startedAt
492
+
493
+ color
494
+ icon
495
+ slugId
496
+ url
497
+
488
498
  health
489
499
  priority
500
+ priorityLabel
501
+ state
502
+
490
503
  targetDate
504
+ startDate
505
+
506
+ progress
507
+ currentProgress
508
+ scope
509
+
510
+ sortOrder
511
+ trashed
512
+
513
+ creator { id }
491
514
  lead { id }
492
515
  }
493
516
  pageInfo { hasNextPage endCursor }
@@ -495,6 +518,58 @@ query Projects($cursor: String) {
495
518
  }
496
519
  """
497
520
 
521
+ TEAMS_QUERY = """
522
+ query Teams($cursor: String) {
523
+ teams(first: 50, after: $cursor) {
524
+ nodes {
525
+ id
526
+ name
527
+ key
528
+ description
529
+ color
530
+ icon
531
+ private
532
+ archivedAt
533
+ createdAt
534
+ updatedAt
535
+
536
+ organization { id }
537
+ parent { id }
538
+
539
+ cyclesEnabled
540
+ cycleDuration
541
+ cycleStartDay
542
+ cycleCooldownTime
543
+
544
+ issueCount
545
+ issueEstimationType
546
+ issueEstimationAllowZero
547
+ issueEstimationExtended
548
+ issueOrderingNoPriorityFirst
549
+
550
+ autoArchivePeriod
551
+ autoClosePeriod
552
+ autoCloseChildIssues
553
+ autoCloseParentIssues
554
+
555
+ groupIssueHistory
556
+ timezone
557
+ inviteHash
558
+ joinByDefault
559
+
560
+ slackNewIssue
561
+ slackIssueComments
562
+ slackIssueStatuses
563
+
564
+ triageEnabled
565
+ requirePriorityToLeaveTriage
566
+ upcomingCycleCount
567
+ }
568
+ pageInfo { hasNextPage endCursor }
569
+ }
570
+ }
571
+ """
572
+
498
573
 
499
574
  # Paginated resources configuration
500
575
  PAGINATED_RESOURCES = [
@@ -515,6 +590,7 @@ PAGINATED_RESOURCES = [
515
590
  ("project_milestone", PROJECT_MILESTONES_QUERY, "projectMilestones"),
516
591
  ("project_status", PROJECT_STATUSES_QUERY, "projectStatuses"),
517
592
  ("projects", PROJECTS_QUERY, "projects"),
593
+ ("teams", TEAMS_QUERY, "teams"),
518
594
  ]
519
595
 
520
596
 
ingestr/src/sources.py CHANGED
@@ -3386,3 +3386,41 @@ class InfluxDBSource:
3386
3386
  start_date=start_date,
3387
3387
  end_date=end_date,
3388
3388
  ).with_resources(table)
3389
+
3390
+
3391
+ class WiseSource:
3392
+ def handles_incrementality(self) -> bool:
3393
+ return True
3394
+
3395
+ def dlt_source(self, uri: str, table: str, **kwargs):
3396
+ parsed = urlparse(uri)
3397
+ params = parse_qs(parsed.query)
3398
+ api_key = params.get("api_key")
3399
+
3400
+ if not api_key:
3401
+ raise MissingValueError("api_key", "Wise")
3402
+
3403
+ if table not in ["profiles", "transfers", "balances"]:
3404
+ raise ValueError(
3405
+ f"Resource '{table}' is not supported for Wise source yet, if you are interested in it please create a GitHub issue at https://github.com/bruin-data/ingestr"
3406
+ )
3407
+
3408
+ start_date = kwargs.get("interval_start")
3409
+ if start_date:
3410
+ start_date = ensure_pendulum_datetime(start_date).in_timezone("UTC")
3411
+ else:
3412
+ start_date = pendulum.datetime(2020, 1, 1).in_timezone("UTC")
3413
+
3414
+ end_date = kwargs.get("interval_end")
3415
+ if end_date:
3416
+ end_date = ensure_pendulum_datetime(end_date).in_timezone("UTC")
3417
+ else:
3418
+ end_date = None
3419
+
3420
+ from ingestr.src.wise import wise_source
3421
+
3422
+ return wise_source(
3423
+ api_key=api_key[0],
3424
+ start_date=start_date,
3425
+ end_date=end_date,
3426
+ ).with_resources(table)
@@ -94,7 +94,7 @@ def async_stripe_source(
94
94
  )(endpoint)
95
95
 
96
96
 
97
- @dlt.source
97
+ @dlt.source(max_table_nesting=0)
98
98
  def incremental_stripe_source(
99
99
  endpoints: Tuple[str, ...],
100
100
  stripe_secret_key: str = dlt.secrets.value,
@@ -0,0 +1,68 @@
1
+ from typing import Iterable
2
+
3
+ import dlt
4
+ import pendulum
5
+ from dlt.common.typing import TDataItem
6
+ from dlt.sources import DltResource
7
+
8
+ from .client import WiseClient
9
+
10
+
11
+ @dlt.source(max_table_nesting=0)
12
+ def wise_source(
13
+ api_key: str,
14
+ start_date: pendulum.DateTime,
15
+ end_date: pendulum.DateTime | None = None,
16
+ ) -> Iterable[DltResource]:
17
+ client = WiseClient(api_key)
18
+
19
+ # List of all profiles belonging to user.
20
+ @dlt.resource(write_disposition="merge", name="profiles", primary_key="id")
21
+ def profiles() -> Iterable[TDataItem]:
22
+ yield from client.fetch_profiles()
23
+
24
+ # List transfers for a profile.
25
+ @dlt.resource(write_disposition="merge", name="transfers", primary_key="id")
26
+ def transfers(
27
+ profiles=profiles,
28
+ datetime=dlt.sources.incremental(
29
+ "created",
30
+ initial_value=start_date,
31
+ end_value=end_date,
32
+ range_end="closed",
33
+ range_start="closed",
34
+ ),
35
+ ):
36
+ if datetime.end_value is None:
37
+ end_dt = pendulum.now(tz="UTC")
38
+ else:
39
+ end_dt = datetime.end_value
40
+
41
+ start_dt = datetime.last_value
42
+
43
+ for profile in profiles:
44
+ yield from client.fetch_transfers(profile["id"], start_dt, end_dt)
45
+
46
+ # Retrieve the user's multi-currency account balance accounts. It returns all balance accounts the profile has.
47
+ @dlt.resource(write_disposition="merge", name="balances", primary_key="id")
48
+ def balances(
49
+ profiles=profiles,
50
+ datetime=dlt.sources.incremental(
51
+ "modificationTime",
52
+ initial_value=start_date,
53
+ end_value=end_date,
54
+ range_end="closed",
55
+ range_start="closed",
56
+ ),
57
+ ) -> Iterable[TDataItem]:
58
+ if datetime.end_value is None:
59
+ end_dt = pendulum.now(tz="UTC")
60
+ else:
61
+ end_dt = datetime.end_value
62
+
63
+ start_dt = datetime.last_value
64
+
65
+ for profile in profiles:
66
+ yield from client.fetch_balances(profile["id"], start_dt, end_dt)
67
+
68
+ return profiles, transfers, balances
@@ -0,0 +1,63 @@
1
+ from typing import Iterable
2
+
3
+ import pendulum
4
+ from dlt.sources.helpers.requests import Client
5
+
6
+
7
+ class WiseClient:
8
+ BASE_URL = "https://api.transferwise.com"
9
+
10
+ def __init__(self, api_key: str) -> None:
11
+ self.session = Client(raise_for_status=False).session
12
+ self.session.headers.update({"Authorization": f"Bearer {api_key}"})
13
+
14
+ # https://docs.wise.com/api-docs/api-reference/profile#list-profiles
15
+ def fetch_profiles(self) -> Iterable[dict]:
16
+ url = f"{self.BASE_URL}/v2/profiles"
17
+ resp = self.session.get(url)
18
+ resp.raise_for_status()
19
+ for profile in resp.json():
20
+ yield profile
21
+
22
+ # https://docs.wise.com/api-docs/api-reference/transfer#list-transfers
23
+ def fetch_transfers(
24
+ self, profile_id: str, start_time=pendulum.DateTime, end_time=pendulum.DateTime
25
+ ):
26
+ offset = 0
27
+
28
+ while True:
29
+ data = self.session.get(
30
+ f"{self.BASE_URL}/v1/transfers",
31
+ params={
32
+ "profile": profile_id,
33
+ "createdDateStart": start_time.to_date_string(),
34
+ "createdDateEnd": end_time.to_date_string(),
35
+ "limit": 100,
36
+ "offset": offset,
37
+ },
38
+ )
39
+ response_data = data.json()
40
+
41
+ if not response_data or len(response_data) == 0:
42
+ break
43
+
44
+ for transfer in response_data:
45
+ transfer["created"] = pendulum.parse(transfer["created"])
46
+
47
+ yield transfer
48
+ offset += 100
49
+
50
+ # https://docs.wise.com/api-docs/api-reference/balance#list
51
+ def fetch_balances(
52
+ self, profile_id: str, start_time=pendulum.DateTime, end_time=pendulum.DateTime
53
+ ) -> Iterable[dict]:
54
+ url = f"{self.BASE_URL}/v4/profiles/{profile_id}/balances"
55
+ resp = self.session.get(url, params={"types": "STANDARD,SAVINGS"})
56
+ resp.raise_for_status()
57
+ for balance in resp.json():
58
+ balance["modificationTime"] = pendulum.parse(balance["modificationTime"])
59
+ if (
60
+ balance["modificationTime"] > start_time
61
+ and balance["modificationTime"] < end_time
62
+ ):
63
+ yield balance
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.82
3
+ Version: 0.13.84
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
@@ -83,7 +83,7 @@ Requires-Dist: google-cloud-storage==3.1.0
83
83
  Requires-Dist: google-crc32c==1.6.0
84
84
  Requires-Dist: google-resumable-media==2.7.2
85
85
  Requires-Dist: googleapis-common-protos==1.69.0
86
- Requires-Dist: greenlet==3.2.3
86
+ Requires-Dist: greenlet==3.2.4
87
87
  Requires-Dist: grpc-google-iam-v1==0.14.2
88
88
  Requires-Dist: grpc-interceptor==0.15.4
89
89
  Requires-Dist: grpcio-status==1.62.3
@@ -2,16 +2,16 @@ ingestr/conftest.py,sha256=OE2yxeTCosS9CUFVuqNypm-2ftYvVBeeq7egm3878cI,1981
2
2
  ingestr/main.py,sha256=qoWHNcHh0-xVnyQxbQ-SKuTxPb1RNV3ENkCpqO7CLrk,26694
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=S1Cgqn9Xc0avTk0r_rJ8fMXucqD7c5O68FsKjSWj2Pc,21
5
+ ingestr/src/buildinfo.py,sha256=WN5lOieDcoxVlYgBMBodzZWgJZUv46uGz-LyWD1-F_M,21
6
6
  ingestr/src/destinations.py,sha256=M2Yni6wiWcrvZ8EPJemidqxN156l0rehgCc7xuil7mo,22840
7
7
  ingestr/src/errors.py,sha256=Ufs4_DfE77_E3vnA1fOQdi6cmuLVNm7_SbFLkL1XPGk,686
8
- ingestr/src/factory.py,sha256=rF5Ry4o4t8KulSPBtrd7ZKCI_0TH1DAetG0zs9H7oik,6792
8
+ ingestr/src/factory.py,sha256=SQr7s-WCx_jL8DmPbeCc4VuE35z1J7gCAwKGAkOpv2M,6836
9
9
  ingestr/src/filters.py,sha256=LLecXe9QkLFkFLUZ92OXNdcANr1a8edDxrflc2ko_KA,1452
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/partition.py,sha256=BrIP6wFJvyR7Nus_3ElnfxknUXeCipK_E_bB8kZowfc,969
13
13
  ingestr/src/resource.py,sha256=ZqmZxFQVGlF8rFPhBiUB08HES0yoTj8sZ--jKfaaVps,1164
14
- ingestr/src/sources.py,sha256=Yaej1Iy75JPOccw6jYL6kbhzYFq33t4YelGT-NiAA5I,119661
14
+ ingestr/src/sources.py,sha256=NfD7pj0WOD1wQOV3oZkHmGOAwHMuCCO_Mzf8F8EpobU,120918
15
15
  ingestr/src/table_definition.py,sha256=REbAbqdlmUMUuRh8nEQRreWjPVOQ5ZcfqGkScKdCrmk,390
16
16
  ingestr/src/time.py,sha256=H_Fk2J4ShXyUM-EMY7MqCLZQhlnZMZvO952bmZPc4yE,254
17
17
  ingestr/src/version.py,sha256=J_2xgZ0mKlvuHcjdKCx2nlioneLH0I47JiU_Slr_Nwc,189
@@ -85,7 +85,7 @@ ingestr/src/kinesis/helpers.py,sha256=SO2cFmWNGcykUYmjHdfxWsOQSkLQXyhFtfWnkcUOM0
85
85
  ingestr/src/klaviyo/__init__.py,sha256=o_noUgbxLk36s4f9W56_ibPorF0n7kVapPUlV0p-jfA,7875
86
86
  ingestr/src/klaviyo/client.py,sha256=tPj79ia7AW0ZOJhzlKNPCliGbdojRNwUFp8HvB2ym5s,7434
87
87
  ingestr/src/klaviyo/helpers.py,sha256=_i-SHffhv25feLDcjy6Blj1UxYLISCwVCMgGtrlnYHk,496
88
- ingestr/src/linear/__init__.py,sha256=G2x4HaRl8WWReDJ5HElnNTfVIybdztrIB3acl1ickyo,11003
88
+ ingestr/src/linear/__init__.py,sha256=f0f9MBEV3tK-tTT2DUOj-0-xl2QRJs1x0nYv2Lo1FZQ,12277
89
89
  ingestr/src/linear/helpers.py,sha256=pPqtJrY3D3U2IaqTsD6ChvwMLYiUVtmQ8wTvetz5U7A,3640
90
90
  ingestr/src/linkedin_ads/__init__.py,sha256=CAPWFyV24loziiphbLmODxZUXZJwm4JxlFkr56q0jfo,1855
91
91
  ingestr/src/linkedin_ads/dimension_time_enum.py,sha256=EmHRdkFyTAfo4chGjThrwqffWJxmAadZMbpTvf0xkQc,198
@@ -125,7 +125,7 @@ ingestr/src/solidgate/__init__.py,sha256=Ts83j-JSnFsFuF4tDhVOfZKg7H0-bIpfn3kg1ZO
125
125
  ingestr/src/solidgate/helpers.py,sha256=mAsW_1hpD7ab3Y2vw8fxHi4yD3aT1geLdIYZ7ycyxBc,5690
126
126
  ingestr/src/sql_database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
127
  ingestr/src/sql_database/callbacks.py,sha256=sEFFmXxAURY3yeBjnawigDtq9LBCvi8HFqG4kLd7tMU,2002
128
- ingestr/src/stripe_analytics/__init__.py,sha256=mK8dGKAlMPVqGE9gG30XfbvOvgVD0yWhNpt-D3iavDY,6385
128
+ ingestr/src/stripe_analytics/__init__.py,sha256=6c4zNY5CgmyjbEF94Ch8zUefdl_w8sLI3FjHUyPcC2U,6406
129
129
  ingestr/src/stripe_analytics/helpers.py,sha256=KGtRcSrhKEqzJ3AWpgDV2o4cuBFaIwu2Gc1KgvVWTtg,11764
130
130
  ingestr/src/stripe_analytics/settings.py,sha256=xt1-ljwP4nLTNUa8l3KwFbtK8FtQHgHpzGF5uPKfRsw,2246
131
131
  ingestr/src/telemetry/event.py,sha256=W7bs4uVfPakQ5otmiqgqu1l5SqjYx1p87wudnWXckBc,949
@@ -134,6 +134,8 @@ ingestr/src/tiktok_ads/__init__.py,sha256=_upvSv79uktELTzg78VavYgQftfhp4YM6OXfpk
134
134
  ingestr/src/tiktok_ads/tiktok_helpers.py,sha256=jmWHvZzN1Vt_PWrJkgq5a2wIwon-OBEzXoZx0jEy-74,3905
135
135
  ingestr/src/trustpilot/__init__.py,sha256=ofhjep4qRPIi8q41qc97QVex8UbWF-Fd7gUsqeQlQX8,1279
136
136
  ingestr/src/trustpilot/client.py,sha256=zKYt5C7nrR83Id0KN49EPmtml8MEtlSPlAosEFU3VXY,1616
137
+ ingestr/src/wise/__init__.py,sha256=EiXWE89Jb2Do1t8HK1-GZRS2KwgaiThxsHVEpaBcoh4,2087
138
+ ingestr/src/wise/client.py,sha256=2hTCuOB24-HiwTp02o5i99KF5rjI0CXqgIb9V6gAjjE,2247
137
139
  ingestr/src/zendesk/__init__.py,sha256=tmJ_jdb6kpwmEKpcv6Im71-bOZI6h-Tcofe18OH4I24,17762
138
140
  ingestr/src/zendesk/settings.py,sha256=Vdj706nTJFQ-3KH4nO97iYCQuba3dV3E9gfnmLK6xwU,2294
139
141
  ingestr/src/zendesk/helpers/__init__.py,sha256=YTJejCiUjfIcsj9FrkY0l-JGYDI7RRte1Ydq5FDH_0c,888
@@ -151,8 +153,8 @@ ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ
151
153
  ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
152
154
  ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
153
155
  ingestr/tests/unit/test_smartsheets.py,sha256=eiC2CCO4iNJcuN36ONvqmEDryCA1bA1REpayHpu42lk,5058
154
- ingestr-0.13.82.dist-info/METADATA,sha256=4edZaTP7apukcZRUY4J00G99O3d7Qxz37csXXCNhaww,15182
155
- ingestr-0.13.82.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
156
- ingestr-0.13.82.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
157
- ingestr-0.13.82.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
158
- ingestr-0.13.82.dist-info/RECORD,,
156
+ ingestr-0.13.84.dist-info/METADATA,sha256=RPTXlNxSvMJjGa4dE8VH_7gZsv9dqw51BI7E3dzJQbw,15182
157
+ ingestr-0.13.84.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
158
+ ingestr-0.13.84.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
159
+ ingestr-0.13.84.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
160
+ ingestr-0.13.84.dist-info/RECORD,,