changeset 457:5b625d91bc7f draft

Uploaded
author francesco_lapi
date Wed, 17 Sep 2025 14:26:58 +0000
parents a6e45049c1b9
children 5fcbde1f92f7
files COBRAxy/custom_data_generator_beta.py COBRAxy/custom_data_generator_beta.xml COBRAxy/flux_simulation_beta.xml COBRAxy/flux_to_map.xml COBRAxy/metabolic_model_setting.py COBRAxy/metabolic_model_setting.xml COBRAxy/ras_to_bounds_beta.xml
diffstat 7 files changed, 406 insertions(+), 411 deletions(-) [+]
line wrap: on
line diff
--- a/COBRAxy/custom_data_generator_beta.py	Fri Sep 12 17:28:45 2025 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-"""
-Custom data generator for COBRA models.
-
-This script loads a COBRA model (built-in or custom), optionally applies
-medium and gene nomenclature settings, derives reaction-related metadata
-(GPR rules, formulas, bounds, objective coefficients, medium membership,
-and compartments for ENGRO2), and writes a tabular summary.
-"""
-
-import os
-import csv
-import cobra
-import argparse
-import pandas as pd
-import utils.general_utils as utils
-from typing import Optional, Tuple, List
-import utils.model_utils as modelUtils
-import logging
-
-ARGS : argparse.Namespace
-def process_args(args: List[str] = None) -> argparse.Namespace:
-    """
-    Parse command-line arguments for CustomDataGenerator.
-    """
-
-    parser = argparse.ArgumentParser(
-        usage="%(prog)s [options]",
-        description="Generate custom data from a given model"
-    )
-
-    parser.add_argument("--out_log", type=str, required=True,
-                        help="Output log file")
-
-    parser.add_argument("--model", type=str,
-                        help="Built-in model identifier (e.g., ENGRO2, Recon, HMRcore)")
-    parser.add_argument("--input", type=str,
-                        help="Custom model file (JSON or XML)")
-    parser.add_argument("--name", type=str, required=True,
-                        help="Model name (default or custom)")
-    
-    parser.add_argument("--medium_selector", type=str, required=True,
-                        help="Medium selection option")
-
-    parser.add_argument("--gene_format", type=str, default="Default",
-                        help="Gene nomenclature format: Default (original), ENSNG, HGNC_SYMBOL, HGNC_ID, ENTREZ")
-    
-    parser.add_argument("--out_tabular", type=str,
-                        help="Output file for the merged dataset (CSV or XLSX)")
-    
-    parser.add_argument("--tool_dir", type=str, default=os.path.dirname(__file__),
-                        help="Tool directory (passed from Galaxy as $__tool_directory__)")
-
-
-    return parser.parse_args(args)
-
-################################- INPUT DATA LOADING -################################
-def load_custom_model(file_path :utils.FilePath, ext :Optional[utils.FileFormat] = None) -> cobra.Model:
-    """
-    Loads a custom model from a file, either in JSON, XML, MAT, or YML format.
-
-    Args:
-        file_path : The path to the file containing the custom model.
-        ext : explicit file extension. Necessary for standard use in galaxy because of its weird behaviour.
-
-    Raises:
-        DataErr : if the file is in an invalid format or cannot be opened for whatever reason.    
-    
-    Returns:
-        cobra.Model : the model, if successfully opened.
-    """
-    ext = ext if ext else file_path.ext
-    try:
-        if ext is utils.FileFormat.XML:
-            return cobra.io.read_sbml_model(file_path.show())
-        
-        if ext is utils.FileFormat.JSON:
-            return cobra.io.load_json_model(file_path.show())
-
-        if ext is utils.FileFormat.MAT:
-            return cobra.io.load_matlab_model(file_path.show())
-
-        if ext is utils.FileFormat.YML:
-            return cobra.io.load_yaml_model(file_path.show())
-
-    except Exception as e: raise utils.DataErr(file_path, e.__str__())
-    raise utils.DataErr(
-        file_path,
-        f"Unrecognized format '{file_path.ext}'. Only JSON, XML, MAT, YML are supported."
-    )
-
-
-###############################- FILE SAVING -################################
-def save_as_csv_filePath(data :dict, file_path :utils.FilePath, fieldNames :Tuple[str, str]) -> None:
-    """
-    Saves any dictionary-shaped data in a .csv file created at the given file_path as FilePath.
-
-    Args:
-        data : the data to be written to the file.
-        file_path : the path to the .csv file.
-        fieldNames : the names of the fields (columns) in the .csv file.
-    
-    Returns:
-        None
-    """
-    with open(file_path.show(), 'w', newline='') as csvfile:
-        writer = csv.DictWriter(csvfile, fieldnames = fieldNames, dialect="excel-tab")
-        writer.writeheader()
-
-        for key, value in data.items():
-            writer.writerow({ fieldNames[0] : key, fieldNames[1] : value })
-
-def save_as_csv(data :dict, file_path :str, fieldNames :Tuple[str, str]) -> None:
-    """
-    Saves any dictionary-shaped data in a .csv file created at the given file_path as string.
-
-    Args:
-        data : the data to be written to the file.
-        file_path : the path to the .csv file.
-        fieldNames : the names of the fields (columns) in the .csv file.
-    
-    Returns:
-        None
-    """
-    with open(file_path, 'w', newline='') as csvfile:
-        writer = csv.DictWriter(csvfile, fieldnames = fieldNames, dialect="excel-tab")
-        writer.writeheader()
-
-        for key, value in data.items():
-            writer.writerow({ fieldNames[0] : key, fieldNames[1] : value })
-
-def save_as_tabular_df(df: pd.DataFrame, path: str) -> None:
-    """
-    Save a pandas DataFrame as a tab-separated file, creating directories as needed.
-
-    Args:
-        df: The DataFrame to write.
-        path: Destination file path (will be written as TSV).
-
-    Raises:
-        DataErr: If writing the output fails for any reason.
-
-    Returns:
-        None
-    """
-    try:
-        os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
-        df.to_csv(path, sep="\t", index=False)
-    except Exception as e:
-        raise utils.DataErr(path, f"failed writing tabular output: {e}")
-
-
-###############################- ENTRY POINT -################################
-def main(args:List[str] = None) -> None:
-    """
-    Initialize and generate custom data based on the frontend input arguments.
-    
-    Returns:
-        None
-    """
-    # Parse args from frontend (Galaxy XML)
-    global ARGS
-    ARGS = process_args(args)
-
-
-    if ARGS.input:
-        # Load a custom model from file
-        model = load_custom_model(
-            utils.FilePath.fromStrPath(ARGS.input), utils.FilePath.fromStrPath(ARGS.name).ext)
-    else:
-        # Load a built-in model
-
-        try:
-            model_enum = utils.Model[ARGS.model]  # e.g., Model['ENGRO2']
-        except KeyError:
-            raise utils.ArgsErr("model", "one of Recon/ENGRO2/HMRcore/Custom_model", ARGS.model)
-
-        # Load built-in model (Model.getCOBRAmodel uses tool_dir to locate local models)
-        try:
-            model = model_enum.getCOBRAmodel(toolDir=ARGS.tool_dir)
-        except Exception as e:
-            # Wrap/normalize load errors as DataErr for consistency
-            raise utils.DataErr(ARGS.model, f"failed loading built-in model: {e}")
-
-    # Determine final model name: explicit --name overrides, otherwise use the model id
-    
-    model_name = ARGS.name if ARGS.name else ARGS.model
-    
-    if ARGS.name == "ENGRO2" and ARGS.medium_selector != "Default":
-        df_mediums = pd.read_csv(ARGS.tool_dir + "/local/medium/medium.csv", index_col = 0)
-        ARGS.medium_selector = ARGS.medium_selector.replace("_", " ")
-        medium = df_mediums[[ARGS.medium_selector]]
-        medium = medium[ARGS.medium_selector].to_dict()
-
-        # Reset all medium reactions lower bound to zero
-        for rxn_id, _ in model.medium.items():
-            model.reactions.get_by_id(rxn_id).lower_bound = float(0.0)
-        
-        # Apply selected medium uptake bounds (negative for uptake)
-        for reaction, value in medium.items():
-            if value is not None:
-                model.reactions.get_by_id(reaction).lower_bound = -float(value)
-
-    if (ARGS.name == "Recon" or ARGS.name == "ENGRO2") and ARGS.gene_format != "Default":
-        logging.basicConfig(level=logging.INFO)
-        logger = logging.getLogger(__name__)
-
-        model = modelUtils.translate_model_genes(
-            model=model,
-            mapping_df= pd.read_csv(ARGS.tool_dir + "/local/mappings/genes_human.csv", dtype={'entrez_id': str}),
-            target_nomenclature=ARGS.gene_format,
-            source_nomenclature='HGNC_symbol',
-            logger=logger
-        )
-
-    # generate data
-    rules = modelUtils.generate_rules(model, asParsed = False)
-    reactions = modelUtils.generate_reactions(model, asParsed = False)
-    bounds = modelUtils.generate_bounds(model)
-    medium = modelUtils.get_medium(model)
-    objective_function = modelUtils.extract_objective_coefficients(model)
-    
-    if ARGS.name == "ENGRO2":
-        compartments = modelUtils.generate_compartments(model)
-
-    df_rules = pd.DataFrame(list(rules.items()), columns = ["ReactionID", "GPR"])
-    df_reactions = pd.DataFrame(list(reactions.items()), columns = ["ReactionID", "Formula"])
-
-    df_bounds = bounds.reset_index().rename(columns = {"index": "ReactionID"})
-    df_medium = medium.rename(columns = {"reaction": "ReactionID"})
-    df_medium["InMedium"] = True
-
-    merged = df_reactions.merge(df_rules, on = "ReactionID", how = "outer")
-    merged = merged.merge(df_bounds, on = "ReactionID", how = "outer")
-    merged = merged.merge(objective_function, on = "ReactionID", how = "outer")
-    if ARGS.name == "ENGRO2": 
-        merged = merged.merge(compartments, on = "ReactionID", how = "outer")
-    merged = merged.merge(df_medium, on = "ReactionID", how = "left")
-
-    merged["InMedium"] = merged["InMedium"].fillna(False)
-
-    merged = merged.sort_values(by = "InMedium", ascending = False)
-
-    if not ARGS.out_tabular:
-        raise utils.ArgsErr("out_tabular", "output path (--out_tabular) is required when output_format == tabular", ARGS.out_tabular)
-    save_as_tabular_df(merged, ARGS.out_tabular)
-    expected = ARGS.out_tabular
-
-    # verify output exists and non-empty
-    if not expected or not os.path.exists(expected) or os.path.getsize(expected) == 0:
-        raise utils.DataErr(expected, "Output not created or empty")
-
-    print("CustomDataGenerator: completed successfully")
-
-if __name__ == '__main__':
-    main()
\ No newline at end of file
--- a/COBRAxy/custom_data_generator_beta.xml	Fri Sep 12 17:28:45 2025 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-<tool id="CustomDataGenerator - Beta" name="Custom Data Generator - BETA" version="2.0.0">
-
-	<requirements>
-        <requirement type="package" version="1.24.4">numpy</requirement>
-        <requirement type="package" version="2.0.3">pandas</requirement>
-		<requirement type="package" version="0.29.0">cobra</requirement>
-        <requirement type="package" version="5.2.2">lxml</requirement>
-	</requirements>
-
-    <macros>
-        <import>marea_macros.xml</import>
-    </macros>
-
-    <command detect_errors="exit_code">
-        <![CDATA[
-      	python $__tool_directory__/custom_data_generator_beta.py
-        --tool_dir $__tool_directory__
-        --medium_selector $cond_model.cond_medium.medium_selector
-        #if $cond_model.model_selector == 'Custom_model'
-            --input $cond_model.input
-            --name $cond_model.input.element_identifier
-        #else
-            --model $cond_model.model_selector
-            --name $cond_model.model_selector
-        #end if
-
-        --gene_format $cond_model.gene_format
-        
-        --out_log $log
-        --out_tabular $out_tabular
-        ]]>
-    </command>
-    <inputs>
-        <conditional name="cond_model">
-            <expand macro="options_model"/>
-            
-            <!-- ENGRO2 -->
-            <when value="ENGRO2">
-                <param name="name" argument="--name" type="text" value="ENGRO2" hidden="true" />
-                <conditional name="cond_medium">
-                    <expand macro="options_ras_to_bounds_medium"/>
-                </conditional>
-
-                <param name="gene_format" argument="--gene_format" type="select" label="Gene nomenclature format:">
-                    <option value="Default" selected="true">Keep original gene nomenclature (HGNC Symbol)</option>
-                    <option value="ENSG">ENSNG (Ensembl Gene ID)</option>
-                    <option value="HGNC_ID">HGNC ID</option>
-                    <option value="entrez_id">Entrez Gene ID</option>
-                </param>
-            </when>
-
-            <!-- Recon -->
-            <when value="Recon">
-                <param name="name" argument="--name" type="text" value="Recon" hidden="true" />
-                <conditional name="cond_medium">
-                    <param name="medium_selector" argument="--medium_selector" type="select" label="Medium">
-                        <option value="Default" selected="true">Default (Recon built-in medium)</option>
-                    </param>
-                    <when value="Default">
-                        <!-- Nessun parametro aggiuntivo necessario -->
-                    </when>
-                </conditional>
-                <param name="gene_format" argument="--gene_format" type="select" label="Gene nomenclature format:">
-                    <option value="Default" selected="true">Keep original gene nomenclature (HGNC Symbol)</option>
-                    <option value="ENSG">ENSNG (Ensembl Gene ID)</option>
-                    <option value="HGNC_ID">HGNC ID</option>
-                    <option value="entrez_id">Entrez Gene ID</option>
-                </param>
-            </when>
-
-            <!-- Custom model -->
-            <when value="Custom_model">
-                <param name="input" argument="--input" type="data" format="json,xml" label="Custom model file:" />
-                <conditional name="cond_medium">
-                    <param name="medium_selector" argument="--medium_selector" type="select" label="Medium">
-                        <option value="Default" selected="true">Don't use a separate medium file (use model defaults)</option>
-                    </param>
-                    <when value="Default">
-                        <!-- Nessun parametro aggiuntivo necessario -->
-                    </when>
-                </conditional>
-                <param name="gene_format" argument="--gene_format" type="select" label="Gene nomenclature format:">
-                    <option value="Default" selected="true">Keep original gene nomenclature</option>
-                </param>
-            </when>
-        </conditional>
-
-    </inputs>
-
-    <outputs>
-        <data name="log" format="txt" label="CustomDataGenerator - Log" />
-        <data name="out_tabular" format="tabular" label="${cond_model.model_selector}_model_tabular" optional="true"/>
-    </outputs>
-
-    <help>
-    <![CDATA[
-What it does
--------------
-
-This tool generates four files containing reactions, rules, reaction bounds and medium composition respectively, starting from a custom model in JSON or XML format.
-Reactions and rules can be used as inputs for the RAS and RPS generator tools.
-
-Accepted files:
-    - A model: JSON, XML, MAT or YAML (.yml) file reporting reactions and rules contained in the model. Supported compressed formats: .zip, .gz and .bz2. Filename must follow the pattern: {model_name}.{extension}.[zip|gz|bz2]
-
-Output:
--------------
-
-The tool generates:
-    - rules: reporting the rules for each reaction in the custom model given. Format: csv (tab separated).
-    - reactions: reporting the reactions in the custom model given. Format: csv (tab separated).
-    - reaction bounds: reporting the lower and upper bounds of each model reaction. Format: csv (tab separated).
-    - medium composition: reporting the list of exchange/transport reactions. Format: csv (tab separated).
-    - a log file (.txt).
-    ]]>
-    </help>
-    <expand macro="citations" />
-</tool>
\ No newline at end of file
--- a/COBRAxy/flux_simulation_beta.xml	Fri Sep 12 17:28:45 2025 +0000
+++ b/COBRAxy/flux_simulation_beta.xml	Wed Sep 17 14:26:58 2025 +0000
@@ -57,14 +57,14 @@
 
     <inputs>
         <conditional name="model_and_bounds">
