"""
This module most notably implements the Element class, which serves as the main container for
physical data going into the ebisim computations.

Besides that, there are some small helper functions to translate certain element properties,
which may offer convenience to the user.
"""
from __future__ import annotations

import logging
from typing import NamedTuple, Tuple, Union, Optional
import numpy as np

from .utils import load_dr_data, patch_namedtuple_docstrings
from .xs import precompute_rr_quantities, lookup_lotz_factors
from .physconst import MINIMAL_KBT, MINIMAL_N_1D, Q_E, K_B, PI

logger = logging.getLogger(__name__)

logger.debug("Loading element data and shell configurations.")
from .resources import (  # noqa: E402
    ELEMENT_Z as _ELEM_Z,
    ELEMENT_ES as _ELEM_ES,
    ELEMENT_NAME as _ELEM_NAME,
    ELEMENT_A as _ELEM_A,
    ELEMENT_IP as _ELEM_IP,
    SHELL_ORDER as _SHELLORDER,
    SHELL_N as _SHELL_N,
    SHELL_CFG as _SHELL_CFG,
    SHELL_EBIND as _SHELL_EBIND
)

logger.debug("Loading DR data.")
_DR_DATA = load_dr_data()

# ----- Helper functions for translating chemical symbols
def element_z(element: str) -> int:
    """
    Returns the proton number of the given element.

    Parameters
    ----------
    element :
        The full name or the abbreviated symbol of the element.

    Returns
    -------
        Proton number
    """
    if len(element) < 3:
        idx = _ELEM_ES.index(element)
    else:
        idx = _ELEM_NAME.index(element)
    return _ELEM_Z[idx]

def element_symbol(element: Union[str, int]) -> str:
    """
    Returns the abbreviated symbol of the given element.

    Parameters
    ----------
    element :
        The full name or the proton number of the element.

    Returns
    -------
        Element symbol
    """
    if isinstance(element, int):
        idx = _ELEM_Z.index(element)
    else:
        idx = _ELEM_NAME.index(element)
    return _ELEM_ES[idx]

def element_name(element: Union[str, int]) -> str:
    """
    Returns the name of the given element.

    Parameters
    ----------
    element :
        The abbreviated symbol or the proton number of the element.

    Returns
    -------
        Element name
    """
    if isinstance(element, int):
        idx = _ELEM_Z.index(element)
    else:
        idx = _ELEM_ES.index(element)
    return _ELEM_NAME[idx]

def element_identify(element_id: Union[str, int]) -> Tuple[int, str, str]:
    """
    Returns the proton number, name, and element symbol relating to the supplied element_id.

    Parameters
    ----------
    element_id :
        The proton number, name or symbol of a chemical element.

    Returns
    -------
        (proton number, name, symbol)

    Raises
    ------
    ValueError
        If the element_id could not be identified or found in the database.
    """
    try:
        if isinstance(element_id, int):
            z = element_id
        else:
            z = element_z(element_id)
        symbol = element_symbol(z)
        name = element_name(z)
    except ValueError as e:
        raise ValueError(f"Unable to interpret element_id = {element_id}, "
                         + "ebisim only supports elements up to Z = 105.") from e
    return z, name, symbol

class Element(NamedTuple):
    """
    The Element class is one of the main data structures in ebisim.
    Virtually any function relies on information provided in this data structure.
    The leading fields of the underlying tuple contain physcial properties,
    whereas the `n` `kT` and `cx` fields are optional and only required for
    advanced simulations.

    Instead of populating the fields manually, the user should choose one of the
    factory functions that meets their needs best.

    For basic simulations and cross sections calculations only the
    physical / chemical properties of and element are needed.
    In these cases use the generic `get()` method to create instances of this class.

    Advanced simulations require additional information about the initial
    particle densities, temperature and participation in charge exchange.
    The user will likely want to chose between the `get_ions()` and `get_gas()` methods,
    which offer a convenient interface for generating this data based on simple parameters.
    If these functions are not flexible enough, the `get()` method can be used to populate
    the required fields manually.

    This class is derived from collections.namedtuple which facilitates use with numba-compiled
    functions.

    See Also
    --------
    ebisim.elements.Element.get
    ebisim.elements.Element.get_ions
    ebisim.elements.Element.get_gas
    """

    z: int
    symbol: str
    name: str
    a: float
    ip: float
    e_cfg: np.ndarray
    e_bind: np.ndarray
    rr_z_eff: np.ndarray
    rr_n_0_eff: np.ndarray
    dr_cs: np.ndarray
    dr_e_res: np.ndarray
    dr_strength: np.ndarray
    ei_lotz_a: np.ndarray
    ei_lotz_b: np.ndarray
    ei_lotz_c: np.ndarray
    n: Optional[np.ndarray] = None
    kT: Optional[np.ndarray] = None
    cx: bool = True

    @classmethod
    def get(cls,
            element_id: Union[str, int],
            a: Optional[float] = None,
            n: Optional[np.ndarray] = None,
            kT: Optional[np.ndarray] = None,
            cx: bool = True) -> Element:
        """
        Factory method to create instances of the Element class.

        Parameters
        ----------
        element_id :
            The full name, abbreviated symbol, or proton number of the element of interest.
        a :
            If provided sets the mass number of the Element object otherwise a reasonable
            value is chosen automatically.
        n :
            <1/m>
            Only needed for advanced simulations!
            Array holding the initial ion line densities of each charge state.
            If provided, has to be an array of length Z+1, where Z is the nuclear charge.
        kT :
            <eV>
            Only needed for advanced simulations!
            Array holding the initial ion line densities of each charge state.
            If provided, has to be an array of length Z+1, where Z is the nuclear charge.
        cx :
            Only needed for advanced simulations!
            Boolean flag determining whether the neutral particles of this element contribute
            to charge exchange with ions.

        Returns
        -------
            An instance of Element with the user-supplied and generated data.

        Raises
        ------
        ValueError
            If the Element could not be identified or a meaningless mass number is provided.

        ValueError
            If the passed arrays for `n` or `kT` have the wrong shape.
        """
        # Basic element info
        z, name, symbol = element_identify(element_id)

        # Mass number and ionisation potential
        idx = _ELEM_Z.index(z)
        ip = float(_ELEM_IP[idx])
        if a is None:
            a = _ELEM_A[idx]
        if a <= 0:
            raise ValueError("Mass number 'a' cannot be smaller than or equal to 0.")

        # Electron configuration and shell binding energies
        e_cfg = _SHELL_CFG[z].copy()
        e_bind = _SHELL_EBIND[z].copy()

        # Precomputations for radiative recombination
        rr_z_eff, rr_n_0_eff = precompute_rr_quantities(e_cfg, _SHELL_N)

        # Data for computations of dielectronic recombination cross sections
        dr_cs = _DR_DATA[z]["dr_cs"].copy()
        dr_e_res = _DR_DATA[z]["dr_e_res"].copy()
        dr_strength = _DR_DATA[z]["dr_strength"].copy()

        # Precompute the factors for the Lotz formula for EI cross section
        ei_lotz_a, ei_lotz_b, ei_lotz_c = lookup_lotz_factors(e_cfg, _SHELLORDER)

        # Validate n and kT
        if n is not None:
            if len(n) != z+1:
                raise ValueError(f"n has the wrong shape {n.shape} for an element with z = {z}.")
            if np.any(n < MINIMAL_N_1D):
                n = np.maximum(n, MINIMAL_N_1D)
                logger.warning(
                    "One or more entries of 'n' were smaller than the minimal internal value.\n"
                    + f"Entries smaller than {MINIMAL_N_1D} were raised to that value."
                )

        if kT is not None:
            if len(kT) != z+1:
                raise ValueError(f"kT has the wrong shape {kT.shape} for an element with z = {z}.")
            if np.any(kT < MINIMAL_KBT):
                kT = np.maximum(kT, MINIMAL_KBT)
                logger.warning(
                    "One or more entries of 'kT' were smaller than the minimal internal value.\n"
                    + f"Entries smaller than {MINIMAL_KBT} were raised to that value."
                )

        # Make sure that all arrays are readonly - better safe than sorry
        arrays = [
            e_cfg,
            e_bind,
            rr_z_eff,
            rr_n_0_eff,
            dr_cs,
            dr_e_res,
            dr_strength,
            ei_lotz_a,
            ei_lotz_b,
            ei_lotz_c,
            n,
            kT,
        ]
        for arr in arrays:
            if arr is not None:
                arr.setflags(write=False)

        return cls(
            z=z,
            symbol=symbol,
            name=name,
            a=a,
            ip=ip,
            e_cfg=e_cfg,
            e_bind=e_bind,
            rr_z_eff=rr_z_eff,
            rr_n_0_eff=rr_n_0_eff,
            dr_cs=dr_cs,
            dr_e_res=dr_e_res,
            dr_strength=dr_strength,
            ei_lotz_a=ei_lotz_a,
            ei_lotz_b=ei_lotz_b,
            ei_lotz_c=ei_lotz_c,
            n=n,
            kT=kT,
            cx=cx,
        )

    @classmethod
    def get_gas(cls,
                element_id: Union[str, int],
                p: float,
                r_dt: float,
                T: float = 300.0,
                cx: bool = True,
                a: Optional[float] = None) -> Element:
        """
        Factory method for defining a neutral gas injection target.
        A gas target is a target with constant density in charge state 0.

        Parameters
        ----------
        element_id :
            The full name, abbreviated symbol, or proton number of the element of interest.
        p :
            <mbar>
            Gas pressure.
        r_dt :
            <m>
            Drift tube radius, required to compute linear density from volumetric density.
        T :
            <K>
            Gas temperature, by default 300 K (approx. room temperature)
        cx :
            Boolean flag determining whether the neutral particles of this element contribute
            to charge exchange with ions.
        a :
            If provided sets the mass number of the Element object otherwise a reasonable
            value is chosen automatically.

        Returns
        -------
            Element instance with automatically populated `n` and `kT` fields.

        Raises
        ------
        ValueError
            If the density resulting from the pressure and temperature is smaller than the
            internal minimal value.
        """
        z, _name, _symbol = element_identify(element_id)
        _n = np.full(z + 1, MINIMAL_N_1D, dtype=np.float64)
        _kT = np.full(z + 1, MINIMAL_KBT, dtype=np.float64)

        _n[0] = (p * 100) / (K_B * T) * PI * r_dt**2  # Convert from mbar to Pa and compute density
        if _n[0] < MINIMAL_N_1D:
            raise ValueError("The resulting density is smaller than the internal minimal value.")
        _kT[0] = K_B * T / Q_E

        return cls.get(element_id, a=a, n=_n, kT=_kT, cx=cx)

    @classmethod
    def get_ions(cls,
                 element_id: Union[str, int],
                 nl: float,
                 kT: float = 10.0,
                 q: int = 1,
                 cx: bool = True,
                 a: Optional[float] = None) -> Element:
        """
        Factory method for defining a pulsed ion injection target.
        An ion target has a given density in the charge state of choice q.

        Parameters
        ----------
        element_id :
            The full name, abbreviated symbol, or proton number of the element of interest.
        nl :
            <1/m>
            Linear density of the initial charge state (ions per unit length).
        kT :
            <eV>
            Temperature / kinetic energy of the injected ions.
        q :
            Initial charge state.
        cx :
            Boolean flag determining whether the neutral particles of this element contribute
            to charge exchange with ions.
        a :
            If provided sets the mass number of the Element object otherwise a reasonable
            value is chosen automatically.

        Returns
        -------
            Element instance with automatically populated `n` and `kT` fields.

        Raises
        ------
        ValueError
            If the requested density is smaller than the internal minimal value.
        """
        if nl < MINIMAL_N_1D:
            raise ValueError("The requested density is smaller than the internal minimal value.")

        z, _name, _symbol = element_identify(element_id)
        _n = np.full(z + 1, MINIMAL_N_1D, dtype=np.float64)
        _kT = np.full(z + 1, MINIMAL_KBT, dtype=np.float64)

        _n[q] = nl
        _kT[q] = kT

        return cls.get(element_id, a=a, n=_n, kT=_kT, cx=cx)

    @classmethod
    def as_element(cls, element: Union[Element, str, int]) -> Element:
        """
        If `element` is already an instance of `Element` it is returned.
        If `element` is a string or int identyfying an element an appropriate `Element` instance is
        returned.

        Parameters
        ----------
        element :
            An instance of the Element class, or an identifier for the element, i.e. either its
            name, symbol or proton number.

        Returns
        -------
            An instance of Element reflecting the input value.
        """
        if isinstance(element, cls):
            return element
        elif isinstance(element, (str, int)):
            return cls.get(element)
        else:
            raise TypeError("Could not convert {element} to ebisim.Element")

    def latex_isotope(self) -> str:
        """
        Returns the isotope as a LaTeX formatted string.

        Returns
        -------
        str
            LaTeX formatted string describing the isotope.
        """
        return fr"$\mathsf{{^{{{self.a}}}_{{{self.z}}}{self.symbol}}}$"

    def __str__(self) -> str:
        return f"Element: {self.name} ({self.symbol}, Z = {self.z}, A = {self.a})"

