<commit_before>"""Class to run an anomalous phased fourier on MR results"""

import iotbx.pdb
import os
from scipy.spatial import distance
import simbad_util
import mtz_util
import numpy as np

__author__ = "Adam Simpkin"
__date__ = "17 Mar 2017"
__version__ = "0.1"

class _AnomScore(object):
    """An anomalous phased fourier scoring class"""

    __slots__ = ("peaks_over_8_rms", "peaks_over_8_rms_within_2A_of_model",
                 "peaks_over_12_rms", "peaks_over_12_rms_within_2A_of_model")

    def __init__(self, peaks_over_8_rms, peaks_over_8_rms_within_2A_of_model,
                 peaks_over_12_rms, peaks_over_12_rms_within_2A_of_model):
        self.peaks_over_8_rms = peaks_over_8_rms
        self.peaks_over_8_rms_within_2A_of_model = peaks_over_8_rms_within_2A_of_model
        self.peaks_over_12_rms = peaks_over_12_rms
        self.peaks_over_12_rms_within_2A_of_model = peaks_over_12_rms_within_2A_of_model

    def __repr__(self):
        return "{0}(peaks_over_8_rms={1} " \
                "peaks_over_8_rms_within_2A_of_model={2} " \
                "peaks_over_12_rms={3} " \
                "peaks_over_12_rms_within_2A_of_model={4}".format(self.__class__.__name__,
                                                                  self.peaks_over_8_rms,
                                                                  self.peaks_over_8_rms_within_2A_of_model,
                                                                  self.peaks_over_12_rms,
                                                                  self.peaks_over_8_rms_within_2A_of_model)
    def _as_dict(self):
        """Convert the :obj:`_MrScore <simbad.util.mr_util._MrScore>`
        object to a dictionary"""
        dict = {}
        for k in self.__slots__:
            dict[k] = getattr(self, k)
        return dict

