Skip to content

Commit 3155cd0

Browse files
authored
Add Jupyter Voilá example (#154)
1 parent 5681bbb commit 3155cd0

File tree

5 files changed

+183
-0
lines changed

5 files changed

+183
-0
lines changed

.github/workflows/extensions.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
quarto-script-r: extensions/quarto-script-r/**
5252
top-5-income-share-streamlit: extensions/top-5-income-share-streamlit/**
5353
usage-metrics-dashboard: extensions/usage-metrics-dashboard/**
54+
voila-example: extensions/voila-example/**
5455
5556
# Runs for each extension that has changed from `simple-extension-changes`
5657
# Lints and packages in preparation for tests and and release.

extensions/voila-example/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Secure Hashes with Voilà
2+
3+
## About this example
4+
Voilà allows you to convert a Jupyter Notebook into an interactive dashboard that allows you to share your work with others.
5+
6+
## Learn more
7+
* [Voilà](https://voila.readthedocs.io/en/stable/index.html)
8+
* [Voilà Gallery](https://voila-gallery.org/)
9+
10+
## Requirements
11+
* Python version 3.11.3 or higher
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import ipywidgets as widgets\n",
10+
"import hashlib, glob\n",
11+
"from IPython.display import display"
12+
]
13+
},
14+
{
15+
"attachments": {},
16+
"cell_type": "markdown",
17+
"metadata": {},
18+
"source": [
19+
"# hash\n",
20+
"\n",
21+
"How can you check that a copy of a file you send to someone isn't accidentally modified in transit?\n",
22+
"\n",
23+
"[Secure hashes](https://en.wikipedia.org/wiki/Cryptographic_hash_function) are one way to perform such a check.\n",
24+
"\n",
25+
"1. Choose a hashing algorithm.\n",
26+
"2. Upload a small file to the dashboard.\n",
27+
"3. Generate a hash.\n",
28+
"4. [Send](https://wormhole.app/) the file to a friend, and have them repeat steps 1, 2, and 3. (Email works too.)\n",
29+
"5. Compare hashes!"
30+
]
31+
},
32+
{
33+
"cell_type": "code",
34+
"execution_count": null,
35+
"metadata": {},
36+
"outputs": [],
37+
"source": [
38+
"algo = widgets.ToggleButtons(\n",
39+
" options=[\"md5\", \"sha1\", \"sha256\"],\n",
40+
" description=\"Select a hashing algorithm:\"\n",
41+
")\n",
42+
"\n",
43+
"uploader = widgets.FileUpload()\n",
44+
"select_file = widgets.Select(options=\"\", description =\"Select a file to hash:\")\n",
45+
"files = glob.glob(\"\")\n",
46+
"current_file = None\n",
47+
"out = widgets.Output()\n",
48+
"button = widgets.Button(description = \"Generate file hash:\")"
49+
]
50+
},
51+
{
52+
"cell_type": "code",
53+
"execution_count": null,
54+
"metadata": {},
55+
"outputs": [],
56+
"source": [
57+
"# adapted from\n",
58+
"# https://stackoverflow.com/a/44873382\n",
59+
"# in python 3.11+ you can use hashlib.file_hash() instead \n",
60+
"def shasum(filename, digest):\n",
61+
" h = hashlib.new(digest)\n",
62+
" b = bytearray(128*1024)\n",
63+
" mv = memoryview(b)\n",
64+
" with open(filename, 'rb', buffering=0) as f:\n",
65+
" for n in iter(lambda : f.readinto(mv), 0):\n",
66+
" h.update(mv[:n])\n",
67+
" print(f\"{digest}: {h.hexdigest()}\")\n",
68+
" return f\"{digest}: {h.hexdigest()}\""
69+
]
70+
},
71+
{
72+
"cell_type": "code",
73+
"execution_count": null,
74+
"metadata": {},
75+
"outputs": [],
76+
"source": [
77+
"# adpated from\n",
78+
"# https://github.com/fomightez/3Dscatter_plot-binder\n",
79+
"@out.capture(clear_output=True, wait=True)\n",
80+
"def on_button_clicked(b):\n",
81+
" global uploader, algo, select_file, current_file\n",
82+
" current_file = select_file.value\n",
83+
" filehash = shasum(current_file, algo.value)\n",
84+
" vbox.children = [algo, uploader, select_file, button, out]\n",
85+
"\n",
86+
"def on_file_upload(change):\n",
87+
" global uploader, select_file, current_file\n",
88+
" for details_dict in change[\"new\"]:\n",
89+
" files.append(details_dict[\"name\"])\n",
90+
"\n",
91+
" with open(details_dict[\"name\"], \"wb\") as f:\n",
92+
" f.write(details_dict[\"content\"])\n",
93+
" \n",
94+
" current_file = select_file.value\n",
95+
" select_file.options = tuple(files)\n",
96+
" select_file.value\n",
97+
"\n",
98+
"button.on_click(on_button_clicked)\n",
99+
"vbox = widgets.VBox([algo, uploader, select_file, button])"
100+
]
101+
},
102+
{
103+
"cell_type": "code",
104+
"execution_count": null,
105+
"metadata": {},
106+
"outputs": [],
107+
"source": [
108+
"uploader.observe(on_file_upload, \"value\")\n",
109+
"vbox"
110+
]
111+
}
112+
],
113+
"metadata": {
114+
"kernelspec": {
115+
"display_name": ".venv",
116+
"language": "python",
117+
"name": "python3"
118+
},
119+
"language_info": {
120+
"name": "python",
121+
"version": "3.8.6"
122+
},
123+
"orig_nbformat": 4,
124+
"vscode": {
125+
"interpreter": {
126+
"hash": "58b987bbdc7808225bbe23a8c8714df9864b164f01439c091b16b96ae31865d2"
127+
}
128+
}
129+
},
130+
"nbformat": 4,
131+
"nbformat_minor": 2
132+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"version": 1,
3+
"locale": "en_US.UTF-8",
4+
"metadata": {
5+
"appmode": "jupyter-static",
6+
"entrypoint": "hash.ipynb"
7+
},
8+
"python": {
9+
"version": "3.11.3",
10+
"package_manager": {
11+
"name": "pip",
12+
"package_file": "requirements.txt"
13+
}
14+
},
15+
"extension": {
16+
"name": "voila-example",
17+
"title": "Secure Hashes with Voilà",
18+
"description": "Voilà allows you to convert a Jupyter Notebook into an interactive dashboard that allows you to share your work with others.",
19+
"homepage": "https://github.com/posit-dev/connect-extensions/tree/main/extensions/voila-example",
20+
"category": "example",
21+
"minimumConnectVersion": "2025.04.0",
22+
"version": "1.0.0"
23+
},
24+
"environment": {
25+
"python": {
26+
"requires": "~=3.11.3"
27+
}
28+
},
29+
"files": {
30+
"hash.ipynb": {
31+
"checksum": "72f44e0fa678573992c7cf553ecb9a6f"
32+
},
33+
"requirements.txt": {
34+
"checksum": "1cb2feddc42a60989c117cc1559be098"
35+
}
36+
}
37+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
voila==0.5.6
2+
ipywidgets==8.1.2

0 commit comments

Comments
 (0)