_ELEMENT_DOC = dict(
    z="Atomic number",
    symbol="Element symbol e.g. H, He, Li",
    name="Element name",
    a="Mass number / approx. mass in proton masses",
    ip="Ionisation potential",
    e_cfg=f"""\
Numpy array of electron configuration in different charge states.
The index of each row corresponds to the charge state.
The columns are the subshells sorted as in {_SHELLORDER}.""",
    e_bind=f"""\
Numpy array of binding energies associated with electron subshells.
The index of each row corresponds to the charge state.
The columns are the subshells sorted as in {_SHELLORDER}.""",
    rr_z_eff="Numpy array of effective nuclear charges for RR cross sections.",
    rr_n_0_eff="Numpy array of effective valence shell numbers for RR cross sections.",
    dr_cs="Numpy array of charge states for DR cross sections.",
    dr_e_res="Numpy array of resonance energies for DR cross sections.",
    dr_strength="Numpy array of transition strengths for DR cross sections.",
    ei_lotz_a="Numpy array of precomputed Lotz factor 'a' for each entry of 'e_cfg'.",
    ei_lotz_b="Numpy array of precomputed Lotz factor 'b' for each entry of 'e_cfg'.",
    ei_lotz_c="Numpy array of precomputed Lotz factor 'c' for each entry of 'e_cfg'.",
    n="<1/m> Array holding the initial linear density of each charge state.",
    kT="<eV> Array holding the initial temperature of each charge state.",
    cx="""\
Boolean flag determining whether neutral particles of this target are
considered as charge exchange partners.""",
)
patch_namedtuple_docstrings(Element, _ELEMENT_DOC)

def get_element(element_id: Union[str, int], a: Optional[float] = None) -> Element:
    """
    [LEGACY]
    Factory function to create instances of the Element class.

    Parameters
    ----------
    element_id : str or int
        The full name, abbreviated symbol, or proton number of the element of interest.
    a : int or None, optional
        If provided sets the (isotopic) mass number of the Element object otherwise a reasonable
        value is chosen automatically, by default None.

    Returns
    -------
    ebisim.elements.Element
        An instance of Element with the physical data corresponding to the supplied element_id, and
        optionally mass number.

    Raises
    ------
    ValueError
        If the Element could not be identified or a meaningless mass number is provided.
    """
    return Element.get(element_id, a)


import pandas as pnd
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as st
def dim_2():
    frutas = pnd.read_csv("/Users/rauln/Documents/Desarrolo orientado a objetos/Albaricoques-cerezas-clustering/codigo-cap09/datas/frutas.csv", names=['DIAMETRO','PESO'], header=None)

    # Extraer x e y
    x = frutas.DIAMETRO
    y = frutas.PESO
    # Define los límites
    deltaX = (max(x) - min(x))/10
    deltaY = (max(y) - min(y))/10
    xmin = min(x) - deltaX
    xmax = max(x) + deltaX
    ymin = min(y) - deltaY
    ymax = max(y) + deltaY
    print(xmin, xmax, ymin, ymax)
    # Crear meshgrid
    xx, yy = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]

    posiciones = np.vstack([xx.ravel(), yy.ravel()])
    values = np.vstack([x, y])
    kernel = st.gaussian_kde(values)
    f = np.reshape(kernel(posiciones).T, xx.shape)

    fig = plt.figure(figsize=(8,8))
    ax = fig.gca()
    ax.set_xlim(xmin, xmax)
    ax.set_ylim(ymin, ymax)
    cfset = ax.contourf(xx, yy, f, cmap='coolwarm')
    ax.imshow(np.rot90(f), cmap='coolwarm', extent=[xmin, xmax, ymin, ymax])
    cset = ax.contour(xx, yy, f, colors='k')
    ax.clabel(cset, inline=1, fontsize=10)
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    plt.show()


import csv
import cv2
import numpy as np
import os
import sklearn

#read csv lines
lines = []
with open('data/driving_log.csv') as csvfile:
    reader = csv.reader(csvfile)
    for line in reader:
        lines.append(line)

from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
train_lines, validation_lines = train_test_split(lines, test_size=0.2)

def generator(lines, batch_size=32):
    num_lines = len(lines)
    while 1: #loop forever so the generator never terminates
        shuffle(lines)
        for offset in range(0, num_lines, batch_size):
            batch_samples = lines[offset:offset+batch_size]

            images = []
            measurements = []
            correction = 0.38 #correction factor for side cameras

            for batch_sample in batch_samples:
                for i in range(3):
                    source_path = batch_sample[i]
                    filename = source_path.split('/')[-1]
                    current_path = 'data/IMG/' + filename
                    image = cv2.imread(current_path)
                    images.append(image)
                    measurement = float(batch_sample[3])
                    if(i == 1):
                        measurement = measurement + correction #left camera
                    elif (i == 2):
                        measurement = measurement - correction #right camera
                    measurements.append(measurement)

            augmented_images = []
            augmented_measurements = []

            for image,measurement in zip(images, measurements):
                augmented_images.append(image)
                augmented_measurements.append(measurement)
                augmented_images.append(cv2.flip(image,1)) #flip images horizontally
                augmented_measurements.append(measurement*-1.0) #reverse steering measurement

            X_train = np.array(augmented_images)
            y_train = np.array(augmented_measurements)

            yield shuffle(X_train, y_train)

#compile and train model using the generator function
train_generator = generator(train_lines, batch_size=32)
validation_generator = generator(validation_lines, batch_size=32)

from keras.models import Sequential, Model
from keras.layers import Flatten, Dense, Lambda, Cropping2D, Dropout
from keras.layers.convolutional import Convolution2D
from keras.layers.pooling import MaxPooling2D

#NVIDIA Network
model = Sequential()
model.add(Lambda(lambda x: (x / 255.0) - 0.5, input_shape=(160,320,3))) #normalize image
model.add(Cropping2D(cropping=((70,25),(0,0)))) #crop images to remove "noise" data - top and bottom
model.add(Convolution2D(24,5,5,subsample=(2,2),activation="relu"))
model.add(Convolution2D(36,5,5,subsample=(2,2),activation="relu"))
model.add(Convolution2D(48,5,5,subsample=(2,2),activation="relu"))
model.add(Convolution2D(64,3,3,activation="relu"))
model.add(Convolution2D(64,3,3,activation="relu"))
model.add(Flatten())
model.add(Dense(100))
model.add(Dense(50))
model.add(Dense(10))
model.add(Dense(1))