class AnomSearch():
    """An anomalous phased fourier running class

    Attributes
    ----------
    mtz : str
        The path to the input MTZ
    output_dir : str
        The path to the output directory
    model : class
        Class object containing the PDB code for the input model

    Example
    -------
    >>> from simbad.util import anomalous_util
    >>> anomalous_search = anomalous_util.AnomSearch("<mtz>", "<output_dir>")
    >>> anomalous_search.run("<model>")
    """

    def __init__(self, mtz, output_dir):
        self._mtz = None
        self._f = None
        self._sigf = None
        self._dano = None
        self._sigdano = None
        self._free = None
        self._space_group = None
        self._resolution = None
        self._cell_parameters = None
        self._output_dir = None

        self.name = None
        self.mtz = mtz
        self.output_dir = output_dir
        self.work_dir = None

    @property
    def mtz(self):
        """The input MTZ file"""
        return self._mtz

    @mtz.setter
    def mtz(self, mtz):
        """Define the input MTZ file"""
        self._mtz = mtz

    @property
    def output_dir(self):
        """The path to the working directory"""
        return self._output_dir

    @output_dir.setter
    def output_dir(self, output_dir):
        """Define the working directory"""
        self._output_dir = output_dir

    @staticmethod
    def cleanup(logfile):
        """Simple function to clean up log files after a run"""
        os.remove(logfile)

    def run(self, model):
        """Function to run SFALL/CAD/FFT to create phased anomalous fourier map"""

        # Make output directory
        self.work_dir = os.path.join(self.output_dir, model.pdb_code, "anomalous")
        os.mkdir(self.work_dir)

        self._f, self._sigf, self._dano, self._sigdano, self._free = mtz_util.get_labels(self.mtz)
        self._space_group, self._resolution, self._cell_parameters = mtz_util.crystal_data(self.mtz)

        # Create path to the placed mr solution
        input_model = os.path.join(self.output_dir, model.pdb_code, "mr", "molrep", "{0}_mr_output.1.pdb".format(
            model.pdb_code))
        self.name = model.pdb_code

        # Run programs
        self.sfall(input_model)
        self.cad()
        self.fft()
        self.peakmax()
        self.csymmatch()
        return

    def search_results(self, min_dist=2.0):
        """Function to extract search results"""

        print "running search"

        heavy_atom_model = os.path.join(self.work_dir, "csymmatch_{0}.pdb".format(self.name))
        input_model = os.path.join(self.output_dir, self.name, "mr", "molrep", "{0}_mr_output.1.pdb".format(
            self.name))

        peaks_over_8_rms_coordinates = []
        peaks_over_12_rms_coordinates = []

        # Get the coordinates of peaks larger than 8 rms / 12 rms
        pdb_input = iotbx.pdb.pdb_input(file_name=heavy_atom_model)
        hierarchy = pdb_input.construct_hierarchy()
        for residue_group in hierarchy.models()[0].chains()[0].residue_groups():
            for atom_group in residue_group.atom_groups():
                for atom in atom_group.atoms():
                    if atom.b >= 8:
                        peaks_over_8_rms_coordinates.append((atom.xyz[0], atom.xyz[1], atom.xyz[2]))
                    if atom.b >= 12:
                        peaks_over_12_rms_coordinates.append((atom.xyz[0], atom.xyz[1], atom.xyz[2]))

        # Get the atom coordinates of the placed MTZ
        input_model_coordinates = []

        pdb_input = iotbx.pdb.pdb_input(file_name=input_model)
        hierarchy = pdb_input.construct_hierarchy()

        for residue_group in hierarchy.models()[0].chains()[0].residue_groups():
            for atom_group in residue_group.atom_groups():
                for atom in atom_group.atoms():
                    input_model_coordinates.append((atom.xyz[0], atom.xyz[1], atom.xyz[2]))

        # Find the number of peaks within min dist (default 2A) of protein
        peaks_over_8_rms_within_2 = 0
        peaks_over_12_rms_within_2 = 0

        for peak_coordinate in peaks_over_8_rms_coordinates:
            for atom_coordinate in input_model_coordinates:
                dist = distance.euclidean(peak_coordinate, atom_coordinate)
                if dist <= min_dist:
                    peaks_over_8_rms_within_2 += 1
                    break

        for peak_coordinate in peaks_over_12_rms_coordinates:
            for atom_coordinate in input_model_coordinates:
                dist = distance.euclidean(peak_coordinate, atom_coordinate)
                if dist <= min_dist:
                    peaks_over_12_rms_within_2 += 1
                    break

        score = _AnomScore(peaks_over_8_rms=len(peaks_over_8_rms_coordinates),
                           peaks_over_12_rms=len(peaks_over_12_rms_coordinates),
                           peaks_over_8_rms_within_2A_of_model=peaks_over_8_rms_within_2,
                           peaks_over_12_rms_within_2A_of_model=peaks_over_12_rms_within_2)
        print score

    def sfall(self, model):
        """Function to run SFALL to calculated structure factors for the placed MR model

        Parameters
        ----------
        model : str
            path to placed model from MR
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.mtz : str
            mtz file input to :obj:`AnomSearch`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`
        self._f : str
            f column label set by :obj: `AnomSearch`
        self._sigf : str
            sigf column label set by :obj: `AnomSearch`
        self._free : str
            free column label set by :obj: `AnomSearch`

        Returns
        -------
        file
            mtz file containing FCalc and PHICalc columns
        """

        cmd = ["sfall",
               "HKLOUT", os.path.join(self.work_dir, "sfall_{0}.mtz".format(self.name)),
               "XYZIN", model,
               "HKLIN", self.mtz]
        command_line = os.linesep.join(map(str, cmd))

        key = """LABIN  FP={0} SIGFP={1} FREE={2}
labout -
   FC=FCalc PHIC=PHICalc
MODE SFCALC -
    XYZIN -
    HKLIN
symmetry '{3}'
badd 0.0
vdwr 2.5
end""".format(self._f, self._sigf, self._free, self._space_group)

        logfile = os.path.join(self.work_dir, 'sfall_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

        self.cleanup(logfile)

    def cad(self):
        """Function to run CAD to combine the calculated structure factors and the anomalous signal

        Parameters
        ----------
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.mtz : str
            mtz file input to :obj: `AnomSearch`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`
        self._f : str
            f column label set by :obj: `AnomSearch`
        self._sigf : str
            sigf column label set by :obj: `AnomSearch`
        self._free : str
            free column label set by :obj: `AnomSearch`
        self._dano : str
            dano column label set by :obj: `AnomSearch`
        self._sigdano : str
            sigdano column label set by :obj: `AnomSearch`
        self._resolution : float
            mtz resolution set by :obj: `AnomSearch`

        Returns
        -------
        file
            mtz file containing FCalc, PHICalc, DANO and SIGDANO columns
        """

        cmd = ["cad",
               "HKLIN1", self.mtz,
               "HKLIN2", os.path.join(self.work_dir, "sfall_{0}.mtz".format(self.name)),
               "HKLOUT", os.path.join(self.work_dir, "cad_{0}.mtz".format(self.name))]
        command_line = os.linesep.join(map(str, cmd))

        key = """monitor BRIEF
labin file 1 -
    E1 = {0} -
    E2 = {1} -
    E3 = {2} -
    E4 = {3} -
    E5 = {4}
labout file 1 -
    E1 = {0} -
    E2 = {1} -
    E3 = {2} -
    E4 = {3} -
    E5 = {4}
ctypin file 1 -
    E1 = F -
    E2 = Q -
    E3 = I -
    E4 = D -
    E5 = Q
resolution file 1 50 {5}
labin file 2 -
    E1 = FCalc -
    E2 = PHICalc
labout file 2 -
    E1 = FCalc -
    E2 = PHICalc
ctypin file 2 -
    E1 = F -
    E2 = P""".format(self._f, self._sigf, self._free, self._dano, self._sigdano, self._resolution)

        logfile = os.path.join(self.work_dir, 'cad_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

        self.cleanup(logfile)

    def fft(self):
        """Function to run FFT to create phased anomalous fourier map

        Parameters
        ----------
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`

        Returns
        -------
        file
            anomalous phased fourier map file
        file
            log file containing the results from the anomalous phased fourier
        """

        cmd = ["fft",
               "HKLIN", os.path.join(self.work_dir, "cad_{0}.mtz".format(self.name)),
               "MAPOUT", os.path.join(self.work_dir, "fft_{0}.map".format(self.name))]
        command_line = os.linesep.join(map(str, cmd))

        key = """xyzlim asu
scale F1 1.0
labin -
   DANO=DANO SIG1=SIGDANO PHI=PHICalc
end"""
        logfile = os.path.join(self.work_dir, 'fft_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

    def peakmax(self):
        """Function to run peakmax to return the peaks from FFT

        Parameters
        ----------
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`

        Returns
        -------
        file
            PDB file containing peaks
        file
            HA file containing peaks
        file
            log file containing the peaks identified by the anomalous phased fourier
        """

        cmd = ["peakmax",
               "MAPIN", os.path.join(self.work_dir, "fft_{0}.map".format(self.name)),
               "XYZOUT", os.path.join(self.work_dir, "peakmax_{0}.pdb".format(self.name)),
               "XYZFRC", os.path.join(self.work_dir, "peakmax_{0}.ha".format(self.name))]
        command_line = os.linesep.join(map(str, cmd))

        key = """threshhold -
    rms -
    3.0
numpeaks 50
output brookhaven frac
residue WAT
atname OW
chain X"""

        logfile = os.path.join(self.work_dir, 'peakmax_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

    def csymmatch(self):
        """Function to run csymmatch to correct for symmetry shifted coordinates

        Parameters
        ----------
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`
        self.output_dir : str
            output directory input to :obj: `AnomSearch`

        Returns
        -------
        file
            PDB file containing the symmetry corrected atom coordinates
        """

        cmd = ["csymmatch", "-stdin"]
        command_line = os.linesep.join(map(str, cmd))

        key = """pdbin {0}
pdbin-ref {1}
pdbout {2}
connectivity-radius 2.0""".format(os.path.join(self.work_dir, "peakmax_{0}.pdb".format(self.name)),
                                  os.path.join(self.output_dir, self.name, "mr", "molrep", "{0}_mr_output.1.pdb".format(
                                      self.name)),
                                  os.path.join(self.work_dir, "csymmatch_{0}.pdb".format(self.name)))

        logfile = os.path.join(self.work_dir, 'csymmatch_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

        self.cleanup(logfile)

<commit_msg>Fixed a bug in how the search results were repored<commit_after>"""Class to run an anomalous phased fourier on MR results"""

import iotbx.pdb
import os
from scipy.spatial import distance
import simbad_util
import mtz_util
import numpy as np

__author__ = "Adam Simpkin"
__date__ = "17 Mar 2017"
__version__ = "0.1"

class _AnomScore(object):
    """An anomalous phased fourier scoring class"""

    __slots__ = ("peaks_over_8_rms", "peaks_over_8_rms_within_2A_of_model",
                 "peaks_over_12_rms", "peaks_over_12_rms_within_2A_of_model")

    def __init__(self, peaks_over_8_rms, peaks_over_8_rms_within_2A_of_model,
                 peaks_over_12_rms, peaks_over_12_rms_within_2A_of_model):
        self.peaks_over_8_rms = peaks_over_8_rms
        self.peaks_over_8_rms_within_2A_of_model = peaks_over_8_rms_within_2A_of_model
        self.peaks_over_12_rms = peaks_over_12_rms
        self.peaks_over_12_rms_within_2A_of_model = peaks_over_12_rms_within_2A_of_model

    def __repr__(self):
        return "{0}(peaks_over_8_rms={1} " \
                "peaks_over_8_rms_within_2A_of_model={2} " \
                "peaks_over_12_rms={3} " \
                "peaks_over_12_rms_within_2A_of_model={4}".format(self.__class__.__name__,
                                                                  self.peaks_over_8_rms,
                                                                  self.peaks_over_8_rms_within_2A_of_model,
                                                                  self.peaks_over_12_rms,
                                                                  self.peaks_over_12_rms_within_2A_of_model)
    def _as_dict(self):
        """Convert the :obj:`_MrScore <simbad.util.mr_util._MrScore>`
        object to a dictionary"""
        dict = {}
        for k in self.__slots__:
            dict[k] = getattr(self, k)
        return dict

class AnomSearch():
    """An anomalous phased fourier running class

    Attributes
    ----------
    mtz : str
        The path to the input MTZ
    output_dir : str
        The path to the output directory
    model : class
        Class object containing the PDB code for the input model

    Example
    -------
    >>> from simbad.util import anomalous_util
    >>> anomalous_search = anomalous_util.AnomSearch("<mtz>", "<output_dir>")
    >>> anomalous_search.run("<model>")
    """

    def __init__(self, mtz, output_dir):
        self._mtz = None
        self._f = None
        self._sigf = None
        self._dano = None
        self._sigdano = None
        self._free = None
        self._space_group = None
        self._resolution = None
        self._cell_parameters = None
        self._output_dir = None

        self.name = None
        self.mtz = mtz
        self.output_dir = output_dir
        self.work_dir = None

    @property
    def mtz(self):
        """The input MTZ file"""
        return self._mtz

    @mtz.setter
    def mtz(self, mtz):
        """Define the input MTZ file"""
        self._mtz = mtz

    @property
    def output_dir(self):
        """The path to the working directory"""
        return self._output_dir

    @output_dir.setter
    def output_dir(self, output_dir):
        """Define the working directory"""
        self._output_dir = output_dir

    @staticmethod
    def cleanup(logfile):
        """Simple function to clean up log files after a run"""
        os.remove(logfile)

    def run(self, model):
        """Function to run SFALL/CAD/FFT to create phased anomalous fourier map"""

        # Make output directory
        self.work_dir = os.path.join(self.output_dir, model.pdb_code, "anomalous")
        os.mkdir(self.work_dir)

        self._f, self._sigf, self._dano, self._sigdano, self._free = mtz_util.get_labels(self.mtz)
        self._space_group, self._resolution, self._cell_parameters = mtz_util.crystal_data(self.mtz)

        # Create path to the placed mr solution
        input_model = os.path.join(self.output_dir, model.pdb_code, "mr", "molrep", "{0}_mr_output.1.pdb".format(
            model.pdb_code))
        self.name = model.pdb_code

        # Run programs
        self.sfall(input_model)
        self.cad()
        self.fft()
        self.peakmax()
        self.csymmatch()
        return

    def search_results(self, min_dist=2.0):
        """Function to extract search results"""

        heavy_atom_model = os.path.join(self.work_dir, "csymmatch_{0}.pdb".format(self.name))
        input_model = os.path.join(self.output_dir, self.name, "mr", "molrep", "{0}_mr_output.1.pdb".format(
            self.name))

        peaks_over_8_rms_coordinates = []
        peaks_over_12_rms_coordinates = []

        # Get the coordinates of peaks larger than 8 rms / 12 rms
        pdb_input = iotbx.pdb.pdb_input(file_name=heavy_atom_model)
        hierarchy = pdb_input.construct_hierarchy()
        for residue_group in hierarchy.models()[0].chains()[0].residue_groups():
            for atom_group in residue_group.atom_groups():
                for atom in atom_group.atoms():
                    if atom.b >= 8:
                        peaks_over_8_rms_coordinates.append((atom.xyz[0], atom.xyz[1], atom.xyz[2]))
                    if atom.b >= 12:
                        peaks_over_12_rms_coordinates.append((atom.xyz[0], atom.xyz[1], atom.xyz[2]))

        # Get the atom coordinates of the placed MTZ
        input_model_coordinates = []

        pdb_input = iotbx.pdb.pdb_input(file_name=input_model)
        hierarchy = pdb_input.construct_hierarchy()

        for residue_group in hierarchy.models()[0].chains()[0].residue_groups():
            for atom_group in residue_group.atom_groups():
                for atom in atom_group.atoms():
                    input_model_coordinates.append((atom.xyz[0], atom.xyz[1], atom.xyz[2]))

        # Find the number of peaks within min dist (default 2A) of protein
        peaks_over_8_rms_within_2 = 0
        peaks_over_12_rms_within_2 = 0

        # Find the number of peaks over 8 rms that have an euclidean distance from the protein of less than min_dist
        for peak_coordinate in peaks_over_8_rms_coordinates:
            for atom_coordinate in input_model_coordinates:
                dist = distance.euclidean(peak_coordinate, atom_coordinate)
                if dist <= min_dist:
                    peaks_over_8_rms_within_2 += 1
                    break

        # Find the number of peaks over 12 rms that have an euclidean distance from the protein of less than min_dist
        for peak_coordinate in peaks_over_12_rms_coordinates:
            for atom_coordinate in input_model_coordinates:
                dist = distance.euclidean(peak_coordinate, atom_coordinate)
                if dist <= min_dist:
                    peaks_over_12_rms_within_2 += 1
                    break

        score = _AnomScore(peaks_over_8_rms=len(peaks_over_8_rms_coordinates),
                           peaks_over_12_rms=len(peaks_over_12_rms_coordinates),
                           peaks_over_8_rms_within_2A_of_model=peaks_over_8_rms_within_2,
                           peaks_over_12_rms_within_2A_of_model=peaks_over_12_rms_within_2)
        print score

    def sfall(self, model):
        """Function to run SFALL to calculated structure factors for the placed MR model

        Parameters
        ----------
        model : str
            path to placed model from MR
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.mtz : str
            mtz file input to :obj:`AnomSearch`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`
        self._f : str
            f column label set by :obj: `AnomSearch`
        self._sigf : str
            sigf column label set by :obj: `AnomSearch`
        self._free : str
            free column label set by :obj: `AnomSearch`

        Returns
        -------
        file
            mtz file containing FCalc and PHICalc columns
        """

        cmd = ["sfall",
               "HKLOUT", os.path.join(self.work_dir, "sfall_{0}.mtz".format(self.name)),
               "XYZIN", model,
               "HKLIN", self.mtz]
        command_line = os.linesep.join(map(str, cmd))

        key = """LABIN  FP={0} SIGFP={1} FREE={2}
labout -
   FC=FCalc PHIC=PHICalc
MODE SFCALC -
    XYZIN -
    HKLIN
symmetry '{3}'
badd 0.0
vdwr 2.5
end""".format(self._f, self._sigf, self._free, self._space_group)

        logfile = os.path.join(self.work_dir, 'sfall_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

        self.cleanup(logfile)

    def cad(self):
        """Function to run CAD to combine the calculated structure factors and the anomalous signal

        Parameters
        ----------
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.mtz : str
            mtz file input to :obj: `AnomSearch`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`
        self._f : str
            f column label set by :obj: `AnomSearch`
        self._sigf : str
            sigf column label set by :obj: `AnomSearch`
        self._free : str
            free column label set by :obj: `AnomSearch`
        self._dano : str
            dano column label set by :obj: `AnomSearch`
        self._sigdano : str
            sigdano column label set by :obj: `AnomSearch`
        self._resolution : float
            mtz resolution set by :obj: `AnomSearch`

        Returns
        -------
        file
            mtz file containing FCalc, PHICalc, DANO and SIGDANO columns
        """

        cmd = ["cad",
               "HKLIN1", self.mtz,
               "HKLIN2", os.path.join(self.work_dir, "sfall_{0}.mtz".format(self.name)),
               "HKLOUT", os.path.join(self.work_dir, "cad_{0}.mtz".format(self.name))]
        command_line = os.linesep.join(map(str, cmd))

        key = """monitor BRIEF
labin file 1 -
    E1 = {0} -
    E2 = {1} -
    E3 = {2} -
    E4 = {3} -
    E5 = {4}
labout file 1 -
    E1 = {0} -
    E2 = {1} -
    E3 = {2} -
    E4 = {3} -
    E5 = {4}
ctypin file 1 -
    E1 = F -
    E2 = Q -
    E3 = I -
    E4 = D -
    E5 = Q
resolution file 1 50 {5}
labin file 2 -
    E1 = FCalc -
    E2 = PHICalc
labout file 2 -
    E1 = FCalc -
    E2 = PHICalc
ctypin file 2 -
    E1 = F -
    E2 = P""".format(self._f, self._sigf, self._free, self._dano, self._sigdano, self._resolution)

        logfile = os.path.join(self.work_dir, 'cad_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

        self.cleanup(logfile)

    def fft(self):
        """Function to run FFT to create phased anomalous fourier map

        Parameters
        ----------
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`

        Returns
        -------
        file
            anomalous phased fourier map file
        file
            log file containing the results from the anomalous phased fourier
        """

        cmd = ["fft",
               "HKLIN", os.path.join(self.work_dir, "cad_{0}.mtz".format(self.name)),
               "MAPOUT", os.path.join(self.work_dir, "fft_{0}.map".format(self.name))]
        command_line = os.linesep.join(map(str, cmd))

        key = """xyzlim asu
scale F1 1.0
labin -
   DANO=DANO SIG1=SIGDANO PHI=PHICalc
end"""
        logfile = os.path.join(self.work_dir, 'fft_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

    def peakmax(self):
        """Function to run peakmax to return the peaks from FFT

        Parameters
        ----------
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`

        Returns
        -------
        file
            PDB file containing peaks
        file
            HA file containing peaks
        file
            log file containing the peaks identified by the anomalous phased fourier
        """

        cmd = ["peakmax",
               "MAPIN", os.path.join(self.work_dir, "fft_{0}.map".format(self.name)),
               "XYZOUT", os.path.join(self.work_dir, "peakmax_{0}.pdb".format(self.name)),
               "XYZFRC", os.path.join(self.work_dir, "peakmax_{0}.ha".format(self.name))]
        command_line = os.linesep.join(map(str, cmd))

        key = """threshhold -
    rms -
    3.0
numpeaks 50
output brookhaven frac
residue WAT
atname OW
chain X"""

        logfile = os.path.join(self.work_dir, 'peakmax_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

    def csymmatch(self):
        """Function to run csymmatch to correct for symmetry shifted coordinates

        Parameters
        ----------
        self.name : str
            unique identifier for the input model set by :obj:`AnomSearch.run`
        self.work_dir : str
            working directory set by :obj:`AnomSearch.run`
        self.output_dir : str
            output directory input to :obj: `AnomSearch`

        Returns
        -------
        file
            PDB file containing the symmetry corrected atom coordinates
        """

        cmd = ["csymmatch", "-stdin"]
        command_line = os.linesep.join(map(str, cmd))

        key = """pdbin {0}
pdbin-ref {1}
pdbout {2}
connectivity-radius 2.0""".format(os.path.join(self.work_dir, "peakmax_{0}.pdb".format(self.name)),
                                  os.path.join(self.output_dir, self.name, "mr", "molrep", "{0}_mr_output.1.pdb".format(
                                      self.name)),
                                  os.path.join(self.work_dir, "csymmatch_{0}.pdb".format(self.name)))

        logfile = os.path.join(self.work_dir, 'csymmatch_{0}.log'.format(self.name))
        simbad_util.run_job(command_line, logfile, key)

        self.cleanup(logfile)

<|endoftext|>


<commit_before>#!/usr/bin/env python3

import json
import os
import glob
import multiprocessing as mp
import time
import re
import tqdm
from contextlib import redirect_stdout
from terminaltables import SingleTable
from colorclass import Color
from itertools import product

from fpgaperf import *
import sow

MANDATORY_CONSTRAINTS = {
    "vivado": "xdc",
    "vpr": "pcf",
    "vivado-yosys": "xdc",
    "nextpnr": "xdc",
}

# to find data files
root_dir = os.path.dirname(os.path.abspath(__file__))
src_dir = root_dir + '/src'

def get_reports(out_prefix):
    """Returns all the reports from all the build runs."""
    return matching_pattern(
        os.path.join(root_dir, out_prefix, '*/meta.json'), '(.*)'
    )

def get_builds(out_prefix):
    """Returns all the paths of all the builds in the build directory."""
    build_dir = os.path.join(root_dir, out_prefix)
    builds = []
    for content in os.listdir(build_dir):
        if os.path.isdir(os.path.join(build_dir, content)):
            builds.append(content)

    return builds

def print_summary_table(out_prefix, total_tasks):
    """Prints a summary table of the outcome of each test."""
    builds = get_builds(out_prefix)
    table_data = [
        ['Project', 'Toolchain', 'Family', 'Part', 'Board', 'Options']
    ]
    passed = failed = 0
    for build in builds:
        # Split directory name into columns
        # Example: oneblink_vpr_xc7_a35tcsg326-1_arty_options
        pattern = '([^_]*)_([^_]*)_([^_]*)_([^_]*)_([^_]*)_(.*)'
        row = list(re.match(pattern, build).groups())
        # Check if metadata was generated
        # It is created for successful builds only
        if os.path.exists(os.path.join(root_dir, out_prefix, build,
                                       'meta.json')):
            row.append(Color('{autogreen}passed{/autogreen}'))
            passed += 1
        else:
            row.append(Color('{autored}failed{/autored}'))
            failed += 1
        table_data.append(row)

    table_data.append(
        [
            Color('{autogreen}Passed:{/autogreen}'), passed,
            Color('{autored}Failed:{/autored}'), failed, '', '',
            '{}%'.format(int(passed / total_tasks * 100))
        ]
    )
    table = AsciiTable(table_data)
    table.inner_footing_row_border = True
    print(table.table)

    return failed == 0

def get_device_info(constraint):
    """Returns the device information:
        - FPGA family
        - FPGA part
        - board
    """
    full_info = os.path.splitext(constraint)[0]
    return full_info.split('_')

def user_selected(option):
    return [option] if option else None

def iter_options(args):
    """Returns all the possible combination of:
        - projects,
        - toolchains,
        - families,
        - devices,
        - packages
        - boards.

    Example:
    - path structure:    src/<project>/<toolchain>/<family>_<device>_<package>_<board>.<constraint>
    - valid combination: src/oneblink/vpr/xc7_a35t_csg324-1_arty.pcf
    """

    projects = user_selected(args.project) or get_projects()
    toolchains = user_selected(args.toolchain) or get_toolchains()

    combinations = set()
    for project, toolchain in list(product(projects, toolchains)):
        constraint_path = os.path.join(src_dir, project, 'constr', toolchain)

        if not os.path.exists(constraint_path):
            continue

        for constraint in os.listdir(constraint_path):
            family, device, package, board = get_device_info(constraint)

            # Check if user selected specific family/device/package/board
            family = args.family if args.family else family
            device = args.device if args.device else device
            package = args.package if args.package else package
            board = args.board if args.board else board

            combinations.add(
                (project, toolchain, family, device, package, board)
            )

    return combinations

def worker(arglist):
    def eprint(*args, **kwargs):
        print(*args, file=sys.stderr, **kwargs)

    out_prefix, verbose, project, family, device, package, board, toolchain = arglist
    # We don't want output of all subprocesses here
    # Log files for each build will be placed in build directory
    with redirect_stdout(open(os.devnull, 'w')):
        try:
            run(
                family,
                device,
                package,
                board,
                toolchain,
                project,
                None,  #out_dir
                out_prefix,
                None,  #strategy
                None,  #carry
                None,  #seed
                None,  #build
                verbose
            )
        except Exception as e:
            eprint("\n---------------------")
            eprint(
                "ERROR: {} {} {}{}{} {} test has failed\n".format(
                    project, toolchain, family, device, package, board
                )
            )
            eprint("ERROR MESSAGE: ", e)
            eprint("---------------------\n")

def main():
    import argparse
    parser = argparse.ArgumentParser(
        description='Exhaustively try project-toolchain combinations'
    )
    parser.add_argument('--family', default=None, help='device family')
    parser.add_argument('--device', default=None, help='FPGA device')
    parser.add_argument('--package', default=None, help='FPGA package')
    parser.add_argument('--board', default=None, help='target board')
    parser.add_argument(
        '--project',
        default=None,
        help='run given project only (default: all)'
    )
    parser.add_argument(
        '--toolchain',
        default=None,
        help='run given toolchain only (default: all)'
    )
    parser.add_argument(
        '--out-prefix',
        default='build',
        help='output directory prefix (default: build)'
    )
    parser.add_argument(
        '--dry', action='store_true', help='print commands, don\'t invoke'
    )
    parser.add_argument('--fail', action='store_true', help='fail on error')
    parser.add_argument(
        '--verbose', action='store_true', help='verbose output'
    )
    args = parser.parse_args()

    print('Running exhaustive project-toolchain search')

    tasks = []

    # Always check if given option was overriden by user's argument
    # if not - run all available tests
    combinations = iter_options(args)
    for project, toolchain, family, device, package, board in combinations:
        if toolchain not in MANDATORY_CONSTRAINTS.keys():
            continue

        mandatory_constraint = MANDATORY_CONSTRAINTS[toolchain]
        constraint = "_".join((family, device, package, board)
                              ) + ".{}".format(mandatory_constraint)
        constraint_path = os.path.join(
            src_dir, project, 'constr', toolchain, constraint
        )

        if os.path.exists(constraint_path):
            task = (
                args.out_prefix, args.verbose, project, family, device,
                package, board, toolchain
            )
            tasks.append(task)

    assert len(tasks), "No tasks to run!"

    if not os.path.exists(args.out_prefix):
        os.mkdir(args.out_prefix)

    with mp.Pool(mp.cpu_count()) as pool:
        for _ in tqdm.tqdm(pool.imap_unordered(worker, tasks),
                           total=len(tasks), unit='test'):
            pass

    # Combine results of all tests
    print('Merging results')
    merged_dict = {}

    for report in get_reports(args.out_prefix):
        sow.merge(merged_dict, json.load(open(report, 'r')))

    fout = open('{}/all.json'.format(args.out_prefix), 'w')
    json.dump(merged_dict, fout, indent=4, sort_keys=True)

    result = print_summary_table(args.out_prefix, len(tasks))

    if not result:
        print("ERROR: some tests have failed.")
        exit(1)

if __name__ == '__main__':
    main()
<commit_msg>exhaust: added argument examples<commit_after>#!/usr/bin/env python3

import json
import os
import glob
import multiprocessing as mp
import time
import re
import tqdm
from contextlib import redirect_stdout
from terminaltables import SingleTable
from colorclass import Color
from itertools import product

from fpgaperf import *
import sow

MANDATORY_CONSTRAINTS = {
    "vivado": "xdc",
    "vpr": "pcf",
    "vivado-yosys": "xdc",
    "nextpnr": "xdc",
}

# to find data files
root_dir = os.path.dirname(os.path.abspath(__file__))
src_dir = root_dir + '/src'

def get_reports(out_prefix):
    """Returns all the reports from all the build runs."""
    return matching_pattern(
        os.path.join(root_dir, out_prefix, '*/meta.json'), '(.*)'
    )

def get_builds(out_prefix):
    """Returns all the paths of all the builds in the build directory."""
    build_dir = os.path.join(root_dir, out_prefix)
    builds = []
    for content in os.listdir(build_dir):
        if os.path.isdir(os.path.join(build_dir, content)):
            builds.append(content)

    return builds

def print_summary_table(out_prefix, total_tasks):
    """Prints a summary table of the outcome of each test."""
    builds = get_builds(out_prefix)
    table_data = [
        ['Project', 'Toolchain', 'Family', 'Part', 'Board', 'Options']
    ]
    passed = failed = 0
    for build in builds:
        # Split directory name into columns
        # Example: oneblink_vpr_xc7_a35tcsg326-1_arty_options
        pattern = '([^_]*)_([^_]*)_([^_]*)_([^_]*)_([^_]*)_(.*)'
        row = list(re.match(pattern, build).groups())
        # Check if metadata was generated
        # It is created for successful builds only
        if os.path.exists(os.path.join(root_dir, out_prefix, build,
                                       'meta.json')):
            row.append(Color('{autogreen}passed{/autogreen}'))
            passed += 1
        else:
            row.append(Color('{autored}failed{/autored}'))
            failed += 1
        table_data.append(row)

    table_data.append(
        [
            Color('{autogreen}Passed:{/autogreen}'), passed,
            Color('{autored}Failed:{/autored}'), failed, '', '',
            '{}%'.format(int(passed / total_tasks * 100))
        ]
    )
    table = AsciiTable(table_data)
    table.inner_footing_row_border = True
    print(table.table)

    return failed == 0

def get_device_info(constraint):
    """Returns the device information:
        - FPGA family
        - FPGA part
        - board
    """
    full_info = os.path.splitext(constraint)[0]
    return full_info.split('_')

def user_selected(option):
    return [option] if option else None

def iter_options(args):
    """Returns all the possible combination of:
        - projects,
        - toolchains,
        - families,
        - devices,
        - packages
        - boards.

    Example:
    - path structure:    src/<project>/<toolchain>/<family>_<device>_<package>_<board>.<constraint>
    - valid combination: src/oneblink/vpr/xc7_a35t_csg324-1_arty.pcf
    """

    projects = user_selected(args.project) or get_projects()
    toolchains = user_selected(args.toolchain) or get_toolchains()

    combinations = set()
    for project, toolchain in list(product(projects, toolchains)):
        constraint_path = os.path.join(src_dir, project, 'constr', toolchain)

        if not os.path.exists(constraint_path):
            continue

        for constraint in os.listdir(constraint_path):
            family, device, package, board = get_device_info(constraint)

            # Check if user selected specific family/device/package/board
            family = args.family if args.family else family
            device = args.device if args.device else device
            package = args.package if args.package else package
            board = args.board if args.board else board

            combinations.add(
                (project, toolchain, family, device, package, board)
            )

    return combinations

def worker(arglist):
    def eprint(*args, **kwargs):
        print(*args, file=sys.stderr, **kwargs)

    out_prefix, verbose, project, family, device, package, board, toolchain = arglist
    # We don't want output of all subprocesses here
    # Log files for each build will be placed in build directory
    with redirect_stdout(open(os.devnull, 'w')):
        try:
            run(
                family,
                device,
                package,
                board,
                toolchain,
                project,
                None,  #out_dir
                out_prefix,
                None,  #strategy
                None,  #carry
                None,  #seed
                None,  #build
                verbose
            )
        except Exception as e:
            eprint("\n---------------------")
            eprint(
                "ERROR: {} {} {}{}{} {} test has failed\n".format(
                    project, toolchain, family, device, package, board
                )
            )
            eprint("ERROR MESSAGE: ", e)
            eprint("---------------------\n")

def main():
    import argparse
    parser = argparse.ArgumentParser(
        description='Exhaustively try project-toolchain combinations'
    )
    parser.add_argument(
        '--family', default=None, help='device family: e.g. --family xc7'
    )
    parser.add_argument(
        '--device', default=None, help='FPGA device: e.g. --device a35t'
    )
    parser.add_argument(
        '--package',
        default=None,
        help='FPGA package: e.g. --package csg324-1'
    )
    parser.add_argument(
        '--board', default=None, help='target board: e.g. --board arty'
    )
    parser.add_argument(
        '--project',
        default=None,
        help='run given project only (default: all)'
    )
    parser.add_argument(
        '--toolchain',
        default=None,
        help='run given toolchain only (default: all)'
    )
    parser.add_argument(
        '--out-prefix',
        default='build',
        help='output directory prefix (default: build)'
    )
    parser.add_argument(
        '--dry', action='store_true', help='print commands, don\'t invoke'
    )
    parser.add_argument('--fail', action='store_true', help='fail on error')
    parser.add_argument(
        '--verbose', action='store_true', help='verbose output'
    )
    args = parser.parse_args()

    print('Running exhaustive project-toolchain search')

    tasks = []

    # Always check if given option was overriden by user's argument
    # if not - run all available tests
    combinations = iter_options(args)
    for project, toolchain, family, device, package, board in combinations:
        if toolchain not in MANDATORY_CONSTRAINTS.keys():
            continue

        mandatory_constraint = MANDATORY_CONSTRAINTS[toolchain]
        constraint = "_".join((family, device, package, board)
                              ) + ".{}".format(mandatory_constraint)
        constraint_path = os.path.join(
            src_dir, project, 'constr', toolchain, constraint
        )

        if os.path.exists(constraint_path):
            task = (
                args.out_prefix, args.verbose, project, family, device,
                package, board, toolchain
            )
            tasks.append(task)

    assert len(tasks), "No tasks to run!"

    if not os.path.exists(args.out_prefix):
        os.mkdir(args.out_prefix)

    with mp.Pool(mp.cpu_count()) as pool:
        for _ in tqdm.tqdm(pool.imap_unordered(worker, tasks),
                           total=len(tasks), unit='test'):
            pass

    # Combine results of all tests
    print('Merging results')
    merged_dict = {}

    for report in get_reports(args.out_prefix):
        sow.merge(merged_dict, json.load(open(report, 'r')))

    fout = open('{}/all.json'.format(args.out_prefix), 'w')
    json.dump(merged_dict, fout, indent=4, sort_keys=True)

    result = print_summary_table(args.out_prefix, len(tasks))

    if not result:
        print("ERROR: some tests have failed.")
        exit(1)

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
    Module for definition of custom messages for exceptions for Particles
    classes.
"""

"""
    Message Errors:
"""

_PC_errors = {
    'IncorrectParticlesTuple': """Incorrect length of particles tuple: \
    it shouldn\'t be empty.""",
    'ParticleContainer_DuplicatedValue': 'Object already exists!',
    'ParticleContainer_UnknownValue': 'Value doesn\'t exists!',
    'ParticleContainer_IdNotGenerated': """The particle container didnt\
    generate an unique id."""
}
<commit_msg>remove unused code<commit_after><|endoftext|>


<commit_before>from expsuite import ExperimentSuite
from matplotlib import pyplot as plt
from numpy import *
import time

suite = ExperimentSuite()

# get all experiments
experiments = suite.get_exps()
shape = ceil(sqrt(len(experiments)))
hgraphs = {}

plt.ion()
plt.figure(figsize=(16,10))

def creategraphs():
    for i, exp in enumerate(experiments):
        plt.subplot(shape, shape, i+1)

        hgraphs[exp] = []
        params = suite.read_params(exp)

        for r in range(params['repetitions']):
            h = suite.get_history(exp, r, 'return')
            # if h != []:
            hgraphs[exp].append(plt.plot(h, color='gray', alpha=0.5)[0])

        plt.title(exp)
        plt.gcf().canvas.draw()

if __name__ == '__main__':
    creategraphs()
    while True:
        for i, exp in enumerate(experiments):
            params = suite.read_params(exp)

            for r,p in enumerate(hgraphs[exp]):
                h = suite.get_history(exp, r, 'return')
                if h != []:
                    if len(h) > len(p.get_ydata()):
                        # new data point, draw line in red
                        p.set_color('red')
                    else:
                        p.set_color('gray')

                    # set the new axis limits
                    plt.subplot(shape, shape, i+1)
                    l = plt.xlim()
                    plt.xlim(0, max(len(h)+1, l[1]))
                    l = plt.ylim()
                    print l, len(h), h
                    plt.ylim(min(l[0], min(h)-0.1*abs(min(h))), max(l[1], max(h)+0.1*abs(max(h))))

                    p.set_xdata(range(len(h)))
                    p.set_ydata(list(h))

        plt.gcf().canvas.draw()
        print 'x'
        time.sleep(3)

<commit_msg>no debug outputs<commit_after>from expsuite import ExperimentSuite
from matplotlib import pyplot as plt
from numpy import *
import time

suite = ExperimentSuite()

# get all experiments
experiments = suite.get_exps()
shape = ceil(sqrt(len(experiments)))
hgraphs = {}

plt.ion()
plt.figure(figsize=(16,10))

def creategraphs():
    for i, exp in enumerate(experiments):
        plt.subplot(shape, shape, i+1)

        hgraphs[exp] = []
        params = suite.read_params(exp)

        for r in range(params['repetitions']):
            h = suite.get_history(exp, r, 'return')
            # if h != []:
            hgraphs[exp].append(plt.plot(h, color='gray')[0])

        plt.title(exp)
        plt.gcf().canvas.draw()

if __name__ == '__main__':
    creategraphs()
    while True:
        for i, exp in enumerate(experiments):
            params = suite.read_params(exp)

            for r,p in enumerate(hgraphs[exp]):
                h = suite.get_history(exp, r, 'return')
                if h != []:
                    if len(h) > len(p.get_ydata()):
                        # new data point, draw line in red
                        p.set_color('red')
                    else:
                        p.set_color('gray')

                    # set the new axis limits
                    plt.subplot(shape, shape, i+1)
                    l = plt.xlim()
                    plt.xlim(0, max(len(h)+1, l[1]))
                    l = plt.ylim()
                    plt.ylim(min(l[0], min(h)-0.1*abs(min(h))), max(l[1], max(h)+0.1*abs(max(h))))

                    p.set_xdata(range(len(h)))
                    p.set_ydata(list(h))

        plt.gcf().canvas.draw()
        time.sleep(3)

<|endoftext|>


<commit_before><commit_msg>test: Create stub of fit testing<commit_after>"""
This module contains tests of code from the file named
`../forecastonishing/selection/on_the_fly_selector.py`.

@author: Nikolay Lysenko
"""

import unittest

import numpy as np
import pandas as pd

from forecastonishing.selection.on_the_fly_selector import OnTheFlySelector
from forecastonishing.miscellaneous.simple_forecasters import (
    MovingAverageForecaster,
    MovingMedianForecaster
)

class TestOnTheFlySelector(unittest.TestCase):
    """
    Tests of `OnTheFlySelector` class.
    """

    def test_fit(self) -> type(None):
        """
        Test `fit` method.

        :return:
            None
        """
        candidates = {
            MovingAverageForecaster():
                [{'rolling_kwargs': {'window': w, 'min_periods': 1}}
                 for w in range(1, 3)],
            MovingMedianForecaster():
                [{'rolling_kwargs': {'window': w, 'min_periods': 1}}
                 for w in range(3, 4)]
        }
        selector = OnTheFlySelector(candidates)
        df = pd.DataFrame(
            [[1, 2],
             [1, 3],
             [1, 6],
             [1, 5],
             [2, 3],
             [2, 4],
             [2, 4.5],
             [2, 1]],
            columns=['key', 'target']
        )
        selector.fit(df, 'target', ['key'])
        print(selector.best_scores_)
        true_answer = None
        # TODO: Finish this test.

def main():
    test_loader = unittest.TestLoader()
    suites_list = []
    testers = [
        TestOnTheFlySelector()
    ]
    for tester in testers:
        suite = test_loader.loadTestsFromModule(tester)
        suites_list.append(suite)
    overall_suite = unittest.TestSuite(suites_list)
    unittest.TextTestRunner().run(overall_suite)

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, _
from odoo.addons.mail.wizard.mail_compose_message import _reopen
from odoo.exceptions import UserError

class AccountInvoiceSend(models.TransientModel):
    _name = 'account.invoice.send'
    _inherits = {'mail.compose.message':'composer_id'}
    _description = 'Account Invoice Send'

    is_email = fields.Boolean('Email', default=lambda self: self.env.company.invoice_is_email)
    invoice_without_email = fields.Text(compute='_compute_invoice_without_email', string='invoice(s) that will not be sent')
    is_print = fields.Boolean('Print', default=lambda self: self.env.company.invoice_is_print)
    printed = fields.Boolean('Is Printed', default=False)
    invoice_ids = fields.Many2many('account.move', 'account_move_account_invoice_send_rel', string='Invoices')
    composer_id = fields.Many2one('mail.compose.message', string='Composer', required=True, ondelete='cascade')
    template_id = fields.Many2one(
        'mail.template', 'Use template', index=True,
        domain="[('model', '=', 'account.move')]"
        )

    @api.model
    def default_get(self, fields):
        res = super(AccountInvoiceSend, self).default_get(fields)
        res_ids = self._context.get('active_ids')

        invoices = self.env['account.move'].browse(res_ids).filtered(lambda move: move.is_invoice(include_receipts=True))
        if not invoices:
            raise UserError(_("You can only send invoices."))

        composer = self.env['mail.compose.message'].create({
            'composition_mode': 'comment' if len(res_ids) == 1 else 'mass_mail',
        })
        res.update({
            'invoice_ids': res_ids,
            'composer_id': composer.id,
        })
        return res

    @api.onchange('invoice_ids')
    def _compute_composition_mode(self):
        for wizard in self:
            wizard.composition_mode = 'comment' if len(wizard.invoice_ids) == 1 else 'mass_mail'

    @api.onchange('template_id')
    def onchange_template_id(self):
        if self.composer_id:
            self.composer_id.template_id = self.template_id.id
            self.composer_id.onchange_template_id_wrapper()

    @api.onchange('is_email')
    def _compute_invoice_without_email(self):
        for wizard in self:
            if wizard.is_email and len(wizard.invoice_ids) > 1:
                invoices = self.env['account.move'].search([
                    ('id', 'in', self.env.context.get('active_ids')),
                    ('partner_id.email', '=', False)
                ])
                if invoices:
                    wizard.invoice_without_email = "%s\n%s" % (
                        _("The following invoice(s) will not be sent by email, because the customers don't have email address."),
                        "\n".join([i.reference for i in invoices])
                        )
                else:
                    wizard.invoice_without_email = False
            else:
                wizard.invoice_without_email = False

    def _send_email(self):
        if self.is_email:
            self.composer_id.send_mail()
            if self.env.context.get('mark_invoice_as_sent'):
                self.mapped('invoice_ids').write({'invoice_sent': True})

    def _print_document(self):
        """ to override for each type of models that will use this composer."""
        self.ensure_one()
        action = self.invoice_ids.action_invoice_print()
        action.update({'close_on_report_download': True})
        return action

    def send_and_print_action(self):
        self.ensure_one()
        # Send the mails in the correct language by splitting the ids per lang.
        # This should ideally be fixed in mail_compose_message, so when a fix is made there this whole commit should be reverted.
        # basically self.body (which could be manually edited) extracts self.template_id,
        # which is then not translated for each customer.
        if self.composition_mode == 'mass_mail' and self.template_id:
            active_ids = self.env.context.get('active_ids', self.res_id)
            active_records = self.env[self.model].browse(active_ids)
            langs = active_records.mapped('partner_id.lang')
            default_lang = self.env.context.get('lang', 'en_US')
            for lang in (set(langs) or [default_lang]):
                active_ids_lang = active_records.filtered(lambda r: r.partner_id.lang == lang).ids
                self_lang = self.with_context(active_ids=active_ids_lang, lang=lang)
                self_lang.onchange_template_id()
                self_lang._send_email()
        else:
            self._send_email()
        if self.is_print:
            return self._print_document()
        return {'type': 'ir.actions.act_window_close'}

    def save_as_template(self):
        self.ensure_one()
        self.composer_id.save_as_template()
        action = _reopen(self, self.id, self.model, context=self._context)
        action.update({'name': _('Send Invoice')})
        return action
<commit_msg>[FIX] account: fix send invoice mail issue<commit_after># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, _
from odoo.addons.mail.wizard.mail_compose_message import _reopen
from odoo.exceptions import UserError

class AccountInvoiceSend(models.TransientModel):
    _name = 'account.invoice.send'
    _inherits = {'mail.compose.message':'composer_id'}
    _description = 'Account Invoice Send'

    is_email = fields.Boolean('Email', default=lambda self: self.env.company.invoice_is_email)
    invoice_without_email = fields.Text(compute='_compute_invoice_without_email', string='invoice(s) that will not be sent')
    is_print = fields.Boolean('Print', default=lambda self: self.env.company.invoice_is_print)
    printed = fields.Boolean('Is Printed', default=False)
    invoice_ids = fields.Many2many('account.move', 'account_move_account_invoice_send_rel', string='Invoices')
    composer_id = fields.Many2one('mail.compose.message', string='Composer', required=True, ondelete='cascade')
    template_id = fields.Many2one(
        'mail.template', 'Use template', index=True,
        domain="[('model', '=', 'account.move')]"
        )

    @api.model
    def default_get(self, fields):
        res = super(AccountInvoiceSend, self).default_get(fields)
        res_ids = self._context.get('active_ids')

        invoices = self.env['account.move'].browse(res_ids).filtered(lambda move: move.is_invoice(include_receipts=True))
        if not invoices:
            raise UserError(_("You can only send invoices."))

        composer = self.env['mail.compose.message'].create({
            'composition_mode': 'comment' if len(res_ids) == 1 else 'mass_mail',
        })
        res.update({
            'invoice_ids': res_ids,
            'composer_id': composer.id,
        })
        return res

    @api.onchange('invoice_ids')
    def _compute_composition_mode(self):
        for wizard in self:
            wizard.composition_mode = 'comment' if len(wizard.invoice_ids) == 1 else 'mass_mail'

    @api.onchange('template_id')
    def onchange_template_id(self):
        if self.composer_id:
            self.composer_id.template_id = self.template_id.id
            self.composer_id.onchange_template_id_wrapper()

    @api.onchange('is_email')
    def onchange_is_email(self):
        if self.is_email:
            if not self.composer_id:
                res_ids = self._context.get('active_ids')
                self.composer_id = self.env['mail.compose.message'].create({
                    'composition_mode': 'comment' if len(res_ids) == 1 else 'mass_mail',
                    'template_id': self.template_id.id
                })
            self.composer_id.onchange_template_id_wrapper()

    @api.onchange('is_email')
    def _compute_invoice_without_email(self):
        for wizard in self:
            if wizard.is_email and len(wizard.invoice_ids) > 1:
                invoices = self.env['account.move'].search([
                    ('id', 'in', self.env.context.get('active_ids')),
                    ('partner_id.email', '=', False)
                ])
                if invoices:
                    wizard.invoice_without_email = "%s\n%s" % (
                        _("The following invoice(s) will not be sent by email, because the customers don't have email address."),
                        "\n".join([i.reference for i in invoices])
                        )
                else:
                    wizard.invoice_without_email = False
            else:
                wizard.invoice_without_email = False

    def _send_email(self):
        if self.is_email:
            self.composer_id.send_mail()
            if self.env.context.get('mark_invoice_as_sent'):
                self.mapped('invoice_ids').write({'invoice_sent': True})

    def _print_document(self):
        """ to override for each type of models that will use this composer."""
        self.ensure_one()
        action = self.invoice_ids.action_invoice_print()
        action.update({'close_on_report_download': True})
        return action

    def send_and_print_action(self):
        self.ensure_one()
        # Send the mails in the correct language by splitting the ids per lang.
        # This should ideally be fixed in mail_compose_message, so when a fix is made there this whole commit should be reverted.
        # basically self.body (which could be manually edited) extracts self.template_id,
        # which is then not translated for each customer.
        if self.composition_mode == 'mass_mail' and self.template_id:
            active_ids = self.env.context.get('active_ids', self.res_id)
            active_records = self.env[self.model].browse(active_ids)
            langs = active_records.mapped('partner_id.lang')
            default_lang = self.env.context.get('lang', 'en_US')
            for lang in (set(langs) or [default_lang]):
                active_ids_lang = active_records.filtered(lambda r: r.partner_id.lang == lang).ids
                self_lang = self.with_context(active_ids=active_ids_lang, lang=lang)
                self_lang.onchange_template_id()
                self_lang._send_email()
        else:
            self._send_email()
        if self.is_print:
            return self._print_document()
        return {'type': 'ir.actions.act_window_close'}

    def save_as_template(self):
        self.ensure_one()
        self.composer_id.save_as_template()
        action = _reopen(self, self.id, self.model, context=self._context)
        action.update({'name': _('Send Invoice')})
        return action
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
# Author: Priit Laes, Povi Software LLC
#         Onestein BV
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.openupgrade import openupgrade
from openerp import pooler, SUPERUSER_ID
from datetime import datetime

column_renames = {
    'mrp_bom_mrp_property_rel': [
        ('bom_id', 'mrp_bom_id'),
        ('property_id', 'mrp_property_id')
    ]
}

def move_fields(cr, pool):
    execute = openupgrade.logged_query
    queries = [
        """
UPDATE mrp_bom
SET product_tmpl_id=(SELECT product_tmpl_id
FROM product_product
WHERE product_product.id=mrp_bom.product_id)
""",
        """
ALTER TABLE mrp_bom ALTER COLUMN product_tmpl_id SET NOT NULL
""",
        """
ALTER TABLE mrp_bom DROP CONSTRAINT mrp_bom_bom_id_fkey
"""
    ]
    for sql in queries:
        execute(cr, sql)

def migrate_bom_line_property_rel(cr, uid, row, fields, new_line_id, bom_line_obj):

    """Move from bom to bom_line"""
#     cr2 = self.pool.db.cursor()
    bom_property_rel = []
    # Get the property_ids related to this row
    sql = """SELECT property_id FROM mrp_bom_property_rel WHERE bom_id = {}""".format(row['id'])

    cr.execute(sql)
    for rel_row in cr.dictfetchall():
        # fill temporary m2m bom_property_rel list
        bom_property_rel.append({'bom_id': new_line_id, 'property_id': rel_row['property_id']})

    # remove old mrp_bom_property_rel ?

    # fill new bom to property m2m table
    for values in bom_property_rel:
        bom_line_obj.write(cr, uid, values['bom_id'], {'property_id': [(4, values['property_id'])]})

# TODO check if mrp_bom property_ids needs migration
def migrate_bom_property_rel(cr, pool, uid):
    """Copy mrp_bom_property_rel to mrp_bom_mrp_property_rel
    """
    # Warning: assuming mrp_bom ids did not change!
    # Try openupgrade.rename_tables(cr, table_spec) instead (pre-script)
    pass

def migrate_bom_lines(cr, pool, uid):
    bom_line_obj = pool['mrp.bom.line']
    fields = {
        'bom_id': openupgrade.get_legacy_name('bom_id'),
        'product_uos': openupgrade.get_legacy_name('product_uos'),
        'product_uos_qty': openupgrade.get_legacy_name('product_uos_qty'),
    }
    sql = \
        """
        SELECT id, %(bom_id)s, product_efficiency, product_id, product_qty,
        product_rounding, product_uom, %(product_uos)s, %(product_uos_qty)s,
        routing_id, sequence, type
        FROM mrp_bom
        WHERE %(bom_id)s is NOT NULL
        """ % fields
    cr.execute(sql)
    ids = []
    for row in cr.dictfetchall():
        bom_line_obj.create(cr, SUPERUSER_ID, {
            'bom_id': row[fields['bom_id']],
            'product_efficiency': row['product_efficiency'],
            'product_id': row['product_id'],
            'product_qty': row['product_qty'],
            'product_rounding': row['product_rounding'],
            'product_uom': row['product_uom'],
            'product_uos': row[fields['product_uos']],
            'product_uos_qty': row[fields['product_uos_qty']],
            'routing_id': row['routing_id'],
            'sequence': row['sequence'],
            'type': row['type']
            })
        ids.append(str(row['id']))

#         migrate_bom_line_property_rel(cr, uid, row, fields, new_line_id, bom_line_obj)

        # attribute_value_ids - new in 8.0
        # TODO: property_ids
    # Remove unneeded items
    cr.execute("DELETE FROM mrp_bom WHERE id in (%s)" % ','.join(ids))

def fix_domains(cr, pool, uid):
    sql = """UPDATE ir_act_window SET domain = NULL WHERE domain =
'[(''bom_id'',''='',False)]' AND res_model = 'mrp.bom'"""
    cr.execute(sql)
    cr.commit()

def update_stock_moves(cr, pool, uid):
    stock_move_obj = pool['stock.move']
    sm_ids = stock_move_obj.search(cr, uid, [])
    mrp_production_obj = pool['mrp.production']
    location_obj = pool['stock.location']
    location_id = location_obj.search(cr, uid, [('name', '=', 'Production')])
    for sm in stock_move_obj.browse(cr, uid, sm_ids):
        if 'MO' in sm.name and sm.location_dest_id.id == location_id[0]:
            prod_id = mrp_production_obj.search(cr, uid, [('name', '=', sm.name)])
            sql = """UPDATE stock_move SET raw_material_production_id = %s WHERE id = % s""" % (prod_id[0], sm.id)
            cr.execute(sql)
            cr.commit()
#             sm.write(cr, uid, {'raw_material_production_id': prod_id[0]})

def update_stock_picking_name(cr, pool, uid):
    picking_obj = pool['stock.picking']
    picking_ids = picking_obj.search(cr, uid, [])
    for sp in picking_obj.browse(cr, uid, picking_ids):
        if not sp.name:
            if ':' in sp.origin:
                origin = sp.origin.split(":")[1]
            else:
                origin = sp.origin
            sql = """UPDATE stock_picking SET name = '%s' WHERE id = %s""" % (origin, sp.id)
            picking_exists = picking_obj.search(cr, uid, [('name', '=', origin)])
            if not picking_exists:
                cr.execute(sql)
            else:
                sql = """UPDATE stock_picking SET name = '%s' WHERE id = %s""" % (
                    origin + str(datetime.now().time()), sp.id
                )
                cr.execute(sql)

def migrate_product_supply_method(cr, pool, uid):
    '''
    Procurements of products: change the supply_method for the matching route
    produce -> Manufacture Rule
    :param cr: Database cursor
    '''
    pool = pooler.get_pool(cr.dbname)
    route_obj = pool['stock.location.route']
    template_obj = pool['product.template']

    mto_route_id = route_obj.search(cr, uid, [('name', 'like', 'Manufacture')])
    mto_route_id = mto_route_id and mto_route_id[0] or False

    supply_method_legacy = openupgrade.get_legacy_name('supply_method')
    if mto_route_id:
        product_ids = []
        cr.execute("SELECT id FROM product_template WHERE %s = %%s" % supply_method_legacy, ('produce',))
        for res in cr.fetchall():
            product_ids.append(res[0])

        template_obj.write(cr, uid, product_ids, {'route_ids': [(4, mto_route_id)]})

@openupgrade.migrate()
def migrate(cr, version):
    pool = pooler.get_pool(cr.dbname)
    move_fields(cr, pool)
    uid = SUPERUSER_ID
    migrate_bom_lines(cr, pool, uid)
    migrate_bom_property_rel(cr, pool, uid)
    fix_domains(cr, pool, uid)
    update_stock_moves(cr, pool, uid)
    update_stock_picking_name(cr, pool, uid)
    migrate_product_supply_method(cr, pool, uid)
    openupgrade.rename_columns(cr, column_renames)
<commit_msg>[FIX] don't break on empty list<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
# Author: Priit Laes, Povi Software LLC
#         Onestein BV
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.openupgrade import openupgrade
from openerp import pooler, SUPERUSER_ID
from datetime import datetime

column_renames = {
    'mrp_bom_mrp_property_rel': [
        ('bom_id', 'mrp_bom_id'),
        ('property_id', 'mrp_property_id')
    ]
}

def move_fields(cr, pool):
    execute = openupgrade.logged_query
    queries = [
        """
UPDATE mrp_bom
SET product_tmpl_id=(SELECT product_tmpl_id
FROM product_product
WHERE product_product.id=mrp_bom.product_id)
""",
        """
ALTER TABLE mrp_bom ALTER COLUMN product_tmpl_id SET NOT NULL
""",
        """
ALTER TABLE mrp_bom DROP CONSTRAINT mrp_bom_bom_id_fkey
"""
    ]
    for sql in queries:
        execute(cr, sql)

def migrate_bom_line_property_rel(cr, uid, row, fields, new_line_id, bom_line_obj):

    """Move from bom to bom_line"""
#     cr2 = self.pool.db.cursor()
    bom_property_rel = []
    # Get the property_ids related to this row
    sql = """SELECT property_id FROM mrp_bom_property_rel WHERE bom_id = {}""".format(row['id'])

    cr.execute(sql)
    for rel_row in cr.dictfetchall():
        # fill temporary m2m bom_property_rel list
        bom_property_rel.append({'bom_id': new_line_id, 'property_id': rel_row['property_id']})

    # remove old mrp_bom_property_rel ?

    # fill new bom to property m2m table
    for values in bom_property_rel:
        bom_line_obj.write(cr, uid, values['bom_id'], {'property_id': [(4, values['property_id'])]})

# TODO check if mrp_bom property_ids needs migration
def migrate_bom_property_rel(cr, pool, uid):
    """Copy mrp_bom_property_rel to mrp_bom_mrp_property_rel
    """
    # Warning: assuming mrp_bom ids did not change!
    # Try openupgrade.rename_tables(cr, table_spec) instead (pre-script)
    pass

def migrate_bom_lines(cr, pool, uid):
    bom_line_obj = pool['mrp.bom.line']
    fields = {
        'bom_id': openupgrade.get_legacy_name('bom_id'),
        'product_uos': openupgrade.get_legacy_name('product_uos'),
        'product_uos_qty': openupgrade.get_legacy_name('product_uos_qty'),
    }
    sql = \
        """
        SELECT id, %(bom_id)s, product_efficiency, product_id, product_qty,
        product_rounding, product_uom, %(product_uos)s, %(product_uos_qty)s,
        routing_id, sequence, type
        FROM mrp_bom
        WHERE %(bom_id)s is NOT NULL
        """ % fields
    cr.execute(sql)
    ids = []
    for row in cr.dictfetchall():
        bom_line_obj.create(cr, SUPERUSER_ID, {
            'bom_id': row[fields['bom_id']],
            'product_efficiency': row['product_efficiency'],
            'product_id': row['product_id'],
            'product_qty': row['product_qty'],
            'product_rounding': row['product_rounding'],
            'product_uom': row['product_uom'],
            'product_uos': row[fields['product_uos']],
            'product_uos_qty': row[fields['product_uos_qty']],
            'routing_id': row['routing_id'],
            'sequence': row['sequence'],
            'type': row['type']
            })
        ids.append(str(row['id']))

#         migrate_bom_line_property_rel(cr, uid, row, fields, new_line_id, bom_line_obj)

        # attribute_value_ids - new in 8.0
        # TODO: property_ids
    # Remove unneeded items
    if ids:
        cr.execute("DELETE FROM mrp_bom WHERE id in (%s)" % ','.join(ids))

def fix_domains(cr, pool, uid):
    sql = """UPDATE ir_act_window SET domain = NULL WHERE domain =
'[(''bom_id'',''='',False)]' AND res_model = 'mrp.bom'"""
    cr.execute(sql)
    cr.commit()

def update_stock_moves(cr, pool, uid):
    stock_move_obj = pool['stock.move']
    sm_ids = stock_move_obj.search(cr, uid, [])
    mrp_production_obj = pool['mrp.production']
    location_obj = pool['stock.location']
    location_id = location_obj.search(cr, uid, [('name', '=', 'Production')])
    for sm in stock_move_obj.browse(cr, uid, sm_ids):
        if 'MO' in sm.name and sm.location_dest_id.id == location_id[0]:
            prod_id = mrp_production_obj.search(cr, uid, [('name', '=', sm.name)])
            sql = """UPDATE stock_move SET raw_material_production_id = %s WHERE id = % s""" % (prod_id[0], sm.id)
            cr.execute(sql)
            cr.commit()
#             sm.write(cr, uid, {'raw_material_production_id': prod_id[0]})

def update_stock_picking_name(cr, pool, uid):
    picking_obj = pool['stock.picking']
    picking_ids = picking_obj.search(cr, uid, [])
    for sp in picking_obj.browse(cr, uid, picking_ids):
        if not sp.name:
            if ':' in sp.origin:
                origin = sp.origin.split(":")[1]
            else:
                origin = sp.origin
            sql = """UPDATE stock_picking SET name = '%s' WHERE id = %s""" % (origin, sp.id)
            picking_exists = picking_obj.search(cr, uid, [('name', '=', origin)])
            if not picking_exists:
                cr.execute(sql)
            else:
                sql = """UPDATE stock_picking SET name = '%s' WHERE id = %s""" % (
                    origin + str(datetime.now().time()), sp.id
                )
                cr.execute(sql)

def migrate_product_supply_method(cr, pool, uid):
    '''
    Procurements of products: change the supply_method for the matching route
    produce -> Manufacture Rule
    :param cr: Database cursor
    '''
    pool = pooler.get_pool(cr.dbname)
    route_obj = pool['stock.location.route']
    template_obj = pool['product.template']

    mto_route_id = route_obj.search(cr, uid, [('name', 'like', 'Manufacture')])
    mto_route_id = mto_route_id and mto_route_id[0] or False

    supply_method_legacy = openupgrade.get_legacy_name('supply_method')
    if mto_route_id:
        product_ids = []
        cr.execute("SELECT id FROM product_template WHERE %s = %%s" % supply_method_legacy, ('produce',))
        for res in cr.fetchall():
            product_ids.append(res[0])

        template_obj.write(cr, uid, product_ids, {'route_ids': [(4, mto_route_id)]})

@openupgrade.migrate()
def migrate(cr, version):
    pool = pooler.get_pool(cr.dbname)
    move_fields(cr, pool)
    uid = SUPERUSER_ID
    migrate_bom_lines(cr, pool, uid)
    migrate_bom_property_rel(cr, pool, uid)
    fix_domains(cr, pool, uid)
    update_stock_moves(cr, pool, uid)
    update_stock_picking_name(cr, pool, uid)
    migrate_product_supply_method(cr, pool, uid)
    openupgrade.rename_columns(cr, column_renames)
<|endoftext|>


<commit_before>#!/usr/bin/env python

# Licensed to the StackStorm, Inc ('StackStorm') under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from pyVmomi import vim

from vmwarelib import inventory
from vmwarelib import checkinputs
from vmwarelib.actions import BaseAction

class VMCreateBareBones(BaseAction):
    def run(self, vm_name, cluster, datastore_cluster,
            datastore, resourcepool, cpu_size, ram_size,
            guestos, version, description):
        # Setup Identifiers for objects
        si = self.si
        si_content = si.RetrieveContent()
        #checkinputs.vm_storage(datastore_cluster, datastore)
        checkinputs.one_of_two_strings(datastore_cluster,
                                       datastore,
                                       "Datastore Cluster or Datastore")

        data_center = self.si_content.rootFolder.childEntity[0]
        cluster = inventory.get_cluster(self.si_content, name=cluster)
        data_store_cluster = inventory.get_datastore_cluster(
            self.si_content,
            name=datastore_cluster)
        #data_store = inventory.get_datastore(self.si_content, name=datastore)
        target_folder = data_center.vmFolder

        # If No Resource Pool issued the Default one for
        # the Cluster will be selected.
        if resourcepool:
            resource_pool = inventory.get_resource_pool(self.si_content,
                                                        name=resourcepool)
        else:
            resource_pool = cluster.resourcePool

        # Config created that is required for DS selection
        # Config is BareBones, CPU RAM no more.
        config = vim.vm.ConfigSpec(name=vm_name,
                                   memoryMB=(ram_size * 1024),
                                   numCPUs=cpu_size,
                                   guestId=guestos,
                                   version=version,
                                   cpuHotAddEnabled=True,
                                   memoryHotAddEnabled=True,
                                   annotation=description)

        # if Datastore cluster is provided it will find the
        # recommended Datastore to store VM files
        if datastore_cluster:
            podsel = vim.storageDrs.PodSelectionSpec()
            podsel.storagePod = data_store_cluster

            storage_spec = vim.storageDrs.StoragePlacementSpec(
                type='create',
                configSpec=config,
                resourcePool=resource_pool,
                podSelectionSpec=podsel,
                folder=target_folder)

            # Create Instance of Storage Resource Manager -
            # This is used to identify a Recommended Datastore from a Cluster
            srm = si_content.storageResourceManager
            results = srm.RecommendDatastores(storageSpec=storage_spec)
            rec_ds = results.recommendations[0].action[0]\
                .relocateSpec.datastore
            datastore_path = '[' + rec_ds.name + '] ' + vm_name
        else:
            # No Datastore Cluster has been offered so using the D
            if datastore:
                #data_store = inventory.get_datastore(self.si_content,
                                                     #name=datastore)
                datastore_path = '[' + datastore + '] ' + vm_name
            else:
                raise Exception('Error No Storage Data Provided')

        #Now Datastore is known the remaining
        #of VM Config can be setup and added
        vmx_file = vim.vm.FileInfo(logDirectory=None,
                                   snapshotDirectory=None,
                                   suspendDirectory=None,
                                   vmPathName=datastore_path)
        config.files = vmx_file

        #Create task to Build actual Machine
        task = target_folder.CreateVM_Task(config=config, pool=resource_pool)
        self._wait_for_task(task)
        if task.info.state != vim.TaskInfo.State.success:
            raise Exception(task.info.error.msg)

        return {'vm_id': task.info.result._moId}
<commit_msg>update comment lines<commit_after>#!/usr/bin/env python

# Licensed to the StackStorm, Inc ('StackStorm') under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from pyVmomi import vim

from vmwarelib import inventory
from vmwarelib import checkinputs
from vmwarelib.actions import BaseAction

class VMCreateBareBones(BaseAction):
    def run(self, vm_name, cluster, datastore_cluster,
            datastore, resourcepool, cpu_size, ram_size,
            guestos, version, description):
        # Setup Identifiers for objects
        si = self.si
        si_content = si.RetrieveContent()
        # checkinputs.vm_storage(datastore_cluster, datastore)
        checkinputs.one_of_two_strings(datastore_cluster,
                                       datastore,
                                       "Datastore Cluster or Datastore")

        data_center = self.si_content.rootFolder.childEntity[0]
        cluster = inventory.get_cluster(self.si_content, name=cluster)
        data_store_cluster = inventory.get_datastore_cluster(
            self.si_content,
            name=datastore_cluster)
        # data_store = inventory.get_datastore(self.si_content, name=datastore)
        target_folder = data_center.vmFolder

        # If No Resource Pool issued the Default one for
        # the Cluster will be selected.
        if resourcepool:
            resource_pool = inventory.get_resource_pool(self.si_content,
                                                        name=resourcepool)
        else:
            resource_pool = cluster.resourcePool

        # Config created that is required for DS selection
        # Config is BareBones, CPU RAM no more.
        config = vim.vm.ConfigSpec(name=vm_name,
                                   memoryMB=(ram_size * 1024),
                                   numCPUs=cpu_size,
                                   guestId=guestos,
                                   version=version,
                                   cpuHotAddEnabled=True,
                                   memoryHotAddEnabled=True,
                                   annotation=description)

        # if Datastore cluster is provided it will find the
        # recommended Datastore to store VM files
        if datastore_cluster:
            podsel = vim.storageDrs.PodSelectionSpec()
            podsel.storagePod = data_store_cluster

            storage_spec = vim.storageDrs.StoragePlacementSpec(
                type='create',
                configSpec=config,
                resourcePool=resource_pool,
                podSelectionSpec=podsel,
                folder=target_folder)

            # Create Instance of Storage Resource Manager -
            # This is used to identify a Recommended Datastore from a Cluster
            srm = si_content.storageResourceManager
            results = srm.RecommendDatastores(storageSpec=storage_spec)
            rec_ds = results.recommendations[0].action[0]\
                .relocateSpec.datastore
            datastore_path = '[' + rec_ds.name + '] ' + vm_name
        else:
            # No Datastore Cluster has been offered so using the D
            if datastore:
                #data_store = inventory.get_datastore(self.si_content,
                                                     #name=datastore)
                datastore_path = '[' + datastore + '] ' + vm_name
            else:
                raise Exception('Error No Storage Data Provided')

        # Now Datastore is known the remaining
        # of VM Config can be setup and added
        vmx_file = vim.vm.FileInfo(logDirectory=None,
                                   snapshotDirectory=None,
                                   suspendDirectory=None,
                                   vmPathName=datastore_path)
        config.files = vmx_file

        # Create task to Build actual Machine
        task = target_folder.CreateVM_Task(config=config, pool=resource_pool)
        self._wait_for_task(task)
        if task.info.state != vim.TaskInfo.State.success:
            raise Exception(task.info.error.msg)

        return {'vm_id': task.info.result._moId}
<|endoftext|>