Skip to content

Commit 02de663

Browse files
committed
Merge branch 'main' into nk/websocket_dynamic
2 parents 8ca822c + 80dfeed commit 02de663

3 files changed

Lines changed: 77 additions & 57 deletions

File tree

examples/07_end_to_end/earthquake.ipynb

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"id": "9e5ce935-7f5d-4c37-8bd9-cea707e0aefe",
4141
"metadata": {},
4242
"source": [
43-
"**Note:** This example has been tested for `jupyterlab==4.2.0` and `perspective-python==2.10.0`."
43+
"**Note:** This example has been tested for `jupyterlab==4.2.0` and `perspective-python==3.1.5."
4444
]
4545
},
4646
{
@@ -59,7 +59,7 @@
5959
},
6060
"source": [
6161
"```\n",
62-
"pip install obspy cartopy jupyterlab==4.2.0 perspective-python==2.10.0\n",
62+
"pip install obspy cartopy jupyterlab==4.2.0 perspective-python==3.1.5\n",
6363
"```"
6464
]
6565
},
@@ -204,21 +204,21 @@
204204
"metadata": {},
205205
"outputs": [],
206206
"source": [
207-
"from perspective import PerspectiveWidget, Plugin\n",
207+
"from perspective.widget import PerspectiveWidget\n",
208208
"import ipywidgets as widgets\n",
209209
"from datetime import datetime\n",
210210
"\n",
211211
"# Data schema for Perspective widget\n",
212212
"data = {\n",
213-
" \"longitude\": float,\n",
214-
" \"latitude\": float,\n",
215-
" \"magnitude\": float,\n",
216-
" \"time\": datetime,\n",
213+
" \"longitude\": \"float\",\n",
214+
" \"latitude\": \"float\",\n",
215+
" \"magnitude\": \"float\",\n",
216+
" \"time\": \"datetime\",\n",
217217
"}\n",
218218
"\n",
219219
"datagrid = PerspectiveWidget(\n",
220220
" data,\n",
221-
" plugin=Plugin.GRID,\n",
221+
" plugin=\"Datagrid\",\n",
222222
" group_by=[\"time\"],\n",
223223
" columns=[\"time\", \"longitude\", \"latitude\", \"magnitude\"],\n",
224224
" aggregates={\n",
@@ -231,8 +231,9 @@
231231
"\n",
232232
"worldmap = PerspectiveWidget(\n",
233233
" data,\n",
234-
" plugin=Plugin.MAP_SCATTER,\n",
234+
" plugin=\"Map Scatter\",\n",
235235
" columns=[\"longitude\", \"latitude\", \"magnitude\", \"magnitude\", \"time\", \"time\"],\n",
236+
" binding_mode=\"client-server\",\n",
236237
")\n",
237238
"\n",
238239
"# Create a tab widget with some PerspectiveWidgets inside\n",
@@ -367,12 +368,12 @@
367368
"def update_widget(event: csp.ts[EventData], widget: widgets.widgets.widget_selectioncontainer.Tab):\n",
368369
" if csp.ticked(event):\n",
369370
" # widget.children = [datagrid, worldmap]\n",
370-
" data = {\n",
371-
" \"time\": [event.time],\n",
372-
" \"longitude\": [event.longitude],\n",
373-
" \"latitude\": [event.latitude],\n",
374-
" \"magnitude\": [event.magnitude],\n",
375-
" }\n",
371+
" data = [{\n",
372+
" \"time\": event.time.isoformat(),\n",
373+
" \"longitude\": event.longitude,\n",
374+
" \"latitude\": event.latitude,\n",
375+
" \"magnitude\": event.magnitude,\n",
376+
" }]\n",
376377
" widget.children[0].update(data)\n",
377378
" widget.children[1].update(data)\n",
378379
"\n",
@@ -425,7 +426,7 @@
425426
"name": "python",
426427
"nbconvert_exporter": "python",
427428
"pygments_lexer": "ipython3",
428-
"version": "3.11.9"
429+
"version": "3.11.7"
429430
}
430431
},
431432
"nbformat": 4,

