<commit_before>"""
Extended SymPy hierarchy.
"""

import numpy as np
import sympy
from sympy import Expr, Integer, Float, Function, Symbol
from sympy.core.basic import _aresame
from sympy.functions.elementary.trigonometric import TrigonometricFunction

from devito.tools import Pickable, as_tuple

__all__ = ['FrozenExpr', 'Eq', 'CondEq', 'CondNe', 'Mul', 'Add', 'Pow', 'IntDiv',
           'FunctionFromPointer', 'FieldFromPointer', 'ExternalFunctionCall',
           'FieldFromComposite', 'ListInitializer', 'Byref', 'IndexedPointer',
           'Macro', 'Literal', 'taylor_sin', 'taylor_cos', 'bhaskara_sin',
           'bhaskara_cos', 'INT', 'FLOAT', 'DOUBLE', 'FLOOR', 'cast_mapper']

class FrozenExpr(Expr):

    """
    Use FrozenExpr in place of sympy.Expr to make sure than an e an expression
    is no longer transformable; that is, standard manipulations such as
    xreplace, collect, expand, ... have no effect, thus building a new
    expression identical to self.

    Notes
    -----
    At the moment, only xreplace is overridded (to prevent unpicking factorizations)
    """

    def xreplace(self, rule):
        if self in rule:
            return rule[self]
        elif rule:
            args = []
            for a in self.args:
                try:
                    args.append(a.xreplace(rule))
                except AttributeError:
                    args.append(a)
            args = tuple(args)
            if not _aresame(args, self.args):
                return self.func(*args, evaluate=False)
        return self

    def evalf(self, *args, **kwargs):
        return self

class Eq(sympy.Eq, FrozenExpr):

    """A customized version of sympy.Eq which suppresses evaluation."""

    def __new__(cls, *args, **kwargs):
        return sympy.Eq.__new__(cls, *args, evaluate=False)

class CondEq(sympy.Eq, FrozenExpr):

    """
    A customized version of sympy.Eq representing a conditional equality.
    It suppresses evaluation.
    """

    def __new__(cls, *args, **kwargs):
        return sympy.Eq.__new__(cls, *args, evaluate=False)

    @property
    def canonical(self):
        return self

    @property
    def negated(self):
        return CondNe(*self.args, evaluate=False)

class CondNe(sympy.Ne, FrozenExpr):

    """
    A customized version of sympy.Ne representing a conditional inequality.
    It suppresses evaluation.
    """

    def __new__(cls, *args, **kwargs):
        return sympy.Ne.__new__(cls, *args, evaluate=False)

    @property
    def canonical(self):
        return self

    @property
    def negated(self):
        return CondEq(*self.args, evaluate=False)

class Mul(sympy.Mul, FrozenExpr):
    def __new__(cls, *args, **kwargs):
        return sympy.Mul.__new__(cls, *args, evaluate=False)

class Add(sympy.Add, FrozenExpr):
    def __new__(cls, *args, **kwargs):
        return sympy.Add.__new__(cls, *args, evaluate=False)

class Pow(sympy.Pow, FrozenExpr):
    def __new__(cls, *args, **kwargs):
        return sympy.Pow.__new__(cls, *args, evaluate=False)

class IntDiv(sympy.Expr):

    """
    A support type for integer division. Should only be used by the compiler
    for code generation purposes (i.e., not for symbolic manipulation).
    This works around the annoying way SymPy represents integer division,
    namely as a ``Mul`` between the numerator and the reciprocal of the
    denominator (e.g., ``a*3.args -> (a, 1/3)), which ends up generating
    "weird" C code.
    """

    is_Atom = True

    def __new__(cls, lhs, rhs, params=None):
        try:
            rhs = Integer(rhs)
            if rhs == 0:
                raise ValueError("Cannot divide by 0")
            elif rhs == 1:
                return lhs
        except TypeError:
            # We must be sure the symbolic RHS is of type int
            if not hasattr(rhs, 'dtype'):
                raise ValueError("Symbolic RHS `%s` lacks dtype" % rhs)
            if not issubclass(rhs.dtype, np.integer):
                raise ValueError("Symbolic RHS `%s` must be of type `int`, found "
                                 "`%s` instead" % (rhs, rhs.dtype))
        obj = sympy.Expr.__new__(cls, lhs, rhs)
        obj.lhs = lhs
        obj.rhs = rhs
        return obj

    def __str__(self):
        return "%s / %s" % (self.lhs, self.rhs)

    __repr__ = __str__

class FunctionFromPointer(sympy.Expr, Pickable):

    """
    Symbolic representation of the C notation ``pointer->function(params)``.
    """

    def __new__(cls, function, pointer, params=None):
        args = []
        if isinstance(pointer, str):
            pointer = Symbol(pointer)
        args.append(pointer)
        if isinstance(function, FunctionFromPointer):
            args.append(function)
        elif not isinstance(function, str):
            raise ValueError("`function` must be FunctionFromPointer or str")
        _params = []
        for p in as_tuple(params):
            if isinstance(p, str):
                _params.append(Symbol(p))
            elif not isinstance(p, Expr):
                raise ValueError("`params` must be an iterable of Expr or str")
            else:
                _params.append(p)
        args.extend(_params)
        obj = sympy.Expr.__new__(cls, *args)
        obj.function = function
        obj.pointer = pointer
        obj.params = tuple(_params)
        return obj

    def __str__(self):
        return '%s->%s(%s)' % (self.pointer, self.function,
                               ", ".join(str(i) for i in as_tuple(self.params)))

    __repr__ = __str__

    def _hashable_content(self):
        return super(FunctionFromPointer, self)._hashable_content() +\
            (self.function, self.pointer) + self.params

    @property
    def base(self):
        if isinstance(self.pointer, FunctionFromPointer):
            # FunctionFromPointer may be nested
            return self.pointer.base
        else:
            return self.pointer

    # Pickling support
    _pickle_args = ['function', 'pointer']
    _pickle_kwargs = ['params']
    __reduce_ex__ = Pickable.__reduce_ex__

class ExternalFunctionCall(sympy.Expr, Pickable):

    """
    Symbolic representation of the C notation ``pointer->function(params)``.
    """

    def __new__(cls, function, params=None):
        args = []
        if isinstance(function, ExternalFunctionCall):
            args.append(function)
        elif not isinstance(function, str):
            raise ValueError("`function` must be FunctionFromPointer or str")
        _params = []
        for p in as_tuple(params):
            if isinstance(p, str):
                _params.append(Symbol(p))
            elif isinstance(p, int):
                _params.append(Integer(p))
            elif not isinstance(p, Expr):
                raise ValueError("`params` must be an iterable of Expr, str or int")
            else:
                _params.append(p)
        args.extend(_params)
        obj = sympy.Expr.__new__(cls, *args)
        obj.function = function
        obj.params = tuple(_params)
        return obj

    def __str__(self):
        return '%s(%s)' % (self.function, ",".join(str(i) for i in as_tuple(self.params)))

    __repr__ = __str__

    def _hashable_content(self):
        return super()._hashable_content() + (self.function,) + self.params

    @property
    def base(self):
        return self

    # Pickling support
    _pickle_args = ['function']
    _pickle_kwargs = ['params']
    __reduce_ex__ = Pickable.__reduce_ex__

class FieldFromPointer(FunctionFromPointer, Pickable):

    """
    Symbolic representation of the C notation ``pointer->field``.
    """

    def __new__(cls, field, pointer):
        return FunctionFromPointer.__new__(cls, field, pointer)

    def __str__(self):
        return '%s->%s' % (self.pointer, self.field)

    @property
    def field(self):
        return self.function

    # Our __new__ cannot accept the params argument
    _pickle_kwargs = []

    __repr__ = __str__

class FieldFromComposite(FunctionFromPointer, Pickable):

    """
    Symbolic representation of the C notation ``composite.field``,
    where ``composite`` is a struct/union/...
    """

    def __new__(cls, field, composite):
        return FunctionFromPointer.__new__(cls, field, composite)

    def __str__(self):
        return '%s.%s' % (self.composite, self.field)

    @property
    def field(self):
        return self.function

    @property
    def composite(self):
        return self.pointer

    # Our __new__ cannot accept the params argument
    _pickle_kwargs = []

    __repr__ = __str__

class ListInitializer(sympy.Expr, Pickable):

    """
    Symbolic representation of the C++ list initializer notation ``{a, b, ...}``.
    """

    def __new__(cls, params):
        args = []
        for p in as_tuple(params):
            if isinstance(p, str):
                args.append(Symbol(p))
            elif isinstance(p, int):
                args.append(p)
            elif not isinstance(p, Expr):
                raise ValueError("`params` must be an iterable of Expr or str")
            else:
                args.append(p)
        obj = sympy.Expr.__new__(cls, *args)
        obj.params = tuple(args)
        return obj

    def __str__(self):
        return "{%s}" % ", ".join(str(i) for i in self.params)

    __repr__ = __str__

    # Pickling support
    _pickle_args = ['params']
    __reduce_ex__ = Pickable.__reduce_ex__

class Byref(sympy.Expr, Pickable):

    """
    Symbolic representation of the C notation ``&symbol``.
    """

    def __new__(cls, base):
        if isinstance(base, str):
            base = Symbol(base)
        elif not isinstance(base, sympy.Expr):
            raise ValueError("`base` must be sympy.Expr or str")
        obj = sympy.Expr.__new__(cls, base)
        obj._base = base
        return obj

    @property
    def base(self):
        return self._base

    def __str__(self):
        if self.base.is_Symbol:
            return "&%s" % self.base
        else:
            return "&(%s)" % self.base

    __repr__ = __str__

    # Pickling support
    _pickle_args = ['base']
    __reduce_ex__ = Pickable.__reduce_ex__

class IndexedPointer(sympy.Expr):

    """
    Symbolic representation of the C notation ``symbol[...]``

    Unlike a sympy.Indexed, an IndexedPointer accepts, as base, objects that
    are not necessarily a Symbol or an IndexedBase, such as a FieldFromPointer.
    """

    def __new__(cls, base, index):
        if isinstance(base, (str, sympy.IndexedBase, sympy.Symbol)):
            return sympy.Indexed(base, index)
        elif not isinstance(base, sympy.Basic):
            raise ValueError("`base` must be of type sympy.Basic")
        obj = sympy.Expr.__new__(cls, base)
        obj._base = base
        obj._index = as_tuple(index)
        return obj

    @property
    def base(self):
        return self._base

    @property
    def index(self):
        return self._index

    def __str__(self):
        return "%s%s" % (self.base, ''.join('[%s]' % i for i in self.index))

    __repr__ = __str__

    # Pickling support
    _pickle_args = ['base', 'index']
    __reduce_ex__ = Pickable.__reduce_ex__

class Macro(sympy.Symbol):

    """
    Symbolic representation of a C macro.
    """
    pass

class Literal(sympy.Symbol):

    """
    Symbolic representation of a Literal element.
    """
    pass

