<commit_before># TNC Python interface
# @(#) $Jeannot: tnc.py,v 1.11 2005/01/28 18:27:31 js Exp $

# Copyright (c) 2004-2005, Jean-Sebastien Roy (js@jeannot.org)

# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:

# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""
TNC: A python interface to the TNC non-linear optimizer

TNC is a non-linear optimizer. To use it, you must provide a function to
minimize. The function must take one argument: the list of coordinates where to
evaluate the function; and it must return either a tuple, whose first element is the
value of the function, and whose second argument is the gradient of the function
(as a list of values); or None, to abort the minimization.
"""
from scipy.optimize import moduleTNC
from numpy import asarray, inf, array

__all__ = ['fmin_tnc']

MSG_NONE = 0 # No messages
MSG_ITER = 1 # One line per iteration
MSG_INFO = 2 # Informational messages
MSG_VERS = 4 # Version info
MSG_EXIT = 8 # Exit reasons
MSG_ALL = MSG_ITER + MSG_INFO + MSG_VERS + MSG_EXIT

MSGS = {
        MSG_NONE : "No messages",
        MSG_ITER : "One line per iteration",
        MSG_INFO : "Informational messages",
        MSG_VERS : "Version info",
        MSG_EXIT : "Exit reasons",
        MSG_ALL  : "All messages"
}

INFEASIBLE   = -1 # Infeasible (low > up)
LOCALMINIMUM =  0 # Local minima reach (|pg| ~= 0)
FCONVERGED   =  1 # Converged (|f_n-f_(n-1)| ~= 0)
XCONVERGED   =  2 # Converged (|x_n-x_(n-1)| ~= 0)
MAXFUN       =  3 # Max. number of function evaluations reach
LSFAIL       =  4 # Linear search failed
CONSTANT     =  5 # All lower bounds are equal to the upper bounds
NOPROGRESS   =  6 # Unable to progress
USERABORT    =  7 # User requested end of minimization

RCSTRINGS = {
        INFEASIBLE   : "Infeasible (low > up)",
        LOCALMINIMUM : "Local minima reach (|pg| ~= 0)",
        FCONVERGED   : "Converged (|f_n-f_(n-1)| ~= 0)",
        XCONVERGED   : "Converged (|x_n-x_(n-1)| ~= 0)",
        MAXFUN       : "Max. number of function evaluations reach",
        LSFAIL       : "Linear search failed",
        CONSTANT     : "All lower bounds are equal to the upper bounds",
        NOPROGRESS   : "Unable to progress",
        USERABORT    : "User requested end of minimization"
}

# Changes to interface made by Travis Oliphant, Apr. 2004 for inclusion in
#  SciPy

import optimize
approx_fprime = optimize.approx_fprime

def fmin_tnc(func, x0, fprime=None, args=(), approx_grad=0,
             bounds=None, epsilon=1e-8, scale=None, offset=None,
             messages=MSG_ALL, maxCGit=-1, maxfun=None, eta=-1,
             stepmx=0, accuracy=0, fmin=0, ftol=-1, xtol=-1, pgtol=-1,
             rescale=-1, disp=None):
    """
    Minimize a function with variables subject to bounds, using
    gradient information in a truncated Newton algorithm. This
    method wraps a C implementation of the algorithm.

    Parameters
    ----------
    func : callable ``func(x, *args)``
        Function to minimize.  Must do one of
        1. Return f and g, where f is
        the value of the function and g its gradient (a list of
        floats).
        2. Return the function value but supply gradient function
        seperately as fprime
        3. Return the function value and set approx_grad=True.
        If the function returns None, the minimization
        is aborted.
    x0 : list of floats
        Initial estimate of minimum.
    fprime : callable ``fprime(x, *args)``
        Gradient of func. If None, then either func must return the
        function value and the gradient (``f,g = func(x, *args)``)
        or approx_grad must be True.
    args : tuple
        Arguments to pass to function.
    approx_grad : bool
        If true, approximate the gradient numerically.
    bounds : list
        (min, max) pairs for each element in x0, defining the
        bounds on that parameter. Use None or +/-inf for one of
        min or max when there is no bound in that direction.
    epsilon: float
        Used if approx_grad is True. The stepsize in a finite
        difference approximation for fprime.
    scale : list of floats
        Scaling factors to apply to each variable.  If None, the
        factors are up-low for interval bounded variables and
        1+|x] fo the others.  Defaults to None
    offset : float
        Value to substract from each variable.  If None, the
        offsets are (up+low)/2 for interval bounded variables
        and x for the others.
    messages :
        Bit mask used to select messages display during
        minimization values defined in the MSGS dict.  Defaults to
        MGS_ALL.
    disp : int
        Integer interface to messages.  0 = no message, 5 = all messages
    maxCGit : int
        Maximum number of hessian*vector evaluations per main
        iteration.  If maxCGit == 0, the direction chosen is
        -gradient if maxCGit < 0, maxCGit is set to
        max(1,min(50,n/2)).  Defaults to -1.
    maxfun : int
        Maximum number of function evaluation.  if None, maxfun is
        set to max(100, 10*len(x0)).  Defaults to None.
    eta : float
        Severity of the line search. if < 0 or > 1, set to 0.25.
        Defaults to -1.
    stepmx : float
        Maximum step for the line search.  May be increased during
        call.  If too small, it will be set to 10.0.  Defaults to 0.
    accuracy : float
        Relative precision for finite difference calculations.  If
        <= machine_precision, set to sqrt(machine_precision).
        Defaults to 0.
    fmin : float
        Minimum function value estimate.  Defaults to 0.
    ftol : float
        Precision goal for the value of f in the stoping criterion.
        If ftol < 0.0, ftol is set to 0.0 defaults to -1.
    xtol : float
        Precision goal for the value of x in the stopping
        criterion (after applying x scaling factors).  If xtol <
        0.0, xtol is set to sqrt(machine_precision).  Defaults to
        -1.
    pgtol : float
        Precision goal for the value of the projected gradient in
        the stopping criterion (after applying x scaling factors).
        If pgtol < 0.0, pgtol is set to 1e-2 * sqrt(accuracy).
        Setting it to 0.0 is not recommended.  Defaults to -1.
    rescale : float
        Scaling factor (in log10) used to trigger f value
        rescaling.  If 0, rescale at each iteration.  If a large
        value, never rescale.  If < 0, rescale is set to 1.3.

    Returns
    -------
    x : list of floats
        The solution.
    nfeval : int
        The number of function evaluations.
    rc : int
        Return code as defined in the RCSTRINGS dict.

    Notes
    -----
    The underlying algorithm is truncated Newton, also called
    Newton Conjugate-Gradient. This method differs from
    scipy.optimize.fmin_ncg in that

    1. It wraps a C implementation of the algorithm
    2. It allows each variable to be given an upper and lower bound.

    The algorithm incoporates the bound constraints by determining
    the descent direction as in an unconstrained truncated Newton,
    but never taking a step-size large enough to leave the space
    of feasible x's. The algorithm keeps track of a set of
    currently active constraints, and ignores them when computing
    the minimum allowable step size. (The x's associated with the
    active constraint are kept fixed.) If the maximum allowable
    step size is zero then a new constraint is added. At the end
    of each iteration one of the constraints may be deemed no
    longer active and removed. A constraint is considered
    no longer active is if it is currently active
    but the gradient for that variable points inward from the
    constraint. The specific constraint removed is the one
    associated with the variable of largest index whose
    constraint is no longer active.

    References
    ----------
    Wright S., Nocedal J. (2006), 'Numerical Optimization'

    Nash S.G. (1984), "Newton-Type Minimization Via the Lanczos Method",
    SIAM Journal of Numerical Analysis 21, pp. 770-778

    """
    x0 = asarray(x0, dtype=float).tolist()
    n = len(x0)

    if bounds is None:
        bounds = [(None,None)] * n
    if len(bounds) != n:
        raise ValueError('length of x0 != length of bounds')

    if disp is not None:
        messages = {0:MSG_NONE, 1:MSG_ITER, 2:MSG_INFO, 3:MSG_VERS,
                    4:MSG_EXIT, 5:MSG_ALL}.get(disp, MSG_ALL)

    if approx_grad:
        def func_and_grad(x):
            x = asarray(x)
            f = func(x, *args)
            g = approx_fprime(x, func, epsilon, *args)
            return f, list(g)
    elif fprime is None:
        def func_and_grad(x):
            x = asarray(x)
            f, g = func(x, *args)
            return f, list(g)
    else:
        def func_and_grad(x):
            x = asarray(x)
            f = func(x, *args)
            g = fprime(x, *args)
            return f, list(g)

    """
    low, up   : the bounds (lists of floats)
                if low is None, the lower bounds are removed.
                if up is None, the upper bounds are removed.
                low and up defaults to None
    """
    low = [0]*n
    up = [0]*n
    for i in range(n):
        if bounds[i] is None: l, u = -inf, inf
        else:
            l,u = bounds[i]
            if l is None:
                low[i] = -inf
            else:
                low[i] = l
            if u is None:
                up[i] = inf
            else:
                up[i] = u

    if scale is None:
        scale = []

    if offset is None:
        offset = []

    if maxfun is None:
        maxfun = max(100, 10*len(x0))

    rc, nf, x = moduleTNC.minimize(func_and_grad, x0, low, up, scale, offset,
            messages, maxCGit, maxfun, eta, stepmx, accuracy,
            fmin, ftol, xtol, pgtol, rescale)
    return array(x), nf, rc

if __name__ == '__main__':
    # Examples for TNC

    def example():
        print "Example"
        # A function to minimize
        def function(x):
            f = pow(x[0],2.0)+pow(abs(x[1]),3.0)
            g = [0,0]
            g[0] = 2.0*x[0]
            g[1] = 3.0*pow(abs(x[1]),2.0)
            if x[1]<0:
                g[1] = -g[1]
            return f, g

        # Optimizer call
        x, nf, rc = fmin_tnc(function, [-7, 3], bounds=([-10, 1], [10, 10]))

        print "After", nf, "function evaluations, TNC returned:", RCSTRINGS[rc]
        print "x =", x
        print "exact value = [0, 1]"
        print

    example()
<commit_msg>ENH: optimize: add _minimize_tnc function with standard signature<commit_after># TNC Python interface
# @(#) $Jeannot: tnc.py,v 1.11 2005/01/28 18:27:31 js Exp $

# Copyright (c) 2004-2005, Jean-Sebastien Roy (js@jeannot.org)

# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:

# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""
TNC: A python interface to the TNC non-linear optimizer

TNC is a non-linear optimizer. To use it, you must provide a function to
minimize. The function must take one argument: the list of coordinates where to
evaluate the function; and it must return either a tuple, whose first element is the
value of the function, and whose second argument is the gradient of the function
(as a list of values); or None, to abort the minimization.
"""
from scipy.optimize import moduleTNC
from numpy import asarray, inf, array