model.compile(loss='mse', optimizer='adam')
model.fit_generator(train_generator, samples_per_epoch=len(train_lines)*3, validation_data=validation_generator, nb_val_samples=len(validation_lines), nb_epoch=5, verbose=1)

model.save('model.h5')


from abc import abstractmethod
import numpy as np
from keras import Model
from keras.callbacks import ModelCheckpoint
from pandas import DataFrame
from ml4iiot.algorithm.abstractalgorithm import AbstractAlgorithm
from ml4iiot.utility import str2bool, get_current_out_path
from keras.models import load_model

class AbstractAutoencoder(AbstractAlgorithm):

    def __init__(self, config: dict):
        super().__init__(config)

        self.verbose = str2bool(self.get_config('verbose', default=False))
        self.batch_size = self.get_config('batch_size', default=1)
        self.epochs = self.get_config('epochs', default=1)
        self.save_best_model = self.get_config('save_best_model', default=False)
        self.load_model = self.get_config('load_model', default=None)
        self.shuffle = self.get_config('shuffle', default=True)

        self.next_batch = []
        self.callbacks = []
        self.autoencoder = None

        if self.save_best_model:
            self.callbacks.append(ModelCheckpoint(get_current_out_path('best_model.hdf5'), save_best_only=True, monitor='loss'))

    def init(self) -> None:
        if self.load_model is None:
            self.autoencoder = self.create_autoencoder_model()

            self.compile_autoencoder()
        else:
            self.autoencoder = load_model(self.load_model)

        if self.verbose:
            self.autoencoder.summary()

    @abstractmethod
    def create_autoencoder_model(self) -> Model:
        pass

    def compile_autoencoder(self):
        self.autoencoder.compile(
             optimizer=self.get_config('optimizer', default='adam'),
             loss=self.get_config('loss', default='binary_crossentropy')
        )

    def data_frame_to_input_data(self, data_frame: DataFrame):
        return np.array([data_frame[self.get_config('input')]])

    def fit(self):
        next_batch = np.array(self.next_batch)

        return self.autoencoder.fit(
            next_batch,
            next_batch,
            batch_size=self.batch_size,
            epochs=self.epochs,
            verbose=self.verbose,
            callbacks=self.callbacks,
            shuffle=self.shuffle
        )

    def process(self, data_frame: DataFrame) -> None:
        data_frame['do_fit'] = float('nan')
        data_frame['do_predict'] = float('nan')
        data_frame['loss'] = float('nan')
        input_data = self.data_frame_to_input_data(data_frame)

        if self.do_predict(data_frame):
            data_frame['do_predict'].iloc[-1] = 1

            reconstruction = self.autoencoder.predict(input_data)
            reconstruction_difference = reconstruction - input_data
            reconstruction_error = sum(abs(reconstruction_difference[0]))

            data_frame['reconstruction'] = reconstruction[0]
            data_frame['absolute_reconstruction_error'] = float('nan')
            data_frame['relative_reconstruction_error'] = float('nan')

            data_frame['absolute_reconstruction_error'].iloc[-1] = reconstruction_error
            data_frame['relative_reconstruction_error'].iloc[-1] = reconstruction_error / len(data_frame.index)
        else:
            data_frame['do_predict'].iloc[-1] = 0

        if self.do_fit(data_frame):
            data_frame['do_fit'].iloc[-1] = 1
            self.next_batch.append(input_data[0])

            if len(self.next_batch) >= self.batch_size:
                history = self.fit()
                data_frame['loss'] = np.average(history.history['loss'])
                self.next_batch = []
        else:
            data_frame['do_fit'].iloc[-1] = 0


#importing flask, jsonify, render template and  request
from flask import Flask, jsonify, render_template, request
app = Flask(__name__)

@app.route('/')
def hello_world():
    return render_template("./index.html")

@app.route('/compute')
def compute():
    a = 4
    b = 3
    return str(a * b)

@app.route('/result')
def name_result():
    name = {
        "first": "Paul",
        "last": "Omikunle",
        "course": "Data Science"
    }
    {
        "first": "Lola",
        "last": "Omikunle",
        "course": "Machine learning"
    }
    return jsonify(name)

@app.route('/score', methods= ["POST"])
def score_val():
    DataDict = request.get_json()

    return jsonify(DataDict)

if __name__=='__main__':
    app.run(debug=True)


#!/usr/bin/python3
import random
import time
import sys
import curses
import argparse

__VERSION__ = "v0.1"

# matrix.py
# A cool matrix effect for your terminal!
# Fully customizable, of course.
# by illinoisjackson: https://github.com/illinoisjackson

if sys.version_info <= (3, 0):
    _PY2 = 1
else:
    _PY2 = 0

class ScreenSpace:
    def __init__(self, win):
        self.chars = []
        self.win = win
        self.size = self.win.getmaxyx()
        curses.start_color()
        curses.use_default_colors()
        self.win.nodelay(1)
        for i in range(0, curses.COLORS):
            curses.init_pair(i + 1, i, -1)

    def addchar(self, x, y, char, mods):
        self.chars.append((x, y, char, mods))

    def render(self, debug=None):
        self.win.erase()
        self.size = self.win.getmaxyx()
        for ch in self.chars:
            if ch[0] >= 0:
                if ch[0] < self.size[1] - 1:
                    if ch[1] >= 0:
                        if ch[1] < self.size[0]:
                            try:
                                self.win.addstr(int(ch[1]), int(ch[0]), chr(
                                    ch[2]), curses.color_pair(ch[3]))
                            except:
                                pass
        if debug != None:
            for dstr in range(len(debug)):
                self.win.addstr(dstr, 0, debug[dstr], curses.color_pair(0))
        self.win.refresh()
        self.chars = []

    def destroy(self):
        curses.nocbreak()
        self.win.keypad(0)
        curses.echo()
        curses.endwin()

class Fade:
    def __init__(self, starttick, x, y, headcolor, colors, unics, length, lifetime, speed):
        self.st = starttick
        self.colors = colors
        self.lcolors = len(self.colors)
        self.headcolor = headcolor
        self.unics = unics
        self.length = length
        self.lt = lifetime
        self.speed = speed
        self.pos = [x, y]
        self.time = 0
        self.dstd = 0

    def render(self, tick, screenspace):
        self.time += 1
        for i in range(self.length):
            if i == 0:
                self.pendingcolor = self.headcolor
            else:
                self.pendingcolor = self.colors[int(
                    (i / self.length) * self.lcolors)]
            self.pendingchar = random.choice(self.unics)
            screenspace.addchar(self.pos[0], round(self.pos[1]) - i, self.pendingchar,
                                self.pendingcolor)
        self.pos[1] += self.speed
        if self.time == round(self.lt):
            self.dstd = 1

    def destroy(self):
        self = None

def print_error(s):
    print("\x1b[31m%s" % s)

def parse_chars(chrl, argp):
    charlist = []
    try:
        chrl = chrl.split(",")
        for chrr in chrl:
            if "-" in chrr:
                dashs = chrr.split("-")
                if len(dashs) == 2:
                    charlist.extend(range(int(dashs[0]), int(dashs[1])))
            elif chrr != "":
                charlist.append(int(chrr))
            assert len(charlist) > 0
        return charlist
    except:
        argp.print_help()
        print_error("Could not parse characters list!")
        sys.exit(1)

def parse_colors(cll, argp):
    headcolor = None
    colorlist = []
    try:
        mtchs = re.finditer(r"(\d*\!?)", cll)
        for mt in mtchs:
            mtchg = mt.group()
            if "!" in mtchg:
                headcolor = int(mtchg.strip("!"))
            elif mtchg != "":
                colorlist.append(int(mtchg))
        return headcolor, colorlist
    except:
        argp.print_help()
        print_error("Could not parse colors list!")
        sys.exit(1)

def main(stdscr, params, chars):
    STARTTIME = time.time()
    PARAMS = params
    FADES = [None for fd in range(PARAMS.fades)]
    TICK = 0
    SCSP = ScreenSpace(stdscr)
    for fade in range(PARAMS.fades):
        FADES[fade] = Fade(
            TICK,
            random.randint(0, SCSP.size[1]),
            random.randint(0, SCSP.size[0]),
            PARAMS.headcolor,
            [int(j) for j in PARAMS.colors.rstrip(",").split(",")],
            chars,
            random.randint(PARAMS.minlength, PARAMS.maxlength),
            random.uniform(PARAMS.minlifetime, PARAMS.maxlifetime),
            random.uniform(PARAMS.minspeed, PARAMS.maxspeed))
    try:
        while True:
            time.sleep(PARAMS.delay)
            for fade in range(PARAMS.fades):
                if FADES[fade].dstd == 1:
                    FADES[fade] = Fade(
                        TICK,
                        random.randint(0, SCSP.size[1]),
                        random.randint(0, SCSP.size[0]),
                        PARAMS.headcolor,
                        [int(j) for j in PARAMS.colors.rstrip(",").split(",")],
                        chars,
                        random.randint(PARAMS.minlength, PARAMS.maxlength),
                        random.uniform(PARAMS.minlifetime, PARAMS.maxlifetime),
                        random.uniform(PARAMS.minspeed, PARAMS.maxspeed))
            for fade in FADES:
                fade.render(TICK, SCSP)
            if SCSP.win.getch() == ord('q'):
                SCSP.destroy()
                sys.exit()
            deb = []
            if (time.time() - STARTTIME) < 2:
                deb.append("Press Q or CTRL-C to quit.")
                if _PY2:
                    deb.append("NOTE: Colors do not work properly in python2.")
                    deb.append("Try running in python3 to get colors working.")
            if PARAMS.debug == True:
                deb.append("matrix.py %s" % __VERSION__)
                deb.append("%s\tframes, delay %s, colors %s, fades %s" % (
                    str(TICK), str(PARAMS.delay), PARAMS.colors, str(PARAMS.fades)))
            SCSP.render(deb)
            TICK += 1
    except KeyboardInterrupt:
        SCSP.destroy()
        sys.exit()