INT = Function('INT')
FLOAT = Function('FLOAT')
DOUBLE = Function('DOUBLE')
FLOOR = Function('floor')

cast_mapper = {np.float32: FLOAT, float: DOUBLE, np.float64: DOUBLE}

class taylor_sin(TrigonometricFunction):

    """
    Approximation of the sine function using a Taylor polynomial.
    """

    @classmethod
    def eval(cls, arg):
        return eval_taylor_sin(arg)

class taylor_cos(TrigonometricFunction):

    """
    Approximation of the cosine function using a Taylor polynomial.
    """

    @classmethod
    def eval(cls, arg):
        return 1.0 if arg == 0.0 else eval_taylor_cos(arg)

class bhaskara_sin(TrigonometricFunction):

    """
    Approximation of the sine function using a Bhaskara polynomial.
    """

    @classmethod
    def eval(cls, arg):
        return eval_bhaskara_sin(arg)

class bhaskara_cos(TrigonometricFunction):

    """
    Approximation of the cosine function using a Bhaskara polynomial.
    """

    @classmethod
    def eval(cls, arg):
        return 1.0 if arg == 0.0 else eval_bhaskara_sin(arg + 1.5708)

# Utils

def eval_bhaskara_sin(expr):
    return 16.0*expr*(3.1416-abs(expr))/(49.3483-4.0*abs(expr)*(3.1416-abs(expr)))

def eval_taylor_sin(expr):
    v = expr + Mul(-1/6.0,
                   Mul(expr, expr, expr, evaluate=False),
                   1.0 + Mul(Mul(expr, expr, evaluate=False), -0.05, evaluate=False),
                   evaluate=False)
    try:
        Float(expr)
        return v.doit()
    except (TypeError, ValueError):
        return v

def eval_taylor_cos(expr):
    v = 1.0 + Mul(-0.5,
                  Mul(expr, expr, evaluate=False),
                  1.0 + Mul(expr, expr, -1/12.0, evaluate=False),
                  evaluate=False)
    try:
        Float(expr)
        return v.doit()
    except (TypeError, ValueError):
        return v
<commit_msg>updated comment in ExternalFunctionCall<commit_after>"""
Extended SymPy hierarchy.
"""

import numpy as np
import sympy
from sympy import Expr, Integer, Float, Function, Symbol
from sympy.core.basic import _aresame
from sympy.functions.elementary.trigonometric import TrigonometricFunction

from devito.tools import Pickable, as_tuple

__all__ = ['FrozenExpr', 'Eq', 'CondEq', 'CondNe', 'Mul', 'Add', 'Pow', 'IntDiv',
           'FunctionFromPointer', 'FieldFromPointer', 'ExternalFunctionCall',
           'FieldFromComposite', 'ListInitializer', 'Byref', 'IndexedPointer',
           'Macro', 'Literal', 'taylor_sin', 'taylor_cos', 'bhaskara_sin',
           'bhaskara_cos', 'INT', 'FLOAT', 'DOUBLE', 'FLOOR', 'cast_mapper']

class FrozenExpr(Expr):

    """
    Use FrozenExpr in place of sympy.Expr to make sure than an e an expression
    is no longer transformable; that is, standard manipulations such as
    xreplace, collect, expand, ... have no effect, thus building a new
    expression identical to self.

    Notes
    -----
    At the moment, only xreplace is overridded (to prevent unpicking factorizations)
    """

    def xreplace(self, rule):
        if self in rule:
            return rule[self]
        elif rule:
            args = []
            for a in self.args:
                try:
                    args.append(a.xreplace(rule))
                except AttributeError:
                    args.append(a)
            args = tuple(args)
            if not _aresame(args, self.args):
                return self.func(*args, evaluate=False)
        return self

    def evalf(self, *args, **kwargs):
        return self

class Eq(sympy.Eq, FrozenExpr):

    """A customized version of sympy.Eq which suppresses evaluation."""

    def __new__(cls, *args, **kwargs):
        return sympy.Eq.__new__(cls, *args, evaluate=False)

class CondEq(sympy.Eq, FrozenExpr):

    """
    A customized version of sympy.Eq representing a conditional equality.
    It suppresses evaluation.
    """

    def __new__(cls, *args, **kwargs):
        return sympy.Eq.__new__(cls, *args, evaluate=False)

    @property
    def canonical(self):
        return self

    @property
    def negated(self):
        return CondNe(*self.args, evaluate=False)

class CondNe(sympy.Ne, FrozenExpr):

    """
    A customized version of sympy.Ne representing a conditional inequality.
    It suppresses evaluation.
    """

    def __new__(cls, *args, **kwargs):
        return sympy.Ne.__new__(cls, *args, evaluate=False)

    @property
    def canonical(self):
        return self

    @property
    def negated(self):
        return CondEq(*self.args, evaluate=False)

class Mul(sympy.Mul, FrozenExpr):
    def __new__(cls, *args, **kwargs):
        return sympy.Mul.__new__(cls, *args, evaluate=False)

class Add(sympy.Add, FrozenExpr):
    def __new__(cls, *args, **kwargs):
        return sympy.Add.__new__(cls, *args, evaluate=False)

class Pow(sympy.Pow, FrozenExpr):
    def __new__(cls, *args, **kwargs):
        return sympy.Pow.__new__(cls, *args, evaluate=False)

class IntDiv(sympy.Expr):

    """
    A support type for integer division. Should only be used by the compiler
    for code generation purposes (i.e., not for symbolic manipulation).
    This works around the annoying way SymPy represents integer division,
    namely as a ``Mul`` between the numerator and the reciprocal of the
    denominator (e.g., ``a*3.args -> (a, 1/3)), which ends up generating
    "weird" C code.
    """

    is_Atom = True

    def __new__(cls, lhs, rhs, params=None):
        try:
            rhs = Integer(rhs)
            if rhs == 0:
                raise ValueError("Cannot divide by 0")
            elif rhs == 1:
                return lhs
        except TypeError:
            # We must be sure the symbolic RHS is of type int
            if not hasattr(rhs, 'dtype'):
                raise ValueError("Symbolic RHS `%s` lacks dtype" % rhs)
            if not issubclass(rhs.dtype, np.integer):
                raise ValueError("Symbolic RHS `%s` must be of type `int`, found "
                                 "`%s` instead" % (rhs, rhs.dtype))
        obj = sympy.Expr.__new__(cls, lhs, rhs)
        obj.lhs = lhs
        obj.rhs = rhs
        return obj

    def __str__(self):
        return "%s / %s" % (self.lhs, self.rhs)

    __repr__ = __str__

class FunctionFromPointer(sympy.Expr, Pickable):

    """
    Symbolic representation of the C notation ``pointer->function(params)``.
    """

    def __new__(cls, function, pointer, params=None):
        args = []
        if isinstance(pointer, str):
            pointer = Symbol(pointer)
        args.append(pointer)
        if isinstance(function, FunctionFromPointer):
            args.append(function)
        elif not isinstance(function, str):
            raise ValueError("`function` must be FunctionFromPointer or str")
        _params = []
        for p in as_tuple(params):
            if isinstance(p, str):
                _params.append(Symbol(p))
            elif not isinstance(p, Expr):
                raise ValueError("`params` must be an iterable of Expr or str")
            else:
                _params.append(p)
        args.extend(_params)
        obj = sympy.Expr.__new__(cls, *args)
        obj.function = function
        obj.pointer = pointer
        obj.params = tuple(_params)
        return obj

    def __str__(self):
        return '%s->%s(%s)' % (self.pointer, self.function,
                               ", ".join(str(i) for i in as_tuple(self.params)))

    __repr__ = __str__

    def _hashable_content(self):
        return super(FunctionFromPointer, self)._hashable_content() +\
            (self.function, self.pointer) + self.params

    @property
    def base(self):
        if isinstance(self.pointer, FunctionFromPointer):
            # FunctionFromPointer may be nested
            return self.pointer.base
        else:
            return self.pointer

    # Pickling support
    _pickle_args = ['function', 'pointer']
    _pickle_kwargs = ['params']
    __reduce_ex__ = Pickable.__reduce_ex__

class ExternalFunctionCall(sympy.Expr, Pickable):

    """
    Symbolic representation of the C notation ``function(params)``.
    """

    def __new__(cls, function, params=None):
        args = []
        if isinstance(function, ExternalFunctionCall):
            args.append(function)
        elif not isinstance(function, str):
            raise ValueError("`function` must be FunctionFromPointer or str")
        _params = []
        for p in as_tuple(params):
            if isinstance(p, str):
                _params.append(Symbol(p))
            elif isinstance(p, int):
                _params.append(Integer(p))
            elif not isinstance(p, Expr):
                raise ValueError("`params` must be an iterable of Expr, str or int")
            else:
                _params.append(p)
        args.extend(_params)
        obj = sympy.Expr.__new__(cls, *args)
        obj.function = function
        obj.params = tuple(_params)
        return obj

    def __str__(self):
        return '%s(%s)' % (self.function, ",".join(str(i) for i in as_tuple(self.params)))

    __repr__ = __str__

    def _hashable_content(self):
        return super()._hashable_content() + (self.function,) + self.params

    @property
    def base(self):
        return self

    # Pickling support
    _pickle_args = ['function']
    _pickle_kwargs = ['params']
    __reduce_ex__ = Pickable.__reduce_ex__

class FieldFromPointer(FunctionFromPointer, Pickable):

    """
    Symbolic representation of the C notation ``pointer->field``.
    """

    def __new__(cls, field, pointer):
        return FunctionFromPointer.__new__(cls, field, pointer)

    def __str__(self):
        return '%s->%s' % (self.pointer, self.field)

    @property
    def field(self):
        return self.function

    # Our __new__ cannot accept the params argument
    _pickle_kwargs = []

    __repr__ = __str__

class FieldFromComposite(FunctionFromPointer, Pickable):

    """
    Symbolic representation of the C notation ``composite.field``,
    where ``composite`` is a struct/union/...
    """

    def __new__(cls, field, composite):
        return FunctionFromPointer.__new__(cls, field, composite)

    def __str__(self):
        return '%s.%s' % (self.composite, self.field)

    @property
    def field(self):
        return self.function

    @property
    def composite(self):
        return self.pointer

    # Our __new__ cannot accept the params argument
    _pickle_kwargs = []

    __repr__ = __str__

class ListInitializer(sympy.Expr, Pickable):

    """
    Symbolic representation of the C++ list initializer notation ``{a, b, ...}``.
    """

    def __new__(cls, params):
        args = []
        for p in as_tuple(params):
            if isinstance(p, str):
                args.append(Symbol(p))
            elif isinstance(p, int):
                args.append(p)
            elif not isinstance(p, Expr):
                raise ValueError("`params` must be an iterable of Expr or str")
            else:
                args.append(p)
        obj = sympy.Expr.__new__(cls, *args)
        obj.params = tuple(args)
        return obj

    def __str__(self):
        return "{%s}" % ", ".join(str(i) for i in self.params)

    __repr__ = __str__

    # Pickling support
    _pickle_args = ['params']
    __reduce_ex__ = Pickable.__reduce_ex__

