#  Tools for spectral analysis of a real valued signal.
#
#  The functions in this file were adapted from the python package scipy according to the following license:
#
# License:
# Copyright   2001, 2002 Enthought, Inc.
# All rights reserved.
#
# Copyright   2003-2013 SciPy Developers.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
#     Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#     Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#     Neither the name of Enthought nor the names of the SciPy Developers may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from __future__ import division, print_function, absolute_import

import numpy as np
import pandas as pd
from six import string_types

__all__  = ['fft_wrap','welch', 'psd', 'fft_amplitude']
__all__ += ['pwelch', 'csd', 'coherence']
__all__ += ['fnextpow2']
__all__ += ['hann','hamming','boxcar','general_hamming','get_window']
__all__ += ['TestSpectral']

# --------------------------------------------------------------------------------}
# --- FFT wrap
# --------------------------------------------------------------------------------{
def fft_wrap(t,y,dt=None, output_type='amplitude',averaging='None',averaging_window='hamming',detrend=False,nExp=None, nPerDecade=None):
    """
    Wrapper to compute FFT amplitude or power spectra, with averaging.
    INPUTS:
       output_type      : amplitude, PSD, f x PSD
       averaging : None, Welch, Binning
       averaging_window : Hamming, Hann, Rectangular
    OUTPUTS:
       frq: vector of frequencies
       Y  : Amplitude spectrum, PSD, or f * PSD
       Info: a dictionary of info values
    """

    # Formatting inputs
    output_type      = output_type.lower()
    averaging        = averaging.lower()
    averaging_window = averaging_window.lower()
    y = np.asarray(y)
    y = y[~np.isnan(y)]
    n = len(y)

    if dt is None:
        dtDelta0 = t[1]-t[0]
        # Hack to use a constant dt
        dt = (np.max(t)-np.min(t))/(n-1)
        if dtDelta0 !=dt:
            print('[WARN] dt from tmax-tmin different from dt from t2-t1' )
    Fs = 1/dt
    if averaging =='none':
        frq, PSD, Info = psd(y, fs=Fs, detrend=detrend, return_onesided=True)
    elif averaging =='binning':
        frq, PSD, Info = psd_binned(y, fs=Fs, detrend=detrend, return_onesided=True, nPerDecade=nPerDecade)
    elif averaging=='welch':
        # --- Welch - PSD
        #overlap_frac=0.5
        #return fnextpow2(np.sqrt(len(x)/(1-overlap_frac)))
        nFFTAll=fnextpow2(n)
        if nExp is None:
            nExp=int(np.log(nFFTAll)/np.log(2))-1
        nPerSeg=2**nExp
        if nPerSeg>n:
            print('[WARN] Power of 2 value was too high and was reduced. Disable averaging to use the full spectrum.');
            nExp=int(np.log(nFFTAll)/np.log(2))-1
            nPerSeg=2**nExp
        if averaging_window=='hamming':
           window = hamming(nPerSeg, True)# True=Symmetric, like matlab
        elif averaging_window=='hann':
           window = hann(nPerSeg, True)
        elif averaging_window=='rectangular':
           window = boxcar(nPerSeg)
        else:
            raise Exception('Averaging window unknown {}'.format(averaging_window))
        frq, PSD, Info = pwelch(y, fs=Fs, window=window, detrend=detrend)
        Info.nExp = nExp
    else:
        raise Exception('Averaging method unknown {}'.format(averaging))

    # --- Formatting output
    if output_type=='amplitude':
        deltaf = frq[1]-frq[0]
        Y = np.sqrt(PSD*2*deltaf)
        # NOTE: the above should be the same as:Y=abs(Y[range(nhalf)])/n;Y[1:-1]=Y[1:-1]*2;
    elif output_type=='psd': # one sided
        Y = PSD
    elif output_type=='f x psd':
        Y = PSD*frq
    else:
        raise NotImplementedError('Contact developer')
    if detrend:
        frq= frq[1:]
        Y  = Y[1:]
    return frq, Y, Info

# --------------------------------------------------------------------------------}
# --- Spectral simple (averaging below)
# --------------------------------------------------------------------------------{
def fft_amplitude(y, fs=1.0, detrend ='constant', return_onesided=True):
    """ Returns FFT amplitude of signal """
    frq, PSD, Info = psd(y, fs=fs, detrend=detrend, return_onesided=return_onesided)
    deltaf = frq[1]-frq[0]
    Y = np.sqrt(PSD*2*deltaf)
    return frq, Y, Info

def psd_binned(y, fs=1.0, nPerDecade=10, detrend ='constant', return_onesided=True):
    """
    Return PSD binned with nPoints per decade
    """
    # --- First  return regular PSD
    frq, PSD, Info = psd(y, fs=fs, detrend=detrend, return_onesided=return_onesided)

    add0=False
    if frq[0]==0:
        add0=True
        f0   = 0
        PSD0 = PSD[0]
        frq=frq[1:]
        PSD=PSD[1:]

    # -- Then bin per decase
    log_f = np.log10(frq)
    ndecades = np.ceil(log_f[-1] -log_f[0])
    xbins = np.linspace(log_f[0], log_f[-1], int(ndecades*nPerDecade))

    # Using Pandas to bin..
    df = pd.DataFrame(data=np.column_stack((log_f,PSD)), columns=['x','y'])
    xmid  = (xbins[:-1]+xbins[1:])/2
    df['Bin'] = pd.cut(df['x'], bins=xbins, labels=xmid ) # Adding a column that has bin attribute
    df2  = df.groupby('Bin').mean()                     # Average by bin
    df2  = df2.reindex(xmid)
    log_f_bin = df2['x'].values
    PSD_bin = df2['y'].values
    frq2= 10**log_f_bin
    PSD2= PSD_bin
    if add0:
        frq2=np.concatenate(  ([f0  ], frq2)  )
        PSD2=np.concatenate(  ([PSD0], PSD2)  )
    b = ~np.isnan(frq2)
    frq2 = frq2[b]
    PSD2 = PSD2[b]

    #import matplotlib.pyplot as plt
    #fig,ax = plt.subplots(1, 1, sharey=False, figsize=(6.4,4.8)) # (6.4,4.8)
    #fig.subplots_adjust(left=0.12, right=0.95, top=0.95, bottom=0.11, hspace=0.20, wspace=0.20)
    #ax.plot(log_f, PSD, label='')
    #ax.plot(log_f_bin, PSD_bin, 'o', label='')
    #for x in xbins:
    #    ax.axvline(x, ls=':', c=(0.5,0.5,0.5))
    #ax.set_xlabel('')
    #ax.set_ylabel('')
    #ax.legend()
    #plt.show()

    #Info.df    = frq[1]-frq[0]
    #Info.fMax  = frq[-1]
    #Info.LFreq = len(frq)
    #Info.LSeg  = len(Y)
    #Info.LWin  = len(Y)
    #Info.LOvlp = 0
    #Info.nFFT  = len(Y)
    #Info.nseg  = 1
    Info.nPerDecade  = nPerDecade
    Info.xbins  = xbins

    return frq2, PSD2, Info

def psd(y, fs=1.0, detrend ='constant', return_onesided=True):
    """ Perform PSD without averaging """
    if not return_onesided:
        raise NotImplementedError('Double sided todo')

    if detrend is None:
        detrend=False

    if detrend=='constant' or detrend==True:
        m=np.mean(y);
    else:
        m=0;

    n = len(y)
    if n%2==0:
        nhalf = int(n/2+1)
    else:
        nhalf = int((n+1)/2)

    frq = np.arange(nhalf)*fs/n;
    Y   = np.fft.rfft(y-m) #Y = np.fft.fft(y)
    PSD = abs(Y[range(nhalf)])**2 /(n*fs) # PSD
    PSD[1:-1] = PSD[1:-1]*2;
    class InfoClass():
        pass
    Info = InfoClass();
    Info.df    = frq[1]-frq[0]
    Info.fMax  = frq[-1]
    Info.LFreq = len(frq)
    Info.LSeg  = len(Y)
    Info.LWin  = len(Y)
    Info.LOvlp = 0
    Info.nFFT  = len(Y)
    Info.nseg  = 1
    return frq, PSD, Info

# --------------------------------------------------------------------------------}
# --- Windows
# --------------------------------------------------------------------------------{
"""The suite of window functions."""
def fnextpow2(x):
    return 2**np.ceil( np.log(x)*0.99999999999/np.log(2));

def fDefaultWinLen(x,overlap_frac=0.5):
    return fnextpow2(np.sqrt(len(x)/(1-overlap_frac)))

def fDefaultWinLenMatlab(x):
    return  np.fix((len(x)-3)*2./9.)

def _len_guards(M):
    """Handle small or incorrect window lengths"""
    if int(M) != M or M < 0:
        raise ValueError('Window length M must be a non-negative integer')
    return M <= 1

def _extend(M, sym):
    """Extend window by 1 sample if needed for DFT-even symmetry"""
    if not sym:
        return M + 1, True
    else:
        return M, False

def _truncate(w, needed):
    """Truncate window by 1 sample if needed for DFT-even symmetry"""
    if needed:
        return w[:-1]
    else:
        return w

def general_cosine(M, a, sym=True):
    if _len_guards(M):
        return np.ones(M)
    M, needs_trunc = _extend(M, sym)

    fac = np.linspace(-np.pi, np.pi, M)
    w = np.zeros(M)
    for k in range(len(a)):
        w += a[k] * np.cos(k * fac)

    return _truncate(w, needs_trunc)

def boxcar(M, sym=True):
    """Return a boxcar or rectangular window.

    Also known as a rectangular window or Dirichlet window, this is equivalent
    to no window at all.
    """
    if _len_guards(M):
        return np.ones(M)
    M, needs_trunc = _extend(M, sym)

    w = np.ones(M, float)

    return _truncate(w, needs_trunc)

def hann(M, sym=True): # same as hanning(*args, **kwargs):
    return general_hamming(M, 0.5, sym)

def general_hamming(M, alpha, sym=True):
    r"""Return a generalized Hamming window.
    The generalized Hamming window is constructed by multiplying a rectangular
    window by one period of a cosine function [1]_.
    w(n) = \alpha - \left(1 - \alpha\right) \cos\left(\frac{2\pi{n}}{M-1}\right)
              \qquad 0 \leq n \leq M-1
    """
    return general_cosine(M, [alpha, 1. - alpha], sym)

def hamming(M, sym=True):
    r"""Return a Hamming window.
    The Hamming window is a taper formed by using a raised cosine with
    non-zero endpoints, optimized to minimize the nearest side lobe.
     w(n) = 0.54 - 0.46 \cos\left(\frac{2\pi{n}}{M-1}\right)
               \qquad 0 \leq n \leq M-1
    """
    return general_hamming(M, 0.54, sym)

_win_equiv_raw = {
    ('boxcar', 'box', 'ones', 'rect', 'rectangular'): (boxcar, False),
    ('hamming', 'hamm', 'ham'): (hamming, False),
    ('hanning', 'hann', 'han'): (hann, False),
}

# Fill dict with all valid window name strings
_win_equiv = {}
for k, v in _win_equiv_raw.items():
    for key in k:
        _win_equiv[key] = v[0]

# Keep track of which windows need additional parameters
_needs_param = set()
for k, v in _win_equiv_raw.items():
    if v[1]:
        _needs_param.update(k)

def get_window(window, Nx, fftbins=True):
    """
    Return a window.

    Parameters
    ----------
    window : string, float, or tuple
        The type of window to create. See below for more details.
    Nx : int
        The number of samples in the window.
    fftbins : bool, optional
        If True (default), create a "periodic" window, ready to use with
        `ifftshift` and be multiplied by the result of an FFT (see also
        `fftpack.fftfreq`).
        If False, create a "symmetric" window, for use in filter design.
    """
    sym = not fftbins
    try:
        beta = float(window)
    except (TypeError, ValueError):
        args = ()
        if isinstance(window, tuple):
            winstr = window[0]
            if len(window) > 1:
                args = window[1:]
        elif isinstance(window, string_types):
            if window in _needs_param:
                raise ValueError("The '" + window + "' window needs one or "
                                 "more parameters -- pass a tuple.")
            else:
                winstr = window
        else:
            raise ValueError("%s as window type is not supported." %
                             str(type(window)))

        try:
            winfunc = _win_equiv[winstr]
        except KeyError:
            raise ValueError("Unknown window type.")

        params = (Nx,) + args + (sym,)
    else:
        winfunc = kaiser
        params = (Nx, beta, sym)

    return winfunc(*params)