__all__ = ['fmin_tnc']

MSG_NONE = 0 # No messages
MSG_ITER = 1 # One line per iteration
MSG_INFO = 2 # Informational messages
MSG_VERS = 4 # Version info
MSG_EXIT = 8 # Exit reasons
MSG_ALL = MSG_ITER + MSG_INFO + MSG_VERS + MSG_EXIT

MSGS = {
        MSG_NONE : "No messages",
        MSG_ITER : "One line per iteration",
        MSG_INFO : "Informational messages",
        MSG_VERS : "Version info",
        MSG_EXIT : "Exit reasons",
        MSG_ALL  : "All messages"
}

INFEASIBLE   = -1 # Infeasible (low > up)
LOCALMINIMUM =  0 # Local minima reach (|pg| ~= 0)
FCONVERGED   =  1 # Converged (|f_n-f_(n-1)| ~= 0)
XCONVERGED   =  2 # Converged (|x_n-x_(n-1)| ~= 0)
MAXFUN       =  3 # Max. number of function evaluations reach
LSFAIL       =  4 # Linear search failed
CONSTANT     =  5 # All lower bounds are equal to the upper bounds
NOPROGRESS   =  6 # Unable to progress
USERABORT    =  7 # User requested end of minimization

RCSTRINGS = {
        INFEASIBLE   : "Infeasible (low > up)",
        LOCALMINIMUM : "Local minima reach (|pg| ~= 0)",
        FCONVERGED   : "Converged (|f_n-f_(n-1)| ~= 0)",
        XCONVERGED   : "Converged (|x_n-x_(n-1)| ~= 0)",
        MAXFUN       : "Max. number of function evaluations reach",
        LSFAIL       : "Linear search failed",
        CONSTANT     : "All lower bounds are equal to the upper bounds",
        NOPROGRESS   : "Unable to progress",
        USERABORT    : "User requested end of minimization"
}

# Changes to interface made by Travis Oliphant, Apr. 2004 for inclusion in
#  SciPy

import optimize
approx_fprime = optimize.approx_fprime

def fmin_tnc(func, x0, fprime=None, args=(), approx_grad=0,
             bounds=None, epsilon=1e-8, scale=None, offset=None,
             messages=MSG_ALL, maxCGit=-1, maxfun=None, eta=-1,
             stepmx=0, accuracy=0, fmin=0, ftol=-1, xtol=-1, pgtol=-1,
             rescale=-1, disp=None):
    """
    Minimize a function with variables subject to bounds, using
    gradient information in a truncated Newton algorithm. This
    method wraps a C implementation of the algorithm.

    Parameters
    ----------
    func : callable ``func(x, *args)``
        Function to minimize.  Must do one of
        1. Return f and g, where f is
        the value of the function and g its gradient (a list of
        floats).
        2. Return the function value but supply gradient function
        seperately as fprime
        3. Return the function value and set approx_grad=True.
        If the function returns None, the minimization
        is aborted.
    x0 : list of floats
        Initial estimate of minimum.
    fprime : callable ``fprime(x, *args)``
        Gradient of func. If None, then either func must return the
        function value and the gradient (``f,g = func(x, *args)``)
        or approx_grad must be True.
    args : tuple
        Arguments to pass to function.
    approx_grad : bool
        If true, approximate the gradient numerically.
    bounds : list
        (min, max) pairs for each element in x0, defining the
        bounds on that parameter. Use None or +/-inf for one of
        min or max when there is no bound in that direction.
    epsilon: float
        Used if approx_grad is True. The stepsize in a finite
        difference approximation for fprime.
    scale : list of floats
        Scaling factors to apply to each variable.  If None, the
        factors are up-low for interval bounded variables and
        1+|x] fo the others.  Defaults to None
    offset : float
        Value to substract from each variable.  If None, the
        offsets are (up+low)/2 for interval bounded variables
        and x for the others.
    messages :
        Bit mask used to select messages display during
        minimization values defined in the MSGS dict.  Defaults to
        MGS_ALL.
    disp : int
        Integer interface to messages.  0 = no message, 5 = all messages
    maxCGit : int
        Maximum number of hessian*vector evaluations per main
        iteration.  If maxCGit == 0, the direction chosen is
        -gradient if maxCGit < 0, maxCGit is set to
        max(1,min(50,n/2)).  Defaults to -1.
    maxfun : int
        Maximum number of function evaluation.  if None, maxfun is
        set to max(100, 10*len(x0)).  Defaults to None.
    eta : float
        Severity of the line search. if < 0 or > 1, set to 0.25.
        Defaults to -1.
    stepmx : float
        Maximum step for the line search.  May be increased during
        call.  If too small, it will be set to 10.0.  Defaults to 0.
    accuracy : float
        Relative precision for finite difference calculations.  If
        <= machine_precision, set to sqrt(machine_precision).
        Defaults to 0.
    fmin : float
        Minimum function value estimate.  Defaults to 0.
    ftol : float
        Precision goal for the value of f in the stoping criterion.
        If ftol < 0.0, ftol is set to 0.0 defaults to -1.
    xtol : float
        Precision goal for the value of x in the stopping
        criterion (after applying x scaling factors).  If xtol <
        0.0, xtol is set to sqrt(machine_precision).  Defaults to
        -1.
    pgtol : float
        Precision goal for the value of the projected gradient in
        the stopping criterion (after applying x scaling factors).
        If pgtol < 0.0, pgtol is set to 1e-2 * sqrt(accuracy).
        Setting it to 0.0 is not recommended.  Defaults to -1.
    rescale : float
        Scaling factor (in log10) used to trigger f value
        rescaling.  If 0, rescale at each iteration.  If a large
        value, never rescale.  If < 0, rescale is set to 1.3.

    Returns
    -------
    x : list of floats
        The solution.
    nfeval : int
        The number of function evaluations.
    rc : int
        Return code as defined in the RCSTRINGS dict.

    Notes
    -----
    The underlying algorithm is truncated Newton, also called
    Newton Conjugate-Gradient. This method differs from
    scipy.optimize.fmin_ncg in that

    1. It wraps a C implementation of the algorithm
    2. It allows each variable to be given an upper and lower bound.

    The algorithm incoporates the bound constraints by determining
    the descent direction as in an unconstrained truncated Newton,
    but never taking a step-size large enough to leave the space
    of feasible x's. The algorithm keeps track of a set of
    currently active constraints, and ignores them when computing
    the minimum allowable step size. (The x's associated with the
    active constraint are kept fixed.) If the maximum allowable
    step size is zero then a new constraint is added. At the end
    of each iteration one of the constraints may be deemed no
    longer active and removed. A constraint is considered
    no longer active is if it is currently active
    but the gradient for that variable points inward from the
    constraint. The specific constraint removed is the one
    associated with the variable of largest index whose
    constraint is no longer active.

    References
    ----------
    Wright S., Nocedal J. (2006), 'Numerical Optimization'

    Nash S.G. (1984), "Newton-Type Minimization Via the Lanczos Method",
    SIAM Journal of Numerical Analysis 21, pp. 770-778

    """
    # handle fprime/approx_grad
    if approx_grad:
        fun = func
        jac = None
    elif fprime is None:
        def fun(x):
            return func(x, *args)[0]
        def jac(x):
            return func(x, *args)[1]
    else:
        fun = func
        jac = fprime

    # build options
    opts = {'eps'  : epsilon,
            'scale': scale,
            'offset': offset,
            'messages': messages,
            'mesg_num': disp,
            'disp': False, # for the two previous options to be considered
            'maxCGit': maxCGit,
            'maxfev': maxfun,
            'eta': eta,
            'stepmx': stepmx,
            'accuracy': accuracy,
            'minfev': fmin,
            'ftol': ftol,
            'xtol': xtol,
            'pgtol': pgtol,
            'rescale': rescale,
            'disp': False}

    x, info = _minimize_tnc(fun, x0, args, jac, bounds, options=opts,
                            full_output=True)

    return x, info['nfev'], info['status']

