labdata 0.1.6__tar.gz → 0.1.8__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.
Files changed (59) hide show
  1. {labdata-0.1.6 → labdata-0.1.8}/PKG-INFO +1 -1
  2. labdata-0.1.8/labdata/__init__.py +15 -0
  3. {labdata-0.1.6 → labdata-0.1.8}/labdata/cli.py +5 -2
  4. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/utils.py +2 -1
  5. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/decisiontask.py +87 -3
  6. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/index.py +20 -1
  7. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/segmentation.py +4 -3
  8. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/sorting.py +211 -127
  9. labdata-0.1.8/labdata/dashboard/video.py +554 -0
  10. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/ephys.py +10 -22
  11. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/onephoton.py +2 -2
  12. {labdata-0.1.6 → labdata-0.1.8}/labdata.egg-info/PKG-INFO +1 -1
  13. labdata-0.1.6/labdata/__init__.py +0 -11
  14. labdata-0.1.6/labdata/dashboard/video.py +0 -119
  15. {labdata-0.1.6 → labdata-0.1.8}/LICENSE +0 -0
  16. {labdata-0.1.6 → labdata-0.1.8}/README.md +0 -0
  17. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/__init__.py +0 -0
  18. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/caiman.py +0 -0
  19. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/ec2.py +0 -0
  20. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/ephys.py +0 -0
  21. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/pose.py +0 -0
  22. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/schedulers.py +0 -0
  23. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/singularity.py +0 -0
  24. {labdata-0.1.6 → labdata-0.1.8}/labdata/compute/suite2p.py +0 -0
  25. {labdata-0.1.6 → labdata-0.1.8}/labdata/copy.py +0 -0
  26. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/__init__.py +0 -0
  27. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/admin.py +0 -0
  28. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/compute.py +0 -0
  29. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/console.py +0 -0
  30. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/explorer/__init__.py +0 -0
  31. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/explorer/cellsegmentation.py +0 -0
  32. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/explorer/spikesorting.py +0 -0
  33. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/notes.py +0 -0
  34. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/procedures.py +0 -0
  35. {labdata-0.1.6 → labdata-0.1.8}/labdata/dashboard/schema_graph.py +0 -0
  36. {labdata-0.1.6 → labdata-0.1.8}/labdata/export.py +0 -0
  37. {labdata-0.1.6 → labdata-0.1.8}/labdata/rules/__init__.py +0 -0
  38. {labdata-0.1.6 → labdata-0.1.8}/labdata/rules/ephys.py +0 -0
  39. {labdata-0.1.6 → labdata-0.1.8}/labdata/rules/imaging.py +0 -0
  40. {labdata-0.1.6 → labdata-0.1.8}/labdata/rules/utils.py +0 -0
  41. {labdata-0.1.6 → labdata-0.1.8}/labdata/s3.py +0 -0
  42. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/__init__.py +0 -0
  43. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/general.py +0 -0
  44. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/histology.py +0 -0
  45. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/procedures.py +0 -0
  46. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/tasks.py +0 -0
  47. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/twophoton.py +0 -0
  48. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/utils.py +0 -0
  49. {labdata-0.1.6 → labdata-0.1.8}/labdata/schema/video.py +0 -0
  50. {labdata-0.1.6 → labdata-0.1.8}/labdata/stacks.py +0 -0
  51. {labdata-0.1.6 → labdata-0.1.8}/labdata/utils.py +0 -0
  52. {labdata-0.1.6 → labdata-0.1.8}/labdata/widgets.py +0 -0
  53. {labdata-0.1.6 → labdata-0.1.8}/labdata.egg-info/SOURCES.txt +0 -0
  54. {labdata-0.1.6 → labdata-0.1.8}/labdata.egg-info/dependency_links.txt +0 -0
  55. {labdata-0.1.6 → labdata-0.1.8}/labdata.egg-info/entry_points.txt +0 -0
  56. {labdata-0.1.6 → labdata-0.1.8}/labdata.egg-info/requires.txt +0 -0
  57. {labdata-0.1.6 → labdata-0.1.8}/labdata.egg-info/top_level.txt +0 -0
  58. {labdata-0.1.6 → labdata-0.1.8}/pyproject.toml +0 -0
  59. {labdata-0.1.6 → labdata-0.1.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labdata
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: Package to manage data in experimental neuroscience labs
5
5
  Author-email: Joao Couto <jpcouto@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/jcouto/labdata
@@ -0,0 +1,15 @@
1
+ VERSION = "0.1.8"
2
+
3
+ from .utils import *
4
+ from .copy import copy_to_upload_server
5
+ from .s3 import copy_to_s3
6
+
7
+ _project = (prefs.get('database', {}) or {}).get('database.project', None)
8
+ if 'LABDATA_DATABASE_PROJECT' in os.environ.keys():
9
+ if len(os.environ['LABDATA_DATABASE_PROJECT']):
10
+ _project = os.environ['LABDATA_DATABASE_PROJECT']
11
+
12
+ plugins = load_plugins(project=_project)
13
+ for _name, _mod in plugins.items():
14
+ globals()[_name] = _mod
15
+
@@ -91,15 +91,18 @@ Server commands (don't run on experimental computers):
91
91
  open_browser = True)
92
92
  else:
93
93
  import subprocess as sub
94
+ import os
94
95
  main_webpage_path = Path(__file__).parent / 'dashboard' / 'index.py'
95
- command = f'streamlit run {str(main_webpage_path)}'
96
+ command = f'streamlit run --server.showEmailPrompt=False --browser.gatherUsageStats=False {str(main_webpage_path)}'
97
+ if 'LABDATA_VERBOSE' in os.environ.keys():
98
+ print(f'Running streamlit dashboard: {command}')
96
99
  if not args.project is None:
97
100
  command += f' project={args.project}'
98
101
  if args.console:
99
102
  command += ' console=1'
100
103
  if args.admin:
101
104
  command += ' admin=1'
102
- sub.run(command.split(' '))
105
+ sub.run(command,shell=True)
103
106
 
104
107
  def subjects(self):
105
108
  parser = argparse.ArgumentParser(
@@ -279,7 +279,8 @@ class BaseCompute():
279
279
  self.dataset_key = None
280
280
  self.is_container = False
281
281
  if allow_s3 is None:
282
- self.allow_s3 = prefs['allow_s3_download']
282
+ allow_s3 = prefs['allow_s3_download']
283
+ self.allow_s3 = allow_s3
283
284
  if 'LABDATA_CONTAINER' in os.environ.keys():
284
285
  # then it is running inside a container
285
286
  self.is_container = True
@@ -105,7 +105,8 @@ def decisionmaking_tab(schema = None):
105
105
  date_str = end_date.strftime('%Y-%m-%d')
106
106
  date_filter = (f'session_datetime > (DATE("{date_str}") - INTERVAL 3 MONTH) '
107
107
  f'AND DATE(session_datetime) <= "{date_str}"')
108
- summarysource = pd.DataFrame((schema.Session*schema.DecisionTask.TrialSet() & keys & 'trialset_description NOT LIKE "%opto%"'
108
+ summarysource = pd.DataFrame((schema.Session * schema.DecisionTask.TrialSet() * schema.Subject() & keys
109
+ & 'trialset_description NOT LIKE "%opto%"'
109
110
  & date_filter).fetch())
110
111
  if not len(summarysource):
111
112
  return []
@@ -113,14 +114,66 @@ def decisionmaking_tab(schema = None):
113
114
  lambda x: x.strftime("%y-%m-%d"))
114
115
  return summarysource
115
116
 
116
- ctrl_col, date_col, _ = st.columns([1, 2, 6])
117
+ @st.cache_data
118
+ def get_session_setups(selected_indices, end_date):
119
+ keys = [dict(subject_name=i) for i in selected_indices]
120
+ date_str = end_date.strftime('%Y-%m-%d')
121
+ date_filter = (f'session_datetime > (DATE("{date_str}") - INTERVAL 3 MONTH) '
122
+ f'AND DATE(session_datetime) <= "{date_str}"')
123
+ try:
124
+ df = pd.DataFrame(
125
+ (schema.Session() * schema.Dataset() & keys & date_filter).fetch(
126
+ 'subject_name', 'session_name', 'setup_name', as_dict=True)
127
+ )
128
+ if len(df):
129
+ return (df.dropna(subset=['setup_name'])
130
+ .drop_duplicates(['subject_name', 'session_name'])
131
+ [['subject_name', 'session_name', 'setup_name']])
132
+ except Exception:
133
+ pass
134
+ return pd.DataFrame(columns=['subject_name', 'session_name', 'setup_name'])
135
+
136
+ @st.cache_data
137
+ def get_sort_data(subject_names):
138
+ keys = [dict(subject_name=s) for s in subject_names]
139
+ try:
140
+ subjects_df = pd.DataFrame(
141
+ (schema.Subject() & keys).fetch('subject_name', 'subject_dob', 'strain_name', as_dict=True)
142
+ )
143
+ except Exception:
144
+ subjects_df = pd.DataFrame({'subject_name': list(subject_names)})
145
+ try:
146
+ last_setups = pd.DataFrame(
147
+ (schema.Session() * schema.Dataset() & keys).fetch(
148
+ 'subject_name', 'session_datetime', 'setup_name', as_dict=True)
149
+ )
150
+ if len(last_setups):
151
+ last_setups = (last_setups.sort_values('session_datetime')
152
+ .drop_duplicates('subject_name', keep='last')
153
+ [['subject_name', 'setup_name']])
154
+ subjects_df = subjects_df.merge(last_setups, on='subject_name', how='left')
155
+ else:
156
+ subjects_df['setup_name'] = None
157
+ except Exception:
158
+ subjects_df['setup_name'] = None
159
+ return subjects_df
160
+
161
+ ctrl_col, date_col, sort_col, _ = st.columns([1, 2, 2, 5])
117
162
  with ctrl_col:
118
163
  if st.button('Refresh', icon=':material/refresh:'):
119
164
  get_max_date.clear()
120
165
  get_subjects_last_ran.clear()
121
166
  get_summary_data.clear()
167
+ get_sort_data.clear()
168
+ get_session_setups.clear()
122
169
  with date_col:
123
170
  end_date = st.date_input('End date', value=get_max_date(), label_visibility='collapsed')
171
+ with sort_col:
172
+ lbl, sel = st.columns([1, 2])
173
+ lbl.markdown('Sort by')
174
+ with sel:
175
+ sort_by = st.selectbox('Sort by', ['Name', 'Age', 'Strain', 'Last setup'],
176
+ label_visibility='collapsed')
124
177
 
125
178
  if end_date is None:
126
179
  return
@@ -137,6 +190,33 @@ def decisionmaking_tab(schema = None):
137
190
  st.write('Could not find sessions for the specified dates..')
138
191
  return
139
192
 
193
+ # Merge per-session setup into summary
194
+ setup_df = get_session_setups(tuple(selected_indices), end_date)
195
+ if len(setup_df):
196
+ summarysource = summarysource.merge(setup_df, on=['subject_name', 'session_name'], how='left')
197
+
198
+ # Compute Y-axis sort order
199
+ if sort_by == 'Last setup' and 'setup_name' in summarysource.columns:
200
+ last_setup = (summarysource[['subject_name', 'session_datetime', 'setup_name']]
201
+ .dropna(subset=['setup_name'])
202
+ .sort_values('session_datetime')
203
+ .drop_duplicates('subject_name', keep='last'))
204
+ ordered = last_setup.sort_values('setup_name')['subject_name'].tolist()
205
+ sort_order = ordered + [s for s in selected_indices if s not in ordered]
206
+ else:
207
+ sort_field_map = {
208
+ 'Name': ('subject_name', True),
209
+ 'Age': ('subject_dob', True),
210
+ 'Strain': ('strain_name', True),
211
+ }
212
+ sort_field, ascending = sort_field_map.get(sort_by, ('subject_name', True))
213
+ sort_df = get_sort_data(tuple(selected_indices))
214
+ if sort_field in sort_df.columns:
215
+ sort_order = (sort_df[sort_df['subject_name'].isin(selected_indices)]
216
+ .sort_values(sort_field, ascending=ascending)['subject_name'].tolist())
217
+ else:
218
+ sort_order = sorted(selected_indices)
219
+
140
220
  # Build heatmap chart
141
221
  has_perf = 'performance_easy' in summarysource.columns and 'performance' in summarysource.columns
142
222
  point_selector = alt.selection_point(on="click")
@@ -144,6 +224,10 @@ def decisionmaking_tab(schema = None):
144
224
  scale=alt.Scale(scheme='spectral', reverse=True, domain=[0.5,1], clamp=True)).title('Perf easy')
145
225
  if has_perf else alt.value('#4477AA'))
146
226
  tooltip_fields = ['subject_name:N', 'session_name:N', 'session_datetime:T']
227
+ if 'strain_name' in summarysource.columns:
228
+ tooltip_fields.append('strain_name:N')
229
+ if 'setup_name' in summarysource.columns:
230
+ tooltip_fields.append('setup_name:N')
147
231
  if has_perf:
148
232
  tooltip_fields += ['performance_easy:Q', 'performance:Q']
149
233
  if 'n_trials' in summarysource.columns:
@@ -151,7 +235,7 @@ def decisionmaking_tab(schema = None):
151
235
  chart = alt.Chart(summarysource).mark_rect().encode(
152
236
  x=alt.X('session_datetime:T', timeUnit='yearmonthdate',
153
237
  scale=alt.Scale(nice={'interval': 'day', 'step': 1})).axis(labelAngle=45, format='%Y-%m-%d').title('Date'),
154
- y=alt.Y('subject_name:O').title('Subject name'),
238
+ y=alt.Y('subject_name:O', sort=sort_order).title('Subject name'),
155
239
  color=color_enc,
156
240
  tooltip=tooltip_fields,
157
241
  ).properties(title='Behavioral performance').add_params(point_selector)
@@ -289,4 +289,23 @@ tab_name = st.sidebar.radio(
289
289
  index=_tab_options.index(_tab_default),
290
290
  key='sidebar_tab')
291
291
 
292
- page_names_to_funcs[tab_name]()
292
+ try:
293
+ page_names_to_funcs[tab_name]()
294
+ except Exception as _e:
295
+ import pymysql, datajoint as dj
296
+ def _is_mysql_err(exc):
297
+ return isinstance(exc, pymysql.err.Error) or (
298
+ hasattr(exc, '__cause__') and isinstance(exc.__cause__, pymysql.err.Error))
299
+ if _is_mysql_err(_e):
300
+ try:
301
+ conn = dj.conn()
302
+ if conn._conn is not None:
303
+ try:
304
+ conn._conn.close()
305
+ except Exception:
306
+ pass
307
+ conn.connect()
308
+ except Exception:
309
+ pass
310
+ st.rerun()
311
+ raise
@@ -584,6 +584,7 @@ def segmentation_tab(schema=None):
584
584
  return cell_list.index(tup)
585
585
  return -1
586
586
 
587
+ _btn_clicked = False
587
588
  nav_prev, nav_next, *_ = st.columns([1, 1, 6])
588
589
  if nav_prev.button('**p**rev cell', key='seg_prev_cell', disabled=not cell_list):
589
590
  idx = _nav_cursor()
@@ -591,14 +592,14 @@ def segmentation_tab(schema=None):
591
592
  pn, rn = cell_list[idx]
592
593
  st.session_state['seg_picked_rois'] = [{'plane_num': pn, 'roi_num': rn}]
593
594
  get_traces.clear()
594
- st.rerun()
595
+ _btn_clicked = True
595
596
  if nav_next.button('**n**ext cell', key='seg_next_cell', disabled=not cell_list):
596
597
  idx = _nav_cursor()
597
598
  idx = (idx + 1) % len(cell_list)
598
599
  pn, rn = cell_list[idx]
599
600
  st.session_state['seg_picked_rois'] = [{'plane_num': pn, 'roi_num': rn}]
600
601
  get_traces.clear()
601
- st.rerun()
602
+ _btn_clicked = True
602
603
 
603
604
  # Keyboard: w or p/s or n = prev/next cell, d/a = time forward/back
604
605
  _kb_html = """
@@ -714,7 +715,7 @@ def segmentation_tab(schema=None):
714
715
  width='content',
715
716
  )
716
717
 
717
- if scatter is not None:
718
+ if scatter is not None and not _btn_clicked:
718
719
  click_key = f'seg_last_click_{pname}_{pnum}'
719
720
  current_click = (event.selection or {}).get('roi_click', [])
720
721
  if current_click and current_click != st.session_state.get(click_key, []):