-            <param name="model_and_bounds" argument="--model_and_bounds" type="select" label="Upload mode:" help="Choose whether to upload the model and bounds in separate files or to upload multiple complete model files.">
+            <param name="model_and_bounds" argument="--model_and_bounds" type="select" label="Upload model:" help="Choose whether to upload the model and bounds in separate files or to upload multiple complete model files.">
                 <option value="True" selected="true">Model + bounds (separate files)</option>
                 <option value="False">Multiple complete models</option>
             </param>
 
             <when value="True">
                 <param name="model_upload" argument="--model_upload" type="data" format="csv,tsv,tabular"
-                    label="Model (rules) file:"
+                    label="Model tabular file:"
                     help="Upload a CSV/TSV file that contains the model reaction rules. Recommended columns: ReactionID, Reaction (formula), Rule (GPR). Optional columns: name, lower_bound, upper_bound, InMedium. If bounds are present here they may be overridden by separate bound files." />
 
                 <param name="inputs" argument="--inputs" multiple="true" type="data" format="tabular,csv,tsv"
@@ -89,19 +89,19 @@
             </when>
         </conditional>
 
-        <param name="n_samples" argument="--n_samples" type="integer" label="Samples:" value="1000"/>
+        <param name="n_samples" argument="--n_samples" type="integer" label="Samples:" value="0" help="Select 0 Set to 0 if you don’t want to perform sampling but only optimization."/>
         <param name="n_batches" argument="--n_batches" type="integer" label="Batches:" value="1" help="This is useful for computational performances."/>
         <param name="seed" argument="--seed" type="integer" label="Seed:" value="0" help="Random seed."/>
 
         <param type="select" argument="--output_types" multiple="true" name="output_types" label="Desired outputs from sampling">
             <option value="mean" selected="true">Mean</option>