def colortest(stdscr):
    # http://stackoverflow.com/questions/18551558/how-to-use-terminal-color-palette-with-curses
    curses.start_color()
    curses.use_default_colors()
    stdscr.addstr(0, 0, "%s colors on %s.\n" %
                  (str(curses.COLORS), str(curses.termname())))
    for i in range(0, curses.COLORS):
        curses.init_pair(i + 1, i, -1)
    for i in range(curses.COLORS):
        try:
            stdscr.addstr(str(i) + "\t", curses.color_pair(i))
        except:
            pass
    stdscr.getch()

if __name__ == '__main__':
    agp = argparse.ArgumentParser(
        description="A cool matrix effect for your terminal!")
    agp.add_argument("-d", "--delay", type=float, default=0.1,
                     help="set delay between frames in seconds (default 0.1)")
    agp.add_argument("-n", "--fades", type=int, default=40,
                     help="set number of \"fades\" on screen (default 25)")
    agp.add_argument("-miln", "--minlength", type=int, default=4,
                     help="set minimum length of fades (default 4)")
    agp.add_argument("-mxln", "--maxlength", type=int, default=20,
                     help="set maximum length of fades (default 20)")
    agp.add_argument("-mis", "--minspeed", type=float, default=0.5,
                     help="set minimum speed of fades (default 0.5)")
    agp.add_argument("-mxs", "--maxspeed", type=float, default=2,
                     help="set maximum speed of fades (default 2)")
    agp.add_argument("-milt", "--minlifetime", type=int, default=10,
                     help="set minimum lifetime of fades (default 10)")
    agp.add_argument("-mxlt", "--maxlifetime", type=int, default=30,
                     help="set maximum lifetime of fades (default 30)")
    agp.add_argument("-c", "--colors", type=str, default="83,84,119,120",
                     help="set fade colors.\n" +
                     "Must be a comma separated list of values on the terminal's color palette.\n" +
                     "(default 83,84,119,120)")
    agp.add_argument("-cc", "--headcolor", type=int, default=16,
                     help="set fade head color. (default 16)")
    agp.add_argument("-C", "--chars", type=str, default="32-128",
                     help="set fade characters. Is a comma separated list of unicode ranges.\n" +
                     "Can be one character or a range. [Example: -C 32-55,66,2605,2606,6785-8941]\n" +
                     "(default 32-128)")
    agp.add_argument("-g", "--debug", action="store_true",
                     help="show debug statistics in upper left corner")
    agp.add_argument("-t", "--colortest", action="store_true",
                     help="output a color test screen")
    args = agp.parse_args()
    if args.colortest == True:
        curses.wrapper(colortest)
    else:
        chars = parse_chars(args.chars, agp)
        curses.wrapper(main, args, chars)


import numpy as np
import pandas as pd
from datasets import Dataset
from transformers import AutoTokenizer
from transformers import AutoModelForMultipleChoice
import torch
from torch.utils.data import DataLoader
from torch.nn.functional import softmax

from wikipeida_processor import add_context

from utils import *

model_dir = "./models/deberta-v3-large"
tokenizer = AutoTokenizer.from_pretrained(model_dir)
model = AutoModelForMultipleChoice.from_pretrained(model_dir).cuda()
model.eval()

def preprocess(example):
    # The AutoModelForMultipleChoice class expects a set of question/answer pairs
    # so we'll copy our question 5 times before tokenizing
    first_sentence = [example['prompt']] * 5
    second_sentence = []
    for option in options:
        second_sentence.append(example[option])
    # Our tokenizer will turn our text into token IDs BERT can understand
    tokenized_example = tokenizer(first_sentence, second_sentence, truncation=True)
    tokenized_example['label'] = option_to_index[example['answer']]
    return tokenized_example

def infer(query: pd):
    mask = [-999 if (query[i] == "")[0] else 0 for i in ["A", "B", "C", "D", "E"]]
    query = add_context(query)
    query.index = list(range(len(query)))
    query["id"] = list(range(len(query)))
    query["prompt"] = query["context"].apply(lambda x: x[:1750]) + " #### " + query["question"]
    query["answer"] = "A"

    tokenized_test_dataset = Dataset.from_pandas(query[['prompt', 'A', 'B', 'C', 'D', 'E', 'answer']]).map(preprocess, remove_columns=['prompt', 'A', 'B', 'C', 'D', 'E', 'answer'])
    tokenized_test_dataset = tokenized_test_dataset.remove_columns(["__index_level_0__"])
    data_collator = DataCollatorForMultipleChoice(tokenizer=tokenizer)
    test_dataloader = DataLoader(tokenized_test_dataset, batch_size=1, shuffle=False, collate_fn=data_collator)

    test_predictions = []
    for batch in test_dataloader:
        for k in batch.keys():
            batch[k] = batch[k].cuda()
        with torch.no_grad():
            outputs = model(**batch)
        test_predictions.append(outputs.logits.cpu().detach())
    test_predictions[0] = test_predictions[0] + torch.FloatTensor(mask)
    prob = softmax(test_predictions[0])
    prob = float(torch.max(prob))
    test_predictions = torch.cat(test_predictions)
    test_predictions = test_predictions.numpy()
    predictions_as_ids = np.argsort(-test_predictions, 1)
    predictions_as_answer_letters = np.array(list('ABCDE'))[predictions_as_ids]
    predictions_as_string = query['prediction'] = [
    ' '.join(row) for row in predictions_as_answer_letters[:, :3]
    ]
    return predictions_as_string[0][0], prob


import os
import itertools
import argparse
from torch.utils.data import DataLoader
import torch

from tactile_gym.assets import add_assets_path
from tactile_gym.utils.general_utils import str2bool

from tactile_gym_sim2real.pix2pix.image_generator import DataGenerator
from tactile_gym_sim2real.image_transforms import *

parser = argparse.ArgumentParser()
parser.add_argument("--batch_size", type=int, default=1, help="size of the batches")
parser.add_argument("--n_cpu", type=int, default=8, help="number of cpu threads to use during batch generation")
parser.add_argument("--channels", type=int, default=1, help="number of image channels")
parser.add_argument("--shuffle", type=str2bool, default=True, help="shuffle the generated image data")
opt = parser.parse_args()

# Parameters
augmentation_params = {
          'dims':        (256, 256),
          'rshift':      None,  # (0.025, 0.025),
          'rzoom':       None,  # (0.98, 1),
          'thresh':      True,
          'brightlims':  None,  # [0.3,1.0,-50,50], # alpha limits for contrast, beta limits for brightness
          'noise_var':   None,  # 0.001,
          'stdiz':       False,
          'normlz':      True,
          'joint_aug':   False
          }

add_border = True
image_size_str = str(augmentation_params['dims'][0]) + 'x' + str(augmentation_params['dims'][1])

if add_border:

    border_images_path = add_assets_path(
        os.path.join('robot_assets', 'tactip', 'tactip_reference_images')
    )

    saved_file_dir = os.path.join(
        border_images_path,
        'standard',  # TODO: automate flat/standard/right_angle choice
        image_size_str,
    )

    border_gray = torch.FloatTensor(np.load(os.path.join(saved_file_dir, "nodef_gray.npy")))
    border_mask = torch.FloatTensor(np.load(os.path.join(saved_file_dir, "border_mask.npy")))

# data collected for task
# task_dirs = ['edge_2d']
task_dirs = ['surface_3d']
# task_dirs = ['spherical_probe']
# task_dirs = ['edge_2d', 'surface_3d']

# for GAN data
# data_dirs = ['tap']
data_dirs = ['shear']
# data_dirs = ['tap', 'shear']

# combine the data directories
combined_dirs = list(itertools.product(task_dirs, data_dirs))
combined_paths = [os.path.join(*i) for i in combined_dirs]

# data dir real -> sim
training_real_data_dirs = [os.path.join('../data_collection/real/data/', data_path, 'csv_train') for data_path in combined_paths]
validation_real_data_dirs = [os.path.join('../data_collection/real/data/', data_path, 'csv_val') for data_path in combined_paths]
training_sim_data_dirs = [os.path.join('../data_collection/sim/data/', data_path, image_size_str, 'csv_train') for data_path in combined_paths]
validation_sim_data_dirs = [os.path.join('../data_collection/sim/data/', data_path, image_size_str, 'csv_val') for data_path in combined_paths]

# Configure dataloaders
training_generator = DataGenerator(real_data_dirs=training_real_data_dirs,
                                   sim_data_dirs=training_sim_data_dirs,
                                   dim=augmentation_params['dims'],
                                   stdiz=augmentation_params['stdiz'],
                                   normlz=augmentation_params['normlz'],
                                   thresh=augmentation_params['thresh'],
                                   rshift=augmentation_params['rshift'],
                                   rzoom=augmentation_params['rzoom'],
                                   brightlims=augmentation_params['brightlims'],
                                   noise_var=augmentation_params['noise_var'],
                                   joint_aug=augmentation_params['joint_aug'])

val_generator = DataGenerator(real_data_dirs=validation_real_data_dirs,
                              sim_data_dirs=validation_sim_data_dirs,
                              dim=augmentation_params['dims'],
                              stdiz=augmentation_params['stdiz'],
                              normlz=augmentation_params['normlz'],
                              thresh=augmentation_params['thresh'],
                              rshift=None,
                              rzoom=None,
                              brightlims=None,
                              noise_var=None,
                              joint_aug=False)

training_loader = torch.utils.data.DataLoader(training_generator,
                                              batch_size=opt.batch_size,
                                              shuffle=opt.shuffle,
                                              num_workers=opt.n_cpu)

