"""
Module with phase_hypo_refine project-related functionality.
Copyright Schrodinger LLC, All Rights Reserved.
"""
import csv
import os
import tempfile
import zipfile
from schrodinger.application.phase.packages import conformer_reader
from schrodinger.application.phase.packages import phase_utils
from schrodinger.infra import phase
from schrodinger.utils import fileutils
[docs]def create_project(infile, fd, conf_options, dest_dir=None):
"""
Creates a multi-conformer zipped Phase project from a Maestro/SD file.
:param infile: Input Maestro/SD file
:type infile: str
:param fd: Feature definitions
:type fd: list of phase.PhpFeatureDefinition
:param conf_options: Conformer generation options
:type conf_options: phase.PhpConfOptions
:param dest_dir: Directory in which to create the project (default: cwd).
:type dest_dir: str
:return: Zipped project name including any leading path
:rtype: str
"""
project_path = derive_project_name(infile)
job_base = fileutils.get_basename(project_path)
job_name = job_base + "_new_project"
fd_file = job_name + ".def"
if dest_dir:
project_path = os.path.join(dest_dir, project_path)
fd_file = os.path.join(dest_dir, fd_file)
project = phase.PhpProject()
project.saveFeatureDefToFile(fd, fd_file)
read_mode = phase.FILE_SINGLE_CONF
title_prop = ""
act_prop = ""
save = False
hypo_fmt = "single"
energy_prop = ""
project.newProject(project_path, infile, fd_file, read_mode, title_prop,
act_prop, job_name, save, hypo_fmt, energy_prop)
fileutils.force_remove(fd_file)
job_name = job_base + "_generate_confs"
project.generateConfs(conf_options, job_name, save)
project.closeProject()
return zip_project(project_path, dest_dir)
[docs]def create_projects(args, dest_dir=None):
"""
Creates any required zipped projects from Maestro/SD files in args. No
action is taken for an argument which already corresponds to a zipped
project. args.actives and args.decoys are replaced with the zipped project
names if zipped projects are created. If args.valid is set, the actives2
and/or decoys2 values are similarly replaced.
:param args: argparser.Namespace with command line arguments
:type args: argparser.Namespace
:param dest_dir: Directory in which to create projects (default: cwd).
:type dest_dir: str
"""
hypo = phase.PhpHypoAdaptor(args.hypo)
fd = hypo.getFd()
conf_options = conformer_reader.get_confgen_options(args)
zipped_projects = {}
for key, infile in get_screen_dict(args).items():
if phase_utils.is_phase_project_path(infile, zipped=True):
zipped_projects[key] = infile
else:
zipped_projects[key] = create_project(infile, fd, conf_options,
dest_dir)
args.actives = zipped_projects["actives"]
args.decoys = zipped_projects["decoys"]
if args.valid:
args.valid = "\"{}\",\"{}\"".format(zipped_projects["actives2"],
zipped_projects["decoys2"])
[docs]def derive_project_name(file_path, zipped=False):
"""
Determines the name of the Phase project to associate with the supplied
Maestro file, SD file, or zipped project name. For example, if file_path is
"/home/jsmith/actives.maegz", the returned value would be "actives.phprj".
If zipped is True, the returned value would be "actives.phzip".
:param file_path: Name of Maestro/SD file or zipped project
:type file_path: str
:return: Project name derived from file_path
:rtype: str
"""
project_base = fileutils.get_basename(file_path)
if zipped:
return project_base + phase.PHASE_ZPRJ_FILE_EXT
return project_base + phase.PHASE_PRJ_FILE_EXT
[docs]def get_ligand_titles(zipped_project, unique=False):
"""
Extracts ligand titles from a zipped project. If unique is True, only
unique titles will be returned.
:param zipped_project: Name of zipped project
:type zipped_project: str
:param unique: Whether to return only unique titles
:type unique: bool
:return: Ligand titles
:rtype: list(str)
"""
titles = []
summary_file = phase_utils.get_internal_zip_path(
phase.PHASE_PROJECT_LIGANDS_DB, phase.PHASE_DATABASE_SUMMARY)
summary_zip, summary_disk = get_project_file_names(zipped_project,
summary_file)
with tempfile.TemporaryDirectory() as tmpdir_name:
with zipfile.ZipFile(zipped_project, 'r') as zfile:
zfile.extract(summary_zip, tmpdir_name)
summary_tmpdir = os.path.join(tmpdir_name, summary_disk)
with open(summary_tmpdir, 'r') as csv_fh:
reader = csv.DictReader(csv_fh)
for row in reader:
titles.append(row['title'])
if unique:
# Make titles unique, but preserve original order
title_dict = {}
for title in titles:
title_dict[title] = ""
titles = list(title_dict.keys())
return titles
[docs]def get_project_file_names(project_path, file_name):
"""
Returns the appropriate file name to use, zip_file_path, to extract the
indicated file from the provided zipped Phase project, and the file name
to use, disk_file_path, to access the extracted file on disk. Use forward
slashes in file_name if it's located in a subdirectory of the project, e.g.,
hypotheses/AADRR_1.phypo. The returned zip_file_path always contains forward
slashes, whereas disk_file_path contains the appropriate platform-dependent
separators.
:param project_path: Path to zipped project
:type project_path: str
:param file_name: Name of file in archive
:type file_name: str
:return: tuple of zip_file_path, disk_file_path
:rtype: str, str
"""
project_dir = derive_project_name(project_path)
zip_file_path = phase_utils.get_internal_zip_path(project_dir, file_name)
disk_file_path = os.path.join(project_dir, *(file_name.split("/")))
return zip_file_path, disk_file_path
[docs]def get_screen_dict(args):
"""
Creates a dictionary from the active/decoy files to screen:
"actives" --> args.actives
"decoys" --> args.decoys
"actives2" --> actives2 file extracted from args.valid, if provided
"decoys2" --> decoys2 file extracted from args.valid, if provided
:param args: Command line arguments
:type args: argparse.Namespace
:return: Dictionary of active/decoy files keyed as described above
:rtype: dict of str:str
"""
screen_dict = {"actives": args.actives, "decoys": args.decoys}
if args.valid:
tokens = next(csv.reader([args.valid]))
screen_dict["actives2"] = phase_utils.get_proper_path(tokens[0])
screen_dict["decoys2"] = phase_utils.get_proper_path(tokens[1])
return screen_dict
[docs]def zip_project(project_path, dest_dir=None):
"""
Zips a Phase project, removes the project directory, and returns the name
of the zipped project.
:param project_path: Path to project (.phprj)
:type project_path: str
:param dest_dir: Destination directory for zipped project (default: cwd).
:type dest_dir: str
:return: Zipped project name (.phzip) including any leading path
:rtype: str
"""
zipped_project = os.path.basename(
fileutils.splitext(project_path)[0]) + phase.PHASE_ZPRJ_FILE_EXT
zip_dir = "."
if dest_dir:
zip_dir = dest_dir
zipped_project = os.path.join(zip_dir, zipped_project)
project = phase.PhpProject()
project.zipProject(zip_dir, project_path)
fileutils.force_rmtree(project_path)
return zipped_project