-            <option value="median" selected="true">Median</option>
-            <option value="quantiles" selected="true">Quantiles</option>
+            <option value="median" selected="false">Median</option>
+            <option value="quantiles" selected="false">Quantiles</option>
             <option value="fluxes" selected="false">All fluxes</option>
         </param>
 
-        <param type="select" argument="--output_types_analysis" multiple="true" name="output_types_analysis" label="Desired outputs from flux analysis">
-            <option value="pFBA" selected="false">pFBA</option>
+        <param type="select" argument="--output_types_analysis" multiple="true" name="output_types_analysis" label="Desired outputs from optimization">
+            <option value="pFBA" selected="true">pFBA</option>
             <option value="FVA" selected="false">FVA</option>
             <option value="sensitivity" selected="false">Sensitivity reaction knock-out (Biomass)</option>
         </param>
@@ -131,10 +131,12 @@
 What it does
 -------------
 
-This tool generates flux samples starting from metabolic models using CBS (Corner-based sampling) or OPTGP (Improved Artificial Centering Hit-and-Run sampler) algorithms.
+This tool generates flux distributions for each samples using:
+1. a sampling-based strategy: CBS (Corner-based sampling) or OPTGP (Improved Artificial Centering Hit-and-Run sampler) algorithms.
+2. an optimization-based strategy:   parsimonious-FBA (optimized by Biomass), FVA (with configurable optimality percentage), Biomass sensitivity analysis (single reaction knock-out)
 
 Two upload modes are supported:
-1. **Model + bounds**: Upload one base model and multiple bound files (one per context/cell type)
+1. **Model + bounds**: Upload one base model (tabular file) and multiple bound files (one per context/cell type)
 2. **Multiple complete models**: Upload multiple complete model files, each with integrated bounds
 
 It can return sampled fluxes by applying summary statistics: 
@@ -142,11 +144,6 @@
    - median
    - quantiles (0.25, 0.50, 0.75)
 
-Flux analysis can be performed over the metabolic model:
-   - parsimonious-FBA (optimized by Biomass)
-   - FVA (with configurable optimality percentage)
-   - Biomass sensitivity analysis (single reaction knock-out)
-
 Output:
 -------------
 
@@ -160,4 +157,6 @@
 ]]>
     </help>
     <expand macro="citations_fluxes" />
-</tool>
\ No newline at end of file
+
+</tool>
+
--- a/COBRAxy/flux_to_map.xml	Fri Sep 12 17:28:45 2025 +0000
+++ b/COBRAxy/flux_to_map.xml	Wed Sep 17 14:26:58 2025 +0000
@@ -87,7 +87,7 @@
 			</when>
 
 			<when value="dataset_class">
-				<param name="input_data_fluxes" argument="--input_data_fluxes" type="data" format="tabular, csv, tsv" label="All fluxes" />
+				<param name="input_data_fluxes" argument="--input_data_fluxes" type="data" format="tabular, csv, tsv" label="Input flux data" />
 				<param name="input_class_fluxes" argument="--input_class_fluxes" type="data" format="tabular, csv, tsv" label="Sample group specification" />
 			</when>
 		</conditional>