val_loader = torch.utils.data.DataLoader(val_generator,
                                         batch_size=opt.batch_size,
                                         shuffle=opt.shuffle,
                                         num_workers=opt.n_cpu)

for (i_batch, sample_batched) in enumerate(training_loader,0):

    real_images = sample_batched['real']
    sim_images = sample_batched['sim']

    if add_border:
        sim_images[:, :, border_mask == 1] = border_gray[border_mask == 1] / 255.0

    cv2.namedWindow("training_images")

    for i in range(opt.batch_size):

        # convert image to opencv format, not pytorch
        real_image = np.swapaxes(np.swapaxes(real_images[i].numpy(), 0, 2), 0, 1)
        sim_image  = np.swapaxes(np.swapaxes(sim_images[i].numpy(), 0, 2), 0, 1)
        overlay_image = cv2.addWeighted(real_image, 0.25, sim_image, 0.9, 0)[...,np.newaxis]

        disp_image = np.concatenate([real_image, sim_image, overlay_image], axis=1)

    # show image
    cv2.imshow("training_images", disp_image)
    k = cv2.waitKey(500)
    if k == 27:    # Esc key to stop
        break


import datetime
import logging

import requests

from commands.feriados.constants import month_names, FERIADOS_URL, ESP_DAY

logger = logging.getLogger(__name__)

def get_feriados(year, **request_args):
    try:
        url = FERIADOS_URL.format(year=year)
        r = requests.get(url, **request_args)
        logger.info(f'Retrieved feriados from {r.url}')
    except Exception:
        logger.error("Error requestion feriados", exc_info=True)
        return None
    if r.status_code != 200:
        logger.info(f"Response not 200. {r.status_code} {r.reason}")
        return None

    feriados = r.json()
    logger.info("Feriados: %s", feriados)

    return feriados

def filter_past_feriados(today, feriados):
    """Returns the future feriados. Filtering past feriados."""
    return (
        f for f in feriados
        if (f['mes'], f['dia']) > (today.month, today.day)
    )

def next_feriado_message(today, nextest_feriado):
    """Get message corresponding to how many days are left until next feriado"""
    # Get days until next feriado
    next_feriado_date = datetime.datetime(
        day=nextest_feriado['dia'], month=nextest_feriado['mes'], year=today.year,
        hour=today.hour, minute=today.minute, second=min(today.second + 1, 59),
        tzinfo=datetime.timezone(datetime.timedelta(hours=-3))
    )

    # In python, timedeltas can have negative days if we do a-b and b > a). See timedelta docs for details
    faltan = (next_feriado_date - today)
    days_to_feriado = max(faltan.days, 0)

    if days_to_feriado == 0:
        feriado_msg = f"Hoy es feriado por *{nextest_feriado['motivo']}*! ðŸŽ‰\n"
    else:
        feriado_msg = (
            f"Faltan *{days_to_feriado} dÃas* para el prÃ³ximo feriado\n"
            f"- *{ESP_DAY[next_feriado_date.weekday()]} {nextest_feriado['dia']}"
            f" de {month_names[nextest_feriado['mes']]}* | _{nextest_feriado['motivo']}_\n"
        )

    return feriado_msg

def prettify_feriados(feriados, limit=None):
    """Receives a feriado generator of dict of following feriados and pretty prints them.
    ({
        "motivo": "AÃ±o Nuevo",
        "tipo": "inamovible",
        "dia": 1,
        "mes": 1,
        "id": "aÃ±o-nuevo"
    }, {
        "motivo": "Carnaval",
        "tipo": "inamovible",
        "dia": 4,
        "mes": 3,
        "id": "carnaval"
    },
        ...
    )
    Output:
        ðŸ‘‰ 25 de diciembre  -  Navidad | inamovible
        ðŸ‘‰ 31 de diciembre  -  Feriado Puente TurÃstico | puente
    """
    res = ''
    for feriado in list(feriados)[:limit]:
        fecha = f"{feriado['dia']} de {month_names[feriado['mes']]}"
        res += f"ðŸ‘‰ *{fecha}* -  {feriado['motivo']} | _{feriado['tipo']}_\n"

    return res

def read_limit_from_args(args):
    """Limit the amount of feriados to show."""
    DEFAULT = 10

    if not args:
        return DEFAULT
    elif args[0].upper() == 'ALL':
        # my_list[:None] == my_list
        return None
    else:
        try:
            return int(args[0])
        except (TypeError, ValueError):
            return DEFAULT


import typing
from decimal import Decimal

import pydantic
from pyrsistent import pmap
from typing_extensions import TypedDict

from noobit_markets.base.request import (
    # retry_request,
    _validate_data,
)

# Base
from noobit_markets.base import ntypes
from noobit_markets.base.models.result import Result
from noobit_markets.base.models.rest.request import NoobitRequestAddOrder
from noobit_markets.base.models.rest.response import (
    NoobitResponseNewOrder, NoobitResponseSymbols, NoobitResponseItemOrder,
    NoobitResponseTrades, T_OrderParsedItem, T_OrderParsedRes,
    T_PrivateTradesParsedItem,
    T_PrivateTradesParsedRes,
)
from noobit_markets.base.models.frozenbase import FrozenBaseModel

# FTX
from noobit_markets.exchanges.ftx.types import F_ORDERSIDE, F_ORDERSTATUS, F_ORDERSTATUS_TO_N, F_ORDERTYPE, F_ORDERTYPE_FROM_N, F_ORDERSIDE_FROM_N, F_ORDERTYPE_TO_N
from noobit_markets.exchanges.ftx.rest.auth import FtxAuth
from noobit_markets.exchanges.ftx import endpoints
from noobit_markets.exchanges.ftx.rest.base import get_result_content_from_req

__all__ = "post_neworder_ftx"

# ============================================================
# FTX REQUEST
# ============================================================

class FtxRequestNewOrder(FrozenBaseModel):

    market: str
    side: F_ORDERSIDE
    price: Decimal
    type: F_ORDERTYPE
    size: Decimal
    reduceOnly: typing.Optional[bool]
    ioc: typing.Optional[bool]
    postOnly: typing.Optional[bool]
    clientId: typing.Optional[str]

class _ParsedReq(TypedDict):

    market: typing.Any
    side: typing.Any
    price: typing.Any
    type: typing.Any
    size: typing.Any
    reduceOnly: typing.Any
    ioc: typing.Any
    postOnly: typing.Any
    clientId: typing.Any

def parse_request(
    valid_request: NoobitRequestAddOrder,
    symbol_to_exchange: ntypes.SYMBOL_TO_EXCHANGE
) -> _ParsedReq:

    payload: _ParsedReq = {
        "market": symbol_to_exchange(valid_request.symbol),
        "side": F_ORDERSIDE_FROM_N(valid_request.side),
        "price": None if valid_request.ordType == "MARKET" else valid_request.price,
        "type": F_ORDERTYPE_FROM_N(valid_request.ordType),
        "size": valid_request.orderQty,
        "reduceOnly": None,
        "ioc": None,
        "postOnly": None,
        "clientId": valid_request.clOrdID
    }
    return payload

# ============================================================
# FTX RESPONSE
# ============================================================

# SAMPLE RESPONSE

# {
#   "success": true,
#   "result": {
#     "createdAt": "2019-03-05T09:56:55.728933+00:00",
#     "filledSize": 0,
#     "future": "XRP-PERP",
#     "id": 9596912,
#     "market": "XRP-PERP",
#     "price": 0.306525,
#     "remainingSize": 31431,
#     "side": "sell",
#     "size": 31431,
#     "status": "open",
#     "type": "limit",
#     "reduceOnly": false,
#     "ioc": false,
#     "postOnly": false,
#     "clientId": null,
#   }
# }

class FtxResponseNewOrder(FrozenBaseModel):

    createdAt: str
    filledSize: Decimal
    future: typing.Optional[str]
    id: pydantic.PositiveInt
    market: str
    price: Decimal
    remainingSize: Decimal
    side: F_ORDERSIDE
    size: Decimal
    status: F_ORDERSTATUS
    type: F_ORDERTYPE
    reduceOnly: bool
    ioc: bool
    postOnly: bool
    clientId: typing.Optional[str]

def parse_result(
    result_data: FtxResponseNewOrder,
    symbol_from_exchange: ntypes.SYMBOL_FROM_EXCHANGE,
) -> T_OrderParsedItem:

    symbol = symbol_from_exchange(result_data.market)

    parsed: T_OrderParsedItem = {
        "orderID": result_data.id,
        "symbol": symbol,
        "currency": symbol.split("-")[1],
        "side": result_data.side,
        "ordType": F_ORDERTYPE_TO_N(result_data.type),
        "execInst": None,
        "clOrdID": result_data.clientId,
        "account": None,
        "cashMargin": "cash",    #? stick to spot for now
        "marginRatio": 1,
        "marginAmt": 0,
        "ordStatus": F_ORDERSTATUS_TO_N(result_data.status),
        "workingIndicator": True,
        "ordRejReason": None,
        "timeInForce": "GTC",
        "transactTime": result_data.createdAt,
        "sendingTime": None,
        "effectiveTime": None,
        "validUntilTime": None,
        "expireTime": None,
        "displayQty": None,
        "grossTradeAmt": result_data.size * result_data.price, #? not sure this means what we think
        "orderQty": result_data.size,
        "cashOrderQty": result_data.size * result_data.price, #? one of this/above has to be incorrect
        "orderPercent": None,
        "cumQty": result_data.filledSize,
        "leavesQty": result_data.remainingSize,
        "price": result_data.price,
        "stopPx": None,
        "avgPx": result_data.avgFillPrice,
        "fills": None,
        "commission": 0, # FIXME should be None = change noobit response model
        "targetStrategy": None,
        "targetStrategyParameters": None,
        "text": None
    }
    return parsed

