universal-mcp-applications 0.1.30rc2__py3-none-any.whl → 0.1.36rc2__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 (105) hide show
  1. universal_mcp/applications/ahrefs/app.py +52 -198
  2. universal_mcp/applications/airtable/app.py +23 -122
  3. universal_mcp/applications/apollo/app.py +111 -464
  4. universal_mcp/applications/asana/app.py +417 -1567
  5. universal_mcp/applications/aws_s3/app.py +36 -103
  6. universal_mcp/applications/bill/app.py +546 -1957
  7. universal_mcp/applications/box/app.py +1068 -3981
  8. universal_mcp/applications/braze/app.py +364 -1430
  9. universal_mcp/applications/browser_use/app.py +2 -8
  10. universal_mcp/applications/cal_com_v2/app.py +207 -625
  11. universal_mcp/applications/calendly/app.py +61 -200
  12. universal_mcp/applications/canva/app.py +45 -110
  13. universal_mcp/applications/clickup/app.py +207 -674
  14. universal_mcp/applications/coda/app.py +146 -426
  15. universal_mcp/applications/confluence/app.py +310 -1098
  16. universal_mcp/applications/contentful/app.py +36 -151
  17. universal_mcp/applications/crustdata/app.py +28 -107
  18. universal_mcp/applications/dialpad/app.py +283 -756
  19. universal_mcp/applications/digitalocean/app.py +1766 -5777
  20. universal_mcp/applications/domain_checker/app.py +3 -54
  21. universal_mcp/applications/e2b/app.py +14 -64
  22. universal_mcp/applications/elevenlabs/app.py +9 -47
  23. universal_mcp/applications/exa/app.py +6 -17
  24. universal_mcp/applications/falai/app.py +24 -101
  25. universal_mcp/applications/figma/app.py +53 -137
  26. universal_mcp/applications/file_system/app.py +2 -13
  27. universal_mcp/applications/firecrawl/app.py +51 -152
  28. universal_mcp/applications/fireflies/app.py +59 -281
  29. universal_mcp/applications/fpl/app.py +91 -528
  30. universal_mcp/applications/fpl/utils/fixtures.py +15 -49
  31. universal_mcp/applications/fpl/utils/helper.py +25 -89
  32. universal_mcp/applications/fpl/utils/league_utils.py +20 -64
  33. universal_mcp/applications/ghost_content/app.py +52 -161
  34. universal_mcp/applications/github/app.py +19 -56
  35. universal_mcp/applications/gong/app.py +88 -248
  36. universal_mcp/applications/google_calendar/app.py +16 -68
  37. universal_mcp/applications/google_docs/app.py +88 -188
  38. universal_mcp/applications/google_drive/app.py +141 -463
  39. universal_mcp/applications/google_gemini/app.py +12 -64
  40. universal_mcp/applications/google_mail/app.py +28 -157
  41. universal_mcp/applications/google_searchconsole/app.py +15 -48
  42. universal_mcp/applications/google_sheet/app.py +103 -580
  43. universal_mcp/applications/google_sheet/helper.py +10 -37
  44. universal_mcp/applications/hashnode/app.py +57 -269
  45. universal_mcp/applications/heygen/app.py +44 -122
  46. universal_mcp/applications/http_tools/app.py +10 -32
  47. universal_mcp/applications/hubspot/api_segments/crm_api.py +460 -1573
  48. universal_mcp/applications/hubspot/api_segments/marketing_api.py +74 -262
  49. universal_mcp/applications/hubspot/app.py +23 -87
  50. universal_mcp/applications/jira/app.py +2071 -7986
  51. universal_mcp/applications/klaviyo/app.py +494 -1376
  52. universal_mcp/applications/linkedin/README.md +9 -2
  53. universal_mcp/applications/linkedin/app.py +392 -212
  54. universal_mcp/applications/mailchimp/app.py +450 -1605
  55. universal_mcp/applications/markitdown/app.py +8 -20
  56. universal_mcp/applications/miro/app.py +217 -699
  57. universal_mcp/applications/ms_teams/app.py +64 -186
  58. universal_mcp/applications/neon/app.py +86 -192
  59. universal_mcp/applications/notion/app.py +21 -36
  60. universal_mcp/applications/onedrive/app.py +16 -38
  61. universal_mcp/applications/openai/app.py +42 -165
  62. universal_mcp/applications/outlook/app.py +24 -84
  63. universal_mcp/applications/perplexity/app.py +4 -19
  64. universal_mcp/applications/pipedrive/app.py +832 -3142
  65. universal_mcp/applications/posthog/app.py +163 -432
  66. universal_mcp/applications/reddit/app.py +40 -139
  67. universal_mcp/applications/resend/app.py +41 -107
  68. universal_mcp/applications/retell/app.py +14 -41
  69. universal_mcp/applications/rocketlane/app.py +221 -934
  70. universal_mcp/applications/scraper/README.md +7 -4
  71. universal_mcp/applications/scraper/app.py +216 -102
  72. universal_mcp/applications/semanticscholar/app.py +22 -64
  73. universal_mcp/applications/semrush/app.py +43 -77
  74. universal_mcp/applications/sendgrid/app.py +512 -1262
  75. universal_mcp/applications/sentry/app.py +271 -906
  76. universal_mcp/applications/serpapi/app.py +40 -143
  77. universal_mcp/applications/sharepoint/app.py +17 -39
  78. universal_mcp/applications/shopify/app.py +1551 -4287
  79. universal_mcp/applications/shortcut/app.py +155 -417
  80. universal_mcp/applications/slack/app.py +50 -101
  81. universal_mcp/applications/spotify/app.py +126 -325
  82. universal_mcp/applications/supabase/app.py +104 -213
  83. universal_mcp/applications/tavily/app.py +1 -1
  84. universal_mcp/applications/trello/app.py +693 -2656
  85. universal_mcp/applications/twilio/app.py +14 -50
  86. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  87. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  88. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  89. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  90. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  91. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  92. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  93. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  94. universal_mcp/applications/whatsapp/app.py +35 -186
  95. universal_mcp/applications/whatsapp/audio.py +2 -6
  96. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  97. universal_mcp/applications/whatsapp_business/app.py +70 -283
  98. universal_mcp/applications/wrike/app.py +45 -118
  99. universal_mcp/applications/yahoo_finance/app.py +19 -65
  100. universal_mcp/applications/youtube/app.py +75 -261
  101. universal_mcp/applications/zenquotes/app.py +2 -2
  102. {universal_mcp_applications-0.1.30rc2.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/METADATA +2 -2
  103. {universal_mcp_applications-0.1.30rc2.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/RECORD +105 -105
  104. {universal_mcp_applications-0.1.30rc2.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/WHEEL +0 -0
  105. {universal_mcp_applications-0.1.30rc2.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,5 @@
1
1
  from collections.abc import Callable
2
2
  from typing import Any
3
-
4
3
  from loguru import logger
5
4
  from universal_mcp.applications.application import GraphQLApplication
6
5
  from universal_mcp.exceptions import NotAuthorizedError
@@ -8,24 +7,14 @@ from universal_mcp.integrations import Integration
8
7
 
9
8
 
10
9
  class ContentfulApp(GraphQLApplication):
11
- def __init__(
12
- self,
13
- integration: Integration | None = None,
14
- **kwargs: Any,
15
- ) -> None:
10
+ def __init__(self, integration: Integration | None = None, **kwargs: Any) -> None:
16
11
  self.space_id: str | None = None
17
12
  self.environment_id: str = "master"
18
13
  self._access_token: str | None = None
19
14
  self._is_eu_customer: bool = False
20
15
  self._credentials_loaded: bool = False
21
16
  default_base_url = "https://graphql.contentful.com"
22
-
23
- super().__init__(
24
- name="contentful",
25
- base_url=default_base_url,
26
- integration=integration,
27
- **kwargs,
28
- )
17
+ super().__init__(name="contentful", base_url=default_base_url, integration=integration, **kwargs)
29
18
 
30
19
  def _load_credentials_and_construct_url(self) -> bool:
31
20
  """
@@ -37,70 +26,39 @@ class ContentfulApp(GraphQLApplication):
37
26
  """
38
27
  if self._credentials_loaded:
39
28
  return True
40
-
41
29
  logger.debug("Attempting to load Contentful credentials and construct URL...")
42
-
43
30
  if not self.integration:
44
- logger.error(
45
- "Contentful integration not configured. Cannot load credentials or construct URL."
46
- )
47
- # Mark as 'loaded' to prevent retries, even though it failed.
31
+ logger.error("Contentful integration not configured. Cannot load credentials or construct URL.")
48
32
  self._credentials_loaded = True
49
33
  return False
50
-
51
34
  try:
52
35
  credentials = self.integration.get_credentials()
53
36
  except NotAuthorizedError as e:
54
- logger.error(
55
- f"Authorization required or credentials unavailable for Contentful: {e.message}"
56
- )
57
- self._credentials_loaded = True # Prevent retries
37
+ logger.error(f"Authorization required or credentials unavailable for Contentful: {e.message}")
38
+ self._credentials_loaded = True
58
39
  return False
59
40
  except Exception as e:
60
- logger.error(
61
- f"Failed to get credentials from integration: {e}", exc_info=True
62
- )
63
- self._credentials_loaded = True # Prevent retries
41
+ logger.error(f"Failed to get credentials from integration: {e}", exc_info=True)
42
+ self._credentials_loaded = True
64
43
  return False
65
-
66
44
  self.space_id = credentials.get("space_id")
67
- self._access_token = credentials.get("access_token") or credentials.get(
68
- "api_key"
69
- )
70
- self.environment_id = credentials.get(
71
- "environment_id", "master"
72
- ) # Use default if not specified
73
- self._is_eu_customer = credentials.get(
74
- "is_eu_customer", False
75
- ) # Use default if not specified
76
-
45
+ self._access_token = credentials.get("access_token") or credentials.get("api_key")
46
+ self.environment_id = credentials.get("environment_id", "master")
47
+ self._is_eu_customer = credentials.get("is_eu_customer", False)
77
48
  missing_creds = []
78
49
  if not self.space_id:
79
50
  missing_creds.append("'space_id'")
80
51
  if not self._access_token:
81
52
  missing_creds.append("'access_token' or 'api_key'")
82
-
83
53
  if missing_creds:
84
- logger.error(
85
- f"Missing required Contentful credentials in integration: {', '.join(missing_creds)}. "
86
- "API calls will fail."
87
- )
88
- self._credentials_loaded = True # Prevent retries
54
+ logger.error(f"Missing required Contentful credentials in integration: {', '.join(missing_creds)}. API calls will fail.")
55
+ self._credentials_loaded = True
89
56
  return False
90
-
91
- contentful_api_domain = (
92
- "graphql.eu.contentful.com"
93
- if self._is_eu_customer
94
- else "graphql.contentful.com"
95
- )
57
+ contentful_api_domain = "graphql.eu.contentful.com" if self._is_eu_customer else "graphql.contentful.com"
96
58
  self.base_url = f"https://{contentful_api_domain}/content/v1/spaces/{self.space_id}/environments/{self.environment_id}"
97
-
98
59
  self._client = None
99
-
100
60
  logger.info(
101
- f"Contentful credentials loaded and URL constructed successfully. "
102
- f"Space: '{self.space_id}', Environment: '{self.environment_id}'. "
103
- f"Base URL: {self.base_url}"
61
+ f"Contentful credentials loaded and URL constructed successfully. Space: '{self.space_id}', Environment: '{self.environment_id}'. Base URL: {self.base_url}"
104
62
  )
105
63
  self._credentials_loaded = True
106
64
  return True
@@ -114,7 +72,7 @@ class ContentfulApp(GraphQLApplication):
114
72
  return ""
115
73
  if len(parts) == 1 and parts[0] == s and s:
116
74
  return s[0].lower() + s[1:] if len(s) > 1 else s.lower()
117
- return parts[0].lower() + "".join(word.capitalize() for word in parts[1:])
75
+ return parts[0].lower() + "".join((word.capitalize() for word in parts[1:]))
118
76
 
119
77
  @staticmethod
120
78
  def _to_pascal_case(s: str) -> str:
@@ -125,57 +83,36 @@ class ContentfulApp(GraphQLApplication):
125
83
  return ""
126
84
  if len(parts) == 1 and parts[0] == s and s:
127
85
  return s[0].upper() + s[1:] if len(s) > 1 else s.upper()
128
- return "".join(word.capitalize() for word in parts)
86
+ return "".join((word.capitalize() for word in parts))
129
87
 
130
88
  def _ensure_loaded(self) -> bool:
131
89
  """Internal helper to trigger lazy loading and check status."""
132
90
  if not self._credentials_loaded:
133
91
  return self._load_credentials_and_construct_url()
134
- # If already marked as loaded, check if essential parts are actually set (robustness check)
135
92
  return bool(self.base_url and self.space_id and self._access_token)
136
93
 
137
- # --- Tool Methods ---
138
-
139
- def get_entry(
140
- self,
141
- content_type_id: str,
142
- entry_id: str,
143
- fields_to_select: str,
144
- locale: str | None = None,
145
- preview: bool = False,
94
+ async def get_entry(
95
+ self, content_type_id: str, entry_id: str, fields_to_select: str, locale: str | None = None, preview: bool = False
146
96
  ) -> dict[str, Any]:
147
97
  """
148
98
  Fetches a single entry of a specified content type by its ID.
149
99
  (See original docstring for details)
150
100
  """
151
101
  if not self._ensure_loaded():
152
- return {
153
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
154
- }
155
-
102
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
156
103
  query_field = self._to_camel_case(content_type_id)
157
- logger.debug(
158
- f"Fetching entry for content_type_id='{content_type_id}' (query field='{query_field}'), entry_id='{entry_id}'"
159
- )
160
- query_gql = f"""
161
- query GetEntryById($id: String!, $locale: String, $preview: Boolean) {{
162
- {query_field}(id: $id, locale: $locale, preview: $preview) {{
163
- {fields_to_select}
164
- }}
165
- }}
166
- """
104
+ logger.debug(f"Fetching entry for content_type_id='{content_type_id}' (query field='{query_field}'), entry_id='{entry_id}'")
105
+ query_gql = f"\n query GetEntryById($id: String!, $locale: String, $preview: Boolean) {{\n {query_field}(id: $id, locale: $locale, preview: $preview) {{\n {fields_to_select}\n }}\n }}\n "
167
106
  variables: dict[str, Any] = {"id": entry_id, "preview": preview}
168
107
  if locale:
169
108
  variables["locale"] = locale
170
-
171
- # Call the base class query method which uses the configured client
172
109
  try:
173
110
  return self.query(query_gql, variables=variables)
174
111
  except Exception as e:
175
112
  logger.error(f"Error executing get_entry query: {e}", exc_info=True)
176
113
  return {"error": f"Failed to get entry: {e}"}
177
114
 
178
- def get_entries_collection(
115
+ async def get_entries_collection(
179
116
  self,
180
117
  content_type_id: str,
181
118
  fields_to_select_for_item: str,
@@ -191,28 +128,14 @@ class ContentfulApp(GraphQLApplication):
191
128
  (See original docstring for details)
192
129
  """
193
130
  if not self._ensure_loaded():
194
- return {
195
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
196
- }
197
-
131
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
198
132
  collection_field = self._to_camel_case(content_type_id) + "Collection"
199
133
  filter_type = self._to_pascal_case(content_type_id) + "Filter"
200
134
  order_enum_type = self._to_pascal_case(content_type_id) + "Order"
201
135
  logger.debug(
202
- f"Fetching collection for content_type_id='{content_type_id}' "
203
- f"(collection field='{collection_field}', filter type='{filter_type}', order enum='{order_enum_type}')"
136
+ f"Fetching collection for content_type_id='{content_type_id}' (collection field='{collection_field}', filter type='{filter_type}', order enum='{order_enum_type}')"
204
137
  )
205
- query_gql = f"""
206
- query GetEntries(
207
- $limit: Int, $skip: Int, $where: {filter_type}, $order: [{order_enum_type}!], $locale: String, $preview: Boolean
208
- ) {{
209
- {collection_field}(
210
- limit: $limit, skip: $skip, where: $where, order: $order, locale: $locale, preview: $preview
211
- ) {{
212
- total skip limit items {{ {fields_to_select_for_item} }}
213
- }}
214
- }}
215
- """
138
+ query_gql = f"\n query GetEntries(\n $limit: Int, $skip: Int, $where: {filter_type}, $order: [{order_enum_type}!], $locale: String, $preview: Boolean\n ) {{\n {collection_field}(\n limit: $limit, skip: $skip, where: $where, order: $order, locale: $locale, preview: $preview\n ) {{\n total skip limit items {{ {fields_to_select_for_item} }}\n }}\n }}\n "
216
139
  variables: dict[str, Any] = {"preview": preview}
217
140
  if limit is not None:
218
141
  variables["limit"] = limit
@@ -224,16 +147,13 @@ class ContentfulApp(GraphQLApplication):
224
147
  variables["order"] = order
225
148
  if locale:
226
149
  variables["locale"] = locale
227
-
228
150
  try:
229
151
  return self.query(query_gql, variables=variables)
230
152
  except Exception as e:
231
- logger.error(
232
- f"Error executing get_entries_collection query: {e}", exc_info=True
233
- )
153
+ logger.error(f"Error executing get_entries_collection query: {e}", exc_info=True)
234
154
  return {"error": f"Failed to get entries collection: {e}"}
235
155
 
236
- def get_asset(
156
+ async def get_asset(
237
157
  self,
238
158
  asset_id: str,
239
159
  fields_to_select: str = "sys { id } url title description fileName contentType size width height",
@@ -244,25 +164,17 @@ class ContentfulApp(GraphQLApplication):
244
164
  (See original docstring for details)
245
165
  """
246
166
  if not self._ensure_loaded():
247
- return {
248
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
249
- }
250
-
167
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
251
168
  logger.debug(f"Fetching asset_id='{asset_id}'")
252
- query_gql = f"""
253
- query GetAssetById($id: String!, $preview: Boolean) {{
254
- asset(id: $id, preview: $preview) {{ {fields_to_select} }}
255
- }}
256
- """
169
+ query_gql = f"\n query GetAssetById($id: String!, $preview: Boolean) {{\n asset(id: $id, preview: $preview) {{ {fields_to_select} }}\n }}\n "
257
170
  variables: dict[str, Any] = {"id": asset_id, "preview": preview}
258
-
259
171
  try:
260
172
  return self.query(query_gql, variables=variables)
261
173
  except Exception as e:
262
174
  logger.error(f"Error executing get_asset query: {e}", exc_info=True)
263
175
  return {"error": f"Failed to get asset: {e}"}
264
176
 
265
- def get_assets_collection(
177
+ async def get_assets_collection(
266
178
  self,
267
179
  fields_to_select_for_item: str = "sys { id } url title description fileName contentType size width height",
268
180
  limit: int | None = None,
@@ -277,22 +189,9 @@ class ContentfulApp(GraphQLApplication):
277
189
  (See original docstring for details)
278
190
  """
279
191
  if not self._ensure_loaded():
280
- return {
281
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
282
- }
283
-
192
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
284
193
  logger.debug("Fetching assets collection")
285
- query_gql = f"""
286
- query GetAssets(
287
- $limit: Int, $skip: Int, $where: AssetFilter, $order: [AssetOrder!], $locale: String, $preview: Boolean
288
- ) {{
289
- assetCollection(
290
- limit: $limit, skip: $skip, where: $where, order: $order, locale: $locale, preview: $preview
291
- ) {{
292
- total skip limit items {{ {fields_to_select_for_item} }}
293
- }}
294
- }}
295
- """
194
+ query_gql = f"\n query GetAssets(\n $limit: Int, $skip: Int, $where: AssetFilter, $order: [AssetOrder!], $locale: String, $preview: Boolean\n ) {{\n assetCollection(\n limit: $limit, skip: $skip, where: $where, order: $order, locale: $locale, preview: $preview\n ) {{\n total skip limit items {{ {fields_to_select_for_item} }}\n }}\n }}\n "
296
195
  variables: dict[str, Any] = {"preview": preview}
297
196
  if limit is not None:
298
197
  variables["limit"] = limit
@@ -304,18 +203,13 @@ class ContentfulApp(GraphQLApplication):
304
203
  variables["order"] = order
305
204
  if locale:
306
205
  variables["locale"] = locale
307
-
308
206
  try:
309
207
  return self.query(query_gql, variables=variables)
310
208
  except Exception as e:
311
- logger.error(
312
- f"Error executing get_assets_collection query: {e}", exc_info=True
313
- )
209
+ logger.error(f"Error executing get_assets_collection query: {e}", exc_info=True)
314
210
  return {"error": f"Failed to get assets collection: {e}"}
315
211
 
316
- def execute_graphql_query(
317
- self, query_string: str, variables: dict[str, Any] | None = None
318
- ) -> dict[str, Any]:
212
+ async def execute_graphql_query(self, query_string: str, variables: dict[str, Any] | None = None) -> dict[str, Any]:
319
213
  """
320
214
  Executes an arbitrary GraphQL query against the configured Contentful space/environment.
321
215
 
@@ -330,10 +224,7 @@ class ContentfulApp(GraphQLApplication):
330
224
  important
331
225
  """
332
226
  if not self._ensure_loaded():
333
- return {
334
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
335
- }
336
-
227
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
337
228
  logger.debug(f"Executing custom GraphQL query with variables: {variables}")
338
229
  try:
339
230
  return self.query(query_string, variables=variables)
@@ -343,10 +234,4 @@ class ContentfulApp(GraphQLApplication):
343
234
 
344
235
  def list_tools(self) -> list[Callable]:
345
236
  """Returns a list of methods exposed as tools."""
346
- return [
347
- self.get_entry,
348
- self.get_entries_collection,
349
- self.get_asset,
350
- self.get_assets_collection,
351
- self.execute_graphql_query,
352
- ]
237
+ return [self.get_entry, self.get_entries_collection, self.get_asset, self.get_assets_collection, self.execute_graphql_query]
@@ -1,5 +1,4 @@
1
1
  from typing import Any
2
-
3
2
  from universal_mcp.applications.application import APIApplication
4
3
  from universal_mcp.integrations import Integration
5
4
 
@@ -11,15 +10,9 @@ class CrustdataApp(APIApplication):
11
10
 
12
11
  def _get_headers(self) -> dict[str, Any]:
13
12
  api_key = self.integration.get_credentials().get("api_key")
14
- return {
15
- "Authorization": f"Token {api_key}",
16
- "Content-Type": "application/json",
17
- "Accept": "application/json",
18
- }
13
+ return {"Authorization": f"Token {api_key}", "Content-Type": "application/json", "Accept": "application/json"}
19
14
 
20
- def screen_companies(
21
- self, metrics, filters, offset, count, sorts
22
- ) -> dict[str, Any]:
15
+ async def screen_companies(self, metrics, filters, offset, count, sorts) -> dict[str, Any]:
23
16
  """
24
17
  Screens companies based on specified metrics, filters, sorting, and pagination parameters, and returns the result as a JSON-compatible dictionary.
25
18
 
@@ -50,13 +43,7 @@ class CrustdataApp(APIApplication):
50
43
  raise ValueError("Missing required parameter 'count'")
51
44
  if sorts is None:
52
45
  raise ValueError("Missing required parameter 'sorts'")
53
- request_body = {
54
- "metrics": metrics,
55
- "filters": filters,
56
- "offset": offset,
57
- "count": count,
58
- "sorts": sorts,
59
- }
46
+ request_body = {"metrics": metrics, "filters": filters, "offset": offset, "count": count, "sorts": sorts}
60
47
  request_body = {k: v for k, v in request_body.items() if v is not None}
61
48
  url = f"{self.base_url}/screener/screen/"
62
49
  query_params = {}
@@ -64,7 +51,7 @@ class CrustdataApp(APIApplication):
64
51
  response.raise_for_status()
65
52
  return response.json()
66
53
 
67
- def get_headcount_timeseries(self, filters, offset, count, sorts) -> dict[str, Any]:
54
+ async def get_headcount_timeseries(self, filters, offset, count, sorts) -> dict[str, Any]:
68
55
  """
69
56
  Retrieve headcount timeseries data from the data lab endpoint using the provided filters, pagination, and sorting options.
70
57
 
@@ -92,12 +79,7 @@ class CrustdataApp(APIApplication):
92
79
  raise ValueError("Missing required parameter 'count'")
93
80
  if sorts is None:
94
81
  raise ValueError("Missing required parameter 'sorts'")
95
- request_body = {
96
- "filters": filters,
97
- "offset": offset,
98
- "count": count,
99
- "sorts": sorts,
100
- }
82
+ request_body = {"filters": filters, "offset": offset, "count": count, "sorts": sorts}
101
83
  request_body = {k: v for k, v in request_body.items() if v is not None}
102
84
  url = f"{self.base_url}/data_lab/headcount_timeseries/"
103
85
  query_params = {}
@@ -105,9 +87,7 @@ class CrustdataApp(APIApplication):
105
87
  response.raise_for_status()
106
88
  return response.json()
107
89
 
108
- def get_headcount_by_facet_timeseries(
109
- self, filters, offset, count, sorts
110
- ) -> dict[str, Any]:
90
+ async def get_headcount_by_facet_timeseries(self, filters, offset, count, sorts) -> dict[str, Any]:
111
91
  """
112
92
  Retrieves headcount timeseries data aggregated by specified facets using provided filters and sorting options.
113
93
 
@@ -135,12 +115,7 @@ class CrustdataApp(APIApplication):
135
115
  raise ValueError("Missing required parameter 'count'")
136
116
  if sorts is None:
137
117
  raise ValueError("Missing required parameter 'sorts'")
138
- request_body = {
139
- "filters": filters,
140
- "offset": offset,
141
- "count": count,
142
- "sorts": sorts,
143
- }
118
+ request_body = {"filters": filters, "offset": offset, "count": count, "sorts": sorts}
144
119
  request_body = {k: v for k, v in request_body.items() if v is not None}
145
120
  url = f"{self.base_url}/data_lab/headcount_by_facet_timeseries/"
146
121
  query_params = {}
@@ -148,9 +123,7 @@ class CrustdataApp(APIApplication):
148
123
  response.raise_for_status()
149
124
  return response.json()
150
125
 
151
- def get_funding_milestone_timeseries(
152
- self, filters, offset, count, sorts
153
- ) -> dict[str, Any]:
126
+ async def get_funding_milestone_timeseries(self, filters, offset, count, sorts) -> dict[str, Any]:
154
127
  """
155
128
  Retrieves a time series of funding milestone data based on specified filters, pagination, and sorting options.
156
129
 
@@ -178,12 +151,7 @@ class CrustdataApp(APIApplication):
178
151
  raise ValueError("Missing required parameter 'count'")
179
152
  if sorts is None:
180
153
  raise ValueError("Missing required parameter 'sorts'")
181
- request_body = {
182
- "filters": filters,
183
- "offset": offset,
184
- "count": count,
185
- "sorts": sorts,
186
- }
154
+ request_body = {"filters": filters, "offset": offset, "count": count, "sorts": sorts}
187
155
  request_body = {k: v for k, v in request_body.items() if v is not None}
188
156
  url = f"{self.base_url}/data_lab/funding_milestone_timeseries/"
189
157
  query_params = {}
@@ -191,9 +159,7 @@ class CrustdataApp(APIApplication):
191
159
  response.raise_for_status()
192
160
  return response.json()
193
161
 
194
- def get_decision_makers(
195
- self, filters, offset, count, sorts, decision_maker_titles
196
- ) -> dict[str, Any]:
162
+ async def get_decision_makers(self, filters, offset, count, sorts, decision_maker_titles) -> dict[str, Any]:
197
163
  """
198
164
  Retrieves decision makers based on specified filters and parameters.
199
165
 
@@ -238,7 +204,7 @@ class CrustdataApp(APIApplication):
238
204
  response.raise_for_status()
239
205
  return response.json()
240
206
 
241
- def get_web_traffic(self, filters, offset, count, sorts) -> dict[str, Any]:
207
+ async def get_web_traffic(self, filters, offset, count, sorts) -> dict[str, Any]:
242
208
  """
243
209
  Retrieves web traffic data based on provided filters, pagination, and sorting criteria.
244
210
 
@@ -266,12 +232,7 @@ class CrustdataApp(APIApplication):
266
232
  raise ValueError("Missing required parameter 'count'")
267
233
  if sorts is None:
268
234
  raise ValueError("Missing required parameter 'sorts'")
269
- request_body = {
270
- "filters": filters,
271
- "offset": offset,
272
- "count": count,
273
- "sorts": sorts,
274
- }
235
+ request_body = {"filters": filters, "offset": offset, "count": count, "sorts": sorts}
275
236
  request_body = {k: v for k, v in request_body.items() if v is not None}
276
237
  url = f"{self.base_url}/data_lab/webtraffic/"
277
238
  query_params = {}
@@ -279,7 +240,7 @@ class CrustdataApp(APIApplication):
279
240
  response.raise_for_status()
280
241
  return response.json()
281
242
 
282
- def get_investor_portfolio(self, investor_name) -> dict[str, Any]:
243
+ async def get_investor_portfolio(self, investor_name) -> dict[str, Any]:
283
244
  """
284
245
  Retrieves the investment portfolio information for a specified investor.
285
246
 
@@ -299,16 +260,12 @@ class CrustdataApp(APIApplication):
299
260
  if investor_name is None:
300
261
  raise ValueError("Missing required parameter 'investor_name'")
301
262
  url = f"{self.base_url}/data_lab/investor_portfolio"
302
- query_params = {
303
- k: v for k, v in [("investor_name", investor_name)] if v is not None
304
- }
263
+ query_params = {k: v for k, v in [("investor_name", investor_name)] if v is not None}
305
264
  response = self._get(url, params=query_params)
306
265
  response.raise_for_status()
307
266
  return response.json()
308
267
 
309
- def get_job_listings(
310
- self, tickers, dataset, filters, offset, count, sorts
311
- ) -> dict[str, Any]:
268
+ async def get_job_listings(self, tickers, dataset, filters, offset, count, sorts) -> dict[str, Any]:
312
269
  """
313
270
  Retrieves job listings data based on specified parameters.
314
271
 
@@ -342,14 +299,7 @@ class CrustdataApp(APIApplication):
342
299
  raise ValueError("Missing required parameter 'count'")
343
300
  if sorts is None:
344
301
  raise ValueError("Missing required parameter 'sorts'")
345
- request_body = {
346
- "tickers": tickers,
347
- "dataset": dataset,
348
- "filters": filters,
349
- "offset": offset,
350
- "count": count,
351
- "sorts": sorts,
352
- }
302
+ request_body = {"tickers": tickers, "dataset": dataset, "filters": filters, "offset": offset, "count": count, "sorts": sorts}
353
303
  request_body = {k: v for k, v in request_body.items() if v is not None}
354
304
  url = f"{self.base_url}/data_lab/job_listings/Table/"
355
305
  query_params = {}
@@ -357,7 +307,7 @@ class CrustdataApp(APIApplication):
357
307
  response.raise_for_status()
358
308
  return response.json()
359
309
 
360
- def search_persons(self, job_id) -> dict[str, Any]:
310
+ async def search_persons(self, job_id) -> dict[str, Any]:
361
311
  """
362
312
  Submits a search request for persons associated with a given asynchronous job and returns the search results as a dictionary.
363
313
 
@@ -376,9 +326,7 @@ class CrustdataApp(APIApplication):
376
326
  """
377
327
  if job_id is None:
378
328
  raise ValueError("Missing required parameter 'job_id'")
379
- request_body = {
380
- "job_id": job_id,
381
- }
329
+ request_body = {"job_id": job_id}
382
330
  request_body = {k: v for k, v in request_body.items() if v is not None}
383
331
  url = f"{self.base_url}/screener/person/search"
384
332
  query_params = {}
@@ -386,7 +334,7 @@ class CrustdataApp(APIApplication):
386
334
  response.raise_for_status()
387
335
  return response.json()
388
336
 
389
- def search_companies(self, filters, page) -> dict[str, Any]:
337
+ async def search_companies(self, filters, page) -> dict[str, Any]:
390
338
  """
391
339
  Searches for companies using specified filters and pagination parameters.
392
340
 
@@ -408,10 +356,7 @@ class CrustdataApp(APIApplication):
408
356
  raise ValueError("Missing required parameter 'filters'")
409
357
  if page is None:
410
358
  raise ValueError("Missing required parameter 'page'")
411
- request_body = {
412
- "filters": filters,
413
- "page": page,
414
- }
359
+ request_body = {"filters": filters, "page": page}
415
360
  request_body = {k: v for k, v in request_body.items() if v is not None}
416
361
  url = f"{self.base_url}/screener/company/search"
417
362
  query_params = {}
@@ -419,9 +364,7 @@ class CrustdataApp(APIApplication):
419
364
  response.raise_for_status()
420
365
  return response.json()
421
366
 
422
- def enrich_person(
423
- self, linkedin_profile_url, enrich_realtime, fields
424
- ) -> dict[str, Any]:
367
+ async def enrich_person(self, linkedin_profile_url, enrich_realtime, fields) -> dict[str, Any]:
425
368
  """
426
369
  Retrieves enriched person data from LinkedIn profile using the provided profile URL, enrichment mode, and requested fields.
427
370
 
@@ -449,18 +392,14 @@ class CrustdataApp(APIApplication):
449
392
  url = f"{self.base_url}/screener/person/enrich"
450
393
  query_params = {
451
394
  k: v
452
- for k, v in [
453
- ("linkedin_profile_url", linkedin_profile_url),
454
- ("enrich_realtime", enrich_realtime),
455
- ("fields", fields),
456
- ]
395
+ for k, v in [("linkedin_profile_url", linkedin_profile_url), ("enrich_realtime", enrich_realtime), ("fields", fields)]
457
396
  if v is not None
458
397
  }
459
398
  response = self._get(url, params=query_params)
460
399
  response.raise_for_status()
461
400
  return response.json()
462
401
 
463
- def enrich_company(self, company_domain, enrich_realtime) -> dict[str, Any]:
402
+ async def enrich_company(self, company_domain, enrich_realtime) -> dict[str, Any]:
464
403
  """
465
404
  Retrieves enriched company data using the provided company domain and enrichment mode.
466
405
 
@@ -483,19 +422,12 @@ class CrustdataApp(APIApplication):
483
422
  if enrich_realtime is None:
484
423
  raise ValueError("Missing required parameter 'enrich_realtime'")
485
424
  url = f"{self.base_url}/screener/company"
486
- query_params = {
487
- k: v
488
- for k, v in [
489
- ("company_domain", company_domain),
490
- ("enrich_realtime", enrich_realtime),
491
- ]
492
- if v is not None
493
- }
425
+ query_params = {k: v for k, v in [("company_domain", company_domain), ("enrich_realtime", enrich_realtime)] if v is not None}
494
426
  response = self._get(url, params=query_params)
495
427
  response.raise_for_status()
496
428
  return response.json()
497
429
 
498
- def get_linked_in_posts(self, company_linkedin_url) -> dict[str, Any]:
430
+ async def get_linked_in_posts(self, company_linkedin_url) -> dict[str, Any]:
499
431
  """
500
432
  Fetches LinkedIn posts for a specified company using its LinkedIn URL.
501
433
 
@@ -515,18 +447,12 @@ class CrustdataApp(APIApplication):
515
447
  if company_linkedin_url is None:
516
448
  raise ValueError("Missing required parameter 'company_linkedin_url'")
517
449
  url = f"{self.base_url}/screener/linkedin_posts"
518
- query_params = {
519
- k: v
520
- for k, v in [("company_linkedin_url", company_linkedin_url)]
521
- if v is not None
522
- }
450
+ query_params = {k: v for k, v in [("company_linkedin_url", company_linkedin_url)] if v is not None}
523
451
  response = self._get(url, params=query_params)
524
452
  response.raise_for_status()
525
453
  return response.json()
526
454
 
527
- def search_linked_in_posts(
528
- self, keyword, page, sort_by, date_posted
529
- ) -> dict[str, Any]:
455
+ async def search_linked_in_posts(self, keyword, page, sort_by, date_posted) -> dict[str, Any]:
530
456
  """
531
457
  Searches LinkedIn posts using the provided keyword and filters, returning the search results as a dictionary.
532
458
 
@@ -554,12 +480,7 @@ class CrustdataApp(APIApplication):
554
480
  raise ValueError("Missing required parameter 'sort_by'")
555
481
  if date_posted is None:
556
482
  raise ValueError("Missing required parameter 'date_posted'")
557
- request_body = {
558
- "keyword": keyword,
559
- "page": page,
560
- "sort_by": sort_by,
561
- "date_posted": date_posted,
562
- }
483
+ request_body = {"keyword": keyword, "page": page, "sort_by": sort_by, "date_posted": date_posted}
563
484
  request_body = {k: v for k, v in request_body.items() if v is not None}
564
485
  url = f"{self.base_url}/screener/linkedin_posts/keyword_search/"
565
486
  query_params = {}