Skip to content

Commit a16aa99

Browse files
committed
rework cli.py
1 parent 602c23b commit a16aa99

File tree

1 file changed

+110
-98
lines changed

1 file changed

+110
-98
lines changed

src/prodigy_prot/cli.py

Lines changed: 110 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,113 +2,125 @@
22
Binding affinity predictor based on Intermolecular Contacts (ICs).
33
"""
44

5+
import argparse
56
import logging
67
import sys
8+
from argparse import RawTextHelpFormatter
9+
from pathlib import Path
710

8-
from prodigy_prot.modules.parsers import parse_structure
11+
from Bio.PDB.Structure import Structure
12+
13+
from prodigy_prot.modules.parsers import (get_parser, parse_structure,
14+
validate_structure)
915
from prodigy_prot.modules.prodigy import Prodigy
10-
from prodigy_prot.modules.utils import check_path
16+
17+
# setup logging
18+
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format="%(message)s")
19+
log = logging.getLogger("Prodigy")
20+
21+
22+
ap = argparse.ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
23+
ap.add_argument(
24+
"input_path",
25+
help="Path to either: \n- Structure in PDB or mmCIF format\n- Directory containing structure files",
26+
)
27+
ap.add_argument(
28+
"--distance-cutoff",
29+
type=float,
30+
default=5.5,
31+
help="Distance cutoff to calculate ICs",
32+
)
33+
ap.add_argument(
34+
"--acc-threshold",
35+
type=float,
36+
default=0.05,
37+
help="Accessibility threshold for BSA analysis",
38+
)
39+
ap.add_argument(
40+
"--temperature",
41+
type=float,
42+
default=25.0,
43+
help="Temperature (C) for Kd prediction",
44+
)
45+
ap.add_argument("--contact_list", action="store_true", help="Output a list of contacts")
46+
ap.add_argument(
47+
"--pymol_selection",
48+
action="store_true",
49+
help="Output a script to highlight the interface (pymol)",
50+
)
51+
ap.add_argument(
52+
"-q",
53+
"--quiet",
54+
action="store_true",
55+
help="Outputs only the predicted affinity value",
56+
)
57+
_co_help = """
58+
By default, all intermolecular contacts are taken into consideration,
59+
a molecule being defined as an isolated group of amino acids sharing
60+
a common chain identifier. In specific cases, for example
61+
antibody-antigen complexes, some chains should be considered as a
62+
single molecule.
63+
64+
Use the --selection option to provide collections of chains that should
65+
be considered for the calculation. Separate by a space the chains that
66+
are to be considered _different_ molecules. Use commas to include multiple
67+
chains as part of a single group:
68+
69+
--selection A B => Contacts calculated (only) between chains A and B.
70+
--selection A,B C => Contacts calculated (only) between \
71+
chains A and C; and B and C.
72+
--selection A B C => Contacts calculated (only) between \
73+
chains A and B; B and C; and A and C.
74+
"""
75+
sel_opt = ap.add_argument_group("Selection Options", description=_co_help)
76+
sel_opt.add_argument("--selection", nargs="+", metavar=("A B", "A,B C"))
1177

1278

1379
def main():
14-
try:
15-
import argparse
16-
from argparse import RawTextHelpFormatter
17-
except ImportError as err:
18-
print(
19-
"[!] The binding affinity prediction tools require Python 2.7+",
20-
file=sys.stderr,
80+
args = ap.parse_args()
81+
log.setLevel(logging.ERROR if args.quiet else logging.INFO)
82+
83+
struct_path = Path(args.input_path)
84+
85+
input_list = []
86+
if struct_path.is_file():
87+
input_list.append(struct_path)
88+
89+
elif struct_path.is_dir():
90+
for input_f in struct_path.glob("*"):
91+
if Path(input_f).suffix in [".pdb", ".cif", ".ent"]:
92+
input_list.append(input_f)
93+
94+
elif not struct_path.exists():
95+
log.error(f"File {struct_path} does not exist")
96+
sys.exit(1)
97+
98+
else:
99+
log.error(f"Input path {struct_path} is neither a valid file nor a directory")
100+
sys.exit(1)
101+
102+
for input_f in input_list:
103+
structure, n_chains, n_res = parse_structure(str(input_f))
104+
105+
if len(input_list) > 1:
106+
log.info("#" * 42)
107+
108+
log.info(
109+
"[+] Parsed structure file {0} ({1} chains, {2} residues)".format(
110+
structure.id, n_chains, n_res
111+
)
21112
)
22-
raise ImportError(err)
23-
24-
ap = argparse.ArgumentParser(
25-
description=__doc__, formatter_class=RawTextHelpFormatter
26-
)
27-
ap.add_argument("structf", help="Structure to analyse in PDB or mmCIF format")
28-
ap.add_argument(
29-
"--distance-cutoff",
30-
type=float,
31-
default=5.5,
32-
help="Distance cutoff to calculate ICs",
33-
)
34-
ap.add_argument(
35-
"--acc-threshold",
36-
type=float,
37-
default=0.05,
38-
help="Accessibility threshold for BSA analysis",
39-
)
40-
ap.add_argument(
41-
"--temperature",
42-
type=float,
43-
default=25.0,
44-
help="Temperature (C) for Kd prediction",
45-
)
46-
ap.add_argument(
47-
"--contact_list", action="store_true", help="Output a list of contacts"
48-
)
49-
ap.add_argument(
50-
"--pymol_selection",
51-
action="store_true",
52-
help="Output a script to highlight the interface (pymol)",
53-
)
54-
ap.add_argument(
55-
"-q",
56-
"--quiet",
57-
action="store_true",
58-
help="Outputs only the predicted affinity value",
59-
)
60-
_co_help = """
61-
By default, all intermolecular contacts are taken into consideration,
62-
a molecule being defined as an isolated group of amino acids sharing
63-
a common chain identifier. In specific cases, for example
64-
antibody-antigen complexes, some chains should be considered as a
65-
single molecule.
66-
67-
Use the --selection option to provide collections of chains that should
68-
be considered for the calculation. Separate by a space the chains that
69-
are to be considered _different_ molecules. Use commas to include multiple
70-
chains as part of a single group:
71-
72-
--selection A B => Contacts calculated (only) between chains A and B.
73-
--selection A,B C => Contacts calculated (only) between \
74-
chains A and C; and B and C.
75-
--selection A B C => Contacts calculated (only) between \
76-
chains A and B; B and C; and A and C.
77-
"""
78-
sel_opt = ap.add_argument_group("Selection Options", description=_co_help)
79-
sel_opt.add_argument("--selection", nargs="+", metavar=("A B", "A,B C"))
80-
81-
cmd = ap.parse_args()
82-
83-
# setup logging
84-
log_level = logging.ERROR if cmd.quiet else logging.INFO
85-
logging.basicConfig(level=log_level, stream=sys.stdout, format="%(message)s")
86-
logger = logging.getLogger("Prodigy")
87-
88-
struct_path = check_path(cmd.structf)
89-
90-
# Parse structure
91-
structure, n_chains, n_res = parse_structure(struct_path)
92-
logger.info(
93-
"[+] Parsed structure file {0} ({1} chains, {2} residues)".format(
94-
structure.id, n_chains, n_res
113+
prodigy = Prodigy(structure, args.selection, args.temperature)
114+
prodigy.predict(
115+
distance_cutoff=args.distance_cutoff, acc_threshold=args.acc_threshold
95116
)
96-
)
97-
prodigy = Prodigy(structure, cmd.selection, cmd.temperature)
98-
prodigy.predict(
99-
distance_cutoff=cmd.distance_cutoff, acc_threshold=cmd.acc_threshold
100-
)
101-
prodigy.print_prediction(quiet=cmd.quiet)
102-
103-
# Print out interaction network
104-
if cmd.contact_list:
105-
fname = struct_path[:-4] + ".ic"
106-
prodigy.print_contacts(fname)
107-
108-
# Print out interaction network
109-
if cmd.pymol_selection:
110-
fname = struct_path[:-4] + ".pml"
111-
prodigy.print_pymol_script(fname)
117+
prodigy.print_prediction(quiet=args.quiet)
118+
119+
if args.contact_list:
120+
prodigy.print_contacts(outfile=str(struct_path.with_suffix(".ic")))
121+
122+
if args.pymol_selection:
123+
prodigy.print_pymol_script(outfile=str(struct_path.with_suffix(".pml")))
112124

113125

114126
if __name__ == "__main__":

0 commit comments

Comments
 (0)