| 
542
 | 
     1 """
 | 
| 
 | 
     2 Convert a tabular (CSV/TSV/Tabular) description of a COBRA model into a COBRA file.
 | 
| 
 | 
     3 
 | 
| 
 | 
     4 Supported output formats: SBML, JSON, MATLAB (.mat), YAML.
 | 
| 
 | 
     5 The script logs to a user-provided file for easier debugging in Galaxy.
 | 
| 
 | 
     6 """
 | 
| 
 | 
     7 
 | 
| 
 | 
     8 import os
 | 
| 
 | 
     9 import cobra
 | 
| 
 | 
    10 import argparse
 | 
| 
 | 
    11 from typing import List
 | 
| 
 | 
    12 import logging
 | 
| 
 | 
    13 
 | 
| 
 | 
    14 try:
 | 
| 
 | 
    15     from .utils import model_utils as modelUtils
 | 
| 
 | 
    16     from .utils import general_utils as utils
 | 
| 
 | 
    17 except:
 | 
| 
 | 
    18     import utils.model_utils as modelUtils
 | 
| 
 | 
    19     import utils.general_utils as utils
 | 
| 
 | 
    20 
 | 
| 
 | 
    21 ARGS : argparse.Namespace
 | 
| 
 | 
    22 def process_args(args: List[str] = None) -> argparse.Namespace:
 | 
| 
 | 
    23     """
 | 
| 
 | 
    24     Parse command-line arguments for the CSV-to-COBRA conversion tool.
 | 
| 
 | 
    25 
 | 
| 
 | 
    26     Returns:
 | 
| 
 | 
    27         argparse.Namespace: Parsed arguments.
 | 
| 
 | 
    28     """
 | 
| 
 | 
    29     parser = argparse.ArgumentParser(
 | 
| 
 | 
    30     usage="%(prog)s [options]",
 | 
| 
 | 
    31     description="Convert a tabular/CSV file to a COBRA model"
 | 
| 
 | 
    32     )
 | 
| 
 | 
    33 
 | 
| 
 | 
    34 
 | 
| 
 | 
    35     parser.add_argument("--out_log", type=str, required=True,
 | 
| 
 | 
    36     help="Output log file")
 | 
| 
 | 
    37 
 | 
| 
 | 
    38 
 | 
| 
 | 
    39     parser.add_argument("--input", type=str, required=True,
 | 
| 
 | 
    40     help="Input tabular file (CSV/TSV)")
 | 
| 
 | 
    41 
 | 
| 
 | 
    42 
 | 
| 
 | 
    43     parser.add_argument("--format", type=str, required=True, choices=["sbml", "json", "mat", "yaml"],
 | 
| 
 | 
    44     help="Model format (SBML, JSON, MATLAB, YAML)")
 | 
| 
 | 
    45 
 | 
| 
 | 
    46 
 | 
| 
 | 
    47     parser.add_argument("--output", type=str, required=True,
 | 
| 
 | 
    48     help="Output model file path")
 | 
| 
 | 
    49 
 | 
| 
 | 
    50     parser.add_argument("--tool_dir", type=str, default=os.path.dirname(os.path.abspath(__file__)),
 | 
| 
 | 
    51     help="Tool directory (default: auto-detected package location)")
 | 
| 
 | 
    52 
 | 
| 
 | 
    53 
 | 
| 
 | 
    54     return parser.parse_args(args)
 | 
| 
 | 
    55 
 | 
| 
 | 
    56 
 | 
| 
 | 
    57 ###############################- ENTRY POINT -################################
 | 
| 
 | 
    58 
 | 
| 
 | 
    59 def main(args: List[str] = None) -> None:
 | 
| 
 | 
    60     """
 | 
| 
 | 
    61     Entry point: parse arguments, build the COBRA model from a CSV/TSV file,
 | 
| 
 | 
    62     and save it in the requested format.
 | 
| 
 | 
    63 
 | 
| 
 | 
    64     Returns:
 | 
| 
 | 
    65         None
 | 
| 
 | 
    66     """
 | 
| 
 | 
    67     global ARGS
 | 