def _minimize_tnc(fun, x0, args=(), jac=None, bounds=None, options={},
                  full_output=False):
    """
    Minimize a scalar function of one or more variables using a truncated
    Newton (TNC) algorithm.

    Options for the TNC algorithm are:
        eps: float
            Step size used for numerical approximation of the jacobian.
        scale : list of floats
            Scaling factors to apply to each variable.  If None, the
            factors are up-low for interval bounded variables and
            1+|x] fo the others.  Defaults to None
        offset : float
            Value to substract from each variable.  If None, the
            offsets are (up+low)/2 for interval bounded variables
            and x for the others.
        disp : bool
           Set to True to print convergence messages.
        maxCGit : int
            Maximum number of hessian*vector evaluations per main
            iteration.  If maxCGit == 0, the direction chosen is
            -gradient if maxCGit < 0, maxCGit is set to
            max(1,min(50,n/2)).  Defaults to -1.
        maxfev : int
            Maximum number of function evaluation.  if None, `maxfev` is
            set to max(100, 10*len(x0)).  Defaults to None.
        eta : float
            Severity of the line search. if < 0 or > 1, set to 0.25.
            Defaults to -1.
        stepmx : float
            Maximum step for the line search.  May be increased during
            call.  If too small, it will be set to 10.0.  Defaults to 0.
        accuracy : float
            Relative precision for finite difference calculations.  If
            <= machine_precision, set to sqrt(machine_precision).
            Defaults to 0.
        minfev : float
            Minimum function value estimate.  Defaults to 0.
        ftol : float
            Precision goal for the value of f in the stoping criterion.
            If ftol < 0.0, ftol is set to 0.0 defaults to -1.
        xtol : float
            Precision goal for the value of x in the stopping
            criterion (after applying x scaling factors).  If xtol <
            0.0, xtol is set to sqrt(machine_precision).  Defaults to
            -1.
        pgtol : float
            Precision goal for the value of the projected gradient in
            the stopping criterion (after applying x scaling factors).
            If pgtol < 0.0, pgtol is set to 1e-2 * sqrt(accuracy).
            Setting it to 0.0 is not recommended.  Defaults to -1.
        rescale : float
            Scaling factor (in log10) used to trigger f value
            rescaling.  If 0, rescale at each iteration.  If a large
            value, never rescale.  If < 0, rescale is set to 1.3.

    This function is called by the `minimize` function with `method=TNC`.
    It is not supposed to be called directly.
    """
    # retrieve useful options
    epsilon  = options.get('eps', 1e-8)
    scale    = options.get('scale')
    offset   = options.get('offset')
    messages = options.get('messages', MSG_ALL)
    mesg_num  = options.get('mesg_num')
    maxCGit  = options.get('maxCGit', -1)
    maxfun   = options.get('maxfev')
    eta      = options.get('eta', -1)
    stepmx   = options.get('stepmx', 0)
    accuracy = options.get('accuracy', 0)
    fmin     = options.get('minfev', 0)
    ftol     = options.get('ftol', -1)
    xtol     = options.get('xtol', -1)
    pgtol    = options.get('pgtol', -1)
    rescale  = options.get('rescale', -1)
    disp     = options.get('disp', True)

    x0 = asarray(x0, dtype=float).tolist()
    n = len(x0)

    if bounds is None:
        bounds = [(None,None)] * n
    if len(bounds) != n:
        raise ValueError('length of x0 != length of bounds')

    if disp or mesg_num is not None:
        messages = {0:MSG_NONE, 1:MSG_ITER, 2:MSG_INFO, 3:MSG_VERS,
                    4:MSG_EXIT, 5:MSG_ALL}.get(mesg_num, MSG_ALL)
    else:
        messages = MSG_NONE

    if jac is None:
        def func_and_grad(x):
            x = asarray(x)
            f = fun(x, *args)
            g = approx_fprime(x, fun, epsilon, *args)
            return f, list(g)
    else:
        def func_and_grad(x):
            x = asarray(x)
            f = fun(x, *args)
            g = jac(x, *args)
            return f, list(g)

    """
    low, up   : the bounds (lists of floats)
                if low is None, the lower bounds are removed.
                if up is None, the upper bounds are removed.
                low and up defaults to None
    """
    low = [0]*n
    up = [0]*n
    for i in range(n):
        if bounds[i] is None: l, u = -inf, inf
        else:
            l,u = bounds[i]
            if l is None:
                low[i] = -inf
            else:
                low[i] = l
            if u is None:
                up[i] = inf
            else:
                up[i] = u

    if scale is None:
        scale = []

    if offset is None:
        offset = []

    if maxfun is None:
        maxfun = max(100, 10*len(x0))

    rc, nf, x = moduleTNC.minimize(func_and_grad, x0, low, up, scale, offset,
            messages, maxCGit, maxfun, eta, stepmx, accuracy,
            fmin, ftol, xtol, pgtol, rescale)

    xopt = array(x)
    if full_output:
        funv, jacv = func_and_grad(xopt)
        info = {'solution': xopt,
                'fun': funv,
                'jac': jacv,
                'nfev': nf,
                'status': rc,
                'message': RCSTRINGS[rc],
                'success': -1 < rc < 3}
        return xopt, info
    else:
        return xopt

if __name__ == '__main__':
    # Examples for TNC

    def example():
        print "Example"
        # A function to minimize
        def function(x):
            f = pow(x[0],2.0)+pow(abs(x[1]),3.0)
            g = [0,0]
            g[0] = 2.0*x[0]
            g[1] = 3.0*pow(abs(x[1]),2.0)
            if x[1]<0:
                g[1] = -g[1]
            return f, g

        # Optimizer call
        x, nf, rc = fmin_tnc(function, [-7, 3], bounds=([-10, 1], [10, 10]))

        print "After", nf, "function evaluations, TNC returned:", RCSTRINGS[rc]
        print "x =", x
        print "exact value = [0, 1]"
        print

    example()
<|endoftext|>


<commit_before>import re
import csv
import copy
from django.http import (HttpResponse, HttpResponseGone, HttpResponseRedirect,
                         HttpResponseNotFound)
from django.template import RequestContext
from django.views import generic
from django.shortcuts import (render_to_response, get_object_or_404, render,
                              redirect)
from django.db.models import Q, Count, Max, Min
from django.core.serializers import serialize
from django.core.urlresolvers import reverse
from django.contrib.syndication.views import Feed
from django.templatetags.static import static
from django.utils.feedgenerator import Atom1Feed
from django.conf import settings
from dateutil import rrule
from datetime import datetime
from pynaco.naco import normalizeSimplified
from name.decorators import jsonp
from name.models import Name, Identifier, Location
from . import serializers
from rest_framework.renderers import JSONRenderer

VOCAB_DOMAIN = settings.VOCAB_DOMAIN

class JSONResponse(HttpResponse):
    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data, renderer_context={'indent': 4})
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

def normalize_query(query_string):
    """Splits the query string in individual keywords, removing
    unnecessary spaces and grouping quoted words together.

    Example:
    >>> normalize_query('some random words "with quotes" and spaces')
        ['some', 'random', 'words', 'with quotes', 'and', 'spaces']
    """
    findterms = re.compile(r'"([^"]+)"|(\S+)').findall
    normspace = re.compile(r'\s{2,}').sub

    terms = (findterms(query_string))

    return [normspace(' ', (t[0] or t[1]).strip()) for t in terms]

def compose_query(query_string):
    """Composes a Q object from the query_string.

    This will first normalize the query_sting and split the string up
    into words. Then it will iterate over the list of words creating Q
    object for each one. Last, it reduces the generated Q objects into
    a single Q object by combining them using the & operator.

    The reduce function essential performs the following operation for
    a query_string that contains 4 words,

        (((Q1 & Q2) & Q3) & Q4)

    where Q1-4 are instances of the Q object.
    """
    # Normalize the query_string which will also split the single string
    # into a list of terms.
    terms = normalize_query(query_string)

    # Create a generator that produces an instance of the Q object
    # for each term.
    qs = (Q(name__icontains=term) for term in terms)

    # Use bitwise AND to compose the objects into a single instance of Q.
    return reduce(lambda x, y: x & y, qs)

def label(request, name_value):
    """Find a name by label.

    If the label matches a single name, users are redirected
    to the name_entry_detail page. Otherwise, if the label
    does not exist, or the specified label matches multiple Name
    objects, a status code 404 is returned.
    """
    if not name_value:
        return HttpResponseNotFound()

    normalized_name = normalizeSimplified(name_value)
    try:
        name = Name.objects.get(normalized_name=normalized_name)

        return HttpResponseRedirect(
            reverse('name_entry_detail', args=[name.name_id]))

    except Name.DoesNotExist:
        return HttpResponseNotFound(
            u'No matching term found - authoritative, or variant - for \"{0}\"'
            .format(name_value))

    except Name.MultipleObjectsReturned:
        return HttpResponseNotFound(
            u'There are multiple Name objects with the same name: \'{0}\'.'
            .format(normalized_name))

class AtomSiteNewsFeed(Feed):
    """Sets up atom feed"""

    feed_type = Atom1Feed
    link = "/name/feed/"
    title = "Name App"
    subtitle = "new records"

    def items(self):
        # last 5 added items
        return Name.objects.order_by('-date_created')[:20]

    def item_title(self, item):
        return item.name

    def item_location(self, item):
        """
        Returns an extra keyword arguments dictionary that is used
        with the `add_item` call of the feed generator. Add the
        'content' field of the 'Entry' item, to be used by the custom
        feed generator.
        """
        location_set = []
        for l in Location.objects.filter(belong_to_name=item):
            location_set.append(
                'georss:point', "%s %s" % (l.latitude, l.longitude)
            )
        return location_set

    def item_description(self, item):
        return "Name Type: %s" % item.get_name_type_label()

    def item_link(self, item):
        return VOCAB_DOMAIN + 'name/' + item.name_id + '/'

