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,246 @@
1
+ """
2
+ Monday.com source for data extraction via GraphQL API.
3
+
4
+ This source provides access to Monday.com app installation data.
5
+ """
6
+
7
+ from typing import Any, Iterable, Iterator, Optional
8
+
9
+ import dlt
10
+ from dlt.sources import DltResource
11
+
12
+ from .helpers import MondayClient, normalize_dict
13
+
14
+
15
+ @dlt.source(max_table_nesting=0, name="monday_source")
16
+ def monday_source(
17
+ api_token: str,
18
+ params: list[str],
19
+ start_date: Optional[str] = None,
20
+ end_date: Optional[str] = None,
21
+ ) -> Iterable[DltResource]:
22
+ """
23
+ Monday.com data source.
24
+
25
+ Args:
26
+ api_token: Monday.com API token for authentication
27
+ params: Table-specific parameters in format [table_type, ...params]
28
+ start_date: Optional start date for date-filtered queries (YYYY-MM-DD)
29
+ end_date: Optional end date for date-filtered queries (YYYY-MM-DD)
30
+
31
+ Yields:
32
+ DltResource: Data resource for the requested table
33
+ """
34
+ monday_client = MondayClient(api_token)
35
+
36
+ @dlt.resource(
37
+ name="account",
38
+ write_disposition="replace",
39
+ )
40
+ def fetch_account() -> Iterator[dict[str, Any]]:
41
+ """
42
+ Fetch account information from Monday.com.
43
+
44
+ Table format: account (no parameters needed)
45
+ """
46
+ if len(params) != 0:
47
+ raise ValueError("Account table must be in the format `account`")
48
+
49
+ yield normalize_dict(monday_client.get_account())
50
+
51
+ @dlt.resource(
52
+ name="account_roles",
53
+ write_disposition="replace",
54
+ )
55
+ def fetch_account_roles() -> Iterator[dict[str, Any]]:
56
+ """
57
+ Fetch account roles from Monday.com.
58
+
59
+ Table format: account_roles (no parameters needed)
60
+ """
61
+ if len(params) != 0:
62
+ raise ValueError(
63
+ "Account roles table must be in the format `account_roles`"
64
+ )
65
+
66
+ yield from monday_client.get_account_roles()
67
+
68
+ @dlt.resource(
69
+ name="users",
70
+ write_disposition="replace",
71
+ )
72
+ def fetch_users() -> Iterator[dict[str, Any]]:
73
+ """
74
+ Fetch users from Monday.com.
75
+
76
+ Table format: users (no parameters needed)
77
+ """
78
+ if len(params) != 0:
79
+ raise ValueError("Users table must be in the format `users`")
80
+
81
+ yield from monday_client.get_users()
82
+
83
+ @dlt.resource(
84
+ name="boards",
85
+ write_disposition="merge",
86
+ primary_key="id",
87
+ )
88
+ def fetch_boards(
89
+ updated_at: dlt.sources.incremental[str] = dlt.sources.incremental(
90
+ "updated_at", initial_value=start_date
91
+ ),
92
+ ) -> Iterator[dict[str, Any]]:
93
+ """
94
+ Fetch boards from Monday.com.
95
+
96
+ Table format: boards (no parameters needed)
97
+ """
98
+ if len(params) != 0:
99
+ raise ValueError("Boards table must be in the format `boards`")
100
+
101
+ yield from monday_client.get_boards()
102
+
103
+ @dlt.resource(
104
+ name="workspaces",
105
+ write_disposition="replace",
106
+ )
107
+ def fetch_workspaces() -> Iterator[dict[str, Any]]:
108
+ """
109
+ Fetch workspaces from Monday.com.
110
+
111
+ Table format: workspaces (no parameters needed)
112
+ """
113
+ if len(params) != 0:
114
+ raise ValueError("Workspaces table must be in the format `workspaces`")
115
+
116
+ yield from monday_client.get_workspaces()
117
+
118
+ @dlt.resource(
119
+ name="webhooks",
120
+ write_disposition="replace",
121
+ )
122
+ def fetch_webhooks() -> Iterator[dict[str, Any]]:
123
+ """
124
+ Fetch webhooks from Monday.com.
125
+
126
+ Table format: webhooks (no parameters needed)
127
+ """
128
+ if len(params) != 0:
129
+ raise ValueError("Webhooks table must be in the format `webhooks`")
130
+
131
+ yield from monday_client.get_webhooks()
132
+
133
+ @dlt.resource(
134
+ name="updates",
135
+ write_disposition="merge",
136
+ primary_key="id",
137
+ )
138
+ def fetch_updates(
139
+ updated_at: dlt.sources.incremental[str] = dlt.sources.incremental(
140
+ "updated_at", initial_value=start_date
141
+ ),
142
+ ) -> Iterator[dict[str, Any]]:
143
+ """
144
+ Fetch updates from Monday.com.
145
+
146
+ Table format: updates (no parameters needed)
147
+ Requires start_date and end_date parameters
148
+ """
149
+ if len(params) != 0:
150
+ raise ValueError("Updates table must be in the format `updates`")
151
+
152
+ yield from monday_client.get_updates(start_date=start_date, end_date=end_date)
153
+
154
+ @dlt.resource(
155
+ name="teams",
156
+ write_disposition="replace",
157
+ )
158
+ def fetch_teams() -> Iterator[dict[str, Any]]:
159
+ """
160
+ Fetch teams from Monday.com.
161
+
162
+ Table format: teams (no parameters needed)
163
+ """
164
+ if len(params) != 0:
165
+ raise ValueError("Teams table must be in the format `teams`")
166
+
167
+ yield from monday_client.get_teams()
168
+
169
+ @dlt.resource(
170
+ name="tags",
171
+ write_disposition="replace",
172
+ )
173
+ def fetch_tags() -> Iterator[dict[str, Any]]:
174
+ """
175
+ Fetch tags from Monday.com.
176
+
177
+ Table format: tags (no parameters needed)
178
+ """
179
+ if len(params) != 0:
180
+ raise ValueError("Tags table must be in the format `tags`")
181
+
182
+ yield from monday_client.get_tags()
183
+
184
+ @dlt.resource(
185
+ name="custom_activities",
186
+ write_disposition="replace",
187
+ )
188
+ def fetch_custom_activities() -> Iterator[dict[str, Any]]:
189
+ """
190
+ Fetch custom activities from Monday.com.
191
+
192
+ Table format: custom_activities (no parameters needed)
193
+ """
194
+ if len(params) != 0:
195
+ raise ValueError(
196
+ "Custom activities table must be in the format `custom_activities`"
197
+ )
198
+
199
+ yield from monday_client.get_custom_activities()
200
+
201
+ @dlt.resource(
202
+ name="board_columns",
203
+ write_disposition="replace",
204
+ )
205
+ def fetch_board_columns() -> Iterator[dict[str, Any]]:
206
+ """
207
+ Fetch board columns from Monday.com.
208
+
209
+ Table format: board_columns (no parameters needed)
210
+ """
211
+ if len(params) != 0:
212
+ raise ValueError(
213
+ "Board columns table must be in the format `board_columns`"
214
+ )
215
+
216
+ yield from monday_client.get_board_columns()
217
+
218
+ @dlt.resource(
219
+ name="board_views",
220
+ write_disposition="replace",
221
+ )
222
+ def fetch_board_views() -> Iterator[dict[str, Any]]:
223
+ """
224
+ Fetch board views from Monday.com.
225
+
226
+ Table format: board_views (no parameters needed)
227
+ """
228
+ if len(params) != 0:
229
+ raise ValueError("Board views table must be in the format `board_views`")
230
+
231
+ yield from monday_client.get_board_views()
232
+
233
+ return (
234
+ fetch_account,
235
+ fetch_account_roles,
236
+ fetch_users,
237
+ fetch_boards,
238
+ fetch_workspaces,
239
+ fetch_webhooks,
240
+ fetch_updates,
241
+ fetch_teams,
242
+ fetch_tags,
243
+ fetch_custom_activities,
244
+ fetch_board_columns,
245
+ fetch_board_views,
246
+ )
@@ -0,0 +1,392 @@
1
+ from typing import Any, Dict, Iterator, Optional
2
+
3
+ from ingestr.src.http_client import create_client
4
+
5
+ from .settings import (
6
+ ACCOUNT_QUERY,
7
+ ACCOUNT_ROLES_QUERY,
8
+ BOARD_COLUMNS_QUERY,
9
+ BOARD_VIEWS_QUERY,
10
+ BOARDS_QUERY,
11
+ CUSTOM_ACTIVITIES_QUERY,
12
+ MAX_PAGE_SIZE,
13
+ TAGS_QUERY,
14
+ TEAMS_QUERY,
15
+ UPDATES_QUERY,
16
+ USERS_QUERY,
17
+ WEBHOOKS_QUERY,
18
+ WORKSPACES_QUERY,
19
+ )
20
+
21
+
22
+ def _paginate(
23
+ client: "MondayClient",
24
+ query: str,
25
+ field_name: str,
26
+ limit: int = 100,
27
+ extra_variables: Optional[Dict[str, Any]] = None,
28
+ ) -> Iterator[Dict[str, Any]]:
29
+ """
30
+ Helper function to paginate through Monday.com API results.
31
+
32
+ Args:
33
+ client: MondayClient instance
34
+ query: GraphQL query with $limit and $page variables
35
+ field_name: Name of the field in the response to extract
36
+ limit: Number of results per page
37
+ extra_variables: Additional variables to pass to the query
38
+
39
+ Yields:
40
+ Normalized dictionaries from the API response
41
+ """
42
+ page = 1
43
+
44
+ while True:
45
+ variables = {
46
+ "limit": min(limit, MAX_PAGE_SIZE),
47
+ "page": page,
48
+ }
49
+
50
+ if extra_variables:
51
+ variables.update(extra_variables)
52
+
53
+ data = client._execute_query(query, variables)
54
+ items = data.get(field_name, [])
55
+
56
+ if not items:
57
+ break
58
+
59
+ for item in items:
60
+ yield normalize_dict(item)
61
+
62
+ if len(items) < limit:
63
+ break
64
+
65
+ page += 1
66
+
67
+
68
+ def _get_all_board_ids(client: "MondayClient") -> list[str]:
69
+ """
70
+ Collect all board IDs from the Monday.com API.
71
+
72
+ Args:
73
+ client: MondayClient instance
74
+
75
+ Returns:
76
+ List of board IDs as strings
77
+ """
78
+ board_ids = []
79
+ for board in _paginate(client, BOARDS_QUERY, "boards", MAX_PAGE_SIZE):
80
+ board_id = board.get("id")
81
+ if board_id:
82
+ board_ids.append(str(board_id))
83
+ return board_ids
84
+
85
+
86
+ def _fetch_nested_board_data(
87
+ client: "MondayClient", query: str, nested_field: str
88
+ ) -> Iterator[Dict[str, Any]]:
89
+ """
90
+ Fetch nested data from boards (columns, views, etc).
91
+
92
+ Args:
93
+ client: MondayClient instance
94
+ query: GraphQL query to execute
95
+ nested_field: Name of the nested field to extract (e.g., "columns", "views")
96
+
97
+ Yields:
98
+ Dict containing nested data with board_id added
99
+ """
100
+ board_ids = _get_all_board_ids(client)
101
+
102
+ if not board_ids:
103
+ return
104
+
105
+ for board_id in board_ids:
106
+ variables = {"board_ids": [board_id]}
107
+ data = client._execute_query(query, variables)
108
+ boards = data.get("boards", [])
109
+
110
+ for board in boards:
111
+ nested_items = board.get(nested_field, [])
112
+
113
+ if nested_items and isinstance(nested_items, list):
114
+ for item in nested_items:
115
+ item_data = item.copy()
116
+ item_data["board_id"] = board.get("id")
117
+ yield normalize_dict(item_data)
118
+
119
+
120
+ def _fetch_simple_list(
121
+ client: "MondayClient", query: str, field_name: str
122
+ ) -> Iterator[Dict[str, Any]]:
123
+ """
124
+ Fetch a simple list of items from Monday.com API without pagination.
125
+
126
+ Args:
127
+ client: MondayClient instance
128
+ query: GraphQL query to execute
129
+ field_name: Name of the field in the response to extract
130
+
131
+ Yields:
132
+ Normalized dictionaries from the API response
133
+ """
134
+ data = client._execute_query(query)
135
+ items = data.get(field_name, [])
136
+
137
+ for item in items:
138
+ yield normalize_dict(item)
139
+
140
+
141
+ def normalize_dict(data: Dict[str, Any]) -> Dict[str, Any]:
142
+ """
143
+ Normalize dictionary fields by detecting their structure:
144
+ - Convert nested objects with 'id' field to {field_name}_id
145
+ - Convert objects with other fields to flattened {field_name}_{subfield}
146
+ - Convert arrays to JSON strings for storage
147
+ - Preserve null values
148
+
149
+ Args:
150
+ data: The dictionary to normalize
151
+
152
+ Returns:
153
+ Normalized dictionary with flattened structure
154
+
155
+ Example:
156
+ >>> normalize_dict({"user": {"id": "123"}, "plan": {"tier": "pro"}})
157
+ {"user_id": "123", "plan_tier": "pro"}
158
+ """
159
+ import json
160
+
161
+ normalized: Dict[str, Any] = {}
162
+
163
+ for key, value in data.items():
164
+ if value is None:
165
+ # Keep null values as-is
166
+ normalized[key] = None
167
+ elif isinstance(value, dict):
168
+ # If the dict has only an 'id' field, replace with {key}_id
169
+ if "id" in value and len(value) == 1:
170
+ normalized[f"{key}_id"] = value["id"]
171
+ # If dict has multiple fields, flatten them
172
+ elif value:
173
+ for subkey, subvalue in value.items():
174
+ normalized[f"{key}_{subkey}"] = subvalue
175
+ elif isinstance(value, list):
176
+ # If list contains dicts with only 'id' field, extract ids
177
+ if value and isinstance(value[0], dict) and list(value[0].keys()) == ["id"]:
178
+ normalized[key] = [item["id"] for item in value]
179
+ else:
180
+ # Convert other lists to JSON strings for storage
181
+ normalized[key] = json.dumps(value)
182
+ else:
183
+ # Add scalar values directly
184
+ normalized[key] = value
185
+
186
+ return normalized
187
+
188
+
189
+ class MondayClient:
190
+ """Monday.com GraphQL API client."""
191
+
192
+ def __init__(self, api_token: str) -> None:
193
+ self.api_token = api_token
194
+ self.base_url = "https://api.monday.com/v2"
195
+ self.session = create_client()
196
+
197
+ def _headers(self) -> Dict[str, str]:
198
+ return {
199
+ "Authorization": self.api_token,
200
+ "Content-Type": "application/json",
201
+ }
202
+
203
+ def _execute_query(
204
+ self, query: str, variables: Optional[Dict[str, Any]] = None
205
+ ) -> Dict[str, Any]:
206
+ """Execute a GraphQL query against Monday.com API."""
207
+ payload: Dict[str, Any] = {"query": query}
208
+ if variables:
209
+ payload["variables"] = variables
210
+
211
+ response = self.session.post(
212
+ self.base_url,
213
+ headers=self._headers(),
214
+ json=payload,
215
+ )
216
+ response.raise_for_status()
217
+ data = response.json()
218
+
219
+ if "errors" in data:
220
+ raise Exception(f"GraphQL errors: {data['errors']}")
221
+
222
+ return data.get("data", {})
223
+
224
+ def get_account(self) -> Dict[str, Any]:
225
+ """
226
+ Fetch account information from Monday.com API.
227
+
228
+ Returns:
229
+ Dict containing account data
230
+ """
231
+ data = self._execute_query(ACCOUNT_QUERY)
232
+ account = data.get("account", {})
233
+
234
+ if not account:
235
+ raise Exception("No account data returned from Monday.com API")
236
+
237
+ return normalize_dict(account)
238
+
239
+ def get_account_roles(self) -> Iterator[Dict[str, Any]]:
240
+ """
241
+ Fetch account roles from Monday.com API.
242
+
243
+ Yields:
244
+ Dict containing account role data
245
+ """
246
+ yield from _fetch_simple_list(self, ACCOUNT_ROLES_QUERY, "account_roles")
247
+
248
+ def get_users(self, limit: int = MAX_PAGE_SIZE) -> Iterator[Dict[str, Any]]:
249
+ """
250
+ Fetch users from Monday.com API with pagination.
251
+
252
+ Args:
253
+ limit: Number of results per page (max 100)
254
+
255
+ Yields:
256
+ Dict containing user data
257
+ """
258
+ yield from _paginate(self, USERS_QUERY, "users", limit)
259
+
260
+ def get_boards(self, limit: int = MAX_PAGE_SIZE) -> Iterator[Dict[str, Any]]:
261
+ """
262
+ Fetch boards from Monday.com API with pagination.
263
+
264
+ Args:
265
+ limit: Number of results per page (max 100)
266
+
267
+ Yields:
268
+ Dict containing board data
269
+ """
270
+ yield from _paginate(self, BOARDS_QUERY, "boards", limit)
271
+
272
+ def get_workspaces(self) -> Iterator[Dict[str, Any]]:
273
+ """
274
+ Fetch workspaces from Monday.com API.
275
+ First gets all boards to extract unique workspace IDs,
276
+ then fetches workspace details.
277
+
278
+ Yields:
279
+ Dict containing workspace data
280
+ """
281
+ # Collect unique workspace IDs from boards
282
+ workspace_ids = set()
283
+ for board in _paginate(self, BOARDS_QUERY, "boards", MAX_PAGE_SIZE):
284
+ workspace_id = board.get("workspace_id")
285
+ if workspace_id:
286
+ workspace_ids.add(str(workspace_id))
287
+
288
+ if not workspace_ids:
289
+ return
290
+
291
+ # Fetch workspace details
292
+ variables = {"ids": list(workspace_ids)}
293
+ data = self._execute_query(WORKSPACES_QUERY, variables)
294
+ workspaces = data.get("workspaces", [])
295
+
296
+ for workspace in workspaces:
297
+ yield normalize_dict(workspace)
298
+
299
+ def get_webhooks(self) -> Iterator[Dict[str, Any]]:
300
+ """
301
+ Fetch webhooks from Monday.com API.
302
+ First gets all board IDs, then fetches webhooks for each board.
303
+
304
+ Yields:
305
+ Dict containing webhook data
306
+ """
307
+ board_ids = _get_all_board_ids(self)
308
+
309
+ for board_id in board_ids:
310
+ variables = {"board_id": board_id}
311
+ data = self._execute_query(WEBHOOKS_QUERY, variables)
312
+ webhooks = data.get("webhooks", [])
313
+
314
+ for webhook in webhooks:
315
+ yield normalize_dict(webhook)
316
+
317
+ def get_updates(
318
+ self,
319
+ limit: int = MAX_PAGE_SIZE,
320
+ start_date: Optional[str] = None,
321
+ end_date: Optional[str] = None,
322
+ ) -> Iterator[Dict[str, Any]]:
323
+ """
324
+ Fetch updates from Monday.com API.
325
+
326
+ Args:
327
+ limit: Number of results (max 100)
328
+ start_date: Start date in YYYY-MM-DD format
329
+ end_date: End date in YYYY-MM-DD format
330
+
331
+ Yields:
332
+ Dict containing update data
333
+ """
334
+ variables: Dict[str, Any] = {"limit": min(limit, MAX_PAGE_SIZE)}
335
+
336
+ if start_date:
337
+ variables["from_date"] = start_date
338
+ if end_date:
339
+ variables["to_date"] = end_date
340
+
341
+ data = self._execute_query(UPDATES_QUERY, variables)
342
+ updates = data.get("updates", [])
343
+
344
+ for update in updates:
345
+ yield normalize_dict(update)
346
+
347
+ def get_teams(self) -> Iterator[Dict[str, Any]]:
348
+ """
349
+ Fetch teams from Monday.com API.
350
+
351
+ Yields:
352
+ Dict containing team data
353
+ """
354
+ yield from _fetch_simple_list(self, TEAMS_QUERY, "teams")
355
+
356
+ def get_tags(self) -> Iterator[Dict[str, Any]]:
357
+ """
358
+ Fetch tags from Monday.com API.
359
+
360
+ Yields:
361
+ Dict containing tag data
362
+ """
363
+ yield from _fetch_simple_list(self, TAGS_QUERY, "tags")
364
+
365
+ def get_custom_activities(self) -> Iterator[Dict[str, Any]]:
366
+ """
367
+ Fetch custom activities from Monday.com API.
368
+
369
+ Yields:
370
+ Dict containing custom activity data
371
+ """
372
+ yield from _fetch_simple_list(self, CUSTOM_ACTIVITIES_QUERY, "custom_activity")
373
+
374
+ def get_board_columns(self) -> Iterator[Dict[str, Any]]:
375
+ """
376
+ Fetch board columns from Monday.com API.
377
+ First gets all board IDs, then fetches columns for each board.
378
+
379
+ Yields:
380
+ Dict containing board column data with board_id
381
+ """
382
+ yield from _fetch_nested_board_data(self, BOARD_COLUMNS_QUERY, "columns")
383
+
384
+ def get_board_views(self) -> Iterator[Dict[str, Any]]:
385
+ """
386
+ Fetch board views from Monday.com API.
387
+ First gets all board IDs, then fetches views for each board.
388
+
389
+ Yields:
390
+ Dict containing board view data with board_id
391
+ """
392
+ yield from _fetch_nested_board_data(self, BOARD_VIEWS_QUERY, "views")