dara-core 1.14.9__py3-none-any.whl → 1.15.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.
@@ -490,10 +490,11 @@ def create_router(config: Configuration):
490
490
  return result
491
491
 
492
492
  @core_api_router.post('/store', dependencies=[Depends(verify_session)])
493
- async def sync_backend_store(values: Dict[str, Any] = Body()):
493
+ async def sync_backend_store(ws_channel: str = Body(), values: Dict[str, Any] = Body()):
494
494
  registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
495
495
 
496
496
  async def _write(store_uid: str, value: Any):
497
+ WS_CHANNEL.set(ws_channel)
497
498
  store_entry: BackendStoreEntry = await registry_mgr.get(backend_store_registry, store_uid)
498
499
  result = store_entry.store.write(value)
499
500
 
@@ -321,6 +321,9 @@ class WebsocketManager:
321
321
 
322
322
  def __init__(self):
323
323
  self.handlers: Dict[str, WebSocketHandler] = {}
324
+ """
325
+ A mapping of channel IDs to WebSocketHandler instances.
326
+ """
324
327
 
325
328
  def _construct_message(self, payload: LoosePayload, custom: bool) -> ServerMessage:
326
329
  """
@@ -344,24 +347,30 @@ class WebsocketManager:
344
347
  self.handlers[channel_id] = handler
345
348
  return handler
346
349
 
347
- async def broadcast(self, message: LoosePayload, custom=False):
350
+ async def broadcast(self, message: LoosePayload, custom=False, ignore_channel: Optional[str] = None):
348
351
  """
349
352
  Send a message to all connected clients.
350
353
 
351
354
  :param message: The message to send
352
355
  :param custom: Whether the message is a custom message
356
+ :param ignore_channel: A channel ID to ignore when broadcasting
353
357
  """
354
358
  async with anyio.create_task_group() as tg:
355
359
  for handler in self.handlers.values():
360
+ if ignore_channel is not None and handler.channel_id == ignore_channel:
361
+ continue
356
362
  tg.start_soon(handler.send_message, self._construct_message(message, custom))
357
363
 
358
- async def send_message_to_user(self, user_id: str, message: LoosePayload, custom=False):
364
+ async def send_message_to_user(
365
+ self, user_id: str, message: LoosePayload, custom=False, ignore_channel: Optional[str] = None
366
+ ):
359
367
  """
360
368
  Send a message to all connected channels associated with the given user.
361
369
 
362
370
  :param user_id: The user ID to send the message to
363
371
  :param message: The message payload to send
364
372
  :param custom: Whether the message is a custom message
373
+ :param ignore_channel: A channel ID to ignore when sending
365
374
  """
366
375
  channels = get_user_channels(user_id)
367
376
 
@@ -370,6 +379,8 @@ class WebsocketManager:
370
379
 
371
380
  async with anyio.create_task_group() as tg:
372
381
  for channel in channels:
382
+ if ignore_channel is not None and channel == ignore_channel:
383
+ continue
373
384
  tg.start_soon(self.send_message, channel, message, custom)
374
385
 
375
386
  async def send_message(self, channel_id: str, message: LoosePayload, custom=False):
dara/core/persistence.py CHANGED
@@ -10,6 +10,7 @@ from pydantic import BaseModel, Field, PrivateAttr, validator
10
10
 
11
11
  from dara.core.auth.definitions import USER
12
12
  from dara.core.internal.utils import run_user_handler
13
+ from dara.core.internal.websocket import WS_CHANNEL
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from dara.core.interactivity.plain_variable import Variable
@@ -257,7 +258,7 @@ class BackendStore(PersistenceStore):
257
258
  msg = {'store_uid': self.uid, 'value': value}
258
259
 
259
260
  if self.scope == 'global':
260
- return await ws_mgr.broadcast(msg)
261
+ return await ws_mgr.broadcast(msg, ignore_channel=WS_CHANNEL.get())
261
262
 
262
263
  # For user scope, we need to find channels for the user and notify them
263
264
  user = USER.get()
@@ -266,7 +267,7 @@ class BackendStore(PersistenceStore):
266
267
  return
267
268
 
268
269
  user_identifier = user.identity_id or user.identity_name
269
- return await ws_mgr.send_message_to_user(user_identifier, msg)
270
+ return await ws_mgr.send_message_to_user(user_identifier, msg, ignore_channel=WS_CHANNEL.get())
270
271
 
271
272
  async def init(self, variable: 'Variable'):
272
273
  """