def entry_detail(request, name_id):
    """Name Entry Detail View."""
    queryset = (
        Name.objects.select_related().
        prefetch_related('identifier_set__type')
    )

    name_entry = get_object_or_404(queryset, name_id=name_id)

    if name_entry.merged_with:
        return redirect(name_entry.merged_with)

    elif name_entry.is_suppressed():
        return HttpResponseNotFound()

    elif name_entry.is_deleted():
        return HttpResponseGone('The requested record has been deleted!')

    return render(request, 'name/name_detail.html', {'name': name_entry})

def export(request):
    """Exports Names as TSV file."""
    # Create a CSV response.
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="data.tsv"'

    writer = csv.writer(response, delimiter='\t', quoting=csv.QUOTE_MINIMAL)

    # Iterate through the visible Names that are not merged with any other
    # Name records and write a row for each record.
    for n in Name.objects.visible():
        writer.writerow([
            n.get_name_type_label().lower(),
            n.name.encode('utf-8'),
            request.build_absolute_uri(n.get_absolute_url())
        ])

    return response

def opensearch(request):
    """Renders Opensearch XML file.

    Opensearch URL templates are composed in the view
    because it is easier than creating them in the template.
    """
    # Query templates for the URLs.
    search_query = '{0}?q_type=Personal&q={{searchTerms}}'
    search_json_query = '{0}?q={{searchTerms}}'

    # Build the absolute URLs.
    search_url = request.build_absolute_uri(reverse('name_search'))
    search_json_url = request.build_absolute_uri(reverse('name_names'))

    # Compose the full URLs  that will be sent to the template.
    search = search_query.format(search_url)
    search_json = search_json_query.format(search_json_url)
    image = request.build_absolute_uri(static('name/img/favicon.png'))

    urls = dict(search=search, search_json=search_json, image=image)

    return render(request, 'name/opensearch.xml',
                  urls, content_type='text/xml')

def about(request):
    """Renders the name about page to the user."""
    return render(request, 'name/about.html')

def prepare_graph_date_range():
    """
    Several functions use the same code to prepare the dates and values
    for the graphing of event data, so we can make a function for it.
    DRY 4 LYPHE

    returns list of lists
    """

    name_objects = Name.objects.all()
    # grab the dates for the bounds of the graphs
    if name_objects.count() > 0:
        dates = Name.objects.all().aggregate(
            Min("date_created"), Max('date_created'))
        system_start_date = dates['date_created__min']
        system_end_date = dates['date_created__max']

        # setup list for month call data
        daily_edit_counts = []
        # we need to first fill in the first month, because it won't
        # grab that when the for loop iterates to a new month.
        month = system_start_date.strftime('%Y-%m')
        daily_edit_counts.append([datetime.strptime(month, '%Y-%m'), 0])
        # start with a zero count and reset this as we cross a month
        for dt in rrule.rrule(
            rrule.DAILY,
            dtstart=system_start_date,
            until=system_end_date,
        ):
            # if we change months
            if month != dt.strftime('%Y-%m'):
                month = dt.strftime('%Y-%m')
                # make the year and drop in if i doenst exist
                daily_edit_counts.append(
                    [datetime.strptime(month, '%Y-%m'), 0])
        return daily_edit_counts
    else:
        raise Exception('no name objects to construct graph')

def calc_total_by_month(**kwargs):
    """
    totals for the bag / file count by month.
    Returns an ordered dict
    """

    if kwargs['created']:
        # get the daily Name totals
        daily_counts = Name.objects.extra(
            select={'day': 'date_created'}
        ).values('day').annotate(num=Count('date_created'))
    elif kwargs['edited']:
        # get the daily Name totals
        daily_counts = Name.objects.extra(
            select={'day': 'last_modified'}
        ).values('day').annotate(num=Count('last_modified'))
    # make current total events count
    current_total = 0
    month_skeleton = copy.deepcopy(prepare_graph_date_range())
    # fill in totals
    for u in month_skeleton:
        # replace existing value of zero with sum of nums in certain
        # month
        current_month_counts = [e for e in daily_counts if datetime.strftime(
            e['day'], '%Y-%m') == datetime.strftime(u[0], '%Y-%m')]

        for n in current_month_counts:
            current_total += n['num']
        u[1] = current_total
        if not kwargs['running']:
            current_total = 0
    return month_skeleton

def stats(request):
    """
    Renders the name stats page to the user
    """

    name_type_counts = {
        'personal': Name.objects.filter(name_type=0).count(),
        'organization': Name.objects.filter(name_type=1).count(),
        'event': Name.objects.filter(name_type=2).count(),
        'software': Name.objects.filter(name_type=3).count(),
    }
    name_line_graph_data = {
        'creation_running': calc_total_by_month(created=True,
                                                edited=False, running=True),
        'creation_monthly': calc_total_by_month(created=True,
                                                edited=False, running=False),
        'edited_running': calc_total_by_month(created=False,
                                              edited=True, running=True),
        'edited_monthly': calc_total_by_month(created=False,
                                              edited=True, running=False),
    }
    return render_to_response(
        'name/stats.html',
        {
            'name_line_graph_data': name_line_graph_data,
            'name_type_counts': name_type_counts,
            'total_names': Name.objects.count(),
            'total_links': Identifier.objects.count(),
        },
        context_instance=RequestContext(request)
    )

def resolve_type(q_type):
    """Resolve the Name Types passed to the request, and returns
    a list of Name Type IDs.

    The input is expected to be the raw q_type query string value passed
    in with the request.  The input is expected to be a comma delimited
    list such as, `Personal,Event,Software`.  This function will lookup
    up the ID of the Name Type based on the string and return a list
    of Name Type IDs.
    """
    if not q_type:
        return []

    q_type = q_type.split(',')

    # Iterate through the Name Types and create a list of Name IDs
    # using the corresponding strings values in q_type
    return [k for k, v in NAME_TYPE_CHOICES if v in q_type]

# TODO: Look at reducing the number of queries.
def filter_names(request):
    """Return the set of Name objects filtered on the query
    parameters passed with the request.
    """
    q = request.GET.get('q', None)
    name_types = resolve_type(request.GET.get('q_type', None))

    # Retrieve the visible names from the database.
    names = Name.objects.visible()

    # Filter by name type if it is passed with the request.
    if name_types:
        names = names.filter(name_type__in=name_types)

    # Do further filtering if the q parameter is present.
    if q:
        # All names that fit query OR all variants that contain query
        query_filter = compose_query(q) | Q(variant__variant__icontains=q)
        names = names.filter(query_filter).distinct()

    return names

@jsonp
def get_names(request):
    """Gets the JSON encoded version of the Names matching
    the query parameters.

    This also provides the endpoint used for autocompletion
    during search.
    """
    names = filter_names(request)

    # If the request is AJAX, then it is most likely for autocompletion,
    # so we will only return first 10 names.
    if request.is_ajax():
        names = names[:10]

    data = serializers.NameSearchSerializer(
        names, many=True, context={'request': request})

    response = JSONResponse(data.data)
    response['Access-Control-Allow-Origin'] = '*'
    response['Access-Control-Allow-Headers'] = 'X-Requested-With'

    return response

def landing(request):
    """View for the landing page of the Name App.

    Sends Name count statistics in the context to display on the page.
    """
    counts = {'counts': Name.objects.active_type_counts()}
    return render(request, 'name/landing.html', counts)

def map_json(request):
    """Presents the Locations and related Names serialized into JSON."""
    if request.is_ajax():
        data = serialize(
            'json',
            Location.objects.all().filter(status=0),
            use_natural_foreign_keys=True)

        return HttpResponse(data, content_type='application/json')
    return HttpResponseNotFound()

def map(request):
    """Renders the map template."""
    return render(request, 'name/map.html')

class SearchView(generic.ListView):
    model = Name
    template_name = 'name/search.html'
    paginate_by = 15

    VALID_SORTS = {
        'name_a': 'name',
        'name_d': '-name',
        'begin_a': 'begin',
        'begin_d': '-begin',
        'end_a': 'end',
        'end_d': '-end',
    }

    DEFAULT_SORT = VALID_SORTS['name_a']

    def get_sort_method(self):
        order = self.request.GET.get('order', '')
        return self.VALID_SORTS.get(order, self.DEFAULT_SORT)

    def get_queryset(self):
        query = self.request.GET
        if query.get('q') or query.get('q_type'):
            return filter_names(self.request).order_by(self.get_sort_method())
        return Name.objects.none()

def name_json(request, name_id):
    """Returns result of name query in json format."""
    name = get_object_or_404(Name, name_id=name_id)
    data = serializers.NameSerializer(name, context={'request': request})

    return JSONResponse(data.data)

def mads_serialize(request, name_id):
    queryset = (
        Name.objects.select_related().
        prefetch_related('identifier_set__type', 'note_set', 'variant_set')
    )

    context = dict(name=get_object_or_404(queryset, name_id=name_id))

    return render(request, 'name/name.mads.xml',
                  context, content_type='text/xml')
<commit_msg>Import models.NAME_TYPE_CHOICES.<commit_after>import re
import csv
import copy
from django.http import (HttpResponse, HttpResponseGone, HttpResponseRedirect,
                         HttpResponseNotFound)
from django.template import RequestContext
from django.views import generic
from django.shortcuts import (render_to_response, get_object_or_404, render,
                              redirect)