# --------------------------------------------------------------------------------}
# --- Helpers
# --------------------------------------------------------------------------------{
def odd_ext(x, n, axis=-1):
    """
    Odd extension at the boundaries of an array
    Generate a new ndarray by making an odd extension of `x` along an axis.
    """
    if n < 1:
        return x
    if n > x.shape[axis] - 1:
        raise ValueError(("The extension length n (%d) is too big. " +
                         "It must not exceed x.shape[axis]-1, which is %d.")
                         % (n, x.shape[axis] - 1))
    left_end = axis_slice(x, start=0, stop=1, axis=axis)
    left_ext = axis_slice(x, start=n, stop=0, step=-1, axis=axis)
    right_end = axis_slice(x, start=-1, axis=axis)
    right_ext = axis_slice(x, start=-2, stop=-(n + 2), step=-1, axis=axis)
    ext = np.concatenate((2 * left_end - left_ext,
                          x,
                          2 * right_end - right_ext),
                         axis=axis)
    return ext

def even_ext(x, n, axis=-1):
    """
    Even extension at the boundaries of an array
    Generate a new ndarray by making an even extension of `x` along an axis.
    """
    if n < 1:
        return x
    if n > x.shape[axis] - 1:
        raise ValueError(("The extension length n (%d) is too big. " +
                         "It must not exceed x.shape[axis]-1, which is %d.")
                         % (n, x.shape[axis] - 1))
    left_ext = axis_slice(x, start=n, stop=0, step=-1, axis=axis)
    right_ext = axis_slice(x, start=-2, stop=-(n + 2), step=-1, axis=axis)
    ext = np.concatenate((left_ext,
                          x,
                          right_ext),
                         axis=axis)
    return ext

def const_ext(x, n, axis=-1):
    """
    Constant extension at the boundaries of an array
    Generate a new ndarray that is a constant extension of `x` along an axis.
    The extension repeats the values at the first and last element of
    the axis.
    """
    if n < 1:
        return x
    left_end = axis_slice(x, start=0, stop=1, axis=axis)
    ones_shape = [1] * x.ndim
    ones_shape[axis] = n
    ones = np.ones(ones_shape, dtype=x.dtype)
    left_ext = ones * left_end
    right_end = axis_slice(x, start=-1, axis=axis)
    right_ext = ones * right_end
    ext = np.concatenate((left_ext,
                          x,
                          right_ext),
                         axis=axis)
    return ext

def zero_ext(x, n, axis=-1):
    """
    Zero padding at the boundaries of an array
    Generate a new ndarray that is a zero padded extension of `x` along
    an axis.
    """
    if n < 1:
        return x
    zeros_shape = list(x.shape)
    zeros_shape[axis] = n
    zeros = np.zeros(zeros_shape, dtype=x.dtype)
    ext = np.concatenate((zeros, x, zeros), axis=axis)
    return ext