@@ -254,4 +254,5 @@
 ]]>
 	</help>
 	<expand macro="citations" />
-</tool>
\ No newline at end of file
+
+</tool>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COBRAxy/metabolic_model_setting.py	Wed Sep 17 14:26:58 2025 +0000
@@ -0,0 +1,256 @@
+"""
+Scripts to generate a tabular file of a metabolic model (built-in or custom).
+
+This script loads a COBRA model (built-in or custom), optionally applies
+medium and gene nomenclature settings, derives reaction-related metadata
+(GPR rules, formulas, bounds, objective coefficients, medium membership,
+and compartments for ENGRO2), and writes a tabular summary.
+"""
+
+import os
+import csv
+import cobra
+import argparse
+import pandas as pd
+import utils.general_utils as utils
+from typing import Optional, Tuple, List
+import utils.model_utils as modelUtils
+import logging
+
+ARGS : argparse.Namespace
+def process_args(args: List[str] = None) -> argparse.Namespace:
+    """
+    Parse command-line arguments for metabolic_model_setting.
+    """
+
+    parser = argparse.ArgumentParser(
+        usage="%(prog)s [options]",
+        description="Generate custom data from a given model"
+    )
+
+    parser.add_argument("--out_log", type=str, required=True,
+                        help="Output log file")
+
+    parser.add_argument("--model", type=str,
+                        help="Built-in model identifier (e.g., ENGRO2, Recon, HMRcore)")
+    parser.add_argument("--input", type=str,
+                        help="Custom model file (JSON or XML)")
+    parser.add_argument("--name", type=str, required=True,
+                        help="Model name (default or custom)")
+    
+    parser.add_argument("--medium_selector", type=str, required=True,
+                        help="Medium selection option")
+
+    parser.add_argument("--gene_format", type=str, default="Default",
+                        help="Gene nomenclature format: Default (original), ENSNG, HGNC_SYMBOL, HGNC_ID, ENTREZ")
+    
+    parser.add_argument("--out_tabular", type=str,
+                        help="Output file for the merged dataset (CSV or XLSX)")
+    
+    parser.add_argument("--tool_dir", type=str, default=os.path.dirname(__file__),
+                        help="Tool directory (passed from Galaxy as $__tool_directory__)")
+
+
+    return parser.parse_args(args)
+
+################################- INPUT DATA LOADING -################################
+def load_custom_model(file_path :utils.FilePath, ext :Optional[utils.FileFormat] = None) -> cobra.Model:
+    """
+    Loads a custom model from a file, either in JSON, XML, MAT, or YML format.
+
+    Args:
+        file_path : The path to the file containing the custom model.
+        ext : explicit file extension. Necessary for standard use in galaxy because of its weird behaviour.
+
+    Raises:
+        DataErr : if the file is in an invalid format or cannot be opened for whatever reason.    
+    
+    Returns:
+        cobra.Model : the model, if successfully opened.
+    """
+    ext = ext if ext else file_path.ext
+    try:
+        if ext is utils.FileFormat.XML:
+            return cobra.io.read_sbml_model(file_path.show())
+        
+        if ext is utils.FileFormat.JSON:
+            return cobra.io.load_json_model(file_path.show())
+
+        if ext is utils.FileFormat.MAT:
+            return cobra.io.load_matlab_model(file_path.show())
+
+        if ext is utils.FileFormat.YML:
+            return cobra.io.load_yaml_model(file_path.show())
+
+    except Exception as e: raise utils.DataErr(file_path, e.__str__())
+    raise utils.DataErr(
+        file_path,
+        f"Unrecognized format '{file_path.ext}'. Only JSON, XML, MAT, YML are supported."
+    )
+
+
+###############################- FILE SAVING -################################
+def save_as_csv_filePath(data :dict, file_path :utils.FilePath, fieldNames :Tuple[str, str]) -> None:
+    """
+    Saves any dictionary-shaped data in a .csv file created at the given file_path as FilePath.
+
+    Args:
+        data : the data to be written to the file.
+        file_path : the path to the .csv file.
+        fieldNames : the names of the fields (columns) in the .csv file.
+    
+    Returns:
+        None
+    """
+    with open(file_path.show(), 'w', newline='') as csvfile:
+        writer = csv.DictWriter(csvfile, fieldnames = fieldNames, dialect="excel-tab")
+        writer.writeheader()
+
+        for key, value in data.items():
+            writer.writerow({ fieldNames[0] : key, fieldNames[1] : value })
+
+def save_as_csv(data :dict, file_path :str, fieldNames :Tuple[str, str]) -> None:
+    """
+    Saves any dictionary-shaped data in a .csv file created at the given file_path as string.
+
+    Args:
+        data : the data to be written to the file.
+        file_path : the path to the .csv file.
+        fieldNames : the names of the fields (columns) in the .csv file.
+    
+    Returns:
+        None
+    """
+    with open(file_path, 'w', newline='') as csvfile:
+        writer = csv.DictWriter(csvfile, fieldnames = fieldNames, dialect="excel-tab")
+        writer.writeheader()
+
+        for key, value in data.items():
+            writer.writerow({ fieldNames[0] : key, fieldNames[1] : value })
+
+def save_as_tabular_df(df: pd.DataFrame, path: str) -> None:
+    """
+    Save a pandas DataFrame as a tab-separated file, creating directories as needed.
+
+    Args:
+        df: The DataFrame to write.
+        path: Destination file path (will be written as TSV).
+
+    Raises:
+        DataErr: If writing the output fails for any reason.
+
+    Returns:
+        None
+    """
+    try:
+        os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
+        df.to_csv(path, sep="\t", index=False)
+    except Exception as e:
+        raise utils.DataErr(path, f"failed writing tabular output: {e}")
+
+
+###############################- ENTRY POINT -################################
+def main(args:List[str] = None) -> None:
+    """
+    Initialize and generate custom data based on the frontend input arguments.
+    
+    Returns:
+        None
+    """
+    # Parse args from frontend (Galaxy XML)
+    global ARGS
+    ARGS = process_args(args)
+
+
+    if ARGS.input:
+        # Load a custom model from file
+        model = load_custom_model(
+            utils.FilePath.fromStrPath(ARGS.input), utils.FilePath.fromStrPath(ARGS.name).ext)
+    else:
+        # Load a built-in model
+
+        try:
+            model_enum = utils.Model[ARGS.model]  # e.g., Model['ENGRO2']
+        except KeyError:
+            raise utils.ArgsErr("model", "one of Recon/ENGRO2/HMRcore/Custom_model", ARGS.model)
+
+        # Load built-in model (Model.getCOBRAmodel uses tool_dir to locate local models)
+        try:
+            model = model_enum.getCOBRAmodel(toolDir=ARGS.tool_dir)
+        except Exception as e:
+            # Wrap/normalize load errors as DataErr for consistency
+            raise utils.DataErr(ARGS.model, f"failed loading built-in model: {e}")
+
+    # Determine final model name: explicit --name overrides, otherwise use the model id
+    
+    model_name = ARGS.name if ARGS.name else ARGS.model
+    
+    if ARGS.name == "ENGRO2" and ARGS.medium_selector != "Default":
+        df_mediums = pd.read_csv(ARGS.tool_dir + "/local/medium/medium.csv", index_col = 0)
+        ARGS.medium_selector = ARGS.medium_selector.replace("_", " ")
+        medium = df_mediums[[ARGS.medium_selector]]
+        medium = medium[ARGS.medium_selector].to_dict()
+
+        # Reset all medium reactions lower bound to zero
+        for rxn_id, _ in model.medium.items():
+            model.reactions.get_by_id(rxn_id).lower_bound = float(0.0)
+        
+        # Apply selected medium uptake bounds (negative for uptake)
+        for reaction, value in medium.items():
+            if value is not None:
+                model.reactions.get_by_id(reaction).lower_bound = -float(value)
+
+    if (ARGS.name == "Recon" or ARGS.name == "ENGRO2") and ARGS.gene_format != "Default":
+        logging.basicConfig(level=logging.INFO)
+        logger = logging.getLogger(__name__)
+
+        model = modelUtils.translate_model_genes(
+            model=model,
+            mapping_df= pd.read_csv(ARGS.tool_dir + "/local/mappings/genes_human.csv", dtype={'entrez_id': str}),
+            target_nomenclature=ARGS.gene_format,
+            source_nomenclature='HGNC_symbol',
+            logger=logger
+        )
+
+    # generate data
+    rules = modelUtils.generate_rules(model, asParsed = False)
+    reactions = modelUtils.generate_reactions(model, asParsed = False)
+    bounds = modelUtils.generate_bounds(model)
+    medium = modelUtils.get_medium(model)
+    objective_function = modelUtils.extract_objective_coefficients(model)
+    
+    if ARGS.name == "ENGRO2":
+        compartments = modelUtils.generate_compartments(model)
+
+    df_rules = pd.DataFrame(list(rules.items()), columns = ["ReactionID", "GPR"])
+    df_reactions = pd.DataFrame(list(reactions.items()), columns = ["ReactionID", "Formula"])
+
+    df_bounds = bounds.reset_index().rename(columns = {"index": "ReactionID"})
+    df_medium = medium.rename(columns = {"reaction": "ReactionID"})
+    df_medium["InMedium"] = True
+
+    merged = df_reactions.merge(df_rules, on = "ReactionID", how = "outer")
+    merged = merged.merge(df_bounds, on = "ReactionID", how = "outer")
+    merged = merged.merge(objective_function, on = "ReactionID", how = "outer")
+    if ARGS.name == "ENGRO2": 
+        merged = merged.merge(compartments, on = "ReactionID", how = "outer")
+    merged = merged.merge(df_medium, on = "ReactionID", how = "left")
+
+    merged["InMedium"] = merged["InMedium"].fillna(False)
+
+    merged = merged.sort_values(by = "InMedium", ascending = False)
+
+    if not ARGS.out_tabular:
+        raise utils.ArgsErr("out_tabular", "output path (--out_tabular) is required when output_format == tabular", ARGS.out_tabular)
+    save_as_tabular_df(merged, ARGS.out_tabular)
+    expected = ARGS.out_tabular
+
+    # verify output exists and non-empty
+    if not expected or not os.path.exists(expected) or os.path.getsize(expected) == 0:
+        raise utils.DataErr(expected, "Output not created or empty")
+
+    print("Metabolic_model_setting: completed successfully")
+
+if __name__ == '__main__':
+
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COBRAxy/metabolic_model_setting.xml	Wed Sep 17 14:26:58 2025 +0000
@@ -0,0 +1,116 @@
+<tool id=""Metabolic model setting" name="Metabolic model setting" version="2.0.0">
+
+	<requirements>
+        <requirement type="package" version="1.24.4">numpy</requirement>
+        <requirement type="package" version="2.0.3">pandas</requirement>
+		<requirement type="package" version="0.29.0">cobra</requirement>
+        <requirement type="package" version="5.2.2">lxml</requirement>
+	</requirements>
+
+    <macros>
+        <import>marea_macros.xml</import>
+    </macros>
+
+    <command detect_errors="exit_code">
+        <![CDATA[
+      	python $__tool_directory__/metabolic_model_setting.py
+        --tool_dir $__tool_directory__
+        --medium_selector $cond_model.cond_medium.medium_selector
+        #if $cond_model.model_selector == 'Custom_model'
+            --input $cond_model.input
+            --name $cond_model.input.element_identifier
+        #else
+            --model $cond_model.model_selector
+            --name $cond_model.model_selector
+        #end if
+
+        --gene_format $cond_model.gene_format
+        
+        --out_log $log
+        --out_tabular $out_tabular
+        ]]>
+    </command>
+    <inputs>
+        <conditional name="cond_model">
+            <expand macro="options_model"/>
+            
+            <!-- ENGRO2 -->
+            <when value="ENGRO2">
+                <param name="name" argument="--name" type="text" value="ENGRO2" hidden="true" />
+                <conditional name="cond_medium">
+                    <expand macro="options_ras_to_bounds_medium"/>
+                </conditional>
+
+                <param name="gene_format" argument="--gene_format" type="select" label="Gene nomenclature format:">
+                    <option value="Default" selected="true">Keep original gene nomenclature (HGNC Symbol)</option>
+                    <option value="ENSG">ENSNG (Ensembl Gene ID)</option>
+                    <option value="HGNC_ID">HGNC ID</option>
+                    <option value="entrez_id">Entrez Gene ID</option>
+                </param>
+            </when>
+
+            <!-- Recon -->
+            <when value="Recon">
+                <param name="name" argument="--name" type="text" value="Recon" hidden="true" />
+                <conditional name="cond_medium">
+                    <param name="medium_selector" argument="--medium_selector" type="select" label="Medium">
+                        <option value="Default" selected="true">Default (Recon built-in medium)</option>
+                    </param>
+                    <when value="Default">
+                        <!-- Nessun parametro aggiuntivo necessario -->
+                    </when>
+                </conditional>
+                <param name="gene_format" argument="--gene_format" type="select" label="Gene nomenclature format:">
+                    <option value="Default" selected="true">Keep original gene nomenclature (HGNC Symbol)</option>
+                    <option value="ENSG">ENSNG (Ensembl Gene ID)</option>
+                    <option value="HGNC_ID">HGNC ID</option>
+                    <option value="entrez_id">Entrez Gene ID</option>
+                </param>
+            </when>
+
+            <!-- Custom model -->
+            <when value="Custom_model">
+                <param name="input" argument="--input" type="data" format="json,xml" label="Custom model file:" />
+                <conditional name="cond_medium">
+                    <param name="medium_selector" argument="--medium_selector" type="select" label="Medium">
+                        <option value="Default" selected="true">Don't use a separate medium file (use model defaults)</option>
+                    </param>
+                    <when value="Default">
+                        <!-- Nessun parametro aggiuntivo necessario -->
+                    </when>
+                </conditional>
+                <param name="gene_format" argument="--gene_format" type="select" label="Gene nomenclature format:">
+                    <option value="Default" selected="true">Keep original gene nomenclature</option>
+                </param>
+            </when>
+        </conditional>
+
+    </inputs>
+
+    <outputs>
+        <data name="log" format="txt" label="CustomDataGenerator - Log" />
+        <data name="out_tabular" format="tabular" label="${cond_model.model_selector}_model_tabular" optional="true"/>
+    </outputs>
+
+    <help>
+    <![CDATA[
+What it does
+-------------
+This tool generates one file containing the main information of the metabolic model, starting from a custom model. 
+This file can be used as input for the RAS  generator tool, RPS generator tools, and flux simulator tool.
+
+Accepted files:
+    - A model: JSON, XML, MAT or YAML (.yml) file reporting reactions and rules contained in the model. Supported compressed formats: .zip, .gz and .bz2. Filename must follow the pattern: {model_name}.{extension}.[zip|gz|bz2]
+
+Output:
+-------------
+
+The tool generates a tabular file containing:
+	- a tabular file (.tabular) containing reaction IDs, reaction formula, GPR rules, reaction bounds, the objective function coefficients, the pathways in which the reaction is involved and which reactions are part of the medium.
+    - a log file (.txt).
+    ]]>
+    </help>
+    <expand macro="citations" />
+
+</tool>
+
--- a/COBRAxy/ras_to_bounds_beta.xml	Fri Sep 12 17:28:45 2025 +0000
+++ b/COBRAxy/ras_to_bounds_beta.xml	Wed Sep 17 14:26:58 2025 +0000
@@ -64,28 +64,23 @@
 What it does
 -------------
 
