omniload 0.0.0.dev0__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.
- omniload/conftest.py +72 -0
- omniload/main.py +810 -0
- omniload/src/.gitignore +10 -0
- omniload/src/adjust/__init__.py +108 -0
- omniload/src/adjust/adjust_helpers.py +122 -0
- omniload/src/airtable/__init__.py +84 -0
- omniload/src/allium/__init__.py +128 -0
- omniload/src/anthropic/__init__.py +277 -0
- omniload/src/anthropic/helpers.py +525 -0
- omniload/src/applovin/__init__.py +316 -0
- omniload/src/applovin_max/__init__.py +117 -0
- omniload/src/appsflyer/__init__.py +325 -0
- omniload/src/appsflyer/client.py +110 -0
- omniload/src/appstore/__init__.py +142 -0
- omniload/src/appstore/client.py +126 -0
- omniload/src/appstore/errors.py +15 -0
- omniload/src/appstore/models.py +117 -0
- omniload/src/appstore/resources.py +179 -0
- omniload/src/arrow/__init__.py +81 -0
- omniload/src/asana_source/__init__.py +281 -0
- omniload/src/asana_source/helpers.py +30 -0
- omniload/src/asana_source/settings.py +158 -0
- omniload/src/attio/__init__.py +102 -0
- omniload/src/attio/helpers.py +65 -0
- omniload/src/blob.py +95 -0
- omniload/src/bruin/__init__.py +76 -0
- omniload/src/chess/__init__.py +180 -0
- omniload/src/chess/helpers.py +35 -0
- omniload/src/chess/settings.py +18 -0
- omniload/src/clickup/__init__.py +85 -0
- omniload/src/clickup/helpers.py +47 -0
- omniload/src/collector/spinner.py +43 -0
- omniload/src/couchbase_source/__init__.py +118 -0
- omniload/src/couchbase_source/helpers.py +135 -0
- omniload/src/cursor/__init__.py +83 -0
- omniload/src/cursor/helpers.py +188 -0
- omniload/src/customer_io/__init__.py +486 -0
- omniload/src/customer_io/helpers.py +530 -0
- omniload/src/destinations.py +982 -0
- omniload/src/docebo/__init__.py +589 -0
- omniload/src/docebo/client.py +435 -0
- omniload/src/docebo/helpers.py +97 -0
- omniload/src/dune/__init__.py +104 -0
- omniload/src/dune/helpers.py +108 -0
- omniload/src/dynamodb/__init__.py +86 -0
- omniload/src/elasticsearch/__init__.py +80 -0
- omniload/src/elasticsearch/helpers.py +141 -0
- omniload/src/errors.py +26 -0
- omniload/src/facebook_ads/__init__.py +403 -0
- omniload/src/facebook_ads/exceptions.py +19 -0
- omniload/src/facebook_ads/helpers.py +296 -0
- omniload/src/facebook_ads/settings.py +224 -0
- omniload/src/facebook_ads/utils.py +53 -0
- omniload/src/factory.py +305 -0
- omniload/src/filesystem/__init__.py +133 -0
- omniload/src/filesystem/helpers.py +114 -0
- omniload/src/filesystem/readers.py +187 -0
- omniload/src/filters.py +62 -0
- omniload/src/fireflies/__init__.py +151 -0
- omniload/src/fireflies/helpers.py +753 -0
- omniload/src/fluxx/__init__.py +10013 -0
- omniload/src/fluxx/helpers.py +233 -0
- omniload/src/frankfurter/__init__.py +157 -0
- omniload/src/frankfurter/helpers.py +48 -0
- omniload/src/freshdesk/__init__.py +103 -0
- omniload/src/freshdesk/freshdesk_client.py +151 -0
- omniload/src/freshdesk/settings.py +23 -0
- omniload/src/fundraiseup/__init__.py +95 -0
- omniload/src/fundraiseup/client.py +81 -0
- omniload/src/github/__init__.py +202 -0
- omniload/src/github/helpers.py +207 -0
- omniload/src/github/queries.py +129 -0
- omniload/src/github/settings.py +24 -0
- omniload/src/google_ads/__init__.py +198 -0
- omniload/src/google_ads/field.py +17 -0
- omniload/src/google_ads/metrics.py +254 -0
- omniload/src/google_ads/predicates.py +37 -0
- omniload/src/google_ads/reports.py +411 -0
- omniload/src/google_ads/test_google_ads.py +184 -0
- omniload/src/google_analytics/__init__.py +144 -0
- omniload/src/google_analytics/helpers.py +312 -0
- omniload/src/google_sheets/README.md +95 -0
- omniload/src/google_sheets/__init__.py +166 -0
- omniload/src/google_sheets/helpers/__init__.py +15 -0
- omniload/src/google_sheets/helpers/api_calls.py +160 -0
- omniload/src/google_sheets/helpers/data_processing.py +316 -0
- omniload/src/gorgias/__init__.py +595 -0
- omniload/src/gorgias/helpers.py +166 -0
- omniload/src/hostaway/__init__.py +302 -0
- omniload/src/hostaway/client.py +288 -0
- omniload/src/http/__init__.py +38 -0
- omniload/src/http/readers.py +146 -0
- omniload/src/http_client.py +24 -0
- omniload/src/hubspot/__init__.py +800 -0
- omniload/src/hubspot/helpers.py +417 -0
- omniload/src/hubspot/settings.py +329 -0
- omniload/src/indeed/__init__.py +153 -0
- omniload/src/indeed/helpers.py +228 -0
- omniload/src/influxdb/__init__.py +46 -0
- omniload/src/influxdb/client.py +34 -0
- omniload/src/intercom/__init__.py +142 -0
- omniload/src/intercom/helpers.py +674 -0
- omniload/src/intercom/settings.py +279 -0
- omniload/src/isoc_pulse/__init__.py +159 -0
- omniload/src/jira_source/__init__.py +377 -0
- omniload/src/jira_source/helpers.py +510 -0
- omniload/src/jira_source/settings.py +184 -0
- omniload/src/kafka/__init__.py +120 -0
- omniload/src/kafka/helpers.py +241 -0
- omniload/src/kinesis/__init__.py +153 -0
- omniload/src/kinesis/helpers.py +96 -0
- omniload/src/klaviyo/__init__.py +237 -0
- omniload/src/klaviyo/client.py +212 -0
- omniload/src/klaviyo/helpers.py +19 -0
- omniload/src/linear/__init__.py +634 -0
- omniload/src/linear/helpers.py +111 -0
- omniload/src/linkedin_ads/__init__.py +266 -0
- omniload/src/linkedin_ads/dimension_time_enum.py +17 -0
- omniload/src/linkedin_ads/helpers.py +246 -0
- omniload/src/loader.py +69 -0
- omniload/src/mailchimp/__init__.py +126 -0
- omniload/src/mailchimp/helpers.py +226 -0
- omniload/src/mailchimp/settings.py +164 -0
- omniload/src/masking.py +344 -0
- omniload/src/mixpanel/__init__.py +62 -0
- omniload/src/mixpanel/client.py +104 -0
- omniload/src/monday/__init__.py +246 -0
- omniload/src/monday/helpers.py +392 -0
- omniload/src/monday/settings.py +325 -0
- omniload/src/mongodb/__init__.py +281 -0
- omniload/src/mongodb/helpers.py +975 -0
- omniload/src/notion/__init__.py +69 -0
- omniload/src/notion/helpers/__init__.py +14 -0
- omniload/src/notion/helpers/client.py +178 -0
- omniload/src/notion/helpers/database.py +92 -0
- omniload/src/notion/settings.py +17 -0
- omniload/src/partition.py +32 -0
- omniload/src/personio/__init__.py +345 -0
- omniload/src/personio/helpers.py +100 -0
- omniload/src/phantombuster/__init__.py +65 -0
- omniload/src/phantombuster/client.py +87 -0
- omniload/src/pinterest/__init__.py +82 -0
- omniload/src/pipedrive/__init__.py +212 -0
- omniload/src/pipedrive/helpers/__init__.py +37 -0
- omniload/src/pipedrive/helpers/custom_fields_munger.py +116 -0
- omniload/src/pipedrive/helpers/pages.py +129 -0
- omniload/src/pipedrive/settings.py +41 -0
- omniload/src/pipedrive/typing.py +17 -0
- omniload/src/plusvibeai/__init__.py +335 -0
- omniload/src/plusvibeai/helpers.py +544 -0
- omniload/src/plusvibeai/settings.py +252 -0
- omniload/src/primer/__init__.py +45 -0
- omniload/src/primer/helpers.py +79 -0
- omniload/src/quickbooks/__init__.py +117 -0
- omniload/src/reddit_ads/__init__.py +183 -0
- omniload/src/reddit_ads/helpers.py +232 -0
- omniload/src/resource.py +40 -0
- omniload/src/revenuecat/__init__.py +83 -0
- omniload/src/revenuecat/helpers.py +237 -0
- omniload/src/salesforce/__init__.py +170 -0
- omniload/src/salesforce/helpers.py +78 -0
- omniload/src/shopify/__init__.py +1953 -0
- omniload/src/shopify/exceptions.py +17 -0
- omniload/src/shopify/helpers.py +202 -0
- omniload/src/shopify/settings.py +19 -0
- omniload/src/slack/__init__.py +290 -0
- omniload/src/slack/helpers.py +218 -0
- omniload/src/slack/settings.py +36 -0
- omniload/src/smartsheets/__init__.py +82 -0
- omniload/src/snapchat_ads/__init__.py +455 -0
- omniload/src/snapchat_ads/client.py +72 -0
- omniload/src/snapchat_ads/helpers.py +630 -0
- omniload/src/snapchat_ads/settings.py +130 -0
- omniload/src/socrata_source/__init__.py +83 -0
- omniload/src/socrata_source/helpers.py +85 -0
- omniload/src/socrata_source/settings.py +8 -0
- omniload/src/solidgate/__init__.py +219 -0
- omniload/src/solidgate/helpers.py +154 -0
- omniload/src/sources.py +5408 -0
- omniload/src/sql_database/__init__.py +0 -0
- omniload/src/sql_database/callbacks.py +66 -0
- omniload/src/stripe_analytics/__init__.py +183 -0
- omniload/src/stripe_analytics/helpers.py +386 -0
- omniload/src/stripe_analytics/settings.py +80 -0
- omniload/src/table_definition.py +15 -0
- omniload/src/testdata/fakebqcredentials.json +14 -0
- omniload/src/tiktok_ads/__init__.py +150 -0
- omniload/src/tiktok_ads/tiktok_helpers.py +130 -0
- omniload/src/time.py +11 -0
- omniload/src/trustpilot/__init__.py +48 -0
- omniload/src/trustpilot/client.py +48 -0
- omniload/src/version.py +6 -0
- omniload/src/wise/__init__.py +68 -0
- omniload/src/wise/client.py +63 -0
- omniload/src/zendesk/__init__.py +480 -0
- omniload/src/zendesk/helpers/__init__.py +39 -0
- omniload/src/zendesk/helpers/api_helpers.py +119 -0
- omniload/src/zendesk/helpers/credentials.py +68 -0
- omniload/src/zendesk/helpers/talk_api.py +132 -0
- omniload/src/zendesk/settings.py +71 -0
- omniload/src/zoom/__init__.py +99 -0
- omniload/src/zoom/helpers.py +102 -0
- omniload/testdata/.gitignore +2 -0
- omniload/testdata/create_replace.csv +21 -0
- omniload/testdata/delete_insert_expected.csv +6 -0
- omniload/testdata/delete_insert_part1.csv +5 -0
- omniload/testdata/delete_insert_part2.csv +6 -0
- omniload/testdata/merge_expected.csv +5 -0
- omniload/testdata/merge_part1.csv +4 -0
- omniload/testdata/merge_part2.csv +5 -0
- omniload/tests/unit/test_smartsheets.py +133 -0
- omniload-0.0.0.dev0.dist-info/METADATA +439 -0
- omniload-0.0.0.dev0.dist-info/RECORD +218 -0
- omniload-0.0.0.dev0.dist-info/WHEEL +4 -0
- omniload-0.0.0.dev0.dist-info/entry_points.txt +2 -0
- omniload-0.0.0.dev0.dist-info/licenses/LICENSE.Apache-2.0 +201 -0
- omniload-0.0.0.dev0.dist-info/licenses/LICENSE.md +21 -0
- omniload-0.0.0.dev0.dist-info/licenses/NOTICE +35 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Gorgias source helpers"""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Iterable, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from dlt.common.pendulum import pendulum
|
|
7
|
+
from dlt.common.time import ensure_pendulum_datetime
|
|
8
|
+
from dlt.common.typing import Dict, TDataItems
|
|
9
|
+
from dlt.sources.helpers import requests
|
|
10
|
+
from pyrate_limiter import Duration, Limiter, Rate
|
|
11
|
+
from requests.auth import HTTPBasicAuth
|
|
12
|
+
|
|
13
|
+
RETRY_COUNT = 10
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_max_datetime_from_datetime_fields(
|
|
17
|
+
item: Dict[str, Any],
|
|
18
|
+
) -> Tuple[str, Optional[pendulum.DateTime]]:
|
|
19
|
+
"""Get the maximum datetime from any field that ends with _datetime"""
|
|
20
|
+
|
|
21
|
+
max_field_name = None
|
|
22
|
+
max_field_value = None
|
|
23
|
+
for field in item:
|
|
24
|
+
if field.endswith("_datetime") and item[field] is not None:
|
|
25
|
+
dt = ensure_pendulum_datetime(item[field])
|
|
26
|
+
if not max_field_name or dt > max_field_value:
|
|
27
|
+
max_field_name = field
|
|
28
|
+
max_field_value = dt
|
|
29
|
+
|
|
30
|
+
return max_field_name, max_field_value
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def convert_datetime_fields(item: Dict[str, Any]) -> Dict[str, Any]:
|
|
34
|
+
for field in item:
|
|
35
|
+
if field.endswith("_datetime") and item[field] is not None:
|
|
36
|
+
item[field] = ensure_pendulum_datetime(item[field])
|
|
37
|
+
|
|
38
|
+
if "updated_datetime" not in item:
|
|
39
|
+
_, max_datetime = get_max_datetime_from_datetime_fields(item)
|
|
40
|
+
item["updated_datetime"] = max_datetime
|
|
41
|
+
|
|
42
|
+
return item
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def find_latest_timestamp_from_page(
|
|
46
|
+
items: list[Dict[str, Any]],
|
|
47
|
+
) -> Optional[Dict[str, Any]]:
|
|
48
|
+
latest_time = None
|
|
49
|
+
for item in items:
|
|
50
|
+
_, max_field_value = get_max_datetime_from_datetime_fields(item)
|
|
51
|
+
if not latest_time or ensure_pendulum_datetime(max_field_value) > latest_time:
|
|
52
|
+
latest_time = max_field_value
|
|
53
|
+
|
|
54
|
+
return latest_time
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class GorgiasApi:
|
|
58
|
+
"""
|
|
59
|
+
A Gorgias API client that can be used to get pages of data from Gorgias.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
domain: str,
|
|
65
|
+
email: str,
|
|
66
|
+
api_key: str,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Args:
|
|
70
|
+
domain: The domain of your Gorgias account.
|
|
71
|
+
email: The email associated with your Gorgias account.
|
|
72
|
+
api_key: The API key for accessing the Gorgias API.
|
|
73
|
+
"""
|
|
74
|
+
self.domain = domain
|
|
75
|
+
self.email = email
|
|
76
|
+
self.api_key = api_key
|
|
77
|
+
|
|
78
|
+
def get_pages(
|
|
79
|
+
self,
|
|
80
|
+
resource: str,
|
|
81
|
+
params: Optional[Dict[str, Any]] = None,
|
|
82
|
+
start_date: Optional[str] = None,
|
|
83
|
+
end_date: Optional[str] = None,
|
|
84
|
+
) -> Iterable[TDataItems]:
|
|
85
|
+
"""Get all pages from Gorgias using requests.
|
|
86
|
+
Iterates through all pages and yield each page items.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
resource: The resource to get pages for (e.g. products, orders, customers).
|
|
90
|
+
params: Query params to include in the request.
|
|
91
|
+
|
|
92
|
+
Yields:
|
|
93
|
+
List of data items from the page
|
|
94
|
+
"""
|
|
95
|
+
url = f"https://{self.domain}.gorgias.com/api/{resource}"
|
|
96
|
+
rate = Rate(2, Duration.SECOND)
|
|
97
|
+
limiter = Limiter(rate, raise_when_fail=False)
|
|
98
|
+
|
|
99
|
+
start_date_obj = ensure_pendulum_datetime(start_date) if start_date else None
|
|
100
|
+
|
|
101
|
+
if not params:
|
|
102
|
+
params = {}
|
|
103
|
+
|
|
104
|
+
params["limit"] = 100
|
|
105
|
+
if "order_by" not in params:
|
|
106
|
+
params["order_by"] = "updated_datetime:desc"
|
|
107
|
+
|
|
108
|
+
while True:
|
|
109
|
+
limiter.try_acquire(f"gorgias-{self.domain}")
|
|
110
|
+
|
|
111
|
+
# this is to retry a back-off if we get a 429
|
|
112
|
+
for i in range(RETRY_COUNT):
|
|
113
|
+
try:
|
|
114
|
+
response = requests.get(
|
|
115
|
+
url, params=params, auth=HTTPBasicAuth(self.email, self.api_key)
|
|
116
|
+
)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
retry_after = int(response.headers.get("Retry-After", 10))
|
|
119
|
+
print(
|
|
120
|
+
f"Got an error from Gorgias API, retrying after {retry_after} seconds",
|
|
121
|
+
e,
|
|
122
|
+
)
|
|
123
|
+
time.sleep(retry_after)
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
break
|
|
127
|
+
|
|
128
|
+
if len(response.json()["data"]) == 0:
|
|
129
|
+
break
|
|
130
|
+
|
|
131
|
+
json = response.json()
|
|
132
|
+
|
|
133
|
+
items = self.__filter_items_in_range(json["data"], start_date, end_date)
|
|
134
|
+
if len(items) > 0:
|
|
135
|
+
yield items
|
|
136
|
+
|
|
137
|
+
# if there is no cursor, yield the items first and then break the loop
|
|
138
|
+
cursor = json.get("meta", {}).get("next_cursor")
|
|
139
|
+
params["cursor"] = cursor
|
|
140
|
+
if not cursor:
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
if start_date_obj:
|
|
144
|
+
max_datetime = find_latest_timestamp_from_page(json["data"])
|
|
145
|
+
if start_date_obj > ensure_pendulum_datetime(max_datetime):
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
def __filter_items_in_range(
|
|
149
|
+
self,
|
|
150
|
+
items: list[Dict[str, Any]],
|
|
151
|
+
start_date: Optional[str],
|
|
152
|
+
end_date: Optional[str],
|
|
153
|
+
) -> list[Dict[str, Any]]:
|
|
154
|
+
start_date_obj = ensure_pendulum_datetime(start_date) if start_date else None
|
|
155
|
+
end_date_obj = ensure_pendulum_datetime(end_date) if end_date else None
|
|
156
|
+
|
|
157
|
+
filtered = []
|
|
158
|
+
for item in items:
|
|
159
|
+
converted_item = convert_datetime_fields(item)
|
|
160
|
+
if start_date_obj and item["updated_datetime"] < start_date_obj:
|
|
161
|
+
continue
|
|
162
|
+
if end_date_obj and item["updated_datetime"] > end_date_obj:
|
|
163
|
+
continue
|
|
164
|
+
filtered.append(converted_item)
|
|
165
|
+
|
|
166
|
+
return filtered
|
|
@@ -0,0 +1,302 @@
|
|
|
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 HostawayClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dlt.source(max_table_nesting=0)
|
|
12
|
+
def hostaway_source(
|
|
13
|
+
api_key: str,
|
|
14
|
+
start_date: pendulum.DateTime,
|
|
15
|
+
end_date: pendulum.DateTime | None = None,
|
|
16
|
+
) -> Iterable[DltResource]:
|
|
17
|
+
"""
|
|
18
|
+
Hostaway API source for fetching listings and fee settings data.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
api_key: Hostaway API key for Bearer token authentication
|
|
22
|
+
start_date: Start date for incremental loading
|
|
23
|
+
end_date: End date for incremental loading (defaults to current time)
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Iterable[DltResource]: DLT resources for listings and/or fee settings
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
client = HostawayClient(api_key)
|
|
30
|
+
|
|
31
|
+
@dlt.resource(
|
|
32
|
+
write_disposition="merge",
|
|
33
|
+
name="listings",
|
|
34
|
+
primary_key="id",
|
|
35
|
+
)
|
|
36
|
+
def listings(
|
|
37
|
+
datetime=dlt.sources.incremental(
|
|
38
|
+
"latestActivityOn",
|
|
39
|
+
initial_value=start_date,
|
|
40
|
+
end_value=end_date,
|
|
41
|
+
range_end="closed",
|
|
42
|
+
range_start="closed",
|
|
43
|
+
),
|
|
44
|
+
) -> Iterable[TDataItem]:
|
|
45
|
+
"""
|
|
46
|
+
Fetch listings from Hostaway API with incremental loading.
|
|
47
|
+
Uses latestActivityOn field as the incremental cursor.
|
|
48
|
+
"""
|
|
49
|
+
start_dt = datetime.last_value
|
|
50
|
+
end_dt = (
|
|
51
|
+
datetime.end_value
|
|
52
|
+
if datetime.end_value is not None
|
|
53
|
+
else pendulum.now(tz="UTC")
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
yield from client.fetch_listings(start_dt, end_dt)
|
|
57
|
+
|
|
58
|
+
@dlt.resource(
|
|
59
|
+
write_disposition="merge",
|
|
60
|
+
name="listing_fee_settings",
|
|
61
|
+
primary_key="id",
|
|
62
|
+
)
|
|
63
|
+
def listing_fee_settings(
|
|
64
|
+
datetime=dlt.sources.incremental(
|
|
65
|
+
"updatedOn",
|
|
66
|
+
initial_value=start_date,
|
|
67
|
+
end_value=end_date,
|
|
68
|
+
range_end="closed",
|
|
69
|
+
range_start="closed",
|
|
70
|
+
),
|
|
71
|
+
) -> Iterable[TDataItem]:
|
|
72
|
+
"""
|
|
73
|
+
Fetch listing fee settings from Hostaway API with incremental loading.
|
|
74
|
+
Uses updatedOn field as the incremental cursor.
|
|
75
|
+
"""
|
|
76
|
+
start_dt = datetime.last_value
|
|
77
|
+
end_dt = (
|
|
78
|
+
datetime.end_value
|
|
79
|
+
if datetime.end_value is not None
|
|
80
|
+
else pendulum.now(tz="UTC")
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
yield from client.fetch_all_listing_fee_settings(start_dt, end_dt)
|
|
84
|
+
|
|
85
|
+
@dlt.resource(
|
|
86
|
+
write_disposition="replace",
|
|
87
|
+
name="listing_agreements",
|
|
88
|
+
)
|
|
89
|
+
def listing_agreements() -> Iterable[TDataItem]:
|
|
90
|
+
"""
|
|
91
|
+
Fetch listing agreements from Hostaway API.
|
|
92
|
+
|
|
93
|
+
Note: Uses replace mode, so no incremental loading.
|
|
94
|
+
"""
|
|
95
|
+
very_old_date = pendulum.datetime(1970, 1, 1, tz="UTC")
|
|
96
|
+
now = pendulum.now(tz="UTC")
|
|
97
|
+
yield from client.fetch_all_listing_agreements(very_old_date, now)
|
|
98
|
+
|
|
99
|
+
@dlt.resource(
|
|
100
|
+
write_disposition="replace",
|
|
101
|
+
name="listing_pricing_settings",
|
|
102
|
+
)
|
|
103
|
+
def listing_pricing_settings() -> Iterable[TDataItem]:
|
|
104
|
+
"""
|
|
105
|
+
Fetch listing pricing settings from Hostaway API.
|
|
106
|
+
|
|
107
|
+
Note: Uses replace mode, so no incremental loading.
|
|
108
|
+
"""
|
|
109
|
+
very_old_date = pendulum.datetime(1970, 1, 1, tz="UTC")
|
|
110
|
+
now = pendulum.now(tz="UTC")
|
|
111
|
+
yield from client.fetch_all_listing_pricing_settings(very_old_date, now)
|
|
112
|
+
|
|
113
|
+
@dlt.resource(
|
|
114
|
+
write_disposition="replace",
|
|
115
|
+
name="cancellation_policies",
|
|
116
|
+
)
|
|
117
|
+
def cancellation_policies() -> Iterable[TDataItem]:
|
|
118
|
+
yield from client.fetch_cancellation_policies()
|
|
119
|
+
|
|
120
|
+
@dlt.resource(
|
|
121
|
+
write_disposition="replace",
|
|
122
|
+
name="cancellation_policies_airbnb",
|
|
123
|
+
)
|
|
124
|
+
def cancellation_policies_airbnb() -> Iterable[TDataItem]:
|
|
125
|
+
yield from client.fetch_cancellation_policies_airbnb()
|
|
126
|
+
|
|
127
|
+
@dlt.resource(
|
|
128
|
+
write_disposition="replace",
|
|
129
|
+
name="cancellation_policies_marriott",
|
|
130
|
+
)
|
|
131
|
+
def cancellation_policies_marriott() -> Iterable[TDataItem]:
|
|
132
|
+
yield from client.fetch_cancellation_policies_marriott()
|
|
133
|
+
|
|
134
|
+
@dlt.resource(
|
|
135
|
+
write_disposition="replace",
|
|
136
|
+
name="cancellation_policies_vrbo",
|
|
137
|
+
)
|
|
138
|
+
def cancellation_policies_vrbo() -> Iterable[TDataItem]:
|
|
139
|
+
yield from client.fetch_cancellation_policies_vrbo()
|
|
140
|
+
|
|
141
|
+
@dlt.resource(
|
|
142
|
+
write_disposition="replace",
|
|
143
|
+
name="reservations",
|
|
144
|
+
selected=False,
|
|
145
|
+
)
|
|
146
|
+
def reservations() -> Iterable[TDataItem]:
|
|
147
|
+
yield from client.fetch_reservations()
|
|
148
|
+
|
|
149
|
+
@dlt.transformer(
|
|
150
|
+
data_from=reservations,
|
|
151
|
+
write_disposition="replace",
|
|
152
|
+
name="finance_fields",
|
|
153
|
+
)
|
|
154
|
+
def finance_fields(reservation_item: TDataItem) -> Iterable[TDataItem]:
|
|
155
|
+
@dlt.defer
|
|
156
|
+
def _get_finance_field(res_id):
|
|
157
|
+
return list(client.fetch_finance_field(res_id))
|
|
158
|
+
|
|
159
|
+
reservation_id_val = reservation_item.get("id")
|
|
160
|
+
if reservation_id_val:
|
|
161
|
+
yield _get_finance_field(reservation_id_val)
|
|
162
|
+
|
|
163
|
+
@dlt.resource(
|
|
164
|
+
write_disposition="replace",
|
|
165
|
+
name="reservation_payment_methods",
|
|
166
|
+
)
|
|
167
|
+
def reservation_payment_methods() -> Iterable[TDataItem]:
|
|
168
|
+
yield from client.fetch_reservation_payment_methods()
|
|
169
|
+
|
|
170
|
+
@dlt.transformer(
|
|
171
|
+
data_from=reservations,
|
|
172
|
+
write_disposition="replace",
|
|
173
|
+
name="reservation_rental_agreements",
|
|
174
|
+
)
|
|
175
|
+
def reservation_rental_agreements(
|
|
176
|
+
reservation_item: TDataItem,
|
|
177
|
+
) -> Iterable[TDataItem]:
|
|
178
|
+
@dlt.defer
|
|
179
|
+
def _get_rental_agreement(res_id):
|
|
180
|
+
return list(client.fetch_reservation_rental_agreement(res_id))
|
|
181
|
+
|
|
182
|
+
reservation_id = reservation_item.get("id")
|
|
183
|
+
if reservation_id:
|
|
184
|
+
yield _get_rental_agreement(reservation_id)
|
|
185
|
+
|
|
186
|
+
@dlt.transformer(
|
|
187
|
+
data_from=listings,
|
|
188
|
+
write_disposition="replace",
|
|
189
|
+
name="listing_calendars",
|
|
190
|
+
)
|
|
191
|
+
def listing_calendars(listing_item: TDataItem) -> Iterable[TDataItem]:
|
|
192
|
+
@dlt.defer
|
|
193
|
+
def _get_calendar(lst_id):
|
|
194
|
+
return list(client.fetch_listing_calendar(lst_id))
|
|
195
|
+
|
|
196
|
+
listing_id_val = listing_item.get("id")
|
|
197
|
+
if listing_id_val:
|
|
198
|
+
yield _get_calendar(listing_id_val)
|
|
199
|
+
|
|
200
|
+
@dlt.resource(
|
|
201
|
+
write_disposition="replace",
|
|
202
|
+
name="conversations",
|
|
203
|
+
)
|
|
204
|
+
def conversations() -> Iterable[TDataItem]:
|
|
205
|
+
yield from client.fetch_conversations()
|
|
206
|
+
|
|
207
|
+
@dlt.resource(
|
|
208
|
+
write_disposition="replace",
|
|
209
|
+
name="message_templates",
|
|
210
|
+
)
|
|
211
|
+
def message_templates() -> Iterable[TDataItem]:
|
|
212
|
+
yield from client.fetch_message_templates()
|
|
213
|
+
|
|
214
|
+
@dlt.resource(
|
|
215
|
+
write_disposition="replace",
|
|
216
|
+
name="bed_types",
|
|
217
|
+
)
|
|
218
|
+
def bed_types() -> Iterable[TDataItem]:
|
|
219
|
+
yield from client.fetch_bed_types()
|
|
220
|
+
|
|
221
|
+
@dlt.resource(
|
|
222
|
+
write_disposition="replace",
|
|
223
|
+
name="property_types",
|
|
224
|
+
)
|
|
225
|
+
def property_types() -> Iterable[TDataItem]:
|
|
226
|
+
yield from client.fetch_property_types()
|
|
227
|
+
|
|
228
|
+
@dlt.resource(
|
|
229
|
+
write_disposition="replace",
|
|
230
|
+
name="countries",
|
|
231
|
+
)
|
|
232
|
+
def countries() -> Iterable[TDataItem]:
|
|
233
|
+
yield from client.fetch_countries()
|
|
234
|
+
|
|
235
|
+
@dlt.resource(
|
|
236
|
+
write_disposition="replace",
|
|
237
|
+
name="account_tax_settings",
|
|
238
|
+
)
|
|
239
|
+
def account_tax_settings() -> Iterable[TDataItem]:
|
|
240
|
+
yield from client.fetch_account_tax_settings()
|
|
241
|
+
|
|
242
|
+
@dlt.resource(
|
|
243
|
+
write_disposition="replace",
|
|
244
|
+
name="user_groups",
|
|
245
|
+
)
|
|
246
|
+
def user_groups() -> Iterable[TDataItem]:
|
|
247
|
+
yield from client.fetch_user_groups()
|
|
248
|
+
|
|
249
|
+
@dlt.resource(
|
|
250
|
+
write_disposition="replace",
|
|
251
|
+
name="guest_payment_charges",
|
|
252
|
+
)
|
|
253
|
+
def guest_payment_charges() -> Iterable[TDataItem]:
|
|
254
|
+
yield from client.fetch_guest_payment_charges()
|
|
255
|
+
|
|
256
|
+
@dlt.resource(
|
|
257
|
+
write_disposition="replace",
|
|
258
|
+
name="coupons",
|
|
259
|
+
)
|
|
260
|
+
def coupons() -> Iterable[TDataItem]:
|
|
261
|
+
yield from client.fetch_coupons()
|
|
262
|
+
|
|
263
|
+
@dlt.resource(
|
|
264
|
+
write_disposition="replace",
|
|
265
|
+
name="webhook_reservations",
|
|
266
|
+
)
|
|
267
|
+
def webhook_reservations() -> Iterable[TDataItem]:
|
|
268
|
+
yield from client.fetch_webhook_reservations()
|
|
269
|
+
|
|
270
|
+
@dlt.resource(
|
|
271
|
+
write_disposition="replace",
|
|
272
|
+
name="tasks",
|
|
273
|
+
)
|
|
274
|
+
def tasks() -> Iterable[TDataItem]:
|
|
275
|
+
yield from client.fetch_tasks()
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
listings,
|
|
279
|
+
listing_fee_settings,
|
|
280
|
+
listing_agreements,
|
|
281
|
+
listing_pricing_settings,
|
|
282
|
+
cancellation_policies,
|
|
283
|
+
cancellation_policies_airbnb,
|
|
284
|
+
cancellation_policies_marriott,
|
|
285
|
+
cancellation_policies_vrbo,
|
|
286
|
+
reservations,
|
|
287
|
+
finance_fields,
|
|
288
|
+
reservation_payment_methods,
|
|
289
|
+
reservation_rental_agreements,
|
|
290
|
+
listing_calendars,
|
|
291
|
+
conversations,
|
|
292
|
+
message_templates,
|
|
293
|
+
bed_types,
|
|
294
|
+
property_types,
|
|
295
|
+
countries,
|
|
296
|
+
account_tax_settings,
|
|
297
|
+
user_groups,
|
|
298
|
+
guest_payment_charges,
|
|
299
|
+
coupons,
|
|
300
|
+
webhook_reservations,
|
|
301
|
+
tasks,
|
|
302
|
+
)
|