diff --git a/actions/pm-build-model b/actions/pm-build-model index 6992028b2156bb8caa424fd52ecbfa50614f2807..daafa09c30934bfcac3d48b40117056201fa327c 100755 --- a/actions/pm-build-model +++ b/actions/pm-build-model @@ -1,89 +1,77 @@ -import os, argparse -import ost -from ost import io, settings -from ost.bindings import dssp -from promod3 import modelling -from promod3.core import helper +''' +Automatically build a model from alignments and template structures. -### CHANGELOG - START -# 2016-03-02 - created -### CHANGELOG - END +Example usage: + pm build-model -f aln.fasta -p tpl.pdb + This reads a target-template alignment from aln.fasta and a matching + structure from tpl.pdb and produces a gap-less model which is stored + as model.pdb. -### EXIT STATUS - START -# 1 - template structure file does not exist -# 2 - template structure file has unsupported file extension -# 3 - alignment file does not exist -# 4 - alignment file has unsupported file extension -# 5 - failed to perform modelling (internal error) -# 6 - failed to write results to file -### EXIT STATUS - END +Please see the ProMod3 documentation for details on more advanced usage. -### SETUP - START -DESCRIPTION = '''\ -Automatically build a model from an alignment and a template structure. +Possible exit codes: + 0: all went well + 1: an unhandled exception was raised + 2: arguments cannot be parsed or required arguments are missing + 3: failed to perform modelling (internal error) + 4: failed to write results to file + other non-zero: failure in argument checking (see doc. PM3ArgumentParser) ''' -### SETUP - END -### FUNCTIONS - START -def _ParserArgs(): - p = argparse.ArgumentParser(prog="pm build-model", description=DESCRIPTION, - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - p.add_argument('template_file', metavar='<TEMPLATE STRUCTURE>', type=str, - help='File providing coordinates, either PDB or mmCIF ' - +'format, plain or gzipped.') - p.add_argument('alignment_file', metavar='<ALIGNMENT>', type=str, - help='File providing the target-template alignment') - p.add_argument('-o', '--model-file', metavar='<FILENAME>', type=str, - help='File to store model coordinates.', default='model.pdb') - opts = p.parse_args() +import os +import ost +from ost import io, settings +from ost.bindings import dssp +from promod3 import modelling +from promod3.core import pm3argparse, helper + +# make sure we see output when passing '-h' +ost.PushVerbosityLevel(2) - # check arguments - helper.FileExists('Template structure', 1, opts.template_file) - helper.FileExtension('Template structure', 2, opts.template_file, - ('pdb', 'cif'), gzip=True) - helper.FileExists('Alignment', 3, opts.alignment_file) - helper.FileExtension('Alignment', 4, opts.alignment_file, ('fasta', 'fas'), - gzip=False) - return opts -### FUNCTIONS - END +# parse command line +parser = pm3argparse.PM3ArgumentParser(__doc__, action=True) +parser.AddAlignment() +parser.AddStructure(attach_views=True) +parser.AssembleParser() +parser.add_argument('-o', '--model-file', metavar='<FILENAME>', type=str, + default='model.pdb', help='File to store model coordinates'+ + ' (default: %(default)s).') +# lots of checking being done here -> see PM3ArgumentParser +opts = parser.Parse() -### MAIN - START -opts = _ParserArgs() +# report alignments and add dssp +ost.PushVerbosityLevel(3) +if len(opts.alignments) == 1: + ost.LogInfo("Build model with the following alignment:") +elif len(opts.alignments) > 1: + ost.LogInfo("Build " + str(len(opts.alignments)) + " models with the "+ + "following alignments:") +dssp_usable = True +for aln in opts.alignments: + ost.LogInfo(aln.ToString(80)) + if dssp_usable: + for i in range(1, aln.GetCount()): + try: + dssp.AssignDSSP(aln.GetSequence(i).GetAttachedView()) + except settings.FileNotFound: + dssp_usable = False + break +if not dssp_usable: + ost.LogInfo("dssp binary is missing. You can get better modelling results "+ + "if you install dssp.") -# load template structure -tpl = io.LoadEntity(opts.template_file) -# load target-template alignment -aln = io.LoadAlignment(opts.alignment_file) -# TODO: check that alignment sequences are in template -# try to add sec. structure -try: - dssp.AssignDSSP(tpl) -except settings.FileNotFound: - print "dssp binary is missing. You can get better modelling results if " +\ - "you install dssp." +# model it try: - ost.PushVerbosityLevel(3) - ost.LogInfo("Build model with following alignment:") - ost.LogInfo(aln.ToString(80)) # get raw model - aln.AttachView(1, tpl.CreateFullView()) - mhandle = modelling.BuildRawModel(aln) + mhandle = modelling.BuildRawModel(opts.alignments) # build final model final_model = modelling.BuildFromRawModel(mhandle) - ost.PopVerbosityLevel() except Exception as ex: - err_tpl = "An exception of type {0} occured. Arguments:\n{1!r}" - message = err_tpl.format(type(ex).__name__, ex.args) - helper.MsgErrorAndExit("Modelling failed!\n" + message, 5) + helper.MsgErrorAndExit("Failed to perform modelling! An exception of type "+ + type(ex).__name__ + " occured: " + str(ex), 3) + +# output +ost.PopVerbosityLevel() io.SavePDB(final_model, opts.model_file) if not os.path.isfile(opts.model_file): - helper.MsgErrorAndExit("Failed to write model file '%s'." % opts.model_file, 6) -### MAIN - END - - -# NOTE: this follows build-rawmodel -# -> interface, error checks and code may change to: -# - follow pm-help -# - use pm3argparse to get inputs -# - check that we have desired alignments in file -# - multi-template??? :o \ No newline at end of file + helper.MsgErrorAndExit("Failed to write model file '%s'." % opts.model_file, 4) diff --git a/actions/pm-build-rawmodel b/actions/pm-build-rawmodel index d527254df7270d5bee2b7b0dce828db27e569d8c..2efc3c4fbe4de151c48d85e8a8b60864ca479268 100755 --- a/actions/pm-build-rawmodel +++ b/actions/pm-build-rawmodel @@ -1,90 +1,55 @@ -import os, argparse -from ost import io -from promod3 import modelling -from promod3.core import helper - -### CHANGELOG - START -# 2013-11-07 - created -### CHANGELOG - END - -### EXIT STATUS - START -# 1 - template structure file does not exist -# 2 - template structure file has unsupported file extension -# 3 - alignment file does not exist -# 4 - alignment file has unsupported file extension -# 5 - failed to write results to file -### EXIT STATUS - END - -### SETUP - START -DESCRIPTION = '''\ -Create a very basic raw model from an alignment and a template structure. +''' +Create a very basic raw model from alignments and template structures. Conserved parts are copied from the template and an incomplete pdb file is generated. -''' -### SETUP - END - -### FUNCTIONS - START -def _ParserArgs(): - p = argparse.ArgumentParser(prog="pm build-rawmodel", description=DESCRIPTION, - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - p.add_argument('template_file', metavar='<TEMPLATE STRUCTURE>', type=str, - help='File providing coordinates, either PDB or mmCIF ' - +'format, plain or gzipped.') - p.add_argument('alignment_file', metavar='<ALIGNMENT>', type=str, - help='File providing the target-template alignment') - p.add_argument('-o', '--model-file', metavar='<FILENAME>', type=str, - help='File to store model coordinates.', default='model.pdb') - opts = p.parse_args() - - # check arguments - helper.FileExists('Template structure', 1, opts.template_file) - helper.FileExtension('Template structure', 2, opts.template_file, - ('pdb', 'cif'), gzip=True) - helper.FileExists('Alignment', 3, opts.alignment_file) - helper.FileExtension('Alignment', 4, opts.alignment_file, ('fasta', 'fas'), - gzip=False) - return opts -### FUNCTIONS - END -### MAIN - START -opts = _ParserArgs() - -# load template structure -tpl = io.LoadEntity(opts.template_file) -# load target-template alignment -aln = io.LoadAlignment(opts.alignment_file) -# check that alignment sequences are in template -# mark target and template sequence in alignment -# auto-mode: chains in alignment are marked as sep.A, sep.B, etc. -# how can we do multi-whatever things? +Example usage: + pm build-rawmodel -f aln.fasta -p tpl.pdb + This reads a target-template alignment from aln.fasta and a matching + structure from tpl.pdb and produces a raw model which is stored as + model.pdb. + +Please see the ProMod3 documentation for details on more advanced usage. + +Possible exit codes: + 0: all went well + 1: an unhandled exception was raised + 2: arguments cannot be parsed or required arguments are missing + 3: failed to generate raw model (internal error) + 4: failed to write results to file + other non-zero: failure in argument checking (see doc. PM3ArgumentParser) +''' -aln.AttachView(1, tpl.CreateFullView()) -result = modelling.BuildRawModel(aln) -io.SavePDB(result.model, opts.model_file) +import os +import ost +from ost import io +from promod3 import modelling +from promod3.core import pm3argparse, helper + +# make sure we see output when passing '-h' +ost.PushVerbosityLevel(2) + +# parse command line +parser = pm3argparse.PM3ArgumentParser(__doc__, action=True) +parser.AddAlignment() +parser.AddStructure(attach_views=True) +parser.AssembleParser() +parser.add_argument('-o', '--model-file', metavar='<FILENAME>', type=str, + default='model.pdb', help='File to store model coordinates'+ + ' (default: %(default)s).') +# lots of checking being done here -> see PM3ArgumentParser +opts = parser.Parse() + +# get raw model +try: + mhandle = modelling.BuildRawModel(opts.alignments) +except Exception as ex: + helper.MsgErrorAndExit("Failed to generate raw model! An exception of "+ + "type " + type(ex).__name__ + " occured: " + str(ex), + 3) + +# output +ost.PopVerbosityLevel() +io.SavePDB(mhandle.model, opts.model_file) if not os.path.isfile(opts.model_file): - helper.MsgErrorAndExit("Failed to write model file '%s'." % opts.model_file, 5) -### MAIN - END - - - -# parse args -# - we need an input alignment -# - only 2 seqs allowed, option switch to mark which one is target -# - what happens on arbitrary sequences? One has to correspond with the -# template structure -# - what happens for a alignment list? -# - option for name model file -# - default does not overwrite, but add switch to do so -# - switch to turn of reporting gaps -# - option to go calpha only -# prepare stuff -# build model -# store stuff -# return stuff? -# report gaps? -# documentation! - -## Emacs magic -# Local Variables: -# mode: python -# End: + helper.MsgErrorAndExit("Failed to write model file '%s'." % opts.model_file, 4) diff --git a/actions/tests/test_action_build-model.py b/actions/tests/test_action_build-model.py index a6ec7407dbf10172fb7e4b4070f2c19a1384ce5a..a0889fd0c048c720c04d8ec7d4c07fba01f22047 100644 --- a/actions/tests/test_action_build-model.py +++ b/actions/tests/test_action_build-model.py @@ -23,9 +23,10 @@ class HelpActionTests(test_actions.ActionTestCase): # test with proper inputs (and check resulting file) pdbpath = os.path.join('data', 'gly.pdb') faspath = os.path.join('data', 'seq_ins.fasta') - self.RunExitStatusTest(0, [pdbpath, faspath]) + my_args = ['-p', pdbpath, '-f', faspath] + self.RunExitStatusTest(0, my_args) io.LoadPDB('model.pdb') - self.RunExitStatusTest(0, ['-o mymodel.pdb', pdbpath, faspath]) + self.RunExitStatusTest(0, my_args + ['-o', 'mymodel.pdb']) io.LoadPDB('mymodel.pdb') # clean up this test os.remove('model.pdb') @@ -35,20 +36,14 @@ class HelpActionTests(test_actions.ActionTestCase): # test error codes pdbpath = os.path.join('data', 'gly.pdb') faspath = os.path.join('data', 'seq.fasta') - # 1 - template structure file does not exist - self.RunExitStatusTest(1, ['dummy.pdb', faspath]) - # 2 - template structure file has unsupported file extension - self.RunExitStatusTest(2, [faspath, faspath]) - # 3 - alignment file does not exist - self.RunExitStatusTest(3, [pdbpath, 'dummy.fasta']) - # 4 - alignment file has unsupported file extension - self.RunExitStatusTest(4, [pdbpath, pdbpath]) - # 5 - failed to perform modelling (internal error) + my_args = ['-p', pdbpath, '-f', faspath] + # 2 - wrong input (most of this tested in pm3argparse!) + self.RunExitStatusTest(2, ['-p', pdbpath]) + # 3 - failed to perform modelling (internal error) # -> we cannot really test this one... - # 6 - failed to write results to file + # 4 - failed to write results to file failpath = os.path.join('cannotpossiblyexist', 'whatever.pdb') - self.RunExitStatusTest(6, ['-o '+failpath, pdbpath, faspath]) - + self.RunExitStatusTest(4, ['-o', failpath] + my_args) if __name__ == "__main__": from ost import testutils diff --git a/actions/tests/test_action_build-rawmodel.py b/actions/tests/test_action_build-rawmodel.py index 4c920610cedb043ca1be46627bbc722b221ddfa2..3f222c62a0644597920c1d7654764deb2e94a818 100644 --- a/actions/tests/test_action_build-rawmodel.py +++ b/actions/tests/test_action_build-rawmodel.py @@ -23,9 +23,10 @@ class HelpActionTests(test_actions.ActionTestCase): # test with proper inputs (and check resulting file) pdbpath = os.path.join('data', 'gly.pdb') faspath = os.path.join('data', 'seq.fasta') - self.RunExitStatusTest(0, [pdbpath, faspath]) + my_args = ['-p', pdbpath, '-f', faspath] + self.RunExitStatusTest(0, my_args) io.LoadPDB('model.pdb') - self.RunExitStatusTest(0, ['-o mymodel.pdb', pdbpath, faspath]) + self.RunExitStatusTest(0, my_args + ['-o', 'mymodel.pdb']) io.LoadPDB('mymodel.pdb') # clean up this test os.remove('model.pdb') @@ -35,18 +36,14 @@ class HelpActionTests(test_actions.ActionTestCase): # test error codes pdbpath = os.path.join('data', 'gly.pdb') faspath = os.path.join('data', 'seq.fasta') - # 1 - template structure file does not exist - self.RunExitStatusTest(1, ['dummy.pdb', faspath]) - # 2 - template structure file has unsupported file extension - self.RunExitStatusTest(2, [faspath, faspath]) - # 3 - alignment file does not exist - self.RunExitStatusTest(3, [pdbpath, 'dummy.fasta']) - # 4 - alignment file has unsupported file extension - self.RunExitStatusTest(4, [pdbpath, pdbpath]) - # 5 - failed to write results to file + my_args = ['-p', pdbpath, '-f', faspath] + # 2 - wrong input (most of this tested in pm3argparse!) + self.RunExitStatusTest(2, ['-p', pdbpath]) + # 3 - failed to generate raw model (internal error) + # -> we cannot really test this one... + # 4 - failed to write results to file failpath = os.path.join('cannotpossiblyexist', 'whatever.pdb') - self.RunExitStatusTest(5, ['-o '+failpath, pdbpath, faspath]) - + self.RunExitStatusTest(4, ['-o', failpath] + my_args) if __name__ == "__main__": from ost import testutils diff --git a/core/pymod/core/pm3argparse.py b/core/pymod/core/pm3argparse.py index 7ad9e341a3d63f9cf1081e305f33266b50d11f66..de9a6320da9c9acdd521fcde6f2bf2798e6dcf65 100644 --- a/core/pymod/core/pm3argparse.py +++ b/core/pymod/core/pm3argparse.py @@ -343,7 +343,7 @@ class PM3ArgumentParser(argparse.ArgumentParser): argparse.ArgumentParser.__init__(self, prog=prog, description=description, formatter_class=\ - argparse.ArgumentDefaultsHelpFormatter) + argparse.RawDescriptionHelpFormatter) self.action = action self.activate = set() diff --git a/core/tests/test_pm3argparse.py b/core/tests/test_pm3argparse.py index daa54464ded1f2278888933862b76a13f9d4d1db..f7e9ff7bce47a59a17d5bd68220c4cf5e6e041c6 100644 --- a/core/tests/test_pm3argparse.py +++ b/core/tests/test_pm3argparse.py @@ -951,13 +951,14 @@ class PM3ArgParseTests(unittest.TestCase): def checkView(self, alns): # helper: checks if all alignments have attached views for templates for aln in alns: - self.assertTrue(aln.GetSequence(1).HasAttachedView()) - for col in aln: - res = col.GetResidue(1) - all_good = (res.IsValid() and res.one_letter_code == col[1])\ - or (not res.IsValid() and '-' == col[1]) - mymsg = "mismatch col. " + str(col) + " with res. " + str(res) - self.assertTrue(all_good, msg=mymsg) + for i in range(1, aln.GetCount()): + self.assertTrue(aln.GetSequence(i).HasAttachedView()) + for col in aln: + res = col.GetResidue(i) + all_good = (res.IsValid() and res.one_letter_code == col[i])\ + or (not res.IsValid() and '-' == col[i]) + mymsg = "mismatch col. " + str(col) + " with res. " + str(res) + self.assertTrue(all_good, msg=mymsg) def testAttachView(self): parser = pm3argparse.PM3ArgumentParser(__doc__, action=False) diff --git a/doc/tests/test_doctests.py b/doc/tests/test_doctests.py index 369b64fc4c53b905be17ab6741a4be73d0948ec8..f6cdb7c31fda6f10f7cc04b9a376dba40b55d1c6 100644 --- a/doc/tests/test_doctests.py +++ b/doc/tests/test_doctests.py @@ -132,9 +132,9 @@ class DocTests(unittest.TestCase): out_lines = serr.splitlines() self.assertRegexpMatches(out_lines[0], "usage: .*") self.assertEqual(out_lines[2].strip(), - "Place the description of your script right in the file and import it via") + "Place the description of your script right in the file and import") self.assertEqual(out_lines[3].strip(), - "'__doc__' as description to the parser ('-h', '--help').") + "it via '__doc__' as description to the parser ('-h', '--help').") self.assertEqual(out_lines[5].strip(), "optional arguments:") self.assertGreater(len(out_lines), 5)