from django.db.models import Q, Count, Max, Min
from django.core.serializers import serialize
from django.core.urlresolvers import reverse
from django.contrib.syndication.views import Feed
from django.templatetags.static import static
from django.utils.feedgenerator import Atom1Feed
from django.conf import settings
from dateutil import rrule
from datetime import datetime
from pynaco.naco import normalizeSimplified
from name.decorators import jsonp
from name.models import Name, Identifier, Location, NAME_TYPE_CHOICES
from . import serializers
from rest_framework.renderers import JSONRenderer

VOCAB_DOMAIN = settings.VOCAB_DOMAIN

class JSONResponse(HttpResponse):
    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data, renderer_context={'indent': 4})
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

def normalize_query(query_string):
    """Splits the query string in individual keywords, removing
    unnecessary spaces and grouping quoted words together.

    Example:
    >>> normalize_query('some random words "with quotes" and spaces')
        ['some', 'random', 'words', 'with quotes', 'and', 'spaces']
    """
    findterms = re.compile(r'"([^"]+)"|(\S+)').findall
    normspace = re.compile(r'\s{2,}').sub

    terms = (findterms(query_string))

    return [normspace(' ', (t[0] or t[1]).strip()) for t in terms]

def compose_query(query_string):
    """Composes a Q object from the query_string.

    This will first normalize the query_sting and split the string up
    into words. Then it will iterate over the list of words creating Q
    object for each one. Last, it reduces the generated Q objects into
    a single Q object by combining them using the & operator.

    The reduce function essential performs the following operation for
    a query_string that contains 4 words,

        (((Q1 & Q2) & Q3) & Q4)

    where Q1-4 are instances of the Q object.
    """
    # Normalize the query_string which will also split the single string
    # into a list of terms.
    terms = normalize_query(query_string)

    # Create a generator that produces an instance of the Q object
    # for each term.
    qs = (Q(name__icontains=term) for term in terms)

    # Use bitwise AND to compose the objects into a single instance of Q.
    return reduce(lambda x, y: x & y, qs)

def label(request, name_value):
    """Find a name by label.

    If the label matches a single name, users are redirected
    to the name_entry_detail page. Otherwise, if the label
    does not exist, or the specified label matches multiple Name
    objects, a status code 404 is returned.
    """
    if not name_value:
        return HttpResponseNotFound()

    normalized_name = normalizeSimplified(name_value)
    try:
        name = Name.objects.get(normalized_name=normalized_name)

        return HttpResponseRedirect(
            reverse('name_entry_detail', args=[name.name_id]))

    except Name.DoesNotExist:
        return HttpResponseNotFound(
            u'No matching term found - authoritative, or variant - for \"{0}\"'
            .format(name_value))

    except Name.MultipleObjectsReturned:
        return HttpResponseNotFound(
            u'There are multiple Name objects with the same name: \'{0}\'.'
            .format(normalized_name))

class AtomSiteNewsFeed(Feed):
    """Sets up atom feed"""

    feed_type = Atom1Feed
    link = "/name/feed/"
    title = "Name App"
    subtitle = "new records"

    def items(self):
        # last 5 added items
        return Name.objects.order_by('-date_created')[:20]

    def item_title(self, item):
        return item.name

    def item_location(self, item):
        """
        Returns an extra keyword arguments dictionary that is used
        with the `add_item` call of the feed generator. Add the
        'content' field of the 'Entry' item, to be used by the custom
        feed generator.
        """
        location_set = []
        for l in Location.objects.filter(belong_to_name=item):
            location_set.append(
                'georss:point', "%s %s" % (l.latitude, l.longitude)
            )
        return location_set

    def item_description(self, item):
        return "Name Type: %s" % item.get_name_type_label()

    def item_link(self, item):
        return VOCAB_DOMAIN + 'name/' + item.name_id + '/'

def entry_detail(request, name_id):
    """Name Entry Detail View."""
    queryset = (
        Name.objects.select_related().
        prefetch_related('identifier_set__type')
    )

    name_entry = get_object_or_404(queryset, name_id=name_id)

    if name_entry.merged_with:
        return redirect(name_entry.merged_with)

    elif name_entry.is_suppressed():
        return HttpResponseNotFound()

    elif name_entry.is_deleted():
        return HttpResponseGone('The requested record has been deleted!')

    return render(request, 'name/name_detail.html', {'name': name_entry})

def export(request):
    """Exports Names as TSV file."""
    # Create a CSV response.
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="data.tsv"'

    writer = csv.writer(response, delimiter='\t', quoting=csv.QUOTE_MINIMAL)

    # Iterate through the visible Names that are not merged with any other
    # Name records and write a row for each record.
    for n in Name.objects.visible():
        writer.writerow([
            n.get_name_type_label().lower(),
            n.name.encode('utf-8'),
            request.build_absolute_uri(n.get_absolute_url())
        ])

    return response

def opensearch(request):
    """Renders Opensearch XML file.

    Opensearch URL templates are composed in the view
    because it is easier than creating them in the template.
    """
    # Query templates for the URLs.
    search_query = '{0}?q_type=Personal&q={{searchTerms}}'
    search_json_query = '{0}?q={{searchTerms}}'

    # Build the absolute URLs.
    search_url = request.build_absolute_uri(reverse('name_search'))
    search_json_url = request.build_absolute_uri(reverse('name_names'))

    # Compose the full URLs  that will be sent to the template.
    search = search_query.format(search_url)
    search_json = search_json_query.format(search_json_url)
    image = request.build_absolute_uri(static('name/img/favicon.png'))

    urls = dict(search=search, search_json=search_json, image=image)

    return render(request, 'name/opensearch.xml',
                  urls, content_type='text/xml')

def about(request):
    """Renders the name about page to the user."""
    return render(request, 'name/about.html')

def prepare_graph_date_range():
    """
    Several functions use the same code to prepare the dates and values
    for the graphing of event data, so we can make a function for it.
    DRY 4 LYPHE

    returns list of lists
    """

    name_objects = Name.objects.all()
    # grab the dates for the bounds of the graphs
    if name_objects.count() > 0:
        dates = Name.objects.all().aggregate(
            Min("date_created"), Max('date_created'))
        system_start_date = dates['date_created__min']
        system_end_date = dates['date_created__max']

        # setup list for month call data
        daily_edit_counts = []
        # we need to first fill in the first month, because it won't
        # grab that when the for loop iterates to a new month.
        month = system_start_date.strftime('%Y-%m')
        daily_edit_counts.append([datetime.strptime(month, '%Y-%m'), 0])
        # start with a zero count and reset this as we cross a month
        for dt in rrule.rrule(
            rrule.DAILY,
            dtstart=system_start_date,
            until=system_end_date,
        ):
            # if we change months
            if month != dt.strftime('%Y-%m'):
                month = dt.strftime('%Y-%m')
                # make the year and drop in if i doenst exist
                daily_edit_counts.append(
                    [datetime.strptime(month, '%Y-%m'), 0])
        return daily_edit_counts
    else:
        raise Exception('no name objects to construct graph')

def calc_total_by_month(**kwargs):
    """
    totals for the bag / file count by month.
    Returns an ordered dict
    """

    if kwargs['created']:
        # get the daily Name totals
        daily_counts = Name.objects.extra(
            select={'day': 'date_created'}
        ).values('day').annotate(num=Count('date_created'))
    elif kwargs['edited']:
        # get the daily Name totals
        daily_counts = Name.objects.extra(
            select={'day': 'last_modified'}
        ).values('day').annotate(num=Count('last_modified'))
    # make current total events count
    current_total = 0
    month_skeleton = copy.deepcopy(prepare_graph_date_range())
    # fill in totals
    for u in month_skeleton:
        # replace existing value of zero with sum of nums in certain
        # month
        current_month_counts = [e for e in daily_counts if datetime.strftime(
            e['day'], '%Y-%m') == datetime.strftime(u[0], '%Y-%m')]

        for n in current_month_counts:
            current_total += n['num']
        u[1] = current_total
        if not kwargs['running']:
            current_total = 0
    return month_skeleton

def stats(request):
    """
    Renders the name stats page to the user
    """

    name_type_counts = {
        'personal': Name.objects.filter(name_type=0).count(),
        'organization': Name.objects.filter(name_type=1).count(),
        'event': Name.objects.filter(name_type=2).count(),
        'software': Name.objects.filter(name_type=3).count(),
    }
    name_line_graph_data = {
        'creation_running': calc_total_by_month(created=True,
                                                edited=False, running=True),
        'creation_monthly': calc_total_by_month(created=True,
                                                edited=False, running=False),
        'edited_running': calc_total_by_month(created=False,
                                              edited=True, running=True),
        'edited_monthly': calc_total_by_month(created=False,
                                              edited=True, running=False),
    }
    return render_to_response(
        'name/stats.html',
        {
            'name_line_graph_data': name_line_graph_data,
            'name_type_counts': name_type_counts,
            'total_names': Name.objects.count(),
            'total_links': Identifier.objects.count(),
        },
        context_instance=RequestContext(request)
    )

def resolve_type(q_type):
    """Resolve the Name Types passed to the request, and returns
    a list of Name Type IDs.

    The input is expected to be the raw q_type query string value passed
    in with the request.  The input is expected to be a comma delimited
    list such as, `Personal,Event,Software`.  This function will lookup
    up the ID of the Name Type based on the string and return a list
    of Name Type IDs.
    """
    if not q_type:
        return []

    q_type = q_type.split(',')

    # Iterate through the Name Types and create a list of Name IDs
    # using the corresponding strings values in q_type
    return [k for k, v in NAME_TYPE_CHOICES if v in q_type]

