KarrLab/bpforms

View on GitHub
bpforms/__main__.py

Summary

Maintainability
B
4 hrs
Test Coverage
A
99%
""" bpforms command line interface

:Author: Jonathan Karr <karr@mssm.edu>
:Date: 2019-01-31
:Copyright: 2019, Karr Lab
:License: MIT
"""

from wc_utils.util.chem import OpenBabelUtils
import bpforms
import bpforms.util
import cement


class BaseController(cement.Controller):
    """ Base controller for command line application """

    class Meta:
        label = 'base'
        description = "bpforms"
        help = "bpforms"
        arguments = [
            (['-v', '--version'], dict(action='version', version=bpforms.__version__)),
        ]

    @cement.ex(hide=True)
    def _default(self):
        raise SystemExit(self._parser.print_help())


class ValidateController(cement.Controller):
    """ Validate a biopolymer form """

    class Meta:
        label = 'validate'
        description = 'Validate a biopolymer form'
        help = 'Validate a biopolymer form'
        stacked_on = 'base'
        stacked_type = 'nested'
        arguments = [
            (['alphabet'], dict(type=str, help='Biopolymer alphabet')),
            (['seq'], dict(type=str, help='Sequence of monomeric forms')),
            (['--circular'], dict(action='store_true', default=False, help='Biopolymer circularity')),
        ]

    @cement.ex(hide=True)
    def _default(self):
        args = self.app.pargs
        type = bpforms.util.get_form(args.alphabet)

        try:
            form = type().from_str(args.seq)
        except Exception as error:
            raise SystemExit('Form is invalid: {}'.format(str(error)))
        form.circular = args.circular

        errors = form.validate()
        if errors:
            raise SystemExit('Form is invalid:\n  {}'.format('\n  '.join(errors)))

        print('Form is valid')


class GetPropertiesController(cement.Controller):
    """ Calculate physical properties such as length, chemical formula, molecular weight, and charge """

    class Meta:
        label = 'get-properties'
        description = 'Calculate physical properties such as length, chemical formula, molecular weight, and charge'
        help = 'Calculate physical properties such as length, chemical formula, molecular weight, and charge'
        stacked_on = 'base'
        stacked_type = 'nested'
        arguments = [
            (['alphabet'], dict(type=str, help='Biopolymer alphabet')),
            (['seq'], dict(type=str, help='Sequence of monomeric forms')),
            (['--circular'], dict(action='store_true', default=False, help='Biopolymer circularity')),
            (['--ph'], dict(default=None, type=float,
                            help='pH at which calculate major protonation state of each monomeric form')),
            (['--major-tautomer'], dict(action='store_true', default=False,
                                        help='If set, calculate the major tautomer')),
            (['--dearomatize'], dict(action='store_true', default=False,
                                     help='If set, dearomatize molecule')),
        ]

    @cement.ex(hide=True)
    def _default(self):
        args = self.app.pargs
        type = bpforms.util.get_form(args.alphabet)

        try:
            form = type().from_str(args.seq)
        except Exception as error:
            raise SystemExit('Form is invalid: {}'.format(str(error)))
        form.circular = args.circular

        errors = form.validate()
        if errors:
            raise SystemExit('Form is invalid:\n  {}'.format('\n  '.join(errors)))

        smiles = None
        formula = None
        mol_wt = None
        charge = None
        try:
            if args.ph is None:
                formula = form.get_formula()
                mol_wt = form.get_mol_wt()
                charge = form.get_charge()
                structure = form.get_structure()[0]
            else:
                structure = form.get_major_micro_species(args.ph, major_tautomer=args.major_tautomer, dearomatize=args.dearomatize)
                if structure is not None:
                    formula = OpenBabelUtils.get_formula(structure)
                    mol_wt = formula.get_molecular_weight()
                    charge = structure.GetTotalCharge()

            if structure is not None:
                smiles = OpenBabelUtils.export(structure, 'smiles')
        except Exception:
            pass

        print('Length: {}'.format(len(form)))
        print('Structure: {}'.format(smiles))
        print('Formula: {}'.format(formula))
        print('Molecular weight: {}'.format(mol_wt))
        print('Charge: {}'.format(charge))


