From 597fe655f376638dac946807d035493fb47fb3e7 Mon Sep 17 00:00:00 2001 From: Tom David Mueller Date: Tue, 22 Apr 2025 16:04:40 +0200 Subject: [PATCH 1/7] add side by side view --- .../FLASHDeconv/FLASHDeconvLayoutManager.py | 27 ++++-- content/FLASHDeconv/FLASHDeconvViewer.py | 93 +++++++++++++------ content/FLASHTnT/FLASHTnTLayoutManager.py | 27 ++++-- content/FLASHTnT/FLASHTnTViewer.py | 90 ++++++++++++------ 4 files changed, 164 insertions(+), 73 deletions(-) diff --git a/content/FLASHDeconv/FLASHDeconvLayoutManager.py b/content/FLASHDeconv/FLASHDeconvLayoutManager.py index 46b2d13e..6d4e06dd 100644 --- a/content/FLASHDeconv/FLASHDeconvLayoutManager.py +++ b/content/FLASHDeconv/FLASHDeconvLayoutManager.py @@ -191,16 +191,29 @@ def setSequenceView(): resetSettingsToDefault(st.session_state.num_of_experiment_to_show) ### title and setting buttons -c1, c2, c3, c4 = st.columns([6, 1, 1, 1]) +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 +): + 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" + ) + # Load existing layout setting file -v_space(1, c2) -c2.button("Load Setting", key="load_btn_clicked") +v_space(1, c3) +c3.button("Load Setting", key="load_btn_clicked") # Save current layout setting (only after "Saved" button) -v_space(1, c3) -c3.download_button( +v_space(1, c4) +c4.download_button( label="Save Setting", data=json.dumps(getTrimmedLayoutSetting()), file_name='FLASHViewer_layout_settings.json', @@ -209,8 +222,8 @@ def setSequenceView(): ) # Reset settings to default -v_space(1, c4) -c4.button("Reset Setting", key="reset_btn_clicked") +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: diff --git a/content/FLASHDeconv/FLASHDeconvViewer.py b/content/FLASHDeconv/FLASHDeconvViewer.py index 33a43202..5b6150a1 100644 --- a/content/FLASHDeconv/FLASHDeconvViewer.py +++ b/content/FLASHDeconv/FLASHDeconvViewer.py @@ -171,40 +171,73 @@ def select_experiment(): # Map names to index name_to_index = {n : i for i, n in enumerate(results)} -### for only single experiment on one view -st.selectbox( - "choose experiment", results, - key="selected_experiment_dropdown", - index=name_to_index[st.session_state.selected_experiment0] if 'selected_experiment0' in st.session_state else None, - on_change=select_experiment -) - -if 'selected_experiment0' in st.session_state: - layout_info = DEFAULT_LAYOUT - if "saved_layout_setting" in st.session_state: # when layout manager was used - layout_info = st.session_state["saved_layout_setting"][0] - with st.spinner('Loading component...'): - sendDataToJS(st.session_state.selected_experiment0, layout_info) - - -### for multiple experiments on one view -if "saved_layout_setting" in st.session_state and len(st.session_state["saved_layout_setting"]) > 1: - - for exp_index, exp_layout in enumerate(st.session_state["saved_layout_setting"]): - if exp_index == 0: continue # skip the first experiment - - st.divider() # horizontal line - +if ( + "saved_layout_setting" in st.session_state + and len(st.session_state["saved_layout_setting"]) == 2 + and st.session_state.side_by_side_view +): + c1, c2 = st.columns(2) + with c1: st.selectbox( "choose experiment", results, - key=f'selected_experiment_dropdown_{exp_index}', - index = name_to_index[st.session_state[f'selected_experiment{exp_index}']] if f'selected_experiment{exp_index}' in st.session_state else None, + key="selected_experiment_dropdown", + index=name_to_index[st.session_state.selected_experiment0] if 'selected_experiment0' in st.session_state else None, on_change=select_experiment ) - # if #experiment input files are less than #layouts, all the pre-selection will be the first experiment - if f"selected_experiment{exp_index}" in st.session_state: - layout_info = st.session_state["saved_layout_setting"][exp_index] + if 'selected_experiment0' in st.session_state: + layout_info = DEFAULT_LAYOUT + if "saved_layout_setting" in st.session_state: # when layout manager was used + layout_info = st.session_state["saved_layout_setting"][0] with st.spinner('Loading component...'): - sendDataToJS(st.session_state["selected_experiment%d" % exp_index], layout_info, 'flash_viewer_grid_%d' % exp_index) + sendDataToJS(st.session_state.selected_experiment0, layout_info) + with c2: + st.selectbox( + "choose experiment", results, + key=f'selected_experiment_dropdown_1', + index = name_to_index[st.session_state[f'selected_experiment1']] if f'selected_experiment1' in st.session_state else None, + on_change=select_experiment + ) + if f"selected_experiment1" in st.session_state: + layout_info = st.session_state["saved_layout_setting"][1] + with st.spinner('Loading component...'): + sendDataToJS(st.session_state["selected_experiment1"], layout_info, 'flash_viewer_grid_1') + +else: + ### for only single experiment on one view + st.selectbox( + "choose experiment", results, + key="selected_experiment_dropdown", + index=name_to_index[st.session_state.selected_experiment0] if 'selected_experiment0' in st.session_state else None, + on_change=select_experiment + ) + + + if 'selected_experiment0' in st.session_state: + layout_info = DEFAULT_LAYOUT + if "saved_layout_setting" in st.session_state: # when layout manager was used + layout_info = st.session_state["saved_layout_setting"][0] + with st.spinner('Loading component...'): + sendDataToJS(st.session_state.selected_experiment0, layout_info) + + + ### for multiple experiments on one view + if "saved_layout_setting" in st.session_state and len(st.session_state["saved_layout_setting"]) > 1: + + for exp_index, exp_layout in enumerate(st.session_state["saved_layout_setting"]): + if exp_index == 0: continue # skip the first experiment + + st.divider() # horizontal line + + st.selectbox( + "choose experiment", results, + key=f'selected_experiment_dropdown_{exp_index}', + index = name_to_index[st.session_state[f'selected_experiment{exp_index}']] if f'selected_experiment{exp_index}' in st.session_state else None, + on_change=select_experiment + ) + # if #experiment input files are less than #layouts, all the pre-selection will be the first experiment + if f"selected_experiment{exp_index}" in st.session_state: + layout_info = st.session_state["saved_layout_setting"][exp_index] + with st.spinner('Loading component...'): + sendDataToJS(st.session_state["selected_experiment%d" % exp_index], layout_info, 'flash_viewer_grid_%d' % exp_index) save_params(params) diff --git a/content/FLASHTnT/FLASHTnTLayoutManager.py b/content/FLASHTnT/FLASHTnTLayoutManager.py index df60a603..3d23d0d6 100644 --- a/content/FLASHTnT/FLASHTnTLayoutManager.py +++ b/content/FLASHTnT/FLASHTnTLayoutManager.py @@ -183,16 +183,29 @@ def setSequenceView(): resetSettingsToDefault(st.session_state.num_of_experiment_to_show) ### title and setting buttons -c1, c2, c3, c4 = st.columns([6, 1, 1, 1]) +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 +): + 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" + ) + # Load existing layout setting file -v_space(1, c2) -c2.button("Load Setting", key="load_btn_clicked") +v_space(1, c3) +c3.button("Load Setting", key="load_btn_clicked") # Save current layout setting (only after "Saved" button) -v_space(1, c3) -c3.download_button( +v_space(1, c4) +c4.download_button( label="Save Setting", data=json.dumps(getTrimmedLayoutSetting()), file_name='FLASHViewer_layout_settings.json', @@ -201,8 +214,8 @@ def setSequenceView(): ) # Reset settings to default -v_space(1, c4) -c4.button("Reset Setting", key="reset_btn_clicked") +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: diff --git a/content/FLASHTnT/FLASHTnTViewer.py b/content/FLASHTnT/FLASHTnTViewer.py index 39f3e29e..f16b4cfd 100644 --- a/content/FLASHTnT/FLASHTnTViewer.py +++ b/content/FLASHTnT/FLASHTnTViewer.py @@ -261,41 +261,73 @@ def select_experiment(): # Map names to index name_to_index = {n : i for i, n in enumerate(results)} -### for only single experiment on one view -st.selectbox( - "choose experiment", results, - key="selected_experiment_dropdown_tagger", - index=name_to_index[st.session_state.selected_experiment0_tagger] if 'selected_experiment0_tagger' in st.session_state else None, - on_change=select_experiment -) +if ( + "saved_layout_setting" in st.session_state + and len(st.session_state["saved_layout_setting"]) == 2 + and st.session_state.side_by_side_view +): + c1, c2 = st.columns(2) + with c1: + st.selectbox( + "choose experiment", results, + key="selected_experiment_dropdown_tagger", + index=name_to_index[st.session_state.selected_experiment0_tagger] if 'selected_experiment0_tagger' in st.session_state else None, + on_change=select_experiment + ) + if 'selected_experiment0_tagger' in st.session_state: + layout_info = DEFAULT_LAYOUT + if "saved_layout_setting_tagger" in st.session_state: # when layout manager was used + layout_info = st.session_state["saved_layout_setting_tagger"][0] + with st.spinner('Loading component...'): + sendDataToJS(st.session_state.selected_experiment0_tagger, layout_info) + with c2: + st.selectbox( + "choose experiment", results, + key=f'selected_experiment_dropdown_1_tagger', + index = name_to_index[st.session_state[f'selected_experiment1_tagger']] if f'selected_experiment1_tagger' in st.session_state else None, + on_change=select_experiment + ) + if f"selected_experiment1_tagger" in st.session_state: + layout_info = st.session_state["saved_layout_setting_tagger"][1] + with st.spinner('Loading component...'): + sendDataToJS(st.session_state["selected_experiment1_tagger"], layout_info, 'flash_viewer_grid_1') + +else: + ### for only single experiment on one view + st.selectbox( + "choose experiment", results, + key="selected_experiment_dropdown_tagger", + index=name_to_index[st.session_state.selected_experiment0_tagger] if 'selected_experiment0_tagger' in st.session_state else None, + on_change=select_experiment + ) -if 'selected_experiment0_tagger' in st.session_state: - layout_info = DEFAULT_LAYOUT - if "saved_layout_setting_tagger" in st.session_state: # when layout manager was used - layout_info = st.session_state["saved_layout_setting_tagger"][0] - with st.spinner('Loading component...'): - sendDataToJS(st.session_state.selected_experiment0_tagger, layout_info) + if 'selected_experiment0_tagger' in st.session_state: + layout_info = DEFAULT_LAYOUT + if "saved_layout_setting_tagger" in st.session_state: # when layout manager was used + layout_info = st.session_state["saved_layout_setting_tagger"][0] + with st.spinner('Loading component...'): + sendDataToJS(st.session_state.selected_experiment0_tagger, layout_info) -### for multiple experiments on one view -if "saved_layout_setting_tagger" in st.session_state and len(st.session_state["saved_layout_setting_tagger"]) > 1: + ### for multiple experiments on one view + if "saved_layout_setting_tagger" in st.session_state and len(st.session_state["saved_layout_setting_tagger"]) > 1: - for exp_index, exp_layout in enumerate(st.session_state["saved_layout_setting_tagger"]): - if exp_index == 0: continue # skip the first experiment + for exp_index, exp_layout in enumerate(st.session_state["saved_layout_setting_tagger"]): + if exp_index == 0: continue # skip the first experiment - st.divider() # horizontal line + st.divider() # horizontal line - st.selectbox( - "choose experiment", results, - key=f'selected_experiment_dropdown_{exp_index}_tagger', - index = name_to_index[st.session_state[f'selected_experiment{exp_index}_tagger']] if f'selected_experiment{exp_index}_tagger' in st.session_state else None, - on_change=select_experiment - ) + st.selectbox( + "choose experiment", results, + key=f'selected_experiment_dropdown_{exp_index}_tagger', + index = name_to_index[st.session_state[f'selected_experiment{exp_index}_tagger']] if f'selected_experiment{exp_index}_tagger' in st.session_state else None, + on_change=select_experiment + ) - # if #experiment input files are less than #layouts, all the pre-selection will be the first experiment - if f"selected_experiment{exp_index}_tagger" in st.session_state: - layout_info = st.session_state["saved_layout_setting_tagger"][exp_index] - with st.spinner('Loading component...'): - sendDataToJS(st.session_state["selected_experiment%d_tagger" % exp_index], layout_info, 'flash_viewer_grid_%d' % exp_index) + # if #experiment input files are less than #layouts, all the pre-selection will be the first experiment + if f"selected_experiment{exp_index}_tagger" in st.session_state: + layout_info = st.session_state["saved_layout_setting_tagger"][exp_index] + with st.spinner('Loading component...'): + sendDataToJS(st.session_state["selected_experiment%d_tagger" % exp_index], layout_info, 'flash_viewer_grid_%d' % exp_index) save_params(params) \ No newline at end of file From cbd278c4a20f41ca6f14ecadaf3589657316a9e4 Mon Sep 17 00:00:00 2001 From: Tom David Mueller Date: Tue, 22 Apr 2025 17:31:40 +0200 Subject: [PATCH 2/7] update component names --- content/FLASHDeconv/FLASHDeconvLayoutManager.py | 4 ++-- content/FLASHTnT/FLASHTnTLayoutManager.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/FLASHDeconv/FLASHDeconvLayoutManager.py b/content/FLASHDeconv/FLASHDeconvLayoutManager.py index 6d4e06dd..d12da191 100644 --- a/content/FLASHDeconv/FLASHDeconvLayoutManager.py +++ b/content/FLASHDeconv/FLASHDeconvLayoutManager.py @@ -7,10 +7,10 @@ 'MS1 deconvolved heatmap', 'Scan table', 'Deconvolved spectrum (Scan table needed)', - 'Annotated spectrum (Scan table needed)', + 'Raw spectrum (Scan table needed)', 'Mass table (Scan table needed)', '3D S/N plot (Mass table needed)', - 'QScore ECDF Plot (report_FDR must be enabled)' + 'Score Distribution Plot' # "Sequence view" and "Internal fragment map" is added when "input_sequence" is submitted ] diff --git a/content/FLASHTnT/FLASHTnTLayoutManager.py b/content/FLASHTnT/FLASHTnTLayoutManager.py index 3d23d0d6..7ceff004 100644 --- a/content/FLASHTnT/FLASHTnTLayoutManager.py +++ b/content/FLASHTnT/FLASHTnTLayoutManager.py @@ -7,7 +7,7 @@ 'Sequence view (Protein table needed)', 'Internal fragment map (Protein table needed)', 'Tag table (Protein table needed)', - 'Spectrum view (Tag table needed)', + 'Sequence tag view (Tag table needed)', ] COMPONENT_NAMES=[ From 05ecfe11d793bc76928924bb0d567b30d34c4452 Mon Sep 17 00:00:00 2001 From: Tom David Mueller Date: Tue, 22 Apr 2025 17:34:35 +0200 Subject: [PATCH 3/7] update github user --- Dockerfile | 2 +- settings.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 86a7c6a0..bbb08fc3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,7 +34,7 @@ ARG PORT=8501 ARG GITHUB_TOKEN ENV GH_TOKEN=${GITHUB_TOKEN} # Streamlit app Gihub user name (to download artifact from). -ARG GITHUB_USER=t0mdavid-m +ARG GITHUB_USER=OpenMS # Streamlit app Gihub repository name (to download artifact from). ARG GITHUB_REPO=FLASHViewer # Name of the zip file containing the windows executable diff --git a/settings.json b/settings.json index b84c3a8f..9cddc605 100644 --- a/settings.json +++ b/settings.json @@ -1,6 +1,6 @@ { "app-name": "FLASHApp", - "github-user": "t0mdavid-m", + "github-user": "OpenMS", "repository-name": "FLASHViewer", "analytics": { "google-analytics": { From 388fa3045bdd45ffc421cdeef308589cbf5ea84c Mon Sep 17 00:00:00 2001 From: Tom David Mueller Date: Wed, 23 Apr 2025 10:24:22 +0200 Subject: [PATCH 4/7] store layouts across sessions --- .../FLASHDeconv/FLASHDeconvLayoutManager.py | 69 ++++++++++++++----- content/FLASHDeconv/FLASHDeconvViewer.py | 40 +++++------ content/FLASHTnT/FLASHTnTLayoutManager.py | 69 ++++++++++++++----- content/FLASHTnT/FLASHTnTViewer.py | 40 +++++------ 4 files changed, 136 insertions(+), 82 deletions(-) diff --git a/content/FLASHDeconv/FLASHDeconvLayoutManager.py b/content/FLASHDeconv/FLASHDeconvLayoutManager.py index d12da191..1c216643 100644 --- a/content/FLASHDeconv/FLASHDeconvLayoutManager.py +++ b/content/FLASHDeconv/FLASHDeconvLayoutManager.py @@ -1,6 +1,10 @@ +import json + import streamlit as st + from src.common.common import page_setup, v_space, save_params -import json +from src.workflow.FileManager import FileManager +from pathlib import Path COMPONENT_OPTIONS=[ 'MS1 raw heatmap', @@ -26,12 +30,37 @@ # "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 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): @@ -126,14 +155,13 @@ def getTrimmedLayoutSetting(): def handleEditAndSaveButtons(): # if "Edit" button was clicked, if "edit_btn_clicked" in st.session_state and st.session_state["edit_btn_clicked"]: - # reset variables based on "saved_layout_setting" - st.session_state["num_of_experiment_to_show"] = len(st.session_state["saved_layout_setting"]) + 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 st.session_state.saved_layout_setting] - # remove saved state, if any - del st.session_state["saved_layout_setting"] + for exp in get_layout()[0]] # if "Save" button was clicked, if "layout_saved" in st.session_state and st.session_state["layout_saved"]: @@ -141,14 +169,13 @@ def handleEditAndSaveButtons(): 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" - st.session_state["saved_layout_setting"] = getTrimmedLayoutSetting() + 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 "saved_layout_setting" in st.session_state: - del st.session_state["saved_layout_setting"] 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) @@ -179,6 +206,8 @@ def setSequenceView(): setSequenceView() # handles "onclick" of buttons +if st.session_state.get("edit_mode") is None: + st.session_state["edit_mode"] = True handleSettingButtons() handleEditAndSaveButtons() @@ -198,13 +227,17 @@ def setSequenceView(): 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 + ('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" + help="If checked, experiments will be shown side-by-side", + disabled=(not st.session_state.edit_mode) ) # Load existing layout setting file @@ -230,10 +263,10 @@ def setSequenceView(): st.file_uploader("Choose a json file", type="json", key="uploaded_json_file") ### Main part -if "saved_layout_setting" in st.session_state: +if (not st.session_state.edit_mode) and (get_layout() is not None): # show saved-mode - for index_of_experiment in range(len(st.session_state.saved_layout_setting)): - layout_info_per_experiment = st.session_state.saved_layout_setting[index_of_experiment] + 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)) @@ -251,10 +284,8 @@ def setSequenceView(): ### 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=False if "saved_layout_setting" in st.session_state else True) -save_btn_col.button("Save", key="layout_saved", - disabled=True if "saved_layout_setting" in st.session_state else False) +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: diff --git a/content/FLASHDeconv/FLASHDeconvViewer.py b/content/FLASHDeconv/FLASHDeconvViewer.py index 5b6150a1..565d59c9 100644 --- a/content/FLASHDeconv/FLASHDeconvViewer.py +++ b/content/FLASHDeconv/FLASHDeconvViewer.py @@ -140,8 +140,8 @@ def setSequenceViewInDefaultView(): def select_experiment(): st.session_state.selected_experiment0 = st.session_state.selected_experiment_dropdown - if "saved_layout_setting" in st.session_state and len(st.session_state["saved_layout_setting"]) > 1: - for exp_index in range(1, len(st.session_state["saved_layout_setting"])): + if len(layout) > 1: + for exp_index in range(1, len(layout)): if st.session_state[f'selected_experiment_dropdown_{exp_index}'] is None: continue st.session_state[f"selected_experiment{exp_index}"] = st.session_state[f'selected_experiment_dropdown_{exp_index}'] @@ -163,6 +163,15 @@ def select_experiment(): ) results = file_manager.get_results_list(['deconv_dfs', 'anno_dfs']) +if file_manager.result_exists('layout', 'layout'): + layout = file_manager.get_results('layout', 'layout')['layout'] + side_by_side = layout['side_by_side'] + layout = layout['layout'] + +else: + layout = [DEFAULT_LAYOUT] + side_by_side = False + ### if no input file is given, show blank page if len(results) == 0: st.error('No results to show yet. Please run a workflow first!') @@ -171,11 +180,7 @@ def select_experiment(): # Map names to index name_to_index = {n : i for i, n in enumerate(results)} -if ( - "saved_layout_setting" in st.session_state - and len(st.session_state["saved_layout_setting"]) == 2 - and st.session_state.side_by_side_view -): +if len(layout) == 2 and side_by_side: c1, c2 = st.columns(2) with c1: st.selectbox( @@ -185,11 +190,8 @@ def select_experiment(): on_change=select_experiment ) if 'selected_experiment0' in st.session_state: - layout_info = DEFAULT_LAYOUT - if "saved_layout_setting" in st.session_state: # when layout manager was used - layout_info = st.session_state["saved_layout_setting"][0] with st.spinner('Loading component...'): - sendDataToJS(st.session_state.selected_experiment0, layout_info) + sendDataToJS(st.session_state.selected_experiment0, layout[0]) with c2: st.selectbox( "choose experiment", results, @@ -198,9 +200,8 @@ def select_experiment(): on_change=select_experiment ) if f"selected_experiment1" in st.session_state: - layout_info = st.session_state["saved_layout_setting"][1] with st.spinner('Loading component...'): - sendDataToJS(st.session_state["selected_experiment1"], layout_info, 'flash_viewer_grid_1') + sendDataToJS(st.session_state["selected_experiment1"], layout[1], 'flash_viewer_grid_1') else: ### for only single experiment on one view @@ -213,17 +214,13 @@ def select_experiment(): if 'selected_experiment0' in st.session_state: - layout_info = DEFAULT_LAYOUT - if "saved_layout_setting" in st.session_state: # when layout manager was used - layout_info = st.session_state["saved_layout_setting"][0] with st.spinner('Loading component...'): - sendDataToJS(st.session_state.selected_experiment0, layout_info) - + sendDataToJS(st.session_state.selected_experiment0, layout[0]) ### for multiple experiments on one view - if "saved_layout_setting" in st.session_state and len(st.session_state["saved_layout_setting"]) > 1: + if len(layout) > 1: - for exp_index, exp_layout in enumerate(st.session_state["saved_layout_setting"]): + for exp_index, exp_layout in enumerate(layout): if exp_index == 0: continue # skip the first experiment st.divider() # horizontal line @@ -236,8 +233,7 @@ def select_experiment(): ) # if #experiment input files are less than #layouts, all the pre-selection will be the first experiment if f"selected_experiment{exp_index}" in st.session_state: - layout_info = st.session_state["saved_layout_setting"][exp_index] with st.spinner('Loading component...'): - sendDataToJS(st.session_state["selected_experiment%d" % exp_index], layout_info, 'flash_viewer_grid_%d' % exp_index) + sendDataToJS(st.session_state["selected_experiment%d" % exp_index], layout[exp_index], 'flash_viewer_grid_%d' % exp_index) save_params(params) diff --git a/content/FLASHTnT/FLASHTnTLayoutManager.py b/content/FLASHTnT/FLASHTnTLayoutManager.py index 7ceff004..29aa9ddd 100644 --- a/content/FLASHTnT/FLASHTnTLayoutManager.py +++ b/content/FLASHTnT/FLASHTnTLayoutManager.py @@ -1,6 +1,10 @@ +import json + import streamlit as st + from src.common.common import page_setup, v_space, save_params -import json +from src.workflow.FileManager import FileManager +from pathlib import Path COMPONENT_OPTIONS=[ 'Protein table', @@ -18,12 +22,37 @@ 'deconv_spectrum' ] +# Setup cache access +file_manager = FileManager( + st.session_state["workspace"], + Path(st.session_state['workspace'], 'flashtnt', 'cache') +) + +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_tagger"] = [[['']]] # 1D: experiment, 2D: row, 3D: column, element=component name st.session_state["num_of_experiment_to_show_tagger"] = num_of_exp for index in range(1, num_of_exp): st.session_state.layout_setting_tagger.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): @@ -118,14 +147,13 @@ def getTrimmedLayoutSetting(): def handleEditAndSaveButtons(): # if "Edit" button was clicked, if "edit_btn_clicked" in st.session_state and st.session_state["edit_btn_clicked"]: - # reset variables based on "saved_layout_setting" - st.session_state["num_of_experiment_to_show"] = len(st.session_state["saved_layout_setting_tagger"]) + 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_tagger"] = [[[COMPONENT_OPTIONS[COMPONENT_NAMES.index(col)] for col in row if col] for row in exp if row] - for exp in st.session_state.saved_layout_setting_tagger] - # remove saved state, if any - del st.session_state["saved_layout_setting_tagger"] + for exp in get_layout()[0]] # if "Save" button was clicked, if "layout_saved_tagger" in st.session_state and st.session_state["layout_saved_tagger"]: @@ -133,14 +161,13 @@ def handleEditAndSaveButtons(): 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" - st.session_state["saved_layout_setting_tagger"] = getTrimmedLayoutSetting() + 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 "saved_layout_setting_tagger" in st.session_state: - del st.session_state["saved_layout_setting_tagger"] 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) @@ -171,6 +198,8 @@ def setSequenceView(): #setSequenceView() # handles "onclick" of buttons +if st.session_state.get("edit_mode") is None: + st.session_state["edit_mode"] = True handleSettingButtons() handleEditAndSaveButtons() @@ -190,13 +219,17 @@ def setSequenceView(): 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 + ('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" + help="If checked, experiments will be shown side-by-side", + disabled=(not st.session_state.edit_mode) ) # Load existing layout setting file @@ -222,10 +255,10 @@ def setSequenceView(): st.file_uploader("Choose a json file", type="json", key="uploaded_json_file") ### Main part -if "saved_layout_setting_tagger" in st.session_state: +if (not st.session_state.edit_mode) and (get_layout() is not None): # show saved-mode - for index_of_experiment in range(len(st.session_state.saved_layout_setting_tagger)): - layout_info_per_experiment = st.session_state.saved_layout_setting_tagger[index_of_experiment] + 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)) @@ -243,10 +276,8 @@ def setSequenceView(): ### 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=False if "saved_layout_setting_tagger" in st.session_state else True) -save_btn_col.button("Save", key="layout_saved_tagger", - disabled=True if "saved_layout_setting_tagger" in st.session_state else False) +edit_btn_col.button("Edit", key="edit_btn_clicked", disabled=st.session_state.edit_mode) +save_btn_col.button("Save", key="layout_saved_tagger", 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_tagger: diff --git a/content/FLASHTnT/FLASHTnTViewer.py b/content/FLASHTnT/FLASHTnTViewer.py index f16b4cfd..82593279 100644 --- a/content/FLASHTnT/FLASHTnTViewer.py +++ b/content/FLASHTnT/FLASHTnTViewer.py @@ -232,8 +232,8 @@ def setSequenceViewInDefaultView(): def select_experiment(): st.session_state.selected_experiment0_tagger = st.session_state.selected_experiment_dropdown_tagger - if "saved_layout_setting_tagger" in st.session_state and len(st.session_state["saved_layout_setting_tagger"]) > 1: - for exp_index in range(1, len(st.session_state["saved_layout_setting_tagger"])): + if len(layout) > 1: + for exp_index in range(1, len(layout)): if st.session_state[f'selected_experiment_dropdown_{exp_index}_tagger'] is None: continue st.session_state[f"selected_experiment{exp_index}_tagger"] = st.session_state[f'selected_experiment_dropdown_{exp_index}_tagger'] @@ -253,6 +253,15 @@ def select_experiment(): ['deconv_dfs', 'anno_dfs', 'tag_dfs', 'protein_dfs'] ) +if file_manager.result_exists('layout', 'layout'): + layout = file_manager.get_results('layout', 'layout')['layout'] + side_by_side = layout['side_by_side'] + layout = layout['layout'] + +else: + layout = [DEFAULT_LAYOUT] + side_by_side = False + ### if no input file is given, show blank page if len(results) == 0: st.error('No results to show yet. Please run a workflow first!') @@ -261,11 +270,7 @@ def select_experiment(): # Map names to index name_to_index = {n : i for i, n in enumerate(results)} -if ( - "saved_layout_setting" in st.session_state - and len(st.session_state["saved_layout_setting"]) == 2 - and st.session_state.side_by_side_view -): +if len(layout) == 2 and side_by_side: c1, c2 = st.columns(2) with c1: st.selectbox( @@ -275,11 +280,8 @@ def select_experiment(): on_change=select_experiment ) if 'selected_experiment0_tagger' in st.session_state: - layout_info = DEFAULT_LAYOUT - if "saved_layout_setting_tagger" in st.session_state: # when layout manager was used - layout_info = st.session_state["saved_layout_setting_tagger"][0] with st.spinner('Loading component...'): - sendDataToJS(st.session_state.selected_experiment0_tagger, layout_info) + sendDataToJS(st.session_state.selected_experiment0_tagger, layout[0]) with c2: st.selectbox( "choose experiment", results, @@ -288,9 +290,8 @@ def select_experiment(): on_change=select_experiment ) if f"selected_experiment1_tagger" in st.session_state: - layout_info = st.session_state["saved_layout_setting_tagger"][1] with st.spinner('Loading component...'): - sendDataToJS(st.session_state["selected_experiment1_tagger"], layout_info, 'flash_viewer_grid_1') + sendDataToJS(st.session_state["selected_experiment1_tagger"], layout[1], 'flash_viewer_grid_1') else: ### for only single experiment on one view @@ -302,17 +303,13 @@ def select_experiment(): ) if 'selected_experiment0_tagger' in st.session_state: - layout_info = DEFAULT_LAYOUT - if "saved_layout_setting_tagger" in st.session_state: # when layout manager was used - layout_info = st.session_state["saved_layout_setting_tagger"][0] with st.spinner('Loading component...'): - sendDataToJS(st.session_state.selected_experiment0_tagger, layout_info) - + sendDataToJS(st.session_state.selected_experiment0_tagger, layout[0]) ### for multiple experiments on one view - if "saved_layout_setting_tagger" in st.session_state and len(st.session_state["saved_layout_setting_tagger"]) > 1: + if len(layout) > 1: - for exp_index, exp_layout in enumerate(st.session_state["saved_layout_setting_tagger"]): + for exp_index, exp_layout in enumerate(layout): if exp_index == 0: continue # skip the first experiment st.divider() # horizontal line @@ -326,8 +323,7 @@ def select_experiment(): # if #experiment input files are less than #layouts, all the pre-selection will be the first experiment if f"selected_experiment{exp_index}_tagger" in st.session_state: - layout_info = st.session_state["saved_layout_setting_tagger"][exp_index] with st.spinner('Loading component...'): - sendDataToJS(st.session_state["selected_experiment%d_tagger" % exp_index], layout_info, 'flash_viewer_grid_%d' % exp_index) + sendDataToJS(st.session_state["selected_experiment%d_tagger" % exp_index], layout[exp_index], 'flash_viewer_grid_%d' % exp_index) save_params(params) \ No newline at end of file From 801f65480cf116d4d0a460a65cf890fe04a41855 Mon Sep 17 00:00:00 2001 From: Tom David Mueller Date: Wed, 23 Apr 2025 10:39:30 +0200 Subject: [PATCH 5/7] actually implement loading logic --- .../FLASHDeconv/FLASHDeconvLayoutManager.py | 23 ++++++++++++++++++- content/FLASHTnT/FLASHTnTLayoutManager.py | 23 ++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/content/FLASHDeconv/FLASHDeconvLayoutManager.py b/content/FLASHDeconv/FLASHDeconvLayoutManager.py index 1c216643..02b12673 100644 --- a/content/FLASHDeconv/FLASHDeconvLayoutManager.py +++ b/content/FLASHDeconv/FLASHDeconvLayoutManager.py @@ -151,6 +151,20 @@ def getTrimmedLayoutSetting(): 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, @@ -213,7 +227,14 @@ def setSequenceView(): # initialize setting information if "layout_setting" not in st.session_state: - resetSettingsToDefault() + 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: diff --git a/content/FLASHTnT/FLASHTnTLayoutManager.py b/content/FLASHTnT/FLASHTnTLayoutManager.py index 29aa9ddd..21304052 100644 --- a/content/FLASHTnT/FLASHTnTLayoutManager.py +++ b/content/FLASHTnT/FLASHTnTLayoutManager.py @@ -143,6 +143,20 @@ def getTrimmedLayoutSetting(): 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, @@ -205,7 +219,14 @@ def setSequenceView(): # initialize setting information if "layout_setting_tagger" not in st.session_state: - resetSettingsToDefault() + if get_layout() is not None: + # load layout setting from cache + st.session_state['layout_setting_tagger'] = 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_tagger) != st.session_state.num_of_experiment_to_show: From b4d1409de95e284714b6b5826684ff20dd3e83e4 Mon Sep 17 00:00:00 2001 From: Tom David Mueller Date: Wed, 23 Apr 2025 10:43:40 +0200 Subject: [PATCH 6/7] update to new repo name --- .gitignore | 2 +- Dockerfile | 2 +- README.md | 12 ++++++------ settings.json | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 7467237c..baf5a6b3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ build.log run_app.spec clean-up-workspaces.log -# FLASHViewer +# FLASHApp workspaces Pipfile workspaces-flashtaggerviewer diff --git a/Dockerfile b/Dockerfile index bbb08fc3..bd856863 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ ENV GH_TOKEN=${GITHUB_TOKEN} # Streamlit app Gihub user name (to download artifact from). ARG GITHUB_USER=OpenMS # Streamlit app Gihub repository name (to download artifact from). -ARG GITHUB_REPO=FLASHViewer +ARG GITHUB_REPO=FLASHApp # Name of the zip file containing the windows executable ARG ASSET_NAME=OpenMS-App.zip diff --git a/README.md b/README.md index aa441ed1..9f955c83 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# FLASHViewer +# FLASHApp -FLASHViewer for visualizing FLASHDeconv's results \ +FLASHApp for visualizing FLASHDeconv's results \ This app is based on [OpenMS streamlit template project](https://github.com/OpenMS/streamlit-template). ![Overview](https://github.com/user-attachments/assets/35fe2c24-7dbc-40cd-b8b5-b7504768ade1) @@ -28,7 +28,7 @@ for more documentation on submodules ## Build -To build FLASHViewer, you first need to build the `openms-streamlit-vue-component` +To build FLASHApp, you first need to build the `openms-streamlit-vue-component` and copy the build from `./openms-streamlit-vue-component/dist` to `./js-component/dist`. @@ -37,7 +37,7 @@ You then should set streamlit to production in two locations: * in `./.streamlit/config.toml` set `developmentMode` to `false` * in `./src/components.py` set `_RELEASE` to `True` -These steps should be done before building any version of FLASHViewer. +These steps should be done before building any version of FLASHApp. ### Docker @@ -46,7 +46,7 @@ First you need to build an image locally. Prerequisite: `src/components.py` has `RELEASE=True` and `dist/` contains a build of the Vue component. These should be the settings on the `main` branch. -build image with: `docker build -f Dockerfile --no-cache -t flashviewer:latest --build-arg GITHUB_TOKEN= .` +build image with: `docker build -f Dockerfile --no-cache -t flashapp:latest --build-arg GITHUB_TOKEN= .` You should see a successful output, but you can check if an image is built with: @@ -54,6 +54,6 @@ You should see a successful output, but you can check if an image is built with: After it has been built you can run the image with: -`docker run -p 8501:8501 flashviewer:latest` +`docker run -p 8501:8501 flashapp:latest` Navigate to `http://localhost:8501` in your browser. diff --git a/settings.json b/settings.json index 9cddc605..508f8882 100644 --- a/settings.json +++ b/settings.json @@ -1,7 +1,7 @@ { "app-name": "FLASHApp", "github-user": "OpenMS", - "repository-name": "FLASHViewer", + "repository-name": "FLASHApp", "analytics": { "google-analytics": { "enabled": false, From 95ba301fe0000d4b8a4a7890beb13226e76674cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20David=20M=C3=BCller?= <57191390+t0mdavid-m@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:55:14 +0200 Subject: [PATCH 7/7] Update content/FLASHTnT/FLASHTnTLayoutManager.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- content/FLASHTnT/FLASHTnTLayoutManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/FLASHTnT/FLASHTnTLayoutManager.py b/content/FLASHTnT/FLASHTnTLayoutManager.py index 21304052..386d9fec 100644 --- a/content/FLASHTnT/FLASHTnTLayoutManager.py +++ b/content/FLASHTnT/FLASHTnTLayoutManager.py @@ -222,7 +222,7 @@ def setSequenceView(): if get_layout() is not None: # load layout setting from cache st.session_state['layout_setting_tagger'] = getExpandedLayoutSetting(get_layout()[0]) - st.session_state['num_of_experiment_to_show'] = len(st.session_state.layout_setting) + st.session_state['num_of_experiment_to_show'] = len(st.session_state.layout_setting_tagger) st.session_state['side_by_side_view'] = get_layout()[1] st.session_state["edit_mode"] = False else: