Source code for schrodinger.application.phase.pt_hypothesis
"""
Module for manipulating hypothesis entries in the project table.
"""
import os
import schrodinger
from schrodinger import project
from schrodinger.application.phase import constants
from schrodinger.application.phase import hypothesis
from schrodinger.infra import mm
from schrodinger.infra import mmproj
from schrodinger.infra import phase
from schrodinger.Qt import QtCore
from schrodinger.structure import Structure
from schrodinger.utils import log
# Logging
logger = log.get_output_logger(__file__)
# Constants
HYP_DATA_FILENAME = "hypothesis.hyp"
maestro = schrodinger.get_maestro()
hypo_monitor = None
[docs]class PhaseHypothesisMonitor(QtCore.QObject):
"""
This class provides signals that are emitted when hypothesis is changed.
These signals should be emitted with the hypothesis entry id.
:ivar hypothesisChanged: signal emitted when hypothesis is changed
:vartype hypothesisChanged: `QtCore.pyqtSignal`
"""
hypothesisChanged = QtCore.pyqtSignal(int)
[docs]def get_hypo_monitor():
"""
This function returns global PhaseHypothesisMonitor object that should
be used to monitor signals emitted when hypothesis is changed.
:return: PhaseHypothesisMonitor
:rtype: `PhaseHypothesisMonitor`
"""
global hypo_monitor
if hypo_monitor is None:
hypo_monitor = PhaseHypothesisMonitor()
return hypo_monitor
[docs]def update_hypothesis_entry(hypo, entry_id):
"""
Updates the structure in the project table for the given entry ID with
the current PhaseHypothesis.
:param entry_id: entry id to set hypothesis to
:type entry_id: int or str
"""
pt = maestro.project_table_get()
if entry_id not in pt:
return
# Set the hypothesis ID so the property pentrates internal cts
hypo.setHypoID(pt[entry_id][phase.PHASE_HYPO_ID])
hypo_ct = Structure(hypo.getHypoCt())
# Only copy over hypothesis specific properties out of the PhaseHypothesis
for copy_prop in [phase.PHASE_SITE_TYPES] + constants.base64_properties:
if copy_prop in hypo_ct.property:
pt[entry_id][copy_prop] = hypo_ct.property[copy_prop]
elif copy_prop in pt[entry_id].getStructure().property:
del pt[entry_id][copy_prop]
# Set the hypothesis ct as the entry structure, not copying properties. This
# perserves any current non-hypothesis properties in the entry, as well as
# the hypothesis properties set above.
pt[entry_id].setStructure(hypo_ct, props=False)
# Note that the setStructure call occurs after property changes since
# Maestro synchronizes the workspace as soon as an entry structure is
# changed. This primaryily affects phase_markers.update_hypothesis_entry,
# which cycles any reference cts included in the workspace.
# Emit signal that indicates that hypothesis changed
get_hypo_monitor().hypothesisChanged.emit(int(entry_id))
def _get_hypothesis_datafile(entry_id):
"""
Returns the path to a hypothesis data file for a given hypothesis entry ID.
If the entry is not a hypothesis, or the HYP_DATA_FILENAME does not exist,
return None.
:param entry_id: hypothesis entry ID
:type entry_id: int
:return: path to corresponding HYP_DATA_FILENAME
:rtype: str
"""
pt = maestro.project_table_get()
if entry_id not in pt:
return None
try:
entry_dir = mmproj.mmproj_index_entry_get_additional_data_dir(
pt, pt[entry_id].index)
except IOError as e:
logger.warning("Failed to read data directory for entry %s" % entry_id)
return None
# Return the .hyp datafile should it exist in the additional data directory
hypothesis_data_file = os.path.join(entry_dir, HYP_DATA_FILENAME)
if not os.path.isfile(hypothesis_data_file):
return None
return hypothesis_data_file
def _get_hypothesis_rows(hypo_entry_id):
"""
Collects all entries in the group associated with the passed hypothesis
entry ID, which contain the same hypothesis ID property value.
:param hypo_entry_id: hypothesis entry ID
:param hypo_entry_id: int
:return: list of rows associated with the given hypothesis
:rtype: list of `project.ProjectRow`
"""
# Only include sub entries with the same phase.PHASE_HYPO_ID
pt = maestro.project_table_get()
if hypo_entry_id not in pt:
return []
if not is_hypothesis_entry(hypo_entry_id):
raise RuntimeError("Entry %d is not valid hypothesis." % hypo_entry_id)
hypo_id = pt[hypo_entry_id][phase.PHASE_HYPO_ID]
# MAE-37599: Extract rows for all entries in this group
row_model = pt.project_model.getRowModel()
group_id = row_model.getEntryGroupId(int(hypo_entry_id))
# Return the entry itself if not within a group
if not group_id:
return [pt[hypo_entry_id]]
status, group_entry_ids = row_model.getGroupEntryIds(group_id)
if status != mmproj.MMPROJ_OK:
raise RuntimeError("Unable to extract group entries for %s" % group_id)
hypothesis_rows = []
for group_eid in group_entry_ids:
row = pt[group_eid]
if row[phase.PHASE_HYPO_ID] == hypo_id:
hypothesis_rows.append(row)
return hypothesis_rows
[docs]def get_hypothesis_from_project(entry_id):
"""
Gets hypothesis with a given entry id from the project. It creates a
hypothesis object from entries within same hypothesis group.
:param entry_id: hypothesis ID
:type entry_id: int
:return: Phase hypothesis
:rtype: `PhaseHypothesis`
"""
sts = [
row.getStructure(copy=False) for row in _get_hypothesis_rows(entry_id)
]
hypo_st, ref_st, additional_sts = hypothesis.sorted_hypo_structures(sts)
# Added this check for some legacy scripts. If no hypothesis structure
# is found return None.
if hypo_st is mm.MMCT_INVALID_CT:
return None
return hypothesis.PhaseHypothesis(hypo_st, ref_st, additional_sts)
[docs]def convert_project_hypotheses(project_handle):
"""
Converts all hypothesis datafiles in the original Phase format into
PhaseHypothesis (PhpHypoAdaptor) objects. If successful, each hypothesis is
updated in the project to the new format. If the given entry ID does not
have a corresponding hypothesis datafile, nothing is done.
:param project_handle: `project.Project` handle
:type project_handle: int
"""
pt = project.Project(project_handle=project_handle)
for row in pt.all_rows:
entry_id = int(row.entry_id)
# Check that the entry is a hypothesis in the old format
index = mmproj.mmproj_project_entry_id_to_index(pt.handle, entry_id)
if not mmproj.mmproj_phase_entry_has_hypothesis(pt.handle, index):
continue
# Extract the hypothesis data file from the project
hyp_archive = _get_hypothesis_datafile(entry_id)
if not hyp_archive:
continue
# Create a PhaseHypothesis from the archive and the entry structure
ref_ct = row.getStructure().handle
try:
hypo = hypothesis.PhaseHypothesis(row.title, hyp_archive, ref_ct)
except phase.PhpException as e:
logger.warning("WARNING: " + str(e))
continue
# If successful, replace the row with this hypothesis structure
pt[entry_id].setStructure(hypo.getHypoCt(), sync_workspace=False)
[docs]def is_hypothesis_entry(entry_id):
"""
Returns whether project table row is a Phase hypothesis entry or not.
:param entry_id: entry ID for row in project table
:type entry_id: int or str
:return: True if the row is a hypothesis entry, false Otherwise
:rtype: bool
"""
# Return False if maestro unavailable or entry not in PT
if not maestro:
return False
pt = maestro.project_table_get()
return pt.project_model.getRowModel().isHypothesisEntry(int(entry_id))
[docs]def get_hypothesis_entry_ids(entry_id):
"""
Returns entry_ids of all entries in the group associated with the
passed hypothesis entry ID.
:param hypo_entry_id: hypothesis entry ID
:param hypo_entry_id: int
:return: list of entry ids associated with the given hypothesis
:rtype: list
"""
return [row.entry_id for row in _get_hypothesis_rows(entry_id)]