<commit_before>"""Utilities for nuclear problems."""

import functools
import re

from sympy import (
    Symbol, Function, Sum, symbols, Wild, KroneckerDelta, IndexedBase, Integer
)
from sympy.physics.quantum.cg import CG, Wigner3j, Wigner6j, Wigner9j, cg_simp

from .drudge import Tensor
from .fock import BogoliubovDrudge
from .term import Range
from .utils import sympy_key

class _QNOf(Function):
    """Base class for quantum number access symbolic functions.
    """

    # To be override by subclasses.
    _latex_header = None

    def _latex(self, printer):
        """Form the LaTeX printing.

        The printing will be the header given in the subclasses followed by the
        printing of the orbit wrapped in braces.
        """
        return ''.join([
            self._latex_header, '{', printer.doprint(self.args[0]), '}'
        ])

    def _eval_is_integer(self):
        return True

class JOf(_QNOf):
    """Symbolic access of j quantum number of an orbit."""
    _latex_header = 'j_'

class TildeOf(_QNOf):
    """Symbolic access of the tilde part of an orbit."""
    _latex_header = r'\tilde'

class MOf(_QNOf):
    """Symbolic access of the m quantum number of an orbit."""
    _latex_header = 'm_'

class NOf(_QNOf):
    """Symbolic access of the n quantum number of an orbit."""
    _latex_header = 'n_'

class LOf(_QNOf):
    """Symbolic access of the l quantum number in an orbit."""
    _latex_header = 'l_'

class PiOf(_QNOf):
    """Symbolic access of the parity of an orbit."""
    _latex_header = r'\pi_'

class TOf(_QNOf):
    """Symbolic access to the t quantum number of an orbit."""
    _latex_header = 't_'

_SUFFIXED = re.compile(r'^([a-zA-Z]+)([0-9]+)$')

def _decor_base(symb: Symbol, op, **kwargs):
    """Decorate the base part of the given symbol.

    The given symbol must have a digits-suffixed name, then the given
    operation will be applied to the base part, and recombined with the
    suffix to form the resulted symbol.

    Keyword arguments are all forward to the Symbol constructor.
    """
    m = _SUFFIXED.match(symb.name)
    if not m:
        raise ValueError('Invalid symbol name to parse', symb)

    name, suffix = m.groups()
    return Symbol(op(name) + suffix, **kwargs)

def form_tilde(orig: Symbol):
    """Form the tilde symbol for a given orbit symbol.
    """
    return _decor_base(orig, lambda x: x + 'tilde')

def form_m(orig: Symbol):
    """Form the symbol for m quantum number for a given orbit symbol.
    """
    return _decor_base(orig, lambda _: 'm', integer=True)

class NuclearBogoliubovDrudge(BogoliubovDrudge):
    """Utility drudge for nuclear theories based on Bogoliubov transformation.

    Different from the base :py:class:`BogoliubovDrudge` class, which
    concentrates on the transformation and the commutation rules, here we have
    more utility around the specifics about the nuclear Hamiltonian, especially
    the spherical symmetry.

    .. warning::

        This work is still in progress and highly experimental.  Please at least
        check some of the result before actual usage.

    """

    def __init__(
            self, ctx, coll_j_range=Range('J', 0, Symbol('Jmax') + 1),
            coll_m_range=Range('M'),
            coll_j_dumms=tuple(
                Symbol('J{}'.format(i), integer=True) for i in range(1, 30)
            ),
            coll_m_dumms=tuple(
                Symbol('M{}'.format(i), integer=True) for i in range(1, 30)
            ),
            tilde_range=Range(r'\tilde{Q}'), form_tilde=form_tilde,
            m_range=Range('m'), form_m=form_m, **kwargs
    ):
        """Initialize the drudge object."""
        super().__init__(ctx, **kwargs)

        # Convenient names for quantum number access functions inside drudge
        # scripts.
        self.set_name(
            j_=JOf, tilde_=TildeOf, m_=MOf, n_=NOf, l_=LOf, pi_=PiOf
        )

        self.coll_j_range = coll_j_range
        self.coll_m_range = coll_m_range
        self.coll_j_dumms = coll_j_dumms
        self.coll_m_dumms = coll_m_dumms
        self.set_dumms(coll_j_range, coll_j_dumms)
        self.set_dumms(coll_m_range, coll_m_dumms)

        self.tilde_range = tilde_range
        self.form_tilde = form_tilde
        self.tilde_dumms = tuple(form_tilde(i) for i in self.qp_dumms)
        self.set_dumms(tilde_range, self.tilde_dumms)

        self.m_range = m_range
        self.form_m = form_m
        self.m_dumms = tuple(form_m(i) for i in self.qp_dumms)
        self.set_dumms(m_range, self.m_dumms)

        self.add_resolver_for_dumms()

        # Add utility about CG coefficients and related things.
        self.set_name(
            CG=CG, Wigner3j=Wigner3j, Wigner6j=Wigner6j, Wigner9j=Wigner9j
        )
        self.set_tensor_method('simplify_cg', self.simplify_cg)

        # For angular momentum decoupling.
        #
        # Here is a dictionary giving a callable for all implemented
        # decoupling.
        self._angdec_funcs = {}

        # Cases with total order of 4.
        qp_dumms = self.qp_dumms
        cj = coll_j_dumms[0]
        cm = coll_m_dumms[0]
        jkt1, jkt2, jkt3, jkt4 = [JOf(TildeOf(i)) for i in qp_dumms[:4]]
        mk1, mk2, mk3, mk4 = [MOf(i) for i in qp_dumms[:4]]
        order_4_cases = {
            (0, 4): (_NEG_UNITY ** (jkt1 + jkt2 + cj + cm), {0, 1}),
            (1, 3): (_NEG_UNITY ** (jkt2 - mk2 + cj), {1}),
            (2, 2): (_UNITY, {}),
            (3, 1): (_NEG_UNITY ** (jkt3 - mk3 + cj), {2}),
            (4, 0): (_NEG_UNITY ** (jkt3 + jkt4 + cj + cm), {2, 3}),
        }
        for k, v in order_4_cases.items():
            self._angdec_funcs[k] = functools.partial(
                self._form_angdec_def_4, v[0], v[1]
            )
            continue

        order_2_cases = {
            (0, 2): KroneckerDelta(mk1, -mk2) * _NEG_UNITY ** (jkt2 + mk2),
            (1, 1): KroneckerDelta(mk1, mk2) * _UNITY,
            (2, 0): KroneckerDelta(mk1, -mk2) * _NEG_UNITY ** (jkt1 - mk1)
        }
        for k, v in order_2_cases.items():
            self._angdec_funcs[k] = functools.partial(
                self._form_angdec_def_2, v
            )
            continue
        self.set_tensor_method('do_angdec', self.do_angdec)

    def _form_angdec_def_4(self, phase, neg_ms, base, res_base):
        """Form the angular momentum decoupled form for total order of 4.

        The phase is the over all phase, and the negative m's in the CG
        coefficients can be given in a container, as **zero-based** index.
        """

        ks = self.qp_dumms[:4]
        k1, k2, k3, k4 = ks
        kts = [TildeOf(i) for i in ks]
        kt1, kt2, kt3, kt4 = kts
        jkt1, jkt2, jkt3, jkt4 = [JOf(i) for i in kts]
        # Here the m's already includes the phase.
        mk1, mk2, mk3, mk4 = [
            MOf(ks[i]) * (-1 if i in neg_ms else 1)
            for i in range(4)
        ]
        cj = self.coll_j_dumms[0]
        cm = self.coll_m_dumms[0]

        res = self.define(base[k1, k2, k3, k4], self.sum(
            (cj, self.coll_j_range), (cm, self.coll_m_range[-cj, cj + 1]),
            phase * res_base[cj, kt1, kt2, kt3, kt4] *
            CG(jkt1, mk1, jkt2, mk2, cj, cm) *
            CG(jkt3, mk3, jkt4, mk4, cj, cm)
        ))
        return res

    def _form_angdec_def_2(self, phase, base, res_base):
        """Form the angular momentum decoupled form for total order of 2.

        The phase should include any phase factor except the deltas of pi, J, t
        and the dense tensor indexing.
        """

        ks = self.qp_dumms[:2]
        k1, k2 = ks
        kts = [TildeOf(i) for i in ks]
        kt1, kt2 = kts

        res = self.define(base[k1, k2], self.sum(
            phase * KroneckerDelta(PiOf(kt1), PiOf(kt2))
            * KroneckerDelta(JOf(kt1), JOf(kt2))
            * KroneckerDelta(TOf(kt1), TOf(kt2))
            * res_base[LOf(kt1), JOf(kt1), TOf(kt1), NOf(kt1), NOf(kt2)]
        ))
        return res

    def form_angdec_def(
            self, base: IndexedBase, cr_order: int, an_order: int,
            res_base: IndexedBase = None
    ):
        """Form the tensor definition for angular momentum decoupled form.

        Here for creation and annihilation orders which have been implemented, a
        :py:class:`TensorDef` object will be created as the definition for the
        angular momentum decoupling of the given tensor.  The resulted
        definitions can be used with :py:meth:`do_angdec` method.

        Parameters
        ----------

        base
            The tensor to be rewritten.

        cr_order
            The quasi-particle creation order of the tensor.

        an_order
            The quasi-particle annihilation order of the tensor.

        res_base
            The tensor to be used on the RHS for the tilde tensor.  By default,
            it will have the same name as the LHS.

        """

        res_base = base if res_base is None else res_base
        key = (cr_order, an_order)
        if key not in self._angdec_funcs:
            raise NotImplementedError(
                'Invalid creation/annihilation order to decouple', key
            )
        return self._angdec_funcs[key](base, res_base)

    def do_angdec(self, tensor: Tensor, defs):
        """Expand quasi-particle summation into the tilde and m parts.
        """

        substed = tensor.subst_all(defs)

        # Cache as locals for Spark serialization.
        tilde_range = self.tilde_range
        form_tilde = self.form_tilde
        m_range = self.m_range
        form_m = self.form_m

        def expand(dumm: Symbol):
            """Expand a summation over quasi-particle orbitals."""
            tilde = TildeOf(dumm)
            jtilde = JOf(tilde)
            return [
                (form_tilde(dumm), tilde_range, tilde),
                (form_m(dumm), m_range[-jtilde, jtilde + 1], MOf(dumm))
            ]

        return substed.expand_sums(self.qp_range, expand)

    def simplify_cg(self, tensor: Tensor):
        """Simplify CG coefficients in the expression.

        Here we just concentrate on CG rather than the general case.
        """

        tensor = tensor.map2amps(_canon_cg)

        # Initial simplification of some summations.
        tensor = tensor.simplify_sums(simplify=self._simplify_amp_sum)

        # Deltas could come from some simplification rules.
        tensor = tensor.simplify_deltas()

        # Some summations could become simplifiable after the delta resolution.
        tensor = tensor.simplify_sums()

        return tensor

    @staticmethod
    def _simplify_amp_sum(expr: Sum):
        """Attempt to simplify amplitude sums for nuclear problems.

        Here we specially concentrate on the simplification involving
        Clebosch-Gordan coefficients.  Since this functionality is put into a
        separate tensor function, here we need to invoke it explicitly, since it
        will not be called automatically during the default simplification for
        performance reason.
        """

        # TODO: Add more simplifications here.
        attempts = [
            cg_simp,
            _simpl_varsh_872_4,

        ]
        for attempt in attempts:
            res = attempt(expr)
            if res is not None and res != expr:
                return res
            continue

        return None

