-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathFLASHDeconvLayoutManager.py
More file actions
348 lines (294 loc) · 13.2 KB
/
FLASHDeconvLayoutManager.py
File metadata and controls
348 lines (294 loc) · 13.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
import json
import streamlit as st
from src.common.common import page_setup, v_space, save_params
from src.workflow.FileManager import FileManager
from pathlib import Path
COMPONENT_OPTIONS=[
'MS1 raw heatmap',
'MS2 raw heatmap',
'MS1 deconvolved heatmap',
'MS2 deconvolved heatmap',
'Scan table',
'Deconvolved spectrum (Scan table needed)',
'Raw spectrum (Scan table needed)',
'Mass table (Scan table needed)',
'Feature table',
'3D S/N plot (Mass table needed)',
'Score Distribution Plot',
'TIC Chromatogram',
# "Sequence view" and "Internal fragment map" is added when "input_sequence" is submitted
]
COMPONENT_NAMES=[
'ms1_raw_heatmap',
'ms2_raw_heatmap',
'ms1_deconv_heat_map',
'ms2_deconv_heat_map',
'scan_table',
'deconv_spectrum',
'anno_spectrum',
'mass_table',
'feature_table',
'3D_SN_plot',
'fdr_plot',
'tic_chromatogram',
# "sequence view" and "internal fragment map" added when "input_sequence" is submitted
]
# Setup cache access
file_manager = FileManager(
st.session_state["workspace"],
Path(st.session_state['workspace'], 'flashdeconv', 'cache')
)
def get_sequence():
# Check if layout has been set
if not file_manager.result_exists('sequence', 'sequence'):
return None
# fetch layout from cache
sequence = file_manager.get_results('sequence', 'sequence')['sequence']
return sequence['input_sequence'], sequence['fixed_mod_cysteine'], sequence['fixed_mod_methionine']
def set_layout(layout, side_by_side=False):
file_manager.store_data('layout', 'layout',
{
'layout': layout,
'side_by_side': side_by_side
}
)
def get_layout():
# Check if layout has been set
if not file_manager.result_exists('layout', 'layout'):
return None
# fetch layout from cache
layout = file_manager.get_results('layout', 'layout')['layout']
return layout['layout'], layout['side_by_side']
def resetSettingsToDefault(num_of_exp=1):
st.session_state["layout_setting"] = [[['']]] # 1D: experiment, 2D: row, 3D: column, element=component name
st.session_state["num_of_experiment_to_show"] = num_of_exp
for index in range(1, num_of_exp):
st.session_state.layout_setting.append([['']])
if file_manager.result_exists('layout', 'layout'):
file_manager.remove_results('layout')
st.session_state["edit_mode"] = True
def containerForNewComponent(exp_index, row_index, col_index):
def isThisComponentUnique(new_component_option):
if any(col for row in st.session_state.layout_setting[exp_index] for col in row if col==new_component_option):
st.session_state["component_error_message"] = 'Duplicated component!'
return False
else:
return True
def addNewComponent():
new_component_option = 'SelectNewComponent%d%d%d'%(exp_index, row_index, col_index)
if isThisComponentUnique(st.session_state[new_component_option]):
st.session_state.layout_setting[exp_index][row_index][col_index] = st.session_state[new_component_option]
# new component
st.selectbox("New component to add", ['Select...'] + COMPONENT_OPTIONS,
key='SelectNewComponent%d%d%d'%(exp_index, row_index, col_index),
on_change=addNewComponent,
placeholder='Select...',
)
def layoutEditorPerExperiment(exp_index):
layout_info = st.session_state.layout_setting[exp_index]
for row_index, row in enumerate(layout_info):
st_cols = st.columns(len(row)+1 if len(row)<3 else len(row))
for col_index, col in enumerate(row):
if not col: # if empty, add newComponent container
with st_cols[col_index].container():
containerForNewComponent(exp_index, row_index, col_index)
else:
with st_cols[col_index]:
c1, c2 = st.columns([5, 1])
c1.info(col)
if c2.button("x", key='DelButton%d%d%d'%(exp_index, row_index, col_index), type='primary'):
layout_info[row_index].pop(col_index)
st.rerun()
# new column button
if len(row) < 3: # limit for #column is 3
if st_cols[-1].button("***+***", key='NewColumnButton%d%d'%(exp_index, row_index)):
layout_info[row_index].append('')
st.rerun()
# new row button
if st.button("***+***", key='NewRowButton%d'%exp_index):
layout_info.append([''])
st.rerun()
def validateSubmittedLayout(input_layout=None):
layout_setting = input_layout if input_layout is not None else st.session_state.layout_setting
# check if submitted layout is empty
if not any(col for exp in layout_setting for row in exp for col in row if col):
return 'Empty input'
# check if submitted layout contains "needed" components
for exp in layout_setting:
submitted_components = [col for row in exp for col in row if col]
required_components = [comp.split('(')[1].split('needed')[0].rstrip() for comp in submitted_components if 'needed' in comp]
if required_components:
for required in required_components:
required_exist = False
for submitted in submitted_components:
if submitted.startswith(required):
required_exist = True
if not required_exist:
return 'Required component is missing'
return ''
def getTrimmedLayoutSetting():
trimmed_layout_setting = []
for exp in st.session_state.layout_setting:
rows = []
for row in exp:
cols = []
for col in row:
if col:
cols.append(COMPONENT_NAMES[COMPONENT_OPTIONS.index(col)])
if cols:
rows.append(cols)
if rows:
trimmed_layout_setting.append(rows)
return trimmed_layout_setting
def getExpandedLayoutSetting(trimmed_layout_setting):
expanded_layout_setting = []
for exp in trimmed_layout_setting:
rows = []
for row in exp:
cols = []
for col in row:
if col:
cols.append(COMPONENT_OPTIONS[COMPONENT_NAMES.index(col)])
if cols:
rows.append(cols)
if rows:
expanded_layout_setting.append(rows)
return expanded_layout_setting
def handleEditAndSaveButtons():
# if "Edit" button was clicked,
if "edit_btn_clicked" in st.session_state and st.session_state["edit_btn_clicked"]:
st.session_state["edit_mode"] = True
# reset variables based on saved layout setting
st.session_state["num_of_experiment_to_show"] = len(get_layout()[0]) if get_layout() is not None else 1
st.session_state["layout_setting"] = [[[COMPONENT_OPTIONS[COMPONENT_NAMES.index(col)]
for col in row if col]
for row in exp if row]
for exp in get_layout()[0]]
# if "Save" button was clicked,
if "layout_saved" in st.session_state and st.session_state["layout_saved"]:
got_error = validateSubmittedLayout()
st.session_state['save_btn_error_message'] = got_error # to show error msg at the end
if not got_error:
# get only submitted info from "layout_setting"
set_layout(getTrimmedLayoutSetting(), side_by_side=st.session_state['side_by_side_view'])
st.session_state["edit_mode"] = False
def handleSettingButtons():
if "reset_btn_clicked" in st.session_state and st.session_state.reset_btn_clicked:
resetSettingsToDefault()
if "uploaded_json_file" in st.session_state and st.session_state.uploaded_json_file is not None:
uploaded_layout = json.load(st.session_state.uploaded_json_file)
validated = validateSubmittedLayout(uploaded_layout)
if validated!='':
st.session_state["component_error_message"] = validated
else:
st.session_state.layout_setting = [[[COMPONENT_OPTIONS[COMPONENT_NAMES.index(col)]
for col in row if col]
for row in exp if row]
for exp in uploaded_layout]
st.session_state.num_of_experiment_to_show = len(uploaded_layout)
def setSequenceView():
if get_sequence() is not None:
global COMPONENT_OPTIONS
COMPONENT_OPTIONS = COMPONENT_OPTIONS + ['Sequence view (Mass table needed)',
'Internal fragment map (Mass table needed)']
global COMPONENT_NAMES
COMPONENT_NAMES = COMPONENT_NAMES + ['sequence_view', 'internal_fragment_map']
# page initialization
params = page_setup()
# when sequence is submitted, add "Sequence View" as a component option
setSequenceView()
# handles "onclick" of buttons
if st.session_state.get("edit_mode") is None:
st.session_state["edit_mode"] = True
handleSettingButtons()
handleEditAndSaveButtons()
# initialize setting information
if "layout_setting" not in st.session_state:
if get_layout() is not None:
# load layout setting from cache
st.session_state['layout_setting'] = getExpandedLayoutSetting(get_layout()[0])
st.session_state['num_of_experiment_to_show'] = len(st.session_state.layout_setting)
st.session_state['side_by_side_view'] = get_layout()[1]
st.session_state["edit_mode"] = False
else:
resetSettingsToDefault()
# the "num_of_experiment_to_show" changed
elif "num_of_experiment_to_show" in st.session_state and \
len(st.session_state.layout_setting) != st.session_state.num_of_experiment_to_show:
resetSettingsToDefault(st.session_state.num_of_experiment_to_show)
### title and setting buttons
c1, c2, c3, c4, c5 = st.columns([6, 1, 1, 1, 1])
c1.title("Layout Manager")
# side-by-side view option for 2 experiments
if 'side_by_side_view' not in st.session_state:
st.session_state['side_by_side_view'] = False
if (
('num_of_experiment_to_show' in st.session_state
and st.session_state.num_of_experiment_to_show == 2)
or
(not st.session_state.edit_mode
and (get_layout() is not None and len(get_layout()[0]) == 2))
):
v_space(1, c2)
st.session_state['side_by_side_view'] = c2.checkbox(
"Side-by-Side View", value=st.session_state['side_by_side_view'],
help="If checked, experiments will be shown side-by-side",
disabled=(not st.session_state.edit_mode)
)
# Load existing layout setting file
v_space(1, c3)
c3.button("Load Setting", key="load_btn_clicked")
# Save current layout setting (only after "Saved" button)
v_space(1, c4)
c4.download_button(
label="Save Setting",
data=json.dumps(getTrimmedLayoutSetting()),
file_name='FLASHViewer_layout_settings.json',
mime='json',
disabled=(validateSubmittedLayout()!=''),
)
# Reset settings to default
v_space(1, c5)
c5.button("Reset Setting", key="reset_btn_clicked")
### space for File Uploader, when "Load Setting" button is clicked
if "load_btn_clicked" in st.session_state and st.session_state.load_btn_clicked:
st.file_uploader("Choose a json file", type="json", key="uploaded_json_file")
### Main part
if (not st.session_state.edit_mode) and (get_layout() is not None):
# show saved-mode
for index_of_experiment in range(len(get_layout()[0])):
layout_info_per_experiment = get_layout()[0][index_of_experiment]
with st.expander("Experiment #%d"%(index_of_experiment+1), expanded=True):
for row_index, row in enumerate(layout_info_per_experiment):
st_cols = st.columns(len(row))
for col_index, col in enumerate(row):
st_cols[col_index].info(COMPONENT_OPTIONS[COMPONENT_NAMES.index(col)])
else:
# show edit-mode
st.selectbox("**#Experiments to view at once**", [1, 2, 3, 4, 5],
key="num_of_experiment_to_show",
)
for index_of_experiment in range(st.session_state.num_of_experiment_to_show):
with st.expander("Experiment #%d"%(index_of_experiment+1)):
layoutEditorPerExperiment(index_of_experiment)
### buttons for edit/save
_, edit_btn_col, save_btn_col = st.columns([9, 1, 1])
edit_btn_col.button("Edit", key="edit_btn_clicked", disabled=st.session_state.edit_mode)
save_btn_col.button("Save", key="layout_saved", disabled=(not st.session_state.edit_mode))
### showing error/success message
if "save_btn_error_message" in st.session_state and st.session_state.layout_saved:
error_message = st.session_state["save_btn_error_message"]
if error_message:
st.error('Error: '+error_message, icon="🚨")
else:
st.success('Layouts Saved', icon="✔️")
if "component_error_message" in st.session_state and st.session_state.component_error_message:
st.error('Error: ' + st.session_state.component_error_message, icon="🚨")
del st.session_state["component_error_message"]
### TIPs (TODO: Add image)
st.info("""
**💡 Tips**
- If nothing is set, the default layout will be used in the **👀 Viewer** page
- Don't forget to click "save" on the bottom-right corner to save your setting
""")
save_params(params)