class Byref(sympy.Expr, Pickable):

    """
    Symbolic representation of the C notation ``&symbol``.
    """

    def __new__(cls, base):
        if isinstance(base, str):
            base = Symbol(base)
        elif not isinstance(base, sympy.Expr):
            raise ValueError("`base` must be sympy.Expr or str")
        obj = sympy.Expr.__new__(cls, base)
        obj._base = base
        return obj

    @property
    def base(self):
        return self._base

    def __str__(self):
        if self.base.is_Symbol:
            return "&%s" % self.base
        else:
            return "&(%s)" % self.base

    __repr__ = __str__

    # Pickling support
    _pickle_args = ['base']
    __reduce_ex__ = Pickable.__reduce_ex__

class IndexedPointer(sympy.Expr):

    """
    Symbolic representation of the C notation ``symbol[...]``

    Unlike a sympy.Indexed, an IndexedPointer accepts, as base, objects that
    are not necessarily a Symbol or an IndexedBase, such as a FieldFromPointer.
    """

    def __new__(cls, base, index):
        if isinstance(base, (str, sympy.IndexedBase, sympy.Symbol)):
            return sympy.Indexed(base, index)
        elif not isinstance(base, sympy.Basic):
            raise ValueError("`base` must be of type sympy.Basic")
        obj = sympy.Expr.__new__(cls, base)
        obj._base = base
        obj._index = as_tuple(index)
        return obj

    @property
    def base(self):
        return self._base

    @property
    def index(self):
        return self._index

    def __str__(self):
        return "%s%s" % (self.base, ''.join('[%s]' % i for i in self.index))

    __repr__ = __str__

    # Pickling support
    _pickle_args = ['base', 'index']
    __reduce_ex__ = Pickable.__reduce_ex__

class Macro(sympy.Symbol):

    """
    Symbolic representation of a C macro.
    """
    pass

class Literal(sympy.Symbol):

    """
    Symbolic representation of a Literal element.
    """
    pass

INT = Function('INT')
FLOAT = Function('FLOAT')
DOUBLE = Function('DOUBLE')
FLOOR = Function('floor')

cast_mapper = {np.float32: FLOAT, float: DOUBLE, np.float64: DOUBLE}

class taylor_sin(TrigonometricFunction):

    """
    Approximation of the sine function using a Taylor polynomial.
    """

    @classmethod
    def eval(cls, arg):
        return eval_taylor_sin(arg)

class taylor_cos(TrigonometricFunction):

    """
    Approximation of the cosine function using a Taylor polynomial.
    """

    @classmethod
    def eval(cls, arg):
        return 1.0 if arg == 0.0 else eval_taylor_cos(arg)

class bhaskara_sin(TrigonometricFunction):

    """
    Approximation of the sine function using a Bhaskara polynomial.
    """

    @classmethod
    def eval(cls, arg):
        return eval_bhaskara_sin(arg)

class bhaskara_cos(TrigonometricFunction):

    """
    Approximation of the cosine function using a Bhaskara polynomial.
    """

    @classmethod
    def eval(cls, arg):
        return 1.0 if arg == 0.0 else eval_bhaskara_sin(arg + 1.5708)

# Utils

def eval_bhaskara_sin(expr):
    return 16.0*expr*(3.1416-abs(expr))/(49.3483-4.0*abs(expr)*(3.1416-abs(expr)))

def eval_taylor_sin(expr):
    v = expr + Mul(-1/6.0,
                   Mul(expr, expr, expr, evaluate=False),
                   1.0 + Mul(Mul(expr, expr, evaluate=False), -0.05, evaluate=False),
                   evaluate=False)
    try:
        Float(expr)
        return v.doit()
    except (TypeError, ValueError):
        return v

def eval_taylor_cos(expr):
    v = 1.0 + Mul(-0.5,
                  Mul(expr, expr, evaluate=False),
                  1.0 + Mul(expr, expr, -1/12.0, evaluate=False),
                  evaluate=False)
    try:
        Float(expr)
        return v.doit()
    except (TypeError, ValueError):
        return v
<|endoftext|>


<commit_before>from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.showbase.PhysicsManagerGlobal import *

from direct.directnotify import DirectNotifyGlobal
import sys

class ForceGroup(DirectObject):

    notify = DirectNotifyGlobal.directNotify.newCategory('ForceGroup')
    id = 1

    def __init__(self, name=None):
        if (name == None):
            self.name = 'ForceGroup-%d' % ForceGroup.id
            ForceGroup.id += 1
        else:
            self.name = name

        self.node = ForceNode(self.name)
        self.nodePath = NodePath(self.node)
        self.fEnabled = 0

        self.particleEffect = None

    def cleanup(self):
        self.node.clear()
        self.nodePath.removeNode()
        del self.nodePath
        del self.node
        del self.particleEffect

    def enable(self):
        """Convenience function to enable all forces in force group"""
        for i in range(self.node.getNumForces()):
            f = self.node.getForce(i)
            f.setActive(1)
        self.fEnabled = 1

    def disable(self):
        """Convenience function to disable all forces in force group"""
        for i in range(self.node.getNumForces()):
            f = self.node.getForce(i)
            f.setActive(0)
        self.fEnabled = 0

    def isEnabled(self):
        return self.fEnabled

    def addForce(self, force):
        self.node.addForce(force)
        if (self.particleEffect):
            self.particleEffect.addForce(force)

    def removeForce(self, force):
        self.node.removeForce(force)
        if (self.particleEffect != None):
            self.particleEffect.removeForce(force)

    # Get/set
    def getName(self):
        return self.name
    def getNode(self):
        return self.node
    def getNodePath(self):
        return self.nodePath

    # Utility functions
    def __getitem__(self, index):
        numForces = self.node.getNumForces()
        if ((index < 0) or (index >= numForces)):
            raise IndexError
        return self.node.getForce(index)

    def __len__(self):
        return self.node.getNumForces()

    def asList(self):
        l = []
        for i in range(self.node.getNumForces()):
            l.append(self.node.getForce(i))
        return l

    def printParams(self, file = sys.stdout, targ = 'self'):
        file.write('# Force parameters\n')
        for i in range(self.node.getNumForces()):
            f = self.node.getForce(i)
            fname = 'force%d' % i
            if isinstance(f, LinearForce):
                amplitude = f.getAmplitude()
                massDependent = f.getMassDependent()
                if isinstance(f, LinearCylinderVortexForce):
                    file.write(fname + ' = LinearCylinderVortexForce(%.4f, %.4f, %.4f, %.4f, %d)\n' % (f.getRadius(), f.getLength(), f.getCoef(), amplitude, massDependent))
                elif isinstance(f, LinearDistanceForce):
                    radius = f.getRadius()
                    falloffType = f.getFalloffType()
                    ftype = 'FTONEOVERR'
                    if (falloffType == LinearDistanceForce.FTONEOVERR):
                        ftype = 'FTONEOVERR'
                    elif (falloffType == LinearDistanceForce.FTONEOVERRSQUARED):
                        ftype = 'FTONEOVERRSQUARED'
                    elif (falloffType == LinearDistanceForce.FTONEOVERRCUBED):
                        ftype = 'FTONEOVERRCUBED'
                    forceCenter = f.getForceCenter()
                    if isinstance(f, LinearSinkForce):
                        file.write(fname + ' = LinearSinkForce(Point3(%.4f, %.4f, %.4f), LinearDistanceForce.%s, %.4f, %.4f, %d)\n' % (forceCenter[0], forceCenter[1], forceCenter[2], ftype, radius, amplitude, massDependent))
                    elif isinstance(f, LinearSourceForce):
                        file.write(fname + ' = LinearSourceForce(Point3(%.4f, %.4f, %.4f), LinearDistanceForce.%s, %.4f, %.4f, %d)\n' % (forceCenter[0], forceCenter[1], forceCenter[2], ftype, radius, amplitude, massDependent))
                elif isinstance(f, LinearFrictionForce):
                    file.write(fname + ' = LinearFrictionForce(%.4f, %.4f, %d)\n' % (f.getCoef(), amplitude, massDependent))
                elif isinstance(f, LinearJitterForce):
                    file.write(fname + ' = LinearJitterForce(%.4f, %d)\n' % (amplitude, massDependent))
                elif isinstance(f, LinearNoiseForce):
                    file.write(fname + ' = LinearNoiseForce(%.4f, %d)\n' % (amplitude, massDependent))
                elif isinstance(f, LinearVectorForce):
                    vec = f.getLocalVector()
                    file.write(fname + ' = LinearVectorForce(Vec3(%.4f, %.4f, %.4f), %.4f, %d)\n' % (vec[0], vec[1], vec[2], amplitude, massDependent))
            elif isinstance(f, AngularForce):
                if isinstance(f, AngularVectorForce):
                    vec = f.getQuat()
                    file.write(fname + ' = AngularVectorForce(Quat(%.4f, %.4f, %.4f))\n' % (vec[0], vec[1], vec[2], vec[3]))
            file.write(fname + '.setActive(%d)\n' % f.getActive())
            file.write(targ + '.addForce(%s)\n' % fname)
<commit_msg>vector mask code is now printed to an exported ptf file<commit_after>from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.showbase.PhysicsManagerGlobal import *

from direct.directnotify import DirectNotifyGlobal
import sys