# ============================================================
# FETCH
# ============================================================

async def post_neworder_ftx(
        client: ntypes.CLIENT,
        symbol: ntypes.SYMBOL,
        symbols_resp: NoobitResponseSymbols,
        side: ntypes.ORDERSIDE,
        ordType: ntypes.ORDERTYPE,
        clOrdID: str,
        orderQty: Decimal,
        price: Decimal,
        timeInForce: ntypes.TIMEINFORCE,
        stopPrice: typing.Optional[Decimal] = None,
        quoteOrderQty: typing.Optional[Decimal] = None,
        # prevent unintentional passing of following args
        *,
        logger: typing.Optional[typing.Callable] = None,
        auth=FtxAuth(),
        base_url: pydantic.AnyHttpUrl = endpoints.FTX_ENDPOINTS.private.url,
        endpoint: str = endpoints.FTX_ENDPOINTS.private.endpoints.new_order,
    ) -> Result[NoobitResponseItemOrder, pydantic.ValidationError]:

    symbol_from_exchange = lambda x: {
        f"{v.noobit_base}{v.noobit_quote}": k
        for k, v in symbols_resp.asset_pairs.items()
    }[x]
    symbol_to_exchange = lambda x: {
        k: v.exchange_pair for k, v in symbols_resp.asset_pairs.items()
    }[x]

    req_url = "/".join([base_url, "orders"])
    method = "POST"

    valid_noobit_req = _validate_data(
        NoobitRequestAddOrder, pmap({
            "exchange": "FTX",
            "symbol": symbol,
            "symbols_resp": symbols_resp,
            "side":side,
            "ordType":ordType,
            "clOrdID":clOrdID,
            "orderQty":orderQty,
            "price":price,
            "timeInForce": timeInForce,
            "quoteOrderQty": quoteOrderQty,
            "stopPrice": stopPrice,
        })
    )
    if valid_noobit_req.is_err():
        return valid_noobit_req

    # if logger:
    #     logger(f"New Order - Noobit Request : {valid_noobit_req.value}")

    parsed_req = parse_request(valid_noobit_req.value, symbol_to_exchange)

    valid_ftx_req = _validate_data(FtxRequestNewOrder, pmap(parsed_req))
    if valid_ftx_req.is_err():
        return valid_ftx_req

    # ? should be more elegant way to do this
    querystr = f"?market={valid_ftx_req.value.market}"
    req_url += querystr
    headers = auth.headers(method, f"/api/orders{querystr}")

    if logger:
        logger(f"New Order - Parsed Request : {valid_ftx_req.value}")

    result_content = await get_result_content_from_req(
        client, method, req_url, FrozenBaseModel(), headers
    )
    if result_content.is_err():
        return result_content

    if logger:
        logger(f"New Order - Result content : {result_content.value}")

    valid_result_content = _validate_data(
        FtxResponseNewOrder, result_content.value
    )
    if valid_result_content.is_err():
        return valid_result_content

    parsed_result = parse_result(
        valid_result_content.value, symbol_from_exchange
    )

    valid_parsed_response = _validate_data(
        NoobitResponseNewOrder,
        pmap(
            {
                **parsed_result,
                "rawJson": result_content.value,
                "exchange": "FTX",
            }
        ),
    )
    return valid_parsed_response


from pathlib import Path
from unittest.mock import PropertyMock

from requests import RequestException

from packageship.application.cli.commands.installdep import InstallDepCommand
from packageship.application.common.exc import ElasticSearchQueryException
from test.cli import DATA_BASE_INFO
from test.cli.depend_commands import DependTestBase

INSTALLDEP_EXPECTED_DATA_FOLDER = Path(Path(__file__).parent, "expected_data")

class TestInstallDep(DependTestBase):
    """
    class for TestInstallDep
    """
    cmd_class = InstallDepCommand
    binary_file = "os-version-binary.json"
    component_file = "os-version-binary-component.json"

    def test_wrong_level_negative(self):
        """
        test Wrong parameter,Leve is a negative number
        """
        self.command_params = ["Judy", "-level=-100"]
        self.excepted_str = """
ERROR_CONTENT  :Request parameter error
HINT           :Please check the parameter is valid and query again
        """
        self.assert_exc_result()

    def test_wrong_level_0(self):
        """
        test Wrong parameter, Level is zero
        """
        self.command_params = ["Judy", "-level=0"]
        self.excepted_str = """
ERROR_CONTENT  :Request parameter error
HINT           :Please check the parameter is valid and query again
            """
        self.assert_exc_result()

    def test_wrong_package_name(self):
        """
        test the package name is not in the database
        """
        self.command_params = ["test"]
        self.excepted_str = """
ERROR_CONTENT  :The querying package does not exist in the databases
HINT           :Use the correct package name and try again
            """
        self.assert_exc_result()

    def test_wrong_db_name(self):
        """
        test the package name is not in the database
        """
        self.command_params = ["CUnit", "-dbs=test"]
        self.excepted_str = """
ERROR_CONTENT  :Request parameter error
HINT           :Please check the parameter is valid and query again
                """
        self.assert_exc_result()

    def test_true_level_0(self):
        """
        test Correct parameter,Only the package name
        """

        self.command_params = ["Judy"]
        self.excepted_str = self.read_file_content(
            "installdep_Judy.txt",
            folder=INSTALLDEP_EXPECTED_DATA_FOLDER,
            is_json=False)
        self.assert_result()

    def test_true_level_1(self):
        """
        The installation dependency specifies a query level of 1
        """

        self.command_params = ["Judy", "-level=1"]
        self.excepted_str = self.read_file_content(
            "install_Judy_level_1.txt",
            folder=INSTALLDEP_EXPECTED_DATA_FOLDER,
            is_json=False)
        self.assert_result()

    def test_true_level_one_db(self):
        """
        The installation dependency specifies the query level as 1
        and specifies the database
        """
        self.command_params = ["Judy", "-level=1", "-dbs=os-version"]
        self.excepted_str = self.read_file_content(
            "installdep_Judy_level_1_dbs.txt",
            folder=INSTALLDEP_EXPECTED_DATA_FOLDER,
            is_json=False)
        self.assert_result()

    def test_true_level_db(self):
        """
        The installation depends on the specified query for the specified database
        """
        self.command_params = ["Judy", "-dbs=os-version"]
        self._execute_command()
        self.excepted_str = self.read_file_content(
            "installdep_Judy_dbs.txt",
            folder=INSTALLDEP_EXPECTED_DATA_FOLDER,
            is_json=False)
        self.assert_result()

    def test_error_es_data_return_not_resp(self):
        """test_get_install_error_data_to_raise_keyerror"""

        self.command_params = ["Judy", "-dbs=os-version"]
        self.excepted_str = """
ERROR_CONTENT  :The querying package does not exist in the databases
HINT           :Use the correct package name and try again
    """
        self.mock_es_search(side_effect=[DATA_BASE_INFO, {}])
        self.assert_exc_result()

    def test_request_raise_requestexception(self):
        """test_request_raise_requestexception"""

        self.command_params = ["Judy", "-dbs=os-version"]
        self.excepted_str = """
ERROR_CONTENT  :
HINT           :The remote connection is abnormal, please check the 'remote_host' parameter value to ensure the connectivity of the remote address
    """
        self._to_update_kw_and_make_mock(
            "packageship.application.common.remote.RemoteService.request",
            effect=RequestException,
        )
        self.assert_exc_result()

    def test_request_text_raise_jsonerror(self):
        """test_request_text_raise_jsonerror"""

        self.command_params = ["Judy", "-dbs=os-version"]
        self.excepted_str = """
ERROR_CONTENT  :{"test":\'123\',}
HINT           :The content is not a legal json format,please check the parameters is valid
    """
        self._to_update_kw_and_make_mock(
            "packageship.application.common.remote.RemoteService.text",
            new_callable=PropertyMock,
            return_value="""{"test":'123',}""",
        )
        self.assert_exc_result()

    def test_request_status_500(self):
        """test_request_status_500"""

        self.command_params = ["Judy", "-dbs=os-version"]
        self.excepted_str = """
ERROR_CONTENT  :Server error
HINT           :Please check the service and try again
    """

        self._to_update_kw_and_make_mock(
            "packageship.application.common.remote.RemoteService.status_code",
            new_callable=PropertyMock,
            return_value=500,
        )

        self._to_update_kw_and_make_mock(
            "packageship.application.common.remote.RemoteService.text",
            new_callable=PropertyMock,
            return_value="",
        )
        self.assert_exc_result()


from turing import get_response
from weather import get_forecast
from package import get_package
from apscheduler.schedulers.background import BackgroundScheduler
import json
import re
from const import Status
from itchatUtils import send_msg,send_img

