Source code for swamp.mr.mr
import abc
import os
import dill
import shutil
from prettytable import PrettyTable
from swamp.logger import SwampLogger
ABC = abc.ABCMeta('ABC', (object,), {})
[docs]class Mr(ABC):
"""This is an abstract class for MR pipelines. It implements data structures and methods commonly used in MR tasks.
:param str id: unique identifier for the :py:obj:`~swamp.mr.mr.Mr` instance
:param str target_fa: target's fasta filename
:param str target_mtz: target's mtz filename
:param str workdir: working directory where the MR pipeline will be executed
:param str phased_mtz: filename of the target's mtz containing phase information (default None)
:param `~swamp.logger.swamplogger.SwampLogger` logger: logging interface for the MR pipeline (default None)
:param bool silent: if set to True the logger will not print messages (default False)
:ivar list results: A list with the figures of merit obtained after the completion of the pipeline
:ivar bool error: True if errors have occurred at some point on the pipeline
:ivar list results: contains the results obtained in the MR pipeline
"""
def __init__(self, id, target_fa, target_mtz, workdir, phased_mtz=None, logger=None, silent=False):
self._init_params = locals()
self.id = id
self.workdir = workdir
self.make_workdir()
os.chdir(self.workdir)
self.target_fa = target_fa
self.target_mtz = target_mtz
self.phased_mtz = phased_mtz
self.error = False
self.results = []
if logger is None:
self.logger = SwampLogger(__name__, silent=silent)
self.logger.init(logfile=os.path.join(self.workdir, "swamp_%s.debug" % id), use_console=True,
console_level='info', logfile_level='debug')
else:
self.logger = logger
self.logger.silent = silent
# ------------------ Abstract methods and properties ------------------
[docs] @abc.abstractmethod
def run(self):
"""Abstract method to run the mr pipeline"""
pass
[docs] @abc.abstractmethod
def append_results(self):
"""Abstract method to append the results obtained so far into :py:attr:`~swamp.mr.mr.Mr.results`"""
pass
@property
@abc.abstractmethod
def cleanup_dir_list(self):
""" Abstract property to hold the location of the directories to cleanup after completion of the pipeline"""
pass
# ------------------ Some general properties ------------------
@property
def _result_table_fields(self):
"""A list of the column names in the :py:attr:`~swamp.mr.mr.Mr.table_contents`"""
return ["SEARCH ID", "RUN ID", "LLG", "TFZ", "PHSR_CC_LOC", "PHSR_CC_ALL", "RFMC_RFREE", "RFMC_RFACT",
"RFMC_CC_LOC", "RFMC_CC_ALL", "SHXE_CC", "SHXE_ACL", "IS_EXTENDED", "SOLUTION"]
@property
def init_params(self):
"""A dictionary to store the initial parameters used to instantiate the :py:obj:`~swamp.mr.mr.Mr`"""
if "self" in self._init_params:
del self._init_params["self"]
if '__class__' in self._init_params.keys():
del self._init_params['__class__']
return dict(self._init_params)
@init_params.setter
def init_params(self, value):
if "self" in value:
del value["self"]
if "__class__" in value:
del value['__class__']
self._init_params = value
@property
def result_table_fname(self):
"""Filename where the string representation of :py:attr:`~swamp.mr.mr.Mr.table_contents` will be written"""
return os.path.join(self.workdir, "results.table")
@property
def result_pickle_fname(self):
"""Filename where the :py:obj:`~swamp.mr.mr.Mr` instance will be pickled"""
return os.path.join(self.workdir, "results.pckl")
@property
def pipeline_header(self):
"""Header displayed when initiating :py:obj:`~swamp.logger.swamplogger.SwampLogger`"""
return """\n**********************************************************************
********************** {} ******************
**********************************************************************
"""
@property
def table_contents(self):
"""String representation of :py:attr:`~swamp.mr.mr.Mr.results` displayed as a table"""
self.results = sorted(self.results, key=lambda x: x[10] if x[10] != 'NA' else float('0.0'), reverse=True)
table = PrettyTable(self._result_table_fields)
for result in self.results:
if len(result) == len(self._result_table_fields):
table.add_row(result)
else:
self.logger.warning('Results out of bounds! %s' % ', '.join(result))
table.sortby = 'SHXE_CC'
table.reversesort = True
return table.get_string()
# ------------------ Some general methods ------------------
[docs] def make_workdir(self):
"""Method to create the working directory of the :py:obj:`~swamp.mr.mr.Mr` instance"""
if not (os.path.isdir(self.workdir)):
os.makedirs(self.workdir)
[docs] def store_pickle(self, fname=None, mode="ab"):
"""Method to pickle the :py:obj:`~swamp.mr.mr.Mr` instance into a file
:param str fname: filename where the pickle will be created (default None)
:param str mode: mode that will be used to open the file handle (default 'ab')
"""
if fname is None:
fname = self.result_pickle_fname
with open(fname, mode) as pickle_file:
dill.dump(self, pickle_file, protocol=1)
pickle_file.close()
[docs] def create_result_table_outfile(self, fname=None):
"""Method to write string representation of :py:attr:`~swamp.mr.mr.Mr.table_contents` into \
:py:attr:`~swamp.mr.mr.Mr.result_table_fname` or into a specific file if `fname` is set.
:param str fname: filename where the result table will be created (default None)
"""
if fname is None:
fname = self.result_table_fname
# If the run was aborted, nothing to log here
if self.error:
self.logger.warning("Previous errors prevented the creation of a result table")
return
# If the file doesnt exists, add the header
if not (os.path.isfile(fname)):
with open(fname, "w") as logfile:
logfile.write(self.table_contents)
logfile.close()
# ------------------ Some protected and static methods ------------------
def _cleanup_files(self):
"""Method to cleanup the files indicated at :py:attr:`~swamp.mr.mr.Mr.cleanup_dir_list`"""
self.logger.info("Saving disk space now...")
for directory in self.cleanup_dir_list:
self.logger.debug("Removing %s" % directory)
if os.path.isdir(directory):
shutil.rmtree(directory)
@staticmethod
def _inform_args(**kwargs):
"""Create a string representation of the initial parameters used for the creation of this instance, as stored \
in :py:attr:`~swamp.mr.mr.Mr.init_params`"""
msg = "Arguments provided:\n\n"
for key in kwargs.keys():
if key != "self":
msg += "\t%s: %s\n" % (key, kwargs[key])
return msg