class ForceGroup(DirectObject):

    notify = DirectNotifyGlobal.directNotify.newCategory('ForceGroup')
    id = 1

    def __init__(self, name=None):
        if (name == None):
            self.name = 'ForceGroup-%d' % ForceGroup.id
            ForceGroup.id += 1
        else:
            self.name = name

        self.node = ForceNode(self.name)
        self.nodePath = NodePath(self.node)
        self.fEnabled = 0

        self.particleEffect = None

    def cleanup(self):
        self.node.clear()
        self.nodePath.removeNode()
        del self.nodePath
        del self.node
        del self.particleEffect

    def enable(self):
        """Convenience function to enable all forces in force group"""
        for i in range(self.node.getNumForces()):
            f = self.node.getForce(i)
            f.setActive(1)
        self.fEnabled = 1

    def disable(self):
        """Convenience function to disable all forces in force group"""
        for i in range(self.node.getNumForces()):
            f = self.node.getForce(i)
            f.setActive(0)
        self.fEnabled = 0

    def isEnabled(self):
        return self.fEnabled

    def addForce(self, force):
        self.node.addForce(force)
        if (self.particleEffect):
            self.particleEffect.addForce(force)

    def removeForce(self, force):
        self.node.removeForce(force)
        if (self.particleEffect != None):
            self.particleEffect.removeForce(force)

    # Get/set
    def getName(self):
        return self.name
    def getNode(self):
        return self.node
    def getNodePath(self):
        return self.nodePath

    # Utility functions
    def __getitem__(self, index):
        numForces = self.node.getNumForces()
        if ((index < 0) or (index >= numForces)):
            raise IndexError
        return self.node.getForce(index)

    def __len__(self):
        return self.node.getNumForces()

    def asList(self):
        l = []
        for i in range(self.node.getNumForces()):
            l.append(self.node.getForce(i))
        return l

    def printParams(self, file = sys.stdout, targ = 'self'):
        file.write('# Force parameters\n')
        for i in range(self.node.getNumForces()):
            f = self.node.getForce(i)
            fname = 'force%d' % i
            if isinstance(f, LinearForce):
                amplitude = f.getAmplitude()
                massDependent = f.getMassDependent()
                if isinstance(f, LinearCylinderVortexForce):
                    file.write(fname + ' = LinearCylinderVortexForce(%.4f, %.4f, %.4f, %.4f, %d)\n' % (f.getRadius(), f.getLength(), f.getCoef(), amplitude, massDependent))
                elif isinstance(f, LinearDistanceForce):
                    radius = f.getRadius()
                    falloffType = f.getFalloffType()
                    ftype = 'FTONEOVERR'
                    if (falloffType == LinearDistanceForce.FTONEOVERR):
                        ftype = 'FTONEOVERR'
                    elif (falloffType == LinearDistanceForce.FTONEOVERRSQUARED):
                        ftype = 'FTONEOVERRSQUARED'
                    elif (falloffType == LinearDistanceForce.FTONEOVERRCUBED):
                        ftype = 'FTONEOVERRCUBED'
                    forceCenter = f.getForceCenter()
                    if isinstance(f, LinearSinkForce):
                        file.write(fname + ' = LinearSinkForce(Point3(%.4f, %.4f, %.4f), LinearDistanceForce.%s, %.4f, %.4f, %d)\n' % (forceCenter[0], forceCenter[1], forceCenter[2], ftype, radius, amplitude, massDependent))
                    elif isinstance(f, LinearSourceForce):
                        file.write(fname + ' = LinearSourceForce(Point3(%.4f, %.4f, %.4f), LinearDistanceForce.%s, %.4f, %.4f, %d)\n' % (forceCenter[0], forceCenter[1], forceCenter[2], ftype, radius, amplitude, massDependent))
                elif isinstance(f, LinearFrictionForce):
                    file.write(fname + ' = LinearFrictionForce(%.4f, %.4f, %d)\n' % (f.getCoef(), amplitude, massDependent))
                elif isinstance(f, LinearJitterForce):
                    file.write(fname + ' = LinearJitterForce(%.4f, %d)\n' % (amplitude, massDependent))
                elif isinstance(f, LinearNoiseForce):
                    file.write(fname + ' = LinearNoiseForce(%.4f, %d)\n' % (amplitude, massDependent))
                elif isinstance(f, LinearVectorForce):
                    vec = f.getLocalVector()
                    file.write(fname + ' = LinearVectorForce(Vec3(%.4f, %.4f, %.4f), %.4f, %d)\n' % (vec[0], vec[1], vec[2], amplitude, massDependent))
                mask = f.getVectorMasks()
                file.write(fname + '.setVectorMasks(%d, %d, %d)\n' % (mask[0],mask[1],mask[2]))
            elif isinstance(f, AngularForce):
                if isinstance(f, AngularVectorForce):
                    vec = f.getQuat()
                    file.write(fname + ' = AngularVectorForce(Quat(%.4f, %.4f, %.4f))\n' % (vec[0], vec[1], vec[2], vec[3]))
            file.write(fname + '.setActive(%d)\n' % f.getActive())
            file.write(targ + '.addForce(%s)\n' % fname)
<|endoftext|>


<commit_before>__author__ = 'Jernej'
from commander import commander
from comm import comm

D=","

class mainserver:
    def __init__(self):
        self.client=comm()
        self.commander=commander()

    def run(self):
        status=""
        data=""
        #while connection not broken by client
        #while result!=c.DISCONNECT:
        i=0
        while self.client.connected():
            self.client.startVideoStream()
            data = self.client.readmsg()
            #new data available
            if data != '':
                status=self.commander.update(data)
                #TODO: key commands ack ... auto, alt hold, modes
            #self.client.sendmsg("test:"+str(i))

            self.client.sendmsg(self.commander.sensors.getStrArr())
            #i+=1
            #print(time.time() * 1000)
            self.commander.control()
            #time.sleep(0.5)
        self.commander.failsafe()
        self.client.reset()

<commit_msg>RPI SenseHat code integration and code optimization<commit_after>__author__ = 'Jernej'
from commander import commander
from comm import comm

D=","

class mainserver:
    def __init__(self):
        self.client=comm()
        self.commander=commander()

    def run(self):
        status=""
        data=""
        #while connection not broken by client
        #while result!=c.DISCONNECT:
        i=0
        while self.client.connected():
            #self.client.startVideoStream()
            data = self.client.readmsg()
            #new data available
            if data != '':
                status=self.commander.update(data)
                #TODO: key commands ack ... auto, alt hold, modes
            #self.client.sendmsg("test:"+str(i))

            self.client.sendmsg(self.commander.sensors.getStrArr())
            #i+=1
            #print(time.time() * 1000)
            self.commander.control()
            #time.sleep(0.5)
        self.commander.failsafe()
        self.client.reset()

<|endoftext|>


<commit_before>from __future__ import unicode_literals

import contextlib
import functools
import json
import traceback

from django.conf import settings
from django.conf.urls import url
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.http import HttpResponse, Http404
from django.views.generic import View
from django.views.decorators.csrf import csrf_exempt

from .exceptions import ErrorResponse, AuthenticationFailed, SerializationError
from .http import Response
from .jsonapi import TopLevel, Included

class EndpointSet(View):

    @classmethod
    def as_view(cls, **initkwargs):
        view_mapping_kwargs = initkwargs.pop("view_mapping_kwargs", {})
        view = super(EndpointSet, cls).as_view(**initkwargs)

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            mapping = cls.view_mapping(**view_mapping_kwargs)
            for verb, method in mapping.items():
                if hasattr(self, method):
                    setattr(self, verb, getattr(self, method))
            self.requested_method = mapping.get(request.method.lower())
            self.args = args
            self.kwargs = kwargs
            self.request = request
            return self.dispatch(request, *args, **kwargs)

        functools.update_wrapper(view, cls, updated=())
        functools.update_wrapper(view, cls.dispatch, assigned=())
        return csrf_exempt(view)

    def dispatch(self, request, *args, **kwargs):
        try:
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            self.check_authentication(handler)
            self.prepare()
            self.check_permissions(handler)
            response = handler(request, *args, **kwargs)
            if not isinstance(response, HttpResponse):
                raise ValueError("view did not return an HttpResponse (got: {})".format(type(response)))
        except Exception as exc:
            response = self.handle_exception(exc)
        return response

    def handle_exception(self, exc):
        if isinstance(exc, ErrorResponse):
            return exc.response
        elif isinstance(exc, Http404):
            return self.render_error(exc.args[0], status=404)
        else:
            if settings.DEBUG or getattr(settings, "PINAX_API_DEBUG", False):
                traceback.print_exc()
                return self.render_error(
                    traceback.format_exc().splitlines()[-1],
                    status=500
                )
            else:
                return self.render_error("unknown server error", status=500)

    def prepare(self):
        pass

    def check_authentication(self, handler):
        user = None
        backends = []
        backends.extend(getattr(handler, "authentication", []))
        backends.extend(getattr(self, "middleware", {}).get("authentication", []))
        for backend in backends:
            try:
                user = backend.authenticate(self.request)
            except AuthenticationFailed as exc:
                raise ErrorResponse(**self.error_response_kwargs(str(exc), status=401))
            if user:
                self.request.user = user
                break
        else:
            if not self.request.user.is_authenticated():
                raise ErrorResponse(**self.error_response_kwargs("Authentication Required.", status=401))

    def check_permissions(self, handler):
        perms = []
        perms.extend(getattr(handler, "permissions", []))
        perms.extend(getattr(self, "middleware", {}).get("permissions", []))
        for perm in perms:
            res = perm(self.request, view=self)
            if res is None:
                continue
            if isinstance(res, tuple):
                ok, status, msg = res
            else:
                ok, status, msg = res, 403, "Permission Denied."
            if not ok:
                raise ErrorResponse(**self.error_response_kwargs(msg, status=status))

    def parse_data(self):
        # @@@ this method is not the most ideal implementation generally, but
        # until a better design comes along, we roll with it!
        try:
            return json.loads(self.request.body.decode(settings.DEFAULT_CHARSET))
        except json.JSONDecodeError as e:
            raise ErrorResponse(**self.error_kwargs(str(e), title="Invalid JSON", status=400))

    @contextlib.contextmanager
    def validate(self, resource_class, obj=None):
        data = self.parse_data()
        if "data" not in data:
            raise ErrorResponse(**self.error_kwargs('Missing "data" key in payload.', status=400))
        if "attributes" not in data["data"]:
            raise ErrorResponse(**self.error_kwargs('Missing "attributes" key in data.', status=400))
        resource = resource_class()
        try:
            resource.populate(data["data"], obj=obj)
            yield resource
        except ValidationError as exc:
            raise ErrorResponse(
                TopLevel.from_validation_error(exc, resource_class).serializable(),
                status=400,
            )

    def render(self, resource, meta=None):
        try:
            payload = self.create_top_level(resource, meta=meta).serializable(request=self.request)
        except SerializationError as exc:
            return self.render_error(str(exc), status=400)
        else:
            return Response(payload, status=200)

    def render_create(self, resource, meta=None):
        try:
            payload = self.create_top_level(resource, meta=meta).serializable(request=self.request)
        except SerializationError as exc:
            return self.render_error(str(exc), status=400)
        else:
            res = Response(payload, status=201)
            res["Location"] = resource.get_self_link(request=self.request)
            return res

    def render_delete(self):
        return Response({}, status=200)

    def error_response_kwargs(self, message, title=None, status=400, extra=None):
        if extra is None:
            extra = {}
        err = dict(extra)
        err.update({
            "status": str(status),
            "detail": message,
        })
        if title is not None:
            err["title"] = title
        return {
            "data": TopLevel(errors=[err]).serializable(),
            "status": status,
        }

    def render_error(self, *args, **kwargs):
        return Response(**self.error_response_kwargs(*args, **kwargs))

    def get_object_or_404(self, qs, **kwargs):
        try:
            return qs.get(**kwargs)
        except ObjectDoesNotExist:
            raise Http404("{} does not exist.".format(qs.model._meta.verbose_name.capitalize()))

    def create_top_level(self, resource, linkage=False, meta=None):
        kwargs = {
            "data": resource,
            "meta": meta,
            "links": True,
            "linkage": linkage,
        }
        if "include" in self.request.GET:
            kwargs["included"] = Included(self.request.GET["include"].split(","))
        return TopLevel(**kwargs)

