dara-core 1.19.1__py3-none-any.whl → 1.20.0__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.
- dara/core/__init__.py +1 -0
- dara/core/auth/basic.py +13 -7
- dara/core/auth/definitions.py +2 -2
- dara/core/auth/utils.py +1 -1
- dara/core/base_definitions.py +7 -42
- dara/core/data_utils.py +16 -17
- dara/core/definitions.py +8 -8
- dara/core/interactivity/__init__.py +4 -0
- dara/core/interactivity/actions.py +20 -22
- dara/core/interactivity/any_data_variable.py +7 -135
- dara/core/interactivity/any_variable.py +1 -1
- dara/core/interactivity/client_variable.py +71 -0
- dara/core/interactivity/data_variable.py +8 -266
- dara/core/interactivity/derived_data_variable.py +6 -290
- dara/core/interactivity/derived_variable.py +335 -201
- dara/core/interactivity/filtering.py +29 -2
- dara/core/interactivity/loop_variable.py +2 -2
- dara/core/interactivity/non_data_variable.py +5 -68
- dara/core/interactivity/plain_variable.py +87 -14
- dara/core/interactivity/server_variable.py +325 -0
- dara/core/interactivity/state_variable.py +2 -2
- dara/core/interactivity/switch_variable.py +15 -15
- dara/core/interactivity/tabular_variable.py +94 -0
- dara/core/interactivity/url_variable.py +10 -90
- dara/core/internal/cache_store/cache_store.py +5 -20
- dara/core/internal/dependency_resolution.py +27 -69
- dara/core/internal/devtools.py +10 -3
- dara/core/internal/execute_action.py +9 -3
- dara/core/internal/multi_resource_lock.py +70 -0
- dara/core/internal/normalization.py +0 -5
- dara/core/internal/pandas_utils.py +105 -3
- dara/core/internal/pool/definitions.py +1 -1
- dara/core/internal/pool/task_pool.py +1 -1
- dara/core/internal/registries.py +3 -2
- dara/core/internal/registry.py +1 -1
- dara/core/internal/registry_lookup.py +5 -3
- dara/core/internal/routing.py +52 -121
- dara/core/internal/store.py +2 -29
- dara/core/internal/tasks.py +372 -182
- dara/core/internal/utils.py +25 -3
- dara/core/internal/websocket.py +1 -1
- dara/core/js_tooling/js_utils.py +2 -0
- dara/core/logging.py +10 -6
- dara/core/persistence.py +26 -4
- dara/core/umd/dara.core.umd.js +751 -1386
- dara/core/visual/dynamic_component.py +10 -13
- {dara_core-1.19.1.dist-info → dara_core-1.20.0.dist-info}/METADATA +10 -10
- {dara_core-1.19.1.dist-info → dara_core-1.20.0.dist-info}/RECORD +51 -47
- {dara_core-1.19.1.dist-info → dara_core-1.20.0.dist-info}/LICENSE +0 -0
- {dara_core-1.19.1.dist-info → dara_core-1.20.0.dist-info}/WHEEL +0 -0
- {dara_core-1.19.1.dist-info → dara_core-1.20.0.dist-info}/entry_points.txt +0 -0
|
@@ -17,47 +17,26 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
import asyncio
|
|
21
20
|
from collections.abc import Coroutine
|
|
22
|
-
from typing import Any, Callable, List, Optional, Union
|
|
23
|
-
from uuid import uuid4
|
|
21
|
+
from typing import Any, Callable, List, Optional, Union
|
|
24
22
|
|
|
25
23
|
from pandas import DataFrame
|
|
26
|
-
from
|
|
27
|
-
from pydantic import ConfigDict, SerializerFunctionWrapHandler, model_serializer
|
|
24
|
+
from typing_extensions import deprecated
|
|
28
25
|
|
|
29
26
|
from dara.core.base_definitions import (
|
|
30
|
-
BaseTask,
|
|
31
27
|
Cache,
|
|
32
28
|
CacheArgType,
|
|
33
|
-
PendingTask,
|
|
34
|
-
PendingValue,
|
|
35
|
-
)
|
|
36
|
-
from dara.core.interactivity.any_data_variable import (
|
|
37
|
-
AnyDataVariable,
|
|
38
|
-
DataFrameSchema,
|
|
39
|
-
DataVariableRegistryEntry,
|
|
40
29
|
)
|
|
41
30
|
from dara.core.interactivity.any_variable import AnyVariable
|
|
42
31
|
from dara.core.interactivity.derived_variable import (
|
|
43
32
|
DerivedVariable,
|
|
44
|
-
DerivedVariableRegistryEntry,
|
|
45
|
-
DerivedVariableResult,
|
|
46
|
-
)
|
|
47
|
-
from dara.core.interactivity.filtering import (
|
|
48
|
-
FilterQuery,
|
|
49
|
-
Pagination,
|
|
50
|
-
apply_filters,
|
|
51
|
-
coerce_to_filter_query,
|
|
52
33
|
)
|
|
53
|
-
from dara.core.internal.cache_store import CacheStore
|
|
54
|
-
from dara.core.internal.hashing import hash_object
|
|
55
|
-
from dara.core.internal.pandas_utils import append_index, df_convert_to_internal
|
|
56
|
-
from dara.core.internal.tasks import MetaTask, Task, TaskManager
|
|
57
|
-
from dara.core.logging import eng_logger
|
|
58
34
|
|
|
59
35
|
|
|
60
|
-
|
|
36
|
+
@deprecated(
|
|
37
|
+
'DerivedDataVariable is deprecated and will be removed in a future version. Use dara.core.interactivity.derived_variable.DerivedVariable instead, it can now return DataFrames'
|
|
38
|
+
)
|
|
39
|
+
class DerivedDataVariable(DerivedVariable):
|
|
61
40
|
"""
|
|
62
41
|
DerivedDataVariable represents a variable designed to hold datasets computed
|
|
63
42
|
by a resolver function like a normal DerivedVariable.
|
|
@@ -65,13 +44,6 @@ class DerivedDataVariable(AnyDataVariable, DerivedVariable):
|
|
|
65
44
|
Note: the resolver function must return a DataFrame.
|
|
66
45
|
"""
|
|
67
46
|
|
|
68
|
-
uid: str
|
|
69
|
-
filters: Optional[FilterQuery] = None
|
|
70
|
-
variables: List[AnyVariable]
|
|
71
|
-
polling_interval: Optional[int] = None
|
|
72
|
-
deps: Optional[List[AnyVariable]] = None
|
|
73
|
-
model_config = ConfigDict(extra='forbid')
|
|
74
|
-
|
|
75
47
|
def __init__(
|
|
76
48
|
self,
|
|
77
49
|
func: Union[
|
|
@@ -107,9 +79,6 @@ class DerivedDataVariable(AnyDataVariable, DerivedVariable):
|
|
|
107
79
|
- `deps = [var1, var2]` - `func` is ran whenever one of these vars changes
|
|
108
80
|
:param uid: the unique identifier for this variable; if not provided a random one is generated
|
|
109
81
|
"""
|
|
110
|
-
cache = Cache.Policy.from_arg(cache)
|
|
111
|
-
|
|
112
|
-
# Initialize the DV underneath, which puts an entry in the derived variable registry
|
|
113
82
|
super().__init__(
|
|
114
83
|
func=func,
|
|
115
84
|
cache=cache,
|
|
@@ -118,257 +87,4 @@ class DerivedDataVariable(AnyDataVariable, DerivedVariable):
|
|
|
118
87
|
polling_interval=polling_interval,
|
|
119
88
|
deps=deps,
|
|
120
89
|
run_as_task=run_as_task,
|
|
121
|
-
_get_value=DerivedDataVariable.get_value,
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
# Also put an entry in the data variable registry under the same uid; this way we can send a request
|
|
125
|
-
# for either the DV (to update the cached value) or the DataVariable (to get the cached value)
|
|
126
|
-
from dara.core.internal.registries import data_variable_registry
|
|
127
|
-
|
|
128
|
-
data_variable_registry.register(
|
|
129
|
-
str(self.uid),
|
|
130
|
-
DataVariableRegistryEntry(
|
|
131
|
-
type='derived',
|
|
132
|
-
cache=cache,
|
|
133
|
-
uid=str(self.uid),
|
|
134
|
-
get_data=DerivedDataVariable.get_data,
|
|
135
|
-
get_total_count=DerivedDataVariable.get_total_count,
|
|
136
|
-
get_schema=DerivedDataVariable.get_schema,
|
|
137
|
-
),
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
@staticmethod
|
|
141
|
-
def _get_schema_cache_key(cache_key: str) -> str:
|
|
142
|
-
"""
|
|
143
|
-
Get a unique cache key for the data variable's schema.
|
|
144
|
-
|
|
145
|
-
:param cache_key: cache_key of the DerivedDataVariable
|
|
146
|
-
"""
|
|
147
|
-
return f'schema-{cache_key}'
|
|
148
|
-
|
|
149
|
-
@staticmethod
|
|
150
|
-
async def _filter_data(
|
|
151
|
-
data: Union[DataFrame, Any, None],
|
|
152
|
-
count_cache_key: str,
|
|
153
|
-
var_entry: DataVariableRegistryEntry,
|
|
154
|
-
store: CacheStore,
|
|
155
|
-
filters: Optional[Union[FilterQuery, dict]] = None,
|
|
156
|
-
pagination: Optional[Pagination] = None,
|
|
157
|
-
) -> Optional[DataFrame]:
|
|
158
|
-
"""
|
|
159
|
-
Helper function to apply filters and pagination to a dataframe.
|
|
160
|
-
Also verifies if the data is a DataFrame.
|
|
161
|
-
|
|
162
|
-
:param data: data to filter
|
|
163
|
-
:param count_cache_key: cache key to store the count under
|
|
164
|
-
:param var_entry: data variable entry
|
|
165
|
-
:param store: store instance
|
|
166
|
-
:param filters: filters to use
|
|
167
|
-
:param pagination: pagination to use
|
|
168
|
-
"""
|
|
169
|
-
if data is not None and not isinstance(data, DataFrame):
|
|
170
|
-
raise ValueError(f'Data returned by DerivedDataVariable resolver must be a DataFrame, found {type(data)}')
|
|
171
|
-
|
|
172
|
-
# Right before we filter, append index column to the dataset
|
|
173
|
-
data = append_index(data)
|
|
174
|
-
|
|
175
|
-
filtered_data, count = apply_filters(data, coerce_to_filter_query(filters), pagination)
|
|
176
|
-
|
|
177
|
-
# Cache the count
|
|
178
|
-
await store.set(var_entry, key=count_cache_key, value=count, pin=True)
|
|
179
|
-
|
|
180
|
-
return filtered_data
|
|
181
|
-
|
|
182
|
-
@classmethod
|
|
183
|
-
async def get_value(
|
|
184
|
-
cls,
|
|
185
|
-
var_entry: DerivedVariableRegistryEntry,
|
|
186
|
-
store: CacheStore,
|
|
187
|
-
task_mgr: TaskManager,
|
|
188
|
-
args: List[Any],
|
|
189
|
-
force_key: Optional[str] = None,
|
|
190
|
-
) -> DerivedVariableResult:
|
|
191
|
-
"""
|
|
192
|
-
Update the underlying derived variable.
|
|
193
|
-
Wrapper around DerivedVariable.get_value which does not return the value (returns `True` instead).
|
|
194
|
-
|
|
195
|
-
:param var: the registry entry for the underlying derived variable
|
|
196
|
-
:param store: the store instance to check for cached values
|
|
197
|
-
:param task_mgr: task manager instance
|
|
198
|
-
:param args: the arguments to call the underlying function with
|
|
199
|
-
:param force: whether to ignore cache
|
|
200
|
-
"""
|
|
201
|
-
_uid_short = f'{var_entry.uid[:3]}..{var_entry.uid[-3:]}'
|
|
202
|
-
eng_logger.info(
|
|
203
|
-
f'Derived Data Variable {_uid_short} calling superclass get_value', {'uid': var_entry.uid, 'args': args}
|
|
204
|
-
)
|
|
205
|
-
value = await super().get_value(var_entry, store, task_mgr, args, force_key)
|
|
206
|
-
|
|
207
|
-
# Pin the value in the store until it's read by get data
|
|
208
|
-
await asyncio.gather(
|
|
209
|
-
store.set(registry_entry=var_entry, key=value['cache_key'], value=value['value'], pin=True),
|
|
210
|
-
store.set(
|
|
211
|
-
registry_entry=var_entry,
|
|
212
|
-
key=cls._get_schema_cache_key(value['cache_key']),
|
|
213
|
-
value=build_table_schema(
|
|
214
|
-
df_convert_to_internal(cast(DataFrame, value['value'])),
|
|
215
|
-
)
|
|
216
|
-
if isinstance(value['value'], DataFrame)
|
|
217
|
-
else None,
|
|
218
|
-
pin=True,
|
|
219
|
-
),
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
eng_logger.info(
|
|
223
|
-
f'Derived Data Variable {_uid_short} received result from superclass',
|
|
224
|
-
{'uid': var_entry.uid, 'result': value},
|
|
225
90
|
)
|
|
226
|
-
|
|
227
|
-
# If the value is a task, then we need to return it
|
|
228
|
-
if isinstance(value['value'], BaseTask):
|
|
229
|
-
return value
|
|
230
|
-
|
|
231
|
-
return {'cache_key': value['cache_key'], 'value': True}
|
|
232
|
-
|
|
233
|
-
@classmethod
|
|
234
|
-
async def get_data(
|
|
235
|
-
cls,
|
|
236
|
-
dv_entry: DerivedVariableRegistryEntry,
|
|
237
|
-
data_entry: DataVariableRegistryEntry,
|
|
238
|
-
cache_key: str,
|
|
239
|
-
store: CacheStore,
|
|
240
|
-
filters: Optional[Union[FilterQuery, dict]] = None,
|
|
241
|
-
pagination: Optional[Pagination] = None,
|
|
242
|
-
format_for_display: bool = False,
|
|
243
|
-
) -> Union[BaseTask, DataFrame, None]:
|
|
244
|
-
"""
|
|
245
|
-
Get the filtered data from the underlying derived variable stored under the specified cache_key.
|
|
246
|
-
|
|
247
|
-
:param var_entry: the registry entry for the data variable
|
|
248
|
-
:param cache_key: cache_key of the underlying DerivedVariable
|
|
249
|
-
:param store: the store instance to check for cached values
|
|
250
|
-
:param filters: the filters to apply to the data
|
|
251
|
-
:param pagination: the pagination to apply to the data
|
|
252
|
-
"""
|
|
253
|
-
_uid_short = f'{data_entry.uid[:3]}..{data_entry.uid[-3:]}'
|
|
254
|
-
data_cache_key = f'{cache_key}_{hash_object(filters or {})}_{hash_object(pagination or {})}'
|
|
255
|
-
count_cache_key = f'{cache_key}_{hash_object(filters or {})}'
|
|
256
|
-
|
|
257
|
-
# Check for cached result of the entire data variable
|
|
258
|
-
data_store_entry = await store.get(data_entry, key=data_cache_key)
|
|
259
|
-
|
|
260
|
-
# if there's a pending task for this exact request, subscribe to the pending task and return it
|
|
261
|
-
if isinstance(data_store_entry, PendingTask):
|
|
262
|
-
data_store_entry.add_subscriber()
|
|
263
|
-
return data_store_entry
|
|
264
|
-
|
|
265
|
-
# Found cached result
|
|
266
|
-
if isinstance(data_store_entry, DataFrame):
|
|
267
|
-
return data_store_entry
|
|
268
|
-
|
|
269
|
-
# First retrieve the cached data for underlying DV
|
|
270
|
-
data = await store.get(dv_entry, key=cache_key, unpin=True)
|
|
271
|
-
|
|
272
|
-
# Value could have been made pending in the meantime
|
|
273
|
-
if isinstance(data, PendingValue):
|
|
274
|
-
data = await data.wait()
|
|
275
|
-
|
|
276
|
-
eng_logger.info(
|
|
277
|
-
f'Derived Data Variable {_uid_short} retrieved underlying DV value', {'uid': dv_entry.uid, 'value': data}
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
# if the DV returned a task (Task/PendingTask), return a MetaTask which will do the filtering on the task result
|
|
281
|
-
if isinstance(data, BaseTask):
|
|
282
|
-
task_id = f'{dv_entry.uid}_Filter_MetaTask_{str(uuid4())}'
|
|
283
|
-
|
|
284
|
-
eng_logger.info(
|
|
285
|
-
f'Derived Data Variable {_uid_short} creating filtering metatask',
|
|
286
|
-
{'uid': dv_entry.uid, 'task_id': task_id, 'cache_key': data_cache_key},
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
return MetaTask(
|
|
290
|
-
cls._filter_data,
|
|
291
|
-
[data, count_cache_key, data_entry, store, filters, pagination],
|
|
292
|
-
notify_channels=data.notify_channels,
|
|
293
|
-
process_as_task=False,
|
|
294
|
-
cache_key=data_cache_key,
|
|
295
|
-
reg_entry=data_entry, # task results are set as the variable result
|
|
296
|
-
task_id=task_id,
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
# Run the filtering
|
|
300
|
-
data = await cls._filter_data(data, count_cache_key, data_entry, store, filters, pagination)
|
|
301
|
-
if format_for_display and data is not None:
|
|
302
|
-
data = data.copy()
|
|
303
|
-
for col in data.columns:
|
|
304
|
-
if data[col].dtype == 'object':
|
|
305
|
-
# We need to convert all values to string to avoid issues with displaying data in the Table component, for example when displaying datetime and number objects in the same column
|
|
306
|
-
data.loc[:, col] = data[col].apply(str)
|
|
307
|
-
|
|
308
|
-
return data
|
|
309
|
-
|
|
310
|
-
@classmethod
|
|
311
|
-
async def get_total_count(
|
|
312
|
-
cls, data_entry: DataVariableRegistryEntry, store: CacheStore, cache_key: str, filters: Optional[FilterQuery]
|
|
313
|
-
):
|
|
314
|
-
"""
|
|
315
|
-
Get total count of the derived data variable.
|
|
316
|
-
"""
|
|
317
|
-
count_cache_key = f'{cache_key}_{hash_object(filters or {})}'
|
|
318
|
-
entry = await store.get(data_entry, key=count_cache_key, unpin=True)
|
|
319
|
-
|
|
320
|
-
# No entry means this filter setup has not been done yet, this shouldn't happen
|
|
321
|
-
if entry is None:
|
|
322
|
-
raise ValueError('Requested count for filter setup which has not been performed yet')
|
|
323
|
-
|
|
324
|
-
return entry
|
|
325
|
-
|
|
326
|
-
@classmethod
|
|
327
|
-
async def get_schema(cls, derived_entry: DerivedVariableRegistryEntry, store: CacheStore, cache_key: str):
|
|
328
|
-
"""
|
|
329
|
-
Get the schema of the derived data variable.
|
|
330
|
-
"""
|
|
331
|
-
return cast(
|
|
332
|
-
DataFrameSchema, await store.get(derived_entry, key=cls._get_schema_cache_key(cache_key), unpin=True)
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
@classmethod
|
|
336
|
-
async def resolve_value(
|
|
337
|
-
cls,
|
|
338
|
-
data_entry: DataVariableRegistryEntry,
|
|
339
|
-
dv_entry: DerivedVariableRegistryEntry,
|
|
340
|
-
store: CacheStore,
|
|
341
|
-
task_mgr: TaskManager,
|
|
342
|
-
args: List[Any],
|
|
343
|
-
filters: Optional[Union[FilterQuery, dict]] = None,
|
|
344
|
-
force_key: Optional[str] = None,
|
|
345
|
-
):
|
|
346
|
-
"""
|
|
347
|
-
Helper method to resolve the filtered value of a derived data variable.
|
|
348
|
-
Under the hood runs the underlying DerivedVariable, starts its task if required, and then filters the result.
|
|
349
|
-
|
|
350
|
-
:param data_entry: the registry entry for the data variable
|
|
351
|
-
:param dv_entry: the registry entry for the underlying derived variable
|
|
352
|
-
:param store: the store instance to check for cached values
|
|
353
|
-
:param task_mgr: task manager instance
|
|
354
|
-
:param args: the arguments to call the underlying function with
|
|
355
|
-
:param filters: the filters to apply to the data
|
|
356
|
-
:param force_key: unique key for forced execution, if provided forces cache bypass
|
|
357
|
-
:param pagination: the pagination to apply to the data
|
|
358
|
-
"""
|
|
359
|
-
dv_result = await cls.get_value(dv_entry, store, task_mgr, args, force_key)
|
|
360
|
-
|
|
361
|
-
# If the intermediate result was a task/metatask, we need to run it
|
|
362
|
-
# get_data will then pick up the result from the pending task for it
|
|
363
|
-
if isinstance(dv_result['value'], (Task, MetaTask)):
|
|
364
|
-
await task_mgr.run_task(dv_result['value'], None)
|
|
365
|
-
|
|
366
|
-
return await cls.get_data(dv_entry, data_entry, dv_result['cache_key'], store, filters)
|
|
367
|
-
|
|
368
|
-
@model_serializer(mode='wrap')
|
|
369
|
-
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
370
|
-
parent_dict = nxt(self)
|
|
371
|
-
# nested is not supported for DerivedDataVariable so remove from serialised form
|
|
372
|
-
# it's included because we inherit from DV which has the field
|
|
373
|
-
parent_dict.pop('nested')
|
|
374
|
-
return {**parent_dict, '__typename': 'DerivedDataVariable', 'uid': str(parent_dict['uid'])}
|