polydata-client 0.1.1__tar.gz → 0.1.3__tar.gz
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.
- polydata_client-0.1.3/CHANGELOG.md +53 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/PKG-INFO +1 -1
- polydata_client-0.1.3/polydata/__init__.py +10 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/polydata/client.py +89 -7
- {polydata_client-0.1.1 → polydata_client-0.1.3}/polydata/common.py +209 -9
- {polydata_client-0.1.1 → polydata_client-0.1.3}/polydata_client.egg-info/PKG-INFO +1 -1
- {polydata_client-0.1.1 → polydata_client-0.1.3}/pyproject.toml +1 -1
- polydata_client-0.1.1/CHANGELOG.md +0 -31
- polydata_client-0.1.1/polydata/__init__.py +0 -4
- {polydata_client-0.1.1 → polydata_client-0.1.3}/LICENSE +0 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/MANIFEST.in +0 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/NOTICE +0 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/README.md +0 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/polydata_client.egg-info/SOURCES.txt +0 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/polydata_client.egg-info/dependency_links.txt +0 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/polydata_client.egg-info/requires.txt +0 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/polydata_client.egg-info/top_level.txt +0 -0
- {polydata_client-0.1.1 → polydata_client-0.1.3}/setup.cfg +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Polydata Python client will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.3] - 2026-10-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `update_data_view()` adds `update_method`: `"create_or_replace"` (default) keeps views that read from the updated view, while `"drop_and_create"` allows column changes.
|
|
13
|
+
- `create_component()`, `list_components()` and `list_widgets()`.
|
|
14
|
+
- `create_or_update_component()` and `create_or_update_widget()`, so the same script can create components and widgets on a brand-new hub or update them on an existing one.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `create_or_update_view()` replaces `allow_delete_then_create` with `update_method` (`"create_or_replace"` default, or `"drop_and_create"` when a query changes the view's columns). It no longer guesses from a failed update. With `"drop_and_create"`, `dependent_views` are recreated even if the update fails, and the view keeps its history.
|
|
19
|
+
|
|
20
|
+
### Removed
|
|
21
|
+
|
|
22
|
+
- `upload_data_source()` and `upload_large_csv_in_batches()` no longer accept `force_cascade`. A table replace no longer deletes the views that depend on it; if one is in the way, the replace fails with a clear error. To rebuild dependent views, use `create_or_update_view()` with `dependent_views`.
|
|
23
|
+
|
|
24
|
+
## [0.1.2] - 2026-07-23
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `create_or_update_view()` adds `allow_delete_then_create` option, which helps creators who change their column names.
|
|
29
|
+
- `create_or_update_view()` also adds `dependent_views` which are dropped and rebuilt around the recreate.
|
|
30
|
+
|
|
31
|
+
## [0.1.1] - 2026-01-27
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Added utilities to manage and update the cache
|
|
36
|
+
- `client.get_cache_stats()` Gets the Redis cache statistics.
|
|
37
|
+
- `client.clear_hub_cache()` Clears all cached data app pages for a Hub.
|
|
38
|
+
|
|
39
|
+
## [0.1.0] - 2026-01-16
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Initial release of Polydata Python client
|
|
44
|
+
- Support for data source uploads (create, replace, append)
|
|
45
|
+
- Data view management (create, update, get, list)
|
|
46
|
+
- Data app creation and page updates
|
|
47
|
+
- Component and widget management
|
|
48
|
+
- Ticker creation and updates
|
|
49
|
+
- Custom endpoint creation and management
|
|
50
|
+
- Helper utilities for create-or-update patterns
|
|
51
|
+
- Batch upload support for large CSV files
|
|
52
|
+
- Comprehensive error handling
|
|
53
|
+
- API connection testing
|
|
@@ -79,7 +79,6 @@ class PolydataClient:
|
|
|
79
79
|
type: str,
|
|
80
80
|
ignore_superset_validation: bool = False,
|
|
81
81
|
explicitly_delete_view: bool = False,
|
|
82
|
-
force_cascade: bool = False,
|
|
83
82
|
) -> Dict:
|
|
84
83
|
"""
|
|
85
84
|
Upload, replace, or append data to a data source.
|
|
@@ -94,8 +93,6 @@ class PolydataClient:
|
|
|
94
93
|
(only applies to replace operations)
|
|
95
94
|
explicitly_delete_view: If True, delete view before replacing
|
|
96
95
|
(only applies to replace operations)
|
|
97
|
-
force_cascade: If True, use CASCADE when dropping columns
|
|
98
|
-
(only applies to replace operations)
|
|
99
96
|
|
|
100
97
|
Returns:
|
|
101
98
|
Dict: Response from the API
|
|
@@ -130,7 +127,6 @@ class PolydataClient:
|
|
|
130
127
|
"explicitly_delete_view": str(
|
|
131
128
|
explicitly_delete_view
|
|
132
129
|
).lower(),
|
|
133
|
-
"force_cascade": str(force_cascade).lower(),
|
|
134
130
|
}
|
|
135
131
|
)
|
|
136
132
|
|
|
@@ -241,7 +237,11 @@ class PolydataClient:
|
|
|
241
237
|
return True
|
|
242
238
|
|
|
243
239
|
def update_data_view(
|
|
244
|
-
self,
|
|
240
|
+
self,
|
|
241
|
+
hub_schema: str,
|
|
242
|
+
view_name: str,
|
|
243
|
+
view_query: str,
|
|
244
|
+
update_method: str = "create_or_replace",
|
|
245
245
|
) -> bool:
|
|
246
246
|
"""
|
|
247
247
|
Update an existing data view using the API endpoint.
|
|
@@ -250,11 +250,14 @@ class PolydataClient:
|
|
|
250
250
|
hub_schema: The hub schema name
|
|
251
251
|
view_name: Name of the view to update
|
|
252
252
|
view_query: New SQL query for the view
|
|
253
|
+
update_method: "create_or_replace" (default) keeps views that read
|
|
254
|
+
from this one, but can't change its columns. "drop_and_create"
|
|
255
|
+
can change columns, but fails if other views read from this one.
|
|
253
256
|
|
|
254
257
|
Returns:
|
|
255
258
|
bool: True if successful, False otherwise
|
|
256
259
|
"""
|
|
257
|
-
payload = {"view_query": view_query}
|
|
260
|
+
payload = {"view_query": view_query, "update_method": update_method}
|
|
258
261
|
response = self.session.patch(
|
|
259
262
|
f"{self.base_url}/data/views/{hub_schema}/{view_name}",
|
|
260
263
|
json=payload,
|
|
@@ -266,6 +269,30 @@ class PolydataClient:
|
|
|
266
269
|
)
|
|
267
270
|
return True
|
|
268
271
|
|
|
272
|
+
def delete_data_view(self, hub_schema: str, view_name: str) -> bool:
|
|
273
|
+
"""
|
|
274
|
+
Delete an existing data view using the API endpoint.
|
|
275
|
+
|
|
276
|
+
Drops both the physical Postgres view and its hub_views metadata
|
|
277
|
+
record.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
hub_schema: The hub schema name
|
|
281
|
+
view_name: Name of the view to delete
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
bool: True if successful, False otherwise
|
|
285
|
+
"""
|
|
286
|
+
response = self.session.delete(
|
|
287
|
+
f"{self.base_url}/data/views/{hub_schema}/{view_name}",
|
|
288
|
+
)
|
|
289
|
+
self._handle_response(
|
|
290
|
+
response,
|
|
291
|
+
success_message=f"✓ Deleted data view: {view_name}",
|
|
292
|
+
error_context=f"Delete view {view_name}",
|
|
293
|
+
)
|
|
294
|
+
return True
|
|
295
|
+
|
|
269
296
|
def get_data_view(
|
|
270
297
|
self, hub_schema: str, view_name: str, include_data: bool = True
|
|
271
298
|
) -> pd.DataFrame:
|
|
@@ -370,6 +397,48 @@ class PolydataClient:
|
|
|
370
397
|
)
|
|
371
398
|
return True
|
|
372
399
|
|
|
400
|
+
def create_component(
|
|
401
|
+
self, hub_schema: str, component_name: str, component_config: dict
|
|
402
|
+
) -> bool:
|
|
403
|
+
"""
|
|
404
|
+
Create a new component using the API endpoint.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
hub_schema: The hub schema name
|
|
408
|
+
component_name: Name for the component
|
|
409
|
+
component_config: Component configuration dict
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
bool: True if successful, False otherwise
|
|
413
|
+
"""
|
|
414
|
+
# Unlike update_component, the create endpoint takes the config as a
|
|
415
|
+
# dict rather than a JSON string.
|
|
416
|
+
payload = {"component_config": component_config}
|
|
417
|
+
|
|
418
|
+
response = self.session.post(
|
|
419
|
+
f"{self.base_url}/apps/components/{hub_schema}/{component_name}",
|
|
420
|
+
json=payload,
|
|
421
|
+
)
|
|
422
|
+
self._handle_response(
|
|
423
|
+
response,
|
|
424
|
+
success_message=f"✓ Created component: {component_name}",
|
|
425
|
+
error_context=f"Create component {component_name}",
|
|
426
|
+
)
|
|
427
|
+
return True
|
|
428
|
+
|
|
429
|
+
def list_components(self, hub_schema: str) -> pd.DataFrame:
|
|
430
|
+
"""List a hub's components as a DataFrame (one row per component)."""
|
|
431
|
+
response = self.session.get(
|
|
432
|
+
f"{self.base_url}/apps/components/{hub_schema}"
|
|
433
|
+
)
|
|
434
|
+
response_data = self._handle_response(
|
|
435
|
+
response, error_context=f"List components for hub {hub_schema}"
|
|
436
|
+
)
|
|
437
|
+
components_list = response_data.get("data", [])
|
|
438
|
+
df = pd.DataFrame(components_list)
|
|
439
|
+
print(f"✓ Retrieved {len(df)} components for hub: {hub_schema}")
|
|
440
|
+
return df
|
|
441
|
+
|
|
373
442
|
def create_widget(
|
|
374
443
|
self, hub_schema: str, widget_name: str, widget_config: dict
|
|
375
444
|
) -> bool:
|
|
@@ -428,6 +497,19 @@ class PolydataClient:
|
|
|
428
497
|
)
|
|
429
498
|
return True
|
|
430
499
|
|
|
500
|
+
def list_widgets(self, hub_schema: str) -> pd.DataFrame:
|
|
501
|
+
"""List a hub's widgets as a DataFrame (one row per widget)."""
|
|
502
|
+
response = self.session.get(
|
|
503
|
+
f"{self.base_url}/apps/widgets/{hub_schema}"
|
|
504
|
+
)
|
|
505
|
+
response_data = self._handle_response(
|
|
506
|
+
response, error_context=f"List widgets for hub {hub_schema}"
|
|
507
|
+
)
|
|
508
|
+
widgets_list = response_data.get("data", [])
|
|
509
|
+
df = pd.DataFrame(widgets_list)
|
|
510
|
+
print(f"✓ Retrieved {len(df)} widgets for hub: {hub_schema}")
|
|
511
|
+
return df
|
|
512
|
+
|
|
431
513
|
def create_ticker(
|
|
432
514
|
self,
|
|
433
515
|
hub_schema: str,
|
|
@@ -719,4 +801,4 @@ class PolydataClient:
|
|
|
719
801
|
f" - Cleared {deleted} cached pages out of {cached} that were cached ({total} total pages)"
|
|
720
802
|
)
|
|
721
803
|
|
|
722
|
-
return True
|
|
804
|
+
return True
|
|
@@ -30,6 +30,8 @@ def create_or_update_view(
|
|
|
30
30
|
view_name: str,
|
|
31
31
|
view_query: str,
|
|
32
32
|
hub_views_df: pd.DataFrame,
|
|
33
|
+
update_method: str = "create_or_replace",
|
|
34
|
+
dependent_views: dict | None = None,
|
|
33
35
|
):
|
|
34
36
|
"""
|
|
35
37
|
Create or update a data view based on whether it exists.
|
|
@@ -43,6 +45,18 @@ def create_or_update_view(
|
|
|
43
45
|
view_name: View name (without schema prefix)
|
|
44
46
|
view_query: SQL query for the view
|
|
45
47
|
hub_views_df: DataFrame from client.list_data_views(hub_schema=hub_schema)
|
|
48
|
+
update_method: How an existing view is updated.
|
|
49
|
+
- "create_or_replace" (default): keeps views that read from this
|
|
50
|
+
one, but can't rename, reorder or remove columns.
|
|
51
|
+
- "drop_and_create": use when the query changes the view's
|
|
52
|
+
columns. Views that read from this one block the drop, so list
|
|
53
|
+
them in dependent_views.
|
|
54
|
+
dependent_views: Mapping of {view_name: view_query} for views that read
|
|
55
|
+
from this view. Only used with "drop_and_create": they are deleted
|
|
56
|
+
before the update and recreated after it (in reverse order), even
|
|
57
|
+
if the update fails. Every view needs a query (a ValueError is
|
|
58
|
+
raised before anything is deleted otherwise), and each query must
|
|
59
|
+
be valid against the NEW definition of this view.
|
|
46
60
|
|
|
47
61
|
Example:
|
|
48
62
|
```python
|
|
@@ -63,18 +77,65 @@ def create_or_update_view(
|
|
|
63
77
|
"""
|
|
64
78
|
full_view_name = f"{hub_schema}.{view_name}"
|
|
65
79
|
|
|
66
|
-
if hub_view_exists(full_view_name, hub_views_df):
|
|
67
|
-
client.
|
|
80
|
+
if not hub_view_exists(full_view_name, hub_views_df):
|
|
81
|
+
client.create_data_view(
|
|
68
82
|
hub_schema=hub_schema,
|
|
69
83
|
view_name=view_name,
|
|
70
84
|
view_query=view_query,
|
|
71
85
|
)
|
|
72
|
-
|
|
73
|
-
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
valid_update_methods = ["create_or_replace", "drop_and_create"]
|
|
89
|
+
if update_method not in valid_update_methods:
|
|
90
|
+
raise ValueError(
|
|
91
|
+
f"Invalid update_method '{update_method}'. "
|
|
92
|
+
f"Must be one of: {valid_update_methods}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if update_method == "create_or_replace":
|
|
96
|
+
client.update_data_view(
|
|
74
97
|
hub_schema=hub_schema,
|
|
75
98
|
view_name=view_name,
|
|
76
99
|
view_query=view_query,
|
|
77
100
|
)
|
|
101
|
+
else:
|
|
102
|
+
# Views that read from this one block its (non-CASCADE) drop, so they
|
|
103
|
+
# are deleted first and recreated afterwards, even if the update fails.
|
|
104
|
+
dependent_views = dependent_views or {}
|
|
105
|
+
missing_queries = [
|
|
106
|
+
dependent_name
|
|
107
|
+
for dependent_name, dependent_query in dependent_views.items()
|
|
108
|
+
if not dependent_query or not dependent_query.strip()
|
|
109
|
+
]
|
|
110
|
+
if missing_queries:
|
|
111
|
+
raise ValueError(
|
|
112
|
+
f"dependent_views {missing_queries} have no view_query, so "
|
|
113
|
+
"they couldn't be recreated after the update."
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
for dependent_name in dependent_views:
|
|
117
|
+
if hub_view_exists(f"{hub_schema}.{dependent_name}", hub_views_df):
|
|
118
|
+
client.delete_data_view(
|
|
119
|
+
hub_schema=hub_schema,
|
|
120
|
+
view_name=dependent_name,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
client.update_data_view(
|
|
125
|
+
hub_schema=hub_schema,
|
|
126
|
+
view_name=view_name,
|
|
127
|
+
view_query=view_query,
|
|
128
|
+
update_method="drop_and_create",
|
|
129
|
+
)
|
|
130
|
+
finally:
|
|
131
|
+
for dependent_name, dependent_query in reversed(
|
|
132
|
+
list(dependent_views.items())
|
|
133
|
+
):
|
|
134
|
+
client.create_data_view(
|
|
135
|
+
hub_schema=hub_schema,
|
|
136
|
+
view_name=dependent_name,
|
|
137
|
+
view_query=dependent_query,
|
|
138
|
+
)
|
|
78
139
|
|
|
79
140
|
|
|
80
141
|
def ticker_exists(ticker_name: str, hub_tickers_df: pd.DataFrame) -> bool:
|
|
@@ -161,6 +222,150 @@ def create_or_update_ticker(
|
|
|
161
222
|
)
|
|
162
223
|
|
|
163
224
|
|
|
225
|
+
def endpoint_exists(endpoint_name: str, hub_endpoints_df: pd.DataFrame) -> bool:
|
|
226
|
+
"""
|
|
227
|
+
Check if an endpoint exists in the hub.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
endpoint_name: Endpoint name to check
|
|
231
|
+
hub_endpoints_df: DataFrame from client.list_endpoints()
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
True if the endpoint exists, False otherwise
|
|
235
|
+
"""
|
|
236
|
+
if hub_endpoints_df.empty:
|
|
237
|
+
return False
|
|
238
|
+
return endpoint_name in hub_endpoints_df["hub_endpoint_name"].values
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def create_or_update_endpoint(
|
|
242
|
+
client,
|
|
243
|
+
hub_schema: str,
|
|
244
|
+
endpoint_name: str,
|
|
245
|
+
view_name: str,
|
|
246
|
+
params_config: dict,
|
|
247
|
+
hub_views_df: pd.DataFrame,
|
|
248
|
+
hub_endpoints_df: pd.DataFrame,
|
|
249
|
+
subscription_plan_type_name: str = "enterprise",
|
|
250
|
+
):
|
|
251
|
+
"""
|
|
252
|
+
Create or update an endpoint based on whether it exists.
|
|
253
|
+
|
|
254
|
+
Mirrors create_or_update_view: checks hub_endpoints_df and calls either
|
|
255
|
+
client.update_endpoint() or client.create_endpoint() (resolving the backing
|
|
256
|
+
view's id from hub_views_df) accordingly.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
client: PolydataClient instance
|
|
260
|
+
hub_schema: Schema name
|
|
261
|
+
endpoint_name: Human-readable endpoint name
|
|
262
|
+
view_name: Backing view name (without schema prefix)
|
|
263
|
+
params_config: Endpoint params config (allowed_params/allowed_columns)
|
|
264
|
+
hub_views_df: DataFrame from client.list_data_views()
|
|
265
|
+
hub_endpoints_df: DataFrame from client.list_endpoints()
|
|
266
|
+
subscription_plan_type_name: Plan required when creating the endpoint
|
|
267
|
+
(default: "enterprise"); ignored on update
|
|
268
|
+
|
|
269
|
+
Example:
|
|
270
|
+
```python
|
|
271
|
+
from polydata import PolydataClient
|
|
272
|
+
from polydata.common import create_or_update_endpoint
|
|
273
|
+
|
|
274
|
+
client = PolydataClient(api_key="...", base_url="...")
|
|
275
|
+
hub_views_df = client.list_data_views(hub_schema="my_schema")
|
|
276
|
+
hub_endpoints_df = client.list_endpoints(hub_schema="my_schema")
|
|
277
|
+
|
|
278
|
+
create_or_update_endpoint(
|
|
279
|
+
client=client,
|
|
280
|
+
hub_schema="my_schema",
|
|
281
|
+
endpoint_name="Available Leagues",
|
|
282
|
+
view_name="available_leagues",
|
|
283
|
+
params_config={...},
|
|
284
|
+
hub_views_df=hub_views_df,
|
|
285
|
+
hub_endpoints_df=hub_endpoints_df,
|
|
286
|
+
)
|
|
287
|
+
```
|
|
288
|
+
"""
|
|
289
|
+
if endpoint_exists(endpoint_name, hub_endpoints_df):
|
|
290
|
+
client.update_endpoint(
|
|
291
|
+
hub_schema=hub_schema,
|
|
292
|
+
hub_endpoint_name=endpoint_name,
|
|
293
|
+
params_config=params_config,
|
|
294
|
+
)
|
|
295
|
+
else:
|
|
296
|
+
full_view_name = f"{hub_schema}.{view_name}"
|
|
297
|
+
matching = hub_views_df[hub_views_df["view_name"] == full_view_name]
|
|
298
|
+
if matching.empty:
|
|
299
|
+
raise ValueError(f"Could not find view {full_view_name}")
|
|
300
|
+
client.create_endpoint(
|
|
301
|
+
hub_schema=hub_schema,
|
|
302
|
+
hub_view_id=int(matching.iloc[0]["hub_view_id"]),
|
|
303
|
+
hub_endpoint_name=endpoint_name,
|
|
304
|
+
subscription_plan_type_name=subscription_plan_type_name,
|
|
305
|
+
params_config=params_config,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def component_exists(
|
|
310
|
+
component_name: str, hub_components_df: pd.DataFrame
|
|
311
|
+
) -> bool:
|
|
312
|
+
"""Check if a component exists, given client.list_components() output."""
|
|
313
|
+
if hub_components_df.empty:
|
|
314
|
+
return False
|
|
315
|
+
return component_name in hub_components_df["component_name"].values
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def create_or_update_component(
|
|
319
|
+
client,
|
|
320
|
+
hub_schema: str,
|
|
321
|
+
component_name: str,
|
|
322
|
+
component_config: dict,
|
|
323
|
+
hub_components_df: pd.DataFrame,
|
|
324
|
+
):
|
|
325
|
+
"""Create the component, or update it if it already exists."""
|
|
326
|
+
if component_exists(component_name, hub_components_df):
|
|
327
|
+
client.update_component(
|
|
328
|
+
hub_schema=hub_schema,
|
|
329
|
+
component_name=component_name,
|
|
330
|
+
component_config=component_config,
|
|
331
|
+
)
|
|
332
|
+
else:
|
|
333
|
+
client.create_component(
|
|
334
|
+
hub_schema=hub_schema,
|
|
335
|
+
component_name=component_name,
|
|
336
|
+
component_config=component_config,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def widget_exists(widget_name: str, hub_widgets_df: pd.DataFrame) -> bool:
|
|
341
|
+
"""Check if a widget exists, given client.list_widgets() output."""
|
|
342
|
+
if hub_widgets_df.empty:
|
|
343
|
+
return False
|
|
344
|
+
return widget_name in hub_widgets_df["widget_name"].values
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def create_or_update_widget(
|
|
348
|
+
client,
|
|
349
|
+
hub_schema: str,
|
|
350
|
+
widget_name: str,
|
|
351
|
+
widget_config: dict,
|
|
352
|
+
hub_widgets_df: pd.DataFrame,
|
|
353
|
+
):
|
|
354
|
+
"""Create the widget, or update it if it already exists."""
|
|
355
|
+
if widget_exists(widget_name, hub_widgets_df):
|
|
356
|
+
client.update_widget(
|
|
357
|
+
hub_schema=hub_schema,
|
|
358
|
+
widget_name=widget_name,
|
|
359
|
+
widget_config=widget_config,
|
|
360
|
+
)
|
|
361
|
+
else:
|
|
362
|
+
client.create_widget(
|
|
363
|
+
hub_schema=hub_schema,
|
|
364
|
+
widget_name=widget_name,
|
|
365
|
+
widget_config=widget_config,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
|
|
164
369
|
def upload_large_csv_in_batches(
|
|
165
370
|
client,
|
|
166
371
|
file_path: str,
|
|
@@ -171,7 +376,6 @@ def upload_large_csv_in_batches(
|
|
|
171
376
|
delay_between_chunks: int = 5,
|
|
172
377
|
ignore_superset_validation: bool = False,
|
|
173
378
|
explicitly_delete_view: bool = False,
|
|
174
|
-
force_cascade: bool = False,
|
|
175
379
|
) -> Dict:
|
|
176
380
|
"""
|
|
177
381
|
Upload a large CSV file in batches to avoid memory issues.
|
|
@@ -197,8 +401,6 @@ def upload_large_csv_in_batches(
|
|
|
197
401
|
(only applies to first/replace operation)
|
|
198
402
|
explicitly_delete_view: If True, delete view before replacing
|
|
199
403
|
(only applies to first/replace operation)
|
|
200
|
-
force_cascade: If True, use CASCADE when dropping columns
|
|
201
|
-
(only applies to first/replace operation)
|
|
202
404
|
|
|
203
405
|
Returns:
|
|
204
406
|
Dict: Summary of upload results
|
|
@@ -273,7 +475,6 @@ def upload_large_csv_in_batches(
|
|
|
273
475
|
type="replace",
|
|
274
476
|
ignore_superset_validation=ignore_superset_validation,
|
|
275
477
|
explicitly_delete_view=explicitly_delete_view,
|
|
276
|
-
force_cascade=force_cascade,
|
|
277
478
|
)
|
|
278
479
|
else:
|
|
279
480
|
client.upload_data_source(
|
|
@@ -299,7 +500,6 @@ def upload_large_csv_in_batches(
|
|
|
299
500
|
time.sleep(delay_between_chunks)
|
|
300
501
|
|
|
301
502
|
finally:
|
|
302
|
-
# Clean up temporary directory
|
|
303
503
|
try:
|
|
304
504
|
os.rmdir(temp_dir)
|
|
305
505
|
print("\n 🧹 Cleaned up temporary directory")
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to the Polydata Python client will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [0.1.1] - 2026-01-27
|
|
9
|
-
|
|
10
|
-
### Added
|
|
11
|
-
|
|
12
|
-
- Added utilities to manage and update the cache
|
|
13
|
-
- `client.get_cache_stats()` Gets the Redis cache statistics.
|
|
14
|
-
- `client.clear_hub_cache()` Clears all cached data app pages for a Hub.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## [0.1.0] - 2026-01-16
|
|
18
|
-
|
|
19
|
-
### Added
|
|
20
|
-
|
|
21
|
-
- Initial release of Polydata Python client
|
|
22
|
-
- Support for data source uploads (create, replace, append)
|
|
23
|
-
- Data view management (create, update, get, list)
|
|
24
|
-
- Data app creation and page updates
|
|
25
|
-
- Component and widget management
|
|
26
|
-
- Ticker creation and updates
|
|
27
|
-
- Custom endpoint creation and management
|
|
28
|
-
- Helper utilities for create-or-update patterns
|
|
29
|
-
- Batch upload support for large CSV files
|
|
30
|
-
- Comprehensive error handling
|
|
31
|
-
- API connection testing
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polydata_client-0.1.1 → polydata_client-0.1.3}/polydata_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|