#
# Angular momentum quantities simplification.
#

def _canon_cg(expr):
    """Pose CG coefficients in the expression into canonical form.
    """
    return expr.replace(CG, _canon_cg_core)

def _canon_cg_core(j1, m1, j2, m2, cj, cm):
    r"""Pose a CG into a canonical form.

    When two of the little :math:`m`s has got negation, we flip the signs of all
    of them.  Then the sort keys of :math:`m_1` and :math:`j_1` will be compared
    with that of :math:`m_2` and :math:`j_2`, which may lead to flipping by
    Varsh 8.4.3 Equation 10.

    """

    if m1.has(_NEG_UNITY) and m2.has(_NEG_UNITY):
        m1 *= _NEG_UNITY
        m2 *= _NEG_UNITY
        cm *= _NEG_UNITY

    phase = _UNITY
    if (sympy_key(m1), sympy_key(j1)) > (sympy_key(m2), sympy_key(j2)):
        m1, m2 = m2, m1
        j1, j2 = j2, j1
        phase = _NEG_UNITY ** (j1 + j2 - cj)

    return CG(j1, m1, j2, m2, cj, cm) * phase

def _simpl_varsh_872_4(expr: Sum):
    """Make CG simplification based on Varsh 8.7.2 Equation 4.
    """
    if len(expr.args) != 3:
        return None
    m1 = expr.args[1][0]
    m2 = expr.args[2][0]

    j1, j2, cj1, cm1, cj2, cm2 = symbols('j1 j2 J1 M1 J2 M2', cls=Wild)
    match = expr.args[0].match(
        CG(j1, m1, j2, m2, cj1, cm1) * CG(j1, m1, j2, m2, cj2, cm2)
    )
    if not match:
        return None
    return KroneckerDelta(
        match[cj1], match[cj2]
    ) * KroneckerDelta(match[cm1], match[cm2])

# Utility constants.

_UNITY = Integer(1)
_NEG_UNITY = Integer(-1)
<commit_msg>Fix canonicalization of CG coefficients<commit_after>"""Utilities for nuclear problems."""

import functools
import re

from sympy import (
    Symbol, Function, Sum, symbols, Wild, KroneckerDelta, IndexedBase, Integer
)
from sympy.physics.quantum.cg import CG, Wigner3j, Wigner6j, Wigner9j, cg_simp

from .drudge import Tensor
from .fock import BogoliubovDrudge
from .term import Range
from .utils import sympy_key

class _QNOf(Function):
    """Base class for quantum number access symbolic functions.
    """

    # To be override by subclasses.
    _latex_header = None

    def _latex(self, printer):
        """Form the LaTeX printing.

        The printing will be the header given in the subclasses followed by the
        printing of the orbit wrapped in braces.
        """
        return ''.join([
            self._latex_header, '{', printer.doprint(self.args[0]), '}'
        ])

    def _eval_is_integer(self):
        return True

class JOf(_QNOf):
    """Symbolic access of j quantum number of an orbit."""
    _latex_header = 'j_'

class TildeOf(_QNOf):
    """Symbolic access of the tilde part of an orbit."""
    _latex_header = r'\tilde'

class MOf(_QNOf):
    """Symbolic access of the m quantum number of an orbit."""
    _latex_header = 'm_'

class NOf(_QNOf):
    """Symbolic access of the n quantum number of an orbit."""
    _latex_header = 'n_'

class LOf(_QNOf):
    """Symbolic access of the l quantum number in an orbit."""
    _latex_header = 'l_'

class PiOf(_QNOf):
    """Symbolic access of the parity of an orbit."""
    _latex_header = r'\pi_'

class TOf(_QNOf):
    """Symbolic access to the t quantum number of an orbit."""
    _latex_header = 't_'

_SUFFIXED = re.compile(r'^([a-zA-Z]+)([0-9]+)$')

def _decor_base(symb: Symbol, op, **kwargs):
    """Decorate the base part of the given symbol.

    The given symbol must have a digits-suffixed name, then the given
    operation will be applied to the base part, and recombined with the
    suffix to form the resulted symbol.

    Keyword arguments are all forward to the Symbol constructor.
    """
    m = _SUFFIXED.match(symb.name)
    if not m:
        raise ValueError('Invalid symbol name to parse', symb)

    name, suffix = m.groups()
    return Symbol(op(name) + suffix, **kwargs)

def form_tilde(orig: Symbol):
    """Form the tilde symbol for a given orbit symbol.
    """
    return _decor_base(orig, lambda x: x + 'tilde')

def form_m(orig: Symbol):
    """Form the symbol for m quantum number for a given orbit symbol.
    """
    return _decor_base(orig, lambda _: 'm', integer=True)

class NuclearBogoliubovDrudge(BogoliubovDrudge):
    """Utility drudge for nuclear theories based on Bogoliubov transformation.

    Different from the base :py:class:`BogoliubovDrudge` class, which
    concentrates on the transformation and the commutation rules, here we have
    more utility around the specifics about the nuclear Hamiltonian, especially
    the spherical symmetry.

    .. warning::

        This work is still in progress and highly experimental.  Please at least
        check some of the result before actual usage.

    """

    def __init__(
            self, ctx, coll_j_range=Range('J', 0, Symbol('Jmax') + 1),
            coll_m_range=Range('M'),
            coll_j_dumms=tuple(
                Symbol('J{}'.format(i), integer=True) for i in range(1, 30)
            ),
            coll_m_dumms=tuple(
                Symbol('M{}'.format(i), integer=True) for i in range(1, 30)
            ),
            tilde_range=Range(r'\tilde{Q}'), form_tilde=form_tilde,
            m_range=Range('m'), form_m=form_m, **kwargs
    ):
        """Initialize the drudge object."""
        super().__init__(ctx, **kwargs)

        # Convenient names for quantum number access functions inside drudge
        # scripts.
        self.set_name(
            j_=JOf, tilde_=TildeOf, m_=MOf, n_=NOf, l_=LOf, pi_=PiOf
        )

        self.coll_j_range = coll_j_range
        self.coll_m_range = coll_m_range
        self.coll_j_dumms = coll_j_dumms
        self.coll_m_dumms = coll_m_dumms
        self.set_dumms(coll_j_range, coll_j_dumms)
        self.set_dumms(coll_m_range, coll_m_dumms)

        self.tilde_range = tilde_range
        self.form_tilde = form_tilde
        self.tilde_dumms = tuple(form_tilde(i) for i in self.qp_dumms)
        self.set_dumms(tilde_range, self.tilde_dumms)

        self.m_range = m_range
        self.form_m = form_m
        self.m_dumms = tuple(form_m(i) for i in self.qp_dumms)
        self.set_dumms(m_range, self.m_dumms)

        self.add_resolver_for_dumms()

        # Add utility about CG coefficients and related things.
        self.set_name(
            CG=CG, Wigner3j=Wigner3j, Wigner6j=Wigner6j, Wigner9j=Wigner9j
        )
        self.set_tensor_method('simplify_cg', self.simplify_cg)

        # For angular momentum decoupling.
        #
        # Here is a dictionary giving a callable for all implemented
        # decoupling.
        self._angdec_funcs = {}

        # Cases with total order of 4.
        qp_dumms = self.qp_dumms
        cj = coll_j_dumms[0]
        cm = coll_m_dumms[0]
        jkt1, jkt2, jkt3, jkt4 = [JOf(TildeOf(i)) for i in qp_dumms[:4]]
        mk1, mk2, mk3, mk4 = [MOf(i) for i in qp_dumms[:4]]
        order_4_cases = {
            (0, 4): (_NEG_UNITY ** (jkt1 + jkt2 + cj + cm), {0, 1}),
            (1, 3): (_NEG_UNITY ** (jkt2 - mk2 + cj), {1}),
            (2, 2): (_UNITY, {}),
            (3, 1): (_NEG_UNITY ** (jkt3 - mk3 + cj), {2}),
            (4, 0): (_NEG_UNITY ** (jkt3 + jkt4 + cj + cm), {2, 3}),
        }
        for k, v in order_4_cases.items():
            self._angdec_funcs[k] = functools.partial(
                self._form_angdec_def_4, v[0], v[1]
            )
            continue

        order_2_cases = {
            (0, 2): KroneckerDelta(mk1, -mk2) * _NEG_UNITY ** (jkt2 + mk2),
            (1, 1): KroneckerDelta(mk1, mk2) * _UNITY,
            (2, 0): KroneckerDelta(mk1, -mk2) * _NEG_UNITY ** (jkt1 - mk1)
        }
        for k, v in order_2_cases.items():
            self._angdec_funcs[k] = functools.partial(
                self._form_angdec_def_2, v
            )
            continue
        self.set_tensor_method('do_angdec', self.do_angdec)

    def _form_angdec_def_4(self, phase, neg_ms, base, res_base):
        """Form the angular momentum decoupled form for total order of 4.

        The phase is the over all phase, and the negative m's in the CG
        coefficients can be given in a container, as **zero-based** index.
        """

        ks = self.qp_dumms[:4]
        k1, k2, k3, k4 = ks
        kts = [TildeOf(i) for i in ks]
        kt1, kt2, kt3, kt4 = kts
        jkt1, jkt2, jkt3, jkt4 = [JOf(i) for i in kts]
        # Here the m's already includes the phase.
        mk1, mk2, mk3, mk4 = [
            MOf(ks[i]) * (-1 if i in neg_ms else 1)
            for i in range(4)
        ]
        cj = self.coll_j_dumms[0]
        cm = self.coll_m_dumms[0]

        res = self.define(base[k1, k2, k3, k4], self.sum(
            (cj, self.coll_j_range), (cm, self.coll_m_range[-cj, cj + 1]),
            phase * res_base[cj, kt1, kt2, kt3, kt4] *
            CG(jkt1, mk1, jkt2, mk2, cj, cm) *
            CG(jkt3, mk3, jkt4, mk4, cj, cm)
        ))
        return res

    def _form_angdec_def_2(self, phase, base, res_base):
        """Form the angular momentum decoupled form for total order of 2.

        The phase should include any phase factor except the deltas of pi, J, t
        and the dense tensor indexing.
        """

        ks = self.qp_dumms[:2]
        k1, k2 = ks
        kts = [TildeOf(i) for i in ks]
        kt1, kt2 = kts

        res = self.define(base[k1, k2], self.sum(
            phase * KroneckerDelta(PiOf(kt1), PiOf(kt2))
            * KroneckerDelta(JOf(kt1), JOf(kt2))
            * KroneckerDelta(TOf(kt1), TOf(kt2))
            * res_base[LOf(kt1), JOf(kt1), TOf(kt1), NOf(kt1), NOf(kt2)]
        ))
        return res

    def form_angdec_def(
            self, base: IndexedBase, cr_order: int, an_order: int,
            res_base: IndexedBase = None
    ):
        """Form the tensor definition for angular momentum decoupled form.

        Here for creation and annihilation orders which have been implemented, a
        :py:class:`TensorDef` object will be created as the definition for the
        angular momentum decoupling of the given tensor.  The resulted
        definitions can be used with :py:meth:`do_angdec` method.

        Parameters
        ----------

        base
            The tensor to be rewritten.

        cr_order
            The quasi-particle creation order of the tensor.

        an_order
            The quasi-particle annihilation order of the tensor.

        res_base
            The tensor to be used on the RHS for the tilde tensor.  By default,
            it will have the same name as the LHS.

        """

        res_base = base if res_base is None else res_base
        key = (cr_order, an_order)
        if key not in self._angdec_funcs:
            raise NotImplementedError(
                'Invalid creation/annihilation order to decouple', key
            )
        return self._angdec_funcs[key](base, res_base)

    def do_angdec(self, tensor: Tensor, defs):
        """Expand quasi-particle summation into the tilde and m parts.
        """

        substed = tensor.subst_all(defs)

        # Cache as locals for Spark serialization.
        tilde_range = self.tilde_range
        form_tilde = self.form_tilde
        m_range = self.m_range
        form_m = self.form_m

        def expand(dumm: Symbol):
            """Expand a summation over quasi-particle orbitals."""
            tilde = TildeOf(dumm)
            jtilde = JOf(tilde)
            return [
                (form_tilde(dumm), tilde_range, tilde),
                (form_m(dumm), m_range[-jtilde, jtilde + 1], MOf(dumm))
            ]

        return substed.expand_sums(self.qp_range, expand)

    def simplify_cg(self, tensor: Tensor):
        """Simplify CG coefficients in the expression.

        Here we just concentrate on CG rather than the general case.
        """

        tensor = tensor.map2amps(_canon_cg)

        # Initial simplification of some summations.
        tensor = tensor.simplify_sums(simplify=self._simplify_amp_sum)

        # Deltas could come from some simplification rules.
        tensor = tensor.simplify_deltas()

        # Some summations could become simplifiable after the delta resolution.
        tensor = tensor.simplify_sums()

        return tensor

    @staticmethod
    def _simplify_amp_sum(expr: Sum):
        """Attempt to simplify amplitude sums for nuclear problems.

        Here we specially concentrate on the simplification involving
        Clebosch-Gordan coefficients.  Since this functionality is put into a
        separate tensor function, here we need to invoke it explicitly, since it
        will not be called automatically during the default simplification for
        performance reason.
        """

        # TODO: Add more simplifications here.
        attempts = [
            cg_simp,
            _simpl_varsh_872_4,

        ]
        for attempt in attempts:
            res = attempt(expr)
            if res is not None and res != expr:
                return res
            continue

        return None