class ResourceEndpointSet(EndpointSet):

    parent = None

    @classmethod
    def view_mapping(cls, collection):
        if collection:
            mapping = {
                "get": "list",
                "post": "create",
            }
        else:
            mapping = {
                "get": "retrieve",
                "patch": "update",
                "delete": "destroy",
            }
        return mapping

    @classmethod
    def as_urls(cls):
        urls = [
            url(
                r"^{}$".format(cls.url.collection_regex()),
                cls.as_view(view_mapping_kwargs=dict(collection=True)),
                name="{}-list".format(cls.url.base_name)
            ),
            url(
                r"^{}$".format(cls.url.detail_regex()),
                cls.as_view(view_mapping_kwargs=dict(collection=False)),
                name="{}-detail".format(cls.url.base_name)
            )
        ]
        for related_name, eps in cls.relationships.items():
            urls.extend(eps.as_urls(cls.url, related_name))
        return urls

class RelationshipEndpointSet(EndpointSet):

    @classmethod
    def view_mapping(cls):
        return {
            "get": "retrieve",
            "post": "create",
            "patch": "update",
            "delete": "destroy",
        }

    @classmethod
    def as_urls(cls, base_url, related_name):
        urls = [
            url(
                r"^{}/relationships/{}$".format(
                    base_url.detail_regex(),
                    related_name,
                ),
                cls.as_view(),
                name="-".join([base_url.base_name, related_name, "relationship", "detail"])
            ),
        ]
        return urls

    def create_top_level(self, *args, **kwargs):
        kwargs["linkage"] = True
        return super(RelationshipEndpointSet, self).create_top_level(*args, **kwargs)
<commit_msg>added EndpointSet.debug property<commit_after>from __future__ import unicode_literals

import contextlib
import functools
import json
import traceback

from django.conf import settings
from django.conf.urls import url
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.http import HttpResponse, Http404
from django.views.generic import View
from django.views.decorators.csrf import csrf_exempt

from .exceptions import ErrorResponse, AuthenticationFailed, SerializationError
from .http import Response
from .jsonapi import TopLevel, Included

class EndpointSet(View):

    @classmethod
    def as_view(cls, **initkwargs):
        view_mapping_kwargs = initkwargs.pop("view_mapping_kwargs", {})
        view = super(EndpointSet, cls).as_view(**initkwargs)

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            mapping = cls.view_mapping(**view_mapping_kwargs)
            for verb, method in mapping.items():
                if hasattr(self, method):
                    setattr(self, verb, getattr(self, method))
            self.requested_method = mapping.get(request.method.lower())
            self.args = args
            self.kwargs = kwargs
            self.request = request
            return self.dispatch(request, *args, **kwargs)

        functools.update_wrapper(view, cls, updated=())
        functools.update_wrapper(view, cls.dispatch, assigned=())
        return csrf_exempt(view)

    def dispatch(self, request, *args, **kwargs):
        try:
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            self.check_authentication(handler)
            self.prepare()
            self.check_permissions(handler)
            response = handler(request, *args, **kwargs)
            if not isinstance(response, HttpResponse):
                raise ValueError("view did not return an HttpResponse (got: {})".format(type(response)))
        except Exception as exc:
            response = self.handle_exception(exc)
        return response

    @property
    def debug(self):
        return settings.DEBUG or getattr(settings, "PINAX_API_DEBUG", False)

    def handle_exception(self, exc):
        if isinstance(exc, ErrorResponse):
            return exc.response
        elif isinstance(exc, Http404):
            return self.render_error(exc.args[0], status=404)
        else:
            if self.debug:
                traceback.print_exc()
                return self.render_error(
                    traceback.format_exc().splitlines()[-1],
                    status=500
                )
            else:
                return self.render_error("unknown server error", status=500)

    def prepare(self):
        pass

    def check_authentication(self, handler):
        user = None
        backends = []
        backends.extend(getattr(handler, "authentication", []))
        backends.extend(getattr(self, "middleware", {}).get("authentication", []))
        for backend in backends:
            try:
                user = backend.authenticate(self.request)
            except AuthenticationFailed as exc:
                raise ErrorResponse(**self.error_response_kwargs(str(exc), status=401))
            if user:
                self.request.user = user
                break
        else:
            if not self.request.user.is_authenticated():
                raise ErrorResponse(**self.error_response_kwargs("Authentication Required.", status=401))

    def check_permissions(self, handler):
        perms = []
        perms.extend(getattr(handler, "permissions", []))
        perms.extend(getattr(self, "middleware", {}).get("permissions", []))
        for perm in perms:
            res = perm(self.request, view=self)
            if res is None:
                continue
            if isinstance(res, tuple):
                ok, status, msg = res
            else:
                ok, status, msg = res, 403, "Permission Denied."
            if not ok:
                raise ErrorResponse(**self.error_response_kwargs(msg, status=status))

    def parse_data(self):
        # @@@ this method is not the most ideal implementation generally, but
        # until a better design comes along, we roll with it!
        try:
            return json.loads(self.request.body.decode(settings.DEFAULT_CHARSET))
        except json.JSONDecodeError as e:
            raise ErrorResponse(**self.error_kwargs(str(e), title="Invalid JSON", status=400))

    @contextlib.contextmanager
    def validate(self, resource_class, obj=None):
        data = self.parse_data()
        if "data" not in data:
            raise ErrorResponse(**self.error_kwargs('Missing "data" key in payload.', status=400))
        if "attributes" not in data["data"]:
            raise ErrorResponse(**self.error_kwargs('Missing "attributes" key in data.', status=400))
        resource = resource_class()
        try:
            resource.populate(data["data"], obj=obj)
            yield resource
        except ValidationError as exc:
            raise ErrorResponse(
                TopLevel.from_validation_error(exc, resource_class).serializable(),
                status=400,
            )

    def render(self, resource, meta=None):
        try:
            payload = self.create_top_level(resource, meta=meta).serializable(request=self.request)
        except SerializationError as exc:
            return self.render_error(str(exc), status=400)
        else:
            return Response(payload, status=200)

    def render_create(self, resource, meta=None):
        try:
            payload = self.create_top_level(resource, meta=meta).serializable(request=self.request)
        except SerializationError as exc:
            return self.render_error(str(exc), status=400)
        else:
            res = Response(payload, status=201)
            res["Location"] = resource.get_self_link(request=self.request)
            return res

    def render_delete(self):
        return Response({}, status=200)

    def error_response_kwargs(self, message, title=None, status=400, extra=None):
        if extra is None:
            extra = {}
        err = dict(extra)
        err.update({
            "status": str(status),
            "detail": message,
        })
        if title is not None:
            err["title"] = title
        return {
            "data": TopLevel(errors=[err]).serializable(),
            "status": status,
        }

    def render_error(self, *args, **kwargs):
        return Response(**self.error_response_kwargs(*args, **kwargs))

    def get_object_or_404(self, qs, **kwargs):
        try:
            return qs.get(**kwargs)
        except ObjectDoesNotExist:
            raise Http404("{} does not exist.".format(qs.model._meta.verbose_name.capitalize()))

    def create_top_level(self, resource, linkage=False, meta=None):
        kwargs = {
            "data": resource,
            "meta": meta,
            "links": True,
            "linkage": linkage,
        }
        if "include" in self.request.GET:
            kwargs["included"] = Included(self.request.GET["include"].split(","))
        return TopLevel(**kwargs)

class ResourceEndpointSet(EndpointSet):

    parent = None

    @classmethod
    def view_mapping(cls, collection):
        if collection:
            mapping = {
                "get": "list",
                "post": "create",
            }
        else:
            mapping = {
                "get": "retrieve",
                "patch": "update",
                "delete": "destroy",
            }
        return mapping

    @classmethod
    def as_urls(cls):
        urls = [
            url(
                r"^{}$".format(cls.url.collection_regex()),
                cls.as_view(view_mapping_kwargs=dict(collection=True)),
                name="{}-list".format(cls.url.base_name)
            ),
            url(
                r"^{}$".format(cls.url.detail_regex()),
                cls.as_view(view_mapping_kwargs=dict(collection=False)),
                name="{}-detail".format(cls.url.base_name)
            )
        ]
        for related_name, eps in cls.relationships.items():
            urls.extend(eps.as_urls(cls.url, related_name))
        return urls

class RelationshipEndpointSet(EndpointSet):

    @classmethod
    def view_mapping(cls):
        return {
            "get": "retrieve",
            "post": "create",
            "patch": "update",
            "delete": "destroy",
        }

    @classmethod
    def as_urls(cls, base_url, related_name):
        urls = [
            url(
                r"^{}/relationships/{}$".format(
                    base_url.detail_regex(),
                    related_name,
                ),
                cls.as_view(),
                name="-".join([base_url.base_name, related_name, "relationship", "detail"])
            ),
        ]
        return urls

    def create_top_level(self, *args, **kwargs):
        kwargs["linkage"] = True
        return super(RelationshipEndpointSet, self).create_top_level(*args, **kwargs)
<|endoftext|>


<commit_before>#
# Copyright (c) 2014 Christopher L. Felton
#

from __future__ import division
from __future__ import print_function

import os
from argparse import Namespace

from myhdl import *

from rhea.system import FIFOBus
import rhea.cores as cores
from rhea.cores.fifo import fifo_fast

from rhea.utils.test import run_testbench

def test_ffifo(args=None):
    """ verify the synchronous FIFO
    """

    if args is None:
        args = Namespace(width=8, size=16, name='test')
    else:
        # @todo: verify args has the attributes needed for the FIFOBus
        pass

    reset = ResetSignal(0, active=1, async=True)
    clock = Signal(bool(0))
    fbus = FIFOBus(width=args.width, size=args.size)

    def _bench_ffifo():

        # @todo: use args.fast, args.use_srl_prim
        tbdut = cores.fifo.fifo_fast(clock, reset, fbus, use_srl_prim=False)

        @always(delay(10))
        def tbclk():
            clock.next = not clock

        @instance
        def tbstim():
            fbus.write_data.next = 0xFE
            reset.next = reset.active
            yield delay(33)
            reset.next = not reset.active
            for ii in range(5):
                yield clock.posedge

            # test the normal cases
            for num_bytes in range(1, args.size+1):

                # write some bytes
                for ii in range(num_bytes):
                    #print('nbyte %x wdata %x' % (num_bytes, ii))
                    yield clock.posedge
                    fbus.write_data.next = ii
                    fbus.write.next = True

                yield clock.posedge
                fbus.write.next = False
                fbus.write_data.next = 0xFE

                # if 16 bytes written make sure FIFO is full
                yield clock.posedge
                if num_bytes == args.size:
                    assert fbus.full, "FIFO should be full!"
                    assert not fbus.empty, "FIFO should not be empty"

                for ii in range(num_bytes):
                    fbus.read.next = True
                    yield clock.posedge
                    #print("rdata %x ii %x " % (fbus.read_data, ii))
                    assert fbus.read_valid
                    assert fbus.read_data == ii, "rdata %x ii %x " % (fbus.read_data, ii)

                fbus.read.next = False
                yield clock.posedge
                assert fbus.empty

            # Test overflows
            # Test underflows
            # Test write / read same time

            raise StopSimulation

        return tbdut, tbclk, tbstim

    run_testbench(_bench_ffifo)

