<commit_before>from mdtraj.core.trajectory import Trajectory
__author__ = 'noe'

import mdtraj
import numpy as np

class FeatureReader:

    """
    Reads features from MD data
    """

    def __init__(self, trajectories, topologyfile, featurizer):
        """
        Constructs a feature reader

        :param trajectories:
            list of trajectory files

        :param structurefile:
            structure file (e.g. pdb)

        """
        # files
        self.trajfiles = trajectories
        self.topfile = topologyfile
        self.featurizer = featurizer

        # lengths
        self.lengths = []
        self.totlength = 0

        # iteration
        self.mditer = None
        # current lag time
        self.curr_lag = 0
        # time lagged iterator
        self.mditer2 = None

        # cache size
        self.chunksize = 10000
        self.in_memory = False
        self.Y = None
        # basic statistics
        for traj in trajectories:
            sum_frames = sum(t.n_frames for t in
                             mdtraj.iterload(traj, top=self.topfile,
                                             chunk=self.chunksize))
            self.lengths.append(sum_frames)

        self.totlength = np.sum(self.lengths)

    def describe(self):
        return "Feature reader, features = ", self.featurizer.describe()

    def operate_in_memory(self):
        """
        If called, the output will be stored in memory
        :return:
        """
        self.in_memory = True
        # output data
        self.Y = [np.zeros((self.trajectory_length(itraj), self.dimension()))
                  for itraj in range(0, self.number_of_trajectories())]

    def number_of_trajectories(self):
        """
        Returns the number of trajectories

        :return:
            number of trajectories
        """
        return len(self.trajfiles)

    def trajectory_length(self, itraj):
        """
        Returns the length of trajectory

        :param itraj:
            trajectory index

        :return:
            length of trajectory
        """
        return self.lengths[itraj]

    def trajectory_lengths(self):
        return self.lengths

    def n_frames_total(self):
        return self.totlength

    def dimension(self):
        """
        Returns the number of output dimensions

        :return:
        """
        return self.featurizer.dimension()

    def get_memory_per_frame(self):
        """
        Returns the memory requirements per frame, in bytes

        :return:
        """
        return 4 * self.dimension()

    def get_constant_memory(self):
        """
        Returns the constant memory requirements, in bytes

        :return:
        """
        return 0

    def map_to_memory(self):
        self.reset()
        # iterate over trajectories
        last_chunk = False
        itraj = 0
        while not last_chunk:
            last_chunk_in_traj = False
            t = 0
            while not last_chunk_in_traj:
                y = self.next_chunk()
                L = np.shape(y)[0]
                # last chunk in traj?
                last_chunk_in_traj = (t + L >= self.trajectory_length(itraj))
                # last chunk?
                last_chunk = (
                    last_chunk_in_traj and itraj >= self.number_of_trajectories() - 1)
                # write
                self.Y[itraj][t:t + L] = y
                # increment time
                t += L
            # increment trajectory
            itraj += 1

    def _open_time_lagged(self):
        print "reopening mditer2 with chunk size %s " % self.chunksize
        self.mditer2 = mdtraj.iterload(self.trajfiles[0],
                                       chunk=self.chunksize, top=self.topfile)

    def reset(self):
        """
        resets the chunk reader
        :return:
        """
        self.curr_itraj = 0
        if len(self.trajfiles) >= 1:
            self.mditer = mdtraj.iterload(self.trajfiles[0],
                                          chunk=self.chunksize, top=self.topfile)

            if self.curr_lag > 0:
                self._open_time_lagged()

    def next_chunk(self, lag=0):
        """
        gets the next chunk. If lag > 0, we open another iterator with same chunk
        size and advance it by one, as soon as this method is called with a lag > 0.

        :return: a feature mapped vector X, or X,Y if lag > 0
        """
        chunk = self.mditer.next()

        if lag > 0:
            advanced_once = True
            if self.curr_lag == 0:
                # lag time changed
                self.curr_lag = lag
                self._open_time_lagged()
                # advance second iterator by one chunk
                try:
                    self.mditer2.next()
                except StopIteration:
                    advanced_once = False
            try:
                assert self.mditer2
                if advanced_once:  # we were able to increment mditer2 once
                    adv_chunk = self.mditer2.next()
            except StopIteration:
                # no more data available in mditer2, so we have to take data from
                # last_chunk and padd it with zeros!
                lagged_xyz = np.zeros_like(chunk.xyz)
                lagged_xyz[0:-lag] = chunk.xyz[lag:]
            else:
                # build time lagged Trajectory from current and advanced chunk
                lagged_xyz = np.empty_like(chunk.xyz)
                lagged_xyz[:-lag] = chunk.xyz[lag:]
                lagged_xyz[-lag:] = adv_chunk.xyz[0:lag]
            chunk_lagged = Trajectory(lagged_xyz, chunk.topology)

        if np.max(chunk.time) >= self.trajectory_length(self.curr_itraj) - 1:
            if self.curr_itraj < len(self.trajfiles) - 1:
                print "max chunk time reached for this traj, opening next"
                self.mditer.close()
                self.curr_itraj += 1
                self.mditer = mdtraj.iterload(
                    self.trajfiles[self.curr_itraj], chunk=self.chunksize, top=self.topfile)
                # we open self.mditer2 only if requested due lag parameter!

        # map data
        if lag == 0:
            return self.featurizer.map(chunk)
        else:
            X = self.featurizer.map(chunk)
            Y = self.featurizer.map(chunk_lagged)
            return X, Y
<commit_msg>readd functions<commit_after>from mdtraj.core.trajectory import Trajectory
__author__ = 'noe'

import mdtraj
import numpy as np

class FeatureReader:

    """
    Reads features from MD data
    """

    def __init__(self, trajectories, topologyfile, featurizer):
        """
        Constructs a feature reader

        :param trajectories:
            list of trajectory files

        :param structurefile:
            structure file (e.g. pdb)

        """
        # files
        self.trajfiles = trajectories
        self.topfile = topologyfile
        self.featurizer = featurizer

        # lengths
        self.lengths = []
        self.totlength = 0

        # iteration
        self.mditer = None
        # current lag time
        self.curr_lag = 0
        # time lagged iterator
        self.mditer2 = None

        # cache size
        self.chunksize = 10000
        self.in_memory = False
        self.Y = None
        # basic statistics
        for traj in trajectories:
            sum_frames = sum(t.n_frames for t in
                             mdtraj.iterload(traj, top=self.topfile,
                                             chunk=self.chunksize))
            self.lengths.append(sum_frames)

        self.totlength = np.sum(self.lengths)

    def describe(self):
        return "Feature reader, features = ", self.featurizer.describe()

    def set_chunksize(self, size):
        self.chunksize = size

    def operate_in_memory(self):
        """
        If called, the output will be stored in memory
        :return:
        """
        self.in_memory = True
        # output data
        self.Y = [np.zeros((self.trajectory_length(itraj), self.dimension()))
                  for itraj in range(0, self.number_of_trajectories())]

    def parametrize(self):
        if self.in_memory:
            self.map_to_memory()

    def number_of_trajectories(self):
        """
        Returns the number of trajectories

        :return:
            number of trajectories
        """
        return len(self.trajfiles)

    def trajectory_length(self, itraj):
        """
        Returns the length of trajectory

        :param itraj:
            trajectory index

        :return:
            length of trajectory
        """
        return self.lengths[itraj]

    def trajectory_lengths(self):
        return self.lengths

    def n_frames_total(self):
        return self.totlength

    def dimension(self):
        """
        Returns the number of output dimensions

        :return:
        """
        return self.featurizer.dimension()

    def get_memory_per_frame(self):
        """
        Returns the memory requirements per frame, in bytes

        :return:
        """
        return 4 * self.dimension()

    def get_constant_memory(self):
        """
        Returns the constant memory requirements, in bytes

        :return:
        """
        return 0

    def map_to_memory(self):
        self.reset()
        # iterate over trajectories
        last_chunk = False
        itraj = 0
        while not last_chunk:
            last_chunk_in_traj = False
            t = 0
            while not last_chunk_in_traj:
                y = self.next_chunk()
                L = np.shape(y)[0]
                # last chunk in traj?
                last_chunk_in_traj = (t + L >= self.trajectory_length(itraj))
                # last chunk?
                last_chunk = (
                    last_chunk_in_traj and itraj >= self.number_of_trajectories() - 1)
                # write
                self.Y[itraj][t:t + L] = y
                # increment time
                t += L
            # increment trajectory
            itraj += 1

    def _open_time_lagged(self):
        print "reopening mditer2 with chunk size %s " % self.chunksize
        self.mditer2 = mdtraj.iterload(self.trajfiles[0],
                                       chunk=self.chunksize, top=self.topfile)

    def reset(self):
        """
        resets the chunk reader
        :return:
        """
        self.curr_itraj = 0
        if len(self.trajfiles) >= 1:
            self.mditer = mdtraj.iterload(self.trajfiles[0],
                                          chunk=self.chunksize, top=self.topfile)

            if self.curr_lag > 0:
                self._open_time_lagged()

    def next_chunk(self, lag=0):
        """
        gets the next chunk. If lag > 0, we open another iterator with same chunk
        size and advance it by one, as soon as this method is called with a lag > 0.

        :return: a feature mapped vector X, or X,Y if lag > 0
        """
        chunk = self.mditer.next()

        if lag > 0:
            advanced_once = True
            if self.curr_lag == 0:
                # lag time changed
                self.curr_lag = lag
                self._open_time_lagged()
                # advance second iterator by one chunk
                try:
                    self.mditer2.next()
                except StopIteration:
                    advanced_once = False
            try:
                assert self.mditer2
                if advanced_once:  # we were able to increment mditer2 once
                    adv_chunk = self.mditer2.next()
            except StopIteration:
                # no more data available in mditer2, so we have to take data from
                # last_chunk and padd it with zeros!
                lagged_xyz = np.zeros_like(chunk.xyz)
                lagged_xyz[0:-lag] = chunk.xyz[lag:]
            else:
                # build time lagged Trajectory from current and advanced chunk
                lagged_xyz = np.empty_like(chunk.xyz)
                lagged_xyz[:-lag] = chunk.xyz[lag:]
                lagged_xyz[-lag:] = adv_chunk.xyz[0:lag]
            chunk_lagged = Trajectory(lagged_xyz, chunk.topology)

        if np.max(chunk.time) >= self.trajectory_length(self.curr_itraj) - 1:
            if self.curr_itraj < len(self.trajfiles) - 1:
                print "max chunk time reached for this traj, opening next"
                self.mditer.close()
                self.curr_itraj += 1
                self.mditer = mdtraj.iterload(
                    self.trajfiles[self.curr_itraj], chunk=self.chunksize, top=self.topfile)
                # we open self.mditer2 only if requested due lag parameter!

        # map data
        if lag == 0:
            return self.featurizer.map(chunk)
        else:
            X = self.featurizer.map(chunk)
            Y = self.featurizer.map(chunk_lagged)
            return X, Y
