###################
Adding a new module
###################
Here we provide a comprehensive overview of how to set up a new module in ProteoBench,
currently focused on quantification modules, where you only need to check and maybe
slightly modify some components. For entirely new module types, you will need
to create a new version if it says 'check, modify or add' of a component.
Terms
=====
The following terms capture the crucial components:
*Module*: All code and definitions for creating and comparing
benchmarks of a new data type.
*Intermediate data structure* (:class:`DataFrame`): Data structure needed for the
calculation of the ``datapoint``, e.g. :class:`QuantDatapointHYE`. It contains
the transformed and annotated data of an uploaded data file.
*Datapoint*: Metadata and benchmarking metrics for a given data set. A ``datapoint``, e.g. :class:`QuantDatapointHYE`,
is the data needed for the benchmarking and should also be represented by a json object.
Naming convention
=================
New modules and classes should be given descriptive names, and fit into existing naming schemes.
Go from general to specific properties, and make clear what distinguishes the module
from existing ones, e.g. :class:`DIAQuantPeptidoformModule`.
The modules are stored in the Python package ``proteobench`` in the
``modules`` subpackage: `proteobench.modules `_.
Programmatic structure
======================
The modules are located in the
`proteobench/modules `_
directory. We separated the benchmarking modules into a different steps
that allow for a more modular and portable implementation.
Backend
-------
The backend is organized into six main components that you can extend or customize:
**1. Module implementation** - Define how your benchmarking is performed
- For quantification: Subclass :class:`~proteobench.modules.quant.quant_base_module.QuantModule`
- Implement the ``benchmarking`` method with your specific logic
- Initialize with custom parameters in the ``__init__`` method
**2. Data parsing** - Convert software output to standard format
- Functions in :file:`proteobench/io/parsing/parse_ion.py` parse precursor ion data
- :file:`proteobench/io/parsing/parse_settings.py` handles format conversion
- Settings defined in TOML files in :file:`proteobench/io/parsing/io_parse_settings/`
- For new software tools, extend :func:`~proteobench.io.parsing.parse_ion.load_input_file`
**3. Score calculation** - Compute benchmarking metrics
- Base class: :class:`~proteobench.score.score_base.ScoreBase` (ABC)
- Generates the ``intermediate`` data structure
- For new module types: Create a new class inheriting from ``ScoreBase``
- See :ref:`score-configuration` for detailed information
**4. Result representation** - Store benchmark results as datapoints
- Base class: :class:`~proteobench.datapoint.datapoint_base.DatapointBase` (ABC)
- For quantification: Use or extend :class:`~proteobench.datapoint.quant_datapoint_hye.QuantDatapointHYE`
- Stores metadata and metrics for each benchmark run
- For new module types: Create a new class inheriting from ``DatapointBase``
- See :ref:`datapoint-configuration` for detailed information
**5. Visualization** - Create plots for web interface
- Base class: :class:`~proteobench.plotting.plot_generator_base.PlotGeneratorBase` (ABC)
- For LFQ modules: Use or extend :class:`~proteobench.plotting.plot_generator_lfq_HYE.LFQHYEPlotGenerator`
- Generates module-specific visualizations
- For new module types: Create a new class inheriting from ``PlotGeneratorBase``
- See :ref:`plot-configuration` for detailed information
**6. Parameter parsing** - Parse tool-specific settings
- Functions in :file:`proteobench/io/params` parse parameter setting files
- Customize per software tool in :file:`proteobench/io/params/json/`
Architecture example
....................
Here's how these components work together for a quantification module:
.. code-block:: python
# Module orchestrates the workflow
module = QuantModule()
# 1. Parse input file
input_data = parse_ion.load_input_file(file_path, "MaxQuant")
# 2. Calculate scores (generates intermediate data)
score_calculator = QuantScores(...)
intermediate = score_calculator.generate_intermediate(input_data, replicate_to_raw)
# 3. Create datapoint from intermediate results
datapoint = QuantDatapoint.generate_datapoint(intermediate, "MaxQuant", user_input)
# 4. Generate plots for visualization
plot_generator = LFQHYEPlotGenerator()
plots = plot_generator.generate_in_depth_plots(performance_data, parse_settings)
# 5. Store and display results
# ... persist datapoint and plots to results repository
.. _plot-configuration:
Plot Configuration
..................
ProteoBench uses a modular plotting architecture where each module/module group defines its own plot generation
logic by subclassing :class:`~proteobench.plotting.plot_generator_base.PlotGeneratorBase`. This
section explains how to configure plots for your module.
**Required Methods**
When creating a new plot generator, you must implement four abstract methods:
1. **generate_in_depth_plots()** - Creates the actual plot figures
.. code-block:: python
def generate_in_depth_plots(
self,
performance_data: pd.DataFrame,
parse_settings: any,
**kwargs
) -> Dict[str, go.Figure]:
"""
Generate module-specific plots.
Returns
-------
Dict[str, go.Figure]
Dictionary mapping plot names (keys) to plotly figures (values).
Example: {"logfc": fig1, "cv": fig2, "ma_plot": fig3}
"""
plots = {}
# Example: fold change histogram
plots["logfc"] = self._plot_fold_change_histogram(
performance_data,
parse_settings.species_expected_ratio()
)
# Example: CV violin plot
plots["cv"] = self._plot_cv_violinplot(performance_data)
return plots
2. **get_in_depth_plot_layout()** - Defines how in-depth plots are displayed in the web interface
.. code-block:: python
def get_in_depth_plot_layout(self) -> list:
"""
Define layout configuration for displaying plots.
Returns
-------
list
List of dictionaries, each defining a row of plots with:
- "plots": list of plot names (keys from generate_in_depth_plots)
- "columns": number of columns (1 or 2)
- "titles": dict mapping plot names to display titles
"""
return [
{
"plots": ["logfc", "cv"], # Two plots side-by-side
"columns": 2,
"titles": {
"logfc": "Log2 Fold Change distributions by species",
"cv": "Coefficient of variation distribution",
},
},
{
"plots": ["ma_plot"], # Single plot, full width
"columns": 1,
"titles": {"ma_plot": "MA plot"},
},
]
3. **get_in_depth_plot_descriptions()** - Provides help text for each plot
.. code-block:: python
def get_in_depth_plot_descriptions(self) -> Dict[str, str]:
"""
Get descriptions shown to users for each plot.
Returns
-------
Dict[str, str]
Dictionary mapping plot names to descriptions
"""
return {
"logfc": "Log2 fold changes calculated from the performance data",
"cv": "Coefficients of variation in Condition A and B",
"ma_plot": "MA plot showing mean vs fold change",
}
4. **plot_main_metric()** - Creates the main benchmarking plot (Tab 1)
.. code-block:: python
def plot_main_metric(
self,
result_df: pd.DataFrame,
metric: str = "Median",
hide_annot: bool = False,
**kwargs
) -> go.Figure:
"""
Generate the main performance metric plot.
This plot appears on Tab 1 and shows all datapoints across time
or other primary axis.
Parameters
----------
result_df : pd.DataFrame
DataFrame with columns like 'results', 'software_name', etc.
metric : str
Which metric to display (e.g., "Median", "Mean")
hide_annot : bool
Whether to hide annotations
**kwargs
Additional module-specific parameters
Returns
-------
go.Figure
Plotly figure for the main metric
"""
# Extract metric values from results
all_metrics = [
v2["median_abs_epsilon"]
for v in result_df["results"]
for v2 in v.values()
]
# Create scatter plot
fig = go.Figure()
fig.add_trace(go.Scatter(
x=result_df.index,
y=all_metrics,
mode='markers',
# ... additional configuration
))
return fig
**Example: Creating a Custom Plot Generator**
Here's a complete example for a hypothetical peptide identification module:
.. code-block:: python
from proteobench.plotting.plot_generator_base import PlotGeneratorBase
import plotly.graph_objects as go
class PeptideIDPlotGenerator(PlotGeneratorBase):
"""Plot generator for peptide identification modules."""
def generate_in_depth_plots(self, performance_data, **kwargs):
plots = {}
plots["score_dist"] = self._plot_score_distribution(performance_data)
plots["fdr_curve"] = self._plot_fdr_curve(performance_data)
return plots
def get_in_depth_plot_layout(self):
return [
{
"plots": ["score_dist", "fdr_curve"],
"columns": 2,
"titles": {
"score_dist": "Score Distribution",
"fdr_curve": "FDR vs Number of IDs",
},
}
]
def get_in_depth_plot_descriptions(self):
return {
"score_dist": "Distribution of identification scores",
"fdr_curve": "Trade-off between FDR and identification count",
}
def plot_main_metric(self, result_df, **kwargs):
# Implementation of main metric plot
fig = go.Figure()
# ... create plot
return fig
def _plot_score_distribution(self, data):
"""Helper method to create score distribution plot."""
fig = go.Figure()
# ... implementation
return fig
def _plot_fdr_curve(self, data):
"""Helper method to create FDR curve plot."""
fig = go.Figure()
# ... implementation
return fig
.. _score-configuration:
Score Configuration
...................
ProteoBench uses a modular score calculation architecture where each module defines how to compute
benchmarking metrics by subclassing :class:`~proteobench.score.score_base.ScoreBase`. This section
explains how to configure score calculators for your module.
**Required Methods**
When creating a new score calculator, you must implement the abstract method from ``ScoreBase``:
1. **generate_intermediate()** - Computes metrics and creates the intermediate data structure
.. code-block:: python
def generate_intermediate(
self,
filtered_df: pd.DataFrame,
replicate_to_raw: dict,
) -> pd.DataFrame:
"""
Generate intermediate data structure with computed scores.
This method processes the parsed input data and computes all
benchmarking metrics needed for the module.
Parameters
----------
filtered_df : pd.DataFrame
Parsed and filtered input data from the software tool
replicate_to_raw : dict
Mapping of replicate names to raw file names
Example: {"A": ["run1.raw", "run2.raw"], "B": ["run3.raw", "run4.raw"]}
Returns
-------
pd.DataFrame
Intermediate dataframe containing:
- Original input data columns
- Computed metrics (fold changes, statistics, etc.)
- Additional annotations needed for benchmarking
"""
# 1. Select relevant columns from input
relevant_data = filtered_df[["Raw file", "Precursor", "Intensity"]].copy()
# 2. Add condition/replicate information
replicate_df = self._convert_replicate_mapping(replicate_to_raw)
relevant_data = pd.merge(relevant_data, replicate_df, on="Raw file")
# 3. Compute condition statistics (mean, CV, etc.)
stats_df = self._compute_condition_stats(relevant_data)
# 4. Add species annotations
species_df = filtered_df[["Precursor", "HUMAN", "YEAST", "ECOLI"]].drop_duplicates()
stats_df = pd.merge(stats_df, species_df, on="Precursor")
# 5. Compute performance metrics (epsilon, fold changes, etc.)
result = self._compute_metrics(stats_df, self.expected_ratios)
return result
**Example: Creating a Custom Score Calculator**
Here's a complete example for a hypothetical peptide identification module:
.. code-block:: python
from proteobench.score.score_base import ScoreBase
import pandas as pd
import numpy as np
class PeptideIDScores(ScoreBase):
"""Score calculator for peptide identification modules."""
def __init__(self, fdr_threshold: float = 0.01):
"""
Initialize the score calculator.
Parameters
----------
fdr_threshold : float
FDR threshold for filtering identifications
"""
self.fdr_threshold = fdr_threshold
def generate_intermediate(
self,
filtered_df: pd.DataFrame,
replicate_to_raw: dict,
) -> pd.DataFrame:
"""
Generate intermediate data for peptide ID benchmarking.
Computes metrics like:
- Number of identifications at different FDR levels
- Score distributions
- Decoy hit rates
"""
# 1. Filter by FDR
filtered_df = filtered_df[filtered_df["q_value"] <= self.fdr_threshold]
# 2. Add replicate information
raw_to_replicate = self._invert_mapping(replicate_to_raw)
filtered_df["replicate"] = filtered_df["Raw file"].map(raw_to_replicate)
# 3. Compute identification statistics per replicate
stats = filtered_df.groupby("replicate").agg({
"Peptide": "nunique", # Unique peptides
"Protein": "nunique", # Unique proteins
"Score": ["mean", "std"], # Score statistics
}).reset_index()
# 4. Compute overlap between replicates
stats["overlap_score"] = self._compute_overlap(filtered_df)
# 5. Add decoy information
stats["decoy_rate"] = self._compute_decoy_rate(filtered_df)
return stats
def _invert_mapping(self, replicate_to_raw: dict) -> dict:
"""Convert replicate->raw mapping to raw->replicate."""
return {
raw: replicate
for replicate, raws in replicate_to_raw.items()
for raw in raws
}
def _compute_overlap(self, df: pd.DataFrame) -> float:
"""Compute peptide overlap between replicates."""
replicates = df["replicate"].unique()
if len(replicates) < 2:
return 1.0
peptide_sets = [
set(df[df["replicate"] == rep]["Peptide"])
for rep in replicates
]
# Jaccard similarity
intersection = set.intersection(*peptide_sets)
union = set.union(*peptide_sets)
return len(intersection) / len(union) if union else 0.0
def _compute_decoy_rate(self, df: pd.DataFrame) -> float:
"""Compute rate of decoy hits."""
total = len(df)
decoys = len(df[df["Protein"].str.contains("DECOY")])
return decoys / total if total > 0 else 0.0
.. _datapoint-configuration:
Datapoint Configuration
.......................
ProteoBench uses datapoints to store metadata and benchmarking results for each submission. Each module
defines its datapoint structure by subclassing :class:`~proteobench.datapoint.datapoint_base.DatapointBase`.
This section explains how to configure datapoints for your module.
**Required Components**
When creating a new datapoint class, you must:
1. **Define datapoint attributes** using ``@dataclass`` decorator
.. code-block:: python
from dataclasses import dataclass
from proteobench.datapoint.datapoint_base import DatapointBase
@dataclass
class QuantDatapointHYE(DatapointBase):
"""
Datapoint for LFQ quantification benchmarking.
Stores both metadata (search parameters, software versions) and
results (benchmarking metrics at different filtering levels).
"""
# Metadata fields
id: str
software_name: str
software_version: str
search_engine: str
ident_fdr_psm: float
enable_match_between_runs: bool
enzyme: str
# ... other metadata ...
# Results
results: dict # Contains metrics at different filters
2. **Implement generate_id()** - Create a unique identifier
.. code-block:: python
def generate_id(self) -> None:
"""
Generate a unique hash ID for this benchmark run.
The ID should be deterministic based on the input data and settings,
allowing detection of duplicate submissions.
"""
# Combine relevant fields into a string
id_string = (
f"{self.software_name}_{self.software_version}_"
f"{self.search_engine}_{self.ident_fdr_psm}_"
f"{self.enable_match_between_runs}_{self.enzyme}"
# ... include all distinguishing parameters ...
)
# Create hash
import hashlib
self.id = hashlib.sha256(id_string.encode()).hexdigest()[:10]
3. **Implement generate_datapoint()** - Create datapoint from intermediate data
.. code-block:: python
@staticmethod
def generate_datapoint(
intermediate: pd.DataFrame,
input_format: str,
user_input: dict,
**kwargs
) -> pd.Series:
"""
Generate a datapoint from intermediate data and user input.
Parameters
----------
intermediate : pd.DataFrame
Intermediate DataFrame with computed metrics
input_format : str
Software tool name (e.g., "MaxQuant", "FragPipe")
user_input : dict
Dictionary containing metadata from submission form
Returns
-------
pd.Series
Series containing the datapoint data, ready to be added to results
"""
# 1. Compute metrics at different filtering levels
results = {}
for min_obs in [2, 3, 4, 5]:
filtered = intermediate[intermediate["nr_observed"] >= min_obs]
results[min_obs] = {
"median_abs_epsilon": filtered["abs_epsilon"].median(),
"mean_abs_epsilon": filtered["abs_epsilon"].mean(),
"nr_prec": len(filtered),
}
# 2. Create datapoint object
import dataclasses
datapoint = QuantDatapointHYE(
id="", # Will be set by generate_id()
software_name=input_format,
software_version=user_input.get("software_version", "unknown"),
search_engine=user_input.get("search_engine", input_format),
ident_fdr_psm=user_input.get("ident_fdr_psm", 0.01),
enable_match_between_runs=user_input.get("enable_match_between_runs", False),
enzyme=user_input.get("enzyme", "Trypsin"),
results=results,
)
# 3. Generate unique ID
datapoint.generate_id()
# 4. Convert to pandas Series for storage
return pd.Series(dataclasses.asdict(datapoint))
**Example: Creating a Custom Datapoint**
Here's a complete example for a hypothetical peptide identification module:
.. code-block:: python
from dataclasses import dataclass
from proteobench.datapoint.datapoint_base import DatapointBase
import hashlib
import dataclasses
import pandas as pd
@dataclass
class PeptideIDDatapoint(DatapointBase):
"""Datapoint for peptide identification benchmarking."""
# Metadata
id: str
software_name: str
software_version: str
database: str
fdr_threshold: float
# Results
num_peptides: int
num_proteins: int
replicate_overlap: float
def generate_id(self) -> None:
"""Generate unique ID based on metadata."""
id_string = (
f"{self.software_name}_{self.software_version}_"
f"{self.database}_{self.fdr_threshold}"
)
self.id = hashlib.sha256(id_string.encode()).hexdigest()[:10]
@staticmethod
def generate_datapoint(
intermediate: pd.DataFrame,
input_format: str,
user_input: dict,
**kwargs
) -> pd.Series:
"""Generate datapoint from intermediate results."""
metrics = intermediate.iloc[0] # Assuming aggregated data
datapoint = PeptideIDDatapoint(
id="",
software_name=input_format,
software_version=user_input.get("software_version", "unknown"),
database=user_input.get("database", "unknown"),
fdr_threshold=user_input.get("fdr_threshold", 0.01),
num_peptides=int(metrics["num_peptides"]),
num_proteins=int(metrics["num_proteins"]),
replicate_overlap=float(metrics["overlap_score"]),
)
datapoint.generate_id()
return pd.Series(dataclasses.asdict(datapoint))
Web interface
-------------
The web interface is written in Streamlit. Each module gets assigned a
specific ``page``. There are only few changes necessary
as the main calculations are done in
:class:`~webinterface.pages.base_pages.quant.QuantUIObjects`. It contains most
functionality to create the web interface for each quantification module.
.. warning::
QuantUIObjects should be simplified.
:file:`webinterface.pages.pages_variables` contains files with ``dataclass``\ es for the
text for the different modules in the interface.
Module Variables and Sidebar Configuration
...........................................
Each module requires a variables file in
`webinterface/pages/pages_variables `_
that defines both the module's behavior and its appearance in the sidebar navigation.
**Creating a Module Variables File**
Create a new file following the pattern ``_variables.py`` (e.g., ``lfq_DDA_ion_Astral_variables.py``)
in the appropriate subdirectory under ``pages_variables/`` (e.g., ``Quant/``).
**Required Sidebar Metadata Fields**
Your variables dataclass must include these fields for the module registry to discover and display it:
.. code-block:: python
from dataclasses import dataclass, field
from typing import List
@dataclass
class VariablesDDAQuantAstral:
# ... other module-specific fields ...
# Sidebar metadata (required)
sidebar_label: str = "Quant LFQ DDA ion Astral"
sidebar_path: str = "/Quant_LFQ_DDA_ion_Astral"
sidebar_category: str = "DDA"
keywords: List[str] = field(
default_factory=lambda: ["DDA", "quantification", "Astral", "orbitrap", "precursor", "ion", "LFQ"]
)
# Module metadata (required)
title: str = "DDA Quantification - Precursor Level - Astral"
doc_url: str = "https://proteobench.readthedocs.io/en/latest/available-modules/quant_lfq_DDA_ion_Astral.html"
**Sidebar Configuration Details:**
- **sidebar_label**: Display name shown in the sidebar (keep concise but descriptive)
- **sidebar_path**: URL path for the page (must match the page filename without numeric prefix)
- Example: If your page file is ``8_Quant_LFQ_DDA_ion_Astral.py``, use ``/Quant_LFQ_DDA_ion_Astral``
- **sidebar_category**: Determines which expandable section contains the module
- Valid values: ``"DDA"``, ``"DIA"``, ``"DDA Id"``, ``"Archived"``
- Modules are grouped by this category in the sidebar
- **keywords**: List of search terms for the sidebar search functionality
- Users can filter modules by typing any of these keywords
- Include: data type, technique, instrument, software names, etc.
- **title**: Full descriptive title used in documentation and metadata
- **doc_url**: Link to the module's documentation page
**Release Stage Badges**
ProteoBench automatically displays release stage badges (ALPHA, BETA, ARCHIVED) in the sidebar
based on warning flags in your variables file. The badge is determined by this priority:
1. **Archived** - If ``archived_warning: bool = True`` → Gray "ARCH" badge
2. **Alpha** - If ``alpha_warning: bool = True`` → Orange "ALPHA" badge
3. **Beta** - If ``beta_warning: bool = True`` → Blue "BETA" badge
4. **Live** - If none of the above → No badge (production-ready)
**Example: Alpha Stage Module**
.. code-block:: python
@dataclass
class VariablesDIAQuantSingleCell:
# ... other fields ...
# Release stage flags
alpha_warning: bool = True # Shows ALPHA badge
beta_warning: bool = False
# Sidebar metadata
sidebar_label: str = "Quant LFQ DIA ion Single Cell"
sidebar_path: str = "/Quant_LFQ_DIA_ion_singlecell"
sidebar_category: str = "DIA"
keywords: List[str] = field(
default_factory=lambda: ["DIA", "quantification", "single cell", "Astral"]
)
**Important Notes:**
- The module registry automatically discovers all ``*_variables.py`` files at startup
- No manual registration is needed - just create the file with the required fields
- The release stage badge appears automatically based on the warning flags
- When a module is moved to ``"Archived"`` category, it gets the ARCH badge regardless of other flags
- The sidebar search filters modules by matching the search query against:
- ``sidebar_label`` (display name)
- ``title`` (full title)
- ``keywords`` (all keyword strings)
Relevant functions in :class:`~webinterface.pages.base_pages.quant.QuantUIObjects`
...................................................................................
- Tab 1: :meth:`~webinterface.pages.base_pages.quant.QuantUIObjects.display_all_data_results_main`
shows the description of the module, which is defined in
`webinterface/pages/pages_variables `_
where we define custom text and **unique** component names for each module
(e.g. for the main plot)
to not display on several pages the same plot in the streamlit webinterface.
- Tab 2: :meth:`~webinterface.pages.base_pages.quant.QuantUIObjects.display_submission_form`
displays the submission form based on the module toml configurations in
`proteobench/io/parsing/io_parse_settings `_.
- Tab 2.5: :meth:`~webinterface.pages.base_pages.quant.QuantUIObjects.generate_current_data_plots`
displays the metric plot if a new results were added to the module.
- Tab 3: :meth:`~webinterface.pages.base_pages.quant.QuantUIObjects.display_all_data_results_submitted`
- Tab 4: :meth:`~webinterface.pages.base_pages.quant.QuantUIObjects.display_public_submission_ui`
creates the input fields for the metadata and the
input file format and type. They are given in the
`proteobench/modules/parsing/io_parse_settings `_ folder,
same as for the backend of the module.
:meth:`~webinterface.pages.base_pages.quant.QuantUIObjects.generate_results` gathers the data from the backend
and displays them in several figures. Here you will need to edit and adapt the code
to show the respective figures with the right metadata.
Change the text and the field names accordingly in the ``dataclass``
in `webinterface.pages.pages_variables `_.
Creating a Module Page with StreamlitUI
........................................
Each module page inherits from :class:`~webinterface.pages.base.BaseStreamlitUI`,
which provides a standardized structure for creating multi-tab interfaces with minimal code duplication.
**BaseStreamlitUI Architecture**
The base class handles common functionality:
- Tab creation and rendering
- Standard header elements (title, documentation link, banner)
- Integration with module backend and UI objects
**For Standard Quantification Modules (6 tabs)**
Most quantification modules use the default 6-tab configuration and require minimal code:
.. code-block:: python
# webinterface/pages/2_Quant_LFQ_DDA_ion_QExactive.py
from pages.base import BaseStreamlitUI
from pages.base_pages.quant import QuantUIObjects
from pages.pages_variables.Quant.lfq_DDA_ion_QExactive_variables import VariablesDDAQuant
from pages.texts.generic_texts import WebpageTexts
from proteobench.io.parsing.parse_settings import ParseSettingsBuilder
from proteobench.modules.quant.quant_lfq_ion_DDA_QExactive import DDAQuantIonModuleQExactive
if __name__ == "__main__":
# Instantiate and run directly - no subclass needed
st_ui = BaseStreamlitUI(
variables=VariablesDDAQuant(),
texts=WebpageTexts,
ionmodule=DDAQuantIonModuleQExactive,
parsesettingsbuilder=ParseSettingsBuilder,
uiobjects=QuantUIObjects,
page_name="Quant LFQ DDA ion QExactive",
)
st_ui.main_page()
**Default Tab Configuration (Quant Modules)**
The default :meth:`~webinterface.pages.base.BaseStreamlitUI.get_tab_config` returns:
1. **View Public Results** → ``display_all_data_results_main()``
2. **Upload New Results (Private)** → ``display_submission_form()``
3. **View Single Result** → ``display_indepth_plots()``
4. **View Public + New Results** → ``display_all_data_results_submitted()``
5. **Compare Two Results** → ``display_workflow_comparison()``
6. **Submit New Results** → ``display_public_submission_ui()``
**For Custom Tab Configurations**
If your module needs a different number or order of tabs, create a subclass and override
:meth:`~webinterface.pages.base.BaseStreamlitUI.get_tab_config`:
.. code-block:: python
# webinterface/pages/7_denovo_DDA_HCD.py
from pages.base import BaseStreamlitUI
from pages.base_pages.denovo import DeNovoUIObjects
class StreamlitUI(BaseStreamlitUI):
"""Streamlit UI for the DDA de novo identification module."""
def get_tab_config(self) -> list:
"""Override tab configuration for De Novo module (5 tabs instead of 6)."""
return [
("View Public Results", "display_all_data_results_main"),
("Upload New Results (Private)", "display_submission_form"),
("View Public + New Results", "display_all_data_results_submitted"),
("Compare Results", "display_indepth_plots"),
("Submit New Results", "display_public_submission_ui"),
]
if __name__ == "__main__":
st_ui = StreamlitUI(
variables=VariablesDDADeNovo(),
texts=WebpageTexts,
ionmodule=DDAHCDDeNovoModule,
parsesettingsbuilder=ParseSettingsBuilder,
uiobjects=DeNovoUIObjects,
page_name="De novo DDA-HCD peptidoform",
)
st_ui.main_page()
**Tab Configuration Format**
The :meth:`~webinterface.pages.base.BaseStreamlitUI.get_tab_config` method returns a list of tuples:
.. code-block:: python
[
(tab_display_name, uiobjects_method_name),
...
]
- **tab_display_name**: String shown in the tab header
- **uiobjects_method_name**: Name of the method to call on the ``uiobjects`` instance
Each method is automatically called when its tab is rendered, with the standard header
(title, documentation link, banner) added automatically by
:meth:`~webinterface.pages.base.BaseStreamlitUI._render_tab_header`.
Storing results
----------------
Results are stored in separate GitHub repositories, where the Webinterface first adds
datapoints to an fork of the module-specific results directory. The core
functionality is in
`proteobench.github.gh `_
1. Make a new repository in the
`Proteobench organisation `_
and give it a sensible name, e.g. ``Proteobench/Results_quant_ion_DDA``.
2. Login to `Proteobot organisation `_
(ask for the login details from relevant people)
3. Make a fork of the new repository under ``ProteoBench`` to ``Proteobot``
Documentation
-------------
We strongly recommend to keep documenting your code. The documentation is written in Markdown or richtext
and can be found in the `docs `_ folder. We
use Sphinx and myst-parser to build the website.
1. `docs/available-modules `_
Here you can add a file for your new module, using any of the existing module descriptions as a template.
2. `API documentation for your module `_
will be added automatically. You can see it on the readthedocs page built specifically for your pull request.
To work locally on the documentation and get a live preview, install the requirements and run
`sphinx-autobuild`:
.. code-block:: sh
pip install '.[docs]'
# selecting the docs folder to watch for changes
sphinx-autobuild --watch ./docs ./docs/source/ ./docs/_build/html/
Then browse to http://localhost:8000 to watch the live preview.
Checklist
=========
This checklist is meant to help you add a new module to ProteoBench. It is not
meant to be exhaustive, but it should cover the most important steps. To see which
files need to change for adding a module, have a look at one of the
recent examples. Adding a quant module (based on other quant modules):
`PR 703 `_. Or for adding
a new type of module:
`PR 727 `_.
1. Subclass :class:`~proteobench.modules.quant.quant_base_module.QuantModule` and replace
the :func:`benchmarking` method with your own implementation. You can copy from other
modules in the folder
`proteobench/modules `_
2. Define the input formats using toml files in a new subfolder of
`proteobench/io/parsing/io_parse_settings `_
3. Check, modify or add a parsing procedures in
`proteobench/io/parsing `_
e.g. :file:`parse_ion.py` or :file:`parse_peptidoform.py`.
4. **Create a datapoint class** by subclassing
:class:`~proteobench.datapoint.datapoint_base.DatapointBase` in
`proteobench/datapoint `_.
Define attributes for metadata and results, then implement:
- :meth:`generate_id` - Create unique identifier for the benchmark run
- :meth:`generate_datapoint` - Generate datapoint from intermediate data
See :ref:`datapoint-configuration` for detailed examples.
5. **Create a score calculator** by subclassing
:class:`~proteobench.score.score_base.ScoreBase` in
`proteobench/score `_.
Implement the required method:
- :meth:`generate_intermediate` - Compute metrics and create intermediate data structure
See :ref:`score-configuration` for detailed examples.
6. **Create a plot generator** by subclassing
:class:`~proteobench.plotting.plot_generator_base.PlotGeneratorBase` in
`proteobench/plotting `_.
Implement the four required methods:
- :meth:`generate_in_depth_plots` - Create plot figures
- :meth:`get_in_depth_plot_layout` - Define plot display layout
- :meth:`get_in_depth_plot_descriptions` - Provide plot descriptions
- :meth:`plot_main_metric` - Create the main benchmarking plot
See :ref:`plot-configuration` for detailed examples.
7. Check, modify or add parameter parsing for new tools in
`proteobench/io/params `_
8. **Create a module variables file** in
`webinterface/pages/pages_variables `_
with a ``dataclass`` defining all module-specific settings. **Required fields:**
- **Sidebar metadata** (for automatic discovery and navigation):
- ``sidebar_label: str`` - Display name in sidebar
- ``sidebar_path: str`` - URL path (must match page filename without numeric prefix)
- ``sidebar_category: str`` - Category: ``"DDA"``, ``"DIA"``, ``"DDA Id"``, or ``"Archived"``
- ``keywords: List[str]`` - Search terms for sidebar filtering
- **Module metadata**:
- ``title: str`` - Full descriptive title
- ``doc_url: str`` - Link to documentation page
- **Release stage** (determines badge in sidebar):
- For **Alpha**: Set ``alpha_warning: bool = True`` (orange ALPHA badge)
- For **Beta**: Set ``beta_warning: bool = True`` (blue BETA badge)
- For **Archived**: Set ``sidebar_category: str = "Archived"`` (gray ARCH badge)
- For **Production**: Don't set any warning flags (no badge)
The module will be automatically discovered by the module registry system.
See the "Module Variables and Sidebar Configuration" section above for detailed examples.
9. Add a new page file to
`webinterface/pages `_
using the base functionality and your ``pages_variables`` dataclass. Name the file with
an optional numeric prefix for ordering (e.g., ``8_Quant_LFQ_DDA_ion_Astral.py``).
10. Create a new results repository for the module in
`Proteobench `_ and
a fork in `Proteobot `_