#
# Angular momentum quantities simplification.
#

def _canon_cg(expr):
    """Pose CG coefficients in the expression into canonical form.
    """
    return expr.replace(CG, _canon_cg_core)

def _canon_cg_core(j1, m1, j2, m2, cj, cm):
    r"""Pose a CG into a canonical form.

    When two of the little :math:`m`s has got negation, we flip the signs of all
    of them.  Then the sort keys of :math:`m_1` and :math:`j_1` will be compared
    with that of :math:`m_2` and :math:`j_2`, which may lead to flipping by
    Varsh 8.4.3 Equation 10.

    """

    phase = _UNITY
    if m1.has(_NEG_UNITY) and m2.has(_NEG_UNITY):
        m1 *= _NEG_UNITY
        m2 *= _NEG_UNITY
        cm *= _NEG_UNITY
        phase = _NEG_UNITY ** (j1 + j2 - cj)

    if (sympy_key(m1), sympy_key(j1)) > (sympy_key(m2), sympy_key(j2)):
        m1, m2 = m2, m1
        j1, j2 = j2, j1
        phase = _NEG_UNITY ** (j1 + j2 - cj)

    return CG(j1, m1, j2, m2, cj, cm) * phase

def _simpl_varsh_872_4(expr: Sum):
    """Make CG simplification based on Varsh 8.7.2 Equation 4.
    """
    if len(expr.args) != 3:
        return None
    m1 = expr.args[1][0]
    m2 = expr.args[2][0]

    j1, j2, cj1, cm1, cj2, cm2 = symbols('j1 j2 J1 M1 J2 M2', cls=Wild)
    match = expr.args[0].match(
        CG(j1, m1, j2, m2, cj1, cm1) * CG(j1, m1, j2, m2, cj2, cm2)
    )
    if not match:
        return None
    return KroneckerDelta(
        match[cj1], match[cj2]
    ) * KroneckerDelta(match[cm1], match[cm2])

# Utility constants.

_UNITY = Integer(1)
_NEG_UNITY = Integer(-1)
<|endoftext|>


<commit_before>"""DryDock cluster specification."""
from . import templates

class Container(object):
    """A docker container specification."""
    def __init__(self, name, base="ubuntu", exposed_ports=None, external=False,
                 http_port=0, https_port=0, domain="", volumes=None):
        self.name = name
        self.domain = ""
        self.fqdn = ""
        self.set_domain(domain)
        self.base = base
        self.exposed_ports = {}
        if exposed_ports:
            self.exposed_ports.update(exposed_ports)
        self.http_port = http_port
        self.https_port = https_port
        self.external = external
        self.volumes = volumes if volumes else []
        self.commands = []
        self.skyfqdn = '.'.join([self.name, self.base.split('/')[-1], "containers", "drydock"])

    def set_domain(self, domain):
        """Set fqdn and domain."""
        self.domain = domain
        if self.name == "root":
            self.fqdn = self.domain
        else:
            self.fqdn = self.name + '.' + self.domain

    def get_container_commands(self):
        """Return a list of commands required to construct and use this container specification."""
        commands = []
        run = 'docker run -d'.split(' ')

        run.append("--name {0}".format(self.name))

        for external in sorted(self.exposed_ports.keys()):
            run.append("-p {0}:{1}".format(external, self.exposed_ports[external]))

        for path in self.volumes:
            run.append("-v {0}:{0}".format(path))

        run.append(self.base)
        commands.append("RUN " + ' '.join(run))
        commands.append("ADD supervisor/{0}.conf /etc/supervisor/conf.d/{0}.conf".format(self.name))

        if self.http_port or self.https_port:
            commands.append("ADD sites/{0} /etc/nginx/sites-enabled/{0}".format(self.fqdn))

        commands.extend(self.commands)
        return '\n'.join(commands)

    def get_supervisor_config(self):
        """Write this containers supervisor configuration file to `./supervisor/{Container.name}.conf`"""
        return """[program:{0}]
command=docker start {0}
autostart=true
autorestart=true""".format(self.name)

    def get_nginx_config(self):
        """Write this containers nginx site configuration to `./sites/{Container.fqdn}`"""
        return templates.render_nginx_config(self)

class MetaContainer(Container):
    """A container that stores containers."""
    def __init__(self, name, domain, subcontainers, base="nekroze/drydock", *args, **kwargs):
        super(MetaContainer, self).__init__(*args, name=name, base=base, domain=domain, **kwargs)
        self.containers = {}
        self.reverse_proxies = {}
        self.fqdn = domain

        for sub in subcontainers:
            self.add_container(Container(**sub))

    def set_domain(self, domain):
        """Set fqdn and domain."""
        self.domain = domain
        self.fqdn = domain

    def add_container(self, container):
        """Add the given container object to this meta container."""
        if not container.domain and self.domain:
            container.set_domain(self.domain)

        self.containers[container.name] = container
        self.exposed_ports.update(container.exposed_ports)

        fqdn = container.name + '.' + self.domain
        self.reverse_proxies[fqdn] = {
            "skyfqdn": container.skyfqdn,
            "external": container.external,
            "http": container.http_port,
            "https": container.https_port
        }

    def get_portmaps(self):
        template = "-p {0}:{1}"
        portmaps = [template.format("80", "80"), template.format("443", "443")]

        for name in sorted(self.containers.keys()):
            container = self.containers[name]
            for port in sorted(container.exposed_ports.keys()):

                portmap = template.format(port, port)
                if portmap not in portmaps:
                    portmaps.append(portmap)

        return ' '.join(portmaps)

    def get_volumemaps(self):
        volumes = [""]

        for volume in self.volumes:
            volumes.append("-v {statedir}{volume}:{volume}".format(
                statedir="/var/lib/" + self.name, volume=volume))

        for name in sorted(self.containers.keys()):
            container = self.containers[name]

            for volume in container.volumes:
                volumes.append("-v {statedir}{volume}:{volume}".format(
                    statedir="/var/lib/" + self.name, volume=volume))

        return ' '.join(set(volumes))

    def get_docker_commands(self):
        commands = ["docker build -t {name}-img .".format(name=self.name)]
        commands.append("docker run -d -t --name {name} {name}-img {portmaps}{volumes}".format(
            name=self.name, portmaps=self.get_portmaps(), volumes=self.get_volumemaps()))

        return "\n".join(commands)

    def get_dockerfile(self):
        commands = ["FROM " + self.base, ""]
        ports = [80, 443]
        volumes = []

        for name in sorted(self.containers.keys()):
            container = self.containers[name]

            ports.extend(sorted(list(container.exposed_ports.keys())))
            commands.append(container.get_container_commands())
            commands.append("")

            volumes.extend(container.volumes)

        commands.append("EXPOSE " + ' '.join([str(port) for port in ports]))

        volumes = ['"{0}"'.format(volume) for volume in volumes]
        if volumes:
            commands.append("VOLUME [{0}]".format(", ".join(set(volumes))))

        return '\n'.join(commands)<commit_msg>attempt to fix volume command generation on pypy<commit_after>"""DryDock cluster specification."""