<|endoftext|>


<commit_before># This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""A class implementing a (piecewise-) linear function on qubit amplitudes."""

from typing import Optional, List, Union, Tuple
import numpy as np
from qiskit.circuit import QuantumCircuit

from .piecewise_linear_pauli_rotations import PiecewiseLinearPauliRotations

class LinearAmplitudeFunction(QuantumCircuit):
    r"""A circuit implementing a (piecewise) linear function on qubit amplitudes.

    An amplitude function :math:`F` of a function :math:`f` is a mapping

    .. math::

        F|x\rangle|0\rangle = \sqrt{1 - \hat{f}(x)} |x\rangle|0\rangle + \sqrt{\hat{f}(x)}
            |x\rangle|1\rangle.

    for a function :math:`\hat{f}: \{0, ..., 2^n - 1} \rightarrow [0, 1]`, where :math:`|x\rangle`
    is a :math:`n` qubit state.

    This circuit implements :math:`F` for piecewise linear functions :math:`\hat{f}`.
    In this case, the mapping :math:`F` can be approximately implemented using a Taylor expansion
    and linearly controlled Pauli-Y rotations, see [1, 2] for more detail. This approximation
    uses a ``rescaling_factor`` to determine the accuracy of the Taylor expansion.

    In general, the function of interest :math:`f` is defined from some interval :math:`[a,b]`,
    the ``domain`` to :math:`[c,d]`, the ``image``, instead of :math`\{1, ..., N}` to
    :math:`[0, 1]`. Using an affine transformation we can rescale :math:`f` to :math:`\hat{f}`:

    .. math::

        \hat{f(x)} = \frac{f(\phi(x)) - c}{d - c}

    with

    .. math::

        \phi(x) = a + \frac{b - a}{2^n - 1} x.

    If :math:`f` is a piecewise linear function on :math:`m` intervals
    :math:`[p_{i-1}, p_i], i \in \{1, ..., m\}` with slopes :math:`\alpha_i` and
    offsets `\beta_i` it can be written as

    .. math::

        f(x) = \sum_{i=1}^m 1_{[p_{i-1}, p_i}(x) (\alpha_i x + \beta_i)

    where :math:`1_[a, b]` is an indication function that is 1 if the argument is in the interval
    :math:`[a, b]` and otherwise 0. The breakpoints :math:`p_i` can be specified by the
    ``breakpoints`` argument.

    Examples:

    References:

        [1]: Woerner, S., & Egger, D. J. (2018).
             Quantum Risk Analysis.
             `arXiv:1806.06893 <http://arxiv.org/abs/1806.06893>`_

        [2]: Gacon, J., Zoufal, C., & Woerner, S. (2020).
             Quantum-Enhanced Simulation-Based Optimization.
             `arXiv:2005.10780 <http://arxiv.org/abs/2005.10780>`_
    """

    def __init__(
        self,
        num_state_qubits: int,
        slope: Union[float, List[float]],
        offset: Union[float, List[float]],
        domain: Tuple[float, float],
        image: Tuple[float, float],
        rescaling_factor: float = 1,
        breakpoints: Optional[List[float]] = None,
        name: str = "F",
    ) -> None:
        r"""
        Args:
            num_state_qubits: The number of qubits used to encode the variable :math:`x`.
            slope: The slope of the linear function. Can be a list of slopes if it is a piecewise
                linear function.
            offset: The offset of the linear function. Can be a list of offsets if it is a piecewise
                linear function.
            domain: The domain of the function as tuple :math:`(x_\min{}, x_\max{})`.
            image: The image of the function as tuple :math:`(f_\min{}, f_\max{})`.
            rescaling_factor: The rescaling factor to adjust the accuracy in the Taylor
                approximation.
            breakpoints: The breakpoints if the function is piecewise linear. If None, the function
                is not piecewise.
            name: Name of the circuit.
        """
        if not hasattr(slope, "__len__"):
            slope = [slope]
        if not hasattr(offset, "__len__"):
            offset = [offset]

        # ensure that the breakpoints include the first point of the domain
        if breakpoints is None:
            breakpoints = [domain[0]]
        else:
            if not np.isclose(breakpoints[0], domain[0]):
                breakpoints = [domain[0]] + breakpoints

        _check_sizes_match(slope, offset, breakpoints)
        _check_sorted_and_in_range(breakpoints, domain)

        self._domain = domain
        self._image = image
        self._rescaling_factor = rescaling_factor

        # do rescalings
        a, b = domain
        c, d = image

        mapped_breakpoints = []
        mapped_slope = []
        mapped_offset = []
        for i, point in enumerate(breakpoints):
            mapped_breakpoint = (point - a) / (b - a) * (2 ** num_state_qubits - 1)
            mapped_breakpoints += [mapped_breakpoint]

            # factor (upper - lower) / (2^n - 1) is for the scaling of x to [l,u]
            # note that the +l for mapping to [l,u] is already included in
            # the offsets given as parameters
            mapped_slope += [slope[i] * (b - a) / (2 ** num_state_qubits - 1)]
            mapped_offset += [offset[i]]

        # approximate linear behavior by scaling and contracting around pi/4
        slope_angles = np.zeros(len(breakpoints))
        offset_angles = np.pi / 4 * (1 - rescaling_factor) * np.ones(len(breakpoints))
        for i in range(len(breakpoints)):
            slope_angles[i] = np.pi * rescaling_factor * mapped_slope[i] / 2 / (d - c)
            offset_angles[i] += np.pi * rescaling_factor * (mapped_offset[i] - c) / 2 / (d - c)

        # use PWLPauliRotations to implement the function
        pwl_pauli_rotation = PiecewiseLinearPauliRotations(
            num_state_qubits, mapped_breakpoints, 2 * slope_angles, 2 * offset_angles
        )

        super().__init__(*pwl_pauli_rotation.qregs, name=name)
        self._data = pwl_pauli_rotation.data.copy()
        # self.compose(pwl_pauli_rotation, inplace=True)

    def post_processing(self, scaled_value: float) -> float:
        r"""Map the function value of the approximated :math:`\hat{f}` to :math:`f`.

        Args:
            scaled_value: A function value from the Taylor expansion of :math:`\hat{f}(x)`.

        Returns:
            The ``scaled_value`` mapped back to the domain of :math:`f`, by first inverting
            the transformation used for the Taylor approximation and then mapping back from
            :math:`[0, 1]` to the original domain.
        """
        # revert the mapping applied in the Taylor approximation
        value = scaled_value - 1 / 2 + np.pi / 4 * self._rescaling_factor
        value *= 2 / np.pi / self._rescaling_factor

        # map the value from [0, 1] back to the original domain
        value *= self._image[1] - self._image[0]
        value += self._image[0]

        return value

def _check_sorted_and_in_range(breakpoints, domain):
    if breakpoints is None:
        return

    # check if sorted
    if not np.all(np.diff(breakpoints) > 0):
        raise ValueError("Breakpoints must be unique and sorted.")

    if breakpoints[0] < domain[0] or breakpoints[-1] > domain[1]:
        raise ValueError("Breakpoints must be included in domain.")