-This tool generates the reactions bounds for a given metabolic model (JSON or XML format) both with and without the use of the Reaction Activity Scores (RAS) matrix generated by RAS generator.
-Moreover, it enables to use custom/pre-defined growth mediums to constrain exchange reactions. For a custom medium, It is suggested to use the template file returned by the Custom Data Generator tool.
-If the RAS matrix, generated by the RAS generator tool, is used, then a bounds file is generated for each cell. Otherwise, a single bounds file is returned.
-By default, all reactions in model.medium that are not present in the medium file have lower bound set to 0.0 and not set to the default model value.
+This tool generates the reaction bounds for a given tabular model created by the Metabolic Model Setting Tool and the Reaction Activity Scores (RAS) matrix generated by the RAS Generator.
+
 
 Accepted files:
-    - A model: JSON, XML, MAT or YAML (.yml) file reporting reactions and rules contained in the model. Supported compressed formats: .zip, .gz and .bz2. Filename must follow the pattern: {model_name}.{extension}.[zip|gz|bz2]
+    - A tabular model: tab-separated file containing information about a metabolic model.
     - RAS matrix: tab-separated RAS file as returned by RAS generator. Multiple RAS files having different file name can be uploaded too (e.g. one RAS matrix for normal cells and one for cancer cells). Note that if multiple RAs matrices are uploaded, the bounds are normalzed across all cells.
