Skip to content

Commit 362adef

Browse files
Merge branch 'main' into main
2 parents a1df75c + 452c572 commit 362adef

File tree

8 files changed

+172
-9
lines changed

8 files changed

+172
-9
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+
*~

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
@@ -13,5 +13,6 @@
1313
}
1414
},
1515
"online_deployment": false,
16-
"is_workspace_enabled": true
16+
"is_workspace_enabled": true,
17+
"test": true
1718
}

src/common/common.py

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

243243
captcha_control()
244244

245+
# If run in hosted mode, show captcha as long as it has not been solved
246+
#if not "local" in sys.argv:
247+
# if "controllo" not in st.session_state:
248+
# # Apply captcha by calling the captcha_control function
249+
# captcha_control()
250+
251+
# If run in hosted mode, show captcha as long as it has not been solved
252+
if 'controllo' not in st.session_state or ("controllo" in params.keys() and params["controllo"] == False):
253+
# Apply captcha by calling the captcha_control function
254+
captcha_control()
255+
245256
return params
246257

247258

@@ -395,11 +406,16 @@ def get_current_chunk(df, chunk_size, chunk_index):
395406
)
396407

397408
rows = event["selection"]["rows"]
398-
if not rows:
409+
410+
if st.session_state.settings['test']: # is a test App, return first row as selected
411+
return 1
412+
elif not rows:
399413
return None
400-
# Calculate the index based on the current page and chunk size
401-
base_index = (page - 1) * chunk_size
402-
return base_index + rows[0]
414+
else:
415+
# Calculate the index based on the current page and chunk size
416+
base_index = (page - 1) * chunk_size
417+
print(base_index)
418+
return base_index + rows[0]
403419

404420

405421

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)