from . import templates

class Container(object):
    """A docker container specification."""
    def __init__(self, name, base="ubuntu", exposed_ports=None, external=False,
                 http_port=0, https_port=0, domain="", volumes=None):
        self.name = name
        self.domain = ""
        self.fqdn = ""
        self.set_domain(domain)
        self.base = base
        self.exposed_ports = {}
        if exposed_ports:
            self.exposed_ports.update(exposed_ports)
        self.http_port = http_port
        self.https_port = https_port
        self.external = external
        self.volumes = volumes if volumes else []
        self.commands = []
        self.skyfqdn = '.'.join([self.name, self.base.split('/')[-1], "containers", "drydock"])

    def set_domain(self, domain):
        """Set fqdn and domain."""
        self.domain = domain
        if self.name == "root":
            self.fqdn = self.domain
        else:
            self.fqdn = self.name + '.' + self.domain

    def get_container_commands(self):
        """Return a list of commands required to construct and use this container specification."""
        commands = []
        run = 'docker run -d'.split(' ')

        run.append("--name {0}".format(self.name))

        for external in sorted(self.exposed_ports.keys()):
            run.append("-p {0}:{1}".format(external, self.exposed_ports[external]))

        for path in self.volumes:
            run.append("-v {0}:{0}".format(path))

        run.append(self.base)
        commands.append("RUN " + ' '.join(run))
        commands.append("ADD supervisor/{0}.conf /etc/supervisor/conf.d/{0}.conf".format(self.name))

        if self.http_port or self.https_port:
            commands.append("ADD sites/{0} /etc/nginx/sites-enabled/{0}".format(self.fqdn))

        commands.extend(self.commands)
        return '\n'.join(commands)

    def get_supervisor_config(self):
        """Write this containers supervisor configuration file to `./supervisor/{Container.name}.conf`"""
        return """[program:{0}]
command=docker start {0}
autostart=true
autorestart=true""".format(self.name)

    def get_nginx_config(self):
        """Write this containers nginx site configuration to `./sites/{Container.fqdn}`"""
        return templates.render_nginx_config(self)

class MetaContainer(Container):
    """A container that stores containers."""
    def __init__(self, name, domain, subcontainers, base="nekroze/drydock", *args, **kwargs):
        super(MetaContainer, self).__init__(*args, name=name, base=base, domain=domain, **kwargs)
        self.containers = {}
        self.reverse_proxies = {}
        self.fqdn = domain

        for sub in subcontainers:
            self.add_container(Container(**sub))

    def set_domain(self, domain):
        """Set fqdn and domain."""
        self.domain = domain
        self.fqdn = domain

    def add_container(self, container):
        """Add the given container object to this meta container."""
        if not container.domain and self.domain:
            container.set_domain(self.domain)

        self.containers[container.name] = container
        self.exposed_ports.update(container.exposed_ports)

        fqdn = container.name + '.' + self.domain
        self.reverse_proxies[fqdn] = {
            "skyfqdn": container.skyfqdn,
            "external": container.external,
            "http": container.http_port,
            "https": container.https_port
        }

    def get_portmaps(self):
        template = "-p {0}:{1}"
        portmaps = [template.format("80", "80"), template.format("443", "443")]

        for name in sorted(self.containers.keys()):
            container = self.containers[name]
            for port in sorted(container.exposed_ports.keys()):

                portmap = template.format(port, port)
                if portmap not in portmaps:
                    portmaps.append(portmap)

        return ' '.join(portmaps)

    def get_volumemaps(self):
        volumes = []

        for volume in self.volumes:
            volumes.append("-v {statedir}{volume}:{volume}".format(
                statedir="/var/lib/" + self.name, volume=volume))

        for name in sorted(self.containers.keys()):
            container = self.containers[name]

            for volume in container.volumes:
                volumes.append("-v {statedir}{volume}:{volume}".format(
                    statedir="/var/lib/" + self.name, volume=volume))

        return ' ' + ' '.join(set(volumes))

    def get_docker_commands(self):
        commands = ["docker build -t {name}-img .".format(name=self.name)]
        commands.append("docker run -d -t --name {name} {name}-img {portmaps}{volumes}".format(
            name=self.name, portmaps=self.get_portmaps(), volumes=self.get_volumemaps()))

        return "\n".join(commands)

    def get_dockerfile(self):
        commands = ["FROM " + self.base, ""]
        ports = [80, 443]
        volumes = []

        for name in sorted(self.containers.keys()):
            container = self.containers[name]

            ports.extend(sorted(list(container.exposed_ports.keys())))
            commands.append(container.get_container_commands())
            commands.append("")

            volumes.extend(container.volumes)

        commands.append("EXPOSE " + ' '.join([str(port) for port in ports]))

        volumes = ['"{0}"'.format(volume) for volume in volumes]
        if volumes:
            commands.append("VOLUME [{0}]".format(", ".join(set(volumes))))

        return '\n'.join(commands)<|endoftext|>


<commit_before>#!/usr/python/bin
"""
Author: Michael Seaman
Particle class for Classwork 8
Defines particle objects that are seeded at initialization
and move around accordingly
"""
import numpy as np
from datetime import datetime

class Particle:
    """
    Defines particle objects that are seeded at initialization
and move around accordingly
    """
    def __init__(self, seed = datetime.now, x= 0, y = 0):
        self.x = x
        self.y = y
        self.RNG = np.random.RandomState(seed)

    def move(self, step_size = 1):
        """
        Moves in a random (seeded) direction with a distance of
        an optional stepsize
        """
        switch = RNG.randint(1, 5)
        if(switch == 1):
            y += step_size #Up
        elif(switch == 2):
            x += step_size #Right
        elif(switch == 3):
            y -= step_size #Down
        else:
            x -= step_size #left

    def test_confined_movement():
        """
        Given n number of steps, the particles
        should not deviate n units from the origin
        n = [10, 100, 1000]
        """
        n = [10, 100, 1000]
        p = Particle()
        for x in xrange(n[0]):
            p.move()
        p = Particle()
        for x in xrange(n[0]):
            p.move()

        <commit_msg>Particle should be working now<commit_after>#!/usr/python/bin
"""
Author: Michael Seaman
Particle class for Classwork 8
Defines particle objects that are seeded at initialization
and move around accordingly
"""
import numpy as np
import time
from unittest import TestCase

class Particle:
    """
    Defines particle objects that are seeded at initialization
and move around accordingly
    """
    def __init__(self, seed = int(time.time()*10000), x= 0, y = 0):
        self.x = x
        self.y = y
        self.RNG = np.random.seed(seed)

    def move(self, step_size = 1):
        """
        Moves in a random (seeded) direction with a distance of
        an optional stepsize
        """
        switch = RNG.randint(1, 5)
        if(switch == 1):
            y += step_size #Up
        elif(switch == 2):
            x += step_size #Right
        elif(switch == 3):
            y -= step_size #Down
        else:
            x -= step_size #left

class test_Particle(TestCase):
    """
    Test Class for Particle.
    This class only extends with test functions
    """

    def test_confined_movement():
        """
        Given n number of steps, the particles
        should not deviate n units from the origin
        n = [10, 100, 1000]
        """
        n = [10, 100, 1000]
        testReturn = True
        for numberOfSteps in n:
            p = Particle()
            for x in xrange(numberOfSteps):
                p.move()
            if(p.x > numberOfSteps or p.x < (-1 * numberOfSteps) or p.y > numberOfSteps or p.y < (-1 * numberOfSteps)):
                testReturn = False
        assert(testReturn)

    def test_same_seed_movement(numberOfSteps = 100000):
        """
        2 Particles given the same seed should move identically
        We test that all positions of the two particles are identical
        to the numberOfSteps specified
        """
        p1 = Particle(1024901)
        p2 = Particle(1024901)
        testReturn = True
        for x in xrange(numberOfSteps):
            p1.move()
            p2.move()
            if(p1.x != p2.x or p1.y != p2.y):
                testReturn = False
        assert(testReturn)<|endoftext|>


<commit_before>import torch
from torch.distributions import Distribution, Normal, Categorical

class MixtureNormal(Distribution):
    def __init__(self, pi, loc, scale):
        super(MixtureNormal, self).__init__()

        self.pi = pi
        self.loc = loc
        self.scale = scale
        self.pd = Normal(self.loc, self.scale)
        self.pi_pd = [Categorical(prob) for prob in self.pi]

    def sample(self, sample_shape=torch.Size()):
        with torch.no_grad():
            raw_sample = self.pd.sample()
            index = torch.stack([pd.sample() for pd in self.pi_pd]).unsqueeze(-1)
            return torch.gather(raw_sample, -1, index).squeeze(-1)

    def log_prob(self, value):
        if self._validate_args:
            self._validate_sample(value)

        value = value.unsqueeze(-1).expand_as(self.loc)
        log_probs = self.pd.log_prob(value)
        probs = torch.exp(log_probs)
        weighted_probs = self.pi * probs
        sum_prob = torch.sum(weighted_probs, -1)
        return torch.log(sum_prob)
<commit_msg>Adapt to PyTorch 0.4.0<commit_after>import torch
from torch.distributions import Normal, Categorical, Distribution

class MixtureNormal(Distribution):
    def __init__(self, pi, loc, scale):
        self.pi, self.loc, self.scale = pi, loc, scale

        super(MixtureNormal, self).__init__()

        self.normal_pd = Normal(self.loc, self.scale)
        self.pi_pd = Categorical(self.pi)

    def sample(self, sample_shape=torch.Size()):
        with torch.no_grad():
            raw_sample = self.normal_pd.sample()
            index = self.pi_pd.sample().unsqueeze(-1)
            return torch.gather(raw_sample, -1, index).squeeze(-1)

    def log_prob(self, value):
        if self._validate_args:
            self._validate_sample(value)

        value = value.unsqueeze(-1).expand_as(self.loc)
        log_probs = self.normal_pd.log_prob(value)
        probs = torch.exp(log_probs)
        weighted_probs = self.pi * probs
        sum_prob = torch.sum(weighted_probs, -1)
        return torch.log(sum_prob)
<|endoftext|>


<commit_before>from __future__ import unicode_literals

import os
import dvc.prompt as prompt
import dvc.logger as logger

from dvc.command.base import CmdBase

