Skip to content

Commit f49eaf1

Browse files
committed
Merge branch 'main' of github.com:OpenMS/streamlit-template
2 parents 431c6e9 + 452c572 commit f49eaf1

File tree

9 files changed

+180
-15
lines changed

9 files changed

+180
-15
lines changed

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: continuous-integration
2+
3+
on: [push]
4+
5+
jobs:
6+
test:
7+
runs-on: ${{ matrix.os }}
8+
strategy:
9+
matrix:
10+
os: [ubuntu-latest]
11+
# Requirements file generated with python=3.11
12+
python-version: ["3.11"]
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Set up Python ${{ matrix.python-version }}
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: ${{ matrix.python-version }}
20+
- name: Install dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install -r requirements.txt # test with requirements file so can easily bump with dependabot
24+
pip install pytest
25+
- name: Test
26+
run: |
27+
python -m pytest test_gui.py

.github/workflows/workflow-tests.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ jobs:
2323
- name: Running test cases
2424
run: |
2525
pytest test.py
26+
- name: Running GUI tests
27+
run: |
28+
pytest test_gui.py

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ get-pip.py
99
run_app.bat
1010
python*
1111
**/__pycache__/
12-
gdpr_consent/node_modules/
12+
gdpr_consent/node_modules/
13+
*~

Dockerfile_simple

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ RUN wget -q \
4545
&& rm -f Miniforge3-Linux-x86_64.sh
4646
RUN mamba --version
4747

48-
# Activate and configure the mamba environment
49-
RUN mamba update -n base -c conda-forge mamba && mamba info && mamba create -n streamlit-env python=3.10
50-
# note: activation of mamba needs to go to bashrc because every RUN command spawns new bash
48+
# Setup mamba environment.
49+
COPY environment.yml ./environment.yml
50+
RUN mamba env create -f environment.yml
51+
RUN echo "mamba activate streamlit-env" >> ~/.bashrc
52+
SHELL ["/bin/bash", "--rcfile", "~/.bashrc"]
5153
SHELL ["mamba", "run", "-n", "streamlit-env", "/bin/bash", "-c"]
52-
RUN echo "source activate streamlit-env" > ~/.bashrc
5354

5455
#################################### install streamlit
5556
# install packages
@@ -84,8 +85,9 @@ RUN echo "0 3 * * * /root/miniforge3/envs/streamlit-env/bin/python /app/clean-up
8485

8586
# create entrypoint script to start cron service and launch streamlit app
8687
RUN echo "#!/bin/bash" > /app/entrypoint.sh
87-
RUN echo "service cron start" >> /app/entrypoint.sh
88-
RUN echo "mamba run --no-capture-output -n streamlit-env streamlit run app.py" >> /app/entrypoint.sh
88+
RUN echo "source /root/miniforge3/bin/activate streamlit-env" >> /app/entrypoint.sh && \
89+
echo "service cron start" >> /app/entrypoint.sh && \
90+
echo "streamlit run app.py" >> /app/entrypoint.sh
8991
# make the script executable
9092
RUN chmod +x /app/entrypoint.sh
9193

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ This repository contains a template app for OpenMS workflows in a web applicatio
1818

1919
Documentation for **users** and **developers** is included as pages in [this template app](https://abi-services.cs.uni-tuebingen.de/streamlit-template/), indicated by the 📖 icon.
2020

21+
## Citation
22+
23+
Please cite:
24+
Müller, T. D., Siraj, A., et al. OpenMS WebApps: Building User-Friendly Solutions for MS Analysis. Journal of Proteome Research (2025). [https://doi.org/10.1021/acs.jproteome.4c00872](https://doi.org/10.1021/acs.jproteome.4c00872)
25+
2126
## References
2227

2328
- Pfeuffer, J., Bielow, C., Wein, S. et al. OpenMS 3 enables reproducible analysis of large-scale mass spectrometry data. Nat Methods 21, 365–367 (2024). [https://doi.org/10.1038/s41592-024-02197-7](https://doi.org/10.1038/s41592-024-02197-7)

settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
"tag": "57690c44-d635-43b0-ab43-f8bd3064ca06"
1313
}
1414
},
15-
"online_deployment": false
15+
"online_deployment": false,
16+
"test": true
1617
}