def signaltools_detrend(data, axis=-1, type='linear', bp=0):
    """
    Remove linear trend along axis from data.

    Parameters
    ----------
    data : array_like
        The input data.
    axis : int, optional
        The axis along which to detrend the data. By default this is the
        last axis (-1).
    type : {'linear', 'constant'}, optional
        The type of detrending. If ``type == 'linear'`` (default),
        the result of a linear least-squares fit to `data` is subtracted
        from `data`.
        If ``type == 'constant'``, only the mean of `data` is subtracted.
    bp : array_like of ints, optional
        A sequence of break points. If given, an individual linear fit is
        performed for each part of `data` between two break points.
        Break points are specified as indices into `data`.

    Returns
    -------
    ret : ndarray
        The detrended input data.
    """
    if type not in ['linear', 'l', 'constant', 'c']:
        raise ValueError("Trend type must be 'linear' or 'constant'.")
    data = np.asarray(data)
    dtype = data.dtype.char
    if dtype not in 'dfDF':
        dtype = 'd'
    if type in ['constant', 'c']:
        #print('Removing mean')
        ret = data - np.expand_dims(np.mean(data, axis), axis)
        return ret
    else:
        #print('Removing linear?')
        dshape = data.shape
        N = dshape[axis]
        bp = sort(unique(r_[0, bp, N]))
        if np.any(bp > N):
            raise ValueError("Breakpoints must be less than length "
                             "of data along given axis.")
        Nreg = len(bp) - 1
        # Restructure data so that axis is along first dimension and
        #  all other dimensions are collapsed into second dimension
        rnk = len(dshape)
        if axis < 0:
            axis = axis + rnk
        newdims = r_[axis, 0:axis, axis + 1:rnk]
        newdata = reshape(np.transpose(data, tuple(newdims)),
                          (N, _prod(dshape) // N))
        newdata = newdata.copy()  # make sure we have a copy
        if newdata.dtype.char not in 'dfDF':
            newdata = newdata.astype(dtype)
        # Find leastsq fit and remove it for each piece
        for m in range(Nreg):
            Npts = bp[m + 1] - bp[m]
            A = ones((Npts, 2), dtype)
            A[:, 0] = cast[dtype](np.arange(1, Npts + 1) * 1.0 / Npts)
            sl = slice(bp[m], bp[m + 1])
            coef, resids, rank, s = np.linalg.lstsq(A, newdata[sl])
            newdata[sl] = newdata[sl] - dot(A, coef)
        # Put data back in original shape.
        tdshape = take(dshape, newdims, 0)
        ret = np.reshape(newdata, tuple(tdshape))
        vals = list(range(1, rnk))
        olddims = vals[:axis] + [0] + vals[axis:]
        ret = np.transpose(ret, tuple(olddims))
        return ret

# --------------------------------------------------------------------------------}
# --- Spectral Averaging
# --------------------------------------------------------------------------------{
"""Tools for spectral analysis.  """

def welch(x, fs=1.0, window='hann', nperseg=None, noverlap=None, nfft=None,
          detrend='constant', return_onesided=True, scaling='density',
          axis=-1):
    """Interface identical to scipy.signal """

    if detrend==True:
        detrend='constant'

    freqs, Pxx = csd(x, x, fs, window, nperseg, noverlap, nfft, detrend, return_onesided, scaling, axis)
    return freqs, Pxx.real

#>>>>
def pwelch(x, window='hamming', noverlap=None, nfft=None, fs=1.0, nperseg=None,
          detrend=False, return_onesided=True, scaling='density',
          axis=-1):
    r"""
    NOTE: interface and default options modified to match matlab's implementation
       >> detrend: default to False
       >> window : default to 'hamming'
       >> window: if an integer, use 'hamming(window, sym=True)'

    Estimate power spectral density using Welch's method.

    Welch's method [1]_ computes an estimate of the power spectral
    density by dividing the data into overlapping segments, computing a
    modified periodogram for each segment and averaging the
    periodograms.

    Parameters
    ----------
    x : array_like
        Time series of measurement values
    fs : float, optional
        Sampling frequency of the `x` time series. Defaults to 1.0.
    window : str or tuple or array_like, optional
        Desired window to use. If `window` is a string or tuple, it is
        passed to `get_window` to generate the window values, which are
        DFT-even by default. See `get_window` for a list of windows and
        required parameters. If `window` is array_like it will be used
        directly as the window and its length must be nperseg. Defaults
        to a Hann window.
    nperseg : int, optional
        Length of each segment. Defaults to None, but if window is str or
        tuple, is set to 256, and if window is array_like, is set to the
        length of the window.
    noverlap : int, optional
        Number of points to overlap between segments. If `None`,
        ``noverlap = nperseg // 2``. Defaults to `None`.
    nfft : int, optional
        Length of the FFT used, if a zero padded FFT is desired. If
        `None`, the FFT length is `nperseg`. Defaults to `None`.
    detrend : str or function or `False`, optional
        Specifies how to detrend each segment. If `detrend` is a
        string, it is passed as the `type` argument to the `detrend`
        function. If it is a function, it takes a segment and returns a
        detrended segment. If `detrend` is `False`, no detrending is
        done. Defaults to 'constant'.
    return_onesided : bool, optional
        If `True`, return a one-sided spectrum for real data. If
        `False` return a two-sided spectrum. Note that for complex
        data, a two-sided spectrum is always returned.
    scaling : { 'density', 'spectrum' }, optional
        Selects between computing the power spectral density ('density')
        where `Pxx` has units of V**2/Hz and computing the power
        spectrum ('spectrum') where `Pxx` has units of V**2, if `x`
        is measured in V and `fs` is measured in Hz. Defaults to
        'density'
    axis : int, optional
        Axis along which the periodogram is computed; the default is
        over the last axis (i.e. ``axis=-1``).

    Returns
    -------
    f : ndarray
        Array of sample frequencies.
    Pxx : ndarray
        Power spectral density or power spectrum of x.

    See Also
    --------
    periodogram: Simple, optionally modified periodogram
    lombscargle: Lomb-Scargle periodogram for unevenly sampled data

    Notes
    -----
    An appropriate amount of overlap will depend on the choice of window
    and on your requirements. For the default Hann window an overlap of
    50% is a reasonable trade off between accurately estimating the
    signal power, while not over counting any of the data. Narrower
    windows may require a larger overlap.

    If `noverlap` is 0, this method is equivalent to Bartlett's method
    [2]_.

    .. versionadded:: 0.12.0

    References
    ----------
    .. [1] P. Welch, "The use of the fast Fourier transform for the
           estimation of power spectra: A method based on time averaging
           over short, modified periodograms", IEEE Trans. Audio
           Electroacoust. vol. 15, pp. 70-73, 1967.
    .. [2] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra",
           Biometrika, vol. 37, pp. 1-16, 1950.

    """
    import math
    def fnextpow2(x):
        return 2**math.ceil( math.log(x)*0.99999999999/math.log(2));

    # MANU >>> CHANGE OF DEFAULT OPTIONS
    # MANU - If a length is provided use symmetric hamming window
    if type(window)==int:
        window=hamming(window, True)
    # MANU - do not use 256 as default
    if isinstance(window, string_types) or isinstance(window, tuple):
        if nperseg is None:
            if noverlap is None:
                overlap_frac=0.5
            elif noverlap == 0:
                overlap_frac=0
            else:
                raise NotImplementedError('TODO noverlap set but not nperseg')
            #nperseg = 256  # then change to default
            nperseg=fnextpow2(math.sqrt(x.shape[-1]/(1-overlap_frac)));

    # MANU accepting true as detrend
    if detrend==True:
        detrend='constant'

    freqs, Pxx, Info = csd(x, x, fs, window, nperseg, noverlap, nfft, detrend,
                     return_onesided, scaling, axis, returnInfo=True)

    return freqs, Pxx.real, Info

def csd(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None, nfft=None,
        detrend='constant', return_onesided=True, scaling='density', axis=-1,
        returnInfo=False
        ):
    r"""
    Estimate the cross power spectral density, Pxy, using Welch's
    method.
    """

    freqs, _, Pxy, Info = _spectral_helper(x, y, fs, window, nperseg, noverlap, nfft,
                                     detrend, return_onesided, scaling, axis,
                                     mode='psd')

    # Average over windows.
    if len(Pxy.shape) >= 2 and Pxy.size > 0:
        if Pxy.shape[-1] > 1:
            Pxy = Pxy.mean(axis=-1)
        else:
            Pxy = np.reshape(Pxy, Pxy.shape[:-1])

    if returnInfo:
        return freqs, Pxy, Info
    else:
        return freqs, Pxy

def coherence(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None,
              nfft=None, detrend='constant', axis=-1):
    r"""
    Estimate the magnitude squared coherence estimate, Cxy, of
    discrete-time signals X and Y using Welch's method.

    ``Cxy = abs(Pxy)**2/(Pxx*Pyy)``, where `Pxx` and `Pyy` are power
    spectral density estimates of X and Y, and `Pxy` is the cross
    spectral density estimate of X and Y.
    """

    freqs, Pxx, Infoxx = welch(x, fs, window, nperseg, noverlap, nfft, detrend, axis=axis)
    _, Pyy, Infoyy     = welch(y, fs, window, nperseg, noverlap, nfft, detrend, axis=axis)
    _, Pxy, Infoxy     = csd(x, y, fs, window, nperseg, noverlap, nfft, detrend, axis=axis, returnInfo=True)

    Cxy = np.abs(Pxy)**2 / Pxx / Pyy

    return freqs, Cxy, Infoxx

def _spectral_helper(x, y, fs=1.0, window='hann', nperseg=None, noverlap=None,
                     nfft=None, detrend='constant', return_onesided=True,
                     scaling='spectrum', axis=-1, mode='psd', boundary=None,
                     padded=False):
    """ Calculate various forms of windowed FFTs for PSD, CSD, etc.  """
    if mode not in ['psd', 'stft']:
        raise ValueError("Unknown value for mode %s, must be one of: "
                         "{'psd', 'stft'}" % mode)

    boundary_funcs = {'even': even_ext,
                      'odd': odd_ext,
                      'constant': const_ext,
                      'zeros': zero_ext,
                      None: None}

    if boundary not in boundary_funcs:
        raise ValueError("Unknown boundary option '{0}', must be one of: {1}"
                          .format(boundary, list(boundary_funcs.keys())))

    # If x and y are the same object we can save ourselves some computation.
    same_data = y is x

    if not same_data and mode != 'psd':
        raise ValueError("x and y must be equal if mode is 'stft'")

    axis = int(axis)

    # Ensure we have np.arrays, get outdtype
    x = np.asarray(x)
    if not same_data:
        y = np.asarray(y)
        outdtype = np.result_type(x, y, np.complex64)
    else:
        outdtype = np.result_type(x, np.complex64)

    if not same_data:
        # Check if we can broadcast the outer axes together
        xouter = list(x.shape)
        youter = list(y.shape)
        xouter.pop(axis)
        youter.pop(axis)
        try:
            outershape = np.broadcast(np.empty(xouter), np.empty(youter)).shape
        except ValueError:
            raise ValueError('x and y cannot be broadcast together.')

    if same_data:
        if x.size == 0:
            return np.empty(x.shape), np.empty(x.shape), np.empty(x.shape)
    else:
        if x.size == 0 or y.size == 0:
            outshape = outershape + (min([x.shape[axis], y.shape[axis]]),)
            emptyout = np.rollaxis(np.empty(outshape), -1, axis)
            return emptyout, emptyout, emptyout

    if x.ndim > 1:
        if axis != -1:
            x = np.rollaxis(x, axis, len(x.shape))
            if not same_data and y.ndim > 1:
                y = np.rollaxis(y, axis, len(y.shape))

    # Check if x and y are the same length, zero-pad if necessary
    if not same_data:
        if x.shape[-1] != y.shape[-1]:
            if x.shape[-1] < y.shape[-1]:
                pad_shape = list(x.shape)
                pad_shape[-1] = y.shape[-1] - x.shape[-1]
                x = np.concatenate((x, np.zeros(pad_shape)), -1)
            else:
                pad_shape = list(y.shape)
                pad_shape[-1] = x.shape[-1] - y.shape[-1]
                y = np.concatenate((y, np.zeros(pad_shape)), -1)

    if nperseg is not None:  # if specified by user
        nperseg = int(nperseg)
        if nperseg < 1:
            raise ValueError('nperseg must be a positive integer')

    # parse window; if array like, then set nperseg = win.shape
    win, nperseg = _triage_segments(window, nperseg,input_length=x.shape[-1])

    if nfft is None:
        nfft = nperseg
    elif nfft < nperseg:
        raise ValueError('nfft must be greater than or equal to nperseg.')
    else:
        nfft = int(nfft)

    if noverlap is None:
        noverlap = nperseg//2
    else:
        noverlap = int(noverlap)
    if noverlap >= nperseg:
        raise ValueError('noverlap must be less than nperseg.')
    nstep = nperseg - noverlap

    # Padding occurs after boundary extension, so that the extended signal ends
    # in zeros, instead of introducing an impulse at the end.
    # I.e. if x = [..., 3, 2]
    # extend then pad -> [..., 3, 2, 2, 3, 0, 0, 0]
    # pad then extend -> [..., 3, 2, 0, 0, 0, 2, 3]

    if boundary is not None:
        ext_func = boundary_funcs[boundary]
        x = ext_func(x, nperseg//2, axis=-1)
        if not same_data:
            y = ext_func(y, nperseg//2, axis=-1)

    if padded:
        # Pad to integer number of windowed segments
        # I.e make x.shape[-1] = nperseg + (nseg-1)*nstep, with integer nseg
        nadd = (-(x.shape[-1]-nperseg) % nstep) % nperseg
        zeros_shape = list(x.shape[:-1]) + [nadd]
        x = np.concatenate((x, np.zeros(zeros_shape)), axis=-1)
        if not same_data:
            zeros_shape = list(y.shape[:-1]) + [nadd]
            y = np.concatenate((y, np.zeros(zeros_shape)), axis=-1)

    # Handle detrending and window functions
    if not detrend:
        def detrend_func(d):
            return d
    elif not hasattr(detrend, '__call__'):
        def detrend_func(d):
            return signaltools_detrend(d, type=detrend, axis=-1)
    elif axis != -1:
        # Wrap this function so that it receives a shape that it could
        # reasonably expect to receive.
        def detrend_func(d):
            d = np.rollaxis(d, -1, axis)
            d = detrend(d)
            return np.rollaxis(d, axis, len(d.shape))
    else:
        detrend_func = detrend

    if np.result_type(win,np.complex64) != outdtype:
        win = win.astype(outdtype)

    if scaling == 'density':
        scale = 1.0 / (fs * (win*win).sum())
    elif scaling == 'spectrum':
        scale = 1.0 / win.sum()**2
    else:
        raise ValueError('Unknown scaling: %r' % scaling)

    if mode == 'stft':
        scale = np.sqrt(scale)

    if return_onesided:
        if np.iscomplexobj(x):
            sides = 'twosided'
            #warnings.warn('Input data is complex, switching to ' 'return_onesided=False')
        else:
            sides = 'onesided'
            if not same_data:
                if np.iscomplexobj(y):
                    sides = 'twosided'
                    #warnings.warn('Input data is complex, switching to return_onesided=False')
    else:
        sides = 'twosided'

    if sides == 'twosided':
        raise Exception('NOT IMPLEMENTED')
         #freqs = fftpack.fftfreq(nfft, 1/fs)
    elif sides == 'onesided':
        freqs = np.fft.rfftfreq(nfft, 1/fs)

    # Perform the windowed FFTs
    result = _fft_helper(x, win, detrend_func, nperseg, noverlap, nfft, sides)

    if not same_data:
        # All the same operations on the y data
        result_y = _fft_helper(y, win, detrend_func, nperseg, noverlap, nfft,
                               sides)
        result = np.conjugate(result) * result_y
    elif mode == 'psd':
        result = np.conjugate(result) * result

    result *= scale
    if sides == 'onesided' and mode == 'psd':
        if nfft % 2:
            result[..., 1:] *= 2
        else:
            # Last point is unpaired Nyquist freq point, don't double
            result[..., 1:-1] *= 2

    time = np.arange(nperseg/2, x.shape[-1] - nperseg/2 + 1,
                     nperseg - noverlap)/float(fs)
    if boundary is not None:
        time -= (nperseg/2) / fs

    result = result.astype(outdtype)

    # All imaginary parts are zero anyways
    if same_data and mode != 'stft':
        result = result.real

    # Output is going to have new last axis for time/window index, so a
    # negative axis index shifts down one
    if axis < 0:
        axis -= 1

    # Roll frequency axis back to axis where the data came from
    result = np.rollaxis(result, -1, axis)

    # TODO
    class InfoClass():
        pass
    Info = InfoClass();
    Info.df=freqs[1]-freqs[0]
    Info.fMax=freqs[-1]
    Info.LFreq=len(freqs)
    Info.LSeg=nperseg
    Info.LWin=len(win)
    Info.LOvlp=noverlap
    Info.nFFT=nfft
    Info.nseg=-1
    #print('df:{:.3f} - fm:{:.2f} - nseg:{} - Lf:{:5d} - Lseg:{:5d} - Lwin:{:5d} - Lovlp:{:5d} - Nfft:{:5d} - Lsig:{}'.format(freqs[1]-freqs[0],freqs[-1],-1,len(freqs),nperseg,len(win),noverlap,nfft,x.shape[-1]))
    return freqs, time, result, Info

def _fft_helper(x, win, detrend_func, nperseg, noverlap, nfft, sides):
    """ Calculate windowed FFT """
    # Created strided array of data segments
    if nperseg == 1 and noverlap == 0:
        result = x[..., np.newaxis]
    else:
        # http://stackoverflow.com/a/5568169
        step = nperseg - noverlap
        shape = x.shape[:-1]+((x.shape[-1]-noverlap)//step, nperseg)
        strides = x.strides[:-1]+(step*x.strides[-1], x.strides[-1])
        result = np.lib.stride_tricks.as_strided(x, shape=shape,
                                                 strides=strides)

    # Detrend each data segment individually
    result = detrend_func(result)

    # Apply window by multiplication
    result = win * result

    # Perform the fft. Acts on last axis by default. Zero-pads automatically
    if sides == 'twosided':
        raise Exception('NOT IMPLEMENTED')
        #func = fftpack.fft
    else:
        result = result.real
        func = np.fft.rfft
    result = func(result, n=nfft)

    return result

def _triage_segments(window, nperseg,input_length):
    """
    Parses window and nperseg arguments for spectrogram and _spectral_helper.
    This is a helper function, not meant to be called externally.
    """

    #parse window; if array like, then set nperseg = win.shape
    if isinstance(window, string_types) or isinstance(window, tuple):
        # if nperseg not specified
        if nperseg is None:
            nperseg = 256  # then change to default
        if nperseg > input_length:
            print('nperseg = {0:d} is greater than input length '
                              ' = {1:d}, using nperseg = {1:d}'
                              .format(nperseg, input_length))
            nperseg = input_length
        win = get_window(window, nperseg)
    else:
        win = np.asarray(window)
        if len(win.shape) != 1:
            raise ValueError('window must be 1-D')
        if input_length < win.shape[-1]:
            raise ValueError('window is longer than input signal')
        if nperseg is None:
            nperseg = win.shape[0]
        elif nperseg is not None:
            if nperseg != win.shape[0]:
                raise ValueError("value specified for nperseg is different from"
                                 " length of window")

    return win, nperseg

# --------------------------------------------------------------------------------}
# --- Unittests
# --------------------------------------------------------------------------------{
import unittest

class TestSpectral(unittest.TestCase):

    def test_fft_amplitude(self):
        dt=0.1
        t=np.arange(0,10,dt);
        f0=1;
        A=5;
        y=A*np.sin(2*np.pi*f0*t)
        f,Y,_=fft_amplitude(y,fs=1/dt,detrend=False)
        i=np.argmax(Y)
        self.assertAlmostEqual(Y[i],A)
        self.assertAlmostEqual(f[i],f0)

    def test_fft_binning(self):
        dt=0.1
        t=np.arange(0,10,dt);
        f0=1;
        A=5;
        y=A*np.sin(2*np.pi*f0*t)

        f,   Y, Info  = psd_binned(y, fs=1/dt, nPerDecade=10, detrend ='constant')
        f2, Y2, Info2 = psd       (y, fs=1/dt,           detrend ='constant')
        #print(f)
        #print(Y)

        #import matplotlib.pyplot as plt
        #fig,ax = plt.subplots(1, 1, sharey=False, figsize=(6.4,4.8)) # (6.4,4.8)
        #fig.subplots_adjust(left=0.12, right=0.95, top=0.95, bottom=0.11, hspace=0.20, wspace=0.20)
        #ax.plot( f2, Y2   , label='Full')
        #ax.plot( f,  Y    , label='Binned')
        #ax.set_xlabel('')
        #ax.set_ylabel('')
        #ax.legend()
        #plt.show()

if __name__ == '__main__':
    #TestSpectral().test_fft_binning()
    unittest.main()


import zmq
import json

def main():
    zc = zmq.Context()
    zs = zc.socket(zmq.PULL)
    zs.connect('ipc:///var/run/bluewalker-gateway/relay.zmq')
    while True:
        print(json.dumps(zs.recv_json()))

if __name__ == '__main__':
    main()


import time;
import hashlib;
import logging;

from resources.lib.modules import utils;

try:
    from sqlite3 import dbapi2 as database;

except:
    from pysqlite2 import dbapi2 as database;

logger = logging.getLogger('funimationnow');

def getToken(system):

    try:

        dbcon = database.connect(utils.tokensFile);
        dbcur = dbcon.cursor();

    except:

        return None;

    try:

        dbcur.execute("SELECT * FROM tokens WHERE system IN ('%s')" % system);

        match = dbcur.fetchone();

        if match is not None:

            return match;

        else:
            return None;

    except Exception as inst:
        logger.error(inst);

        return None;

def setToken(system, user, token):

    logger.debug('Attempting to update tokens DB.');

    try:

        logger.debug('Validating DB file exists.');

        utils.makeFile(utils.dataPath);

        dbcon = database.connect(utils.tokensFile);
        dbcur = dbcon.cursor();

        logger.debug('Creating tokens table if it does not exist.');

        dbcur.execute("CREATE TABLE IF NOT EXISTS tokens (""system TEXT, ""user TEXT, ""token TEXT"", ""UNIQUE(system)"");");

        try:

            logger.debug('Attempting to delete tokens entry.');

            dbcur.execute("DELETE FROM tokens WHERE system IN ('%s')" % (system));

        except Exception as inst:
            logger.error(inst);

        try:

            logger.debug('Attempting to insert tokens entry.');

            dbcur.execute("INSERT INTO tokens Values (?, ?, ?)", (system, user, token));

        except Exception as inst:
            logger.error(inst);

        logger.debug('Commiting DB change.');

        dbcon.commit();

        return True;

    except Exception as inst:

        logger.error(inst);

        return False;


import bpy, time, struct, json, math, mathutils
from bpy.props import *

bl_info = {
    "name": "Export fake.grav map",
    "author": "literallyvoid",
    "blender": (2, 78, 0),
    "location": "File > Export > FGM",
    "description": "pls",
    "category": "Import-Export"
    }

class ExportFGM(bpy.types.Operator):
    bl_idname = "export.fgm"
    bl_label = "Export FGM"

    filepath = StringProperty(subtype = "FILE_PATH", name = "File Path", description = "Filepath for exporting", maxlen = 1024, default = "")

    def execute(self, context):

        def t(x, y = None):
            if y == None:
                y = x.y
                x = x.x
                return mathutils.Vector([x, -y])
            else:
                return (x, -y)

        data = []
        objects = []
        for i in range(20):
            objects.append([])
            for o in context.scene.objects:
                if o.layers[i]:
                    objects[-1].append(o)
        for layer in range(20):
            layerData = {"objects": []}
            for obj in objects[layer]:
                if obj.type == "MESH":
                    if "arc" in obj.name.lower():
                        matrix = obj.matrix_world
                        x, y = (matrix * mathutils.Vector([0, 0, 0]))[:2]
                        x2, y2 = (matrix * mathutils.Vector([1, 0, 0]))[:2]
                        x, y = t(x, y)
                        x2, y2 = t(x2, y2)
                        r = math.degrees(math.atan2(y2 - y, x - x2))

                        size = 0
                        for i in range(8):
                            for j in range(3):
                                size = max(size, obj.bound_box[i][j])

                        angles = []
                        for v in obj.data.vertices:
                            nang = math.degrees(math.atan2(t(v.co).y, t(v.co).x)) + r
                            for o in angles:
                                while nang < o - 180:
                                    nang += 360
                                while nang > o + 180:
                                    nang -= 360
                            angles.append(nang)
                        angles = list(sorted(angles))
                        layerData["objects"].append(["arc", t(obj.location).x, t(obj.location).y, size, angles[0] + 180, angles[-1] + 180])
                    else:
                        for e in obj.data.edges:
                            v1 = t(obj.matrix_world * obj.data.vertices[e.vertices[0]].co)
                            v2 = t(obj.matrix_world * obj.data.vertices[e.vertices[1]].co)
                            layerData["objects"].append(["line", v1.x, v1.y, v2.x, v2.y])
                elif obj.type == "CURVE":
                    mesh = obj.to_mesh(context.scene, True, "RENDER")
                    properties = {}
                    if "lava" in obj.name.lower():
                        properties = {"onTouch": "restartLevel"}
                    for e in mesh.edges:
                        v1 = t(obj.matrix_world * mesh.vertices[e.vertices[0]].co)
                        v2 = t(obj.matrix_world * mesh.vertices[e.vertices[1]].co)
                        layerData["objects"].append(["line", v1.x, v1.y, v2.x, v2.y, properties])
                    bpy.data.meshes.remove(mesh)
                elif obj.type == "EMPTY":
                    if "spawn" in obj.name.lower():
                        matrix = obj.matrix_world
                        x, y = (matrix * mathutils.Vector([0, 0, 0]))[:2]
                        x2, y2 = (matrix * mathutils.Vector([1, 0, 0]))[:2]
                        x, y = t(x, y)
                        x2, y2 = t(x2, y2)
                        r = -math.degrees(math.atan2(y2 - y, x - x2)) + 180
                        layerData["objects"].append(["playerSpawn", x, y, r])
                    elif "portal" in obj.name.lower():
                        layerData["objects"].append(["portal", t(obj.location).x, t(obj.location).y])
                elif obj.type == "FONT":
                    matrix = obj.matrix_world
                    x, y = (matrix * mathutils.Vector([0, 0, 0]))[:2]
                    x2, y2 = (matrix * mathutils.Vector([1, 0, 0]))[:2]
                    x, y = t(x, y)
                    x2, y2 = t(x2, y2)
                    r = -math.degrees(math.atan2(x2 - x, y2 - y)) + 90
                    layerData["objects"].append(["text", obj.data.body, obj.data.size, t(obj.location).x, t(obj.location).y, r])
            if len(layerData["objects"]):
                data.append(layerData)
        fi = open(self.properties.filepath, "w")
        fi.write(json.dumps(data))
        fi.close()

        return {"FINISHED"}

    def invoke(self, context, event):
        wm = context.window_manager
        wm.fileselect_add(self)
        return {'RUNNING_MODAL'}

def menu_func(self, context):
    self.layout.operator(ExportFGM.bl_idname, text = "FGM", icon = "BLENDER")

def register():
    bpy.utils.register_class(ExportFGM)
    bpy.types.INFO_MT_file_export.append(menu_func)

def unregister():
    bpy.utils.unregister_class(ExportFGM)
    bpy.types.INFO_MT_file_export.remove(menu_func)

if __name__ ==  "__main__":
    register()


import pandas as pd
from pymongo import MongoClient

LAYER1 = ['tensorflow', 'tensorflow-cpu', 'tensorflow-gpu', 'torch', 'mxnet', 'mxnet-cu112', 'mxnet-cu110', 'mxnet-cu102', 'mxnet-cu102mkl', 'mxnet-cu101', 'mxnet-cu101mkl', 'mxnet-cu100', 'mxnet-cu100mkl',
          'mxnet-cu92', 'mxnet-cu92mkl', 'mxnet-cu90', 'mxnet-cu90mkl', 'mxnet-cu80', 'mxnet-cu80mkl', 'mxnet-native', 'paddlepaddle', 'paddlepaddle-gpu', 'mindspore', 'mindspore-ascend', 'mindspore-gpu']

client = MongoClient(host='127.0.0.1', port=27017)

filter = {
    'layer': {
        '$gt': 1
    }
}
project = {
    '_id': 0,
    'name': 1,
    'dependency': 1,
    'framework': 1
}

result = client['pypi']['dl_packages'].find(
    filter=filter,
    projection=project
)

df = pd.DataFrame(list(result)).drop_duplicates()
df = df[~(df['name'].isin(LAYER1))]
print(df.groupby('framework')['name'].count())

df.to_csv("data/dag.edgs", index=False, header=True)


# -*- coding: utf-8 -*-
"""
Created on Fri Jan 20 16:59:47 2017

@author: changlue.she
"""
import jieba.posseg as pseg
import jieba
import pandas as pd
import numpy as np
from collections import Counter
jieba.load_userdict('C:/Users/Administrator.NBJXUEJUN-LI/Desktop/project/pythonProj/intel_cust_stastis/userDict.txt')
#---------------------------------------------------------------------------------------------------
#rawdata = pd.read_csv('C:/Users/Administrator.NBJXUEJUN-LI/Desktop/project/MSXF/ZhiChi/Statistic/output/conversation.csv',
#                      header=0,encoding='gbk',usecols=[u'消息目标',u'会话ID',u'消息内容'])
#rawdata = rawdata.loc[rawdata[u'消息目标'] == u'机器人',:]
#rawdata = rawdata.sort([u'会话ID'])

rawdata = pd.read_csv('C:/Users/Administrator.NBJXUEJUN-LI/Desktop/project/MSXF/message feature etl/smsResult.csv',
                      header=0,encoding='gbk' )
rawdata = rawdata.drop_duplicates()
#---------------------------------------------------------------------------------------------------
TF_term = []
IDF_term = []
ALL_term = []
#userIDs = list(rawdata[u'会话ID'])
userIDs = range(len(rawdata['x']))
conversations = list(rawdata[u'x'])
oldidx=None
add = ['used to drop']

for idx,userID in enumerate(userIDs):
    conversation = conversations[idx]
    if  len(conversation)>6:
        conversation = [pair.word for pair in pseg.lcut(conversation) if pair.flag in ['n','ns','vs','nv']]
        if len(conversation)>0:
            if userID!=oldidx:
                 oldidx = userID
                 ALL_term += add
                 tf = dict(Counter(add))
                 IDF_term+=tf.keys()
                 TF_term.append(tf)
                 add = conversation
            else:
                 add+= conversation

TF_term.pop(0)
wordFreq  = dict(Counter(ALL_term))
allDocNum = len(TF_term)
IDF       = dict(Counter(IDF_term))
keyWord   = []
tfidf     = []
for tf in TF_term:
    tmp = 0
    for word in tf.keys():
        tf_idf = tf[word]*np.log(allDocNum/IDF[word])
        if tf_idf>tmp:
            tmp  = tf_idf
            keyW = word
            tfs  = tf_idf
        keyWord.append(keyW)
        tfidf.append(tfs)
        tmp = 0
keyWord = dict(Counter(keyWord))

idxs = np.argsort(-np.array(list(keyWord.values())))[:20]
keywords = [list(keyWord.keys())[idx] for idx in idxs]
wordFreq = [wordFreq[word] for word in keywords]
#save those key words
outfile = pd.DataFrame([keywords,wordFreq])
outfile = outfile.T
dirs = 'C:/Users/Administrator.NBJXUEJUN-LI/Desktop/project/MSXF/message feature etl/关键词句.xlsx'
outfile.to_excel(dirs,encoding='gb18030',index=False )


from collections import deque

def solution(priorities, location):
    # 프로세스의 위치를 담아주고
    pro = [i for i in range(len(priorities))]
    # 프로세스의 번호마다 실행한 순서를 기록할 딕셔너리
    p_dic = {i:-1 for i in range(len(priorities))}
    # 이 두가지를 모두 deque의 queue로 지정한다.
    queue = deque(priorities)
    p_queue = deque(pro)
    order = 1
    # queue가 모두 실행할 때 까지 반복하며
    while queue:
        # queue의 max값이 queue의 첫번째면
        if max(queue) == queue[0]:
            # queue를 삭재하고
            queue.popleft()
            # 해당 queue의 실행 순서를 넣고
            p_dic[p_queue.popleft()] = order
            # 순서를 + 1
            order += 1
        # max값이 아니라면
        else:
            # queue값과 프로세스의 위치를 popleft하고
            pri = queue.popleft()
            num = p_queue.popleft()
            # 다시 맨 뒤에 삽입한다.
            queue.append(pri)
            p_queue.append(num)

    # 마지막으로 알고자 하는 프로세스의 실행순서를 반환.
    return p_dic[location]


import re
import json
import os
import datetime

cwd = os.getcwd()

START_DATE = datetime.datetime(2022, 12, 26, 0, 0)

def get_wjc_day(today):
  return (today - START_DATE).days

# Input format is
#     FirstInitial. Lastname, SOG, M, SS, ...
def time_to_float(x):
  return round(x/60, 2)

def write_to_data(manual_player_data, date):
  # Make a json with updated reference to json
  with open(cwd + '/json/beta-wjc/2022-23/manual-player-data.json', 'w') as json_file:
    json_file.write(json.dumps(manual_player_data, indent=4))
  return

def parse_manual_data(matches, date):
  # manual-player-data.json data for reference
  manual_player_data = {}
  with open(cwd + '/json/beta-wjc/2022-23/manual-player-data.json', 'r') as json_file:
    manual_player_data = json.loads(json_file.read())
    # name-lookup-table.json translates short hand name to reference name
    with open(cwd + '/json/beta-wjc/2022-23/name-lookup-table.json', 'r') as json_file_2:
      name_lookup_table = json.loads(json_file_2.read())
      #print(f"\t### Manual Player Data: {manual_player_data}")

      # Pull data from list and update reference to json
      for id in matches:
        # id format is:
        #     FirstInitial. Lastname, SOG, M, SS, ...
        # name, SOG, min, sec = re.match('([\w|\s]*),([0-9]+),([0-9]{2}),([0-5][0-9]?)', id).groups()
        name, SOG, min, sec = re.match('[\s]*([\w|\s|.|\']*),[\s]*([0-9]+)[\s]*,[\s]*([0-9]{1,2})[\s]*,[\s]*([0-5][0-9]{0,1})[\s]*?', id).groups()
        name = name.rstrip()

        TPM = float(min) + time_to_float(float(sec))

        found = False
        for team in name_lookup_table:
          for player in name_lookup_table[team]:
            found = False
            if player.lower() == name.lower():
              name = name_lookup_table[team][player]
              #print(f"\t Player:{player}, Name:{name}")
              #print(f"\tFound manual player data: {manual_player_data[team][name]["SOG"]}, {manual_player_data[team][name]["TPM"]}, {int(SOG)}, {TPM}")
              manual_player_data[team][name]["SOG"] += int(SOG)
              manual_player_data[team][name]["TPM"] = round(manual_player_data[team][name]["TPM"] + TPM, 2)
              found = True
              break

          if found:
            break

  write_to_data(manual_player_data, date)
  return

def main():

  date = str(get_wjc_day(datetime.datetime.today()))

  # Prompt user to input list
  data_list = input('Input, ex FirstInitial. Lastname, SOG, M, SS, ...: ')

  matches = re.findall('[\w|\s|.|\']*,[\s]*[0-9]+[\s]*,[\s]*[0-9]{1,2}[\s]*,[\s]*[0-5][0-9]{0,1}[\s]*?', data_list)

  parse_manual_data(matches, date)

  print(f'''
  Manual player data has been updated and written to /json/beta-wjc/manual-player-data.json
  Run `python src/beta-wjc/merge-data.py` to update the data in /json/beta-wjc/2022-2023/manual-player-data.json
  ''')

if __name__ == "__main__":
  main()


import os
from werkzeug.utils import secure_filename
import logging
from logging.handlers import RotatingFileHandler
import random
from flask import Flask, request, jsonify
from spu import SpeechProcessingUnit

current_dir = os.path.dirname(os.path.realpath(__file__))
log_path = os.path.join(current_dir, "log")
if not os.path.exists(log_path):
    os.mkdir(log_path)
LOG_FILENAME = os.path.join(log_path, 'server.log')
LOG_BACKUP_COUNT = 5
LOG_FILE_SIZE_BYTES = 50 * 1024 * 1024

VOICE_DECODE_ERROR = 10
PREDICTION_ERROR = 12
UNKNOWN_ERROR = 21
INVALID_FORMAT = 30
MISSING_ARGUMENTS = 40

errors = {
    VOICE_DECODE_ERROR: "VOICE_DECODE_ERROR",
    PREDICTION_ERROR: "PREDICTION_ERROR",
    UNKNOWN_ERROR: "UNKNOWN_ERROR",
    INVALID_FORMAT: "INVALID_FORMAT_REQ",
    MISSING_ARGUMENTS: "MISSING_ARGUMENTS_REQ"
}

def init_logger(app):
    handler = RotatingFileHandler(LOG_FILENAME, maxBytes=LOG_FILE_SIZE_BYTES, backupCount=LOG_BACKUP_COUNT)
    handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)
    loggers = [app.logger]
    for logger in loggers:
        logger.addHandler(handler)

spu_core = SpeechProcessingUnit(fs=16000)

app = Flask(__name__)
app.config['SECRET_KEY'] = 'the quick brown fox jumps over the lazy dog'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

init_logger(app)

@app.before_request
def log_request():
    app.logger.debug("Request: %s %s", request.method, request.url)

class WebAppException(Exception):
    def __init__(self, error_code, status_code=None):
        Exception.__init__(self)
        self.status_code = 400
        self.exception = Exception
        self.error_code = error_code
        try:
            self.message = errors[self.error_code]
        except:
            self.error_code = UNKNOWN_ERROR
            self.message = errors[self.error_code]
        if status_code is not None:
            self.status_code = status_code

    def to_dict(self):
        rv = dict()
        rv['status'] = 'failed'
        rv['code'] = self.error_code
        rv['message'] = self.message
        return rv

class ThrowsWebAppException(object):
    def __init__(self, error_code, status_code=None):
        self.error_code = error_code
        self.status_code = status_code

    def __call__(self, function):
        def return_function(*args, **kwargs):
            try:
                return function(*args, **kwargs)
            except Exception as e:
                raise WebAppException(self.error_code)

        return return_function

@app.errorhandler(WebAppException)
def handle_exception(error):
    app.logger.exception(error.message)
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    return response

@ThrowsWebAppException(error_code=PREDICTION_ERROR)
def vad_prediction(audio_file, threshold):
    vad_annotation = spu_core.apply_vad(file_path=audio_file, threshold=threshold, gpu_number=0)
    vad_out = {"sad_annotation": vad_annotation}
    return vad_out

@app.route('/sad', methods=['POST'])
def vad():
    if request.headers['Content-Type'].startswith('multipart/form-data'):
        try:
            # Remember the paramName was set to 'file', we can use that here to grab it
            file = request.files['file']
            # secure_filename makes sure the filename isn't unsafe to save
            temp_path = os.path.join(current_dir, "temp_data")
            if not os.path.exists(temp_path):
                os.mkdir(temp_path)
            save_path = os.path.join(temp_path, str(random.randint(0, 1000000)) + secure_filename(file.filename))
            # We need to append to the file, and write as bytes
            with open(save_path, 'ab') as temp_file:
                temp_file.seek(0, 2)
                file_byes = file.read()
                temp_file.write(file_byes)

            audio_file = save_path
            threshold = float(request.form["threshold"])
        except:
            raise WebAppException(error_code=MISSING_ARGUMENTS)
        response = jsonify(vad_prediction(audio_file, threshold))
        return response
    else:
        raise WebAppException(error_code=INVALID_FORMAT)

if __name__ == '__main__':
    host = '0.0.0.0'
    port = 5005
    app.run(host=host, port=port, debug=True, use_reloader=False, threaded=False)


from Products.Five import BrowserView
from zope.component import getMultiAdapter
from Products.CMFCore.interfaces import ISiteRoot
from OFS.interfaces import IOrderedContainer
from plone.memoize import view
from cciaapd.contenttypes import logger

class SchedeContentView(BrowserView):

    def get_position_in_parent(self, obj):
        """
        Use IOrderedContainer interface to extract the object's manual ordering position
        """
        parent = obj.aq_inner.aq_parent
        ordered = IOrderedContainer(parent, None)
        if ordered is not None:
            return ordered.getObjectPosition(obj.getId())
        return 0

    def sort_by_position(self, a, b):
        """
        Python list sorter cmp() using position in parent.

        Descending order.
        """
        return self.get_position_in_parent(a) - self.get_position_in_parent(b)

    def get_results(self, portal_type):
        """Returns results depending on request and the following logic

        Case 1: a folder with a Scheda set as  default view ->
                return scheda contents filtered by content_types

        Case 2: the context is a Scheda ->
                return the contents of Moduli or Riferimenti according
                to the requeste portal type

        Case 3: as a fallback return an empty list
        """
        if ISiteRoot.providedBy(self.context):
            return []
        if self.has_default_view(self.context):
            return self.find_nephews(portal_type,
                                     self.get_default_view_object(self.context))

        if self.is_scheda(self.context):
            return self.find_nephews(portal_type)
        return []

    @view.memoize
    def has_default_view(self, item):
        """Return true if the default view is set to Scheda"""
        obj = self.get_default_view_object(item)
        if not obj:
            return False
        return obj.portal_type == 'Scheda'

    @view.memoize
    def get_default_view_object(self, item):
        ''' Check for default page and get it from the context
        if default page is not set return None, otherwise the object
        '''
        default_page_method = getattr(item, 'getDefaultPage', None)
        if not default_page_method:
            return None
        default_page = default_page_method()
        obj = item.get(default_page)
        return obj

    def find_nephews(self, portal_type, context=None):
        '''
        Check inside objects all the filtered types and
        return their children.
        Moreover, parent folders must be traversed to get contents of the
        first one where the default view is set as Scheda, if exists.
        '''
        if context is None:
            context = self.context
        results = []
        schede_deep = 0
        for item in context.aq_chain:
            if schede_deep == 2:
                break
            context_state = getMultiAdapter(
                (item, self.request),
                name=u'plone_context_state'
            )
            if context_state.is_default_page():
                # if is a default view, skip it, we'll handle it on the folder.
                # this avoid double entries
                continue
            if not self.is_scheda(item) and not self.has_default_view(item):
                # if the context isn't a scheda and its default_view isn't a
                # scheda, skip it
                continue
            schede_attachments = self.get_scheda_attachments(item, portal_type)
            if schede_attachments:
                schede_deep = schede_deep + 1
                results.extend(schede_attachments)
        return results

    def get_scheda_attachments(self, scheda, portal_type):
        default_view_obj = self.get_default_view_object(scheda)
        if default_view_obj and self.is_scheda(default_view_obj):
            scheda = default_view_obj
        children = scheda.listFolderContents(
            contentFilter={'portal_type': portal_type})
        results = []
        for child in children:
            items = [x for x in child.listFolderContents() if not x.isExpired()]
            if items:
                results.extend(sorted(items, self.sort_by_position))
        return results

    def is_scheda(self, context):
        """Return true if the content is a Scheda"""
        return getattr(context, 'portal_type', "") == "Scheda"


import inspect
import time

from django.http import HttpResponse, JsonResponse
from django.core import serializers
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie

from .models import Transaction
from user_group.models import User

# Create your views here.
# @method_decorator(ensure_csrf_cookie)
def createTransaction(request):
    if request.method == "GET":
        transactions = Transaction.objects.all().values()
        return JsonResponse(list(transactions), safe=False)

    if request.method == "POST":
        userid = int(request.POST['user'])
        amount = float(request.POST['amount'])
        owner = User.objects.get(pk=userid)
        Transaction.objects.create(owner=owner, amount=amount)

        return HttpResponse("Hello from POST " + inspect.stack()[0][3])

# @method_decorator(ensure_csrf_cookie)
def updateTransaction(request):
    if request.method == "POST":
        transactionid = int(request.POST['transactionid'])
        transaction = Transaction.objects.get(pk=transactionid)
        amount = transaction.amount
        transaction.amount = amount + 100
        time.sleep(0.005)
        transaction.save()

        return JsonResponse(serializers.serialize("json",[transaction,]), safe=False)

    if request.method == "GET":
        return HttpResponse("Hello from Get " + inspect.stack()[0][3])

def restoreTransaction(request):
    if request.method == "POST":
        transactionid = int(request.POST['transactionid'])
        transaction = Transaction.objects.get(pk=transactionid)
        amount = transaction.amount
        time.sleep(0.007)
        transaction.amount = amount - 100
        transaction.save()

        return JsonResponse(serializers.serialize("json",[transaction,]), safe=False)

    if request.method == "GET":
        return HttpResponse("Hello from Get " + inspect.stack()[0][3])

def resetTransaction(request):
    if request.method == "POST":
        transactionid = int(request.POST['transactionid'])
        transaction = Transaction.objects.get(pk=transactionid)
        amount = transaction.amount
        transaction.amount = 0
        transaction.save()

        return JsonResponse(serializers.serialize("json",[transaction,]), safe=False)

    if request.method == "GET":
        return HttpResponse("Hello from Get " + inspect.stack()[0][3])

# @method_decorator(ensure_csrf_cookie)
def getTransactions(request):
    if request.method == "POST":
        return HttpResponse("Hello from Post " + inspect.stack()[0][3])

    if request.method == "GET":
        return HttpResponse("Hello from Get " + inspect.stack()[0][3])


from django.shortcuts import render
from .forms import MyForm
from django.contrib import messages
from base64 import b64encode
from .editing import trim_image_to_square
import pickle
pickled_model = pickle.load(open('knn_model_images.pkl', 'rb'))

def one_view(request):
    if request.method == 'POST':
        one_form = MyForm(request.POST, request.FILES)
        if one_form.is_valid():
            # name = one_form.cleaned_data
            image_file = one_form.cleaned_data.get('img')
            data = image_file.read()
            encoded = b64encode(data).decode()
            mime = 'image/jpeg;'
            edited_image_array = trim_image_to_square(image_file)
            edited_image_array = pickled_model.predict(edited_image_array.reshape(1, -1))
            # edited_image_array = edited_image_array.reshape(1, -1)

            """
            data = image_file.read()
            encoded = b64encode(data).decode()
            mime = 'image/jpeg;'
            context = {"image": f"data:{mime}base64,{encoded}",
                       'data': name.get('name'),
                       'one_form': MyForm()
                       }
            """
            context = {'image': edited_image_array, 'one_form': MyForm(), "image2": f"data:{mime}base64,{encoded}"}
            messages.success(request, "Data is in order")
            return render(request, 'myapp/home.html', context)
        else:
            messages.error(request, one_form.errors)
            return render(request, 'myapp/home.html', {'one_form': MyForm()})
    else:
        return render(request, 'myapp/home.html', {'one_form': MyForm()})

def methodology(request):
    return render(request, 'myapp/methodology.html', {})


from utils.utils import myprint as print

from utils.net.utils import Trainable, to_tensor, device
from utils.net.linear.mlp import MLP
from torch import nn, optim
from torch.nn import functional as F

class Classifier(Trainable):
    def __init__(self, input_size, classes, layers, hid_size=1000, hid_smooth=None, drop=0,
                 batch_norm=True, nl='leakyrelu', gated=False, excitability=False, excit_buffer=False, name=None):

        super().__init__()

        self.name = self.__class__.__name__ if name is None else name

        layers = int(layers)
        if layers < 1:
            raise ValueError("The classifier needs to have at least 1 fully-connected layer.")

        ######------SPECIFY MODEL------######

        self.mlp = MLP(input_size=input_size, output_size=classes, layers=layers, hid_size=hid_size,
                       hid_smooth=hid_smooth, batch_norm=batch_norm, gated=gated, excitability=excitability,
                       excit_buffer=excit_buffer, drop=drop, nl=nl, output='excitable')

        self.loss = nn.CrossEntropyLoss(reduction='mean')

    def forward(self, x):
        return self.mlp(x)

    def predict(self, x):
        mode = self.training
        self.eval()
        x = to_tensor(x).to(device)
        y_hat = self(x)
        predicted = y_hat.detach().argmax(1)
        self.train(mode=mode)
        return predicted.cpu().detach().numpy()

    def dist_loss(self, scores, target_scores, T=2.):
        """Compute knowledge-distillation (KD) loss given [scores] and [target_scores].

        Both [scores] and [target_scores] should be tensors, although [target_scores] should be repackaged.
        'Hyperparameter': temperature"""

        log_scores_norm = F.log_softmax(scores / T, dim=1)
        targets_norm = F.softmax(target_scores / T, dim=1)

        # Calculate distillation loss (see e.g., Li and Hoiem, 2017)
        KD_loss_unnorm = -(targets_norm * log_scores_norm)
        KD_loss_unnorm = KD_loss_unnorm.sum(dim=1)                      #--> sum over classes
        KD_loss_unnorm = KD_loss_unnorm.mean()                          #--> average over batch

        # normalize
        KD_loss = KD_loss_unnorm * T**2

        return KD_loss

    def train_batch(self, x, y, gen_x, gen_y, scores, gen_scores, rnt):
        super().train_batch()

        y_hat = self(x)
        total_loss = self.loss(y_hat, y)

        predicted = y_hat.detach().argmax(1)
        correct = (predicted == y).sum().item()

        if gen_x is not None:
            y_hat = self(gen_x)
            replay_loss = self.dist_loss(y_hat, gen_scores, T=2.0)
            total_loss = rnt * total_loss + (1 - rnt) * replay_loss

        total_loss.backward()
        self.optimizer.step()

        return total_loss.item(), correct


#!/bin/env python
import ROOT
import sys
sys.path.append('..')
from collections import namedtuple

from objects.MultiGraph   import MultiGraph
from objects.FitFunctions import crystalballEfficiency
from objects.Fitter       import Fitter
from plot.CMSStyle        import CMS_lumi
from plot.PlotStyle       import PlotStyle

ROOT.TH1.SetDefaultSumw2()
ROOT.gROOT.SetBatch(True)

ROOT.gStyle.SetLegendFont(42)

ROOT.gStyle.SetOptStat(False)

ROOT.gStyle.SetOptFit(000000)

c1 = ROOT.TCanvas('', '', 700, 700)

ROOT.gPad.SetGridx()
ROOT.gPad.SetGridy()

func = ROOT.TF1('func', crystalballEfficiency, 0., 200., 5)
func.SetNpx(100000)
func.SetParameter(0,  37.5 )
func.SetParameter(1,   2.3 )
func.SetParameter(2,   1.4  )
func.SetParameter(3,   3.   )
func.SetParameter(4,   0.96)

func.SetParLimits(0,  10.  ,  50.  )
func.SetParLimits(1,   0.5 ,  30.  )
func.SetParLimits(2,   0.01,  40.  )
# func.SetParLimits(3,   1.01, 999.  )
func.SetParLimits(3,  1.01, 141. )
func.SetParLimits(4,  0.5, 1. )

# func.FixParameter(0, 37. )
# func.FixParameter(1, 7.  )
# func.FixParameter(2, 0.92 )
# func.FixParameter(3, 139. )
# func.FixParameter(4, 0.94 )

func.SetParName(0, 'm_{0}')
func.SetParName(1, 'sigma')
func.SetParName(2, 'alpha')
func.SetParName(3, 'n'    )
func.SetParName(4, 'norm' )

# func = ROOT.TF1('func', '[0]*TMath::Erf((x-[1])/[2])', 0., 180.)  # non def pos erf
# func = ROOT.TF1('func', '0.5 * ([0]*TMath::Erf((x-[1])/[2]) + 1)', 0., 180.) # def pos erf
# func.SetParameter(0,  0.9)
# func.SetParameter(1, 20. )
# func.SetParameter(2, 10. )
#
# func.SetParName(0, 'plateau')
# func.SetParName(1, 'threshold')
# func.SetParName(2, 'width')

myGraph = namedtuple('mygraph', 'file graph function colour legend legendOpt')

# file = '../test/diTau_plots.root'
file = '../test/diTau_plots_good.root'
# file = '../test/diTau_plots_high_mt_os.root'
# file = '../test/diTau_plots_ss.root'
# file = '../test/mu_tau_singleL1_plots.root'
# file = '../test/mu_tau_singleL1_plots_high_mt_os.root'
# file = '../test/mu_tau_singleL1_plots_ss.root'
# file = '../test/mu_tau_crossL1_plots.root'
# file = '../test/mu_tau_crossL1_plots_high_mt_os.root'
# file = '../test/mu_tau_crossL1_plots_ss.root'

graphs = [
    myGraph(file=file, graph='lowmt_zmass_sub_NoIso_HLT/tau_pt'      , function=func, colour=ROOT.kOrange-6, legend='NoIso'     , legendOpt='EP'),
    myGraph(file=file, graph='lowmt_zmass_sub_VLooseIso_HLT/tau_pt'  , function=func, colour=ROOT.kOrange-4, legend='VLooseIso' , legendOpt='EP'),
    myGraph(file=file, graph='lowmt_zmass_sub_LooseIso_HLT/tau_pt'   , function=func, colour=ROOT.kOrange-2, legend='LooseIso'  , legendOpt='EP'),
    myGraph(file=file, graph='lowmt_zmass_sub_MediumIso_HLT/tau_pt'  , function=func, colour=ROOT.kOrange  , legend='MediumIso' , legendOpt='EP'),
    myGraph(file=file, graph='lowmt_zmass_sub_TightIso_HLT/tau_pt'   , function=func, colour=ROOT.kOrange+2, legend='TightIso'  , legendOpt='EP'),
    myGraph(file=file, graph='lowmt_zmass_sub_VTightIso_HLT/tau_pt'  , function=func, colour=ROOT.kOrange+4, legend='VTightIso' , legendOpt='EP'),
    myGraph(file=file, graph='lowmt_zmass_sub_VVTightIso_HLT/tau_pt' , function=func, colour=ROOT.kOrange+6, legend='VVTightIso', legendOpt='EP'),
]

# graphs = [
#     myGraph(file=file, graph='highmt_os_NoIso_HLT/tau_pt'      , function=func, colour=ROOT.kAzure-6, legend='NoIso'     , legendOpt='EP'),
#     myGraph(file=file, graph='highmt_os_VLooseIso_HLT/tau_pt'  , function=func, colour=ROOT.kAzure-4, legend='VLooseIso' , legendOpt='EP'),
#     myGraph(file=file, graph='highmt_os_LooseIso_HLT/tau_pt'   , function=func, colour=ROOT.kAzure-2, legend='LooseIso'  , legendOpt='EP'),
#     myGraph(file=file, graph='highmt_os_MediumIso_HLT/tau_pt'  , function=func, colour=ROOT.kAzure  , legend='MediumIso' , legendOpt='EP'),
#     myGraph(file=file, graph='highmt_os_TightIso_HLT/tau_pt'   , function=func, colour=ROOT.kAzure+2, legend='TightIso'  , legendOpt='EP'),
#     myGraph(file=file, graph='highmt_os_VTightIso_HLT/tau_pt'  , function=func, colour=ROOT.kAzure+4, legend='VTightIso' , legendOpt='EP'),
#     myGraph(file=file, graph='highmt_os_VVTightIso_HLT/tau_pt' , function=func, colour=ROOT.kAzure+6, legend='VVTightIso', legendOpt='EP'),
# ]

# graphs = [
#     myGraph(file=file, graph='ss_NoIso_HLT/tau_pt'      , function=func, colour=ROOT.kViolet-6, legend='NoIso'     , legendOpt='EP'),
#     myGraph(file=file, graph='ss_VLooseIso_HLT/tau_pt'  , function=func, colour=ROOT.kViolet-4, legend='VLooseIso' , legendOpt='EP'),
#     myGraph(file=file, graph='ss_LooseIso_HLT/tau_pt'   , function=func, colour=ROOT.kViolet-2, legend='LooseIso'  , legendOpt='EP'),
#     myGraph(file=file, graph='ss_MediumIso_HLT/tau_pt'  , function=func, colour=ROOT.kViolet  , legend='MediumIso' , legendOpt='EP'),
#     myGraph(file=file, graph='ss_TightIso_HLT/tau_pt'   , function=func, colour=ROOT.kViolet+2, legend='TightIso'  , legendOpt='EP'),
#     myGraph(file=file, graph='ss_VTightIso_HLT/tau_pt'  , function=func, colour=ROOT.kViolet+4, legend='VTightIso' , legendOpt='EP'),
#     myGraph(file=file, graph='ss_VVTightIso_HLT/tau_pt' , function=func, colour=ROOT.kViolet+6, legend='VVTightIso', legendOpt='EP'),
# ]

mg = MultiGraph(graphs=graphs)
mg.Fill()
mg.Draw('AP')

mg.GetYaxis().SetTitle('L1 + HLT efficiency')
mg.GetXaxis().SetTitle('offline #tau p_{T} [GeV]')
mg.GetYaxis().SetTitleOffset(1.3)
mg.GetXaxis().SetTitleOffset(1.3)

mg.GetXaxis().SetRangeUser(0., 170.)
mg.GetYaxis().SetRangeUser(0., 1.05)

# ROOT.gPad.SetLogx()
mg.leg.Draw('SAMEAPZL')

ROOT.gPad.Update()
CMS_lumi(ROOT.gPad, 4, 0)

c1.SaveAs('L1efficiencyDataMC.pdf')


from re import template
from django.shortcuts import render
from django.http import HttpResponse
from blog.models import *
from accounts.models import Profil
from game.models import Gaming

def index(request):

    posts = Post.objects.filter(status='publié')[:4]
    direction = Direction.objects.order_by('-id')[:3]
    partenaires = Partnaire.objects.order_by('-id')[:4]
    games = Gaming.objects.all()[:8]
    authors = Profil.objects.all()[:2]

    template_name = 'layouts/index.html'
    context = {
        'posts': posts,
        'authors': authors,
        'games': games,
        'direction': direction,
        'partenaires': partenaires,
    }

    return render(request, template_name, context)


#!/usr/bin/env python
# -*- coding: utf-8 -*-

from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove

account = KeyboardButton('ACCOUNT')
shop = KeyboardButton('SHOP')
policy = KeyboardButton('TERMS & CONDITIONS')
support = KeyboardButton('SUPPORT')

greet_kb = ReplyKeyboardMarkup(resize_keyboard=True, row_width=1)
greet_kb.add(account, shop, policy, support)

pay = InlineKeyboardButton('💰 PAY', callback_data='PAY')
btn_pay = InlineKeyboardMarkup(row_width=1).add(pay)

back_1 = InlineKeyboardButton('⬅ BACK', callback_data='BACK')
back_docs = InlineKeyboardButton('⬅ BACK', callback_data='BACK 2')

replenish = InlineKeyboardButton('💰 REPLENISH', callback_data='REPLENISH')
btn_replenish = InlineKeyboardMarkup(row_width=1).add(replenish)

сancel = InlineKeyboardButton('СANCEL', callback_data='СANCEL')
btn_сancel = InlineKeyboardMarkup(row_width=1).add(сancel)

a = InlineKeyboardButton('📰 DOCUMENTS', callback_data='DOCS')
b = InlineKeyboardButton('🔎 LOOK UP INFO', callback_data='LOOK UP INFO')
c = InlineKeyboardButton('🗓 FULL INFO', callback_data='FULL INFO')
d = InlineKeyboardButton('🕹 OWN REGISTERED BANK ACC.', callback_data='OWN REGISTERED BANK ACCOUNTS')
e = InlineKeyboardButton('💳 CC ENROLL', callback_data='CC ENROLL')
f = InlineKeyboardButton('💵 BRUT BANK ACCOUNTS', callback_data='BRUT BANK ACCOUNTS')
g = InlineKeyboardButton('📲 REAL USA NUMBER FOR SMS', callback_data='REAL USA NUMBER FOR SMS')

a_real_num = InlineKeyboardButton('ONE SMS - 5$', callback_data='ONE SMS - 5$')
real_num = InlineKeyboardMarkup(row_width=1).add(a_real_num)

h = InlineKeyboardButton('🇺🇸 US DOCUMENTS', callback_data='US DOCS')
j = InlineKeyboardButton('🇨🇦 CA DOCUMENTS', callback_data='CA DOCS')
l = InlineKeyboardButton('🇪🇺 EU DOCUMENTS', callback_data='EU DOCS')
k = InlineKeyboardButton('🌎 SOUTH AMERICA DOCUMENTS', callback_data='NORTH AMERICA DOCS')

a_ua_docs = InlineKeyboardButton('DL FRONT - 40$', callback_data='DL FRONT - 40$')
b_ua_docs = InlineKeyboardButton('DL FRONT & BACK - 60$', callback_data='DL FRONT & BACK - 60$')
c_ua_docs = InlineKeyboardButton('PASSPORT - 40$', callback_data='PASSPORT - 40$')
d_ua_docs = InlineKeyboardButton('SSN FRONT - 25$', callback_data='SSN FRONT - 25$')
e_ua_docs = InlineKeyboardButton('SSN FRONT & BACK - 40$', callback_data='SSN FRONT & BACK - 40$')
f_ua_docs = InlineKeyboardButton('UTILITY BILL - 30$', callback_data='UTILITY BILL - 30$')
g_ua_docs = InlineKeyboardButton('BANK STATEMENT - 35$', callback_data='BANK STATEMENT - 35$')
h_ua_docs = InlineKeyboardButton('DL 2 SIDE + SELFIE - 135$', callback_data='DL 2 SIDE + SELFIE - 135$')
j_ua_docs = InlineKeyboardButton('CC FRONT + BACK SIDE - 50$', callback_data='CC FRONT + BACK SIDE - 50$')
l_ua_docs = InlineKeyboardButton('TAX FROM 1040 & 1099 - 40$', callback_data='TAX FROM 1040 & 1099 - 40$')
k_ua_docs = InlineKeyboardButton('PAYSTUB - 40$', callback_data='PAYSTUB - 40$')

docs = InlineKeyboardMarkup(row_width=1).add(a, b, c, d, e, f, g)
docs_2 = InlineKeyboardMarkup(row_width=1).add(h, j, l, k, back_1)
us_docs = InlineKeyboardMarkup(row_width=1).add(a_ua_docs, b_ua_docs, c_ua_docs, d_ua_docs, e_ua_docs, f_ua_docs, g_ua_docs, h_ua_docs, j_ua_docs, l_ua_docs, k_ua_docs, back_docs)
ca_docs = InlineKeyboardMarkup(row_width=1).add(a_ua_docs, b_ua_docs, c_ua_docs, f_ua_docs, g_ua_docs, h_ua_docs, j_ua_docs, back_docs)
eu_docs = InlineKeyboardMarkup(row_width=1).add(a_ua_docs, b_ua_docs, c_ua_docs, f_ua_docs, g_ua_docs, h_ua_docs, j_ua_docs, back_docs)
na_docs = InlineKeyboardMarkup(row_width=1).add(a_ua_docs, b_ua_docs, c_ua_docs, f_ua_docs, g_ua_docs, h_ua_docs, j_ua_docs, back_docs)

a_full_info = InlineKeyboardButton('FULL INFO - 5$', callback_data='FULL INFO 2 - 5$')
b_full_info = InlineKeyboardButton('FULL INFO + DL - 17$', callback_data='FULL INFO + DL - 17$')
c_full_info = InlineKeyboardButton('FULL INFO CS 400-599 - 10$', callback_data='FULL INFO CS 400-599 - 10$')
d_full_info = InlineKeyboardButton('FULL INFO CS 600-699 - 15$', callback_data='FULL INFO CS 600-699 - 15$')
e_full_info = InlineKeyboardButton('FULL INFO CS 700-799 - 20$', callback_data='FULL INFO CS 700-799 - 20$')
f_full_info = InlineKeyboardButton('FULL INFO CS 800-899 - 45$', callback_data='FULL INFO CS 800-899 - 45$')
full_info = InlineKeyboardMarkup(row_width=1).add(a_full_info, b_full_info, c_full_info, d_full_info, e_full_info, f_full_info, back_1)

a_cc_enroll = InlineKeyboardButton('0.5-1k - 90$', callback_data='0.5-1k - 90$')
b_cc_enroll = InlineKeyboardButton('1k-3k - 150$', callback_data='1k-3k - 150$')
c_cc_enroll = InlineKeyboardButton('3k-5k - 200$', callback_data='3k-5k - 200$')
d_cc_enroll = InlineKeyboardButton('5k-10k - 400$', callback_data='5k-10k - 400$')
e_cc_enroll = InlineKeyboardButton('>10k - 550$', callback_data='>10k - 550$')
cc_enroll = InlineKeyboardMarkup(row_width=1).add(a_cc_enroll, b_cc_enroll, c_cc_enroll, d_cc_enroll, e_cc_enroll, back_1)

a_brut_bank_accounts = InlineKeyboardButton('0.5-1k - 50$', callback_data='bba 0.5-1k - 50$')
b_brut_bank_accounts = InlineKeyboardButton('1k-3k - 100$', callback_data='bba 1k-3k - 100$')
c_brut_bank_accounts = InlineKeyboardButton('3k-5k - 150$', callback_data='bba 3k-5k - 150$')
d_brut_bank_accounts = InlineKeyboardButton('5k-10k - 250$', callback_data='bba 5k-10k - 250$')
e_brut_bank_accounts = InlineKeyboardButton('>10k - 300$', callback_data='bba >10k - 300$')
brut_bank_accounts = InlineKeyboardMarkup(row_width=1).add(a_brut_bank_accounts, b_brut_bank_accounts, c_brut_bank_accounts, d_brut_bank_accounts, e_brut_bank_accounts, back_1)

a_own_reg_bank_acc = InlineKeyboardButton('CITI BANK - 75$', callback_data='CITI BANK - 75$')
b_own_reg_bank_acc = InlineKeyboardButton('BBT BANK - 75$', callback_data='BBT BANK - 75$')
c_own_reg_bank_acc = InlineKeyboardButton('CHASE BANK - 75$', callback_data='CHASE BANK - 75$')
d_own_reg_bank_acc = InlineKeyboardButton('TD BANK - 75$', callback_data='TD BANK - 75$')
e_own_reg_bank_acc = InlineKeyboardButton('CHIME BANK + VCC - 75$', callback_data='CHIME BANK + VCC - 75$')
own_reg_bank_acc = InlineKeyboardMarkup(row_width=1).add(a_own_reg_bank_acc, b_own_reg_bank_acc, c_own_reg_bank_acc, d_own_reg_bank_acc, e_own_reg_bank_acc, back_1)

a_look_up = InlineKeyboardButton('🇺🇸 US DOB + SNN - 20$', callback_data='US DOB + SNN - 20$')
b_look_up = InlineKeyboardButton('🇺🇸 US DRIVER LICENSE - 40$', callback_data='US DRIVER LICENSE - 40$')
c_look_up = InlineKeyboardButton('🇺🇸 US MMN - 35$', callback_data='US MMN - 35$')
d_look_up = InlineKeyboardButton('🇺🇸 US BACKGROUND REPORT - 25$', callback_data='US BACKGROUND REPORT - 25$')
e_look_up = InlineKeyboardButton('🇺🇸 US CREDIT REPORT - 35$', callback_data='US CREDIT REPORT - 35$')
f_look_up = InlineKeyboardButton('🇺🇸 US VEHICLE LOOK UP - 40$', callback_data='US VEHICLE LOOK UP - 40$')
g_look_up = InlineKeyboardButton('🇬🇧 UK SNN + DOB - 20$', callback_data='UK SNN + DOB  - 20$')
h_look_up = InlineKeyboardButton('SPECIAL ORDER', callback_data='SPECIAL ORDER')
look_up = InlineKeyboardMarkup(row_width=1).add(a_look_up, b_look_up, c_look_up, d_look_up, e_look_up, f_look_up, g_look_up, h_look_up, back_1)


import pyarrow  # type: ignore
from pyarrow.cffi import ffi
import rpy2.rinterface as rinterface
import rpy2.robjects as robjects
import rpy2.robjects.conversion as conversion
import rpy2.robjects.packages as packages
import typing
import warnings

rarrow = packages.importr('arrow')

if rinterface.BoolSexpVector(
        rinterface.evalr("""
        utils::packageVersion("arrow") < base::package_version("12.0")
        """)
)[0]:
    warnings.warn('Segfaults may occur with the R package arrow < 12.0')

# make sure a version is installed with the C API
_rarrow_has_c_api = rinterface.BoolSexpVector(
    rinterface.evalr("""
    utils::packageVersion("arrow") >= base::package_version("5.0.0")
    """)
)[0]

if not _rarrow_has_c_api:
    raise ValueError("rpy2_arrow requires R 'arrow' package version >= 5.0.0")

# In arrow >= 7.0.0, pointers can be passed as externalptr,
# bit64::integer64(), or string, all of which prevent possible
# problems with the previous versions which required a double().
_use_r_ptr_string = rinterface.BoolSexpVector(
    rinterface.evalr("""
    utils::packageVersion("arrow") >= base::package_version("6.0.1.9000")
    """)
)[0]

def _rarrow_ptr(ptr) -> typing.Union[str, float]:
    ptr_value = int(ffi.cast('uintptr_t', ptr))
    return str(ptr_value) if _use_r_ptr_string else float(ptr_value)

def _c_ptr_to_int(ptr) -> int:
    return int(ffi.cast('uintptr_t', ptr))

def pyarrow_to_r_array(
        obj: 'pyarrow.lib.Array'
):
    """Create an R `arrow::Arrow` object from a pyarrow Array.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2. By default it will be an `rpy2.robjects.Environment`.
    """
    array_ptr = ffi.new('struct ArrowArray*')
    schema_ptr = ffi.new('struct ArrowSchema*')

    obj._export_to_c(_c_ptr_to_int(array_ptr), _c_ptr_to_int(schema_ptr))
    return rarrow.Array['import_from_c'](_rarrow_ptr(array_ptr), _rarrow_ptr(schema_ptr))

def rarrow_to_py_array(
        obj: robjects.Environment
) -> pyarrow.Array:
    """Create a pyarrow array from an R `arrow::Array` object.

    This is sharing the C/C++ object between the two languages.
    """
    array_ptr = ffi.new('struct ArrowArray*')
    schema_ptr = ffi.new('struct ArrowSchema*')

    obj['export_to_c'](_rarrow_ptr(array_ptr), _rarrow_ptr(schema_ptr))
    return pyarrow.lib.Array._import_from_c(_c_ptr_to_int(array_ptr), _c_ptr_to_int(schema_ptr))

def pyarrow_to_r_recordbatch(
        obj: 'pyarrow.lib.RecordBatch'
):
    """Create an R `arrow::RecordBatch` object from a pyarrow RecordBatch.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2. By default it will be an `rpy2.robjects.Environment`.
    """
    array_ptr = ffi.new('struct ArrowArray*')
    schema_ptr = ffi.new('struct ArrowSchema*')

    obj._export_to_c(_c_ptr_to_int(array_ptr), _c_ptr_to_int(schema_ptr))
    return rarrow.RecordBatch['import_from_c'](_rarrow_ptr(array_ptr), _rarrow_ptr(schema_ptr))

def rarrow_to_py_recordbatch(
        obj: robjects.Environment
) -> pyarrow.lib.RecordBatch:
    """Create a pyarrow record batch from an R `arrow::Array` object.

    This is sharing the C/C++ object between the two languages.
    """
    array_ptr = ffi.new('struct ArrowArray*')
    schema_ptr = ffi.new('struct ArrowSchema*')

    obj['export_to_c'](_rarrow_ptr(array_ptr), _rarrow_ptr(schema_ptr))
    return pyarrow.lib.RecordBatch._import_from_c(_c_ptr_to_int(array_ptr), _c_ptr_to_int(schema_ptr))

def pyarrow_to_r_recordbatchreader(
        obj: 'pyarrow.lib.RecordBatchReader'
):
    """Create an R `arrow::RecordBatchReader` from a pyarrow RecordBatchReader.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2. By default it will be an `rpy2.robjects.Environment`.
    """

    stream_ptr = ffi.new('struct ArrowArrayStream*')
    obj._export_to_c(_c_ptr_to_int(stream_ptr))
    return rarrow.RecordBatchReader['import_from_c'](_rarrow_ptr(stream_ptr))

def rarrow_to_py_recordbatchreader(
        obj: robjects.Environment
) -> pyarrow.lib.RecordBatchReader:
    """Create a pyarrow RecordBatchReader from an R `arrow::RecordBatchReader` object.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2. By default it will be an `rpy2.robjects.Environment`.
    """

    stream_ptr = ffi.new('struct ArrowArrayStream*')
    obj['export_to_c'](_rarrow_ptr(stream_ptr))
    return pyarrow.lib.RecordBatchReader._import_from_c(_c_ptr_to_int(stream_ptr))

def pyarrow_to_r_chunkedarray(
        obj: 'pyarrow.lib.ChunkedArray'
):
    """Create an R `arrow::ChunkedArray` object from a pyarrow ChunkedArray.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2. By default it will be an `rpy2.robjects.Environment`.
    """
    chunks = tuple(pyarrow_to_r_array(x) for x in obj.chunks)
    res = rarrow.ChunkedArray['create'](*chunks)
    return res

def rarrow_to_py_chunkedarray(
        obj: robjects.Environment
) -> pyarrow.lib.ChunkedArray:
    """Create a pyarrow chunked array from an R `arrow::ChunkedArray` object.

    This is sharing the C/C++ object between the two languages.
    """
    chunks = tuple(rarrow_to_py_array(x) for x in obj['chunks'])
    return pyarrow.chunked_array(chunks)

def pyarrow_to_r_datatype(
        obj: 'pyarrow.lib.DataType'
):
    schema_ptr = ffi.new('struct ArrowSchema*')
    obj._export_to_c(_c_ptr_to_int(schema_ptr))
    return rarrow.DataType['import_from_c'](_rarrow_ptr(schema_ptr))

def rarrow_to_py_datatype(
        obj: robjects.Environment
) -> pyarrow.lib.DataType:
    """Create a pyarrow.lib.DataType from an R `arrow::DataType` object.

    This is sharing the C/C++ object between the two languages.
    """
    schema_ptr = ffi.new('struct ArrowSchema*')
    obj['export_to_c'](_rarrow_ptr(schema_ptr))
    return pyarrow.lib.DataType._import_from_c(_c_ptr_to_int(schema_ptr))

def pyarrow_to_r_field(
        obj: 'pyarrow.lib.Field'
):
    schema_ptr = ffi.new('struct ArrowSchema*')
    obj._export_to_c(_c_ptr_to_int(schema_ptr))
    return rarrow.Field['import_from_c'](_rarrow_ptr(schema_ptr))

def rarrow_to_py_field(
        obj: robjects.Environment
) -> pyarrow.lib.Field:
    """Create a pyarrow.lib.Field from an R `arrow::DataType` object.

    This is sharing the C/C++ object between the two languages.
    """

    schema_ptr = ffi.new('struct ArrowSchema*')
    obj['export_to_c'](_rarrow_ptr(schema_ptr))
    return pyarrow.lib.Field._import_from_c(_c_ptr_to_int(schema_ptr))

_as_arrow_table_from_stream_ptr = rinterface.SexpClosure(
    rinterface.evalr(
        """
        function(recordbatchreader) {
        arrow::as_arrow_table(recordbatchreader)
        }
        """
    )
)

def _pyarrow_table_to_r_table_ri(tbl: pyarrow.lib.Table) -> rinterface.SexpEnvironment:
    """Create an R `arrow` Table to mirror an Arrow Table in `pyarrow`."""
    recordbatchreader = pyarrow_to_r_recordbatchreader(tbl.to_reader())
    return _as_arrow_table_from_stream_ptr(recordbatchreader)

def pyarrow_table_to_r_table(obj: pyarrow.lib.Table):
    """Create an R `arrow::Table` object from a pyarrow Table.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2. By default it will be an `rpy2.robjects.Environment`.

    :param:obj: A :class:`pyarrow.lib.Table`.
    :return: The result of conversion rules for R `arrow` `Table` objects.
    By default a :class:`rpy2.robjects.Environment`.
    """
    # `res` is a low-level (rinterface-level) rpy2 object. This is an
    # rpy2 robject-level function. Use the conversion.
    res = _pyarrow_table_to_r_table_ri(obj)
    converter = (
        conversion.get_conversion()  # rpy2 >=3.5.2
        if hasattr(conversion, 'get_conversion') else
        conversion.converter  # rpy2 <3.5.2
    )
    return converter.py2rpy(res)

def rarrow_to_py_table(
        obj: robjects.Environment,
        rpy2py: typing.Optional[
            conversion.Converter] = None
) -> pyarrow.Table:
    """Create a pyarrow Table from an R `arrow::Table` object.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2.
    """

    if rpy2py is None:
        rpy2py = converter
    # TODO: rpy2 conversion forces something a little kludgy here.
    columns = [
        (rpy2py._rpy2py_nc_map[rinterface.SexpEnvironment][x.rclass[0]](x))
        for x in obj['columns']
    ]
    schema = rarrow_to_py_schema(obj['schema'])
    py_table = pyarrow.Table.from_arrays(columns,
                                         schema=schema)
    return py_table

def pyarrow_to_r_schema(
        obj: 'pyarrow.lib.Schema'
):
    """Create an R `arrow::Schema` object from a pyarrow Schema.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2. By default it will be an `rpy2.robjects.Environment`.
    """

    schema_ptr = ffi.new('struct ArrowSchema*')
    obj._export_to_c(_c_ptr_to_int(schema_ptr))
    return rarrow.Schema['import_from_c'](_rarrow_ptr(schema_ptr))

def rarrow_to_py_schema(
        obj: robjects.Environment
) -> pyarrow.Schema:
    """Create a pyarrow Schema from an R `arrow::Schema` object.

    This is sharing the C/C++ object between the two languages.
    The returned object depends on the active conversion rule in
    rpy2. By default it will be an `rpy2.robjects.Environment`.
    """

    schema_ptr = ffi.new('struct ArrowSchema*')
    obj['export_to_c'](_rarrow_ptr(schema_ptr))
    return pyarrow.lib.Schema._import_from_c(_c_ptr_to_int(schema_ptr))

converter: conversion.Converter = conversion.Converter(
    'default arrow conversion',
    template=robjects.default_converter
)

# Pyarrow to R arrow.
converter.py2rpy.register(pyarrow.lib.Array, pyarrow_to_r_array)
converter.py2rpy.register(pyarrow.lib.Field, pyarrow_to_r_field)
converter.py2rpy.register(pyarrow.lib.ChunkedArray,
                          pyarrow_to_r_chunkedarray)
converter.py2rpy.register(pyarrow.lib.RecordBatch,
                          pyarrow_to_r_recordbatch)
converter.py2rpy.register(pyarrow.lib.RecordBatchReader,
                          pyarrow_to_r_recordbatchreader)
converter.py2rpy.register(pyarrow.lib.Schema, pyarrow_to_r_schema)
converter.py2rpy.register(pyarrow.lib.Table, pyarrow_table_to_r_table)
converter.py2rpy.register(pyarrow.lib.DataType, pyarrow_to_r_datatype)

# R arrow to pyarrow.
converter._rpy2py_nc_map.update(
    {
        rinterface.SexpEnvironment:
        conversion.NameClassMap(robjects.Environment)
    }
)

# TODO: use complete class name hierarchy to be safer?
converter._rpy2py_nc_map[rinterface.SexpEnvironment].update(
    {
        'Array': rarrow_to_py_array,
        'ChunkedArray': rarrow_to_py_chunkedarray,
        'Field': rarrow_to_py_field,
        'RecordBatch': rarrow_to_py_recordbatch,
        'RecordBatchReader': rarrow_to_py_recordbatchreader,
        'Schema': rarrow_to_py_schema,
        'Table': rarrow_to_py_table,
        'Type': rarrow_to_py_datatype
    }
)


import asyncio
from abc import abstractmethod
import datetime
from enum import Enum
import json
import os
import threading
import time
from typing import Any, Dict, Optional
import uuid

import anyio
from loguru import logger

from .photon import Photon, HTTPException
from leptonai.api import workspace as ws_api
from leptonai.queue import Queue, Empty
from leptonai.kv import KV
from leptonai.util import asyncfy_with_semaphore

class Status(str, Enum):
    CREATED = "CREATED"
    RUNNING = "RUNNING"
    SUCCESS = "SUCCESS"
    FAILED = "FAILED"

class Worker(Photon):
    # The queue name and the KV name that the worker will use. The queue and the KV
    # will be created if they don't exist. Note that you are responsible for making
    # sure the uniqueness of queue and kv names. If they are left as None, the deployment
    # name will serve as the queue name and the kv name.
    queue_name: Optional[str] = None
    kv_name: Optional[str] = None

    # Whether to save the result of the task to the KV. If set to False, the result
    # of on_task will be simply ignored, and only the task state is saved to the KV.
    # In default, the result is saved to the KV.
    save_result: bool = True

    # The time to sleep when the queue is empty.
    queue_empty_sleep_time: int = 5

    # The maximum number of concurrent tasks that the worker can
    # process. Default to 1 to avoid needing to deal with locks.
    worker_max_concurrency: int = 1

    # The tag used to identify the task id in the payload. In most cases, you should
    # not need to change this.
    LEPTON_TASK_ID_TAG = "_lepton_task_id"

    _loop_started = False

    def init(self):
        """
        Worker's own init function that starts the loop.

        If you build a derived Photon based on Worker, you can explicitly call
        super().init() to start the loop in the init function. Note that when the
        loop starts, the worker will start to process tasks, so any worker specific
        init (like loading models) should be done before calling super().init().

        If your class does not implement the init function, the Worker's init function
        will be called automatically.
        """
        super().init()
        # Logs in to the workspace if not logged in yet.
        ws_api.login()
        # Starts the loop.
        if not self._loop_started:
            self._loop_started = True
            # creates the queue.
            if self.queue_name is None:
                if os.environ.get("LEPTON_DEPLOYMENT_NAME") is None:
                    raise RuntimeError(
                        "queue_name is not set and LEPTON_DEPLOYMENT_NAME is not set"
                    )
                else:
                    self.queue_name = os.environ["LEPTON_DEPLOYMENT_NAME"]
            self._queue = Queue(
                self.queue_name, create_if_not_exists=True, wait_for_creation=True
            )
            # creates the kv.
            if self.kv_name is None:
                if os.environ.get("LEPTON_DEPLOYMENT_NAME") is None:
                    raise RuntimeError(
                        "kv_name is not set and LEPTON_DEPLOYMENT_NAME is not set"
                    )
                else:
                    self.kv_name = os.environ["LEPTON_DEPLOYMENT_NAME"]
            self._kv = KV(
                self.kv_name, create_if_not_exists=True, wait_for_creation=True
            )

            self._worker_semaphore = anyio.Semaphore(self.worker_max_concurrency)
            self._on_task_handler = asyncfy_with_semaphore(
                self.on_task, self._worker_semaphore
            )
            self._lepton_worker_thread_should_exit = False
            threading.Thread(target=self._worker_loop, daemon=True).start()

    def _handle_exit(self, *args, **kwargs) -> None:
        self._lepton_worker_thread_should_exit = True
        super()._handle_exit(*args, **kwargs)

    def _worker_loop(self):
        """Keep polling the queue for new tasks, and dispatch them to `on_task` (through `_on_task`)"""
        loop = asyncio.new_event_loop()

        while True:
            if self._lepton_worker_thread_should_exit:
                logger.info("Received worker should exit signal, exiting")
                break
            try:
                message = self._queue.receive()
                payload = json.loads(message)
                task_id = payload.pop(self.LEPTON_TASK_ID_TAG)
                loop.run_until_complete(self._on_task(task_id, **payload))
            except Empty:
                logger.info(
                    "Queue is empty, sleeping for"
                    f" {self.queue_empty_sleep_time} seconds"
                )
                time.sleep(self.queue_empty_sleep_time)
            except Exception as e:
                logger.error(f"Error in worker loop: {e}")

    async def _on_task(self, task_id: str, **payload):
        """Do some accounting and error handling work, then dispatch the task to `on_task`"""
        logger.info(f"Received task {task_id}")

        task_json = json.loads(self._kv.get(task_id))
        task_json["status"] = Status.RUNNING
        task_json["started_at"] = datetime.datetime.utcnow().isoformat()
        self._kv.put(task_id, json.dumps(task_json))

        try:
            res = await self._on_task_handler(task_id=task_id, **payload)
        except Exception as e:
            logger.exception(f"Task {task_id} failed: {e}")
            task_json["status"] = Status.FAILED
        else:
            logger.info(f"Task {task_id} succeeded")
            task_json["status"] = Status.SUCCESS
            if self.save_result:
                task_json["result"] = res
        finally:
            task_json["finished_at"] = datetime.datetime.utcnow().isoformat()
            self._kv.put(task_id, json.dumps(task_json))

        logger.info(f"Task {task_id} finished")

    @abstractmethod
    def on_task(self, task_id: str, **payload):
        """The actual task handler, should be implemented by concrete Worker subclass"""
        raise NotImplementedError("Concrete Worker subclass should implement on_task")

    @Photon.handler(path="task", method="POST", use_raw_args=True)
    def task_post(self, payload: Dict[str, Any]):
        task_id = uuid.uuid4().hex

        logger.info(f"request: {payload}")

        payload[self.LEPTON_TASK_ID_TAG] = task_id
        self._queue.send(json.dumps(payload))

        task_json = {
            "status": Status.CREATED,
            "created_at": datetime.datetime.utcnow().isoformat(),
        }
        self._kv.put(task_id, json.dumps(task_json))

        return task_id

    @Photon.handler(path="task", method="GET")
    def task_get(self, task_id: str):
        try:
            task_str = self._kv.get(task_id)
        except KeyError:
            raise HTTPException(status_code=404, detail=f"Task ({task_id}) not found")
        else:
            task_json = json.loads(task_str)
            return task_json