if __name__ == '__main__':
    for size in (4, 8, 16):
        args = Namespace(width=8, size=size, name='test')
        test_ffifo(args=args)
<commit_msg>Added more tests for fifo_fast<commit_after>#
# Copyright (c) 2014 Christopher L. Felton
#

from __future__ import division
from __future__ import print_function
import random
from random import randrange
import os
from argparse import Namespace

from myhdl import *
import pytest

from rhea.system import FIFOBus, Clock, Reset
import rhea.cores as cores
from rhea.cores.fifo import fifo_fast

from rhea.utils.test import run_testbench

random.seed(3)

def test_ffifo(args=None):
    """ verify the synchronous FIFO
    """
    clock = Clock(0, frequency=50e6)
    reset = Reset(0, active=0, async=True)

    if args is None:
        args = Namespace(width=8, size=16, name='test')
    else:
        # @todo: verify args has the attributes needed for the FIFOBus
        pass

    fbus = FIFOBus(width=args.width, size=args.size)

    def _bench_ffifo():

        # @todo: use args.fast, args.use_srl_prim
        tbdut = cores.fifo.fifo_fast(clock, reset, fbus, use_srl_prim=False)

        @instance
        def tbstim():
            fbus.write_data.next = 0xFE
            reset.next = reset.active
            yield delay(33)
            reset.next = not reset.active
            for ii in range(5):
                yield clock.posedge

            # test the normal cases

            for num_bytes in range(1, args.size+1):

                # write some bytes
                for ii in range(num_bytes):
                    #print('nbyte %x wdata %x' % (num_bytes, ii))

                    fbus.write_data.next = ii
                    fbus.write.next = True
                    # wait for 1 clock cyle to
                    # allow the fifo ops to occur
                    yield clock.posedge

                fbus.write.next = False
                fbus.write_data.next = 0xFE

                # if 16 bytes written make sure FIFO is full
                yield clock.posedge
                if num_bytes == args.size:
                    assert fbus.count == fbus.size
                    assert fbus.full, "FIFO should be full!"
                    assert not fbus.empty, "FIFO should not be empty"

                for cc in range(5):
                    yield clock.posedge

                for ii in range(num_bytes ):
                    fbus.read.next = True
                    yield clock.posedge
                    #print("rdata %x ii %x " % (fbus.read_data, ii))
                    assert fbus.read_valid
                    assert fbus.read_data == ii, "rdata %x ii %x " % (fbus.read_data, ii)

                fbus.read.next = False
                yield clock.posedge
                assert fbus.empty

            fbus.clear.next = True
            yield clock.posedge
            fbus.clear.next = not fbus.clear
            for ii in range(5):
                yield clock.posedge

            raise StopSimulation
        return tbdut, tbstim

    # normal fifo r/w
    run_testbench(_bench_ffifo)

def test_overflow_ffifo(args=None):
    """ verify the synchronous FIFO
    """
    reset = ResetSignal(0, active=1, async=True)
    clock = Signal(bool(0))

    if args is None:
        args = Namespace(width=8, size=16, name='test')
    else:
        # @todo: verify args has the attributes needed for the FIFOBus
        pass

    fbus = FIFOBus(width=args.width, size=args.size)

    def _bench_fifo_overflow():
        # @todo: use args.fast, args.use_srl_prim
        tbdut = cores.fifo.fifo_fast(clock, reset, fbus, use_srl_prim=False)

        @always(delay(10))
        def tbclk():
            clock.next = not clock

        @instance
        def tbstim():
            fbus.write_data.next = 0xFE
            reset.next = reset.active
            yield delay(33)
            reset.next = not reset.active
            for ii in range(5):
                yield clock.posedge

            # write more bytes
            rand = randrange(args.size+2,2*args.size+1)
            for num_bytes in range(args.size, rand):

                for ii in range(num_bytes):
                    try:
                        fbus.write_data.next = ii
                        fbus.write.next = True
                        yield clock.posedge
                    except ValueError:
                        assert fbus.count == fbus.size
                        assert fbus.full, "FIFO should be full!"
                        assert not fbus.empty, "FIFO should not be empty"
                    else:
                        assert fbus.count <= fbus.size
                        if (fbus.count < fbus.size):
                            assert not fbus.full

                fbus.write.next = False
                fbus.write_data.next = 0xFE
                for cc in range(5):
                    yield clock.posedge

                for ii in range(fbus.size):
                    fbus.read.next = True
                    yield clock.posedge
                    #print("rdata %x ii %x " % (fbus.read_data, ii))
                    assert fbus.read_valid
                    assert fbus.read_data == ii, "rdata %x ii %x " % (fbus.read_data, ii)

                fbus.read.next = False
                yield clock.posedge
                assert fbus.empty

            fbus.clear.next = True
            yield clock.posedge
            fbus.clear.next = not fbus.clear
            for ii in range(5):
                yield clock.posedge
            raise StopSimulation
        return tbdut, tbclk, tbstim
    for kk in range(100):
        with pytest.raises(ValueError):
            run_testbench(_bench_fifo_overflow)

# test underflow
# should give a ValueError
# but instead gives an assertion error
@pytest.mark.xfail
def test_underflow_ffifo(args=None):
    """ verify the synchronous FIFO
    """
    reset = ResetSignal(0, active=1, async=True)
    clock = Signal(bool(0))

    if args is None:
        args = Namespace(width=8, size=16, name='test')
    else:
        # @todo: verify args has the attributes needed for the FIFOBus
        pass

    fbus = FIFOBus(width=args.width, size=args.size)

    def _bench_fifo_underflow():
        # @todo: use args.fast, args.use_srl_prim
        tbdut = cores.fifo.fifo_fast(clock, reset, fbus, use_srl_prim=False)

        @always(delay(10))
        def tbclk():
            clock.next = not clock

        @instance
        def tbstim():
            fbus.write_data.next = 0xFE
            reset.next = reset.active
            yield delay(33)
            reset.next = not reset.active
            for ii in range(5):
                yield clock.posedge

            rand = randrange(args.size+2, 2*args.size+1)
            for num_bytes in range(args.size, rand):

                for ii in range(fbus.size):
                    try:
                        fbus.write_data.next = ii
                        fbus.write.next = True
                        yield clock.posedge
                    except ValueError:
                        assert fbus.count == fbus.size
                        assert fbus.full, "FIFO should be full!"
                        assert not fbus.empty, "FIFO should not be empty"
                    else:
                        assert fbus.count <= fbus.size
                        if (fbus.count < fbus.size):
                            assert not fbus.full

                fbus.write.next = False
                fbus.write_data.next = 0xFE
                for delay_clk in range(5):
                    yield clock.posedge

                if ii == (args.size - 1):
                    assert fbus.count == fbus.size
                    assert fbus.full, "FIFO should be full!"
                    assert not fbus.empty, "FIFO should not be empty"

                yield clock.posedge

                for ii in range(num_bytes):
                    try:
                        fbus.read.next = True
                        yield clock.posedge
                        # yield clock.posedge
                        # test works for 2
                        # yield clock.posedge stmts
                    except ValueError:
                        assert fbus.empty
                    else:
                        #print("rdata %x ii %x " % (fbus.read_data, ii))
                        assert fbus.read_valid
                        assert fbus.read_data == ii, "rdata %x ii %x " % (fbus.read_data, ii)

                fbus.read.next = False
                yield clock.posedge
                assert fbus.empty

            fbus.clear.next = True
            yield clock.posedge
            fbus.clear.next = not fbus.clear
            for ii in range(5):
                yield clock.posedge
            raise StopSimulation
        return tbdut, tbclk,tbstim

    with pytest.raises(ValueError):
        run_testbench(_bench_fifo_underflow)

# read/write at the same time
# fifo should remain unchanged
def test_rw_ffifo(args=None):
    """ verify the synchronous FIFO
    """
    clock = Clock(0, frequency=50e6)
    reset = Reset(0, active=0, async=True)

    if args is None:
        args = Namespace(width=8, size=16, name='test')
    else:
        # @todo: verify args has the attributes needed for the FIFOBus
        pass
    fbus = FIFOBus(width=args.width, size=args.size)

    def _bench_rw_ffifo():

        # @todo: use args.fast, args.use_srl_prim
        tbdut = cores.fifo.fifo_fast(clock, reset, fbus, use_srl_prim=False)

        @instance
        def tbstim():
            fbus.write_data.next = 0xFE
            reset.next = reset.active
            yield delay(33)
            reset.next = not reset.active
            for ii in range(5):
                yield clock.posedge

            for num_bytes in range(1, args.size):

                # write some bytes
                for ii in range(num_bytes):
                    #print('nbyte %x wdata %x' % (num_bytes, ii))

                    fbus.write_data.next = ii
                    fbus.write.next = True
                    yield clock.posedge

                fbus.write.next = False
                fbus.write_data.next = 0xFE
                a = fbus.count

                for cc in range(5):
                    yield clock.posedge

                # if 16 bytes written make sure FIFO is full
                yield clock.posedge
                if num_bytes == args.size:
                    assert fbus.count == fbus.size
                    assert fbus.full, "FIFO should be full!"
                    assert not fbus.empty, "FIFO should not be empty"

                # r/w at the same time, should not
                # change the size of the fifo
                for ii in range(num_bytes, args.size):
                    fbus.write_data.next = ii
                    fbus.write.next = True
                    fbus.read.next = True
                    yield clock.posedge
                    assert fbus.read_data == (ii - num_bytes)
                    assert fbus.read_valid
                    if ii == 0:
                        assert fbus.empty
                    else:
                        assert not fbus.empty

                fbus.write.next = False
                fbus.write_data.next = 0xFE
                fbus.read.next = False

                assert a == fbus.count
                for cc in range(5):
                    yield clock.posedge

                # read remaining bytes
                for ii in range(num_bytes):
                    fbus.read.next = True
                    yield clock.posedge
                    #print("rdata %x ii %x " % (fbus.read_data, ii))
                    assert fbus.read_valid
                    assert fbus.read_data == ((args.size - num_bytes) + ii), \
                                             "rdata %x ii %x " % (fbus.read_data, \
                                             ((args.size - num_bytes) + ii))

                fbus.read.next = False
                yield clock.posedge
                assert fbus.empty

            fbus.clear.next = True
            yield clock.posedge
            fbus.clear.next = not fbus.clear
            for ii in range(5):
                yield clock.posedge

            raise StopSimulation
        return tbdut, tbstim

    # r/w at the same time
    for trial in range(100):
         run_testbench(_bench_rw_ffifo)

if __name__ == '__main__':
    for size in (4, 8, 16):
        args = Namespace(width=8, size=size, name='test')
        test_ffifo(args=args)
<|endoftext|>