src/common/common.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,17 @@ def page_setup(page: str = "") -> dict[str, Any]:
225225

226226
captcha_control()
227227

228+
# If run in hosted mode, show captcha as long as it has not been solved
229+
#if not "local" in sys.argv:
230+
# if "controllo" not in st.session_state:
231+
# # Apply captcha by calling the captcha_control function
232+
# captcha_control()
233+
234+
# If run in hosted mode, show captcha as long as it has not been solved
235+
if 'controllo' not in st.session_state or ("controllo" in params.keys() and params["controllo"] == False):
236+
# Apply captcha by calling the captcha_control function
237+
captcha_control()
238+
228239
return params
229240

230241

@@ -376,11 +387,16 @@ def get_current_chunk(df, chunk_size, chunk_index):
376387
)
377388

378389
rows = event["selection"]["rows"]
379-
if not rows:
390+
391+
if st.session_state.settings['test']: # is a test App, return first row as selected
392+
return 1
393+
elif not rows:
380394
return None
381-
# Calculate the index based on the current page and chunk size
382-
base_index = (page - 1) * chunk_size
383-
return base_index + rows[0]
395+
else:
396+
# Calculate the index based on the current page and chunk size
397+
base_index = (page - 1) * chunk_size
398+
print(base_index)
399+
return base_index + rows[0]
384400

385401

386402

src/workflow/StreamlitUI.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,11 +1114,13 @@ def export_parameters_markdown(self):
11141114
version = result.stderr.split("Version: ")[1].split("-")[0]
11151115

