Skip to content

Commit

Permalink
Merge pull request OpenMS#35 from axelwalter/main
Browse files Browse the repository at this point in the history
Update readme
  • Loading branch information
axelwalter authored Oct 16, 2023
2 parents 922e88f + 722b802 commit 1c9d7c0
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 80 deletions.
100 changes: 46 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
# OpenMS streamlit template [![Open Template!](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://openms-template.streamlit.app/)
# OpenMS streamlit template [![Open Template!](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://abi-services.cs.uni-tuebingen.de/streamlit-template/)

This is a template app for pyOpenMS workflows in a web application.
This is a template app for OpenMS workflows in a web application.

## Template specific concepts
## Features

- **Workspaces:** Directories where all data generated and uploaded can be stored as well as a workspace specific parameter file.
Running the app locally will put the workspaces outside of the repository directory. Running online (e.g. streamlit cloud) the workspaces will be inside the repository directory.
- **Run the app local and online (default):** Launching the app with the local argument let's the user create/remove workspaces. The online version is for hosting where the user gets a workspace with a specific ID.
- Workspaces for user data with unique shareable IDs
- Captcha control
- Deployment with Docker
- Packaged executables for Windows (built via GitHub action)
- Automatic removal of unused workspaces
- Parameters persist within a workspace

run locally:
## Key concepts

`streamlit run app.py local`
**Workspaces**

- **Parameters:** Streamlit offers statefulness via the st.session_state object. However, we want to define default parameters (in `assets/default-params.json`) and store changing parameters for each workspace. Parameters are loaded via the mandatory page_setup function at the start of each page. To track a widget variable via parameters simply give them a key and add a matching entry in the default parameters file. Initialize a widget value from the params dictionary. You can access the value in two was as shown in the workflow example. Re-run the app when changing default parameters.
Directories where all data generated and uploaded can be stored as well as a workspace specific parameter file.

**Run the app locally and online**

Launching the app with the `local` argument let's the user create/remove workspaces. In the online the user gets a workspace with a specific ID.

**Parameters**

Parameters (defaults in `assets/default-params.json`) store changing parameters for each workspace. Parameters are loaded via the page_setup function at the start of each page. To track a widget variable via parameters simply give them a key and add a matching entry in the default parameters file. Initialize a widget value from the params dictionary.

```python
params = page_setup()
Expand All @@ -30,48 +41,29 @@ save_params()
- It is recommended to use a separate file for defining functions per page in the `src` directory.
- The `src/common.py` file contains a set of useful functions for common use (e.g. rendering a table with download button).

## Layout of the template app

The main page contains explanatory text on how to use the app.

The sidebar always contains the OpenMS logo, settings panel and a workspace indicator. The main page contains a workspace selector as well.
Workflow pages contain a selector for all available mzML files, which have been uploaded.

### Pages:

- **File Upload:** Upload files (online one at a time, local multiple), load example data or copy mzML files from directory (local only). Also show all files in the workspace and options to remove them.
- **View Raw Data:** An example page to check out mzML files in detail.
- **Workflow:** Example for a workflow page which has two inputs and a time consuming cached function. The following example code shows a minimal setup for a page and two options how to access widget values for function calls.

```python
import streamlit as st

from src.common import *
from src.workflow import *

# Page name "workflow" will show mzML file selector in sidebar
params = page_setup(page="workflow")

st.title("Workflow")

# Define two widgets with values from parameter file
# To save them as parameters use the same key as in the json file

# We access the x-dimension via local variable
xdimension = st.number_input(
label="x dimension", min_value=1, max_value=20, value=params["example-x-dimension"], step=1, key="example-x-dimension")

st.number_input(label="x dimension", min_value=1, max_value=20,
value=params["example-y-dimension"], step=1, key="example-y-dimension")

# 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 = generate_random_table(xdimension, st.session_state["example-y-dimension"])

# Display dataframe via custom show_table function, which
show_table(df, download_name="random-table")