<commit_before>import datetime
import os
import uuid

from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

from pinax.invitations.models import JoinInvitation
from reversion import revisions as reversion
from slugify import slugify

from . import signals

def avatar_upload(instance, filename):
    ext = filename.split(".")[-1]
    filename = "%s.%s" % (uuid.uuid4(), ext)
    return os.path.join("avatars", filename)

def create_slug(name):
    return slugify(name)[:50]

class BaseTeam(models.Model):

    MEMBER_ACCESS_OPEN = "open"
    MEMBER_ACCESS_APPLICATION = "application"
    MEMBER_ACCESS_INVITATION = "invitation"

    MANAGER_ACCESS_ADD = "add someone"
    MANAGER_ACCESS_INVITE = "invite someone"

    MEMBER_ACCESS_CHOICES = [
        (MEMBER_ACCESS_OPEN, _("open")),
        (MEMBER_ACCESS_APPLICATION, _("by application")),
        (MEMBER_ACCESS_INVITATION, _("by invitation"))
    ]

    MANAGER_ACCESS_CHOICES = [
        (MANAGER_ACCESS_ADD, _("add someone")),
        (MANAGER_ACCESS_INVITE, _("invite someone"))
    ]

    member_access = models.CharField(max_length=20, choices=MEMBER_ACCESS_CHOICES, verbose_name=_("member access"))
    manager_access = models.CharField(max_length=20, choices=MANAGER_ACCESS_CHOICES, verbose_name=_("manager access"))

    class Meta:
        abstract = True
        verbose_name = _("Base")
        verbose_name_plural = _("Bases")

    def can_join(self, user):
        state = self.state_for(user)
        if self.member_access == BaseTeam.MEMBER_ACCESS_OPEN and state is None:
            return True
        elif state == BaseMembership.STATE_INVITED:
            return True
        else:
            return False

    def can_leave(self, user):
        # managers can't leave at the moment
        role = self.role_for(user)
        return role == BaseMembership.ROLE_MEMBER

    def can_apply(self, user):
        state = self.state_for(user)
        return self.member_access == BaseTeam.MEMBER_ACCESS_APPLICATION and state is None

    @property
    def applicants(self):
        return self.memberships.filter(state=BaseMembership.STATE_APPLIED)

    @property
    def invitees(self):
        return self.memberships.filter(state=BaseMembership.STATE_INVITED)

    @property
    def declines(self):
        return self.memberships.filter(state=BaseMembership.STATE_DECLINED)

    @property
    def rejections(self):
        return self.memberships.filter(state=BaseMembership.STATE_REJECTED)

    @property
    def acceptances(self):
        return self.memberships.filter(state__in=[
            BaseMembership.STATE_ACCEPTED,
            BaseMembership.STATE_AUTO_JOINED]
        )

    @property
    def members(self):
        return self.acceptances.filter(role=BaseMembership.ROLE_MEMBER)

    @property
    def managers(self):
        return self.acceptances.filter(role=BaseMembership.ROLE_MANAGER)

    @property
    def owners(self):
        return self.acceptances.filter(role=BaseMembership.ROLE_OWNER)

    def is_owner_or_manager(self, user):
        return self.acceptances.filter(
            role__in=[
                BaseMembership.ROLE_OWNER,
                BaseMembership.ROLE_MANAGER
            ],
            user=user
        ).exists()

    def is_member(self, user):
        return self.members.filter(user=user).exists()

    def is_manager(self, user):
        return self.managers.filter(user=user).exists()

    def is_owner(self, user):
        return self.owners.filter(user=user).exists()

    def is_on_team(self, user):
        return self.acceptances.filter(user=user).exists()

    def add_member(self, user, role=None, state=None):
        # we do this, rather than put the BaseMembership constants in declaration
        # because BaseMembership is not yet defined
        if role is None:
            role = BaseMembership.ROLE_MEMBER
        if state is None:
            state = BaseMembership.STATE_AUTO_JOINED

        membership, created = BaseMembership.objects.get_or_create(
            team=self,
            user=user,
            defaults={"role": role, "state": state},
        )
        return membership

    def add_user(self, user, role):
        state = BaseMembership.STATE_AUTO_JOINED
        if self.manager_access == BaseTeam.MANAGER_ACCESS_INVITE:
            state = BaseMembership.STATE_INVITED
        membership, _ = self.memberships.get_or_create(
            user=user,
            defaults={"role": role, "state": state}
        )
        signals.added_member.send(sender=self, membership=membership)
        return membership

    def invite_user(self, from_user, to_email, role, message=None):
        if not JoinInvitation.objects.filter(signup_code__email=to_email).exists():
            invite = JoinInvitation.invite(from_user, to_email, message, send=False)
            membership, _ = self.memberships.get_or_create(
                invite=invite,
                defaults={"role": role, "state": BaseMembership.STATE_INVITED}
            )
            invite.send_invite()
            signals.invited_user.send(sender=self, membership=membership)
            return membership

    def for_user(self, user):
        try:
            return self.memberships.get(user=user)
        except BaseMembership.DoesNotExist:
            pass

    def state_for(self, user):
        membership = self.for_user(user=user)
        if membership:
            return membership.state

    def role_for(self, user):
        membership = self.for_user(user)
        if membership:
            return membership.role

class SimpleTeam(BaseTeam):

    class Meta:
        verbose_name = _("Simple Team")
        verbose_name_plural = _("Simple Teams")

@python_2_unicode_compatible
class Team(BaseTeam):

    slug = models.SlugField(unique=True)
    name = models.CharField(max_length=100, verbose_name=_("name"))
    avatar = models.ImageField(upload_to=avatar_upload, blank=True, verbose_name=_("avatar"))
    description = models.TextField(blank=True, verbose_name=_("description"))
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="teams_created", verbose_name=_("creator"))
    created = models.DateTimeField(default=timezone.now, editable=False, verbose_name=_("created"))

    class Meta:
        verbose_name = _("Team")
        verbose_name_plural = _("Teams")

    def get_absolute_url(self):
        return reverse("team_detail", args=[self.slug])

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.id:
            self.slug = create_slug(self.name)
        self.full_clean()
        super(Team, self).save(*args, **kwargs)

class BaseMembership(models.Model):

    STATE_APPLIED = "applied"
    STATE_INVITED = "invited"
    STATE_DECLINED = "declined"
    STATE_REJECTED = "rejected"
    STATE_ACCEPTED = "accepted"
    STATE_AUTO_JOINED = "auto-joined"

    ROLE_MEMBER = "member"
    ROLE_MANAGER = "manager"
    ROLE_OWNER = "owner"

    STATE_CHOICES = [
        (STATE_APPLIED, _("applied")),
        (STATE_INVITED, _("invited")),
        (STATE_DECLINED, _("declined")),
        (STATE_REJECTED, _("rejected")),
        (STATE_ACCEPTED, _("accepted")),
        (STATE_AUTO_JOINED, _("auto joined"))
    ]

    ROLE_CHOICES = [
        (ROLE_MEMBER, _("member")),
        (ROLE_MANAGER, _("manager")),
        (ROLE_OWNER, _("owner"))
    ]

    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="memberships", null=True, blank=True, verbose_name=_("user"))
    invite = models.ForeignKey(JoinInvitation, related_name="memberships", null=True, blank=True, verbose_name=_("invite"))
    state = models.CharField(max_length=20, choices=STATE_CHOICES, verbose_name=_("state"))
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default=ROLE_MEMBER, verbose_name=_("role"))
    created = models.DateTimeField(default=timezone.now, verbose_name=_("created"))

    def is_owner(self):
        return self.role == BaseMembership.ROLE_OWNER

    def is_manager(self):
        return self.role == BaseMembership.ROLE_MANAGER

    def is_member(self):
        return self.role == BaseMembership.ROLE_MEMBER

    def promote(self, by):
        role = self.team.role_for(by)
        if role in [BaseMembership.ROLE_MANAGER, BaseMembership.ROLE_OWNER]:
            if self.role == Membership.ROLE_MEMBER:
                self.role = Membership.ROLE_MANAGER
                self.save()
                signals.promoted_member.send(sender=self, membership=self)
                return True
        return False

    def demote(self, by):
        role = self.team.role_for(by)
        if role in [Membership.ROLE_MANAGER, Membership.ROLE_OWNER]:
            if self.role == Membership.ROLE_MANAGER:
                self.role = Membership.ROLE_MEMBER
                self.save()
                signals.demoted_member.send(sender=self, membership=self)
                return True
        return False

    def accept(self, by):
        role = self.team.role_for(by)
        if role in [Membership.ROLE_MANAGER, Membership.ROLE_OWNER]:
            if self.state == Membership.STATE_APPLIED:
                self.state = Membership.STATE_ACCEPTED
                self.save()
                signals.accepted_membership.send(sender=self, membership=self)
                return True
        return False

    def reject(self, by):
        role = self.team.role_for(by)
        if role in [Membership.ROLE_MANAGER, Membership.ROLE_OWNER]:
            if self.state == Membership.STATE_APPLIED:
                self.state = Membership.STATE_REJECTED
                self.save()
                signals.rejected_membership.send(sender=self, membership=self)
                return True
        return False

    def joined(self):
        self.user = self.invite.to_user
        if self.team.manager_access == Team.MANAGER_ACCESS_ADD:
            self.state = Membership.STATE_AUTO_JOINED
        else:
            self.state = Membership.STATE_INVITED
        self.save()

    def status(self):
        if self.user:
            return self.get_state_display()
        if self.invite:
            return self.invite.get_status_display()
        return "Unknown"

    def resend_invite(self):
        if self.invite is not None:
            code = self.invite.signup_code
            code.expiry = timezone.now() + datetime.timedelta(days=5)
            code.save()
            code.send()
            signals.resent_invite.send(sender=self, membership=self)

    def remove(self):
        if self.invite is not None:
            self.invite.signup_code.delete()
            self.invite.delete()
        self.delete()
        signals.removed_membership.send(sender=Membership, team=self.team, user=self.user)

    @property
    def invitee(self):
        return self.user or self.invite.to_user_email

@python_2_unicode_compatible
class SimpleMembership(BaseMembership):

    team = models.ForeignKey(SimpleTeam, related_name="memberships", verbose_name=_("team"))

    def __str__(self):
        return "{0} in {1}".format(self.user, self.team)

    class Meta:
        unique_together = [("team", "user", "invite")]
        verbose_name = _("Simple Membership")
        verbose_name_plural = _("Simple Memberships")

@python_2_unicode_compatible
class Membership(BaseMembership):

    team = models.ForeignKey(Team, related_name="memberships", verbose_name=_("team"))

    def __str__(self):
        return "{0} in {1}".format(self.user, self.team)

    class Meta:
        unique_together = [("team", "user", "invite")]
        verbose_name = _("Membership")
        verbose_name_plural = _("Memberships")

reversion.register(SimpleMembership)
reversion.register(Membership)
<commit_msg>BaseMembership has to be abstract<commit_after>import datetime
import os
import uuid