11161116
markdown.append(
1117-
f"""Data was processed using **{st.session_state.settings['app-name']}** ([{url}]({url})), a web application based on the OpenMS WebApps framework.
1118-
OpenMS ([https://www.openms.de](https://www.openms.de)) is a free and open-source software for LC-MS data analysis [1].
1117+
f"""Data was processed using **{st.session_state.settings['app-name']}** ([{url}]({url})), a web application based on the OpenMS WebApps framework [1].
1118+
OpenMS ([https://www.openms.de](https://www.openms.de)) is a free and open-source software for LC-MS data analysis [2].
11191119
The workflow includes the **OpenMS {version}** TOPP tools {tools} as well as Python scripts. Non-default parameters are listed in the supplementary section below.
11201120
1121-
[1] Sachsenberg, Timo, et al. "OpenMS 3 expands the frontiers of open-source computational mass spectrometry." (2023).
1121+
[1] Müller, Tom David, et al. "OpenMS WebApps: Building User-Friendly Solutions for MS Analysis." (2025) [https://doi.org/10.1021/acs.jproteome.4c00872](https://doi.org/10.1021/acs.jproteome.4c00872).
1122+
\\
1123+
[2] Pfeuffer, Julianus, et al. "OpenMS 3 enables reproducible analysis of large-scale mass spectrometry data." (2024) [https://doi.org/10.1038/s41592-024-02197-7](https://doi.org/10.1038/s41592-024-02197-7).
11221124
"""
11231125
)
11241126
markdown.append(self.non_default_params_summary())

test_gui.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from streamlit.testing.v1 import AppTest
2+
import pytest
3+
from src import fileupload
4+
import json
5+
from pathlib import Path
6+
import shutil
7+
8+
@pytest.fixture
9+
def launch(request):
10+
test = AppTest.from_file(request.param)
11+
12+
## Initialize session state ##
13+
with open("settings.json", "r") as f:
14+
test.session_state.settings = json.load(f)
15+
test.session_state.settings['test'] = True
16+
test.secrets['workspace'] = 'test'
17+
return test
18+
19+
20+
21+
# Test launching of all pages
22+
@pytest.mark.parametrize('launch', (
23+
#"content/quickstart.py", # NOTE: this page does not work due to streamlit.errors.StreamlitPageNotFoundError error
24+
"content/documentation.py",
25+
"content/topp_workflow_file_upload.py",
26+
"content/topp_workflow_parameter.py",
27+
"content/topp_workflow_execution.py",
28+
"content/topp_workflow_results.py",
29+
"content/file_upload.py",
30+
"content/raw_data_viewer.py",
31+
"content/run_example_workflow.py",
32+
"content/download_section.py",
33+
"content/simple_workflow.py",
34+
"content/run_subprocess.py"), indirect=True)
35+
def test_launch(launch):
36+
launch.run()
37+
assert not launch.exception
38+
39+
40+
41+
########### PAGE SPECIFIC TESTS ############
42+
@pytest.mark.parametrize('launch,selection', [("content/documentation.py", 'User Guide'),
43+
("content/documentation.py", 'Installation'),
44+
("content/documentation.py", 'Developers Guide: How to build app based on this template'),
45+
("content/documentation.py", 'Developers Guide: TOPP Workflow Framework'),
46+
("content/documentation.py", 'Developer Guide: Windows Executables'),
47+
("content/documentation.py", 'Developers Guide: Deployment')], indirect=['launch'])
48+
def test_documentation(launch, selection):
49+
launch.run()
50+
launch.selectbox[0].select(selection).run()
51+
assert not launch.exception
52+
53+
54+
@pytest.mark.parametrize('launch', ["content/file_upload.py"], indirect=True)
55+
def test_file_upload_load_example(launch):
56+
launch.run()
57+
for i in launch.tabs:
58+
if i.label == "Example Data":
59+
i.button[0].click().run()
60+
assert not launch.exception
61+
62+
63+
# NOTE: All tabs are automatically checked
64+
@pytest.mark.parametrize('launch,example', [("content/raw_data_viewer.py", 'Blank.mzML'),
65+
("content/raw_data_viewer.py", 'Treatment.mzML'),
66+
("content/raw_data_viewer.py", 'Pool.mzML'),
67+
("content/raw_data_viewer.py", 'Control.mzML')], indirect=['launch'])
68+
def test_view_raw_ms_data(launch, example):
69+
launch.run()
70+
71+
## Load Example file, based on implementation of fileupload.load_example_mzML_files() ###
72+
mzML_dir = Path(launch.session_state.workspace, "mzML-files")
73+
74+
# Copy files from example-data/mzML to workspace mzML directory, add to selected files
75+
for f in Path("example-data", "mzML").glob("*.mzML"):
76+
shutil.copy(f, mzML_dir)
77+
launch.run()
78+
79+
## TODO: Figure out a way to select a spectrum to be displayed
80+
launch.selectbox[0].select(example).run()
81+
assert not launch.exception
82+
83+
84+
@pytest.mark.parametrize('launch,example', [("content/run_example_workflow.py", ['Blank']),
85+
("content/run_example_workflow.py", ['Treatment']),
86+
("content/run_example_workflow.py", ['Pool']),
87+
("content/run_example_workflow.py", ['Control']),
88+
("content/run_example_workflow.py", ['Control', 'Blank'])], indirect=['launch'])
89+
def test_run_workflow(launch, example):
90+
launch.run()
91+
## Load Example file, based on implementation of fileupload.load_example_mzML_files() ###
92+
mzML_dir = Path(launch.session_state.workspace, "mzML-files")
93+
94+
# Copy files from example-data/mzML to workspace mzML directory, add to selected files
95+
for f in Path("example-data", "mzML").glob("*.mzML"):
96+
shutil.copy(f, mzML_dir)
97+
launch.run()
98+
99+
## Select experiments to process
100+
for e in example:
101+
launch.multiselect[0].select(e)
102+
103+
launch.run()
104+
assert not launch.exception
105+
106+
# Press the "Run Workflow" button
107+
launch.button[1].click().run(timeout=60)
108+
assert not launch.exception

0 commit comments

Comments
 (0)