def _check_sizes_match(slope, offset, breakpoints):
    size = len(slope)
    if len(offset) != size:
        raise ValueError("Size mismatch of slope ({}) and offset ({}).".format(size, len(offset)))
    if breakpoints is not None:
        if len(breakpoints) != size:
            raise ValueError(
                "Size mismatch of slope ({}) and breakpoints ({}).".format(size, len(breakpoints))
            )
<commit_msg>Fixed typo in LinearAmplitudeFunction docstring (#6491)<commit_after># This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""A class implementing a (piecewise-) linear function on qubit amplitudes."""

from typing import Optional, List, Union, Tuple
import numpy as np
from qiskit.circuit import QuantumCircuit

from .piecewise_linear_pauli_rotations import PiecewiseLinearPauliRotations

class LinearAmplitudeFunction(QuantumCircuit):
    r"""A circuit implementing a (piecewise) linear function on qubit amplitudes.

    An amplitude function :math:`F` of a function :math:`f` is a mapping

    .. math::

        F|x\rangle|0\rangle = \sqrt{1 - \hat{f}(x)} |x\rangle|0\rangle + \sqrt{\hat{f}(x)}
            |x\rangle|1\rangle.

    for a function :math:`\hat{f}: \{ 0, ..., 2^n - 1 \} \rightarrow [0, 1]`, where
    :math:`|x\rangle` is a :math:`n` qubit state.

    This circuit implements :math:`F` for piecewise linear functions :math:`\hat{f}`.
    In this case, the mapping :math:`F` can be approximately implemented using a Taylor expansion
    and linearly controlled Pauli-Y rotations, see [1, 2] for more detail. This approximation
    uses a ``rescaling_factor`` to determine the accuracy of the Taylor expansion.

    In general, the function of interest :math:`f` is defined from some interval :math:`[a,b]`,
    the ``domain`` to :math:`[c,d]`, the ``image``, instead of :math:`\{ 1, ..., N \}` to
    :math:`[0, 1]`. Using an affine transformation we can rescale :math:`f` to :math:`\hat{f}`:

    .. math::

        \hat{f}(x) = \frac{f(\phi(x)) - c}{d - c}

    with

    .. math::

        \phi(x) = a + \frac{b - a}{2^n - 1} x.

    If :math:`f` is a piecewise linear function on :math:`m` intervals
    :math:`[p_{i-1}, p_i], i \in \{1, ..., m\}` with slopes :math:`\alpha_i` and
    offsets :math:`\beta_i` it can be written as

    .. math::

        f(x) = \sum_{i=1}^m 1_{[p_{i-1}, p_i]}(x) (\alpha_i x + \beta_i)

    where :math:`1_{[a, b]}` is an indication function that is 1 if the argument is in the interval
    :math:`[a, b]` and otherwise 0. The breakpoints :math:`p_i` can be specified by the
    ``breakpoints`` argument.

    Examples:

    References:

        [1]: Woerner, S., & Egger, D. J. (2018).
             Quantum Risk Analysis.
             `arXiv:1806.06893 <http://arxiv.org/abs/1806.06893>`_

        [2]: Gacon, J., Zoufal, C., & Woerner, S. (2020).
             Quantum-Enhanced Simulation-Based Optimization.
             `arXiv:2005.10780 <http://arxiv.org/abs/2005.10780>`_
    """

    def __init__(
        self,
        num_state_qubits: int,
        slope: Union[float, List[float]],
        offset: Union[float, List[float]],
        domain: Tuple[float, float],
        image: Tuple[float, float],
        rescaling_factor: float = 1,
        breakpoints: Optional[List[float]] = None,
        name: str = "F",
    ) -> None:
        r"""
        Args:
            num_state_qubits: The number of qubits used to encode the variable :math:`x`.
            slope: The slope of the linear function. Can be a list of slopes if it is a piecewise
                linear function.
            offset: The offset of the linear function. Can be a list of offsets if it is a piecewise
                linear function.
            domain: The domain of the function as tuple :math:`(x_\min{}, x_\max{})`.
            image: The image of the function as tuple :math:`(f_\min{}, f_\max{})`.
            rescaling_factor: The rescaling factor to adjust the accuracy in the Taylor
                approximation.
            breakpoints: The breakpoints if the function is piecewise linear. If None, the function
                is not piecewise.
            name: Name of the circuit.
        """
        if not hasattr(slope, "__len__"):
            slope = [slope]
        if not hasattr(offset, "__len__"):
            offset = [offset]

        # ensure that the breakpoints include the first point of the domain
        if breakpoints is None:
            breakpoints = [domain[0]]
        else:
            if not np.isclose(breakpoints[0], domain[0]):
                breakpoints = [domain[0]] + breakpoints

        _check_sizes_match(slope, offset, breakpoints)
        _check_sorted_and_in_range(breakpoints, domain)

        self._domain = domain
        self._image = image
        self._rescaling_factor = rescaling_factor

        # do rescalings
        a, b = domain
        c, d = image

        mapped_breakpoints = []
        mapped_slope = []
        mapped_offset = []
        for i, point in enumerate(breakpoints):
            mapped_breakpoint = (point - a) / (b - a) * (2 ** num_state_qubits - 1)
            mapped_breakpoints += [mapped_breakpoint]

            # factor (upper - lower) / (2^n - 1) is for the scaling of x to [l,u]
            # note that the +l for mapping to [l,u] is already included in
            # the offsets given as parameters
            mapped_slope += [slope[i] * (b - a) / (2 ** num_state_qubits - 1)]
            mapped_offset += [offset[i]]

        # approximate linear behavior by scaling and contracting around pi/4
        slope_angles = np.zeros(len(breakpoints))
        offset_angles = np.pi / 4 * (1 - rescaling_factor) * np.ones(len(breakpoints))
        for i in range(len(breakpoints)):
            slope_angles[i] = np.pi * rescaling_factor * mapped_slope[i] / 2 / (d - c)
            offset_angles[i] += np.pi * rescaling_factor * (mapped_offset[i] - c) / 2 / (d - c)

        # use PWLPauliRotations to implement the function
        pwl_pauli_rotation = PiecewiseLinearPauliRotations(
            num_state_qubits, mapped_breakpoints, 2 * slope_angles, 2 * offset_angles
        )

        super().__init__(*pwl_pauli_rotation.qregs, name=name)
        self._data = pwl_pauli_rotation.data.copy()
        # self.compose(pwl_pauli_rotation, inplace=True)

    def post_processing(self, scaled_value: float) -> float:
        r"""Map the function value of the approximated :math:`\hat{f}` to :math:`f`.

        Args:
            scaled_value: A function value from the Taylor expansion of :math:`\hat{f}(x)`.

        Returns:
            The ``scaled_value`` mapped back to the domain of :math:`f`, by first inverting
            the transformation used for the Taylor approximation and then mapping back from
            :math:`[0, 1]` to the original domain.
        """
        # revert the mapping applied in the Taylor approximation
        value = scaled_value - 1 / 2 + np.pi / 4 * self._rescaling_factor
        value *= 2 / np.pi / self._rescaling_factor

        # map the value from [0, 1] back to the original domain
        value *= self._image[1] - self._image[0]
        value += self._image[0]

        return value

def _check_sorted_and_in_range(breakpoints, domain):
    if breakpoints is None:
        return

    # check if sorted
    if not np.all(np.diff(breakpoints) > 0):
        raise ValueError("Breakpoints must be unique and sorted.")

    if breakpoints[0] < domain[0] or breakpoints[-1] > domain[1]:
        raise ValueError("Breakpoints must be included in domain.")

def _check_sizes_match(slope, offset, breakpoints):
    size = len(slope)
    if len(offset) != size:
        raise ValueError("Size mismatch of slope ({}) and offset ({}).".format(size, len(offset)))
    if breakpoints is not None:
        if len(breakpoints) != size:
            raise ValueError(
                "Size mismatch of slope ({}) and breakpoints ({}).".format(size, len(breakpoints))
            )
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Created on Thu Dec 03 16:30:08 2015

@author: lvanhulle
"""

import Shape as s
import arc as a
import Point as p
from parameters import constants as c

def regularDogBone():
    dogBone = s.Shape(None)
    dogBone.addLinesFromCoordinateList([[82.5, 0], [82.5, 9.5], [49.642, 9.5]])
    arc = a.Arc(p.Point(49.642, 9.5), p.Point(28.5, 6.5), c.CW, p.Point(28.5, 82.5), 20)
    dogBone.addLineGroup(arc)
    dogBone.addLinesFromCoordinateList([[28.5, 6.5], [0, 6.5]])
    dogBone.addLineGroup(dogBone.mirror(c.Y))
    dogBone.addLineGroup(dogBone.mirror(c.X))
    return dogBone.translate(82.5, 9.5)

def wideDogBone():
    halfWidth = 5.0
    wideDogBone = s.Shape(None)
    wideDogBone.addLinesFromCoordinateList([[82.5, 0], [82.5, 9.5 + halfWidth], [49.642, 9.5 + halfWidth]])
    wideArc = a.Arc(p.Point(49.642, 9.5 + halfWidth), p.Point(28.5, 6.5 + halfWidth), c.CW, p.Point(28.5, 82.5 + halfWidth), 20)
    wideDogBone.addLineGroup(wideArc)
    wideDogBone.addLinesFromCoordinateList([[28.5, 6.5 + halfWidth], [0, 6.5 + halfWidth]])
    wideDogBone.addLineGroup(wideDogBone.mirror(c.Y))
    wideDogBone.addLineGroup(wideDogBone.mirror(c.X))
    return wideDogBone.translate(82.5, 9.5 + halfWidth)

def rightGrip():
    shape = s.Shape(None)
    shape.addLinesFromCoordinateList([[82.5, 0], [82.5, 9.5], [49.642, 9.5]])
    arc = a.Arc(p.Point(49.642, 9.5), p.Point(28.5, 6.5), c.CW, p.Point(28.5, 82.5), 20)
    shape.addLineGroup(arc)
    shape.addLinesFromCoordinateList([[28.5, 6.5], [28.5, 0]])
    shape.addLineGroup(shape.mirror(c.X))
    return shape.translate(82.5, 9.5)

def leftGrip():
    shape = rightGrip()
    shape = shape.translate(-82.5, -9.5)
    shape = shape.mirror(c.Y)
    return shape.translate(82.5, 9.5)

def grips():
    shape = leftGrip()
    shape.addLineGroup(rightGrip())
    return shape

def center():
    shape = s.Shape(None)
    shape.addLinesFromCoordinateList([[28.5, 6.5], [-28.5, 6.5], [-28.5, -6.5],
                                      [28.5, -6.5], [28.5, 6.5]])
    return shape.translate(82.5, 9.5)

def squareWithHole():
    shape = s.Shape(None)
    shape.addLinesFromCoordinateList([[0,0], [50,0], [50,50], [0,50], [0,0]])
    circle = a.Arc(p.Point(35,25), p.Point(35,25), c.CW, p.Point(25,25))
    shape.addLineGroup(circle)
    return shape<commit_msg>now you can call a full circle easier<commit_after># -*- coding: utf-8 -*-
"""
Created on Thu Dec 03 16:30:08 2015

@author: lvanhulle
"""

import Shape as s
import arc as a
import Point as p
from parameters import constants as c

def regularDogBone():
    dogBone = s.Shape(None)
    dogBone.addLinesFromCoordinateList([[82.5, 0], [82.5, 9.5], [49.642, 9.5]])
    arc = a.Arc(p.Point(49.642, 9.5), p.Point(28.5, 6.5), c.CW, p.Point(28.5, 82.5), 20)
    dogBone.addLineGroup(arc)
    dogBone.addLinesFromCoordinateList([[28.5, 6.5], [0, 6.5]])
    dogBone.addLineGroup(dogBone.mirror(c.Y))
    dogBone.addLineGroup(dogBone.mirror(c.X))
    return dogBone.translate(82.5, 9.5)

def wideDogBone():
    halfWidth = 5.0
    wideDogBone = s.Shape(None)
    wideDogBone.addLinesFromCoordinateList([[82.5, 0], [82.5, 9.5 + halfWidth], [49.642, 9.5 + halfWidth]])
    wideArc = a.Arc(p.Point(49.642, 9.5 + halfWidth), p.Point(28.5, 6.5 + halfWidth), c.CW, p.Point(28.5, 82.5 + halfWidth), 20)
    wideDogBone.addLineGroup(wideArc)
    wideDogBone.addLinesFromCoordinateList([[28.5, 6.5 + halfWidth], [0, 6.5 + halfWidth]])
    wideDogBone.addLineGroup(wideDogBone.mirror(c.Y))
    wideDogBone.addLineGroup(wideDogBone.mirror(c.X))
    return wideDogBone.translate(82.5, 9.5 + halfWidth)

def rightGrip():
    shape = s.Shape(None)
    shape.addLinesFromCoordinateList([[82.5, 0], [82.5, 9.5], [49.642, 9.5]])
    arc = a.Arc(p.Point(49.642, 9.5), p.Point(28.5, 6.5), c.CW, p.Point(28.5, 82.5), 20)
    shape.addLineGroup(arc)
    shape.addLinesFromCoordinateList([[28.5, 6.5], [28.5, 0]])
    shape.addLineGroup(shape.mirror(c.X))
    return shape.translate(82.5, 9.5)

def leftGrip():
    shape = rightGrip()
    shape = shape.translate(-82.5, -9.5)
    shape = shape.mirror(c.Y)
    return shape.translate(82.5, 9.5)

def grips():
    shape = leftGrip()
    shape.addLineGroup(rightGrip())
    return shape

def center():
    shape = s.Shape(None)
    shape.addLinesFromCoordinateList([[28.5, 6.5], [-28.5, 6.5], [-28.5, -6.5],
                                      [28.5, -6.5], [28.5, 6.5]])
    return shape.translate(82.5, 9.5)

def squareWithHole():
    shape = s.Shape(None)
    shape.addLinesFromCoordinateList([[0,0], [50,0], [50,50], [0,50], [0,0]])
    circle = a.Arc(p.Point(35,25), p.Point(35,25), c.CW, p.Point(25,25))
    shape.addLineGroup(circle)
    return shape

def circle(centerX, centerY, radius):
    startPoint = p.Point(centerX+radius, centerY)
    center = p.Point(centerX, centerY)
    return s.Shape(a.Arc(startPoint, startPoint, c.CW, center))<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

"""
dop.client
~~~~~~~~~~~~

This module implements the Digital Ocean API.

:license: MIT, see LICENSE for more details.

"""

import requests

API_HOST = 'api.digitalocean.com'
API_PORT = 80

class Droplet(object):
    def __init__(self, id, name, size_id, image_id, region_id, event_id,
        backups_active, status, ip_address):
        self.id = id
        self.name = name
        self.size_id = size_id
        self.image_id = image_id
        self.region_id = region_id
        self.event_id = event_id
        self.backups_active = backups_active
        self.status = status
        self.ip_address = ip_address

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        size_id = json.get('size_id', -1)
        image_id = json.get('image_id', -1)
        region_id = json.get('region_id', -1)
        event_id = json.get('event_id', -1)
        backups_active = json.get('backups_active', -1)
        status = json.get('status', '')
        ip_address = json.get('ip_address', -1)
        droplet = Droplet(id, name, size_id, image_id, region_id, event_id,
            backups_active, status, ip_address)
        return droplet

class Snapshot(object):
    def __init__(self, name):
        self.name = name

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        name = json.get('name', '')
        snapshot = Snapshot(name)
        return snapshot

class Region(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        region = Region(id, name)
        return region

class Image(object):
    def __init__(self, id, name, distribution):
        self.id = id
        self.name = name
        self.distribution = distribution

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        distribution = json.get('distribution', '')
        image = Image(id, name, distribution)
        return image

class Size(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        size = Size(id, name)
        return size

class SSHKey(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        ssh_key = SSHKey(id, name)
        return ssh_key

class Client(object):

    def __init__(self, client_id, api_key, host=API_HOST, port=API_PORT,
        secure=True):
        self.client_id = client_id
        self.api_key = api_key
        self.host = host
        self.port = port
        self.secure = secure

    def show_active_droplets(self):
        params = {}
        json = self.request('/droplets', method='GET', params=params)
        droplets_json = json.get('droplets', [])
        droplets = [Droplet.from_json(s) for s in droplets_json]
        return droplets

    def regions(self):
        params = {}
        json = self.request('/regions', method='GET', params=params)
        regions_json = json.get('regions', [])
        regions = [Region.from_json(r) for r in regions_json]
        return regions

    def images(self, show_all=True):
        params = {}
        if show_all:
            params['filter'] = 'global'
        else:
            params['filter'] = 'my_images'

        json = self.request('/images', method='GET', params=params)
        images_json = json.get('images', [])
        images = [Image.from_json(i) for i in images_json]
        return images

    def sizes(self):
        params = {}
        json = self.request('/sizes', method='GET', params=params)
        sizes_json = json.get('sizes', [])
        sizes = [Size.from_json(s) for s in sizes_json]
        return sizes

    def all_ssh_keys(self):
        params = {}
        json = self.request('/ssh_keys', method='GET', params=params)
        ssh_keys_json = json.get('ssh_keys', [])
        keys = [SSHKey.from_json(k) for k in ssh_keys_json]
        return keys

    def reboot_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/reboot' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    def show_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s' % (id), method='GET', params=params)
        droplet_json = json.get('droplet', None)
        droplet = Droplet.from_json(droplet_json)
        return droplet

    def create_droplet(self, name="", size_id=-1, image_id=-1, region_id=-1, ssh_key_ids = None, virtio=False):
        params = {
            'name': name,
            'size_id': size_id,
            'image_id': image_id,
            'region_id': region_id,
        }
        if ssh_key_ids:
            params['ssh_key_ids'] = ','.join(ssh_key_ids)
        if virtio:
            params['virtio'] = 1

        json = self.request('/droplets/new', method='GET', params=params)
        droplet_json = json.get('droplet', None)
        droplet = Droplet.from_json(droplet_json)
        return droplet

    def power_cycle_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/power_cycle' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    def power_off_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/power_off' % (id), method='GET',
            params=params)
        return json.get('event_id', None)

    def power_on_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/power_on' % (id), method='GET',
            params=params)
        return json.get('event_id', None)

    def resize_droplet(self, id, size_id):
        params = {
            'size_id': size_id,
        }
        json = self.request('/droplets/%s/resize' % (id), method='POST',
            params=params)
        return json

    def snapshot_droplet(self, id, name):
        params = {
            'name': name,
        }
        json = self.request('/droplets/%s/snapshot' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    def rebuild_droplet(self, id, image_id):
        params = {
            'image_id': image_id,
        }
        json = self.request('/droplets/%s/rebuild' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    def enable_backups_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/enable_backups' % (id),
            method='POST', params=params)
        return json.get('event_id', None)

    def disable_backups_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/disable_backups' % (id),
            method='POST', params=params)
        return json.get('event_id', None)

    def destroy_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/destroy' % (id), method='GET',
            params=params)
        return json.get('event_id', None)

    def show_image(self, image_id):
        params = {
            'image_id': image_id,
        }
        json = self.request('/images/%s' % (image_id), method='GET',
            params=params)
        image_json = json.get('image', None)
        image = Image.from_json(image_json)
        return image

    def shutdown_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/shutdown' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    # WEIRD BEHAVIOUR METHODS
    def reset_root_password(self, id):
        params = {}
        json = self.request('/droplets/%s/reset_root_password' % (id),
            method='GET', params=params)
        print json
        return json.get('event_id', None)

    def restore_droplet(self, id, image_id):
        params = {
            'image_id': image_id,
        }
        json = self.request('/droplets/%s/restore' % (id), method='GET',
            params=params)
        print json
        return json.get('event_id', None)

    def destroy_image(self, image_id):
        params = {}
        json = self.request('/images/%s/destroy' % (image_id), method='GET',
            params=params)
        print json
        event = json.get('event', None)
        return event

    def show_ssh_key(self, id):
        params = {}
        json = self.request('/ssh_keys/%s' % (id), method='GET', params=params)
        ssh_key_json = json.get('ssh_key', None)
        ssh_key = SSHKey.from_json(ssh_key_json)
        return ssh_key

    def destroy_ssh_key(self, id):
        params = {}
        json = self.request('/ssh_keys/%s/destroy' % (id), method='GET',
            params=params)
        ssh_key = SSHKey.from_json(json)
        return ssh_key

    # EXPERIMENTAL: ssh actions #
    def add_ssh_key(self, id):
        params = {}
        json = self.request('/ssh_key/%s/add' % (id), method='GET',
            params=params)
        print json
        ssh_key_json = json.get('ssh_key', None)
        ssh_key = SSHKey.from_json(ssh_key_json)
        return ssh_key

    def edit_ssh_key(self, id):
        params = {}
        json = self.request('/ssh_key/%s/edit' % (id), method='GET',
            params=params)
        ssh_key = SSHKey.from_json(json)
        return ssh_key

    def request(self, target, method='GET', params={}):
        assert method in ['GET', 'POST'], \
            "Only 'GET' 'POST' are allowed."

        headers = {
            'User-Agent': 'dop/client'
        }

        params['client_id'] = self.client_id
        params['api_key'] = self.api_key

        if method == 'POST':
            headers['Content-Type'] = "application/json"
            url = self.get_url(target)
            response = requests.post(url, headers=headers, params=params)
        else:
            url = self.get_url(target)
            response = requests.get(url, headers=headers, params=params)

        if response.status_code == 200:
            if response.json():
                json = response.json()
                error_msg = json.get('error_message', None)
                if error_msg:
                    raise DOPException(error_msg)
                else:
                    return json
            else:
                raise DOPException('Empty json!')
        else:
            error = 'Status code: %d' % (response.status_code)
            raise DOPException(error)
        return json

    def get_url(self, slug):
        port = "" if self.port == 80 else ":%d" % self.port
        protocol = "https://" if self.secure else "http://"
        base_full_url = "%s%s%s%s" % (protocol, self.host, port, slug)
        return base_full_url

class DOPException(Exception):
    pass
<commit_msg>add full response to error message<commit_after># -*- coding: utf-8 -*-

"""
dop.client
~~~~~~~~~~~~

This module implements the Digital Ocean API.

:license: MIT, see LICENSE for more details.

"""

import requests

API_HOST = 'api.digitalocean.com'
API_PORT = 80

class Droplet(object):
    def __init__(self, id, name, size_id, image_id, region_id, event_id,
        backups_active, status, ip_address):
        self.id = id
        self.name = name
        self.size_id = size_id
        self.image_id = image_id
        self.region_id = region_id
        self.event_id = event_id
        self.backups_active = backups_active
        self.status = status
        self.ip_address = ip_address

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        size_id = json.get('size_id', -1)
        image_id = json.get('image_id', -1)
        region_id = json.get('region_id', -1)
        event_id = json.get('event_id', -1)
        backups_active = json.get('backups_active', -1)
        status = json.get('status', '')
        ip_address = json.get('ip_address', -1)
        droplet = Droplet(id, name, size_id, image_id, region_id, event_id,
            backups_active, status, ip_address)
        return droplet

class Snapshot(object):
    def __init__(self, name):
        self.name = name

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        name = json.get('name', '')
        snapshot = Snapshot(name)
        return snapshot

class Region(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        region = Region(id, name)
        return region

class Image(object):
    def __init__(self, id, name, distribution):
        self.id = id
        self.name = name
        self.distribution = distribution

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        distribution = json.get('distribution', '')
        image = Image(id, name, distribution)
        return image

class Size(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        size = Size(id, name)
        return size

class SSHKey(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(json):
        id = json.get('id', -1)
        name = json.get('name', '')
        ssh_key = SSHKey(id, name)
        return ssh_key

class Client(object):

    def __init__(self, client_id, api_key, host=API_HOST, port=API_PORT,
        secure=True):
        self.client_id = client_id
        self.api_key = api_key
        self.host = host
        self.port = port
        self.secure = secure

    def show_active_droplets(self):
        params = {}
        json = self.request('/droplets', method='GET', params=params)
        droplets_json = json.get('droplets', [])
        droplets = [Droplet.from_json(s) for s in droplets_json]
        return droplets

    def regions(self):
        params = {}
        json = self.request('/regions', method='GET', params=params)
        regions_json = json.get('regions', [])
        regions = [Region.from_json(r) for r in regions_json]
        return regions

    def images(self, show_all=True):
        params = {}
        if show_all:
            params['filter'] = 'global'
        else:
            params['filter'] = 'my_images'

        json = self.request('/images', method='GET', params=params)
        images_json = json.get('images', [])
        images = [Image.from_json(i) for i in images_json]
        return images

    def sizes(self):
        params = {}
        json = self.request('/sizes', method='GET', params=params)
        sizes_json = json.get('sizes', [])
        sizes = [Size.from_json(s) for s in sizes_json]
        return sizes

    def all_ssh_keys(self):
        params = {}
        json = self.request('/ssh_keys', method='GET', params=params)
        ssh_keys_json = json.get('ssh_keys', [])
        keys = [SSHKey.from_json(k) for k in ssh_keys_json]
        return keys

    def reboot_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/reboot' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    def show_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s' % (id), method='GET', params=params)
        droplet_json = json.get('droplet', None)
        droplet = Droplet.from_json(droplet_json)
        return droplet

    def create_droplet(self, name="", size_id=-1, image_id=-1, region_id=-1, ssh_key_ids = None, virtio=False):
        params = {
            'name': name,
            'size_id': size_id,
            'image_id': image_id,
            'region_id': region_id,
        }
        if ssh_key_ids:
            params['ssh_key_ids'] = ','.join(ssh_key_ids)
        if virtio:
            params['virtio'] = 1

        json = self.request('/droplets/new', method='GET', params=params)
        droplet_json = json.get('droplet', None)
        droplet = Droplet.from_json(droplet_json)
        return droplet

    def power_cycle_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/power_cycle' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    def power_off_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/power_off' % (id), method='GET',
            params=params)
        return json.get('event_id', None)

    def power_on_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/power_on' % (id), method='GET',
            params=params)
        return json.get('event_id', None)

    def resize_droplet(self, id, size_id):
        params = {
            'size_id': size_id,
        }
        json = self.request('/droplets/%s/resize' % (id), method='POST',
            params=params)
        return json

    def snapshot_droplet(self, id, name):
        params = {
            'name': name,
        }
        json = self.request('/droplets/%s/snapshot' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    def rebuild_droplet(self, id, image_id):
        params = {
            'image_id': image_id,
        }
        json = self.request('/droplets/%s/rebuild' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    def enable_backups_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/enable_backups' % (id),
            method='POST', params=params)
        return json.get('event_id', None)

    def disable_backups_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/disable_backups' % (id),
            method='POST', params=params)
        return json.get('event_id', None)

    def destroy_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/destroy' % (id), method='GET',
            params=params)
        return json.get('event_id', None)

    def show_image(self, image_id):
        params = {
            'image_id': image_id,
        }
        json = self.request('/images/%s' % (image_id), method='GET',
            params=params)
        image_json = json.get('image', None)
        image = Image.from_json(image_json)
        return image

    def shutdown_droplet(self, id):
        params = {}
        json = self.request('/droplets/%s/shutdown' % (id), method='POST',
            params=params)
        return json.get('event_id', None)

    # WEIRD BEHAVIOUR METHODS
    def reset_root_password(self, id):
        params = {}
        json = self.request('/droplets/%s/reset_root_password' % (id),
            method='GET', params=params)
        print json
        return json.get('event_id', None)

    def restore_droplet(self, id, image_id):
        params = {
            'image_id': image_id,
        }
        json = self.request('/droplets/%s/restore' % (id), method='GET',
            params=params)
        print json
        return json.get('event_id', None)

    def destroy_image(self, image_id):
        params = {}
        json = self.request('/images/%s/destroy' % (image_id), method='GET',
            params=params)
        print json
        event = json.get('event', None)
        return event

    def show_ssh_key(self, id):
        params = {}
        json = self.request('/ssh_keys/%s' % (id), method='GET', params=params)
        ssh_key_json = json.get('ssh_key', None)
        ssh_key = SSHKey.from_json(ssh_key_json)
        return ssh_key

    def destroy_ssh_key(self, id):
        params = {}
        json = self.request('/ssh_keys/%s/destroy' % (id), method='GET',
            params=params)
        ssh_key = SSHKey.from_json(json)
        return ssh_key

    # EXPERIMENTAL: ssh actions #
    def add_ssh_key(self, id):
        params = {}
        json = self.request('/ssh_key/%s/add' % (id), method='GET',
            params=params)
        print json
        ssh_key_json = json.get('ssh_key', None)
        ssh_key = SSHKey.from_json(ssh_key_json)
        return ssh_key

    def edit_ssh_key(self, id):
        params = {}
        json = self.request('/ssh_key/%s/edit' % (id), method='GET',
            params=params)
        ssh_key = SSHKey.from_json(json)
        return ssh_key

    def request(self, target, method='GET', params={}):
        assert method in ['GET', 'POST'], \
            "Only 'GET' 'POST' are allowed."

        headers = {
            'User-Agent': 'dop/client'
        }

        params['client_id'] = self.client_id
        params['api_key'] = self.api_key

        if method == 'POST':
            headers['Content-Type'] = "application/json"
            url = self.get_url(target)
            response = requests.post(url, headers=headers, params=params)
        else:
            url = self.get_url(target)
            response = requests.get(url, headers=headers, params=params)

        if response.status_code == 200:
            if response.json():
                json = response.json()
                error_msg = json.get('error_message', None)
                if error_msg:
                    raise DOPException(error_msg)
                else:
                    return json
            else:
                raise DOPException('Empty json!')
        else:
            error = ('Status code: %d, full response: %s' %
                    (response.status_code, response.json()))
            raise DOPException(error)
        return json

    def get_url(self, slug):
        port = "" if self.port == 80 else ":%d" % self.port
        protocol = "https://" if self.secure else "http://"
        base_full_url = "%s%s%s%s" % (protocol, self.host, port, slug)
        return base_full_url

class DOPException(Exception):
    pass
<|endoftext|>


<commit_before>"""Get the status of an integration from the identity vault and auth0."""
import json
import logging
import uuid
from botocore.exceptions import ClientError
from cis_aws import connect
from cis_change_service import common
from cis_identity_vault.models import user
from cis_profile.profile import User
from cis_change_service.exceptions import IntegrationError
from cis_change_service.exceptions import VerificationError
from cis_profile.exceptions import PublisherVerificationFailure
from cis_profile.exceptions import SignatureVerificationFailure
from traceback import format_exc

logger = logging.getLogger(__name__)

class Vault(object):
    """Handles flushing profiles to Dynamo when running local or in stream bypass mode."""

    def __init__(self, sequence_number=None):
        self.connection_object = connect.AWS()
        self.identity_vault_client = None
        self.config = common.get_config()
        self.condition = "unknown"

        if sequence_number is not None:
            self.sequence_number = str(sequence_number)
        else:
            self.sequence_number = str(uuid.uuid4().int)

    def _connect(self):
        self.connection_object.session()
        self.identity_vault_client = self.connection_object.identity_vault_client()
        return self.identity_vault_client

    def _verify(self, profile_json, old_profile=None):
        cis_profile_object = User(user_structure_json=profile_json)
        user_id = cis_profile_object.as_dict()["user_id"].get("value", "")

        try:
            user = self._search_and_merge(cis_profile_object)

            if self.config("verify_signatures", namespace="cis") == "true":
                user.verify_all_signatures()
        except SignatureVerificationFailure as e:
            logger.error(
                "The profile failed to pass signature verification for user: {}".format(user_id),
                extra={"user_id": user_id, "profile": user.as_dict(), "reason": e, "trace": format_exc()},
            )

            raise VerificationError({"code": "invalid_signature", "description": "{}".format(e)}, 403)
        except PublisherVerificationFailure as e:
            logger.error(
                "The profile failed to pass publisher verification for user: {}".format(user_id),
                extra={"user_id": user_id, "profile": user.as_dict(), "reason": e, "trace": format_exc()},
            )
            raise VerificationError({"code": "invalid_publisher", "description": "{}".format(e)}, 403)
        except Exception as e:
            logger.error(
                "The profile produced an unknown error and is not trusted for user: {}".format(user_id),
                extra={"user_id": user_id, "profile": cis_profile_object.as_dict(), "reason": e, "trace": format_exc()},
            )
            logger.error("The error causing a trust problem is: {}".format(e))
            raise VerificationError({"code": "unknown_error", "description": "{}".format(e)}, 500)
        return user

    def _update_attr_owned_by_cis(self, profile_json):
        """Updates the attributes owned by cisv2.  Takes profiles profile_json
        and returns a profile dict with updated values and sigs."""

        user = profile_json
        user.update_timestamp("last_modified")
        user.last_modified.value = user._get_current_utc_time()
        user.sign_attribute("last_modified", "cis")

        # Currently we accept LDAP and Auth0 disabling a user
        # but since CIS is authoritative on this attribute, we rewrite it here
        # XXX THIS IS AN EXCEPTION AND SHOULD BE REMOVED WHEN ONLY HRIS CAN DISABLE USERS
        # A separate scope/endpoint should be made available to disable+delete users on demand, that isn't using their
        # publishers
        if user.active.signature.publisher.name in ["ldap", "access_provider", "hris"]:
            if self.config("verify_signatures", namespace="cis") == "true":
                user.verify_attribute_signature("active")
            user.sign_attribute("active", "cis")
        # XXX this should probably return a User object (and have a self.user in the class) instead
        return user.as_dict()

    def _search_and_merge(self, cis_profile_object):
        self._connect()

        user_id = cis_profile_object.user_id.value

        logger.info("Attempting to locate user: {}".format(user_id))
        vault = user.Profile(self.identity_vault_client.get("table"), self.identity_vault_client.get("client"))

        try:
            res = vault.find_by_id(user_id)
            logger.info("The result of the search contained: {}".format(len(res["Items"])))
        except Exception as e:
            logger.error("Problem finding user profile in identity vault due to: {}".format(e))
            res = {"Items": [0]}

        if len(res["Items"]) > 0:
            self.condition = "update"
            logger.info(
                "A record already exists in the identity vault for user: {}.".format(user_id),
                extra={"user_id": user_id},
            )

            old_user_profile = User(user_structure_json=json.loads(res["Items"][0]["profile"]))
            new_user_profile = copy.deepcopy(old_user_profile)
            new_user_profile.merge(cis_profile_object)
            if self.config("verify_publishers", namespace="cis") == "true":
                logger.info("Verifying publishers.")
                new_user_profile.verify_all_publishers(old_user_profile)
            return new_user_profile
        else:
            self.condition = "create"
            logger.info(
                "A record does not exist in the identity vault for user: {}.".format(user_id),
                extra={"user_id": user_id},
            )
            if self.config("verify_publishers", namespace="cis") == "true":
                logger.info("Verifying publishers.")
                cis_profile_object.verify_all_publishers()
            return cis_profile_object

    def put_profile(self, profile_json):
        """Write profile to the identity vault."""
        try:
            self._connect()

            # XXX Hey its not JSON anymore then.
            if isinstance(profile_json, str):
                profile_json = json.loads(profile_json)

            # Run some code that updates attrs and metadata for attributes cis is trusted to assert
            profile_json = self._verify(profile_json).as_dict()
            profile_json = self._update_attr_owned_by_cis(profile_json)

            if self.config("dynamodb_transactions", namespace="cis") == "true":
                vault = user.Profile(
                    self.identity_vault_client.get("table"), self.identity_vault_client.get("client"), transactions=True
                )
            else:
                vault = user.Profile(
                    self.identity_vault_client.get("table"),
                    self.identity_vault_client.get("client"),
                    transactions=False,
                )

            user_profile = dict(
                id=profile_json["user_id"]["value"],
                primary_email=profile_json["primary_email"]["value"],
                user_uuid=profile_json["uuid"]["value"],
                primary_username=profile_json["primary_username"]["value"],
                sequence_number=self.sequence_number,
                profile=json.dumps(profile_json),
            )

            res = vault.find_or_create(user_profile)
            logger.info(
                "The result of writing the profile to the identity vault was: {}".format(res),
                extra={"user_id": profile_json["user_id"]["value"], "profile": profile_json, "result": res},
            )

            return dict(sequence_number=self.sequence_number, status_code=200, condition=self.condition)
        except ClientError as e:
            logger.error(
                "An error occured writing this profile to dynamodb",
                extra={"profile": profile_json, "error": e, "trace": format_exc()},
            )
            logger.error("The error blocking write is: {}".format(e))
            raise IntegrationError({"code": "integration_exception", "description": "{}".format(e)}, 500)

    def put_profiles(self, profile_list):
        """Write profile to the identity vault."""
        try:
            self._connect()

            if self.config("dynamodb_transactions", namespace="cis") == "true":
                logger.debug("Attempting to put batch of profiles using transactions.")
                vault = user.Profile(
                    self.identity_vault_client.get("table"), self.identity_vault_client.get("client"), transactions=True
                )
            else:
                logger.info("Attempting to put batch of profiles without transactions.")
                vault = user.Profile(
                    self.identity_vault_client.get("table"),
                    self.identity_vault_client.get("client"),
                    transactions=False,
                )

            user_profiles = []

            for profile_json in profile_list:
                if isinstance(profile_json, str):
                    profile_json = json.loads(profile_json)

                # Run some code that updates attrs and metadata for attributes cis is trusted to assert
                profile_json = self._verify(profile_json)
                profile_json = self._update_attr_owned_by_cis(profile_json)
                if verified:
                    logger.debug("Profiles have been verified. Constructing dictionary for storage.")
                    user_profile = dict(
                        id=profile_json["user_id"]["value"],
                        primary_email=profile_json["primary_email"]["value"],
                        user_uuid=profile_json["uuid"]["value"],
                        primary_username=profile_json["primary_username"]["value"],
                        sequence_number=self.sequence_number,
                        profile=json.dumps(profile_json),
                    )
                    user_profiles.append(user_profile)

            logger.debug(
                "Attempting to send a list numbering: {} profiles as a transaction.".format(len(user_profiles))
            )
            result = vault.find_or_create_batch(user_profiles)
        except ClientError as e:
            logger.error(
                "An error occured writing these profiles to dynamodb",
                extra={"profiles": profile_list, "error": e, "trace": format_exc()},
            )
            raise IntegrationError({"code": "integration_exception", "description": "{}".format(e)}, 500)
        return {"creates": result[0], "updates": result[1]}

    def delete_profile(self, profile_json):
        condition = "delete"
        try:
            user_profile = dict(
                id=profile_json["user_id"]["value"],
                primary_email=profile_json["primary_email"]["value"],
                user_uuid=profile_json["uuid"]["value"],
                primary_username=profile_json["primary_username"]["value"],
                sequence_number=self.sequence_number,
                profile=json.dumps(profile_json),
            )

            if self.config("dynamodb_transactions", namespace="cis") == "true":
                logger.debug("Attempting to put batch of profiles using transactions.")
                vault = user.Profile(
                    self.identity_vault_client.get("table"), self.identity_vault_client.get("client"), transactions=True
                )
            else:
                logger.info("Attempting to put batch of profiles without transactions.")
                vault = user.Profile(
                    self.identity_vault_client.get("table"),
                    self.identity_vault_client.get("client"),
                    transactions=False,
                )

            vault.delete(user_profile)
        except ClientError as e:
            logger.error(
                "An error occured removing this profile from dynamodb",
                extra={"profile": profile_json, "error": e, "trace": format_exc()},
            )
            raise IntegrationError({"code": "integration_exception", "description": "{}".format(e)}, 500)
        return {
            "status": 200,
            "message": "user profile deleted for user: {}".format(profile_json["user_id"]["value"]),
            "condition": condition,
        }

    def _get_id(self, profile_json):
        if isinstance(profile_json, str):
            profile_json = json.loads(profile_json)
        return profile_json.get("user_id").get("value").lower()

    def _get_primary_email(self, profile_json):
        if isinstance(profile_json, str):
            profile_json = json.loads(profile_json)
        return profile_json.get("primary_email").get("value").lower()

class Status(object):
    """Does the right thing to query if the event was integrated and return the results."""

    def __init__(self, sequence_number):
        self.connection_object = connect.AWS()
        self.identity_vault_client = None
        self.sequence_number = sequence_number

    def _connect(self):
        self.connection_object.session()
        self.identity_vault_client = self.connection_object.identity_vault_client()
        return self.identity_vault_client

    def query(self):
        """Query the identity vault using the named Global Secondary Index for the sequence number."""
        # Vault returns a dictionary of dictionaries for the statuses of each check.
        self._connect()

        client = self.identity_vault_client.get("client")

        result = client.query(
            TableName=self.identity_vault_client.get("arn").split("/")[1],
            IndexName="{}-sequence_number".format(self.identity_vault_client.get("arn").split("/")[1]),
            Select="ALL_ATTRIBUTES",
            KeyConditionExpression="sequence_number = :sequence_number",
            ExpressionAttributeValues={":sequence_number": {"S": self.sequence_number}},
        )

        return result

    @property
    def all(self):
        """Run all checks and return the results for the given sequence number as a dict."""
        return {"identity_vault": self.check_identity_vault()}

    def check_identity_vault(self):
        """Check the sequence number of the last record put to the identity vault."""
        if self.identity_vault_client is None:
            self._connect()

        query_result = self.query()
        if len(query_result.get("Items")) == 1:
            return True
        else:
            return False
<commit_msg>refactor logic of put_profile(s) for clarity<commit_after>"""Get the status of an integration from the identity vault and auth0."""
import json
import logging
import uuid
import copy
import cis_profile
from botocore.exceptions import ClientError
from cis_aws import connect
from cis_change_service import common
from cis_identity_vault.models import user
from cis_profile.profile import User
from cis_change_service.exceptions import IntegrationError
from cis_change_service.exceptions import VerificationError
from traceback import format_exc

logger = logging.getLogger(__name__)

class Vault(object):
    """Handles flushing profiles to Dynamo when running local or in stream bypass mode."""

    def __init__(self, sequence_number=None):
        self.connection_object = connect.AWS()
        self.identity_vault_client = None
        self.config = common.get_config()
        self.condition = "unknown"

        if sequence_number is not None:
            self.sequence_number = str(sequence_number)
        else:
            self.sequence_number = str(uuid.uuid4().int)

    def _connect(self):
        """
        Connect to the vault
        """
        self.connection_object.session()
        self.identity_vault_client = self.connection_object.identity_vault_client()
        return self.identity_vault_client

    def _update_attr_owned_by_cis(self, user_id, user):
        """
        Updates the attributes owned by CIS itself. Updated attributes:
        - last_modified
        - active (XXX to be removed in the future, see comments below)

        @user_id str of the user's user_id
        @user a cis_profile.User object to update the attributes of

        Returns a cis_profile.User object with updated, signed values
        """

        logger.info("Updating CIS owned attributes", extra={"user_id": user_id})
        user.update_timestamp("last_modified")
        user.last_modified.value = user._get_current_utc_time()
        user.sign_attribute("last_modified", "cis")

        # Currently we accept LDAP and Auth0 disabling a user
        # but since CIS is authoritative on this attribute, we rewrite it here
        # XXX THIS IS AN EXCEPTION AND SHOULD BE REMOVED WHEN ONLY HRIS CAN DISABLE USERS
        # A separate scope/endpoint should be made available to disable+delete users on demand, that isn't using their
        # publishers
        if user.active.signature.publisher.name in ["ldap", "access_provider", "hris"]:
            if self.config("verify_signatures", namespace="cis") == "true":
                logger.info("Verifying signature of attribute active", extra={"user_id": user_id})
                user.verify_attribute_signature("active")
            else:
                logger.warning(
                    "Verifying CIS owned signatures bypassed due to setting `verify_signatures` being false",
                    extra={"user_id": user_id},
                )
            user.sign_attribute("active", "cis")

        # Re-verifying signatures for consistency, since we modified them
        if self.config("verify_signatures", namespace="cis") == "true":
            user.verify_attribute_signature("active")
            user.verify_attribute_signature("last_modified")
        else:
            logger.warning(
                "Verifying CIS owned signatures bypassed due to setting `verify_signatures` being false",
                extra={"user_id": user_id},
            )

        return user

    def _search_and_merge(self, user_id, cis_profile_object):
        """
        Search for an existing user in the vault for the given profile
        If one exist, merge the given profile with the existing user
        If not, return the given profile

        WARNING: This function also verifies the publishers are valid, as this verification requires knowledge of the
        incoming user profile, profile in the vault, and resulting merged profile.

        @cis_profile_object cis_profile.User object of an incoming user
        @user_id str the user id of cis_profile_object

        Returns a cis_profile.User object
        """

        try:
            self._connect()
            vault = user.Profile(self.identity_vault_client.get("table"), self.identity_vault_client.get("client"))
            res = vault.find_by_id(user_id)
            logger.info("Search user in vault results: {}".format(len(res["Items"])))
        except Exception as e:
            logger.error("Problem finding user profile in identity vault due to: {}".format(e))
            res = {"Items": [0]}

        if len(res["Items"]) > 0:
            # This profile exists in the vault and will be merged and it's publishers verified
            self.condition = "update"
            logger.info(
                "A record already exists in the identity vault for user: {}.".format(user_id),
                extra={"user_id": user_id},
            )

            old_user_profile = User(user_structure_json=json.loads(res["Items"][0]["profile"]))
            new_user_profile = copy.deepcopy(old_user_profile)
            new_user_profile.merge(cis_profile_object)
            if self.config("verify_publishers", namespace="cis") == "true":
                logger.info("Verifying publishers", extra={"user_id": user_id})
                try:
                    new_user_profile.verify_all_publishers(old_user_profile)
                except Exception as e:
                    logger.error(
                        "The merged profile failed to pass publisher verification",
                        extra={
                            "user_id": user_id,
                            "profile": new_user_profile.as_dict(),
                            "reason": e,
                            "trace": format_exc(),
                        },
                    )
                    raise VerificationError({"code": "invalid_publisher", "description": "{}".format(e)}, 403)
            else:
                logger.warning(
                    "Bypassing profile publisher verification due to `verify_publishers` setting being false",
                    extra={"user_id": user_id},
                )
            return new_user_profile
        else:
            # This profile as not merged, just verify publishers and return it
            self.condition = "create"
            logger.info(
                "A record does not exist in the identity vault for user: {}.".format(user_id),
                extra={"user_id": user_id},
            )
            if self.config("verify_publishers", namespace="cis") == "true":
                logger.info("Verifying publishers", extra={"user_id": user_id})
                try:
                    cis_profile_object.verify_all_publishers()
                except Exception as e:
                    logger.error(
                        "The profile failed to pass publisher verification",
                        extra={
                            "user_id": user_id,
                            "profile": cis_profile_object.as_dict(),
                            "reason": e,
                            "trace": format_exc(),
                        },
                    )
                    raise VerificationError({"code": "invalid_publisher", "description": "{}".format(e)}, 403)
            else:
                logger.warning(
                    "Bypassing profile publisher verification due to `verify_publishers` setting being false",
                    extra={"user_id": user_id},
                )
            return cis_profile_object

    def put_profile(self, profile):
        """
        Wrapper for a single profile, calls the batch put_profiles method
        """
        return self.put_profiles([profile])

    def put_profiles(self, profiles):
        """
        Merge profile data as necessary with existing profile data for a given user
        Verify profile data is correctly signed and published by allowed publishers
        Write back the result to the identity vault.
        @profiles list of str or cis_profile.User object

        Returns a dictionary containing vault results
        """

        # User profiles that have been verified, validated, merged, etc.
        profiles_to_store = []

        for profile in profiles:
            # Ensure we always have a cis_profile.User at this point (compat)
            if isinstance(profile, str):
                profile = cis_profile.User(user_structure_json=profile)

            user_id = profile.user_id.value
            logger.info("Attempting integration of profile data from user_id {} into the vault".format(user_id))

            # Ensure we merge profile data when we have an existing user in the vault
            # This also does publisher verification
            current_user = self._search_and_merge(user_id, profile)

            # Check profile signatures
            if self.config("verify_signatures", namespace="cis") == "true":
                try:
                    current_user.verify_all_signatures()
                except Exception as e:
                    logger.error(
                        "The profile failed to pass signature verification for user_id: {}".format(user_id),
                        extra={
                            "user_id": user_id,
                            "profile": current_user.as_dict(),
                            "reason": e,
                            "trace": format_exc(),
                        },
                    )
                    raise VerificationError({"code": "invalid_signature", "description": "{}".format(e)}, 403)
            else:
                logger.warning("Bypassing profile signature verification (`verify_signatures` setting is false)")

            # Update any CIS-owned attributes
            current_user = self._update_attr_owned_by_cis(user_id, current_user)

            profiles_to_store.append(current_user)

        # Store resulting user in the vault
        logger.info("Will store {} verified profiles".format(len(profiles_to_store)))
        return self._store_in_vault(profiles_to_store)

    def _store_in_vault(self, profiles):
        """
        Actually store profiles in the vault
        All profiles must have been merged and verified correctly before calling this method

        @profiles list of cis_profiles.User

        Returns dict {"creates": result_of_users_created, "updates": result_of_users_updates}
        """

        try:
            self._connect()

            if self.config("dynamodb_transactions", namespace="cis") == "true":
                logger.debug("Attempting to put batch of profiles ({}) using transactions.".format(len(profiles)))
                vault = user.Profile(
                    self.identity_vault_client.get("table"), self.identity_vault_client.get("client"), transactions=True
                )
            else:
                logger.debug(
                    "Attempting to put batch of profiles ({}) without using transactions.".format(len(profiles))
                )
                vault = user.Profile(
                    self.identity_vault_client.get("table"),
                    self.identity_vault_client.get("client"),
                    transactions=False,
                )

            result = vault.find_or_create_batch(profiles)
        except ClientError as e:
            logger.error(
                "An error occured writing these profiles to dynamodb",
                extra={"profiles": profiles, "error": e, "trace": format_exc()},
            )
            raise IntegrationError({"code": "integration_exception", "description": "{}".format(e)}, 500)
        return {"creates": result[0], "updates": result[1]}

    def delete_profile(self, profile_json):
        # XXX This method should be refactored to look like put_profiles() / put_profile()
        condition = "delete"
        try:
            user_profile = dict(
                id=profile_json["user_id"]["value"],
                primary_email=profile_json["primary_email"]["value"],
                user_uuid=profile_json["uuid"]["value"],
                primary_username=profile_json["primary_username"]["value"],
                sequence_number=self.sequence_number,
                profile=json.dumps(profile_json),
            )

            if self.config("dynamodb_transactions", namespace="cis") == "true":
                logger.debug("Attempting to put batch of profiles using transactions.")
                vault = user.Profile(
                    self.identity_vault_client.get("table"), self.identity_vault_client.get("client"), transactions=True
                )
            else:
                logger.info("Attempting to put batch of profiles without transactions.")
                vault = user.Profile(
                    self.identity_vault_client.get("table"),
                    self.identity_vault_client.get("client"),
                    transactions=False,
                )

            vault.delete(user_profile)
        except ClientError as e:
            logger.error(
                "An error occured removing this profile from dynamodb",
                extra={"profile": profile_json, "error": e, "trace": format_exc()},
            )
            raise IntegrationError({"code": "integration_exception", "description": "{}".format(e)}, 500)
        return {
            "status": 200,
            "message": "user profile deleted for user: {}".format(profile_json["user_id"]["value"]),
            "condition": condition,
        }

class Status(object):
    """Does the right thing to query if the event was integrated and return the results."""

    def __init__(self, sequence_number):
        self.connection_object = connect.AWS()
        self.identity_vault_client = None
        self.sequence_number = sequence_number

    def _connect(self):
        self.connection_object.session()
        self.identity_vault_client = self.connection_object.identity_vault_client()
        return self.identity_vault_client

    def query(self):
        """Query the identity vault using the named Global Secondary Index for the sequence number."""
        # Vault returns a dictionary of dictionaries for the statuses of each check.
        self._connect()

        client = self.identity_vault_client.get("client")

        result = client.query(
            TableName=self.identity_vault_client.get("arn").split("/")[1],
            IndexName="{}-sequence_number".format(self.identity_vault_client.get("arn").split("/")[1]),
            Select="ALL_ATTRIBUTES",
            KeyConditionExpression="sequence_number = :sequence_number",
            ExpressionAttributeValues={":sequence_number": {"S": self.sequence_number}},
        )

        return result

    @property
    def all(self):
        """Run all checks and return the results for the given sequence number as a dict."""
        return {"identity_vault": self.check_identity_vault()}

    def check_identity_vault(self):
        """Check the sequence number of the last record put to the identity vault."""
        if self.identity_vault_client is None:
            self._connect()

        query_result = self.query()
        if len(query_result.get("Items")) == 1:
            return True
        else:
            return False
<|endoftext|>