# TODO: Look at reducing the number of queries.
def filter_names(request):
    """Return the set of Name objects filtered on the query
    parameters passed with the request.
    """
    q = request.GET.get('q', None)
    name_types = resolve_type(request.GET.get('q_type', None))

    # Retrieve the visible names from the database.
    names = Name.objects.visible()

    # Filter by name type if it is passed with the request.
    if name_types:
        names = names.filter(name_type__in=name_types)

    # Do further filtering if the q parameter is present.
    if q:
        # All names that fit query OR all variants that contain query
        query_filter = compose_query(q) | Q(variant__variant__icontains=q)
        names = names.filter(query_filter).distinct()

    return names

@jsonp
def get_names(request):
    """Gets the JSON encoded version of the Names matching
    the query parameters.

    This also provides the endpoint used for autocompletion
    during search.
    """
    names = filter_names(request)

    # If the request is AJAX, then it is most likely for autocompletion,
    # so we will only return first 10 names.
    if request.is_ajax():
        names = names[:10]

    data = serializers.NameSearchSerializer(
        names, many=True, context={'request': request})

    response = JSONResponse(data.data)
    response['Access-Control-Allow-Origin'] = '*'
    response['Access-Control-Allow-Headers'] = 'X-Requested-With'

    return response

def landing(request):
    """View for the landing page of the Name App.

    Sends Name count statistics in the context to display on the page.
    """
    counts = {'counts': Name.objects.active_type_counts()}
    return render(request, 'name/landing.html', counts)

def map_json(request):
    """Presents the Locations and related Names serialized into JSON."""
    if request.is_ajax():
        data = serialize(
            'json',
            Location.objects.all().filter(status=0),
            use_natural_foreign_keys=True)

        return HttpResponse(data, content_type='application/json')
    return HttpResponseNotFound()

def map(request):
    """Renders the map template."""
    return render(request, 'name/map.html')

class SearchView(generic.ListView):
    model = Name
    template_name = 'name/search.html'
    paginate_by = 15

    VALID_SORTS = {
        'name_a': 'name',
        'name_d': '-name',
        'begin_a': 'begin',
        'begin_d': '-begin',
        'end_a': 'end',
        'end_d': '-end',
    }

    DEFAULT_SORT = VALID_SORTS['name_a']

    def get_sort_method(self):
        order = self.request.GET.get('order', '')
        return self.VALID_SORTS.get(order, self.DEFAULT_SORT)

    def get_queryset(self):
        query = self.request.GET
        if query.get('q') or query.get('q_type'):
            return filter_names(self.request).order_by(self.get_sort_method())
        return Name.objects.none()

def name_json(request, name_id):
    """Returns result of name query in json format."""
    name = get_object_or_404(Name, name_id=name_id)
    data = serializers.NameSerializer(name, context={'request': request})

    return JSONResponse(data.data)

def mads_serialize(request, name_id):
    queryset = (
        Name.objects.select_related().
        prefetch_related('identifier_set__type', 'note_set', 'variant_set')
    )

    context = dict(name=get_object_or_404(queryset, name_id=name_id))

    return render(request, 'name/name.mads.xml',
                  context, content_type='text/xml')
<|endoftext|>


<commit_before>from __future__ import unicode_literals

import keyword
import re
from collections import OrderedDict

from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS, connections

class Command(BaseCommand):
    help = "Introspects the database tables in the given database and outputs a Django model module."

    requires_system_checks = False

    db_module = 'django.db'

    def add_arguments(self, parser):
        parser.add_argument('--database', action='store', dest='database',
            default=DEFAULT_DB_ALIAS, help='Nominates a database to '
            'introspect. Defaults to using the "default" database.')

    def handle(self, **options):
        try:
            for line in self.handle_inspection(options):
                self.stdout.write("%s\n" % line)
        except NotImplementedError:
            raise CommandError("Database inspection isn't supported for the currently selected database backend.")

    def handle_inspection(self, options):
        connection = connections[options['database']]
        # 'table_name_filter' is a stealth option
        table_name_filter = options.get('table_name_filter')

        def table2model(table_name):
            return re.sub(r'[^a-zA-Z0-9]', '', table_name.title())

        def strip_prefix(s):
            return s[1:] if s.startswith("u'") else s

        with connection.cursor() as cursor:
            yield "# This is an auto-generated Django model module."
            yield "# You'll have to do the following manually to clean this up:"
            yield "#   * Rearrange models' order"
            yield "#   * Make sure each model has one field with primary_key=True"
            yield "#   * Make sure each ForeignKey has `on_delete` set to the desired behavior."
            yield (
                "#   * Remove `managed = False` lines if you wish to allow "
                "Django to create, modify, and delete the table"
            )
            yield "# Feel free to rename the models, but don't rename db_table values or field names."
            yield "from __future__ import unicode_literals"
            yield ''
            yield 'from %s import models' % self.db_module
            known_models = []
            for table_name in connection.introspection.table_names(cursor):
                if table_name_filter is not None and callable(table_name_filter):
                    if not table_name_filter(table_name):
                        continue
                yield ''
                yield ''
                yield 'class %s(models.Model):' % table2model(table_name)
                known_models.append(table2model(table_name))
                try:
                    relations = connection.introspection.get_relations(cursor, table_name)
                except NotImplementedError:
                    relations = {}
                try:
                    indexes = connection.introspection.get_indexes(cursor, table_name)
                except NotImplementedError:
                    indexes = {}
                try:
                    constraints = connection.introspection.get_constraints(cursor, table_name)
                except NotImplementedError:
                    constraints = {}
                used_column_names = []  # Holds column names used in the table so far
                column_to_field_name = {}  # Maps column names to names of model fields
                for row in connection.introspection.get_table_description(cursor, table_name):
                    comment_notes = []  # Holds Field notes, to be displayed in a Python comment.
                    extra_params = OrderedDict()  # Holds Field parameters such as 'db_column'.
                    column_name = row[0]
                    is_relation = column_name in relations

                    att_name, params, notes = self.normalize_col_name(
                        column_name, used_column_names, is_relation)
                    extra_params.update(params)
                    comment_notes.extend(notes)

                    used_column_names.append(att_name)
                    column_to_field_name[column_name] = att_name

                    # Add primary_key and unique, if necessary.
                    if column_name in indexes:
                        if indexes[column_name]['primary_key']:
                            extra_params['primary_key'] = True
                        elif indexes[column_name]['unique']:
                            extra_params['unique'] = True

                    if is_relation:
                        rel_to = (
                            "self" if relations[column_name][1] == table_name
                            else table2model(relations[column_name][1])
                        )
                        if rel_to in known_models:
                            field_type = 'ForeignKey(%s' % rel_to
                        else:
                            field_type = "ForeignKey('%s'" % rel_to
                    else:
                        # Calling `get_field_type` to get the field type string and any
                        # additional parameters and notes.
                        field_type, field_params, field_notes = self.get_field_type(connection, table_name, row)
                        extra_params.update(field_params)
                        comment_notes.extend(field_notes)

                        field_type += '('

                    # Don't output 'id = meta.AutoField(primary_key=True)', because
                    # that's assumed if it doesn't exist.
                    if att_name == 'id' and extra_params == {'primary_key': True}:
                        if field_type == 'AutoField(':
                            continue
                        elif field_type == 'IntegerField(' and not connection.features.can_introspect_autofield:
                            comment_notes.append('AutoField?')

                    # Add 'null' and 'blank', if the 'null_ok' flag was present in the
                    # table description.
                    if row[6]:  # If it's NULL...
                        if field_type == 'BooleanField(':
                            field_type = 'NullBooleanField('
                        else:
                            extra_params['blank'] = True
                            extra_params['null'] = True

                    field_desc = '%s = %s%s' % (
                        att_name,
                        # Custom fields will have a dotted path
                        '' if '.' in field_type else 'models.',
                        field_type,
                    )
                    if field_type.startswith('ForeignKey('):
                        field_desc += ', models.DO_NOTHING'

                    if extra_params:
                        if not field_desc.endswith('('):
                            field_desc += ', '
                        field_desc += ', '.join(
                            '%s=%s' % (k, strip_prefix(repr(v)))
                            for k, v in extra_params.items())
                    field_desc += ')'
                    if comment_notes:
                        field_desc += '  # ' + ' '.join(comment_notes)
                    yield '    %s' % field_desc
                for meta_line in self.get_meta(table_name, constraints, column_to_field_name):
                    yield meta_line

    def normalize_col_name(self, col_name, used_column_names, is_relation):
        """
        Modify the column name to make it Python-compatible as a field name
        """
        field_params = {}
        field_notes = []

        new_name = col_name.lower()
        if new_name != col_name:
            field_notes.append('Field name made lowercase.')

        if is_relation:
            if new_name.endswith('_id'):
                new_name = new_name[:-3]
            else:
                field_params['db_column'] = col_name

        new_name, num_repl = re.subn(r'\W', '_', new_name)
        if num_repl > 0:
            field_notes.append('Field renamed to remove unsuitable characters.')

        if new_name.find('__') >= 0:
            while new_name.find('__') >= 0:
                new_name = new_name.replace('__', '_')
            if col_name.lower().find('__') >= 0:
                # Only add the comment if the double underscore was in the original name
                field_notes.append("Field renamed because it contained more than one '_' in a row.")

        if new_name.startswith('_'):
            new_name = 'field%s' % new_name
            field_notes.append("Field renamed because it started with '_'.")

        if new_name.endswith('_'):
            new_name = '%sfield' % new_name
            field_notes.append("Field renamed because it ended with '_'.")

        if keyword.iskeyword(new_name):
            new_name += '_field'
            field_notes.append('Field renamed because it was a Python reserved word.')

        if new_name[0].isdigit():
            new_name = 'number_%s' % new_name
            field_notes.append("Field renamed because it wasn't a valid Python identifier.")

        if new_name in used_column_names:
            num = 0
            while '%s_%d' % (new_name, num) in used_column_names:
                num += 1
            new_name = '%s_%d' % (new_name, num)
            field_notes.append('Field renamed because of name conflict.')

        if col_name != new_name and field_notes:
            field_params['db_column'] = col_name

        return new_name, field_params, field_notes

    def get_field_type(self, connection, table_name, row):
        """
        Given the database connection, the table name, and the cursor row
        description, this routine will return the given field type name, as
        well as any additional keyword parameters and notes for the field.
        """
        field_params = OrderedDict()
        field_notes = []

        try:
            field_type = connection.introspection.get_field_type(row[1], row)
        except KeyError:
            field_type = 'TextField'
            field_notes.append('This field type is a guess.')

        # This is a hook for data_types_reverse to return a tuple of
        # (field_type, field_params_dict).
        if type(field_type) is tuple:
            field_type, new_params = field_type
            field_params.update(new_params)

        # Add max_length for all CharFields.
        if field_type == 'CharField' and row[3]:
            field_params['max_length'] = int(row[3])

        if field_type == 'DecimalField':
            if row[4] is None or row[5] is None:
                field_notes.append(
                    'max_digits and decimal_places have been guessed, as this '
                    'database handles decimal fields as float')
                field_params['max_digits'] = row[4] if row[4] is not None else 10
                field_params['decimal_places'] = row[5] if row[5] is not None else 5
            else:
                field_params['max_digits'] = row[4]
                field_params['decimal_places'] = row[5]

        return field_type, field_params, field_notes

    def get_meta(self, table_name, constraints, column_to_field_name):
        """
        Return a sequence comprising the lines of code necessary
        to construct the inner Meta class for the model corresponding
        to the given database table name.
        """
        unique_together = []
        for index, params in constraints.items():
            if params['unique']:
                columns = params['columns']
                if len(columns) > 1:
                    # we do not want to include the u"" or u'' prefix
                    # so we build the string rather than interpolate the tuple
                    tup = '(' + ', '.join("'%s'" % column_to_field_name[c] for c in columns) + ')'
                    unique_together.append(tup)
        meta = ["",
                "    class Meta:",
                "        managed = False",
                "        db_table = '%s'" % table_name]
        if unique_together:
            tup = '(' + ', '.join(unique_together) + ',)'
            meta += ["        unique_together = %s" % tup]
        return meta
