Skip to content

Commit

Permalink
Merge pull request #41 from axelwalter/main
Browse files Browse the repository at this point in the history
Framework for TOPP tool workflows
  • Loading branch information
axelwalter authored Feb 19, 2024
2 parents 3ffba89 + 59a05d2 commit 2490777
Show file tree
Hide file tree
Showing 19 changed files with 1,953 additions and 5 deletions.
4 changes: 4 additions & 0 deletions assets/topp-workflow-default-params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"mzML_files": [
]
}
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ dependencies:
- mono==6.12.0.90
- pip: # dependencies only available through pip
# streamlit dependencies
- streamlit==1.28.0
- streamlit==1.29.0
- streamlit-plotly-events==0.0.6
- streamlit-aggrid==0.3.4.post3
- captcha==0.5.0
4 changes: 2 additions & 2 deletions pages/2_Simple_Workflow.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import streamlit as st

from src.common import page_setup, save_params, show_table
from src import workflow
from src import simpleworkflow
from src.captcha_ import captcha_control

# Page name "workflow" will show mzML file selector in sidebar
Expand Down Expand Up @@ -40,7 +40,7 @@
# Get a dataframe with x and y dimensions via time consuming (sleep) cached function
# If the input has been given before, the function does not run again
# Input x from local variable, input y from session state via key
df = workflow.generate_random_table(xdimension, st.session_state["example-y-dimension"])
df = simpleworkflow.generate_random_table(xdimension, st.session_state["example-y-dimension"])

# Display dataframe via custom show_table function, which will render a download button as well
show_table(df, download_name="random-table")
Expand Down
26 changes: 26 additions & 0 deletions pages/5_TOPP-Workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import streamlit as st
from src.common import page_setup
from src.Workflow import Workflow

# The rest of the page can, but does not have to be changed
if __name__ == "__main__":

params = page_setup()

wf = Workflow()

st.title(wf.name)

t = st.tabs(["📁 **File Upload**", "⚙️ **Configure**", "🚀 **Run**", "📊 **Results**"])
with t[0]:
wf.show_file_upload_section()

with t[1]:
wf.show_parameter_section()

with t[2]:
wf.show_execution_section()

with t[3]:
wf.show_results_section()

399 changes: 399 additions & 0 deletions pages/6_📖_TOPP-Workflow_Docs.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# the requirements.txt file is intended for deployment on streamlit cloud and if the simple container is built
# note that it is much more restricted in terms of installing third-parties / etc.
# preferably use the batteries included or simple docker file for local hosting
streamlit==1.28.0
streamlit==1.29.0
streamlit-plotly-events==0.0.6
streamlit-aggrid==0.3.4.post3
pandas==2.1.2
Expand Down
79 changes: 79 additions & 0 deletions src/Workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import streamlit as st
from .workflow.WorkflowManager import WorkflowManager

class Workflow(WorkflowManager):
# Setup pages for upload, parameter, execution and results.
# For layout use any streamlit components such as tabs (as shown in example), columns, or even expanders.
def __init__(self) -> None:
# Initialize the parent class with the workflow name.
super().__init__("TOPP Workflow", st.session_state["workspace"])

def upload(self)-> None:
t = st.tabs(["MS data", "Example with fallback data"])
with t[0]:
# Use the upload method from StreamlitUI to handle mzML file uploads.
self.ui.upload_widget(key="mzML-files", name="MS data", file_type="mzML")
with t[1]:
# Example with fallback data (not used in workflow)
self.ui.upload_widget(key="image", file_type="png", fallback="assets/OpenMS.png")

def configure(self) -> None:
# Allow users to select mzML files for the analysis.
self.ui.select_input_file("mzML-files", multiple=True)