# At the end of each page, always save parameters (including any changes via widgets with key)
save_params(params)
```
## App layout

- Main page contains explanatory text on how to use the app and a workspace selector.
- Sidebar contains the OpenMS logo, settings panel and a workspace indicator. The main page contains a workspace selector as well.
- See pages in the template app for example use cases. The content of this app serves as a documentation.

## Modify the template to build your own app

- in `src/common.py` update the name of your app and the repository name
- in `clean-up-workspaces.py` update the name of the workspaces directory to `/workspaces-<your-repository-name>`
- e.g. for the streamlit template it's "/workspaces-streamlit-template"
- chose one of the Dockerfiles depending on your use case:
- `Dockerfile` build OpenMS including TOPP tools
- `Dockerfile_simple` uses pyOpenMS only
- update the Dockerfile:
- with the `GITHUB_USER` owning the streamlit app repository
- with the `GITHUB_REPO` name of the streamlit app repository
- if your main streamlit file is not called `app.py` modfify the following line
- `RUN echo "mamba run --no-capture-output -n streamlit-env streamlit run app.py" >> /app/entrypoint.sh`
- update Python package dependency files:
- `requirements.txt` if using `Dockerfile_simple`
- `environment.yml` if using `Dockerfile`
- update `README.md`
- for the Windows executable package:
- update `datas` in `run_app_temp.spec` with the Python packages required for your app
- update main streamlit file name to run in `run_app.py`
3 changes: 1 addition & 2 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import streamlit as st
from captcha.image import ImageCaptcha
from src.common import *
from streamlit.web import cli
from pathlib import Path
import sys

Expand All @@ -20,6 +18,7 @@ def main():
data=file,
file_name="OpenMS-App.zip",
mime="archive/zip",
type="primary"
)
save_params(params)

Expand Down
49 changes: 25 additions & 24 deletions src/captcha_.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,28 +185,29 @@ def captcha_control():
if 'Captcha' not in st.session_state:
st.session_state['Captcha'] = ''.join(random.choices(string.ascii_uppercase + string.digits, k=length_captcha))

#setup the captcha widget
image = ImageCaptcha(width=width, height=height)
data = image.generate(st.session_state['Captcha'])
st.image(data)
col1, col2 = st.columns([20, 80])
capta2_text = col1.text_input('Enter captcha text')

v_space(1, col2)
if col2.button("Verify the code", type="primary"):
capta2_text = capta2_text.replace(" ", "")
# if the captcha is correct, the controllo session state is set to True
if st.session_state['Captcha'].lower() == capta2_text.lower().strip():
del st.session_state['Captcha']
col1.empty()
st.session_state['controllo'] = True
st.rerun()
col1, _ = st.columns(2)
with col1.form("captcha-form"):
#setup the captcha widget
image = ImageCaptcha(width=width, height=height)
data = image.generate(st.session_state['Captcha'])
st.image(data)
c1, c2 = st.columns([70, 30])
capta2_text = c1.text_input('Enter captcha text', max_chars=5)
c2.markdown("##")
if c2.form_submit_button("Verify the code", type="primary"):
capta2_text = capta2_text.replace(" ", "")
# if the captcha is correct, the controllo session state is set to True
if st.session_state['Captcha'].lower() == capta2_text.lower().strip():
del st.session_state['Captcha']
col1.empty()
st.session_state['controllo'] = True
st.rerun()
else:
# if the captcha is wrong, the controllo session state is set to False and the captcha is regenerated
st.error("🚨 Captch is wrong")
del st.session_state['Captcha']
del st.session_state['controllo']
st.rerun()
else:
# if the captcha is wrong, the controllo session state is set to False and the captcha is regenerated
st.error("🚨 Captch is wrong")
del st.session_state['Captcha']
del st.session_state['controllo']
st.rerun()
else:
#wait for the button click
st.stop()
#wait for the button click
st.stop()

0 comments on commit 1c9d7c0

Please sign in to comment.