class CmdGC(CmdBase):
    def run(self):
        msg = "this will remove all cache except the cache that is used in "
        if not self.args.all_branches and not self.args.all_tags:
            msg += "the current git branch"
        elif self.args.all_branches and not self.args.all_tags:
            msg += "all git branches"
        elif not self.args.all_branches and self.args.all_tags:
            msg += "all git tags"
        else:
            msg += "all git branches and all git tags"

        if self.args.repos is not None and len(self.args.repos) > 0:
            msg += " of the current and the following repos:"

            for repo_path in self.args.repos:
                msg += "\n  - %s" % os.path.abspath(repo_path)
        else:
            msg += " of the current repo."

        logger.warning(msg)

        msg = "Are you sure you want to proceed?"
        if not self.args.force and not prompt.confirm(msg):
            return 1

        self.repo.gc(
            all_branches=self.args.all_branches,
            all_tags=self.args.all_tags,
            cloud=self.args.cloud,
            remote=self.args.remote,
            force=self.args.force,
            jobs=self.args.jobs,
            repos=self.args.repos,
        )
        return 0

def add_parser(subparsers, parent_parser):
    GC_HELP = "Collect garbage."
    gc_parser = subparsers.add_parser(
        "gc", parents=[parent_parser], description=GC_HELP, help=GC_HELP
    )
    gc_parser.add_argument(
        "-a",
        "--all-branches",
        action="store_true",
        default=False,
        help="Collect garbage for all branches.",
    )
    gc_parser.add_argument(
        "-T",
        "--all-tags",
        action="store_true",
        default=False,
        help="Collect garbage for all tags.",
    )
    gc_parser.add_argument(
        "-c",
        "--cloud",
        action="store_true",
        default=False,
        help="Collect garbage in remote repository.",
    )
    gc_parser.add_argument(
        "-r", "--remote", help="Remote repository to collect garbage in."
    )
    gc_parser.add_argument(
        "-f",
        "--force",
        action="store_true",
        default=False,
        help="Force garbage collection.",
    )
    gc_parser.add_argument(
        "-j",
        "--jobs",
        type=int,
        default=None,
        help="Number of jobs to run simultaneously.",
    )
    gc_parser.add_argument(
        "-p",
        "--projects",
        dest="repos",
        type=str,
        nargs="*",
        default=None,
        help="Collect garbage for all given projects.",
    )
    gc_parser.set_defaults(func=CmdGC)