class GetMajorMicroSpeciesController(cement.Controller):
    """ Calculate the major protonation and tautomerization """

    class Meta:
        label = 'get-major-micro-species'
        description = 'Calculate the major protonation and tautomerization state of a biopolymer form to a specific pH'
        help = 'Calculate the major protonation and tautomerization state of a biopolymer form to a specific pH'
        stacked_on = 'base'
        stacked_type = 'nested'
        arguments = [
            (['alphabet'], dict(type=str, help='Biopolymer alphabet')),
            (['seq'], dict(type=str, help='Sequence of monomeric forms')),
            (['--circular'], dict(action='store_true', default=False, help='Biopolymer circularity')),
            (['ph'], dict(type=float, help='pH')),
            (['--major-tautomer'], dict(action='store_true', default=False,
                                        help='If set, calculate the major tautomer')),
            (['--dearomatize'], dict(action='store_true', default=False,
                                     help='If set, dearomatize molecule')),
        ]

    @cement.ex(hide=True)
    def _default(self):
        args = self.app.pargs
        type = bpforms.util.get_form(args.alphabet)

        try:
            form = type().from_str(args.seq)
        except Exception as error:
            raise SystemExit('Form is invalid: {}'.format(str(error)))
        form.circular = args.circular

        errors = form.validate()
        if errors:
            raise SystemExit('Form is invalid:\n  {}'.format('\n  '.join(errors)))

        structure = form.get_major_micro_species(args.ph, major_tautomer=args.major_tautomer, dearomatize=args.dearomatize)
        print(OpenBabelUtils.export(structure, 'smiles'))


class BuildAlphabetsController(cement.Controller):
    """ Build DNA, RNA, and protein alphabets from DNAmod, MODOMICS, the PDB Chemical Component Dictionary, RESID,
    and the RNA Modification Database """

    class Meta:
        label = 'build-alphabets'
        description = ('Build DNA, RNA, and protein alphabets from DNAmod, MODOMICS, '
                       'the PDB Chemical Component Dictionary, RESID, and the RNA Modification Database')
        help = ('Build DNA, RNA, and protein alphabets from DNAmod, MODOMICS, '
                'the PDB Chemical Component Dictionary, RESID, and the RNA Modification Database')
        stacked_on = 'base'
        stacked_type = 'nested'
        arguments = [
            (['--ph'], dict(type=float, default=7.4,
                            help='pH at which calculate major protonation state of each monomeric form')),
            (['--major-tautomer'], dict(action='store_true', default=False,
                                        help='If set, calculate the major tautomer')),
            (['--dearomatize'], dict(action='store_true', default=False,
                                     help='If set, dearomatize molecule')),
            (['--max-monomers'], dict(type=float, default=float('inf'),
                                      help='Maximum number of monomeric forms to build. Used for testing')),
            (['--alphabet'], dict(type=str, default=None, dest='alphabets', action='append',
                                  help='Id of alphabet to build. Defualt: build all alphabets')),
        ]

    @cement.ex(hide=True)
    def _default(self):
        args = self.app.pargs
        bpforms.util.build_alphabets(ph=args.ph, major_tautomer=args.major_tautomer, dearomatize=args.dearomatize,
                                     _max_monomers=args.max_monomers, alphabets=args.alphabets)
        print('Alphabets successfully built')


class VizAlphabetController(cement.Controller):
    """ Visualize an alphabet """

    class Meta:
        label = 'viz-alphabet'
        description = 'Visualize an alphabet'
        help = 'Visualize an alphabet'
        stacked_on = 'base'
        stacked_type = 'nested'
        arguments = [
            (['alphabet'], dict(type=str, help='Biopolymer alphabet')),
            (['path'], dict(type=str, help='Path to save visualization of alphabet')),
        ]

    @cement.ex(hide=True)
    def _default(self):
        args = self.app.pargs
        bpform_type = bpforms.util.get_form(args.alphabet)
        bpforms.util.gen_html_viz_alphabet(bpform_type, args.path)
        print('Visualization saved to {}'.format(args.path))


class ExportOntosController(cement.Controller):
    """ Export alphabets of residues and ontology of crosslinks to OBO file """

    class Meta:
        label = 'export-ontos'
        description = 'Export alphabets of residues and ontology of crosslinks to OBO file'
        help = 'Export alphabets of residues and ontology of crosslinks to OBO file'
        stacked_on = 'base'
        stacked_type = 'nested'
        arguments = [
            (['filename'], dict(type=str, help='Path to save ontology')),
            (['--alphabet'], dict(type=str, default=None, dest='alphabets', action='append',
                                  help='Id of alphabet to export. Defualt: export all alphabets')),
            (['--max-monomers'], dict(type=int, default=None, help='Maximum number of monomers to export')),
            (['--max-xlinks'], dict(type=int, default=None, help='Maximum number of crosslinks to export')),
        ]

    @cement.ex(hide=True)
    def _default(self):
        args = self.app.pargs

        if args.alphabets is None:
            alphabets = None
        else:
            alphabets = [bpforms.util.get_alphabet(alph) for alph in args.alphabets]

        bpforms.util.export_ontos_to_obo(filename=args.filename,
                                         alphabets=alphabets,
                                         _max_monomers=args.max_monomers,
                                         _max_xlinks=args.max_xlinks)
        print('Ontology saved to {}'.format(args.filename))


class App(cement.App):
    """ Command line application """
    class Meta:
        label = 'bpforms'
        base_controller = 'base'
        handlers = [
            BaseController,
            ValidateController,
            GetPropertiesController,
            GetMajorMicroSpeciesController,
            BuildAlphabetsController,
            VizAlphabetController,
            ExportOntosController,
        ]


def main():
    with App() as app:
        app.run()