# Create tabs for different analysis steps.
t = st.tabs(
["**Feature Detection**", "**Adduct Detection**", "**SIRIUS Export**", "**Python Custom Tool**"]
)
with t[0]:
# Parameters for FeatureFinderMetabo TOPP tool.
self.ui.input_TOPP("FeatureFinderMetabo")
with t[1]:
# A single checkbox widget for workflow logic.
self.ui.input_widget("run-adduct-detection", False, "Adduct Detection")
# Paramters for MetaboliteAdductDecharger TOPP tool.
self.ui.input_TOPP("MetaboliteAdductDecharger")
with t[2]:
# Paramters for SiriusExport TOPP tool
self.ui.input_TOPP("SiriusExport")
with t[3]:
# Generate input widgets for a custom Python tool, located at src/python-tools.
# Parameters are specified within the file in the DEFAULTS dictionary.
self.ui.input_python("example")

def execution(self) -> None:
# Get mzML input files from self.params.
# Can be done without file manager, however, it ensures everything is correct.
in_mzML = self.file_manager.get_files(self.params["mzML-files"])

# Log any messages.
self.logger.log(f"Number of input mzML files: {len(in_mzML)}")

# Prepare output files for feature detection.
out_ffm = self.file_manager.get_files(in_mzML, "featureXML", "feature-detection")

# Run FeatureFinderMetabo tool with input and output files.
self.executor.run_topp(
"FeatureFinderMetabo", input_output={"in": in_mzML, "out": out_ffm}
)

# Check if adduct detection should be run.
if self.params["run-adduct-detection"]:

# Run MetaboliteAdductDecharger for adduct detection, with disabled logs.
# Without a new file list for output, the input files will be overwritten in this case.
self.executor.run_topp(
"MetaboliteAdductDecharger", {"in": out_ffm, "out_fm": out_ffm}, write_log=False
)

# Example for a custom Python tool, which is located in src/python-tools.
self.executor.run_python("example", {"in": in_mzML})

# Prepare output file for SiriusExport.
out_se = self.file_manager.get_files("sirius.ms", set_results_dir="sirius-export")
self.executor.run_topp("SiriusExport", {"in": self.file_manager.get_files(in_mzML, collect=True),
"in_featureinfo": self.file_manager.get_files(out_ffm, collect=True),
"out": out_se})

def results(self) -> None:
st.warning("Not implemented yet.")
1 change: 1 addition & 0 deletions src/python-tools/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
65 changes: 65 additions & 0 deletions src/python-tools/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import json
import sys

############################
# default paramter values #
###########################
#
# Mandatory keys for each parameter
# key: a unique identifier
# value: the default value
#
# Optional keys for each parameter
# name: the name of the parameter
# hide: don't show the parameter in the parameter section (e.g. for input/output files)
# options: a list of valid options for the parameter
# min: the minimum value for the parameter (int and float)
# max: the maximum value for the parameter (int and float)
# step_size: the step size for the parameter (int and float)
# help: a description of the parameter
# widget_type: the type of widget to use for the parameter (default: auto)
# advanced: whether or not the parameter is advanced (default: False)

DEFAULTS = [
{"key": "in", "value": [], "help": "Input files for Python Script.", "hide": True},
{"key": "out", "value": [], "help": "Output files for Python Script.", "hide": True},
{
"key": "number-slider",
"name": "number of features",
"value": 6,
"min": 2,
"max": 10,
"help": "How many features to consider.",
"widget_type": "slider",
"step_size": 2,
},
{
"key": "selectbox-example",
"value": "a",
"options": ["a", "b", "c"],
},
{
"key": "adavanced-input",
"value": 5,
"step_size": 5,
"help": "An advanced example parameter.",
"advanced": True,
},
{
"key": "checkbox", "value": True
}
]

def get_params():
if len(sys.argv) > 1:
with open(sys.argv[1], "r") as f:
return json.load(f)
else:
return {}

if __name__ == "__main__":
params = get_params()
# Add code here:
print("Writing stdout which will get logged...")
print("Parameters for this example Python tool:")
print(json.dumps(params, indent=4))
File renamed without changes.
1 change: 1 addition & 0 deletions src/workflow/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
Loading

0 comments on commit 2490777

Please sign in to comment.