diff --git a/actions/CMakeLists.txt b/actions/CMakeLists.txt index bda606e42db6c6a8f839389381e879aae495fed8..ec884091e95477123686bf5b67fdce196490ca02 100644 --- a/actions/CMakeLists.txt +++ b/actions/CMakeLists.txt @@ -1,4 +1,4 @@ add_custom_target(actions ALL) ost_action_init() -# ost_action(awesome-action actions) +ost_action(ost-qs-score actions) diff --git a/actions/ost-qs-score b/actions/ost-qs-score new file mode 100644 index 0000000000000000000000000000000000000000..dc78ba4fa251a07510377639ccf09caef3a28662 --- /dev/null +++ b/actions/ost-qs-score @@ -0,0 +1,117 @@ +"""Calculate Quaternary Structure score (QS-score) between two complexes. + +""" + +import os +import sys +import argparse + +import ost +from ost.io import LoadPDB, LoadMMCIF +from ost import PushVerbosityLevel +from ost.mol.alg import qsscoring + + +def _ParseArgs(): + """Parse command-line arguments.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + description=__doc__, + prog="ost qs-score") + + parser.add_argument( + '-v', + '--verbosity', + type=int, + default=3, + help="Set verbosity level.") + parser.add_argument( + "-m", + "--model", + dest="model", + required=True, + help=("Path to the model file.")) + parser.add_argument( + "-r", + "--reference", + dest="reference", + required=True, + help=("Path to the reference file.")) + parser.add_argument( + "-c", + "--chain-mapping", + nargs="+", + type=lambda x: x.split(":"), + dest="chain_mapping", + help=("Mapping of chains between the model and the reference. " + "Each separate mapping consist of key:value pairs where key " + "is the chain name in model and value is the chain name in " + "reference.")) + + opts = parser.parse_args() + if opts.chain_mapping is not None: + try: + opts.chain_mapping = dict(opts.chain_mapping) + except ValueError: + raise ValueError("Cannot parse chain mapping into dictionary. The " + "correct format is: key:value.") + + return opts + + +def _ReadStructureFile(path): + """Safely read structure file into OST entity. + + The functin can read both PDB and mmCIF files. + + :param path: Path to the file. + :type path: :class:`str` + :returns: Entity + :rtype: :class:`~ost.mol.EntityHandle` + """ + if not os.path.isfile(path): + raise IOError("%s is not a file" % path) + try: + ent = LoadPDB(path, profile="SLOPPY") + except IOError: + try: + ent = LoadMMCIF(path, profile="SLOPPY") + except IOError as err: + raise err + + return ent + + +def _Main(): + """Do the magic.""" + + opts = _ParseArgs() + PushVerbosityLevel(opts.verbosity) + # + # Read the input files + ost.LogInfo("Reading model from %s" % opts.model) + model = _ReadStructureFile(opts.model) + ost.LogInfo("Reading reference from %s" % opts.reference) + reference = _ReadStructureFile(opts.reference) + + # + # Perform scoring + try: + qs_scorer = qsscoring.QSscorer(reference, + model) + if opts.chain_mapping is not None: + ost.LogInfo( + "Using custom chain mapping: %s" % str(opts.chain_mapping)) + qs_scorer.chain_mapping = opts.chain_mapping + ost.LogScript('QSscore:', str(qs_scorer.global_score)) + ost.LogScript('Chain mapping used:', str(qs_scorer.chain_mapping)) + except qsscoring.QSscoreError as ex: + # default handling: report failure and set score to 0 + ost.LogError('QSscore failed:', str(ex)) + + +if __name__ == '__main__': + # make script 'hot' + unbuffered = os.fdopen(sys.stdout.fileno(), 'w', 0) + sys.stdout = unbuffered + _Main()