obi-notebook 0.1.dev9__tar.gz → 0.2.dev0__tar.gz
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.
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/PKG-INFO +1 -1
- obi_notebook-0.2.dev0/example.ipynb +147 -0
- obi_notebook-0.2.dev0/src/obi_notebook/get_entities.py +274 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/src/obi_notebook.egg-info/PKG-INFO +1 -1
- obi_notebook-0.1.dev9/example.ipynb +0 -182
- obi_notebook-0.1.dev9/src/obi_notebook/get_entities.py +0 -142
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/.github/workflows/sdist.yml +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/.github/workflows/tox.yml +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/.gitignore +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/CHANGELOG.rst +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/LICENSE.txt +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/README.md +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/demo.gif +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/pyproject.toml +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/setup.cfg +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/src/obi_notebook/__init__.py +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/src/obi_notebook/get_projects.py +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/src/obi_notebook.egg-info/SOURCES.txt +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/src/obi_notebook.egg-info/dependency_links.txt +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/src/obi_notebook.egg-info/requires.txt +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/src/obi_notebook.egg-info/top_level.txt +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/tox.ini +0 -0
- {obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: obi_notebook
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.dev0
|
|
4
4
|
Summary: convenience functions for OBI notebooks
|
|
5
5
|
Author-email: Open Brain Institute <jean-denis.courcol@epfl.ch>
|
|
6
6
|
Maintainer-email: Open Brain Institute <jean-denis.courcol@epfl.ch>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": null,
|
|
6
|
+
"id": "7eb47475-67c6-499a-a2b4-19f8a64974d0",
|
|
7
|
+
"metadata": {},
|
|
8
|
+
"outputs": [],
|
|
9
|
+
"source": [
|
|
10
|
+
"import obi_auth\n",
|
|
11
|
+
"\n",
|
|
12
|
+
"from obi_notebook import get_entities, get_projects"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"cell_type": "markdown",
|
|
17
|
+
"id": "4d984b92-cadd-4b07-9813-b2333272610f",
|
|
18
|
+
"metadata": {},
|
|
19
|
+
"source": [
|
|
20
|
+
"get authentication token for production"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"cell_type": "code",
|
|
25
|
+
"execution_count": null,
|
|
26
|
+
"id": "323cb45c-91e3-451f-8a80-374e61433d31",
|
|
27
|
+
"metadata": {},
|
|
28
|
+
"outputs": [],
|
|
29
|
+
"source": [
|
|
30
|
+
"token = obi_auth.get_token(environment=\"production\")"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"cell_type": "markdown",
|
|
35
|
+
"id": "b60e8643-3ad5-4619-a052-5bd8b64713fd",
|
|
36
|
+
"metadata": {},
|
|
37
|
+
"source": [
|
|
38
|
+
"a widget to select project to use"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"cell_type": "code",
|
|
43
|
+
"execution_count": null,
|
|
44
|
+
"id": "77c47560-1163-4466-83fc-43abc6e0801a",
|
|
45
|
+
"metadata": {},
|
|
46
|
+
"outputs": [],
|
|
47
|
+
"source": [
|
|
48
|
+
"project_context = get_projects.get_projects(token)"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"cell_type": "code",
|
|
53
|
+
"execution_count": null,
|
|
54
|
+
"id": "d538dcac-1bdc-496b-8840-a7b32ea842ed",
|
|
55
|
+
"metadata": {},
|
|
56
|
+
"outputs": [],
|
|
57
|
+
"source": [
|
|
58
|
+
"project_context"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"cell_type": "code",
|
|
63
|
+
"execution_count": null,
|
|
64
|
+
"id": "93029f77-d71f-40f4-8c4d-9f23d2b650bc",
|
|
65
|
+
"metadata": {},
|
|
66
|
+
"outputs": [],
|
|
67
|
+
"source": [
|
|
68
|
+
"circuit_ids = []\n",
|
|
69
|
+
"circuit_ids = get_entities.get_entities(\n",
|
|
70
|
+
" \"circuit\",\n",
|
|
71
|
+
" token,\n",
|
|
72
|
+
" circuit_ids,\n",
|
|
73
|
+
" return_entities=False,\n",
|
|
74
|
+
" multi_select=True,\n",
|
|
75
|
+
" show_pages=True,\n",
|
|
76
|
+
" project_context=project_context,\n",
|
|
77
|
+
" page_size=10,\n",
|
|
78
|
+
" default_scale=\"small\",\n",
|
|
79
|
+
" exclude_scales=[\"single\"],\n",
|
|
80
|
+
" add_columns=[\"subject.name\"],\n",
|
|
81
|
+
")"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"cell_type": "code",
|
|
86
|
+
"execution_count": null,
|
|
87
|
+
"id": "da699ed4-39fd-4602-b54d-58a0b58c2539",
|
|
88
|
+
"metadata": {},
|
|
89
|
+
"outputs": [],
|
|
90
|
+
"source": [
|
|
91
|
+
"circuit_ids"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"cell_type": "code",
|
|
96
|
+
"execution_count": null,
|
|
97
|
+
"id": "a5f5445d-a61a-4fbe-962f-51e701434771",
|
|
98
|
+
"metadata": {},
|
|
99
|
+
"outputs": [],
|
|
100
|
+
"source": [
|
|
101
|
+
"morph_ids = []\n",
|
|
102
|
+
"morph_ids = get_entities.get_entities(\n",
|
|
103
|
+
" \"reconstruction-morphology\",\n",
|
|
104
|
+
" token,\n",
|
|
105
|
+
" morph_ids,\n",
|
|
106
|
+
" return_entities=False,\n",
|
|
107
|
+
" multi_select=True,\n",
|
|
108
|
+
" show_pages=True,\n",
|
|
109
|
+
" project_context=project_context,\n",
|
|
110
|
+
" page_size=100,\n",
|
|
111
|
+
" add_columns=[\"mtypes\"],\n",
|
|
112
|
+
")"
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"cell_type": "code",
|
|
117
|
+
"execution_count": null,
|
|
118
|
+
"id": "ab24b139",
|
|
119
|
+
"metadata": {},
|
|
120
|
+
"outputs": [],
|
|
121
|
+
"source": [
|
|
122
|
+
"morph_ids"
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"metadata": {
|
|
127
|
+
"kernelspec": {
|
|
128
|
+
"display_name": ".venv (3.13.1)",
|
|
129
|
+
"language": "python",
|
|
130
|
+
"name": "python3"
|
|
131
|
+
},
|
|
132
|
+
"language_info": {
|
|
133
|
+
"codemirror_mode": {
|
|
134
|
+
"name": "ipython",
|
|
135
|
+
"version": 3
|
|
136
|
+
},
|
|
137
|
+
"file_extension": ".py",
|
|
138
|
+
"mimetype": "text/x-python",
|
|
139
|
+
"name": "python",
|
|
140
|
+
"nbconvert_exporter": "python",
|
|
141
|
+
"pygments_lexer": "ipython3",
|
|
142
|
+
"version": "3.13.1"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"nbformat": 4,
|
|
146
|
+
"nbformat_minor": 5
|
|
147
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""A table widget to select entities."""
|
|
2
|
+
|
|
3
|
+
import ipywidgets as widgets
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import requests
|
|
7
|
+
from entitysdk import Client, models, types
|
|
8
|
+
from ipydatagrid import DataGrid, TextRenderer
|
|
9
|
+
from IPython.display import clear_output, display
|
|
10
|
+
|
|
11
|
+
LST_MTYPES_ = None
|
|
12
|
+
LST_SPECIES_ = None
|
|
13
|
+
STR_NO_MTYPE = "NONE"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _estimate_column_widths(df, char_width=8, padding=2, max_size=250):
|
|
17
|
+
widths = {}
|
|
18
|
+
for col in df.columns:
|
|
19
|
+
max_len = max(df[col].astype(str).map(len).max(), len(col))
|
|
20
|
+
widths[col] = min(max_size, max_len * char_width + padding)
|
|
21
|
+
return widths
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _resolve_list_to_first_element(obj):
|
|
25
|
+
if isinstance(obj, list):
|
|
26
|
+
if len(obj) > 0:
|
|
27
|
+
return obj[0]["pref_label"]
|
|
28
|
+
return STR_NO_MTYPE
|
|
29
|
+
return obj["pref_label"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
_df_postprocess_funs = {"reconstruction-morphology": {"mtypes": _resolve_list_to_first_element}}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _list_of_existing_mtypes(entity_core_url, token):
|
|
36
|
+
global LST_MTYPES_
|
|
37
|
+
if LST_MTYPES_ is None:
|
|
38
|
+
response = requests.get(
|
|
39
|
+
f"{entity_core_url}/mtype",
|
|
40
|
+
headers={"authorization": f"Bearer {token}"},
|
|
41
|
+
params={"page_size": 1000},
|
|
42
|
+
timeout=30,
|
|
43
|
+
)
|
|
44
|
+
data = response.json()
|
|
45
|
+
df_mtype = pd.json_normalize(data["data"])
|
|
46
|
+
LST_MTYPES_ = list(df_mtype["pref_label"])
|
|
47
|
+
return LST_MTYPES_
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _list_of_existing_species(entity_core_url, token):
|
|
51
|
+
global LST_SPECIES_
|
|
52
|
+
if LST_SPECIES_ is None:
|
|
53
|
+
response = requests.get(
|
|
54
|
+
f"{entity_core_url}/species",
|
|
55
|
+
headers={"authorization": f"Bearer {token}"},
|
|
56
|
+
params={"page_size": 1000},
|
|
57
|
+
timeout=30,
|
|
58
|
+
)
|
|
59
|
+
data = response.json()
|
|
60
|
+
df_species = pd.json_normalize(data["data"])
|
|
61
|
+
LST_SPECIES_ = list(df_species["name"])
|
|
62
|
+
return LST_SPECIES_
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_entities(
|
|
66
|
+
entity_type,
|
|
67
|
+
token,
|
|
68
|
+
result,
|
|
69
|
+
env="production",
|
|
70
|
+
project_context=None,
|
|
71
|
+
return_entities=False,
|
|
72
|
+
multi_select=True,
|
|
73
|
+
page_size=10,
|
|
74
|
+
show_pages=True,
|
|
75
|
+
add_columns=None,
|
|
76
|
+
default_scale=None,
|
|
77
|
+
exclude_scales=None,
|
|
78
|
+
):
|
|
79
|
+
"""Select entities of type entity_type and add them to result.
|
|
80
|
+
|
|
81
|
+
Note: The 'result' parameter is a mutable object (a list) that is modified in-place
|
|
82
|
+
and also returned.
|
|
83
|
+
"""
|
|
84
|
+
if page_size is not None:
|
|
85
|
+
if page_size <= 0:
|
|
86
|
+
raise ValueError("ERROR: Page size must be larger than 0!")
|
|
87
|
+
# TODO: Could add an upper limit as well here
|
|
88
|
+
|
|
89
|
+
if add_columns is None:
|
|
90
|
+
add_columns = []
|
|
91
|
+
if exclude_scales is None:
|
|
92
|
+
exclude_scales = []
|
|
93
|
+
|
|
94
|
+
subdomain = "www" if env == "production" else "staging"
|
|
95
|
+
entity_core_url = f"https://{subdomain}.openbraininstitute.org/api/entitycore"
|
|
96
|
+
|
|
97
|
+
# Widgets
|
|
98
|
+
filters_dict = {}
|
|
99
|
+
if entity_type == "circuit":
|
|
100
|
+
scale_options = [
|
|
101
|
+
_scale.value for _scale in types.CircuitScale if _scale.value not in exclude_scales
|
|
102
|
+
]
|
|
103
|
+
if default_scale is None or default_scale not in scale_options:
|
|
104
|
+
default_scale = scale_options[0]
|
|
105
|
+
scale_filter = widgets.Dropdown(
|
|
106
|
+
options=scale_options,
|
|
107
|
+
value=default_scale,
|
|
108
|
+
description="Scale:",
|
|
109
|
+
)
|
|
110
|
+
filters_dict["scale"] = scale_filter
|
|
111
|
+
elif entity_type == "reconstruction-morphology":
|
|
112
|
+
lst_mtypes = _list_of_existing_mtypes(entity_core_url, token) + [""]
|
|
113
|
+
mtype_filter = widgets.Combobox(
|
|
114
|
+
placeholder="Select M-Type",
|
|
115
|
+
description="M-Type:",
|
|
116
|
+
options=lst_mtypes,
|
|
117
|
+
ensure_option=True,
|
|
118
|
+
)
|
|
119
|
+
filters_dict["mtype__pref_label"] = mtype_filter
|
|
120
|
+
lst_species = _list_of_existing_species(entity_core_url, token) + [""]
|
|
121
|
+
species_filter = widgets.Dropdown(options=lst_species, value="", description="Species:")
|
|
122
|
+
filters_dict["species__name"] = species_filter
|
|
123
|
+
|
|
124
|
+
filters_dict["name__ilike"] = widgets.Text(description="Name:")
|
|
125
|
+
|
|
126
|
+
if show_pages:
|
|
127
|
+
filters_dict["page"] = widgets.Dropdown(
|
|
128
|
+
options=[1],
|
|
129
|
+
value=1,
|
|
130
|
+
description="Results page:",
|
|
131
|
+
disabled=False,
|
|
132
|
+
style={"description_width": "auto"},
|
|
133
|
+
layout=widgets.Layout(width="max-content"),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Output area
|
|
137
|
+
output = widgets.Output()
|
|
138
|
+
|
|
139
|
+
# Fetch and display function
|
|
140
|
+
def fetch_data(filter_values):
|
|
141
|
+
if page_size is None:
|
|
142
|
+
params = {}
|
|
143
|
+
else:
|
|
144
|
+
params = {"page_size": page_size}
|
|
145
|
+
for k, v in filter_values.items():
|
|
146
|
+
if isinstance(v, str):
|
|
147
|
+
if len(v.strip()) == 0:
|
|
148
|
+
continue
|
|
149
|
+
params[k] = v
|
|
150
|
+
|
|
151
|
+
headers = {"authorization": f"Bearer {token}"}
|
|
152
|
+
if project_context:
|
|
153
|
+
headers["virtual-lab-id"] = str(project_context.virtual_lab_id)
|
|
154
|
+
headers["project-id"] = str(project_context.project_id)
|
|
155
|
+
response = requests.get(
|
|
156
|
+
f"{entity_core_url}/{entity_type}",
|
|
157
|
+
headers=headers,
|
|
158
|
+
params=params,
|
|
159
|
+
timeout=30,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
data = response.json()
|
|
164
|
+
df = pd.json_normalize(data["data"])
|
|
165
|
+
pagination = data["pagination"]
|
|
166
|
+
return df, pagination
|
|
167
|
+
except Exception as e:
|
|
168
|
+
print("Error fetching or parsing data:", e)
|
|
169
|
+
return pd.DataFrame(), None
|
|
170
|
+
|
|
171
|
+
grid = None
|
|
172
|
+
|
|
173
|
+
# On change callback
|
|
174
|
+
def on_change(change=None):
|
|
175
|
+
nonlocal result
|
|
176
|
+
nonlocal grid
|
|
177
|
+
with output:
|
|
178
|
+
clear_output()
|
|
179
|
+
filter_values = {k: v.value for k, v in filters_dict.items()}
|
|
180
|
+
df, pagination = fetch_data(filter_values)
|
|
181
|
+
|
|
182
|
+
proper_columns = [
|
|
183
|
+
"id",
|
|
184
|
+
"name",
|
|
185
|
+
"description",
|
|
186
|
+
"brain_region.name",
|
|
187
|
+
"subject.species.name",
|
|
188
|
+
"species.name", # For morphologies
|
|
189
|
+
] + add_columns
|
|
190
|
+
if len(df) == 0:
|
|
191
|
+
if show_pages and filters_dict["page"].value != 1:
|
|
192
|
+
filters_dict["page"].options = [1] # Will update .value as well
|
|
193
|
+
else:
|
|
194
|
+
print("no results")
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
proper_columns = [_col for _col in proper_columns if _col in df.columns]
|
|
198
|
+
df = df[proper_columns].reset_index(drop=True)
|
|
199
|
+
|
|
200
|
+
for colname, fun in _df_postprocess_funs.get(entity_type, {}).items():
|
|
201
|
+
if colname in df.columns:
|
|
202
|
+
df[colname] = df[colname].apply(fun)
|
|
203
|
+
|
|
204
|
+
if show_pages:
|
|
205
|
+
num_pages = np.maximum(
|
|
206
|
+
1, np.ceil(pagination["total_items"] / pagination["page_size"]).astype(int)
|
|
207
|
+
)
|
|
208
|
+
filters_dict["page"].options = range(1, num_pages + 1)
|
|
209
|
+
df.index = df.index + (pagination["page"] - 1) * pagination["page_size"]
|
|
210
|
+
|
|
211
|
+
column_widths = _estimate_column_widths(df)
|
|
212
|
+
grid = DataGrid(
|
|
213
|
+
df,
|
|
214
|
+
layout={"height": "300px"},
|
|
215
|
+
# auto_fit_columns=True,
|
|
216
|
+
auto_fit_params={"area": "all"},
|
|
217
|
+
selection_mode="row", # Enable row selection
|
|
218
|
+
column_widths=column_widths,
|
|
219
|
+
)
|
|
220
|
+
grid.default_renderer = TextRenderer()
|
|
221
|
+
display(grid)
|
|
222
|
+
|
|
223
|
+
def on_selection_change(event, grid=grid):
|
|
224
|
+
with output:
|
|
225
|
+
if not multi_select and len(grid.selections) > 0:
|
|
226
|
+
if (event["new"][-1]["r1"] != event["new"][-1]["r2"]) or len(
|
|
227
|
+
grid.selections
|
|
228
|
+
) > 1: # Multiple rows selected
|
|
229
|
+
if event["new"][-1]["r1"] == event["old"][-1]["r1"]: # r1 unchanged
|
|
230
|
+
new_r = event["new"][-1]["r1"]
|
|
231
|
+
else: # r2 unchanged
|
|
232
|
+
new_r = event["new"][-1]["r2"]
|
|
233
|
+
# Enforce selection of a single row (last one that was selected)
|
|
234
|
+
grid.selections = [
|
|
235
|
+
{
|
|
236
|
+
"r1": new_r,
|
|
237
|
+
"r2": new_r,
|
|
238
|
+
"c1": grid.selections[-1]["c1"],
|
|
239
|
+
"c2": grid.selections[-1]["c2"],
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
result.clear()
|
|
243
|
+
l_ids = set()
|
|
244
|
+
for selection in grid.selections:
|
|
245
|
+
for row in range(selection["r1"], selection["r2"] + 1):
|
|
246
|
+
l_ids.add(df.iloc[row]["id"])
|
|
247
|
+
|
|
248
|
+
if return_entities:
|
|
249
|
+
client = Client(
|
|
250
|
+
api_url=entity_core_url,
|
|
251
|
+
project_context=project_context,
|
|
252
|
+
token_manager=token,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
model_class = getattr(models, entity_type.capitalize())
|
|
256
|
+
retrieved_entities = client.search_entity(
|
|
257
|
+
entity_type=model_class, query={"id__in": list(l_ids)}
|
|
258
|
+
)
|
|
259
|
+
result.extend(retrieved_entities)
|
|
260
|
+
else:
|
|
261
|
+
result.extend(l_ids)
|
|
262
|
+
|
|
263
|
+
grid.observe(on_selection_change, names="selections")
|
|
264
|
+
|
|
265
|
+
for filter_ in filters_dict.values():
|
|
266
|
+
filter_.observe(on_change, names="value")
|
|
267
|
+
|
|
268
|
+
# Display
|
|
269
|
+
display(widgets.HBox(list(filters_dict.values())), output)
|
|
270
|
+
|
|
271
|
+
# Initial load
|
|
272
|
+
on_change()
|
|
273
|
+
|
|
274
|
+
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: obi_notebook
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.dev0
|
|
4
4
|
Summary: convenience functions for OBI notebooks
|
|
5
5
|
Author-email: Open Brain Institute <jean-denis.courcol@epfl.ch>
|
|
6
6
|
Maintainer-email: Open Brain Institute <jean-denis.courcol@epfl.ch>
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cells": [
|
|
3
|
-
{
|
|
4
|
-
"cell_type": "code",
|
|
5
|
-
"execution_count": 1,
|
|
6
|
-
"id": "7eb47475-67c6-499a-a2b4-19f8a64974d0",
|
|
7
|
-
"metadata": {},
|
|
8
|
-
"outputs": [],
|
|
9
|
-
"source": [
|
|
10
|
-
"import obi_auth\n",
|
|
11
|
-
"from obi_notebook import get_projects, get_entities"
|
|
12
|
-
]
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"cell_type": "markdown",
|
|
16
|
-
"id": "4d984b92-cadd-4b07-9813-b2333272610f",
|
|
17
|
-
"metadata": {},
|
|
18
|
-
"source": [
|
|
19
|
-
"get authentication token for production"
|
|
20
|
-
]
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"cell_type": "code",
|
|
24
|
-
"execution_count": 2,
|
|
25
|
-
"id": "323cb45c-91e3-451f-8a80-374e61433d31",
|
|
26
|
-
"metadata": {},
|
|
27
|
-
"outputs": [],
|
|
28
|
-
"source": [
|
|
29
|
-
"token = obi_auth.get_token(environment=\"production\")"
|
|
30
|
-
]
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"cell_type": "markdown",
|
|
34
|
-
"id": "b60e8643-3ad5-4619-a052-5bd8b64713fd",
|
|
35
|
-
"metadata": {},
|
|
36
|
-
"source": [
|
|
37
|
-
"a widget to select project to use"
|
|
38
|
-
]
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"cell_type": "code",
|
|
42
|
-
"execution_count": 3,
|
|
43
|
-
"id": "77c47560-1163-4466-83fc-43abc6e0801a",
|
|
44
|
-
"metadata": {},
|
|
45
|
-
"outputs": [
|
|
46
|
-
{
|
|
47
|
-
"data": {
|
|
48
|
-
"application/vnd.jupyter.widget-view+json": {
|
|
49
|
-
"model_id": "cc2954a000034b5db323c3c5bb43a985",
|
|
50
|
-
"version_major": 2,
|
|
51
|
-
"version_minor": 0
|
|
52
|
-
},
|
|
53
|
-
"text/plain": [
|
|
54
|
-
"Dropdown(description='Select:', options=(('Understanding connectivity', {'id': 'fe443f04-2d56-4c51-8347-8ca5f3…"
|
|
55
|
-
]
|
|
56
|
-
},
|
|
57
|
-
"metadata": {},
|
|
58
|
-
"output_type": "display_data"
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
"source": [
|
|
62
|
-
"project_context = get_projects.get_projects(token)"
|
|
63
|
-
]
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"cell_type": "code",
|
|
67
|
-
"execution_count": 4,
|
|
68
|
-
"id": "d538dcac-1bdc-496b-8840-a7b32ea842ed",
|
|
69
|
-
"metadata": {},
|
|
70
|
-
"outputs": [
|
|
71
|
-
{
|
|
72
|
-
"data": {
|
|
73
|
-
"text/plain": [
|
|
74
|
-
"ProjectContext(project_id=UUID('fe443f04-2d56-4c51-8347-8ca5f3a97ee6'), virtual_lab_id=UUID('cee1a37f-fe43-4589-9197-c46746aff976'), environment=<DeploymentEnvironment.production: 'production'>)"
|
|
75
|
-
]
|
|
76
|
-
},
|
|
77
|
-
"execution_count": 4,
|
|
78
|
-
"metadata": {},
|
|
79
|
-
"output_type": "execute_result"
|
|
80
|
-
}
|
|
81
|
-
],
|
|
82
|
-
"source": [
|
|
83
|
-
"project_context"
|
|
84
|
-
]
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"cell_type": "code",
|
|
88
|
-
"execution_count": 8,
|
|
89
|
-
"id": "93029f77-d71f-40f4-8c4d-9f23d2b650bc",
|
|
90
|
-
"metadata": {},
|
|
91
|
-
"outputs": [
|
|
92
|
-
{
|
|
93
|
-
"data": {
|
|
94
|
-
"application/vnd.jupyter.widget-view+json": {
|
|
95
|
-
"model_id": "371c88af967a42a984fdf8fbf362660f",
|
|
96
|
-
"version_major": 2,
|
|
97
|
-
"version_minor": 0
|
|
98
|
-
},
|
|
99
|
-
"text/plain": [
|
|
100
|
-
"HBox(children=(Dropdown(description='Scale:', options=('single', 'pair', 'small', 'microcircuit', 'region', 's…"
|
|
101
|
-
]
|
|
102
|
-
},
|
|
103
|
-
"metadata": {},
|
|
104
|
-
"output_type": "display_data"
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
"data": {
|
|
108
|
-
"application/vnd.jupyter.widget-view+json": {
|
|
109
|
-
"model_id": "3d13a11549b84a3193044113977ba95b",
|
|
110
|
-
"version_major": 2,
|
|
111
|
-
"version_minor": 0
|
|
112
|
-
},
|
|
113
|
-
"text/plain": [
|
|
114
|
-
"Output()"
|
|
115
|
-
]
|
|
116
|
-
},
|
|
117
|
-
"metadata": {},
|
|
118
|
-
"output_type": "display_data"
|
|
119
|
-
}
|
|
120
|
-
],
|
|
121
|
-
"source": [
|
|
122
|
-
"circuit_ids = []\n",
|
|
123
|
-
"circuit_ids = get_entities.get_entities(\"circuit\", token, circuit_ids,)"
|
|
124
|
-
]
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
"cell_type": "code",
|
|
128
|
-
"execution_count": 9,
|
|
129
|
-
"id": "da699ed4-39fd-4602-b54d-58a0b58c2539",
|
|
130
|
-
"metadata": {},
|
|
131
|
-
"outputs": [
|
|
132
|
-
{
|
|
133
|
-
"data": {
|
|
134
|
-
"text/plain": [
|
|
135
|
-
"['1be432a7-c7fa-422b-bb47-8b189ede363e',\n",
|
|
136
|
-
" 'bf3a620d-5a36-401b-97ac-d27cde014039',\n",
|
|
137
|
-
" 'b907f9a6-04e3-4a51-85f6-25ace2c816dc',\n",
|
|
138
|
-
" 'ba297b44-9340-4646-8684-01ce31ed33f5',\n",
|
|
139
|
-
" '8176966b-cce1-45ef-9642-e87c4436ee7d',\n",
|
|
140
|
-
" 'f5e228e7-1c2b-4fa9-89ae-97026c97f83e']"
|
|
141
|
-
]
|
|
142
|
-
},
|
|
143
|
-
"execution_count": 9,
|
|
144
|
-
"metadata": {},
|
|
145
|
-
"output_type": "execute_result"
|
|
146
|
-
}
|
|
147
|
-
],
|
|
148
|
-
"source": [
|
|
149
|
-
"circuit_ids"
|
|
150
|
-
]
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
"cell_type": "code",
|
|
154
|
-
"execution_count": null,
|
|
155
|
-
"id": "b6b9479f-60b8-4fa1-bd0d-8dbbda0909b8",
|
|
156
|
-
"metadata": {},
|
|
157
|
-
"outputs": [],
|
|
158
|
-
"source": []
|
|
159
|
-
}
|
|
160
|
-
],
|
|
161
|
-
"metadata": {
|
|
162
|
-
"kernelspec": {
|
|
163
|
-
"display_name": "Python (myenv)",
|
|
164
|
-
"language": "python",
|
|
165
|
-
"name": "obi-notebook"
|
|
166
|
-
},
|
|
167
|
-
"language_info": {
|
|
168
|
-
"codemirror_mode": {
|
|
169
|
-
"name": "ipython",
|
|
170
|
-
"version": 3
|
|
171
|
-
},
|
|
172
|
-
"file_extension": ".py",
|
|
173
|
-
"mimetype": "text/x-python",
|
|
174
|
-
"name": "python",
|
|
175
|
-
"nbconvert_exporter": "python",
|
|
176
|
-
"pygments_lexer": "ipython3",
|
|
177
|
-
"version": "3.12.8"
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
"nbformat": 4,
|
|
181
|
-
"nbformat_minor": 5
|
|
182
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
"""A table widget to select entities."""
|
|
2
|
-
|
|
3
|
-
import ipywidgets as widgets
|
|
4
|
-
import pandas as pd
|
|
5
|
-
import requests
|
|
6
|
-
from entitysdk import Client, models
|
|
7
|
-
from ipydatagrid import DataGrid, TextRenderer
|
|
8
|
-
from IPython.display import clear_output, display
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def _estimate_column_widths(df, char_width=8, padding=2, max_size=250):
|
|
12
|
-
widths = {}
|
|
13
|
-
for col in df.columns:
|
|
14
|
-
max_len = max(df[col].astype(str).map(len).max(), len(col))
|
|
15
|
-
widths[col] = min(max_size, max_len * char_width + padding)
|
|
16
|
-
return widths
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def get_entities(
|
|
20
|
-
entity_type, token, result, env="production", project_context=None, return_entities=False
|
|
21
|
-
):
|
|
22
|
-
"""Select entities of type entity_type and add them to result.
|
|
23
|
-
|
|
24
|
-
Note: The 'result' parameter is a mutable object (a list) that is modified in-place
|
|
25
|
-
and also returned.
|
|
26
|
-
"""
|
|
27
|
-
# Widgets
|
|
28
|
-
filters_dict = {}
|
|
29
|
-
if entity_type == "circuit":
|
|
30
|
-
scale_filter = widgets.Dropdown(
|
|
31
|
-
options=["single", "pair", "small", "microcircuit", "region", "system", "whole"],
|
|
32
|
-
description="Scale:",
|
|
33
|
-
)
|
|
34
|
-
filters_dict["scale"] = scale_filter
|
|
35
|
-
|
|
36
|
-
filters_dict["name"] = widgets.Text(description="Name:")
|
|
37
|
-
|
|
38
|
-
# Output area
|
|
39
|
-
output = widgets.Output()
|
|
40
|
-
|
|
41
|
-
subdomain = "www" if env == "production" else "staging"
|
|
42
|
-
entity_core_url = f"https://{subdomain}.openbraininstitute.org/api/entitycore"
|
|
43
|
-
|
|
44
|
-
# Fetch and display function
|
|
45
|
-
def fetch_data(filter_values):
|
|
46
|
-
params = {"page_size": 10}
|
|
47
|
-
for k, v in filter_values.items():
|
|
48
|
-
if k == "name":
|
|
49
|
-
params["name__ilike"] = v
|
|
50
|
-
else:
|
|
51
|
-
params[k] = v
|
|
52
|
-
|
|
53
|
-
headers = {"authorization": f"Bearer {token}"}
|
|
54
|
-
if project_context:
|
|
55
|
-
headers["virtual-lab-id"] = project_context.virtual_lab_id
|
|
56
|
-
headers["project-id"] = project_context.project_id
|
|
57
|
-
response = requests.get(
|
|
58
|
-
f"{entity_core_url}/{entity_type}",
|
|
59
|
-
headers=headers,
|
|
60
|
-
params=params,
|
|
61
|
-
timeout=30,
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
try:
|
|
65
|
-
data = response.json()
|
|
66
|
-
df = pd.json_normalize(data["data"])
|
|
67
|
-
return df
|
|
68
|
-
except Exception as e:
|
|
69
|
-
print("Error fetching or parsing data:", e)
|
|
70
|
-
return pd.DataFrame()
|
|
71
|
-
|
|
72
|
-
grid = None
|
|
73
|
-
|
|
74
|
-
# On change callback
|
|
75
|
-
def on_change(change=None):
|
|
76
|
-
nonlocal result
|
|
77
|
-
nonlocal grid
|
|
78
|
-
with output:
|
|
79
|
-
clear_output()
|
|
80
|
-
filter_values = {k: v.value for k, v in filters_dict.items()}
|
|
81
|
-
df = fetch_data(filter_values)
|
|
82
|
-
|
|
83
|
-
proper_columns = [
|
|
84
|
-
"id",
|
|
85
|
-
"name",
|
|
86
|
-
"description",
|
|
87
|
-
"brain_region.name",
|
|
88
|
-
"subject.species.name",
|
|
89
|
-
]
|
|
90
|
-
if len(df) == 0:
|
|
91
|
-
print("no results")
|
|
92
|
-
return
|
|
93
|
-
|
|
94
|
-
df = df[proper_columns].reset_index(drop=True)
|
|
95
|
-
column_widths = _estimate_column_widths(df)
|
|
96
|
-
grid = DataGrid(
|
|
97
|
-
df,
|
|
98
|
-
layout={"height": "300px"},
|
|
99
|
-
# auto_fit_columns=True,
|
|
100
|
-
auto_fit_params={"area": "all"},
|
|
101
|
-
selection_mode="row", # Enable row selection
|
|
102
|
-
selection_behavior="multi",
|
|
103
|
-
column_widths=column_widths,
|
|
104
|
-
)
|
|
105
|
-
grid.default_renderer = TextRenderer()
|
|
106
|
-
display(grid)
|
|
107
|
-
|
|
108
|
-
def on_selection_change(event, grid=grid):
|
|
109
|
-
with output:
|
|
110
|
-
result.clear()
|
|
111
|
-
l_ids = set()
|
|
112
|
-
for selection in grid.selections:
|
|
113
|
-
for row in range(selection["r1"], selection["r2"] + 1):
|
|
114
|
-
l_ids.add(df.iloc[row]["id"])
|
|
115
|
-
|
|
116
|
-
if return_entities:
|
|
117
|
-
client = Client(
|
|
118
|
-
api_url=entity_core_url,
|
|
119
|
-
project_context=project_context,
|
|
120
|
-
token_manager=token,
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
model_class = getattr(models, entity_type.capitalize())
|
|
124
|
-
retrieved_entities = client.search_entity(
|
|
125
|
-
entity_type=model_class, query={"id__in": list(l_ids)}
|
|
126
|
-
)
|
|
127
|
-
result.extend(retrieved_entities)
|
|
128
|
-
else:
|
|
129
|
-
result.extend(l_ids)
|
|
130
|
-
|
|
131
|
-
grid.observe(on_selection_change, names="selections")
|
|
132
|
-
|
|
133
|
-
for filter_ in filters_dict.values():
|
|
134
|
-
filter_.observe(on_change, names="value")
|
|
135
|
-
|
|
136
|
-
# Display
|
|
137
|
-
display(widgets.HBox(list(filters_dict.values())), output)
|
|
138
|
-
|
|
139
|
-
# Initial load
|
|
140
|
-
on_change()
|
|
141
|
-
|
|
142
|
-
return result
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{obi_notebook-0.1.dev9 → obi_notebook-0.2.dev0}/src/obi_notebook.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|