@@ -58144,30 +58144,39 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
58144
58144
  const val = await response.json();
58145
58145
  return val;
58146
58146
  }, []);
58147
- const syncStoreValues = React__namespace.useCallback(async ({ diff }) => {
58148
- const extrasMap = /* @__PURE__ */ new Map();
58149
- for (const [itemKey, value] of diff.entries()) {
58150
- const extras = STORE_EXTRAS_MAP.get(itemKey);
58151
- if (!extrasMap.has(extras)) {
58152
- extrasMap.set(extras, {});
58147
+ const syncStoreValues = React__namespace.useCallback(
58148
+ async ({ diff }) => {
58149
+ const extrasMap = /* @__PURE__ */ new Map();
58150
+ for (const [itemKey, value] of diff.entries()) {
58151
+ const extras = STORE_EXTRAS_MAP.get(itemKey);
58152
+ if (!extrasMap.has(extras)) {
58153
+ extrasMap.set(extras, {});
58154
+ }
58155
+ extrasMap.get(extras)[itemKey] = value;
58156
+ }
58157
+ async function sendRequest(serializableExtras, storeDiff) {
58158
+ const response = await request(
58159
+ `/api/core/store`,
58160
+ {
58161
+ body: JSON.stringify({
58162
+ values: storeDiff,
58163
+ ws_channel: await client.getChannel()
58164
+ }),
58165
+ method: "POST"
58166
+ },
58167
+ serializableExtras.extras
58168
+ );
58169
+ await handleAuthErrors(response, true);
58170
+ await validateResponse(response, `Failed to sync the store values`);
58153
58171
  }
58154
- extrasMap.get(extras)[itemKey] = value;
58155
- }
58156
- async function sendRequest(serializableExtras, storeDiff) {
58157
- const response = await request(
58158
- `/api/core/store`,
58159
- { body: JSON.stringify(storeDiff), method: "POST" },
58160
- serializableExtras.extras
58172
+ await Promise.allSettled(
58173
+ Array.from(extrasMap.entries()).map(
58174
+ ([serializableExtras, storeDiff]) => sendRequest(serializableExtras, storeDiff)
58175
+ )
58161
58176
  );
58162
- await handleAuthErrors(response, true);
58163
- await validateResponse(response, `Failed to sync the store values`);
58164
- }
58165
- await Promise.allSettled(
58166
- Array.from(extrasMap.entries()).map(
58167
- ([serializableExtras, storeDiff]) => sendRequest(serializableExtras, storeDiff)
58168
- )
58169
- );
58170
- }, []);
58177
+ },
58178
+ [client]
58179
+ );
58171
58180
  const listenToStoreChanges = React__namespace.useCallback(
58172
58181
  ({ updateItem }) => {
58173
58182
  if (!client) {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dara-core
3
- Version: 1.14.9
3
+ Version: 1.15.0
4
4
  Summary: Dara Framework Core
5
5
  Home-page: https://dara.causalens.com/
6
6
  License: Apache-2.0
@@ -20,10 +20,10 @@ Requires-Dist: async-asgi-testclient (>=1.4.11,<2.0.0)
20
20
  Requires-Dist: certifi (>=2024.7.4)
21
21
  Requires-Dist: click (==8.1.3)
22
22
  Requires-Dist: colorama (>=0.4.6,<0.5.0)
23
- Requires-Dist: create-dara-app (==1.14.9)
23
+ Requires-Dist: create-dara-app (==1.15.0)
24
24
  Requires-Dist: croniter (>=1.0.15,<3.0.0)
25
25
  Requires-Dist: cryptography (>=42.0.4)
26
- Requires-Dist: dara-components (==1.14.9) ; extra == "all"
26
+ Requires-Dist: dara-components (==1.15.0) ; extra == "all"
27
27
  Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0)
28
28
  Requires-Dist: fastapi (==0.109.0)
29
29
  Requires-Dist: fastapi_vite_dara (==0.3.1.0)
@@ -51,7 +51,7 @@ Description-Content-Type: text/markdown
51
51
 
52
52
  # Dara Application Framework
53
53
 
54
- <img src="https://github.com/causalens/dara/blob/v1.14.9/img/dara_light.svg?raw=true">
54
+ <img src="https://github.com/causalens/dara/blob/v1.15.0/img/dara_light.svg?raw=true">
55
55
 
56
56
  ![Master tests](https://github.com/causalens/dara/actions/workflows/tests.yml/badge.svg?branch=master)
57
57
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
@@ -96,7 +96,7 @@ source .venv/bin/activate
96
96
  dara start
97
97
  ```
98
98
 
99
- ![Dara App](https://github.com/causalens/dara/blob/v1.14.9/img/components_gallery.png?raw=true)
99
+ ![Dara App](https://github.com/causalens/dara/blob/v1.15.0/img/components_gallery.png?raw=true)
100
100
 
101
101
  Note: `pip` installation uses [PEP 660](https://peps.python.org/pep-0660/) `pyproject.toml`-based editable installs which require `pip >= 21.3` and `setuptools >= 64.0.0`. You can upgrade both with:
102
102
 
@@ -113,9 +113,9 @@ Explore some of our favorite apps - a great way of getting started and getting t
113
113
 
114
114
  | Dara App | Description |
115
115
  | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
116
- | ![Large Language Model](https://github.com/causalens/dara/blob/v1.14.9/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
117
- | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.14.9/img/plot_interactivity.png?raw=true) | Demonstrates how to enable the user to interact with plots, trigger actions based on clicks, mouse movements and other interactions with `Bokeh` or `Plotly` plots |
118
- | ![Graph Editor](https://github.com/causalens/dara/blob/v1.14.9/img/graph_viewer.png?raw=true) | Demonstrates how to use the `CausalGraphViewer` component to display your graphs or networks, customising the displayed information through colors and tooltips, and updating the page based on user interaction. |
116
+ | ![Large Language Model](https://github.com/causalens/dara/blob/v1.15.0/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
117
+ | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.15.0/img/plot_interactivity.png?raw=true) | Demonstrates how to enable the user to interact with plots, trigger actions based on clicks, mouse movements and other interactions with `Bokeh` or `Plotly` plots |
118
+ | ![Graph Editor](https://github.com/causalens/dara/blob/v1.15.0/img/graph_viewer.png?raw=true) | Demonstrates how to use the `CausalGraphViewer` component to display your graphs or networks, customising the displayed information through colors and tooltips, and updating the page based on user interaction. |
119
119
 
120
120
  Check out our [App Gallery](https://dara.causalens.com/gallery) for more inspiration!
121
121
 
@@ -142,9 +142,9 @@ And the supporting UI packages and tools.
142
142
  - `ui-utils` - miscellaneous utility functions
143
143
  - `ui-widgets` - widget components
144
144
 
145
- More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.14.9/CONTRIBUTING.md) file.
145
+ More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.15.0/CONTRIBUTING.md) file.
146
146
 
147
147
  ## License
148
148
 
149
- Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.14.9/LICENSE).
149
+ Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.15.0/LICENSE).
150
150
 
@@ -54,13 +54,13 @@ dara/core/internal/port_utils.py,sha256=AQOUNiFNBYKVUwQ7i9UlY1NQ3sWb5xh5GkO6P1Bm
54
54
  dara/core/internal/registries.py,sha256=9WDczIsNeSmzi6aViIq_b14lmmYGGkdsUGHpv0Sg9zo,3278
55
55
  dara/core/internal/registry.py,sha256=ONCDusqaL0q59Py_r8-fFVN3vbkkDf5TXzNvbB9SrGQ,4305
56
56
  dara/core/internal/registry_lookup.py,sha256=8snHu2wUUsngXjHyHh6eZqL_WwonTTQB6-WBX-R_WZg,2238
57
- dara/core/internal/routing.py,sha256=Wdy11iWDeAf2PyHXZv5mPeJ_BEcoR0XftOl3M8vUhJU,22782
57
+ dara/core/internal/routing.py,sha256=XViBysQSchhX9yoFyDoGe4r5OQ8xMfn6rvA2fi8Yd70,22847
58
58
  dara/core/internal/scheduler.py,sha256=z6OYwazBf3GYo8CzMC9IuGC2P96gI7JwxquT8GaoTMk,12944
59
59
  dara/core/internal/settings.py,sha256=wAWxl-HXjq7PW3twe_CrR-UuMRw9VBudC3eRmevZAhM,3869
60
60
  dara/core/internal/store.py,sha256=qVyU7JfC3zE2vYC2mfjmvECWMlFS9b-nMF1k-alg4Y8,7756
61
61
  dara/core/internal/tasks.py,sha256=XK-GTIyge8RBYAfzNs3rmLYVNSKIarCzPdqRSVGg-4M,24728
62
62
  dara/core/internal/utils.py,sha256=b1YYkn8qHl6-GY6cCm2MS1NXRS9j_rElYCKMZOxJgrY,8232
63
- dara/core/internal/websocket.py,sha256=pbG-4yAalufy24LUZRiqCC5zONEjqPM98djz6ZfYCLk,20940
63
+ dara/core/internal/websocket.py,sha256=yj_IVDfwyjPmljY-9NWktaM0QGG50KrWQERWrpfNn00,21480
64
64
  dara/core/jinja/index.html,sha256=iykqiRh3H_HkcjHJeeSRXRu45nZ2y1sZX5FLdPRhlQY,726
65
65
  dara/core/jinja/index_autojs.html,sha256=MRF5J0vNfzZQm9kPEeLl23sbr08fVSRd_PAUD6Fkc_0,1253
66
66
  dara/core/js_tooling/custom_js_scaffold/index.tsx,sha256=FEzSV5o5Nyzxw6eXvGLi7BkEBkXf3brV34_7ATLnY7o,68
@@ -80,8 +80,8 @@ dara/core/metrics/__init__.py,sha256=2UqpWHv-Ie58QLJIHJ9Szfjq8xifAuwy5FYGUIFwWtI
80
80
  dara/core/metrics/cache.py,sha256=ybofUhZO0TCHeyhB_AtldWk1QTmTKh7GucTXpOkeTFA,2580
81
81
  dara/core/metrics/runtime.py,sha256=YP-6Dz0GeI9_Yr7bUk_-OqShyFySGH_AKpDO126l6es,1833
82
82
  dara/core/metrics/utils.py,sha256=rYlBinxFc7VehFT5cTNXLk8gC74UEj7ZGq6vLgIDpSg,2247
83
- dara/core/persistence.py,sha256=TO94rPAN7jxZKVCC5YA4eE7GGDoNlCPe-BkkItV2VUE,10379
84
- dara/core/umd/dara.core.umd.js,sha256=l6rZUKQV-E-EwfoZRsbdtrlDdJsvd4RnvA_bhoGGZK4,4878822
83
+ dara/core/persistence.py,sha256=9GBLhK_so-T-DnNOVT3zo7mHXNjwIU2TzgcIOr7XEro,10497
84
+ dara/core/umd/dara.core.umd.js,sha256=AjBfi7G00OO78EvnWerCpfruED4xAI4PiKgApSQ2HIM,4879029
85
85
  dara/core/umd/style.css,sha256=YQtQ4veiSktnyONl0CU1iU1kKfcQhreH4iASi1MP7Ak,4095007
86
86
  dara/core/visual/__init__.py,sha256=QN0wbG9HPQ_vXh8BO8DnBXeYLIENVTNtRmYzZf1lx7c,577
87
87
  dara/core/visual/components/__init__.py,sha256=O-Em_glGdZNO0LLl2RWmJSrQiXKxliXg_PuhVXGT81I,1811
@@ -105,8 +105,8 @@ dara/core/visual/themes/__init__.py,sha256=aM4mgoIYo2neBSw5FRzswsht7PUKjLthiHLmF
105
105
  dara/core/visual/themes/dark.py,sha256=UQGDooOc8ric73eHs9E0ltYP4UCrwqQ3QxqN_fb4PwY,1942
106
106
  dara/core/visual/themes/definitions.py,sha256=m3oN0txs65MZepqjj7AKMMxybf2aq5fTjcTwJmHqEbk,2744
107
107
  dara/core/visual/themes/light.py,sha256=-Tviq8oEwGbdFULoDOqPuHO0UpAZGsBy8qFi0kAGolQ,1944
108
- dara_core-1.14.9.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
109
- dara_core-1.14.9.dist-info/METADATA,sha256=oZS9KyXTmBBH1Vantb_p6XR1Hp0lZBaxw8CCtr7-saQ,7390
110
- dara_core-1.14.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
111
- dara_core-1.14.9.dist-info/entry_points.txt,sha256=H__D5sNIGuPIhVam0DChNL-To5k8Y7nY7TAFz9Mz6cc,139
112
- dara_core-1.14.9.dist-info/RECORD,,
108
+ dara_core-1.15.0.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
109
+ dara_core-1.15.0.dist-info/METADATA,sha256=IhMXPa_0o-eECRfArK7m9tXmrGTPYB8k8elFdHI86ME,7390
110
+ dara_core-1.15.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
111
+ dara_core-1.15.0.dist-info/entry_points.txt,sha256=H__D5sNIGuPIhVam0DChNL-To5k8Y7nY7TAFz9Mz6cc,139
112
+ dara_core-1.15.0.dist-info/RECORD,,