class WeChatBot:
    def __init__(self, user):
        self.user = 'filehelper'
        self.status = Status['init']

        self.movie = json.load(open('m.json', 'r'))
        self.movie_index = 0
        self.max_movie = len(self.movie)
        self.is_send_movie_list = False

        self.schedule = BackgroundScheduler()

        self.function = {
            Status['init']: self.handle_text,
            Status['weather']: self.handle_weather,
            Status['schedule']: self.handle_schedule,
            Status['package']: self.handle_package,
            Status['movie']: self.handle_movie,
        }

    def entry(self, text):
        self.function[self.status](text)

    """
    trigger:
       功能
    """

    def handle_text(self, text):
        if re.search(r"定时", text):
            self.handle_schedule(text)
        elif re.search(r"快递", text):
            self.handle_package(text)
        elif re.search(r"天气", text) or re.search(r"气温", text):
            self.handle_weather(text)
        elif re.search(r"电影", text) or re.search(r"正在热映", text) or re.search(r"院线热映", text):
            self.handle_movie(text)
        elif re.search(r"功能", text):
            reply = "本机器人有如下功能:[快递查询],[天气查询],[院线热映],[设定定时任务]"
            send_msg(reply, self.user)
        else:
            reply = '机器人自动回复:' + get_response(text)
            send_msg(reply, self.user)

    """
    trigger:
       定时
    """

    def handle_schedule(self, text):
        try:
            temp = text.split('+')[1:]
            time = temp[0]
            msg = temp[1]
            name = temp[2]
            self.schedule.add_job(send_msg, 'date', run_date=time, kwargs={"msg": msg, "name": name})
            reply = '设置成功'
            self.schedule.start()
        except:
            reply = "设置定时任务请输入:定时发送+时间+msg+姓名,如：定时发送+2019-2-16 21:51:00+晚上好+filehelper"
        send_msg(reply, self.user)

    """
    trigger:
        快递
    """

    def handle_package(self, text):
        try:
            pack_num = re.search(r"(快递)(\+)([0-9]+)", text).group(3)
            reply = get_package(pack_num)
        except:
            reply = "查询快递请输入：快递+运单号，如：快递+12345"
        send_msg(reply, self.user)

    """
    trigger:
       天气
        今日天气，七日天气
    """

    def handle_weather(self, text):
        if re.search(r"今日天气", text):
            try:
                city_name = re.search(r"(今日天气)(\+)(.*)", text).group(3)
                reply = get_forecast('今日天气', city_name)
            except:
                reply = '查询今日天气请输入今日天气 + 城市名，如: 今日天气 + 西安.'
        elif re.search(r"七日天气", text):
            try:
                city_name = re.search(r"(七日天气)(\+)(.*)", text).group(3)
                reply = get_forecast('七日天气', city_name)
            except:
                reply = '查询七日天气请输入七日天气+城市名，如:七日天气+西安'
        else:
            reply = '''查询今日天气请输入今日天气+城市名，如:今日天气+西安.\n查询七日天气请输入七日天气+城市名，如:七日天气+西安。'''
        send_msg(reply, self.user)

    """
    trigger:
        电影，正在热映，院线热映
        in:
            \d 选择
            影院:（一个电影中）查看电影院，
            简介:（一个电影中）查看简介，
            l 展示列表
            q 退出

    """

    def send_list(self):
        temp = ''
        if not self.is_send_movie_list:
            for i, m in enumerate(self.movie):
                temp += f"{i+1}.{m['name']}\n"
            send_msg(temp, self.user)
            self.is_send_movie_list = True

    def handle_movie(self, text):
        self.status = Status['movie']
        self.send_list()
        if text.isdigit():
            try:
                index = int(text) - 1
                if 0 <= index < self.max_movie:
                    self.movie_index = index
                    reply1 = self.movie[index]['basic']
                    img = self.movie[index]['img']
                    send_img(img, self.user)
                    send_msg(reply1, self.user)
                else:
                    send_msg("请输入正确的数字范围1～" + str(self.max_movie), self.user)
            except:
                send_msg("请输入合法的数字1~" + str(self.max_movie), self.user)
        elif re.search(r'影院', text):
            reply2 = ''
            for i in self.movie[self.movie_index]['cinema']:
                reply2 += i + '\n'
            send_msg(reply2, self.user)
        elif re.search(r'简介', text):
            reply3 = self.movie[self.movie_index]['intro']
            send_msg(reply3, self.user)
        elif re.search(r'l', text):
            self.is_send_movie_list = False
            self.send_list()
        elif re.search(r'q', text):
            self.status = Status['init']
            self.movie_index = 0
            self.is_send_movie_list = False


import numpy as np
import os
import pandas as pd
import subprocess
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
from sklearn.linear_model import LinearRegression
from utils import emotion_to_id, aro_val_to_emo
from sklearn.model_selection import train_test_split

def parallel(X, i):
    y = X[:, i]
    X_i = np.concatenate((X[:, :i], X[:, i+1:]), axis=1)
    clf = LinearRegression()
    X_train, X_test, y_train, y_test = train_test_split(X_i, y, test_size = 0.3)
    clf.fit(X_train, y_train)
    return clf.score(X_test, y_test)

class Featurizer():
    def __init__(self):
        self.n_features = 6374

    def run_enterface(self, metadata):
        # open smile, compare feature set
        df = pd.DataFrame()
        labels = []
        banned = ['subject 6', 'subject 23']
        for subject, emotion, sentence, path in metadata.values:
            if(not subject in banned):
                path = '../' + path.replace('.avi', '.wav')
                data_fname = 'data/csvs/' + subject[subject.rindex(' ')+1:] + '_' + emotion + '_' + sentence[-1] + '.csv'

                if(not os.path.isfile(data_fname)):
                    subprocess.run(['SMILExtract', '-C', 'data/config/IS13_ComParE.conf', '-I', path, '-csvoutput', data_fname])

                df_tmp = pd.read_csv(data_fname, sep=';', header=0)
                df_tmp = df_tmp.drop(list(df_tmp.columns)[0], axis=1)
                df = df.append(df_tmp, ignore_index=True)
                labels.append(emotion)
        return df.values, labels

    def run_iemocap(self, metadata):
        data_folder = '../data/IEMOCAP_full_release/'
        df = pd.DataFrame()
        labels = []

        for sess,fname,utt,_,_,arousal,valence,emotion in metadata.values:
            data_fname = 'data/csvs/' + fname + '_' + utt + '.csv'

            if(not os.path.isfile(data_fname)):
                path = data_folder + sess + '/sentences/wav/' + fname +'/' + fname + '_' + utt + '.wav'
                subprocess.run(['SMILExtract', '-C', 'data/config/IS13_ComParE.conf', '-I', path, '-csvoutput', data_fname])

            df_tmp = pd.read_csv(data_fname, sep=';', header=0)
            df_tmp = df_tmp.drop(list(df_tmp.columns)[0], axis=1)
            df = df.append(df_tmp, ignore_index=True)
            labels.append([emotion, arousal, valence])

        return df.values, np.array(labels)

    def run_mosei(self, metadata):
        data_folder = '../data/mosei/Raw/'
        df = pd.DataFrame()
        labels = []

        for path,vid,clip,emotion,arousal,valence in metadata.values:
            path = '../' + path.replace('[modal]', 'Audio').replace('[ext]', '.wav')
            if(os.path.isfile(path)):
                data_fname = 'data/csvs/mosei' + str(vid) + '_' + str(clip) + '.csv'

                if(not os.path.isfile(data_fname)):
                    subprocess.run(['SMILExtract', '-C', 'data/config/IS13_ComParE.conf', '-I', path, '-csvoutput', data_fname])

                df_tmp = pd.read_csv(data_fname, sep=';', header=0)
                df_tmp = df_tmp.drop(list(df_tmp.columns)[0], axis=1)
            else:
                df_tmp = pd.DataFrame([np.nan for _ in range(self.n_features)])
            df = df.append(df_tmp, ignore_index=True)
            labels.append([emotion, arousal, valence])

        return df.values, np.array(labels)

    def run_hci(self, metadata):
        data_folder = '../data/hci_tagging/Sessions/'
        df = pd.DataFrame()
        labels = []

        for sess, aro, val in metadata.values:
            emotion = aro_val_to_emo(aro, val)
            wavs = [x for x in os.listdir(data_folder+'/'+str(sess)) if '.wav' in x]
            if(len(wavs) == 1):
                data_fname = data_folder+'/'+str(sess)+'/'+wavs[0]
                csv_fname = 'data/csvs/hci_'+str(sess)+'.csv'
                if(not os.path.isfile(csv_fname)):
                    subprocess.run(['SMILExtract', '-C', 'data/config/IS13_ComParE.conf', '-I', data_fname, '-csvoutput', csv_fname])

                df_tmp = pd.read_csv(csv_fname, sep=';', header=0)
                df_tmp = df_tmp.drop(list(df_tmp.columns)[0], axis=1)
            elif(len(wavs)>1):
                raise Exception('more than 1 wav file for session: ' + str(sess))
            else:
                df_tmp = pd.DataFrame([np.nan for _ in range(self.n_features)])
            df = df.append(df_tmp, ignore_index=True)
            labels.append([emotion, aro, val])

        return df.values, np.array(labels)

    def select_features_continuous(self, X, y, dataset):
        X = X.astype(np.float32)
        df_f = pd.DataFrame(X)
        df_f.columns = np.arange(df_f.shape[1])

        print("features: ", len(df_f.columns))

        epsilon = 1e-4 # 1e-5
        df_f_tmp = df_f.loc[:,df_f.var()>epsilon]
        tmp = list(df_f_tmp.columns)
        df_f_tmp.columns = np.arange(df_f_tmp.shape[1])

        print("selected after variance filter:", len(tmp))

        X = df_f_tmp.values.astype(np.float32)

        y_tmp = emotion_to_id(y[:,0], dataset)
        # remove nans
        idxs = []
        for i in range(len(X)):
            if(not np.isnan(X[i,:]).any()):
                idxs.append(i)

        X = X[idxs]
        y = np.array(y)[idxs]

        X, tmp = self.select_vif(X, tmp)
        df_f = df_f.loc[:, tmp]
        print("selected after VIF filter:", len(tmp))

        y_emo = y[:,0]
        y_emo = emotion_to_id(y_emo, dataset)
        y_aro = y[:, 1].astype(np.float32)
        y_val = y[:, 2].astype(np.float32)

        self.idxs = []

        for mode in ['emo', 'aro', 'val']:
            idxs = []
            if(mode == 'emo'):
                model = RandomForestClassifier(n_estimators=100)
                model.fit(X, y_emo)
                threshold = 1e-3
            elif(mode == 'aro'):
                model = RandomForestRegressor(n_estimators=100)
                model.fit(X, y_aro)
                threshold = 1e-3
            elif(mode == 'val'):
                model = RandomForestRegressor(n_estimators=100)
                model.fit(X, y_val)
                threshold = 1e-3

            importances = model.feature_importances_
            for i in range(len(importances)):
                if(importances[i]>threshold):
                    idxs.append(tmp[i])
            self.idxs.append(np.array(idxs))

            if(mode == 'emo'):
                df_f_emo = df_f.loc[:, importances>threshold]
                print("selected for emotion:", len(df_f_emo.columns))
                X_emo = df_f_emo.values
            elif(mode == 'aro'):
                df_f_aro = df_f.loc[:, importances>threshold]
                print("selected for arousal:", len(df_f_aro.columns))
                X_aro = df_f_aro.values
            elif(mode == 'val'):
                df_f_val = df_f.loc[:, importances>threshold]
                print("selected for valence:", len(df_f_val.columns))
                X_val = df_f_val.values
        X = [X_emo, X_aro, X_val]

        return X

    def select_features_categorical(self, X, y, dataset):
        df_f = pd.DataFrame(X)
        df_f.columns = np.arange(df_f.shape[1])

        print("features: ", len(df_f.columns))

        epsilon = 1e-4 # 1e-5
        df_f_tmp = df_f.loc[:,df_f.var()>epsilon]
        tmp = list(df_f_tmp.columns)
        df_f_tmp.columns = np.arange(df_f_tmp.shape[1])

        X = df_f_tmp.values.astype(np.float32)

        # remove nans
        idxs = []
        for i in range(len(X)):
            if(not np.isnan(X[i,:]).any()):
                idxs.append(i)

        X = X[idxs]
        y = np.array(y)[idxs]
        y = emotion_to_id(y, dataset)

        X, tmp = self.select_vif(X, tmp)
        df_f = df_f.loc[:, tmp]
        print("selected after VIF filter:", len(tmp))

        clf = RandomForestClassifier()
        clf.fit(X,y)

        self.idxs = []
        # threshold = 1e-3
        threshold = 5e-4
        importances = clf.feature_importances_
        for i in range(len(importances)):
            if(importances[i]>threshold):
                self.idxs.append(tmp[i])
        self.idxs = np.array(self.idxs)

        df_f = df_f.loc[:, importances>threshold]
        print("selected:", len(df_f.columns))

        # df = pd.concat((df_m, df_f), axis=1, ignore_index=True)
        # df.columns = np.arange(df.shape[1])
        X = df_f.values

        return X

    def select_vif(self, X, tmp):
        vifs = [np.inf]
        tmp = np.array(tmp)
        while(np.max(vifs) > 10):
            print(len(tmp), np.max(vifs))
            vifs = []

            results = []
            for i in range(len(X.T)):
                results.append(parallel(X, i))

            for r in results:
                vifs.append(1/(1-r))

            if(len(tmp) > 150):
                if(len(tmp) > 1000):
                    limit = 1000
                elif(len(tmp) > 500):
                    limit = 300
                elif(len(tmp) > 250):
                    limit = 25
                elif(len(tmp) > 150):
                    limit = 5

                data = [[i, vif] for i,vif in enumerate(vifs)]
                data = sorted(data, key=lambda x: x[1], reverse=True)
                idxs = [x[0] for x in data[:limit]]

                X = np.delete(X, idxs, axis=1)
                tmp = np.delete(tmp, idxs)

            else:
                if(np.max(vifs) > 10):
                    idx = np.argmax(vifs)
                    tmp = np.delete(tmp, idx)
                    X = np.concatenate((X[:, :idx], X[:, idx+1:]), axis=1)

        return X, tmp