examples/07_end_to_end/wikimedia.ipynb

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"id": "08447511-b105-416f-a959-285330d417ba",
4949
"metadata": {},
5050
"source": [
51-
"**Note**: This tutorial has been tested for Python 3.11, using JupyterLab 4.2.0 and perspective-python 2.10.0."
51+
"**Note**: This tutorial has been tested for Python 3.11, using JupyterLab 4.2.0 and perspective-python 3.1.5."
5252
]
5353
},
5454
{
@@ -130,11 +130,12 @@
130130
"metadata": {},
131131
"outputs": [],
132132
"source": [
133-
"from perspective import PerspectiveWidget, Plugin\n",
133+
"from perspective.widget import PerspectiveWidget\n",
134134
"\n",
135135
"# Data schema\n",
136-
"data = {\"servername\": str, \"timestamp\": datetime, \"event\": str, \"servername_count\": int}\n",
137-
"widget = PerspectiveWidget(data, plugin=Plugin.XBAR, group_by=[\"servername\"], columns=[\"servername_count\"], theme='Pro Light')\n",
136+
"data = {\"servername\": \"string\", \"timestamp\": \"datetime\", \"event\": \"string\", \"servername_count\": \"integer\"}\n",
137+
"#widget = PerspectiveWidget(data, plugin=\"X Bar\", group_by=[\"servername\"], columns=[\"servername_count\"], aggregates={\"servername_count\": \"last\"}, theme='Pro Light', binding_mode=\"client-server\")\n",
138+
"widget = PerspectiveWidget(data, plugin=\"X Bar\", group_by=[\"servername\"], columns=[\"servername_count\"], aggregates={\"servername_count\": \"last\"}, theme='Pro Light', binding_mode=\"client-server\")\n",
138139
"widget"
139140
]
140141
},
@@ -213,38 +214,39 @@
213214
" if self._running:\n",
214215
" self._running = False\n",
215216
" self._thread.join()\n",
216-
" self._source.close()\n",
217+
" self._source.resp.close()\n",
217218
"\n",
218219
" def _run(self):\n",
219220
" servernames = dict([])\n",
220-
" while self._running:\n",
221-
" for item in self._source:\n",
222-
" if item.event == 'message':\n",
223-
" try:\n",
224-
" change = json.loads(item.data)\n",
225-
" except ValueError:\n",
226-
" pass\n",
227-
" else:\n",
228-
" # discard canary events\n",
229-
" # WMF Data Engineering team produces artificial 'canary' events into \n",
230-
" # each stream multiple times an hour. The presence of these canary\n",
231-
" # events in a stream allow us to differentiate between a broken event\n",
232-
" # stream, and an empty one.\n",
233-
" # We will also filter bot-generated events\n",
234-
" if change['meta']['domain'] == 'canary' or re.search('bot', change['user'], re.IGNORECASE):\n",
235-
" continue\n",
236-
" timestamp = change['meta']['dt']\n",
237-
" event = f\"{timestamp}:: {change['user']} edited {change['title']}\"\n",
238-
" servername = change['server_name']\n",
239-
" # Manually \"tick\" this edge\n",
240-
" self.push_tick(\n",
241-
" WikiData(\n",
242-
" servername=servername,\n",
243-
" servername_count=0,\n",
244-
" timestamp=timestamp,\n",
245-
" event=event\n",
246-
" )\n",
221+
" for item in self._source:\n",
222+
" if not self._running:\n",
223+
" break\n",
224+
" if item.event == 'message':\n",
225+
" try:\n",
226+
" change = json.loads(item.data)\n",
227+
" except ValueError:\n",
228+
" pass\n",
229+
" else:\n",
230+
" # discard canary events\n",
231+
" # WMF Data Engineering team produces artificial 'canary' events into \n",
232+
" # each stream multiple times an hour. The presence of these canary\n",
233+
" # events in a stream allow us to differentiate between a broken event\n",
234+
" # stream, and an empty one.\n",
235+
" # We will also filter bot-generated events\n",
236+
" if change['meta']['domain'] == 'canary' or re.search('bot', change['user'], re.IGNORECASE):\n",
237+
" continue\n",
238+
" timestamp = change['meta']['dt']\n",
239+
" event = f\"{timestamp}:: {change['user']} edited {change['title']}\"\n",
240+
" servername = change['server_name']\n",
241+
" # Manually \"tick\" this edge\n",
242+
" self.push_tick(\n",
243+
" WikiData(\n",
244+
" servername=servername,\n",
245+
" servername_count=0,\n",
246+
" timestamp=timestamp,\n",
247+
" event=event\n",
247248
" )\n",
249+
" )\n",
248250
"\n",
249251
"# Create the graph-time representation of our adapter\n",
250252
"FetchWikiData = py_push_adapter_def(\"FetchWikiData\", FetchWikiDataAdapter, csp.ts[WikiData], url=str)"
@@ -268,15 +270,32 @@
268270
"outputs": [],
269271
"source": [
270272
"@csp.node\n",
271-
"def update_widget(wiki_event: csp.ts[WikiData], widget: PerspectiveWidget):\n",
273+
"def update_widget(wiki_event: csp.ts[WikiData], widget: PerspectiveWidget, throttle: timedelta = timedelta(seconds=0.5)):\n",
274+
" # Updates the perspective widget with batched updates for scalability\n",
275+
" with csp.alarms():\n",
276+
" alarm = csp.alarm(bool)\n",
277+
"\n",
278+
" with csp.state():\n",
279+
" s_buffer = []\n",
280+
"\n",
281+
" with csp.start():\n",
282+
" csp.schedule_alarm(alarm, throttle, True)\n",
283+
" \n",
272284
" if csp.ticked(wiki_event):\n",
273-
" widget.update({\n",
274-
" \"servername\": [wiki_event.servername],\n",
275-
" \"servername_count\": [wiki_event.servername_count],\n",
276-
" \"timestamp\": [wiki_event.timestamp],\n",
277-
" \"event\": [wiki_event.event],\n",
285+
" s_buffer.append({\n",
286+
" \"servername\": wiki_event.servername,\n",
287+
" \"servername_count\": wiki_event.servername_count,\n",
288+
" \"timestamp\": wiki_event.timestamp,\n",
289+
" \"event\": wiki_event.event,\n",
278290
" })\n",
279291
"\n",
292+
" if csp.ticked(alarm):\n",
293+
" if len(s_buffer) > 0:\n",
294+
" widget.update(s_buffer)\n",
295+
" s_buffer = []\n",
296+
"\n",
297+
" csp.schedule_alarm(alarm, throttle, True)\n",
298+
"\n",
280299
"@csp.node\n",
281300
"def compute_server_count(wiki_event: csp.ts[WikiData]) -> csp.ts[WikiData]:\n",
282301
" # takes the raw struct in, creates a copy with the count set and ticks it out\n",
@@ -477,7 +496,7 @@
477496
"@csp.graph\n",
478497
"def wiki_graph():\n",
479498
" print(\"Start of graph building\")\n",
480-
" if csp.is_configured_realtime:\n",
499+
" if csp.is_configured_realtime():\n",
481500
" URL = \"https://stream.wikimedia.org/v2/stream/recentchange\"\n",
482501
" events = FetchWikiData(url=URL)\n",
483502
" else:\n",
@@ -540,7 +559,7 @@
540559
"name": "python",
541560
"nbconvert_exporter": "python",
542561
"pygments_lexer": "ipython3",
543-
"version": "3.11.9"
562+
"version": "3.11.7"
544563
}
545564
},
546565
"nbformat": 4,

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<tbody>
1212
<!-- Basic -->
1313
<tr>
14-
<td rowspan=4><a href="./01_basics/">Basics</a></td>
14+
<td rowspan=5><a href="./01_basics/">Basics</a></td>
1515
<td><a href="./01_basics/e1_basic.py">Basic Graph</a></td>
1616
<td>Basic sum of constant integers</td>
1717
</tr>

0 commit comments

Comments
 (0)