<commit_msg>Clarified command line flags for dvc gc (#1644)<commit_after>from __future__ import unicode_literals

import os
import dvc.prompt as prompt
import dvc.logger as logger

from dvc.command.base import CmdBase

class CmdGC(CmdBase):
    def run(self):
        msg = "this will remove all cache except the cache that is used in "
        if not self.args.all_branches and not self.args.all_tags:
            msg += "the current git branch"
        elif self.args.all_branches and not self.args.all_tags:
            msg += "all git branches"
        elif not self.args.all_branches and self.args.all_tags:
            msg += "all git tags"
        else:
            msg += "all git branches and all git tags"

        if self.args.repos is not None and len(self.args.repos) > 0:
            msg += " of the current and the following repos:"

            for repo_path in self.args.repos:
                msg += "\n  - %s" % os.path.abspath(repo_path)
        else:
            msg += " of the current repo."

        logger.warning(msg)

        msg = "Are you sure you want to proceed?"
        if not self.args.force and not prompt.confirm(msg):
            return 1

        self.repo.gc(
            all_branches=self.args.all_branches,
            all_tags=self.args.all_tags,
            cloud=self.args.cloud,
            remote=self.args.remote,
            force=self.args.force,
            jobs=self.args.jobs,
            repos=self.args.repos,
        )
        return 0

def add_parser(subparsers, parent_parser):
    GC_HELP = (
        "Collect garbage from your DVC cache. \n"
        "Deletes all files in the cache which are not in use by the "
        "specified git references (defaults to just HEAD)."
    )
    gc_parser = subparsers.add_parser(
        "gc", parents=[parent_parser], description=GC_HELP, help=GC_HELP
    )
    gc_parser.add_argument(
        "-a",
        "--all-branches",
        action="store_true",
        default=False,
        help="Keep data files for the tips of all git branches.",
    )
    gc_parser.add_argument(
        "-T",
        "--all-tags",
        action="store_true",
        default=False,
        help="Keep data files for all git tags.",
    )
    gc_parser.add_argument(
        "-c",
        "--cloud",
        action="store_true",
        default=False,
        help="Collect garbage in remote repository.",
    )
    gc_parser.add_argument(
        "-r", "--remote", help="Remote repository to collect garbage in."
    )
    gc_parser.add_argument(
        "-f",
        "--force",
        action="store_true",
        default=False,
        help="Force garbage collection - automatically agree to all prompts.",
    )
    gc_parser.add_argument(
        "-j",
        "--jobs",
        type=int,
        default=None,
        help="Number of jobs to run simultaneously.",
    )
    gc_parser.add_argument(
        "-p",
        "--projects",
        dest="repos",
        type=str,
        nargs="*",
        default=None,
        help="Keep data files required by these projects "
        "in addition to the current one. "
        "Useful if you share a single cache across repos.",
    )
    gc_parser.set_defaults(func=CmdGC)
<|endoftext|>


<commit_before>import csv
# always put the import functions at the top of your file
# it helps when you're debugging to find what modules could be breaking

with open("Source_Data_v2.csv", "r") as my_file:
    # print csvReader.fieldnames
    csvReader = csv.DictReader(my_file)
    my_list = []
    for row in csvReader:
        entry = {}
        for fieldname in csvReader.fieldnames:
            entry[fieldname] = row[fieldname]
        my_list.append(entry)

# this line is what you use whenever you want your script to be run wihtout ipython.
# if you pull this code into an environment (ie import code.parse)
# it will not call this stuff,  because __name__ wont be __main__.
if __name__ == '__main__':
    print "there are {} rows in my file".format(len(my_list))
    print "there are {} columns in my file".format(len(csvReader.fieldnames))
    print "I was called as a script!!"
<commit_msg>ok no more fucking around. parse has a function<commit_after>import csv
# always put the import functions at the top of your file
# it helps when you're debugging to find what modules could be breaking

def parse_csv():
    with open("Source_Data_v2.csv", "r") as my_file:
        # print csvReader.fieldnames
        csvReader = csv.DictReader(my_file)
        my_list = []
        for row in csvReader:
            entry = {}
            for fieldname in csvReader.fieldnames:
                entry[fieldname] = row[fieldname]
            my_list.append(entry)
    return my_list

# this line is what you use whenever you want your script to be run wihtout ipython.
# if you pull this code into an environment (ie import code.parse)
# it will not call this stuff,  because __name__ wont be __main__.
if __name__ == '__main__':
    grabbed_data = parse_csv()
    print "there are {} rows in my file".format(len(grabbed_data))
    print "there are {} columns in my file".format(len(grabbed_data[0].keys()))
    print "I was called as a script!!"
<|endoftext|>


<commit_before>from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.vector import Vector
from random import randint
from PongBall import PongBall
from PongPaddle import PongPaddle

class PongGame(Widget):

    ball = ObjectProperty(None)
    player_1 = ObjectProperty(None)
    player_2 = ObjectProperty(None)

    def serve_ball(self, vel):
        self.ball.center = self.center
        angle = randint(0, 45)
        angle *= 1 if randint(0,1)%2 == 0 else -1
        self.ball.velocity = Vector(*vel).rotate(angle)

    def on_touch_move(self, touch):

        def update_position(position, player):

            if position >= player.size[1]/2 \
                    and position <= self.top-player.size[1]/2:
                player.center_y = position

        if touch.x < self.width/3:
            update_position(touch.y, self.player_1)

        elif touch.x > (self.width*2)/3:
            update_position(touch.y, self.player_2)

    def update(self, dt):
        self.ball.move()
        self.player_1.bounce_ball(self.ball)
        self.player_2.bounce_ball(self.ball)

        if (self.ball.y < 0) or (self.ball.top > self.height):
            self.ball.velocity_y *= -1

        if self.ball.x > self.width:
            self.player_1.score += 1
            self.serve_ball(vel=(-10,0))

        elif self.ball.x < self.x:
            self.player_2.score += 1
            self.serve_ball(vel=(10,0))<commit_msg>code refactored on PongGame class, update function<commit_after>from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.vector import Vector
from random import randint
from PongBall import PongBall
from PongPaddle import PongPaddle

class PongGame(Widget):

    ball = ObjectProperty(None)
    player_1 = ObjectProperty(None)
    player_2 = ObjectProperty(None)

    def serve_ball(self, vel):
        self.ball.center = self.center
        angle = randint(0, 45)
        angle *= 1 if randint(0,1)%2 == 0 else -1
        self.ball.velocity = Vector(*vel).rotate(angle)

    def on_touch_move(self, touch):

        def update_position(position, player):

            if position >= player.size[1]/2 \
                    and position <= self.top-player.size[1]/2:
                player.center_y = position

        if touch.x < self.width/3:
            update_position(touch.y, self.player_1)

        elif touch.x > (self.width*2)/3:
            update_position(touch.y, self.player_2)

    def update(self, dt):
        self.ball.move()
        self.player_1.bounce_ball(self.ball)
        self.player_2.bounce_ball(self.ball)

        def update_states(player, velocity):
            player.score += 1
            self.serve_ball(velocity)

        if (self.ball.y < 0) or (self.ball.top > self.height):
            self.ball.velocity_y *= -1

        if self.ball.x > self.width:
            update_states(self.player_1, (-10, 0))

        elif self.ball.x < self.x:
            update_states(self.player_2, (10, 0))<|endoftext|>


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

# Copyright (c) 2013-2014 CoNWeT Lab., Universidad Politécnica de Madrid

# This file is part of Wirecloud.

# Wirecloud 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.

# Wirecloud 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 Wirecloud.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import, unicode_literals

import json

from django.utils import six
from django.utils.translation import ugettext as _

from wirecloud.commons.utils.template.base import is_valid_name, is_valid_vendor, is_valid_version, TemplateParseException
from wirecloud.commons.utils.translation import get_trans_index

class JSONTemplateParser(object):

    def __init__(self, template, base=None):

        self.base = base
        if isinstance(template, basestring):
            self._info = json.loads(template)
        elif isinstance(template, dict):
            self._info = template
        else:
            raise TemplateParseException('Invalid input data')

        if 'type' not in self._info:
            raise TemplateParseException(_('Missing resource type.'))

        if self._info['type'] not in ('widget', 'operator', 'mashup'):
            raise TemplateParseException(_('Invalid resource type: %s') % self._info['type'])

    def _check_array_fields(self, fields, place=None, required=False):

        if place is None:
            place = self._info

        for field in fields:
            if field not in place:
                if required:
                    raise TemplateParseException('Missing required field: %s' % field)

                place[field] = []
            elif not isinstance(place[field], (list, tuple)):
                raise TemplateParseException('An array value was expected for the %s field' % field)

    def _check_string_fields(self, fields, place=None, required=False):
        if place is None:
            place = self._info

        for field in fields:
            if field not in place:
                if required:
                    raise TemplateParseException('Missing required field: %s' % field)

                place[field] = ''
            elif not isinstance(place[field], six.string_types):
                raise TemplateParseException('A string value was expected for the %s field' % field)

    def _add_translation_index(self, value, **kwargs):
        index = get_trans_index(value)
        if not index:
            return

        if index not in self._info['translation_index_usage']:
            self._info['translation_index_usage'][index] = []

        self._info['translation_index_usage'][index].append(kwargs)

    def _init(self):

        self._check_string_fields(('title', 'description', 'email', 'authors', 'doc', 'image', 'smartphoneimage', 'license', 'licenseurl'))
        if self._info['type'] == 'widget':

            self._check_string_fields(('code_url',), required=True)
            self._check_array_fields(('preferences', 'properties'))

            if self._info.get('code_content_type', None) is None:
                self._info['code_content_type'] = 'text/html'

            if self._info.get('code_charset', None) is None:
                self._info['code_charset'] = 'utf-8'

        if not 'wiring' in self._info:
            self._info['wiring'] = {}

        self._check_array_fields(('inputs', 'outputs'), place=self._info['wiring'])

        # Translations
        self._info['translation_index_usage'] = {}
        if 'translations' not in self._info:
            self._info['translations'] = {}

        self._add_translation_index(self._info['title'], type='resource', field='title')
        self._add_translation_index(self._info['description'], type='resource', field='description')

        if self._info['type'] != 'mashup':
            for preference in self._info['preferences']:
                self._add_translation_index(preference['label'], type='vdef', variable=preference['name'], field='label')
                self._add_translation_index(preference['description'], type='vdef', variable=preference['name'], field='description')

                if preference['type'] == 'list':
                    for option_index, option in enumerate(preference['options']):
                        self._add_translation_index(option['label'], type='upo', variable=preference['name'], option=option_index)

            for prop in self._info['properties']:
                self._add_translation_index(prop['label'], type='vdef', variable=prop['name'], field='label')
                self._add_translation_index(prop['description'], type='vdef', variable=prop['name'], field='description')

            for input_endpoint in self._info['wiring']['inputs']:
                self._add_translation_index(input_endpoint['label'], type='inputendpoint', variable=input_endpoint['name'], field='label')
                self._add_translation_index(input_endpoint['description'], type='inputendpoint', variable=input_endpoint['name'], field='description')
                self._add_translation_index(input_endpoint['actionlabel'], type='inputendpoint', variable=input_endpoint['name'], field='actionlabel')

            for output_endpoint in self._info['wiring']['outputs']:
                self._add_translation_index(output_endpoint['label'], type='outputendpoint', variable=output_endpoint['name'], field='label')
                self._add_translation_index(output_endpoint['description'], type='outputendpoint', variable=output_endpoint['name'], field='description')

    def get_resource_type(self):
        return self._info['type']

    def get_resource_name(self):
        return self._info['name']

    def get_resource_vendor(self):
        return self._info['vendor']

    def get_resource_version(self):
        return self._info['version']

    def get_resource_info(self):

        if not is_valid_vendor(self._info['vendor']):
            raise TemplateParseException(_('ERROR: the format of the vendor is invalid.'))

        if not is_valid_name(self._info['name']):
            raise TemplateParseException(_('ERROR: the format of the name is invalid.'))

        if not is_valid_version(self._info['version']):
            raise TemplateParseException(_('ERROR: the format of the version number is invalid. Format: X.X.X where X is an integer. Ex. "0.1", "1.11" NOTE: "1.01" should be changed to "1.0.1" or "1.1"'))

        return dict(self._info)
<commit_msg>Fix bug in the json parser related to the changelog field<commit_after># -*- coding: utf-8 -*-

# Copyright (c) 2013-2014 CoNWeT Lab., Universidad Politécnica de Madrid

# This file is part of Wirecloud.

# Wirecloud 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.

# Wirecloud 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 Wirecloud.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import, unicode_literals

import json

from django.utils import six
from django.utils.translation import ugettext as _

from wirecloud.commons.utils.template.base import is_valid_name, is_valid_vendor, is_valid_version, TemplateParseException
from wirecloud.commons.utils.translation import get_trans_index

class JSONTemplateParser(object):

    def __init__(self, template, base=None):

        self.base = base
        if isinstance(template, basestring):
            self._info = json.loads(template)
        elif isinstance(template, dict):
            self._info = template
        else:
            raise TemplateParseException('Invalid input data')

        if 'type' not in self._info:
            raise TemplateParseException(_('Missing resource type.'))

        if self._info['type'] not in ('widget', 'operator', 'mashup'):
            raise TemplateParseException(_('Invalid resource type: %s') % self._info['type'])

    def _check_array_fields(self, fields, place=None, required=False):

        if place is None:
            place = self._info

        for field in fields:
            if field not in place:
                if required:
                    raise TemplateParseException('Missing required field: %s' % field)

                place[field] = []
            elif not isinstance(place[field], (list, tuple)):
                raise TemplateParseException('An array value was expected for the %s field' % field)

    def _check_string_fields(self, fields, place=None, required=False):
        if place is None:
            place = self._info

        for field in fields:
            if field not in place:
                if required:
                    raise TemplateParseException('Missing required field: %s' % field)

                place[field] = ''
            elif not isinstance(place[field], six.string_types):
                raise TemplateParseException('A string value was expected for the %s field' % field)

    def _add_translation_index(self, value, **kwargs):
        index = get_trans_index(value)
        if not index:
            return

        if index not in self._info['translation_index_usage']:
            self._info['translation_index_usage'][index] = []

        self._info['translation_index_usage'][index].append(kwargs)

    def _init(self):

        self._check_string_fields(('title', 'description', 'email', 'authors', 'doc', 'changelog', 'image', 'smartphoneimage', 'license', 'licenseurl'))
        if self._info['type'] == 'widget':

            self._check_string_fields(('code_url',), required=True)
            self._check_array_fields(('preferences', 'properties'))

            if self._info.get('code_content_type', None) is None:
                self._info['code_content_type'] = 'text/html'

            if self._info.get('code_charset', None) is None:
                self._info['code_charset'] = 'utf-8'

        if not 'wiring' in self._info:
            self._info['wiring'] = {}

        self._check_array_fields(('inputs', 'outputs'), place=self._info['wiring'])

        # Translations
        self._info['translation_index_usage'] = {}
        if 'translations' not in self._info:
            self._info['translations'] = {}

        self._add_translation_index(self._info['title'], type='resource', field='title')
        self._add_translation_index(self._info['description'], type='resource', field='description')

        if self._info['type'] != 'mashup':
            for preference in self._info['preferences']:
                self._add_translation_index(preference['label'], type='vdef', variable=preference['name'], field='label')
                self._add_translation_index(preference['description'], type='vdef', variable=preference['name'], field='description')

                if preference['type'] == 'list':
                    for option_index, option in enumerate(preference['options']):
                        self._add_translation_index(option['label'], type='upo', variable=preference['name'], option=option_index)

            for prop in self._info['properties']:
                self._add_translation_index(prop['label'], type='vdef', variable=prop['name'], field='label')
                self._add_translation_index(prop['description'], type='vdef', variable=prop['name'], field='description')

            for input_endpoint in self._info['wiring']['inputs']:
                self._add_translation_index(input_endpoint['label'], type='inputendpoint', variable=input_endpoint['name'], field='label')
                self._add_translation_index(input_endpoint['description'], type='inputendpoint', variable=input_endpoint['name'], field='description')
                self._add_translation_index(input_endpoint['actionlabel'], type='inputendpoint', variable=input_endpoint['name'], field='actionlabel')

            for output_endpoint in self._info['wiring']['outputs']:
                self._add_translation_index(output_endpoint['label'], type='outputendpoint', variable=output_endpoint['name'], field='label')
                self._add_translation_index(output_endpoint['description'], type='outputendpoint', variable=output_endpoint['name'], field='description')

    def get_resource_type(self):
        return self._info['type']

    def get_resource_name(self):
        return self._info['name']

    def get_resource_vendor(self):
        return self._info['vendor']

    def get_resource_version(self):
        return self._info['version']

    def get_resource_info(self):

        if not is_valid_vendor(self._info['vendor']):
            raise TemplateParseException(_('ERROR: the format of the vendor is invalid.'))

        if not is_valid_name(self._info['name']):
            raise TemplateParseException(_('ERROR: the format of the name is invalid.'))

        if not is_valid_version(self._info['version']):
            raise TemplateParseException(_('ERROR: the format of the version number is invalid. Format: X.X.X where X is an integer. Ex. "0.1", "1.11" NOTE: "1.01" should be changed to "1.0.1" or "1.1"'))

        return dict(self._info)
<|endoftext|>


<commit_before>import humanize

from PySide.QtGui import QWidget, QFormLayout, QLabel, QGroupBox, QVBoxLayout

from inselect.lib.utils import format_dt_display

from .toggle_widget_label import ToggleWidgetLabel

class BoldLabel(QLabel):
    """A label in a bold font
    """
    pass

class InfoWidget(QGroupBox):
    """Shows information about the document and the scanned image
    """

    STYLESHEET = """
    ToggleWidgetLabel {
        text-decoration: none;
        font-weight: bold;
        color: black;
    }

    BoldLabel {
        font-weight: bold;
    }
    """

    def __init__(self, parent=None):
        super(InfoWidget, self).__init__(parent)

        self.setStyleSheet(self.STYLESHEET)

        layout = QFormLayout()

        layout.addRow(BoldLabel('Document'))

        self._document_path = QLabel()
        layout.addRow('Name', self._document_path)

        self._created_by = QLabel()
        layout.addRow('Created by', self._created_by)

        self._created_on = QLabel()
        layout.addRow('Created on', self._created_on)

        self._last_saved_by = QLabel()
        layout.addRow('Last saved by', self._last_saved_by)

        self._last_saved_on = QLabel()
        layout.addRow('Last saved on', self._last_saved_on)

        layout.addRow(BoldLabel('Scanned image'))
        self._scanned_path = QLabel()
        layout.addRow('Name', self._scanned_path)

        self._scanned_size = QLabel()
        layout.addRow('Size', self._scanned_size)

        self._scanned_dimensions = QLabel()
        layout.addRow('Dimensions', self._scanned_dimensions)

        layout.addRow(BoldLabel('Thumbnail image'))
        self._thumbnail_path = QLabel()
        layout.addRow('Name', self._thumbnail_path)

        self._thumbnail_size = QLabel()
        layout.addRow('Size', self._thumbnail_size)

        self._thumbnail_dimensions = QLabel()
        layout.addRow('Dimensions', self._thumbnail_dimensions)

        labels_widget = QWidget()
        labels_widget.setLayout(layout)
        labels_widget.setVisible(False)

        # Show the labels about the toggle
        vlayout = QVBoxLayout()
        vlayout.addWidget(labels_widget)
        vlayout.addWidget(ToggleWidgetLabel('Information', labels_widget))

        self.setLayout(vlayout)

        #self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)

    def set_document(self, document):
        """Updates controls to reflect the document. Clears controls if
        document is None.
        """
        if document:
            self._document_path.setText(document.document_path.name)

            p = document.properties
            self._created_by.setText(p.get('Created by'))

            dt = p.get('Created on')
            self._created_on.setText(format_dt_display(dt) if dt else '')

            self._last_saved_by.setText(p.get('Saved by'))

            dt = p.get('Saved on')
            self._last_saved_on.setText(format_dt_display(dt) if dt else '')

            self._scanned_path.setText(document.scanned.path.name)
            self._scanned_size.setText(humanize.naturalsize(document.scanned.size_bytes))

            dim = '{0:,} x {1:,}'
            self._scanned_dimensions.setText(dim.format(*document.scanned.dimensions))

            # Thumbnail might not be present
            if document.thumbnail:
                self._thumbnail_path.setText(document.thumbnail.path.name)
                self._thumbnail_size.setText(humanize.naturalsize(document.thumbnail.size_bytes))
                self._thumbnail_dimensions.setText(dim.format(*document.thumbnail.dimensions))
            else:
                self._thumbnail_path.setText('')
                self._thumbnail_size.setText('')
                self._thumbnail_dimensions.setText('')
        else:
            self._document_path.setText('')
            self._created_by.setText('')
            self._created_on.setText('')
            self._last_saved_by.setText('')
            self._last_saved_on.setText('')
            self._scanned_path.setText('')
            self._scanned_size.setText('')
            self._scanned_dimensions.setText('')
            self._thumbnail_path.setText('')
            self._thumbnail_size.setText('')
            self._thumbnail_dimensions.setText('')
<commit_msg>Better prompts for image file name and size<commit_after>import humanize

from PySide.QtGui import QWidget, QFormLayout, QLabel, QGroupBox, QVBoxLayout

from inselect.lib.utils import format_dt_display

from .toggle_widget_label import ToggleWidgetLabel

class BoldLabel(QLabel):
    """A label in a bold font
    """
    pass

class InfoWidget(QGroupBox):
    """Shows information about the document and the scanned image
    """

    STYLESHEET = """
    ToggleWidgetLabel {
        text-decoration: none;
        font-weight: bold;
        color: black;
    }

    BoldLabel {
        font-weight: bold;
    }
    """

    def __init__(self, parent=None):
        super(InfoWidget, self).__init__(parent)

        self.setStyleSheet(self.STYLESHEET)

        layout = QFormLayout()

        layout.addRow(BoldLabel('Document'))

        self._document_path = QLabel()
        layout.addRow('Name', self._document_path)

        self._created_by = QLabel()
        layout.addRow('Created by', self._created_by)

        self._created_on = QLabel()
        layout.addRow('Created on', self._created_on)

        self._last_saved_by = QLabel()
        layout.addRow('Last saved by', self._last_saved_by)

        self._last_saved_on = QLabel()
        layout.addRow('Last saved on', self._last_saved_on)

        layout.addRow(BoldLabel('Scanned image'))
        self._scanned_path = QLabel()
        layout.addRow('File', self._scanned_path)

        self._scanned_size = QLabel()
        layout.addRow('File size', self._scanned_size)

        self._scanned_dimensions = QLabel()
        layout.addRow('Dimensions', self._scanned_dimensions)

        layout.addRow(BoldLabel('Thumbnail image'))
        self._thumbnail_path = QLabel()
        layout.addRow('File', self._thumbnail_path)

        self._thumbnail_size = QLabel()
        layout.addRow('File size', self._thumbnail_size)

        self._thumbnail_dimensions = QLabel()
        layout.addRow('Dimensions', self._thumbnail_dimensions)

        labels_widget = QWidget()
        labels_widget.setLayout(layout)
        labels_widget.setVisible(False)

        # Show the labels about the toggle
        vlayout = QVBoxLayout()
        vlayout.addWidget(labels_widget)
        vlayout.addWidget(ToggleWidgetLabel('Information', labels_widget))

        self.setLayout(vlayout)

        #self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)

    def set_document(self, document):
        """Updates controls to reflect the document. Clears controls if
        document is None.
        """
        if document:
            self._document_path.setText(document.document_path.name)

            p = document.properties
            self._created_by.setText(p.get('Created by'))

            dt = p.get('Created on')
            self._created_on.setText(format_dt_display(dt) if dt else '')

            self._last_saved_by.setText(p.get('Saved by'))

            dt = p.get('Saved on')
            self._last_saved_on.setText(format_dt_display(dt) if dt else '')

            self._scanned_path.setText(document.scanned.path.name)
            self._scanned_size.setText(humanize.naturalsize(document.scanned.size_bytes))

            dim = '{0:,} x {1:,}'
            self._scanned_dimensions.setText(dim.format(*document.scanned.dimensions))

            # Thumbnail might not be present
            if document.thumbnail:
                self._thumbnail_path.setText(document.thumbnail.path.name)
                self._thumbnail_size.setText(humanize.naturalsize(document.thumbnail.size_bytes))
                self._thumbnail_dimensions.setText(dim.format(*document.thumbnail.dimensions))
            else:
                self._thumbnail_path.setText('')
                self._thumbnail_size.setText('')
                self._thumbnail_dimensions.setText('')
        else:
            self._document_path.setText('')
            self._created_by.setText('')
            self._created_on.setText('')
            self._last_saved_by.setText('')
            self._last_saved_on.setText('')
            self._scanned_path.setText('')
            self._scanned_size.setText('')
            self._scanned_dimensions.setText('')
            self._thumbnail_path.setText('')
            self._thumbnail_size.setText('')
            self._thumbnail_dimensions.setText('')
<|endoftext|>


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

# Copyright 2013 Universidad Politécnica de Madrid

# This file is part of Wirecloud.

# Wirecloud is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# Wirecloud 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 General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with Wirecloud.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import
import json

def write_json_description(template_info):
    return json.dumps(template_info, sort_keys=True, indent=4)
<commit_msg>Remove translation_index_usage key when serializing template descriptions to json<commit_after># -*- coding: utf-8 -*-

# Copyright 2013 Universidad Politécnica de Madrid

# This file is part of Wirecloud.

# Wirecloud is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# Wirecloud 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 General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with Wirecloud.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import
import copy
import json

def write_json_description(template_info):
    template_info = copy.copy(template_info)
    del template_info['translation_index_usage']
    return json.dumps(template_info, sort_keys=True, indent=4)
<|endoftext|>


<commit_before>import logging

from insights import combiners, parsers, specs
from insights.core import dr, plugins

log = logging.getLogger(__name__)

def get_simple_module_name(obj):
    return dr.BASE_MODULE_NAMES.get(obj, None)

class Evaluator(object):

    def __init__(self, broker=None):
        self.broker = broker
        self.rule_skips = []
        self.rule_results = []
        self.hostname = None
        self.metadata = {}
        self.metadata_keys = {}

    def pre_process(self):
        pass

    def post_process(self):
        self.hostname = self.broker[combiners.hostname.hostname].fqdn

        for p, r in self.broker.items():
            if plugins.is_rule(p):
                self.handle_result(p, r)

    def run_components(self):
        dr.run(dr.COMPONENTS[dr.GROUPS.single], broker=self.broker)

    def format_response(self, response):
        """
        To be overridden by subclasses to format the response sent back to the
        client.
        """
        return response

    def format_result(self, result):
        """
        To be overridden by subclasses to format individual rule results.
        """
        return result

    def process(self):
        self.pre_process()
        self.run_components()
        self.post_process()
        return self.get_response()

class SingleEvaluator(Evaluator):

    def append_metadata(self, r):
        for k, v in r.items():
            if k != "type":
                self.metadata[k] = v

    def format_response(self, response):
        return response

    def get_response(self):
        r = dict(self.metadata_keys)
        r.update({
            "system": {
                "metadata": self.metadata,
                "hostname": self.hostname
            },
            "reports": self.rule_results,
            "skips": self.rule_skips,
        })
        return self.format_response(r)

    def handle_result(self, plugin, r):
        type_ = r["type"]
        if type_ == "metadata":
            self.append_metadata(r)
        elif type_ == "rule":
            self.rule_results.append(self.format_result({
                "rule_id": "{0}|{1}".format(get_simple_module_name(plugin), r["error_key"]),
                "details": r
            }))
        elif type_ == "skip":
            self.rule_skips.append(r)
        elif type_ == "metadata_key":
            self.metadata_keys[r["key"]] = r["value"]

class InsightsEvaluator(SingleEvaluator):

    def __init__(self, broker=None, system_id=None):
        super(InsightsEvaluator, self).__init__(broker)
        self.system_id = system_id
        self.branch_info = None
        self.product = "rhel"
        self.type = "host"

    def post_process(self):
        self.system_id = self.broker[specs.Specs.machine_id].content[0].strip()

        release = self.broker.get(specs.Specs.redhat_release)
        if release:
            self.release = release.content[0].strip()

        branch_info = self.broker.get(parsers.branch_info.BranchInfo)
        self.branch_info = branch_info.data if branch_info else {}

        md = self.broker.get("metadata.json")
        if md:
            self.product = md.get("product_code")
            self.type = md.get("role")

        super(InsightsEvaluator, self).post_process()

    def format_result(self, result):
        result["system_id"] = self.system_id
        return result

    def format_response(self, response):
        system = response["system"]
        system["remote_branch"] = self.branch_info.get("remote_branch")
        system["remote_leaf"] = self.branch_info.get("remote_leaf")
        system["system_id"] = self.system_id
        system["product"] = self.product
        system["type"] = self.type
        if self.release:
            system["metadata"]["release"] = self.release

        return response
<commit_msg>Make hostname optional on SingleEvaluator (#1105)<commit_after>import logging

from insights import combiners, parsers, specs
from insights.core import dr, plugins

log = logging.getLogger(__name__)

def get_simple_module_name(obj):
    return dr.BASE_MODULE_NAMES.get(obj, None)

class Evaluator(object):

    def __init__(self, broker=None):
        self.broker = broker
        self.rule_skips = []
        self.rule_results = []
        self.hostname = None
        self.metadata = {}
        self.metadata_keys = {}

    def pre_process(self):
        pass

    def post_process(self):
        if combiners.hostname.hostname in self.broker:
            self.hostname = self.broker[combiners.hostname.hostname].fqdn

        for p, r in self.broker.items():
            if plugins.is_rule(p):
                self.handle_result(p, r)

    def run_components(self):
        dr.run(dr.COMPONENTS[dr.GROUPS.single], broker=self.broker)

    def format_response(self, response):
        """
        To be overridden by subclasses to format the response sent back to the
        client.
        """
        return response

    def format_result(self, result):
        """
        To be overridden by subclasses to format individual rule results.
        """
        return result

    def process(self):
        self.pre_process()
        self.run_components()
        self.post_process()
        return self.get_response()

class SingleEvaluator(Evaluator):

    def append_metadata(self, r):
        for k, v in r.items():
            if k != "type":
                self.metadata[k] = v

    def format_response(self, response):
        return response

    def get_response(self):
        r = dict(self.metadata_keys)
        r.update({
            "system": {
                "metadata": self.metadata,
                "hostname": self.hostname
            },
            "reports": self.rule_results,
            "skips": self.rule_skips,
        })
        return self.format_response(r)

    def handle_result(self, plugin, r):
        type_ = r["type"]
        if type_ == "metadata":
            self.append_metadata(r)
        elif type_ == "rule":
            self.rule_results.append(self.format_result({
                "rule_id": "{0}|{1}".format(get_simple_module_name(plugin), r["error_key"]),
                "details": r
            }))
        elif type_ == "skip":
            self.rule_skips.append(r)
        elif type_ == "metadata_key":
            self.metadata_keys[r["key"]] = r["value"]

class InsightsEvaluator(SingleEvaluator):

    def __init__(self, broker=None, system_id=None):
        super(InsightsEvaluator, self).__init__(broker)
        self.system_id = system_id
        self.branch_info = None
        self.product = "rhel"
        self.type = "host"

    def post_process(self):
        self.system_id = self.broker[specs.Specs.machine_id].content[0].strip()

        release = self.broker.get(specs.Specs.redhat_release)
        if release:
            self.release = release.content[0].strip()

        branch_info = self.broker.get(parsers.branch_info.BranchInfo)
        self.branch_info = branch_info.data if branch_info else {}

        md = self.broker.get("metadata.json")
        if md:
            self.product = md.get("product_code")
            self.type = md.get("role")

        super(InsightsEvaluator, self).post_process()

    def format_result(self, result):
        result["system_id"] = self.system_id
        return result

    def format_response(self, response):
        system = response["system"]
        system["remote_branch"] = self.branch_info.get("remote_branch")
        system["remote_leaf"] = self.branch_info.get("remote_leaf")
        system["system_id"] = self.system_id
        system["product"] = self.product
        system["type"] = self.type
        if self.release:
            system["metadata"]["release"] = self.release

        return response
<|endoftext|>


<commit_before>
import os

kDirectory = "DIR"
kExitDirectory = "RID"
kMusic = "MP3"
kOtherFile = "ETC"

class FileSource(object):
   def __init__(self, base, others=None):
      '''
      >>> f = FileSource('/a/b/c', ['d', ' e ', '   ', 'g '])
      >>> f.base
      '/a/b/c'
      >>> f.others
      ['d', 'e', 'g']

      '''
      self.base = base
      if others is None:
         self.others = ['']
      else:
         self.others = []
         # get list of subdirectories from 'others' -- maybe it's a filename, maybe
         # it's a list of strings..
         if hasattr(others, "lower"):
            # it's  a string, so treat it as a filename
            with open(others, "rt") as f:
               for line in f:
                  # get rid of anything after a '#' comment
                  line = line.partition("#")[0]
                  # ..and whitespace
                  line = line.strip()
                  # if there's anything left, add it to the list.
                  if line:
                     self.others.append(line)
         else:
            # add that line to the list if stripping it doesn't result
            # in an empty string.
            self.others = [line.strip() for line in others if line.strip()]

   def GetDirectories(self):
      '''
      >>> f = FileSource('/a/b/c')
      >>> list(f.GetDirectories())
      [u'/a/b/c/']

      >>> f = FileSource('a/b', ['aa', 'bb'])
      >>> list(f.GetDirectories())
      [u'a/b/aa', u'a/b/bb']

      >>> f = FileSource('a/b', ['a  ', ' bb', 'c'])
      >>> list(f.GetDirectories())
      [u'a/b/a', u'a/b/bb', u'a/b/c']

      '''
      for subdir in self.others:
         yield os.path.join(self.base, subdir.decode('utf-8'))

   def GetFiles(self):
      '''
         Walk through all of the directories that we should be looking at. if
         any of them contain files, yield those files back to whoever
         called us, one at a time.
      '''

      def Spelunk(d, visited):
         ''' generator that recursively walks down into directory paths and only
            yields the ExitDirectory message when we're done processing all of
            its children.

            We pass in a set that collects diretories that we've processed to avoid
            accidentally visiting the same directory more than once.

            `d` is the directory that we're looking at
            `visited` is a set object that holds the full path of all the directories that
               we've looked at (prevent processing the same directory 2x)
         '''
         #assert os.path.isdir(d), "{0} isn't a directory!".format(d)
         if not os.path.isdir(d):
            print "{0} isn't a directory!".format(d).encode("utf-8")
            assert False

         dirs = []
         files = []
         # notify that we're entering a directory.
         yield (kDirectory, d)
         # ...then look at all of the items that are in this directory, sorting into
         # two lists -- files and directories.
         for item in os.listdir(d):
            fullpath = os.path.join(d, item)
            if os.path.isdir(fullpath):
               if fullpath not in visited:
                  dirs.append(fullpath)
                  visited.add(fullpath)
            else:
               files.append(fullpath)

         otherFiles = []
         files.sort()
         # first, separate the files in this directory into MP3 files and everything else.
         # We'll yield back the music files first, and then everything else.
         for f in files:
            base, ext = os.path.splitext(f)
            if ext.lower() in (u".mp3",):
               yield (kMusic, f)
            else:
               otherFiles.append(f)
         # we've yielded all the MP3 files, now yield back anything else.
         for f in otherFiles:
            yield (kOtherFile, f)

         # now spelunk into any subdiretories...
         dirs.sort()
         for sub in dirs:
            # if this were Python 3.5+, we could instead use the cooler
            # `yield from` syntax here.
            for t, f in Spelunk(sub, visited):
               yield (t,f)

         # let them know that we're done processing this directory.
         yield (kExitDirectory, d)

      visited = set()
      dirList = self.GetDirectories()
      for d in dirList:
         for (t, f) in Spelunk(d, visited):
            yield (t, f)

   def __iter__(self):
      return self.GetFiles()

if __name__ == "__main__":
   import doctest
   doctest.testmod()<commit_msg>(trivial white space)<commit_after>
import os

kDirectory = "DIR"
kExitDirectory = "RID"
kMusic = "MP3"
kOtherFile = "ETC"

class FileSource(object):
   def __init__(self, base, others=None):
      '''
      >>> f = FileSource('/a/b/c', ['d', ' e ', '   ', 'g '])
      >>> f.base
      '/a/b/c'
      >>> f.others
      ['d', 'e', 'g']

      '''
      self.base = base
      if others is None:
         self.others = ['']
      else:
         self.others = []
         # get list of subdirectories from 'others' -- maybe it's a filename, maybe
         # it's a list of strings..
         if hasattr(others, "lower"):
            # it's  a string, so treat it as a filename
            with open(others, "rt") as f:
               for line in f:
                  # get rid of anything after a '#' comment
                  line = line.partition("#")[0]
                  # ..and whitespace
                  line = line.strip()
                  # if there's anything left, add it to the list.
                  if line:
                     self.others.append(line)
         else:
            # add that line to the list if stripping it doesn't result
            # in an empty string.
            self.others = [line.strip() for line in others if line.strip()]

   def GetDirectories(self):
      '''
      >>> f = FileSource('/a/b/c')
      >>> list(f.GetDirectories())
      [u'/a/b/c/']

      >>> f = FileSource('a/b', ['aa', 'bb'])
      >>> list(f.GetDirectories())
      [u'a/b/aa', u'a/b/bb']

      >>> f = FileSource('a/b', ['a  ', ' bb', 'c'])
      >>> list(f.GetDirectories())
      [u'a/b/a', u'a/b/bb', u'a/b/c']

      '''
      for subdir in self.others:
         yield os.path.join(self.base, subdir.decode('utf-8'))

   def GetFiles(self):
      '''
         Walk through all of the directories that we should be looking at. if
         any of them contain files, yield those files back to whoever
         called us, one at a time.
      '''

      def Spelunk(d, visited):
         ''' generator that recursively walks down into directory paths and only
            yields the ExitDirectory message when we're done processing all of
            its children.

            We pass in a set that collects diretories that we've processed to avoid
            accidentally visiting the same directory more than once.

            `d` is the directory that we're looking at
            `visited` is a set object that holds the full path of all the directories that
               we've looked at (prevent processing the same directory 2x)
         '''
         #assert os.path.isdir(d), "{0} isn't a directory!".format(d)
         if not os.path.isdir(d):
            print "{0} isn't a directory!".format(d).encode("utf-8")
            assert False

         dirs = []
         files = []
         # notify that we're entering a directory.
         yield (kDirectory, d)
         # ...then look at all of the items that are in this directory, sorting into
         # two lists -- files and directories.
         for item in os.listdir(d):
            fullpath = os.path.join(d, item)
            if os.path.isdir(fullpath):
               if fullpath not in visited:
                  dirs.append(fullpath)
                  visited.add(fullpath)
            else:
               files.append(fullpath)

         otherFiles = []
         files.sort()
         # first, separate the files in this directory into MP3 files and everything else.
         # We'll yield back the music files first, and then everything else.
         for f in files:
            base, ext = os.path.splitext(f)
            if ext.lower() in (u".mp3",):
               yield (kMusic, f)
            else:
               otherFiles.append(f)
         # we've yielded all the MP3 files, now yield back anything else.
         for f in otherFiles:
            yield (kOtherFile, f)

         # now spelunk into any subdiretories...
         dirs.sort()
         for sub in dirs:
            # if this were Python 3.5+, we could instead use the cooler
            # `yield from` syntax here.
            for t, f in Spelunk(sub, visited):
               yield (t,f)

         # let them know that we're done processing this directory.
         yield (kExitDirectory, d)

      visited = set()
      dirList = self.GetDirectories()
      for d in dirList:
         for (t, f) in Spelunk(d, visited):
            yield (t, f)

   def __iter__(self):
      return self.GetFiles()

if __name__ == "__main__":
   import doctest
   doctest.testmod()<|endoftext|>