dara-core 1.16.22__py3-none-any.whl → 1.17.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/base_definitions.py +9 -0
- dara/core/definitions.py +45 -12
- dara/core/interactivity/__init__.py +2 -0
- dara/core/interactivity/actions.py +1 -9
- dara/core/interactivity/condition.py +8 -0
- dara/core/interactivity/plain_variable.py +0 -14
- dara/core/interactivity/switch_variable.py +416 -0
- dara/core/internal/dependency_resolution.py +138 -1
- dara/core/main.py +3 -0
- dara/core/umd/dara.core.umd.js +442 -255
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/METADATA +10 -10
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/RECORD +15 -14
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/LICENSE +0 -0
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/WHEEL +0 -0
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/entry_points.txt +0 -0
|
@@ -19,12 +19,14 @@ from typing import Any, List, Literal, Optional, Union
|
|
|
19
19
|
|
|
20
20
|
from typing_extensions import TypedDict, TypeGuard
|
|
21
21
|
|
|
22
|
+
from dara.core.base_definitions import BaseTask, PendingTask
|
|
22
23
|
from dara.core.interactivity import DataVariable, DerivedDataVariable, DerivedVariable
|
|
23
24
|
from dara.core.interactivity.filtering import FilterQuery
|
|
24
25
|
from dara.core.internal.cache_store import CacheStore
|
|
25
26
|
from dara.core.internal.pandas_utils import remove_index
|
|
26
27
|
from dara.core.internal.registry_lookup import RegistryLookup
|
|
27
28
|
from dara.core.internal.tasks import TaskManager
|
|
29
|
+
from dara.core.logging import dev_logger
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
class ResolvedDerivedVariable(TypedDict):
|
|
@@ -50,6 +52,14 @@ class ResolvedDataVariable(TypedDict):
|
|
|
50
52
|
uid: str
|
|
51
53
|
|
|
52
54
|
|
|
55
|
+
class ResolvedSwitchVariable(TypedDict):
|
|
56
|
+
type: Literal['switch']
|
|
57
|
+
uid: str
|
|
58
|
+
value: Any
|
|
59
|
+
value_map: Any
|
|
60
|
+
default: Any
|
|
61
|
+
|
|
62
|
+
|
|
53
63
|
def is_resolved_derived_variable(obj: Any) -> TypeGuard[ResolvedDerivedVariable]:
|
|
54
64
|
return isinstance(obj, dict) and 'uid' in obj and obj.get('type') == 'derived'
|
|
55
65
|
|
|
@@ -62,8 +72,14 @@ def is_resolved_data_variable(obj: Any) -> TypeGuard[ResolvedDataVariable]:
|
|
|
62
72
|
return isinstance(obj, dict) and 'uid' in obj and obj.get('type') == 'data'
|
|
63
73
|
|
|
64
74
|
|
|
75
|
+
def is_resolved_switch_variable(obj: Any) -> TypeGuard[ResolvedSwitchVariable]:
|
|
76
|
+
return isinstance(obj, dict) and 'uid' in obj and obj.get('type') == 'switch'
|
|
77
|
+
|
|
78
|
+
|
|
65
79
|
async def resolve_dependency(
|
|
66
|
-
entry: Union[
|
|
80
|
+
entry: Union[
|
|
81
|
+
ResolvedDerivedDataVariable, ResolvedDataVariable, ResolvedDerivedVariable, ResolvedSwitchVariable, Any
|
|
82
|
+
],
|
|
67
83
|
store: CacheStore,
|
|
68
84
|
task_mgr: TaskManager,
|
|
69
85
|
):
|
|
@@ -84,6 +100,9 @@ async def resolve_dependency(
|
|
|
84
100
|
if is_resolved_data_variable(entry):
|
|
85
101
|
return await _resolve_data_var(entry, store)
|
|
86
102
|
|
|
103
|
+
if is_resolved_switch_variable(entry):
|
|
104
|
+
return await _resolve_switch_var(entry, store, task_mgr)
|
|
105
|
+
|
|
87
106
|
return entry
|
|
88
107
|
|
|
89
108
|
|
|
@@ -149,3 +168,121 @@ async def _resolve_data_var(data_variable_entry: ResolvedDataVariable, store: Ca
|
|
|
149
168
|
var = await registry_mgr.get(data_variable_registry, str(data_variable_entry.get('uid')))
|
|
150
169
|
result = await DataVariable.get_value(var, store, data_variable_entry.get('filters', None))
|
|
151
170
|
return remove_index(result)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _normalize_lookup_key(value: Any) -> str:
|
|
174
|
+
"""
|
|
175
|
+
Normalize a value to a string key that matches JavaScript object key serialization.
|
|
176
|
+
This ensures consistent lookup between Python backend and JavaScript frontend.
|
|
177
|
+
|
|
178
|
+
JavaScript's String() conversion rules:
|
|
179
|
+
- String(true) -> "true", String(false) -> "false"
|
|
180
|
+
- String(null) -> "null", String(undefined) -> "undefined"
|
|
181
|
+
- Numbers and other types are converted to their string representation
|
|
182
|
+
|
|
183
|
+
:param value: The value to normalize as a lookup key
|
|
184
|
+
:return: String representation suitable for object key lookup
|
|
185
|
+
"""
|
|
186
|
+
if isinstance(value, bool):
|
|
187
|
+
# JavaScript String(true) -> "true", String(false) -> "false"
|
|
188
|
+
return str(value).lower()
|
|
189
|
+
elif value is None:
|
|
190
|
+
# JavaScript String(null) -> "null"
|
|
191
|
+
return 'null'
|
|
192
|
+
else:
|
|
193
|
+
# For numbers, strings, and other types, use standard string conversion
|
|
194
|
+
return str(value)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _evaluate_condition(condition: dict) -> bool:
|
|
198
|
+
"""
|
|
199
|
+
Evaluate a condition object and return the boolean result.
|
|
200
|
+
|
|
201
|
+
:param condition: condition dict with 'variable', 'operator', and 'other' keys
|
|
202
|
+
:return: boolean result of the condition evaluation
|
|
203
|
+
"""
|
|
204
|
+
variable_value = condition['variable']
|
|
205
|
+
operator = condition['operator']
|
|
206
|
+
other_value = condition['other']
|
|
207
|
+
|
|
208
|
+
if operator == 'equal':
|
|
209
|
+
return variable_value == other_value
|
|
210
|
+
elif operator == 'truthy':
|
|
211
|
+
return bool(variable_value)
|
|
212
|
+
elif operator == 'not_equal':
|
|
213
|
+
return variable_value != other_value
|
|
214
|
+
else:
|
|
215
|
+
# strictly numeric comparisons, ensure they're numbers just in case
|
|
216
|
+
val_a = float(variable_value)
|
|
217
|
+
val_b = float(other_value)
|
|
218
|
+
|
|
219
|
+
if operator == 'greater_than':
|
|
220
|
+
return val_a > val_b
|
|
221
|
+
elif operator == 'greater_equal':
|
|
222
|
+
return val_a >= val_b
|
|
223
|
+
elif operator == 'less_than':
|
|
224
|
+
return val_a < val_b
|
|
225
|
+
elif operator == 'less_equal':
|
|
226
|
+
return val_a <= val_b
|
|
227
|
+
else:
|
|
228
|
+
raise ValueError(f'Unknown condition operator: {operator}')
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
async def _resolve_switch_var(switch_variable_entry: ResolvedSwitchVariable, store: CacheStore, task_mgr: TaskManager):
|
|
232
|
+
"""
|
|
233
|
+
Resolve a switch variable by evaluating its constituent parts and returning the appropriate value.
|
|
234
|
+
|
|
235
|
+
:param switch_variable_entry: switch variable entry
|
|
236
|
+
:param store: store instance to use for caching
|
|
237
|
+
:param task_mgr: task manager instance
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
async def _resolve_maybe_task(value: Any) -> Any:
|
|
241
|
+
if isinstance(value, BaseTask):
|
|
242
|
+
task_result = await task_mgr.run_task(value)
|
|
243
|
+
if isinstance(task_result, PendingTask):
|
|
244
|
+
return await task_result.value()
|
|
245
|
+
return task_result
|
|
246
|
+
return value
|
|
247
|
+
|
|
248
|
+
# resolve the constituent parts
|
|
249
|
+
resolved_value = await resolve_dependency(switch_variable_entry.get('value'), store, task_mgr)
|
|
250
|
+
resolved_value = await _resolve_maybe_task(resolved_value)
|
|
251
|
+
|
|
252
|
+
resolved_value_map = await resolve_dependency(switch_variable_entry.get('value_map'), store, task_mgr)
|
|
253
|
+
resolved_value_map = await _resolve_maybe_task(resolved_value_map)
|
|
254
|
+
|
|
255
|
+
resolved_default = await resolve_dependency(switch_variable_entry.get('default'), store, task_mgr)
|
|
256
|
+
resolved_default = await _resolve_maybe_task(resolved_default)
|
|
257
|
+
|
|
258
|
+
# The frontend should have already evaluated conditions and sent the resolved value
|
|
259
|
+
# For switch variables, we just need to look up the value in the mapping
|
|
260
|
+
if isinstance(resolved_value_map, dict):
|
|
261
|
+
# value could be a condition object
|
|
262
|
+
if isinstance(resolved_value, dict) and resolved_value.get('__typename') == 'Condition':
|
|
263
|
+
# Evaluate the condition and use the boolean result as the lookup key
|
|
264
|
+
try:
|
|
265
|
+
# First, resolve any nested dependencies in the condition's 'other' field
|
|
266
|
+
condition_copy = resolved_value.copy()
|
|
267
|
+
condition_copy['other'] = await resolve_dependency(condition_copy.get('other'), store, task_mgr)
|
|
268
|
+
condition_copy['other'] = await _resolve_maybe_task(condition_copy['other'])
|
|
269
|
+
|
|
270
|
+
# Also resolve the variable field in case it's a dependency
|
|
271
|
+
condition_copy['variable'] = await resolve_dependency(condition_copy.get('variable'), store, task_mgr)
|
|
272
|
+
condition_copy['variable'] = await _resolve_maybe_task(condition_copy['variable'])
|
|
273
|
+
|
|
274
|
+
# Evaluate the condition
|
|
275
|
+
resolved_value = _evaluate_condition(condition_copy)
|
|
276
|
+
except Exception as e:
|
|
277
|
+
# If condition evaluation fails, log and use default
|
|
278
|
+
dev_logger.error('Error evaluating condition', error=e)
|
|
279
|
+
return resolved_default
|
|
280
|
+
|
|
281
|
+
# Normalize the lookup key to match JavaScript object key serialization
|
|
282
|
+
resolved_value = _normalize_lookup_key(resolved_value)
|
|
283
|
+
|
|
284
|
+
# Try to get the value from the mapping, fall back to default
|
|
285
|
+
return resolved_value_map.get(resolved_value, resolved_default)
|
|
286
|
+
|
|
287
|
+
# If value_map is not a dict (shouldn't happen in normal cases), return default
|
|
288
|
+
return resolved_default
|
dara/core/main.py
CHANGED
|
@@ -314,6 +314,9 @@ def _start_application(config: Configuration):
|
|
|
314
314
|
template_registry.register(name, renderer(config))
|
|
315
315
|
|
|
316
316
|
except Exception as e:
|
|
317
|
+
import traceback
|
|
318
|
+
|
|
319
|
+
traceback.print_exc()
|
|
317
320
|
dev_logger.error(
|
|
318
321
|
'Something went wrong when building application template, there is most likely an issue in the application logic',
|
|
319
322
|
e,
|