from scraper.models import *
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings

class Command(BaseCommand):
	help = """Populate our database with default objects for each model"""

	def handle(self, *kwargs, **options):
		s = School(name='DEFAULT')
		d = Department(name='DEFAULT', school=s)
		i = Instructor(case_id='DEFAULT', name='DEFAULT', dept=d)
		c = Course(course_id='DEFAULT', dept=d, units=3, name='DEFAULT', instructor=i)
		r = Review(description='DEFAULT DEFAULT DESC', course=c)
		se = Section(section_num=101, course=c)

		s.save()
		d.save()
		i.save()
		c.save()
		r.save()
		se.save()


from utils.buy_sell_queue import BuySellQueue
from utils.helpful_scripts import string_to_datetime

class Indicators:
    static_WI_dict = {}

    @staticmethod
    def fill_WI_dict():
        for name, indicator in Indicators.__dict__.items():
            if name[:2] == "WI":
                Indicators.static_WI_dict[name] = indicator.__func__

    @staticmethod
    def fill_target_values(
        indicators_values: dict,
        trade_window: BuySellQueue,
        punch_window: BuySellQueue,
        stop_profit: float,
        stop_loss: float,
    ) -> None:
        column_name = "target"

        stop_loss_trigger_trade = punch_window.get_first_priced_below(
            "SELL", trade_window.last_prices["BUY"] * (1 - stop_loss)
        )
        stop_profit_trigger_trade = punch_window.get_first_priced_above(
            "BUY", trade_window.last_prices["BUY"] * (1 + stop_profit)
        )

        if not stop_loss_trigger_trade and not stop_profit_trigger_trade:
            indicators_values[column_name] = 0
        elif not stop_profit_trigger_trade:
            indicators_values[column_name] = -1
        elif not stop_loss_trigger_trade:
            indicators_values[column_name] = 1
        else:
            indicators_values[column_name] = (
                1
                if (
                    string_to_datetime(stop_loss_trigger_trade["createdAt"])
                    > string_to_datetime(stop_profit_trigger_trade["createdAt"])
                )
                else -1
            )

    @staticmethod
    def fill_features_values(
        indicators_punch_values: dict,
        queue: BuySellQueue,
        slices_lengths: list,
        n_trades_ago_list: list,
    ) -> None:

        indicators_punch_values["seconds-since-midnight"] = int(
            (
                queue.to_dt
                - queue.to_dt.replace(hour=0, minute=0, second=0, microsecond=0)
            ).total_seconds()
        )
        indicators_punch_values["date"] = string_to_datetime(
            queue.common_queue[-1]["createdAt"]
        ).date()

        for side in ["BUY", "SELL"]:
            for n_trades_ago in n_trades_ago_list:
                Indicators.seconds_since_n_trades_ago(
                    indicators_punch_values, queue, n_trades_ago, side
                )
            for window_slice_sec in slices_lengths:
                window_slice = queue.get_side_queue_slice(
                    side, window_slice_sec
                )

                for WI_name, WI_function in Indicators.static_WI_dict.items():
                    column_name = (
                        WI_name + "-" + str(window_slice_sec) + "_sec-" + side
                    )
                    WI_function(
                        indicators_punch_values, window_slice, column_name
                    )

    @staticmethod
    def seconds_since_n_trades_ago(
        indicators_values: dict,
        queue: BuySellQueue,
        n_trades_ago: int,
        side: str,
    ) -> None:
        column_name = (
            "seconds_since-" + str(n_trades_ago) + "-trades_ago-" + side
        )
        if not queue[side]:
            indicators_values[column_name] = 0
            return
        diff = (
            queue.to_dt
            - string_to_datetime(queue[side][-n_trades_ago]["createdAt"])
        ).total_seconds()
        indicators_values[column_name] = diff

    @staticmethod
    def WI_exp_moving_average(
        indicators_values: dict, window: list, WI_column_name: str, alpha=0.5
    ) -> None:
        if not window:
            indicators_values[WI_column_name] = 1
            return
        ema = float(window[0]["price"])
        for i in range(1, len(window)):
            ema = ema + alpha * (float(window[i]["price"]) - ema)
        indicators_values[WI_column_name] = ema / (
            sum(map(lambda trade: float(trade["price"]), window)) / len(window)
        )

    @staticmethod
    def WI_trade_amount(
        indicators_values: dict, window: list, WI_column_name: str
    ) -> None:
        indicators_values[WI_column_name] = len(window)

    @staticmethod
    def WI_trade_volume(
        indicators_values: dict, window: list, WI_column_name: str
    ) -> None:
        if not window:
            indicators_values[WI_column_name] = 0
            return
        indicators_values[WI_column_name] = sum(
            map(lambda trade: float(trade["size"]), window)
        )

    @staticmethod
    def WI_open_close_diff(
        indicators_values: dict, window: list, WI_column_name: str
    ) -> None:
        if not window:
            indicators_values[WI_column_name] = 1
            return
        indicators_values[WI_column_name] = float(window[-1]["price"]) / float(
            window[0]["price"]
        )

    @staticmethod
    def WI_weighted_moving_average(
        indicators_values: dict, window: list, WI_column_name: str
    ) -> None:
        if not window:
            indicators_values[WI_column_name] = 1
            return
        indicators_values[WI_column_name] = (
            sum(
                map(
                    lambda trade: float(trade["price"]) * float(trade["size"]),
                    window,
                )
            )
            / sum(map(lambda trade: float(trade["size"]), window))
        ) / (
            sum(map(lambda trade: float(trade["price"]), window)) / len(window)
        )

    @staticmethod
    def WI_stochastic_oscillator(
        indicators_values: dict, window: list, WI_column_name: str
    ) -> None:
        if not window:
            indicators_values[WI_column_name] = 0.5
            return
        indicators_values[WI_column_name] = (
            float(window[-1]["price"])
            - float(
                min(window, key=lambda trade: float(trade["price"]))["price"]
            )
        ) / (
            max(
                1,
                float(
                    max(window, key=lambda trade: float(trade["price"]))[
                        "price"
                    ]
                )
                - float(
                    min(window, key=lambda trade: float(trade["price"]))[
                        "price"
                    ]
                ),
            )
        )