from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

from pinax.invitations.models import JoinInvitation
from reversion import revisions as reversion
from slugify import slugify

from . import signals

def avatar_upload(instance, filename):
    ext = filename.split(".")[-1]
    filename = "%s.%s" % (uuid.uuid4(), ext)
    return os.path.join("avatars", filename)

def create_slug(name):
    return slugify(name)[:50]

class BaseTeam(models.Model):

    MEMBER_ACCESS_OPEN = "open"
    MEMBER_ACCESS_APPLICATION = "application"
    MEMBER_ACCESS_INVITATION = "invitation"

    MANAGER_ACCESS_ADD = "add someone"
    MANAGER_ACCESS_INVITE = "invite someone"

    MEMBER_ACCESS_CHOICES = [
        (MEMBER_ACCESS_OPEN, _("open")),
        (MEMBER_ACCESS_APPLICATION, _("by application")),
        (MEMBER_ACCESS_INVITATION, _("by invitation"))
    ]

    MANAGER_ACCESS_CHOICES = [
        (MANAGER_ACCESS_ADD, _("add someone")),
        (MANAGER_ACCESS_INVITE, _("invite someone"))
    ]

    member_access = models.CharField(max_length=20, choices=MEMBER_ACCESS_CHOICES, verbose_name=_("member access"))
    manager_access = models.CharField(max_length=20, choices=MANAGER_ACCESS_CHOICES, verbose_name=_("manager access"))

    class Meta:
        abstract = True
        verbose_name = _("Base")
        verbose_name_plural = _("Bases")

    def can_join(self, user):
        state = self.state_for(user)
        if self.member_access == BaseTeam.MEMBER_ACCESS_OPEN and state is None:
            return True
        elif state == BaseMembership.STATE_INVITED:
            return True
        else:
            return False

    def can_leave(self, user):
        # managers can't leave at the moment
        role = self.role_for(user)
        return role == BaseMembership.ROLE_MEMBER

    def can_apply(self, user):
        state = self.state_for(user)
        return self.member_access == BaseTeam.MEMBER_ACCESS_APPLICATION and state is None

    @property
    def applicants(self):
        return self.memberships.filter(state=BaseMembership.STATE_APPLIED)

    @property
    def invitees(self):
        return self.memberships.filter(state=BaseMembership.STATE_INVITED)

    @property
    def declines(self):
        return self.memberships.filter(state=BaseMembership.STATE_DECLINED)

    @property
    def rejections(self):
        return self.memberships.filter(state=BaseMembership.STATE_REJECTED)

    @property
    def acceptances(self):
        return self.memberships.filter(state__in=[
            BaseMembership.STATE_ACCEPTED,
            BaseMembership.STATE_AUTO_JOINED]
        )

    @property
    def members(self):
        return self.acceptances.filter(role=BaseMembership.ROLE_MEMBER)

    @property
    def managers(self):
        return self.acceptances.filter(role=BaseMembership.ROLE_MANAGER)

    @property
    def owners(self):
        return self.acceptances.filter(role=BaseMembership.ROLE_OWNER)

    def is_owner_or_manager(self, user):
        return self.acceptances.filter(
            role__in=[
                BaseMembership.ROLE_OWNER,
                BaseMembership.ROLE_MANAGER
            ],
            user=user
        ).exists()

    def is_member(self, user):
        return self.members.filter(user=user).exists()

    def is_manager(self, user):
        return self.managers.filter(user=user).exists()

    def is_owner(self, user):
        return self.owners.filter(user=user).exists()

    def is_on_team(self, user):
        return self.acceptances.filter(user=user).exists()

    def add_member(self, user, role=None, state=None):
        # we do this, rather than put the BaseMembership constants in declaration
        # because BaseMembership is not yet defined
        if role is None:
            role = BaseMembership.ROLE_MEMBER
        if state is None:
            state = BaseMembership.STATE_AUTO_JOINED

        membership, created = BaseMembership.objects.get_or_create(
            team=self,
            user=user,
            defaults={"role": role, "state": state},
        )
        return membership

    def add_user(self, user, role):
        state = BaseMembership.STATE_AUTO_JOINED
        if self.manager_access == BaseTeam.MANAGER_ACCESS_INVITE:
            state = BaseMembership.STATE_INVITED
        membership, _ = self.memberships.get_or_create(
            user=user,
            defaults={"role": role, "state": state}
        )
        signals.added_member.send(sender=self, membership=membership)
        return membership

    def invite_user(self, from_user, to_email, role, message=None):
        if not JoinInvitation.objects.filter(signup_code__email=to_email).exists():
            invite = JoinInvitation.invite(from_user, to_email, message, send=False)
            membership, _ = self.memberships.get_or_create(
                invite=invite,
                defaults={"role": role, "state": BaseMembership.STATE_INVITED}
            )
            invite.send_invite()
            signals.invited_user.send(sender=self, membership=membership)
            return membership

    def for_user(self, user):
        try:
            return self.memberships.get(user=user)
        except BaseMembership.DoesNotExist:
            pass

    def state_for(self, user):
        membership = self.for_user(user=user)
        if membership:
            return membership.state

    def role_for(self, user):
        membership = self.for_user(user)
        if membership:
            return membership.role

class SimpleTeam(BaseTeam):

    class Meta:
        verbose_name = _("Simple Team")
        verbose_name_plural = _("Simple Teams")

@python_2_unicode_compatible
class Team(BaseTeam):

    slug = models.SlugField(unique=True)
    name = models.CharField(max_length=100, verbose_name=_("name"))
    avatar = models.ImageField(upload_to=avatar_upload, blank=True, verbose_name=_("avatar"))
    description = models.TextField(blank=True, verbose_name=_("description"))
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="teams_created", verbose_name=_("creator"))
    created = models.DateTimeField(default=timezone.now, editable=False, verbose_name=_("created"))

    class Meta:
        verbose_name = _("Team")
        verbose_name_plural = _("Teams")

    def get_absolute_url(self):
        return reverse("team_detail", args=[self.slug])

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.id:
            self.slug = create_slug(self.name)
        self.full_clean()
        super(Team, self).save(*args, **kwargs)

class BaseMembership(models.Model):

    STATE_APPLIED = "applied"
    STATE_INVITED = "invited"
    STATE_DECLINED = "declined"
    STATE_REJECTED = "rejected"
    STATE_ACCEPTED = "accepted"
    STATE_AUTO_JOINED = "auto-joined"

    ROLE_MEMBER = "member"
    ROLE_MANAGER = "manager"
    ROLE_OWNER = "owner"

    STATE_CHOICES = [
        (STATE_APPLIED, _("applied")),
        (STATE_INVITED, _("invited")),
        (STATE_DECLINED, _("declined")),
        (STATE_REJECTED, _("rejected")),
        (STATE_ACCEPTED, _("accepted")),
        (STATE_AUTO_JOINED, _("auto joined"))
    ]

    ROLE_CHOICES = [
        (ROLE_MEMBER, _("member")),
        (ROLE_MANAGER, _("manager")),
        (ROLE_OWNER, _("owner"))
    ]

    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="memberships", null=True, blank=True, verbose_name=_("user"))
    invite = models.ForeignKey(JoinInvitation, related_name="memberships", null=True, blank=True, verbose_name=_("invite"))
    state = models.CharField(max_length=20, choices=STATE_CHOICES, verbose_name=_("state"))
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default=ROLE_MEMBER, verbose_name=_("role"))
    created = models.DateTimeField(default=timezone.now, verbose_name=_("created"))

    class Meta:
        abstract = True

    def is_owner(self):
        return self.role == BaseMembership.ROLE_OWNER

    def is_manager(self):
        return self.role == BaseMembership.ROLE_MANAGER

    def is_member(self):
        return self.role == BaseMembership.ROLE_MEMBER

    def promote(self, by):
        role = self.team.role_for(by)
        if role in [BaseMembership.ROLE_MANAGER, BaseMembership.ROLE_OWNER]:
            if self.role == Membership.ROLE_MEMBER:
                self.role = Membership.ROLE_MANAGER
                self.save()
                signals.promoted_member.send(sender=self, membership=self)
                return True
        return False

    def demote(self, by):
        role = self.team.role_for(by)
        if role in [Membership.ROLE_MANAGER, Membership.ROLE_OWNER]:
            if self.role == Membership.ROLE_MANAGER:
                self.role = Membership.ROLE_MEMBER
                self.save()
                signals.demoted_member.send(sender=self, membership=self)
                return True
        return False

    def accept(self, by):
        role = self.team.role_for(by)
        if role in [Membership.ROLE_MANAGER, Membership.ROLE_OWNER]:
            if self.state == Membership.STATE_APPLIED:
                self.state = Membership.STATE_ACCEPTED
                self.save()
                signals.accepted_membership.send(sender=self, membership=self)
                return True
        return False

    def reject(self, by):
        role = self.team.role_for(by)
        if role in [Membership.ROLE_MANAGER, Membership.ROLE_OWNER]:
            if self.state == Membership.STATE_APPLIED:
                self.state = Membership.STATE_REJECTED
                self.save()
                signals.rejected_membership.send(sender=self, membership=self)
                return True
        return False

    def joined(self):
        self.user = self.invite.to_user
        if self.team.manager_access == Team.MANAGER_ACCESS_ADD:
            self.state = Membership.STATE_AUTO_JOINED
        else:
            self.state = Membership.STATE_INVITED
        self.save()

    def status(self):
        if self.user:
            return self.get_state_display()
        if self.invite:
            return self.invite.get_status_display()
        return "Unknown"

    def resend_invite(self):
        if self.invite is not None:
            code = self.invite.signup_code
            code.expiry = timezone.now() + datetime.timedelta(days=5)
            code.save()
            code.send()
            signals.resent_invite.send(sender=self, membership=self)

    def remove(self):
        if self.invite is not None:
            self.invite.signup_code.delete()
            self.invite.delete()
        self.delete()
        signals.removed_membership.send(sender=Membership, team=self.team, user=self.user)

    @property
    def invitee(self):
        return self.user or self.invite.to_user_email

@python_2_unicode_compatible
class SimpleMembership(BaseMembership):

    team = models.ForeignKey(SimpleTeam, related_name="memberships", verbose_name=_("team"))

    def __str__(self):
        return "{0} in {1}".format(self.user, self.team)

    class Meta:
        unique_together = [("team", "user", "invite")]
        verbose_name = _("Simple Membership")
        verbose_name_plural = _("Simple Memberships")

@python_2_unicode_compatible
class Membership(BaseMembership):

    team = models.ForeignKey(Team, related_name="memberships", verbose_name=_("team"))

    def __str__(self):
        return "{0} in {1}".format(self.user, self.team)

    class Meta:
        unique_together = [("team", "user", "invite")]
        verbose_name = _("Membership")
        verbose_name_plural = _("Memberships")

reversion.register(SimpleMembership)
reversion.register(Membership)
<|endoftext|>