<commit_msg>remove inspect.py<commit_after><|endoftext|>


<commit_before>from abc import abstractmethod

from enable.colors import ColorTrait
from traits.api import (ABCHasStrictTraits, Any, Bool, Event, Instance,
                        Property, Trait, Tuple)

from ..core.component import Component
from ..core.container import Container

def Alias(name):
    return Property(lambda obj: getattr(obj, name),
                    lambda obj, val: setattr(obj, name, val))

class AbstractWindow(ABCHasStrictTraits):

    # The top-level component that this window houses
    component = Instance(Component)

    # The background window of the window.  The entire window first gets
    # painted with this color before the component gets to draw.
    bgcolor = ColorTrait("sys_window")

    alt_pressed = Bool(False)
    control_pressed = Bool(False)
    shift_pressed = Bool(False)

    # When the underlying toolkit control gets resized, this event gets set
    # to the new size of the window, expressed as a tuple (dx, dy).
    resized = Event

    # Kiva GraphicsContext (XXX: is there a base class?)
    _gc = Any

    # The previous component that handled an event.  Used to generate
    # mouse_enter and mouse_leave events.  Right now this can only be
    # None, self.component, or self.overlay.
    _prev_event_handler = Instance(Component)

    # Integer size of the Window (width, height).
    _size = Trait(None, Tuple)

    # --------------------------------------------------------------------------
    #  Abstract methods
    # --------------------------------------------------------------------------

    @abstractmethod
    def _create_key_event(self, event):
        """ Convert a GUI toolkit key event into a KeyEvent. """

    @abstractmethod
    def _create_mouse_event(self, event):
        """ Convert a GUI toolkit mouse event into a MouseEvent. """

    @abstractmethod
    def _get_control_size(self):
        """ Get the size of the underlying toolkit control. """

    @abstractmethod
    def _create_gc(self, size, pix_format="bgra32"):
        """ Create a Kiva graphics context of a specified size.

        This method only gets called when the size of the window itself has
        changed. To perform pre-draw initialization every time in the paint
        loop, use `_init_gc()`.
        """

    @abstractmethod
    def _window_paint(self, event):
        """ Do a GUI toolkit specific screen update. """

    @abstractmethod
    def set_pointer(self, pointer):
        """ Sets the current cursor shape. """

    @abstractmethod
    def set_tooltip(self, tooltip):
        """ Sets the current tooltip. """

    @abstractmethod
    def _set_focus(self):
        """ Sets this window to have keyboard focus. """

    @abstractmethod
    def _get_mouse_event_button(self, event):
        """ Return the name of the mouse button for the toolkit event. """

    @abstractmethod
    def _get_event_size(self, event):
        """ Return width and height of event. """

    @abstractmethod
    def redraw(self, rect=None):
        """ Request a redraw of the window.

        If `rect` is provided, draw within just the (x, y, w, h) rectangle.
        Otherwise, draw over the entire window.
        """

    # -----------------------------------------------------------------------
    # Public methods
    # -----------------------------------------------------------------------

    def __init__(self, **traits):
        self._gc = None
        super(ABCHasStrictTraits, self).__init__(**traits)

        # Create a default component (if necessary):
        if self.component is None:
            self.component = Container()

    def _component_changed(self, old, new):
        if old is not None:
            old.window = None

        if new is None:
            self.component = Container()
            return

        new.window = self

        # If possible, size the new component according to the size of the
        # toolkit control
        size = self._get_control_size()
        if (size is not None) and hasattr(self.component, "size"):
            self.component.origin = [0, 0]
            self.component.size = list(size)
        self.redraw()

    # --------------------------------------------------------------------------
    #  Generic keyboard event handler:
    # --------------------------------------------------------------------------

    def _handle_key_event(self, event_type, event):
        """ **event** should be a toolkit-specific opaque object that will
        be passed in to the backend's _create_key_event() method. It can
        be None if the the toolkit lacks a native "key event" object.

        Returns True if the event has been handled within the Enable object
        hierarchy, or False otherwise.
        """
        # Generate the Enable event
        key_event = self._create_key_event(event_type, event)
        if key_event is None:
            return False

        self.alt_pressed = key_event.alt_down
        self.control_pressed = key_event.control_down
        self.shift_pressed = key_event.shift_down

        # Normal event handling loop
        if (not key_event.handled) and (self.component is not None):
            if self.component.is_in(key_event.x, key_event.y):
                # Fire the actual event
                self.component.dispatch(key_event, event_type)

        return key_event.handled

    # --------------------------------------------------------------------------
    #  Generic mouse event handler:
    # --------------------------------------------------------------------------

    def _handle_mouse_event(self, event_type, event, set_focus=False):
        """ **event** should be a toolkit-specific opaque object that will
        be passed in to the backend's _create_mouse_event() method.  It can
        be None if the the toolkit lacks a native "mouse event" object.

        Returns True if the event has been handled within the Enable object
        hierarchy, or False otherwise.
        """
        if self._size is None:
            return False

        mouse_event = self._create_mouse_event(event)
        xy = (mouse_event.x, mouse_event.y)

        if (not mouse_event.handled) and (self.component is not None):
            # Test to see if we need to generate a mouse_leave event
            if self._prev_event_handler:
                if not self._prev_event_handler.is_in(*xy):
                    mouse_event.handled = False
                    self._prev_event_handler.dispatch(mouse_event,
                                                      "mouse_leave")
                    self._prev_event_handler = None

            if self.component.is_in(*xy):
                # Test to see if we need to generate a mouse_enter event
                if self._prev_event_handler != self.component:
                    self._prev_event_handler = self.component
                    mouse_event.handled = False
                    self.component.dispatch(mouse_event, "mouse_enter")

                # Fire the actual event
                mouse_event.handled = False
                self.component.dispatch(mouse_event, event_type)
        return mouse_event.handled

    def cleanup(self):
        """ Clean up after ourselves.
        """
        if self.component is not None:
            self.component.cleanup(self)
            self.component.window = None
            self.component = None

        self.control = None
        if self._gc is not None:
            self._gc.window = None
            self._gc = None

    def on_paint(self, event=None):
        """ This method is called directly by the UI toolkit's callback
        mechanism on the paint event.
        """
        # Create a new GC if necessary
        size = self._get_control_size()
        if (self._size != tuple(size)) or (self._gc is None):
            self._size = tuple(size)
            self._gc = self._create_gc(size)

        # Always give the GC a chance to initialize
        self._init_gc()

        # Layout components and draw
        if hasattr(self.component, "do_layout"):
            self.component.do_layout()
        gc = self._gc
        self.component.render(gc, view_rect=(0, 0, size[0], size[1]))

        # Perform a paint of the GC to the window (only necessary on backends
        # that render to an off-screen buffer)
        self._window_paint(event)

    def on_close(self, event):
        self.cleanup()

    def on_resize(self, event):
        width, height = self._get_event_size(event)
        self.resized = (width, height)

        component = self.component
        component.origin = [0, 0]
        component.size = [width, height]

    # --------------------------------------------------------------------------
    # Wire up the keyboard event handlers
    # --------------------------------------------------------------------------

    def on_key_press(self, event):
        self._handle_key_event('key_press', event)

    def on_key_release(self, event):
        self._handle_key_event('key_release', event)

    def on_character(self, event):
        self._handle_key_event('character', event)

    # -----------------------------------------------------------------------
    #  Mouse event handlers
    # -----------------------------------------------------------------------

    def on_mouse_enter(self, event):
        self._handle_mouse_event("mouse_enter", event)

    def on_mouse_leave(self, event):
        self._handle_mouse_event("mouse_leave", event)

    def on_mouse_move(self, event):
        self._handle_mouse_event("mouse_move", event)

    def on_mouse_wheel(self, event):
        self._handle_mouse_event("mouse_wheel", event)

    def on_mouse_double_click(self, event):
        name = self._get_mouse_event_button(event)
        self._handle_mouse_event(name + "_dclick", event)

    def on_mouse_press(self, event):
        name = self._get_mouse_event_button(event)
        self._handle_mouse_event(name + "_down", event)

    def on_mouse_release(self, event):
        name = self._get_mouse_event_button(event)
        self._handle_mouse_event(name + "_up", event)

    # -------------------------------------------------------------------------
    #  Private interface
    # -------------------------------------------------------------------------

    def _init_gc(self):
        """ Gives a GC a chance to initialize itself before components perform
        layout and draw.  This is called every time through the paint loop.
        """
        gc = self._gc
        gc.clear(self.bgcolor_)