-    - Medium: tab-separated file containing lower and upper-bounds of medium reactions.
-
-Example of custum growth medium file:
-
 
-+------------+----------------+----------------+
-| Reaction ID|   lower_bound  |   upper_bound  |  
-+============+================+================+
-| r1         |    0.123167    |    0.371355    | 
-+------------+----------------+----------------+   
-| r2         |    0.268765    |    0.765567    |  
-+------------+----------------+----------------+   
-
-
+Example of tabular model:
++-------------+----------+------+-------------+-------------+-------------------+-------------------+-----------+-----------+
+| Reaction ID | Formula  | GPR  | lower_bound | upper_bound | ObjectiveCoefficient | Pathways (one or more) | InMedium  |
++=============+==========+======+=============+=============+===================+===================+===========+===========+
+| r1          |    a+b-->c   GeneA or GeneB   |      |   0.123167  |   0.371355  |    0               |     Glycolysis              |       True    |
++-------------+----------+------+-------------+-------------+-------------------+-------------------+-----------+-----------+
+| r2          |    d+e-->f   GeneC   |      |   0.268765  |   0.765567  |            1       |        Glycolysis           |     False      |
++-------------+----------+------+-------------+-------------+-------------------+-------------------+-----------+-----------+
+ 
+		
 Example for multiple RAS matrices:
     - cancer.csv and normal.csv generated by RAS generator tool (the two class names are 'cancer' and 'normal').
     - This tool returns one unique collection of bounds files for both cancer and normal cells (normalization is performed across all cells).
@@ -95,10 +90,11 @@
 -------------
 
 The tool generates:
-    - bounds: reporting the bounds of the model, or cells if RAS is used. Format: tab-separated.
-    - Classes: a file containing the class of each cell (only if multiple RAS matrices were uploaded). The class name of a RAS matrix corresponds to its file name. Format: tab-separated.
+    - A collection of tab files, one for each sample. Each file contains the lower and upper bounds computed from the RAS values and the FVA, used to perform flux sampling or optimization.
+    - Classes: a file containing the class of each sample. The class name of a RAS matrix corresponds to its file name. Format: tab-separated.
     - a log file (.txt).
     ]]>
     </help>
     <expand macro="citations" />
-</tool>
\ No newline at end of file
+
+</tool>