| 
 | 
    68     ARGS = process_args(args)
 | 
| 
 | 
    69 
 | 
| 
 | 
    70     # configure logging to the requested log file (overwrite each run)
 | 
| 
 | 
    71     logging.basicConfig(filename=ARGS.out_log,
 | 
| 
 | 
    72                         level=logging.DEBUG,
 | 
| 
 | 
    73                         format='%(asctime)s %(levelname)s: %(message)s',
 | 
| 
 | 
    74                         filemode='w')
 | 
| 
 | 
    75 
 | 
| 
 | 
    76     logging.info('Starting fromCSVtoCOBRA tool')
 | 
| 
 | 
    77     logging.debug('Args: input=%s format=%s output=%s tool_dir=%s', ARGS.input, ARGS.format, ARGS.output, ARGS.tool_dir)
 | 
| 
 | 
    78 
 | 
| 
 | 
    79     try:
 | 
| 
 | 
    80         # Basic sanity checks
 | 
| 
 | 
    81         if not os.path.exists(ARGS.input):
 | 
| 
 | 
    82             logging.error('Input file not found: %s', ARGS.input)
 | 
| 
 | 
    83 
 | 
| 
 | 
    84         out_dir = os.path.dirname(os.path.abspath(ARGS.output))
 | 
| 
 | 
    85         
 | 
| 
 | 
    86         if out_dir and not os.path.isdir(out_dir):
 | 
| 
 | 
    87             try:
 | 
| 
 | 
    88                 os.makedirs(out_dir, exist_ok=True)
 | 
| 
 | 
    89                 logging.info('Created missing output directory: %s', out_dir)
 | 
| 
 | 
    90             except Exception as e:
 | 
| 
 | 
    91                 logging.exception('Cannot create output directory: %s', out_dir)
 | 
| 
 | 
    92 
 | 
| 
 | 
    93         model = modelUtils.build_cobra_model_from_csv(ARGS.input)
 | 
| 
 | 
    94         
 | 
| 
 | 
    95         
 | 
| 
 | 
    96         logging.info('Created model with name: %s (ID: %s)', model.name, model.id)
 | 
| 
 | 
    97 
 | 
| 
 | 
    98         # Save model in requested format - Galaxy handles the filename
 | 
| 
 | 
    99         if ARGS.format == "sbml":
 | 
| 
 | 
   100             cobra.io.write_sbml_model(model, ARGS.output)
 | 
| 
 | 
   101         elif ARGS.format == "json":
 | 
| 
 | 
   102             cobra.io.save_json_model(model, ARGS.output)
 | 
| 
 | 
   103         elif ARGS.format == "mat":
 | 
| 
 | 
   104             cobra.io.save_matlab_model(model, ARGS.output)
 | 
| 
 | 
   105         elif ARGS.format == "yaml":
 | 
| 
 | 
   106             cobra.io.save_yaml_model(model, ARGS.output)
 | 
| 
 | 
   107         else:
 | 
| 
 | 
   108             logging.error('Unknown format requested: %s', ARGS.format)
 | 
| 
 | 
   109             raise ValueError(f"Unknown format: {ARGS.format}")
 | 
| 
 | 
   110 
 | 
| 
 | 
   111 
 | 
| 
 | 
   112         logging.info('Model successfully written to %s (format=%s)', ARGS.output, ARGS.format)
 | 
| 
 | 
   113         print(f"Model created successfully in {ARGS.format.upper()} format")
 | 
| 
 | 
   114 
 | 
| 
 | 
   115     except Exception as e:
 | 
| 
 | 
   116         # Log full traceback to the out_log so Galaxy users/admins can see what happened
 | 
| 
 | 
   117         logging.exception('Unhandled exception in fromCSVtoCOBRA')
 | 
| 
 | 
   118         print(f"ERROR: {str(e)}")
 | 
| 
 | 
   119         raise
 | 
| 
 | 
   120 
 | 
| 
 | 
   121 
 | 
| 
 | 
   122 if __name__ == '__main__':
 | 
| 
 | 
   123     main()
 |