<commit_msg>Remove unused modifier key flags<commit_after>from abc import abstractmethod

from enable.colors import ColorTrait
from traits.api import ABCHasStrictTraits, Any, Event, Instance, Trait, Tuple

from ..core.component import Component
from ..core.container import Container

class AbstractWindow(ABCHasStrictTraits):

    # The top-level component that this window houses
    component = Instance(Component)

    # The background window of the window.  The entire window first gets
    # painted with this color before the component gets to draw.
    bgcolor = ColorTrait("sys_window")

    # When the underlying toolkit control gets resized, this event gets set
    # to the new size of the window, expressed as a tuple (dx, dy).
    resized = Event

    # Kiva GraphicsContext (XXX: is there a base class?)
    _gc = Any

    # The previous component that handled an event.  Used to generate
    # mouse_enter and mouse_leave events.  Right now this can only be
    # None, self.component, or self.overlay.
    _prev_event_handler = Instance(Component)

    # Integer size of the Window (width, height).
    _size = Trait(None, Tuple)

    # --------------------------------------------------------------------------
    #  Abstract methods
    # --------------------------------------------------------------------------

    @abstractmethod
    def _create_key_event(self, event):
        """ Convert a GUI toolkit key event into a KeyEvent. """

    @abstractmethod
    def _create_mouse_event(self, event):
        """ Convert a GUI toolkit mouse event into a MouseEvent. """

    @abstractmethod
    def _get_control_size(self):
        """ Get the size of the underlying toolkit control. """

    @abstractmethod
    def _create_gc(self, size, pix_format="bgra32"):
        """ Create a Kiva graphics context of a specified size.

        This method only gets called when the size of the window itself has
        changed. To perform pre-draw initialization every time in the paint
        loop, use `_init_gc()`.
        """

    @abstractmethod
    def _window_paint(self, event):
        """ Do a GUI toolkit specific screen update. """

    @abstractmethod
    def set_pointer(self, pointer):
        """ Sets the current cursor shape. """

    @abstractmethod
    def set_tooltip(self, tooltip):
        """ Sets the current tooltip. """

    @abstractmethod
    def _set_focus(self):
        """ Sets this window to have keyboard focus. """

    @abstractmethod
    def _get_mouse_event_button(self, event):
        """ Return the name of the mouse button for the toolkit event. """

    @abstractmethod
    def _get_event_size(self, event):
        """ Return width and height of event. """

    @abstractmethod
    def redraw(self, rect=None):
        """ Request a redraw of the window.

        If `rect` is provided, draw within just the (x, y, w, h) rectangle.
        Otherwise, draw over the entire window.
        """

    # -----------------------------------------------------------------------
    # Public methods
    # -----------------------------------------------------------------------

    def __init__(self, **traits):
        self._gc = None
        super(ABCHasStrictTraits, self).__init__(**traits)

        # Create a default component (if necessary):
        if self.component is None:
            self.component = Container()

    def _component_changed(self, old, new):
        if old is not None:
            old.window = None

        if new is None:
            self.component = Container()
            return

        new.window = self

        # If possible, size the new component according to the size of the
        # toolkit control
        size = self._get_control_size()
        if (size is not None) and hasattr(self.component, "size"):
            self.component.origin = [0, 0]
            self.component.size = list(size)
        self.redraw()

    # --------------------------------------------------------------------------
    #  Generic keyboard event handler:
    # --------------------------------------------------------------------------

    def _handle_key_event(self, event_type, event):
        """ **event** should be a toolkit-specific opaque object that will
        be passed in to the backend's _create_key_event() method. It can
        be None if the the toolkit lacks a native "key event" object.

        Returns True if the event has been handled within the Enable object
        hierarchy, or False otherwise.
        """
        # Generate the Enable event
        key_event = self._create_key_event(event_type, event)
        if key_event is None:
            return False

        # Normal event handling loop
        if (not key_event.handled) and (self.component is not None):
            if self.component.is_in(key_event.x, key_event.y):
                # Fire the actual event
                self.component.dispatch(key_event, event_type)

        return key_event.handled

    # --------------------------------------------------------------------------
    #  Generic mouse event handler:
    # --------------------------------------------------------------------------

    def _handle_mouse_event(self, event_type, event, set_focus=False):
        """ **event** should be a toolkit-specific opaque object that will
        be passed in to the backend's _create_mouse_event() method.  It can
        be None if the the toolkit lacks a native "mouse event" object.

        Returns True if the event has been handled within the Enable object
        hierarchy, or False otherwise.
        """
        if self._size is None:
            return False

        mouse_event = self._create_mouse_event(event)
        xy = (mouse_event.x, mouse_event.y)

        if (not mouse_event.handled) and (self.component is not None):
            # Test to see if we need to generate a mouse_leave event
            if self._prev_event_handler:
                if not self._prev_event_handler.is_in(*xy):
                    mouse_event.handled = False
                    self._prev_event_handler.dispatch(mouse_event,
                                                      "mouse_leave")
                    self._prev_event_handler = None

            if self.component.is_in(*xy):
                # Test to see if we need to generate a mouse_enter event
                if self._prev_event_handler != self.component:
                    self._prev_event_handler = self.component
                    mouse_event.handled = False
                    self.component.dispatch(mouse_event, "mouse_enter")

                # Fire the actual event
                mouse_event.handled = False
                self.component.dispatch(mouse_event, event_type)
        return mouse_event.handled

    def cleanup(self):
        """ Clean up after ourselves.
        """
        if self.component is not None:
            self.component.cleanup(self)
            self.component.window = None
            self.component = None

        self.control = None
        if self._gc is not None:
            self._gc.window = None
            self._gc = None

    def on_paint(self, event=None):
        """ This method is called directly by the UI toolkit's callback
        mechanism on the paint event.
        """
        # Create a new GC if necessary
        size = self._get_control_size()
        if (self._size != tuple(size)) or (self._gc is None):
            self._size = tuple(size)
            self._gc = self._create_gc(size)

        # Always give the GC a chance to initialize
        self._init_gc()

        # Layout components and draw
        if hasattr(self.component, "do_layout"):
            self.component.do_layout()
        gc = self._gc
        self.component.render(gc, view_rect=(0, 0, size[0], size[1]))

        # Perform a paint of the GC to the window (only necessary on backends
        # that render to an off-screen buffer)
        self._window_paint(event)

    def on_close(self, event):
        self.cleanup()

    def on_resize(self, event):
        width, height = self._get_event_size(event)
        self.resized = (width, height)

        component = self.component
        component.origin = [0, 0]
        component.size = [width, height]

    # --------------------------------------------------------------------------
    # Wire up the keyboard event handlers
    # --------------------------------------------------------------------------

    def on_key_press(self, event):
        self._handle_key_event('key_press', event)

    def on_key_release(self, event):
        self._handle_key_event('key_release', event)

    def on_character(self, event):
        self._handle_key_event('character', event)

    # -----------------------------------------------------------------------
    #  Mouse event handlers
    # -----------------------------------------------------------------------

    def on_mouse_enter(self, event):
        self._handle_mouse_event("mouse_enter", event)

    def on_mouse_leave(self, event):
        self._handle_mouse_event("mouse_leave", event)

    def on_mouse_move(self, event):
        self._handle_mouse_event("mouse_move", event)

    def on_mouse_wheel(self, event):
        self._handle_mouse_event("mouse_wheel", event)

    def on_mouse_double_click(self, event):
        name = self._get_mouse_event_button(event)
        self._handle_mouse_event(name + "_dclick", event)

    def on_mouse_press(self, event):
        name = self._get_mouse_event_button(event)
        self._handle_mouse_event(name + "_down", event)

    def on_mouse_release(self, event):
        name = self._get_mouse_event_button(event)
        self._handle_mouse_event(name + "_up", event)

    # -------------------------------------------------------------------------
    #  Private interface
    # -------------------------------------------------------------------------

    def _init_gc(self):
        """ Gives a GC a chance to initialize itself before components perform
        layout and draw.  This is called every time through the paint loop.
        """
        gc = self._gc
        gc.clear(self.bgcolor_)
<|endoftext|>