#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 25 16:09:49 2018

@author: dzenn
"""

import numpy as np

from brian2 import ms, SpikeMonitor,\
    prefs, PoissonGroup, Hz

from teili.building_blocks.building_block import BuildingBlock
from teili.building_blocks.wta import WTA
from teili.core.groups import Connections
from teili.core import tags as tags_parameters

from teili.models.neuron_models import DPI
from teili.models.synapse_models import DPISyn

from teili.tools.three_way_kernels import A_plus_B_equals_C
from teili.tools.visualizer.DataControllers import Rasterplot

try:
    # from pyqtgraph import QtGui
    from PyQt5 import QtGui
    import pyqtgraph as pg
    QtApp = QtGui.QApplication([])
    SKIP_PYQTGRAPH_RELATED_UNITTESTS = False
except BaseException:
    SKIP_PYQTGRAPH_RELATED_UNITTESTS = True

threeway_params = {}

wta_params = {'we_inp_exc': 2000,
             'we_exc_inh': 300,
             'wi_inh_exc': -300,
             'we_exc_exc': 300,
             'sigm': 2.2,
             'rp_exc': 3 * ms,
             'rp_inh': 2 * ms,
             'ei_connection_probability': 1
             }

threeway_params.update(wta_params)

class Threeway(BuildingBlock):
    """A network of three 1d WTA populations connected to a hidden 2d WTA
    population implementing a three-way relation between three 1d quantities
    A, B and C (e.g. A + B = C) via a hidden population H.

    Attributes:
        Groups (dict): Complete list of keys of neuron groups, synapse groups
                       and WTA substructures of the Threeway building block to
                       be included into Network object for simulation
        monitors (dict): Complete list of Brian2 monitors for all entities of
                       the Threeway building block to be included into Network
                       object for simulation
        num_input_neurons (int): Sizes of input/output populations A, B and C
        num_neurons (int): Total amount of neurons used for the Threeway
                           structure including the hidden population H and
                           populations of inhibitory neurons used for WTA
                           connectivity
        A (WTA): A shortcut for a input/output population A implemented with
                 a Teili 1d WTA building block
        B (WTA): A shortcut for a input/output population B implemented with
                 a Teili 1d WTA building block
        C (WTA): A shortcut for a input/output population C implemented with
                 a Teili 1d WTA building block
        H (WTA): A shortcut for a hidden population H implemented with
                 a Teili 2d WTA building block
        Inp_A (PoissonGroup): PoissonGroup obj. to stimulate population A
        Inp_B (PoissonGroup): PoissonGroup obj. to stimulate population B
        Inp_C (PoissonGroup): PoissonGroup obj. to stimulate population C
        value_a (double): Stored input for A (center of a gaussian bump)
        value_b (double): Stored input for B (center of a gaussian bump)
        value_c (double): Stored input for C (center of a gaussian bump)
        standalone_params (dict): Keys for all standalone parameters necessary
                                  for cpp code generation (TBD)
    """

    def __init__(self, name,
                 neuron_eq_builder=DPI,
                 synapse_eq_builder=DPISyn,
                 block_params=threeway_params,
                 num_input_neurons=16,
                 num_hidden_neurons=256,
                 hidden_layer_gen_func=A_plus_B_equals_C,
                 cutoff=5,
                 additional_statevars=[],
                 spatial_kernel=None,
                 monitor=True,
                 debug=False):
        """
        Args:
            name (str, required): Name of the TW block
            neuron_eq_builder (class, optional): neuron class as imported
                                                 from models/neuron_models
            synapse_eq_builder (class, optional): synapse class as imported
                                                  from models/synapse_models
            block_params (dict, optional): Parameters for neuron populations
            num_input_neurons (int, optional): Sizes of input/output populations A, B and C
            num_hidden_neurons (int, optional): Size of the hidden population H
            hidden_layer_gen_func (class, optional): A class providing connectivity pattern
            cutoff (int, optional): connectivity kernel cutoff of WTAs
            additional_statevars (list, optional): List of additonal
                                                   statevariables which are
                                                   not standard
            monitor (bool, optional): Flag to auto-generate spike and
                                      statemonitors
            debug (bool, optional): Flag to gain additional information
        """
        self.num_input_neurons = num_input_neurons
        self.num_neurons = 3 * int(1.2 * num_input_neurons) + \
            int(1.2 * num_hidden_neurons)
        BuildingBlock.__init__(self, name,
                               neuron_eq_builder,
                               synapse_eq_builder,
                               block_params,
                               debug,
                               monitor)

        self.sub_blocks, self._groups, self.monitors, self.standalone_params = gen_threeway(name,
                                                                          neuron_eq_builder,
                                                                          synapse_eq_builder,
                                                                          num_input_neurons=num_input_neurons,
                                                                          num_hidden_neurons=num_hidden_neurons,
                                                                          hidden_layer_gen_func=hidden_layer_gen_func,
                                                                          additional_statevars=additional_statevars,
                                                                          cutoff=cutoff,
                                                                          spatial_kernel=spatial_kernel,
                                                                          monitor=monitor,
                                                                          debug=debug,
                                                                          block_params=block_params)

        set_TW_tags(self, self._groups)

        # Creating handles for neuron groups and inputs
        self.A = self.sub_blocks['wta_A']
        self.B = self.sub_blocks['wta_B']
        self.C = self.sub_blocks['wta_C']
        self.H = self.sub_blocks['wta_H']

        self.Inp_A = self.A._groups['spike_gen']
        self.Inp_B = self.B._groups['spike_gen']
        self.Inp_C = self.C._groups['spike_gen']

        self.value_a = np.NAN
        self.value_b = np.NAN
        self.value_c = np.NAN

        self.start_time = 0*ms

        self.input_groups.update({'A': self.A._groups['n_exc'],
                                  'B': self.B._groups['n_exc'],
                                  'C': self.C._groups['n_exc']})
        self.output_groups.update({'A': self.A._groups['n_exc'],
                                  'B': self.B._groups['n_exc'],
                                  'C': self.C._groups['n_exc']})
        self.hidden_groups.update({'H': self.H._groups['n_exc']})

    def set_A(self, value):
        """
        Sets spiking rates of neurons of the PoissonGroup Inp_A to satisfy
        a shape of a gaussian bump centered at 'value' between 0 and 1

        Args:
            value (float): a value to be encoded with an activity bump
        """

        self.Inp_A.rates = double2pop_code(value, self.num_input_neurons)
        self.value_a = value

    def set_B(self, value):
        """
        Sets spiking rates of neurons of the PoissonGroup Inp_B to satisfy
        a shape of a gaussian bump centered at 'value' between 0 and 1

        Args:
            value (float): a value to be encoded with an activity bump
        """

        self.Inp_B.rates = double2pop_code(value, self.num_input_neurons)
        self.value_b = value

    def set_C(self, value):
        """
        Sets spiking rates of neurons of the PoissonGroup Inp_C to satisfy
        a shape of a gaussian bump centered at 'value' between 0 and 1

        Args:
            value (float): a value to be encoded with an activity bump
        """

        self.Inp_C.rates = double2pop_code(value, self.num_input_neurons)
        self.value_c = value

    def reset_A(self):
        """
        Resets spiking rates of neurons of the PoissonGroup Inp_A to zero
        (e.g. turns the input A off)
        """
        self.Inp_A.rates = np.zeros(self.num_input_neurons) * Hz
        self.value_a = np.NAN

    def reset_B(self):
        """
        Resets spiking rates of neurons of the PoissonGroup Inp_B to zero
        (e.g. turns the input B off)
        """
        self.Inp_B.rates = np.zeros(self.num_input_neurons) * Hz
        self.value_b = np.NAN

    def reset_C(self):
        """
        Resets spiking rates of neurons of the PoissonGroup Inp_C to zero
        (e.g. turns the input C off)
        """
        self.Inp_C.rates = np.zeros(self.num_input_neurons) * Hz
        self.value_c = np.NAN

    def reset_inputs(self):
        """
        Resets all external inputs of the Threeway block
        """
        self.reset_A()
        self.reset_B()
        self.reset_C()

    def get_values(self, measurement_period=100 * ms):
        """
            Extracts encoded values of A, B and C from the spiking rates of
            the corresponding populations

            Args:
                measurement_period (ms, optional): Sets the interval back from
                current moment in time for the spikes to be included into
                rate calculation
        """

        if self.A.monitor is True and self.B.monitor is True and self.C.monitor is True:
            a = pop_code2double(get_rates(self.monitors['spikemon_A'],
                                          measurement_period=measurement_period))
            b = pop_code2double(get_rates(self.monitors['spikemon_B'],
                                          measurement_period=measurement_period))
            c = pop_code2double(get_rates(self.monitors['spikemon_C'],
                                          measurement_period=measurement_period))
            return a, b, c
        else:
            raise ValueError(
                'Unable to compute population vectors, monitoring has been\
                    turned off!')

    def plot(self):
        """
            Create a rasterplot of spikes of excitatory neurons of populations
            A, B and C
        """
        return plot_threeway_raster(self)

def gen_threeway(name,
                 neuron_eq_builder,
                 synapse_eq_builder,
                 block_params,
                 num_input_neurons,
                 num_hidden_neurons,
                 hidden_layer_gen_func,
                 additional_statevars,
                 cutoff,
                 spatial_kernel,
                 monitor,
                 debug):
    """
        Generator function for a Threeway building block
    """

    # TODO: Replace PoissonGroups as inputs with stimulus generators

    if debug:
        print("Creating WTA's!")

    wta_A = WTA(name + '_wta_A',
                dimensions=1,
                num_inputs=3,
                block_params=block_params,
                num_neurons=num_input_neurons,
                num_inh_neurons=int(0.2*num_input_neurons),
                cutoff=cutoff,
                monitor=True,
                verbose=debug)
    wta_B = WTA(name + '_wta_B',
                dimensions=1,
                num_inputs=3,
                block_params=block_params,
                num_neurons=num_input_neurons,
                num_inh_neurons=int(0.2 * num_input_neurons),
                cutoff=cutoff,
                monitor=True,
                verbose=debug)
    wta_C = WTA(name + '_wta_C',
                dimensions=1,
                num_inputs=3,
                block_params=block_params,
                num_neurons=num_input_neurons,
                num_inh_neurons=int(0.2 * num_input_neurons),
                cutoff=cutoff,
                monitor=True,
                verbose=debug)
    wta_H = WTA(name + '_wta_H',
                dimensions=2,
                num_inputs=3,
                block_params=block_params,
                num_neurons=num_input_neurons,
                num_inh_neurons=int(0.2 * num_hidden_neurons),
                cutoff=cutoff,
                monitor=monitor,
                verbose=debug)

    sub_blocks = {
        'wta_A': wta_A,
        'wta_B': wta_B,
        'wta_C': wta_C,
        'wta_H': wta_H}

    wta_A._groups['spike_gen'] = PoissonGroup(name=name + '_input_group_A',
        N=num_input_neurons, rates=np.zeros(num_input_neurons) * Hz)
    wta_B._groups['spike_gen'] = PoissonGroup(name=name + '_input_group_B',
        N=num_input_neurons, rates=np.zeros(num_input_neurons) * Hz)
    wta_C._groups['spike_gen'] = PoissonGroup(name=name + '_input_group_C',
        N=num_input_neurons, rates=np.zeros(num_input_neurons) * Hz)

    # Creating interpopulation synapse groups
    syn_A_H = Connections(wta_A.groups['n_exc'], wta_H.groups['n_exc'],
                          equation_builder=synapse_eq_builder(),
                          method="euler", name=name + '_s_A_to_H')
    syn_H_A = Connections(wta_H.groups['n_exc'], wta_A.groups['n_exc'],
                          equation_builder=synapse_eq_builder(),
                          method="euler", name=name + '_s_H_to_A')
    syn_B_H = Connections(wta_B.groups['n_exc'], wta_H.groups['n_exc'],
                          equation_builder=synapse_eq_builder(),
                          method="euler", name=name + '_s_B_to_H')
    syn_H_B = Connections(wta_H.groups['n_exc'], wta_B.groups['n_exc'],
                          equation_builder=synapse_eq_builder(),
                          method="euler", name=name + '_s_H_to_B')
    syn_C_H = Connections(wta_C.groups['n_exc'], wta_H.groups['n_exc'],
                          equation_builder=synapse_eq_builder(),
                          method="euler", name=name + '_s_C_to_H')
    syn_H_C = Connections(wta_H.groups['n_exc'], wta_C.groups['n_exc'],
                          equation_builder=synapse_eq_builder(),
                          method="euler", name= name + '_s_H_to_C')

    # Creating input synapse groups
    wta_A._groups['s_inp_exc'] = Connections(wta_A._groups['spike_gen'],
                 wta_A.groups['n_exc'], equation_builder=synapse_eq_builder(),
                            method="euler", name=name + '_s_inp_A')
    wta_B._groups['s_inp_exc'] = Connections(wta_B._groups['spike_gen'],
                 wta_B.groups['n_exc'], equation_builder=synapse_eq_builder(),
                            method="euler", name=name + '_s_inp_B')
    wta_C._groups['s_inp_exc'] = Connections(wta_C._groups['spike_gen'],
                 wta_C.groups['n_exc'], equation_builder=synapse_eq_builder(),
                            method="euler", name=name + '_s_inp_C')

    interPopSynGroups = {
        's_A_to_H' : syn_A_H,
        's_H_to_A' : syn_H_A,
        's_B_to_H' : syn_B_H,
        's_H_to_B' : syn_H_B,
        's_C_to_H' : syn_C_H,
        's_H_to_C' : syn_H_C}

    synGroups = {
            's_inp_A' : wta_A._groups['s_inp_exc'],
            's_inp_B' : wta_B._groups['s_inp_exc'],
            's_inp_C' : wta_C._groups['s_inp_exc']
            }

    for tmp_syn_group in synGroups:
        synGroups[tmp_syn_group].connect('i == j')
        synGroups[tmp_syn_group].weight = wta_A.params['we_inp_exc']

    synGroups.update(interPopSynGroups)

    # Connecting the populations with a given index generation function
    # TODO: add more index generating functions
    index_gen_function = hidden_layer_gen_func

    for tmp_syn_group_name in interPopSynGroups:
        arr_i, arr_j = index_gen_function(
            tmp_syn_group_name[-6], tmp_syn_group_name[-1], num_input_neurons)
        interPopSynGroups[tmp_syn_group_name].connect(i=arr_i, j=arr_j)
        interPopSynGroups[tmp_syn_group_name].weight = wta_A.params['we_inp_exc']

    groups = {}
    groups.update(interPopSynGroups)

    if monitor:
        wta_A.monitors['spikemon_inp'] = SpikeMonitor(
            wta_A._groups['spike_gen'], name='spikemon' + name + '_InpA')
        wta_B.monitors['spikemon_inp'] = SpikeMonitor(
            wta_B._groups['spike_gen'], name='spikemon' + name + '_InpB')
        wta_C.monitors['spikemon_inp'] = SpikeMonitor(
            wta_C._groups['spike_gen'], name='spikemon' + name + '_InpC')

    monitors = {
        'spikemon_InpA': wta_A.monitors['spikemon_inp'],
        'spikemon_InpB': wta_B.monitors['spikemon_inp'],
        'spikemon_InpC': wta_C.monitors['spikemon_inp'],
        'spikemon_A': wta_A.monitors['spikemon_exc'],
        'spikemon_B': wta_B.monitors['spikemon_exc'],
        'spikemon_C': wta_C.monitors['spikemon_exc'],
        'statemon_A': wta_A.monitors['statemon_exc'],
        'statemon_B': wta_B.monitors['statemon_exc'],
        'statemon_C': wta_C.monitors['statemon_exc']}

    # monitors.update(wta_A.monitors, wta_B.monitors, wta_C.monitors, wta_H.monitors)

    standalone_params = {}

    return sub_blocks, groups, monitors, standalone_params

def set_TW_tags(TW_block, _groups):
    '''
    Sets default tags to members of the _groups of the Threeway block

    Args:
        _groups (dictionary): All neuron and synapse groups.

    Returns:
        _groups (dictionary): All neuron and synapse groups with tags appended.
    '''

    TW_block._set_tags(tags_parameters.basic_threeway_1WTA_to_2WTA,
                       _groups['s_A_to_H'])
    TW_block._set_tags(tags_parameters.basic_threeway_1WTA_to_2WTA,
                       _groups['s_B_to_H'])
    TW_block._set_tags(tags_parameters.basic_threeway_1WTA_to_2WTA,
                       _groups['s_C_to_H'])
    TW_block._set_tags(tags_parameters.basic_threeway_2WTA_to_1WTA,
                       _groups['s_H_to_A'])
    TW_block._set_tags(tags_parameters.basic_threeway_2WTA_to_1WTA,
                       _groups['s_H_to_B'])
    TW_block._set_tags(tags_parameters.basic_threeway_2WTA_to_1WTA,
                       _groups['s_H_to_C'])

def plot_threeway_raster(TW):
    """Function to easily visualize Threeway block activity.

    Args:
        TW (Threeway BuildingBlock, required): Threeway block to be visualized

    Returns:
        mainfig : pyqtgraph MainWindow of the plot
        plot_A, plot_B, plot_C : Rasterplot objects of teili visualizer
    """
    app = QtGui.QApplication.instance()
    if app is None:
        app = QtGui.QApplication()
    else:
        print('QApplication instance already exists: %s' % str(app))

    pg.setConfigOptions(antialias=True)

    mainfig = pg.GraphicsWindow(title='Threeway Raster plots')
    mainfig.resize(1200,850)
    subfig1 = mainfig.addPlot(row=0, col=0)
    subfig2 = mainfig.addPlot(row=1, col=0)
    subfig3 = mainfig.addPlot(row=2, col=0)
    subfig1.setXLink(subfig2)
    subfig3.setXLink(subfig1)

    plot_A = Rasterplot([TW.monitors['spikemon_A']],
                        neuron_id_range=(0,TW.A.num_neurons),
                        title="Population A",
                        ylabel='Neuron ID', xlabel='time, s',
                        mainfig=mainfig, subfig_rasterplot=subfig1,
                        backend='pyqtgraph', QtApp=app,
                        show_immediately=False)

    plot_B = Rasterplot([TW.monitors['spikemon_B']],
                        neuron_id_range=(0,TW.B.num_neurons),
                        title="Population B",
                        ylabel='Neuron ID', xlabel='time, s',
                        mainfig=mainfig, subfig_rasterplot=subfig2,
                        backend='pyqtgraph', QtApp=app,
                        show_immediately=False)

    plot_C = Rasterplot([TW.monitors['spikemon_C']],
                        neuron_id_range=(0,TW.C.num_neurons),
                        title="Population C",
                        ylabel='Neuron ID', xlabel='time, s',
                        mainfig=mainfig, subfig_rasterplot=subfig3,
                        backend='pyqtgraph', QtApp=app,
                        show_immediately=True)

    return mainfig

def gaussian(mu, sigma, amplitude, input_size):
    """
        Generate rates based on the gaussian profile
    """
    i = np.arange(input_size)
    shift = mu % 1
    coarse = int(mu - shift)
#    dist = amplitude*np.exp(-np.power((i - shift - int(input_size/2))/input_size, 2.) / (2 * np.power(sigma, 2.)))
    dist = amplitude*np.max([np.exp(-np.power((i - shift -
                            int(input_size/2))/input_size, 2.) /
                            (2 * np.power(sigma, 2.))),
            np.exp(-np.power((i - shift - int(input_size/2))/input_size+1, 2.) /
                       (2 * np.power(sigma, 2.)))], axis = 0)
    return dist[(int(input_size/2) - coarse + i)%input_size]

def double2pop_code(value, input_size, sigma=None, amplitude=100):
    """
        Generate rates based on the gaussian profile
    """
    if sigma is None:
        sigma = 1/input_size

    mu = value*input_size % input_size
    activity = gaussian(mu, sigma, amplitude, input_size)
    return activity * Hz

def pop_code2double(pop_array):
    """Calculate circular mean of an array

    @author: Peter Diehl
    """
    size = len(pop_array)
    complex_unit_roots = np.array(
        [np.exp(1j * (2 * np.pi / size) * cur_pos) for cur_pos in range(size)])
    cur_pos = (np.angle(np.sum(pop_array * complex_unit_roots)) %
               (2 * np.pi)) / (2 * np.pi)
    return cur_pos%1

def get_rates(spikemon, measurement_period=100 * ms):
    """
        Get firing rates of neurons based on most recent activity within
        the measurement period
    """
    rates = np.zeros(len(spikemon.event_trains()))
    rates = [len(spikemon.event_trains()[i][spikemon.event_trains()[i] >
                 spikemon.clock.t - measurement_period]) / measurement_period
             for i in range(len(spikemon.event_trains()))]

    #  if debug and len(spikemon.t):
    #      print('Simulation time', spikemon.t / ms, 'ms')
    return rates

if __name__ == '__main__':

    prefs.codegen.target = "numpy"

    TW = Threeway('TestTW', debug = True)


#Funtion to find genes in abstracts with regex additional to the genes from the Uniprot CBB API
import simplejson as json
import re

def get_regex_genes(articles_doc):
    #Loading the articles from the json file for gene analysis
    articles_doc = json.loads(articles_doc)
    articles_doc = articles_doc
    #splitting the article (for every article) in seperate words and check if the word is a gene or not
    #based on a regular expression.

    for article in articles_doc:

        abstract = article['abstract']
        words = abstract.split()
        for word in words:
            split = split_chars(word)
            for word in split:
                #Checking if a word has untwanted charachters, if they do they'll be removed
                if len(word) > 1 and (word[-1] == "," or word[-1] == "-"):
                    word = word[:-1]
                if len(word) > 1 and word[0] == "-":
                    word = word[1:]
                #Checking if a word meets the demands of being a gene if the word does and it isnt allready in the list of genes from the api_genes
                # it will be added to the list of genes.
                if (hasNumbers(word) or hasLowerUpper(word)) and upperLimit(word) and unwanted(word):
                    api_genes = [x['name'].lower() for x in article['genes']]
                    if word.lower() not in api_genes:
                        genes = article['genes']
                        genes.append({'name': word, 'orthologs': {}, 'eggnog': ''})
    #adding the gense to the json format
    articles_doc = json.dumps(articles_doc)
    #returning the json format of genes
    return articles_doc

#Function to check if a word contains numbers, returns a boolean
def hasNumbers(inputString):
    boolean = False
    if re.search(r'\d', inputString) and (re.search(r'([A-Za-z])', inputString)):
        boolean = True
    return boolean

#Function to check if a word contains lower and upper case characters, returns a boolean
def hasLowerUpper(inputString):
    boolean = False
    if re.search(r'([A-Za-z])+([A-Z])+([a-z])*(.)*', inputString):
        boolean = True
    return boolean

#Function to check when a word contains only uppercase characters it is within the range of 3-5, returns a boolean
def upperLimit(inputString):
    boolean = False
    if inputString.isupper():
        if re.match(r'[A-Z]{3,5}$', inputString):
            boolean = True
    else:
        boolean = True
    return boolean

def split_chars(inputString):
    output = re.findall(r'[\w\',-]+', inputString)
    return output

#Function to check if a word is an unwanted one, returns a boolean
def unwanted(inputString):
    boolean = True
    unwanted = ['induced', 'targeted', 'treated', 'involved', 'pretreated', 'negative', 'positive', 'insensitive',
                'dependent', 'independent', 'involved', 'control','scavenging', 'derived', 'protein', 'related', 'like',
                'mediated', 'related', 'responsive', 'enriched', 'overexpressing', 'governed', 'sensitive', 'absorbing',
                'produced', 'producing', 'associated', 'attributed', 'inflicted', 'ínitiated', 'elicited', 'activated',
                'supplemented', 'supplemental', 'repeat', 'enriched', 'damaging', 'signaling', 'deficient', 'guided']
    words = inputString.split('-')
    for word in words:
        if word.lower() in unwanted:
            boolean = False

    return boolean


# python jarvis.py

import json
import requests
from aiogram import Bot, types, Dispatcher
from aiogram.dispatcher.filters import Text
from pycoingecko import CoinGeckoAPI
from aiogram.utils import executor
from aiogram.types import ReplyKeyboardRemove, \
    ReplyKeyboardMarkup, KeyboardButton, \
    InlineKeyboardMarkup, InlineKeyboardButton
from konf import TOKEN

cg = CoinGeckoAPI()

# цикл для отправки запросов на сайт
def cripta():
    if (cg.status_code == 200):
        cg.encoding = 'utf-8'

btc = (cg.get_price(ids=' bitcoin', vs_currencies='usd'))  # достаем валюту в словарь
btc1 = btc.get('bitcoin')  # достаем значение из словаря - биток
btc2 = btc1.get('usd')

eth = (cg.get_price(ids=' ethereum', vs_currencies='usd'))  # достаем валюту в словарь
eth1 = eth.get('ethereum')  # достаем значение из словаря - эфир
eth2 = eth1.get('usd')

zec = (cg.get_price(ids=' zcash', vs_currencies='usd'))  # достаем валюту в словарь
zec1 = zec.get('zcash')  # достаем значение из словаря - зедкеш
zec2 = zec1.get('usd')

# импорт курсов фиата
r = requests.get('https://www.cbr-xml-daily.ru/daily_json.js')

# обрабатываем Джсон по фиату
def fiat():  # цикл для отправки запросов на сайт
    if (r.status_code == 200):
        r.encoding = 'utf-8'

data = json.loads(r.text)

u = (data['Valute']['USD']['Value'])
e = (data['Valute']['EUR']['Value'])

bot = Bot(token=TOKEN)
dp = Dispatcher(bot)

# кнопки
@dp.message_handler(commands="start")
async def cmd_start(message: types.Message):
    keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True)
    buttons = ["Крипта", "Фиат"]
    keyboard.add(*buttons)
    await message.reply("Какие курсы интересуют?", reply_markup=keyboard)

# from aiogram.dispatcher.filters import Text
@dp.message_handler(Text(equals="Крипта"))
async def crypto(message: types.Message):
    await message.reply('Btc: ' + str(btc2) + ",  " + "Eth: " + str(eth2) + ', ' \
                        + 'Zec: ' + str(zec2))  # вывод цены крипты

@dp.message_handler(Text(equals="Фиат"))
async def fiat(message: types.Message):
    if (r.status_code == 200):
        r.encoding = 'utf-8'
    await message.reply('Usd: ' + str(u) + ",  " + 'Eur: ' + str(e))  # вывод цены фиата

if __name__ == '__main__':
    executor.start_polling(dp)


def execute(
        parameters
):
    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region IMPORTS

    import os
    import time
    import datetime
    import arcpy

    from concurrent.futures import ThreadPoolExecutor
    from concurrent.futures import as_completed

    from scripts import stac
    from scripts import cube
    from scripts import shared

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region EXTRACT PARAMETERS

    # uncomment these when not testing
    in_lyr = parameters[0].valueAsText
    out_nc = parameters[1].valueAsText
    in_start_date = parameters[2].value
    in_end_date = parameters[3].value
    in_collections = parameters[4].valueAsText
    in_band_assets = parameters[5].valueAsText
    in_mask_algorithm = parameters[6].value
    in_quality_flags = parameters[7].valueAsText
    in_max_out_of_bounds = parameters[8].value
    in_max_invalid_pixels = parameters[9].value
    in_keep_mask = parameters[10].value
    in_nodata_value = parameters[11].value
    in_srs = parameters[12].value
    in_res = parameters[13].value
    in_max_threads = parameters[14].value

    # uncomment these when testing
    # in_lyr = r'C:\Users\Lewis\Desktop\arcdea\perth_sa.shp'
    # out_nc = r'C:\Users\Lewis\Desktop\arcdea\s2.nc'
    # in_start_date = datetime.datetime(2023, 1, 1)
    # in_end_date = datetime.datetime.now()
    # in_collections = "'Sentinel 2A';'Sentinel 2B'"
    # in_band_assets = "'Blue';'Green';'Red'"
    # in_mask_algorithm = 'S2Cloudless' #'fMask'  # 'S2Cloudless'
    # in_quality_flags = "Valid"  #"'Valid';'Shadow';'Snow';'Water'"  # "Valid"
    # in_max_out_of_bounds = 10
    # in_max_invalid_pixels = 5
    # in_keep_mask = False
    # in_nodata_value = -999
    # in_srs = 'GDA94 Australia Albers (EPSG: 3577)'  # 'WGS84 (EPSG: 4326)'
    # in_res = 10
    # in_max_threads = None

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region PREPARE ENVIRONMENT

    arcpy.SetProgressor('default', 'Preparing environment...')

    # allow output overwrites
    arcpy.env.overwriteOutput = True

    time.sleep(1)

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region PREPARE PARAMETERS

    arcpy.SetProgressor('default', 'Preparing DEA STAC query parameters...')

    # convert featureclass to bbox and epsg
    fc_bbox = shared.get_bbox_from_featureclass(in_lyr)
    fc_epsg = shared.get_epsg_from_featureclass(in_lyr)

    # convert start and end datetime to date strings
    start_date = in_start_date.date().strftime('%Y-%m-%d')
    end_date = in_end_date.date().strftime('%Y-%m-%d')

    # convert collections and assets to lists
    collections = shared.prepare_collections(in_collections)
    assets = shared.prepare_assets(in_band_assets)

    # convert mask algorithm and flags to list, append to assets
    quality_flags = shared.prepare_quality_flags(in_quality_flags, in_mask_algorithm)
    mask_algorithm = shared.prepare_mask_algorithm(in_mask_algorithm)
    assets = shared.append_mask_band(assets, mask_algorithm)

    # convert max out of bounds, invalid pixel values
    max_out_of_bounds = shared.prepare_max_out_of_bounds(in_max_out_of_bounds)
    max_invalid_pixels = shared.prepare_max_invalid_pixels(in_max_invalid_pixels)

    # convert nodata, epsg and resolution to numerics
    out_nodata = in_nodata_value
    out_epsg = shared.prepare_spatial_reference(in_srs)
    out_res = shared.prepare_resolution(in_res, out_epsg)

    # set output dtype (always int16 for baseline)
    out_dtype = 'int16'

    # validate num threads, if none will use num of cores - 1
    max_threads = shared.prepare_max_threads(in_max_threads)

    time.sleep(1)

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region QUERY STAC ENDPOINT

    arcpy.SetProgressor('default', 'Querying DEA STAC endpoint...')

    try:
        # reproject stac bbox to wgs 1984, fetch all available stac items
        stac_bbox = shared.reproject_bbox(fc_bbox, fc_epsg, 4326)
        stac_features = stac.fetch_all_stac_feats(collections,
                                                  start_date,
                                                  end_date,
                                                  stac_bbox,
                                                  100)
    except Exception as e:
        arcpy.AddError('Error occurred during DEA STAC query. See messages.')
        arcpy.AddMessage(str(e))
        return

    # abort if no stac features found
    if len(stac_features) == 0:
        arcpy.AddWarning('No STAC features were found.')
        return

    time.sleep(1)

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region PREPARING STAC FEATURES

    arcpy.SetProgressor('default', 'Preparing STAC downloads...')

    root_folder = os.path.dirname(out_nc)
    tmp_folder = os.path.join(root_folder, 'tmp')

    shared.drop_temp_folder(tmp_folder)
    shared.create_temp_folder(tmp_folder)

    try:
        # reproject output bbox to requested, convert stac to download objects
        out_bbox = shared.reproject_bbox(fc_bbox, fc_epsg, out_epsg)
        stac_downloads = stac.convert_stac_feats_to_stac_downloads(stac_features,
                                                                   assets,
                                                                   mask_algorithm,
                                                                   quality_flags,
                                                                   max_out_of_bounds,
                                                                   max_invalid_pixels,
                                                                   out_bbox,
                                                                   out_epsg,
                                                                   out_res,
                                                                   out_nodata,
                                                                   out_dtype,
                                                                   tmp_folder)

    except Exception as e:
        arcpy.AddError('Error occurred during STAC download preparation. See messages.')
        arcpy.AddMessage(str(e))
        return

    stac_downloads = stac.group_stac_downloads_by_solar_day(stac_downloads)

    if len(stac_downloads) == 0:
        arcpy.AddWarning('No valid downloads were found.')
        return

    time.sleep(1)

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region DOWNLOAD WCS MASK DATA

    arcpy.SetProgressor('step', 'Downloading and validating mask data...', 0, len(stac_downloads), 1)

    try:
        i = 0
        with ThreadPoolExecutor(max_workers=max_threads) as pool:
            futures = []
            for stac_download in stac_downloads:
                task = pool.submit(cube.worker_read_mask_and_validate, stac_download)
                futures.append(task)

            for future in as_completed(futures):
                arcpy.AddMessage(future.result())

                i += 1
                if i % 1 == 0:
                    arcpy.SetProgressorPosition(i)

    except Exception as e:
        arcpy.AddError('Error occurred while downloading and validating mask data. See messages.')
        arcpy.AddMessage(str(e))
        return

    stac_downloads = cube.remove_mask_invalid_downloads(stac_downloads)

    if len(stac_downloads) == 0:
        arcpy.AddWarning('No valid downloads were found.')
        return

    time.sleep(1)

    arcpy.ResetProgressor()

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region DOWNLOAD WCS VALID DATA

    arcpy.SetProgressor('step', 'Downloading valid data...', 0, len(stac_downloads), 1)

    try:
        i = 0
        with ThreadPoolExecutor(max_workers=max_threads) as pool:
            futures = []
            for download in stac_downloads:
                task = pool.submit(cube.worker_read_bands_and_export, download)
                futures.append(task)

            for future in as_completed(futures):
                arcpy.AddMessage(future.result())

                i += 1
                if i % 1 == 0:
                    arcpy.SetProgressorPosition(i)

    except Exception as e:
        arcpy.AddError('Error occurred while downloading valid data. See messages.')
        arcpy.AddMessage(str(e))
        return

    time.sleep(1)

    arcpy.ResetProgressor()

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region CLEAN AND COMBINE NETCDFS

    arcpy.SetProgressor('default', 'Cleaning and combining NetCDFs...')

    try:
        ds = cube.fix_xr_meta_and_combine(stac_downloads)

    except Exception as e:
        arcpy.AddError('Error occurred while cleaning and combining NetCDFs. See messages.')
        arcpy.AddMessage(str(e))
        return

    time.sleep(1)

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region MASK OUT INVALID NETCDF PIXELS

    arcpy.SetProgressor('default', 'Masking out invalid NetCDF pixels...')

    try:
        ds = cube.apply_xr_mask(ds,
                                quality_flags,
                                out_nodata,
                                in_keep_mask)

    except Exception as e:
        arcpy.AddError('Error occurred while masking out invalid NetCDF pixels. See messages.')
        arcpy.AddMessage(str(e))
        return

    time.sleep(1)

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region EXPORT COMBINED NETCDF

    arcpy.SetProgressor('default', 'Exporting combined NetCDF...')

    try:
        cube.export_xr_to_nc(ds, out_nc)

    except Exception as e:
        arcpy.AddError('Error occurred while exporting combined NetCDF. See messages.')
        arcpy.AddMessage(str(e))
        return

    time.sleep(1)

    # endregion

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # region CLEAN UP ENVIRONMENT

    arcpy.SetProgressor('default', 'Cleaning up environment...')

    cube.safe_close_ncs(tmp_folder)  # TODO: surely there is better way
    shared.drop_temp_folder(tmp_folder)

    time.sleep(1)

    # endregion

#execute(None)


import cv2
import numpy as np
#can also check this https://www.murtazahassan.com/learn-opencv-in-3-hours-chapter-2/

#We will learn how to add different effects to images in OpenCV

#We need to read an image
img = cv2.imread("resources/lena.png")

#add matrix there are 5x5 cells and the numbers are from 0 to 255
kernel = np.ones((5,5),np.uint8)

#convert the image into
#Grayscale
imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#Blur it takes image the scale (has to be odd numbers) and sigmaX
imgBlur = cv2.GaussianBlur(img,(7,7),0)
#Canny finds edges in image add threshold
imgCanny = cv2.Canny(img,200,150)
#dilation better defies the borders of the canny image
# we need a matrix so install numpy for this step
# iterations is the thickness
imgDilated = cv2.dilate(imgCanny,kernel,iterations=1)
#eroded is the oppesite of Dilate it gives smaller lines
imgEroded = cv2.erode(imgDilated,kernel,iterations=1)

cv2.imshow("Gray image",imgGray)
cv2.imshow("Blur image",imgBlur)
cv2.imshow("Canny image",imgCanny)
cv2.imshow("Dilated image",imgDilated)
cv2.imshow("Eroded image",imgEroded)

cv2.waitKey(0)


from __future__ import absolute_import, print_function
from .version import __version__

import time
import string
import sys
import os
import json

from ast import literal_eval
from appdirs import user_config_dir
from functools import partial

from .func_sigs import map_args_to_func_sig, repl_func_defaults
from .func_sigs import flatten_dotted_arg_from_dict
from .casting import funcname, cast, t_attr, t_funcs
from .casting import is_cls, attr

from devapps import common

nil = common.nil
Exc = common.Exc
throw = common.throw
is_str = common.is_str
breakpoint = common.breakpoint
from .common import set_log, info, debug, warn, error, get_structlogger

odict = dict
if common.PY2:
    from .casting import recursive_to_new_style
    from collections import OrderedDict

    # unfortuntatelly in py2 dicts are not insertion ordered.
    # we rely on this, .e.g in pre_parse_cli:
    odict = OrderedDict

# -------------------------------------------------------- Base Config Provider
class Provider(object):
    # fmt: off
    str_only             = False
    allow_short_keys     = False
    set_runner_func      = False
    allow_unknown_attrs  = True
    show_help            = None
    # fmt: on

    def get_inner(cli, d, path, attrs, cfg):
        return d.get(path[-1])

# -------------------------------------------------------------- Key Shortening

def shorten(k):
    """
    k an attrname like foo_bar_baz.
    We return fbb, fbbaz, which we'll match startswith style if exact match
    not unique.
    """
    parts = k.split('_')
    r = ''.join([s[0] for s in parts if s])
    return r, r + parts[-1][1:]

def to_shorts(longs, shorten=shorten):
    """build a dict of short forms pointing to their original forms

    Collisions: We'll complain for foo_bar_baz and foo_baz_baz attrs
    and don't try to be smart here. Thats why we need the 'have' list.
    """
    m = odict()
    have = set()
    for k in longs:
        m[k] = k  # allowed match stsarts with also ok
        sh, sh_ending = shorten(k)
        if sh in have:
            # thats ok, colliding shorts
            m.pop(sh)
        else:
            m[sh] = k
            have.add(sh)
            if sh == sh_ending:
                # thats ok:
                continue
        if sh_ending in have:
            # developer has to rename the attribute if he wants the feature
            throw(
                Exc.cannot_build_unique_short_form,
                key=k,
                colliding=sh_ending,
                have=have,
            )
        m[sh_ending] = k
        have.add(sh_ending)
    return m

def short_to_long(provider, d, attrs, ctx):
    shorts = ctx.get('shorts')
    if not shorts:
        shorts = ctx['shorts'] = to_shorts([l[0] for l in attrs if l])
    nd = odict()
    orig_shorts = {}
    for k in d:
        if k in ('', '/'):
            # a "key" started with a '.' or '/' -> MUST be a positional arg and not a
            # key:
            k = flatten_dotted_arg_from_dict(d[k])
            nd[k] = 'is_set'
            continue

        try:
            # nd[shorts[k]] = d[k]
            h = shorts[k]
        except:
            h = [p for p in shorts if p.startswith(k)]
            if len(h) > 1:
                throw(Exc.non_unique, key=k, have=h)
            if len(h) == 1:
                h = h[0]
                # nd[shorts[h[0]]] = d[k]
            else:
                h = k
        nd[h] = d[k]
        orig_shorts[h] = k
    nd['_orig_shorts_'] = orig_shorts
    return nd

# ------------------------------------------------------------------------- CLI

def cli_prov(App, argv=None):
    if argv is None:
        argv = sys.argv[1:]
    cli = CLI(argv, help_switch=getattr(App, '_cli_help_switch', '-h'))
    return cli

@attr.s
class CLI(Provider):
    # fmt: off
    argvd                  = attr.ib(type= odict, converter = lambda x: CLI.pre_parse_cli(x))
    switches               = attr.ib(default={}, validator = lambda *x: CLI.set_switches(*x))
    allow_short_keys       = attr.ib(True)
    set_runner_func        = attr.ib(True)
    bool_reverse_on_no_val = attr.ib(True)
    str_only               = attr.ib(True)
    allow_unknown_attrs    = attr.ib(False)
    help_switch            = attr.ib('-h')
    # fmt: on

    @staticmethod
    def pre_parse_cli(argv):
        """just take the command line appart, w/o knowledge of app vars yet
        What we do though is to build nested dicts for f.b=bar style vars
        """
        r, _into = odict(), CLI.into
        idx, leng = -1, len(argv) - 1
        while idx < leng:
            idx += 1
            arg = argv[idx]
            if '=' in arg:
                k, v = arg.split('=', 1)
                _into(r, k, v)
                continue
            if idx == leng:
                if arg.startswith('-') and len(arg) > 2:
                    # -hhhc -> hhhc
                    _into(r, arg[:2], arg[1:])
                else:
                    _into(r, arg, 'is_set')
            elif argv[idx + 1].startswith('-') or '=' in argv[idx + 1]:
                # next one starts with - or is a key=val assignment -> this one is
                # bool:
                _into(r, arg, 'is_set')
            elif arg.startswith('-'):
                # app -c foo
                idx += 1
                _into(r, arg, argv[idx])
            else:
                # app 1 2
                _into(r, arg, 'is_set')

        return r

    @staticmethod
    def into(m, k, v):
        l = k.split('.')
        if l[0] in ('', '/'):
            # is a positional arg:
            m[k] = 'is_set'
            return
        # just a float, not a path 1.23:
        if l[0].isdigit():
            m[k] = v
            return

        for p in l[:-1]:
            m = m.setdefault(p, {})
        m[l[-1]] = v

    @staticmethod
    def set_switches(cli, attr, switches):
        if not switches:
            return
        # must preserve the order of original, can't just replace:
        d = odict()
        for k, v in cli.argvd.items():
            if not k.startswith('-'):
                d[k] = v
            else:
                try:
                    d[switches[k]] = v
                except:
                    throw(Exc.not_a_switch, found=k, known=switches)
        cli.argvd = d

    # @staticmethod
    # def is_switch(_, __, s):
    #    ''' not in use '''
    #    if not s in string.ascii_letters:
    #        throw(Exc.not_a_switch, s)

    def cfg(cli):
        hs = cli.argvd.pop(cli.help_switch, None)
        if hs is not None:
            cli.show_help = hs
        return cli.argvd

# --------------------------------------------------------------------- Environ

def conv_str(v):
    if v and v[0] in ('[', '(', '{'):
        try:
            return literal_eval(v)
        except:
            return v
    return v

@attr.s
class Env(Provider):
    prefix = attr.ib(default='', type=str, converter=lambda p: (p + '_'))
    str_only = attr.ib(True)

    def build_env_dict(env):
        e = os.environ
        l = len(env.prefix)
        return odict(
            [(k[l:], conv_str(e[k])) for k in e if k.startswith(env.prefix)]
        )

    def cfg(env):
        return env.build_env_dict()

    def get_inner(env, d, path, attrs, cfg):
        p = path[-1] + '_'
        l = len(p)
        d = odict([(k[l:], v) for k, v in d.items() if k.startswith(p)])
        return d

# ------------------------------------------------------------------------ File

@attr.s
class File(Provider):
    filename = attr.ib(
        default='', type=str, validator=lambda self, attribs, v: self.load(v)
    )
    filetype = attr.ib(default='json')
    ignore_missing = attr.ib(default=None)
    _cfg = attr.ib(default={})

    def cfg(file):
        return file._cfg

    def load(file, fn):
        if not fn:
            return
        die = False
        if is_str(fn):
            die = True
        else:
            # app class given:
            fn = fn.__name__ + '.' + file.filetype
        if file.ignore_missing != None:
            die = not file.ignore_missing
        try:
            if not os.path.exists(fn):
                fn = os.path.join(user_config_dir(), fn)
            fn = os.path.abspath(fn)
            file.filename = fn
            with open(fn) as fd:
                s = fd.read()
            file._cfg = json.loads(s)
        except Exception as ex:
            msg = Exc.file_not_found
            args = odict(exc=ex, fn=fn)
            if die:
                throw(msg, **args)
            debug(msg, **args)

# --------------------------------------------------------- Programmed Defaults

@attr.s
class Defaults:
    cls = attr.ib()
    cfg = {}

have_func = '\x04'

def was_a_dotted_positional_arg(cfg_val, key, prov):
    """
    This handles a special case:
    class App:
        def do_run(app, some_arg_with_dotted_value): pass
    If the `some_arg_with_dotted_value` is given positionally (supported for
    func params), .e.g. foo.bar.baz then we'll CLI preparse it into:
    {foo: {'bar': 'is_set'}} - and foo might be even considered a do_foo func.
    """
    was = flatten_dotted_arg_from_dict(cfg_val)
    if not was:
        return
    was = '%s.%s' % (key, was)
    m = {}
    for k, v in prov[1].items():
        if k == key:
            k, v = was, 'is_set'
        m[k] = v
    prov[1] = m
    return True

def walk_attrs(cls, providers, ctx):
    """
    here we burn startup time - recursively scanning all(!) cls attrs of the app.
    have to see if we should cache the scan results.

    """
    path = cls._path

    # first we build all infos about the attrs within our class:
    # (value, type, isit a (do) function):
    def nfo(cls, k):
        v = getattr(cls, k)
        t = type(v)
        ft = t in t_funcs
        if ft:
            if not k.startswith('do_'):
                return
            k = k[3:]
        return k, v, t, ft

    attrs = [nfo(cls, k) for k in dir(cls) if not k.startswith('_')]

    # now get fetch config from all providers regrding this class nesting
    # level:
    if path:
        providers = list(
            filter(
                lambda p: p[1],
                [
                    [p, p.get_inner(cfg, path, attrs, ctx), str_only]
                    for p, cfg, str_only in providers
                ],
            )
        )
    else:
        # first level:
        h = [l for l in [p[0].show_help for p in providers] if l is not None]
        if h:
            ctx['show_help'] = h[0]
        ctx['funcs'] = {}
    sh_help = ctx.get('show_help')

    # now the shorts matching:
    ctx['shorts'] = None
    # if path == ('inner', 'deep'):
    #    breakpoint()
    providers = [
        p
        if not p[0].allow_short_keys
        else [p[0], short_to_long(p, p[1], attrs, ctx), p[1]]
        for p in providers
    ]

    func, have_attrs = None, set()

    # now the replacement run over all attrs - and the casting:
    # if the value is a nested class we recurse:
    for l in attrs:
        if not l:
            continue
        # key value, type, is function_type of the original class:
        k, v, t, ft = l
        v_orig = v

        have_attrs.add(k)
        # if k == 'b_dflt_False':
        #    breakpoint()
        have_cfg = False
        from_prov = None
        for p in providers:
            cfg_val = p[1].get(k, nil)
            if cfg_val != nil:
                have_cfg = True
                str_only = p[2]
                # e.g. CLI:
                from_prov = p[0].__class__.__name__
                break

        if t != type:
            # an instance. Like: some_bool=True
            # or a function type?
            if ft:
                if have_cfg:
                    if isinstance(cfg_val, dict):
                        _ = p[0].__class__.__name__
                        # uh-oh: A positionally given dotted arg collides with
                        # a do function name?
                        if was_a_dotted_positional_arg(cfg_val, k, p):
                            have_attrs.remove(k)
                            have_cfg = False
                            continue
                        repl_func_defaults(func=v, dflts=cfg_val, provider=_)
                    # (else we just have 'is_set' if its the function req. to
                    # run - then we do not change defaults)
                    if p[0].set_runner_func:
                        # might be nested in cli dict, i.e. earlier attrs still to come
                        func = [path, v]
                continue

            if t == t_attr:
                if have_cfg:
                    typ = v.type or type(v._default)
                    v._default = cast(cfg_val, typ) if str_only else cfg_val
                    # already an attr.ib:
            else:
                if have_cfg:
                    v = cast(cfg_val, t) if str_only else cfg_val
                v = attr.ib(v)
        else:
            # a typ - no value. E.g. some_bool = bool
            caster = cast.get(v)
            if caster:
                if not have_cfg:
                    if sh_help:
                        # values not required then:
                        cfg_val = v.__name__
                        caster = lambda s, v, ctx: 'req:<%s>' % s
                    else:
                        throw(Exc.require_value, key=k)
                v = attr.ib(caster(cfg_val, v, ctx) if str_only else cfg_val)
            else:
                v._path = path + (k,)
                v, inner_func = walk_attrs(v, providers, ctx)
                if inner_func and func:
                    throw(
                        Exc.double_func_call,
                        func1=funcname(func1),
                        func2=funcname(inner_func),
                    )
                if not func and inner_func:
                    func = inner_func

                # v = attr.ib(factory=lambda cls=v: cls())
                v = attr.ib(factory=lambda cls=v: cls())

        v.metadata['orig'] = v_orig
        v.metadata['provider'] = from_prov
        setattr(cls, k, v)

    for p in providers:
        # cli is a provider which sets the runner func:
        p[1].pop('_orig_shorts_', 0)
        if not path:
            # first level:
            h = ctx.get('show_help')
            if h is not None:
                return (attr.s(cls), ((), (show_help, (), {'level': h})))

            if p[0].set_runner_func:
                if not func:
                    dflt_func = getattr(cls, 'do_run', None)
                    if not dflt_func:
                        throw(Exc.cannot_determine_function)
                    func = [(), dflt_func]
                params = [
                    (k, v) for k, v in p[1].items() if not k in have_attrs
                ]
                path, func = func
                args = map_args_to_func_sig(func, params, map_from=1, ctx=ctx)
                return attr.s(cls), (path, (func,) + args)

        # now find unknown kvs at deeper levels:
        if not p[0].allow_unknown_attrs:
            unknown = [k for k in p[1] if not k in have_attrs]
            if unknown:
                throw(Exc.unmatched, unknown=unknown)

    return attr.s(cls), func

def show_help(app, level=None, h=None):
    from .help import render_help

    return render_help(app, level, h)

def inner(app, *pth):
    root = app
    app._root = app
    for p in pth:
        app = getattr(app, p)
    app._root = root
    return app

def err_log_wrapped(func, log, app):
    try:
        return func(app)(*a, **kw)
    except Exception as ex:
        msg = ex.args
        # we might check here if the app already logged (e.g. check last
        # msg)
        throw(ex.__class__.__name__, msg=msg)

def get_func(pth, func_with_args, log_err, log, app, *a, **kw):
    obj = inner(app, *pth)
    func, args, kwg = func_with_args
    args += a
    kwg.update(kw)
    return func(obj, *args, **kwg)

def root(obj):
    return obj._root

def parent(obj):
    r = root_ = root(obj)
    for p in obj._path[:-1]:
        r = getattr(r, p)
        r._root = root_
    return r

def as_dict(obj):
    return attr.asdict(obj)

def configure(
    App, providers=None, req_args_complete=False, log_err=False, log=None
):
    if common.PY2:
        recursive_to_new_style(App)

    set_log(log)
    if not providers:
        # take reasonable defaults
        n = App.__name__
        providers = (cli_prov(App), Env(n), File(App))
    if not isinstance(providers, (tuple, list)):
        providers = [providers]
    App._path = ()
    ctx = {}
    ctx['req_args_complete'] = req_args_complete
    App, pth_func_with_args = walk_attrs(
        App, [[p, p.cfg(), p.str_only] for p in providers], ctx
    )
    # pdt(t0)
    if pth_func_with_args:
        pth, func_with_args = pth_func_with_args
        func = partial(get_func, pth, func_with_args, log_err, log)
    else:
        func = None
    return App, func


import os
from sklearn.svm import SVC
import numpy as np
from sklearn.externals import joblib
import pyaudio
import time
import threading
import random
import string
import wave
from scipy.io.wavfile import write

clf = joblib.load('svm.pkl.cmp')

samplingrate = 8192
monitorrate = 16#0.1sに1回くらいcheckしよう
CHUNK = 8192

p = pyaudio.PyAudio()

print("monitor start")

stream = p.open(
    format = pyaudio.paInt16,
    channels = 1,
    rate = samplingrate,
    input = True,
    frames_per_buffer = CHUNK
)
CHUNK = 512
s = 1.0
RATE = 8192
Hz = RATE
CHUNK = int(RATE*s)
def randomname():
    randlist = [random.choice(string.ascii_letters + string.digits) for i in range(12)]
    return ''.join(randlist)

def worker():
    receive = stream.read(CHUNK+CHUNK//16)
    ret = np.frombuffer(receive, dtype = "int16")[CHUNK//16-1:-1]
    data = np.fft.fft(np.hamming(len(ret))*ret, norm = "ortho")
    data = data[0:CHUNK//2]
    data = np.array([np.abs(data)])
    if clf.predict(data) == [1]:
        print("pashagai emerged!{}".format(random.randint(0, 9)))
        path = os.getcwd() + "/trainincorrect"
        files = os.listdir(path)
        count = len(files)
        fname = randomname()
        np.save(path+ "/" + fname + ".npy", data)
        print(len(data))
        write(path+ "/" + fname + ".wav",RATE,ret)
        print("The data was recorded as incorrect data as {}".format(fname))
    #else:
    #    print("0")
def scheduler(interval, f, wait = True):
    base_time = time.time()
    next_time = 0
    while True:
        t = threading.Thread(target = f)
        t.start()
        if wait:
            t.join()
        next_time = ((base_time - time.time()) % interval) or interval
        time.sleep(next_time)

scheduler(1/4, worker, False)


import azure.functions as func
import logging
import datetime

# from flask import Flask, request, Response, redirect, url_for

app = func.FunctionApp()

# Learn more at aka.ms/pythonprogrammingmodel
# Get started by running the following code to create a function using a HTTP trigger.
# @app.function_name(name="HttpTrigger1")
# @app.route()
# def test_function(req: func.HttpRequest) -> func.HttpResponse:
#     return "Foo"

@app.function_name(name="mytimer")
@app.schedule(schedule="0 */5 * * * *", arg_name="mytimer", run_on_startup=True,
              use_monitor=False)
def test_function(mytimer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')

    logging.info('Python timer trigger function ran at %s', utc_timestamp)

@app.route(route="hello")
def test_function1(req):
    return "Hello"


from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase

from marketapp.models import (
    Category,
    Product,
    Feature,
    FeatureValue,
)
from utils.tests import (
    BaseAPITestCaseMixin,
)

User = get_user_model()

class TestCategoriesAPI(BaseAPITestCaseMixin, APITestCase):
    """
    Класс для тестирования API списка свойств категории или продукта
    """

    category_features_urn = '/api/v1/categories/{}/features/'
    product_features_urn = '/api/v1/products/{}/features/'

    @classmethod
    def setUpTestData(cls):
        cls.user_admin = cls.create_admin()
        cls.user1 = User.objects.create(username='testuser')

        # Создаем разные категории
        categories = Category.objects.bulk_create([
            Category(name=f'cat{i}') for i in range(0, 3)
        ])
        cls.category1: Category = categories[0]
        cls.category2: Category = categories[1]

        # Создаем свойства
        features = Feature.objects.bulk_create(
            [Feature(name=f'feature{i}') for i in range(0, 3)]
        )
        cls.feature1: Feature = features[0]
        cls.feature2: Feature = features[1]

        # Создаем значения свойств
        featurevalues = FeatureValue.objects.bulk_create(
            [FeatureValue(name=f'val1{i}', feature=cls.feature1) for i in range(0, 3)]
        )
        cls.featurevalue1: FeatureValue = featurevalues[0]
        featurevalues = FeatureValue.objects.bulk_create(
            [FeatureValue(name=f'val2{i}', feature=cls.feature2) for i in range(0, 3)]
        )
        cls.featurevalue2: FeatureValue = featurevalues[0]

        # Создаем разные продукты и присоединяем их к категориям
        products = Product.objects.bulk_create([
            Product(name=f'prod1{i}', user=cls.user1) for i in range(0, 3)
        ])
        cls.product1: Product = products[0]
        for product in products:
            product.category_set.add(cls.category1)
            product.feature_set.add(cls.featurevalue1)

        # Еще продукты
        products += Product.objects.bulk_create([
            Product(name=f'prod2{i}', user=cls.user1) for i in range(0, 3)
        ])
        cls.product2: Product = products[0]
        for product in products:
            product.category_set.add(cls.category1)
            product.category_set.add(cls.category2)
            product.feature_set.add(cls.featurevalue1)
            product.feature_set.add(cls.featurevalue2)

    def test_get_list_products_features(self):
        """
        Тест получения списка свойств для продукта.
        """

        self.client.logout()
        for product in (self.product1, self.product2, ):
            product_features_urn = self.product_features_urn.format(product.uuid)
            response = self.client.get(product_features_urn)
            self.assertEqual(response.status_code, status.HTTP_200_OK)

            # Проверяем просто по количеству элементов
            self.assertEqual(response.data['count'], product.feature_set.count())
            queryset = product.feature_set.all()[0].feature.featurevalue_set.filter(
                productfeaturem2m__product__uuid=product.uuid
            )
            self.assertEqual(
                len(response.data['results'][0]['featurevalue_set']),
                queryset.count(),
            )

    def test_get_list_categories_features(self):
        """
        Тест получения списка свойств для категории.
        Пересечение свойств всех продуктов.
        """

        self.client.logout()
        category_features_urn = self.category_features_urn.format(self.category1.uuid)
        response = self.client.get(category_features_urn)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertNotEqual(response.data['count'], 0)

        # Проверяем просто по количеству элементов
        queryset = Feature.objects.filter(
            featurevalue__productfeaturem2m__product__productcategorym2m__category__uuid=self.category1.uuid
        ).distinct()
        self.assertEqual(response.data['count'], queryset.count())
        for i, obj in enumerate(queryset):
            queryset_ = FeatureValue.objects.filter(
                feature=obj,
                productfeaturem2m__product__productcategorym2m__category__uuid=self.category1.uuid
            ).distinct()
            self.assertEqual(
                len(response.data['results'][i]['featurevalue_set']),
                queryset_.count()
            )


import json
import os

import apiai
import telebot

bot_token = os.environ['ROWER_BOT_TOKEN']
dialogflow_token = os.environ['DIALOGFLOW_TOKEN']
bot = telebot.TeleBot(bot_token)
#
# trigger_words = ['@rozzercatspamerbot', 'rowerbot', 'бот', 'кот', 'пиво', 'привет', 'пока', 'дом', 'дим', 'греб',
#                  'галера', 'эй ', 'говно', 'обед', 'еда', 'гор', 'кур', 'ху', 'жен', 'нет']

@bot.message_handler(content_types=['text'])
def get_text_messages(message):
    # if message.chat.type == 'supergroup':
    #     # if in_one_of_trigger_words(message.text):
    #         bot_say(message)
    # else:
    bot_say(message)

# def in_one_of_trigger_words(text):
#     for trigger_word in trigger_words:
#         if trigger_word in text.lower():
#             return True
#     return False

def bot_say(message):
    request = apiai.ApiAI(dialogflow_token).text_request()
    request.lang = 'ru'
    request.session_id = 'BatlabAIBot'
    request.query = message.text
    responseJson = json.loads(request.getresponse().read().decode('utf-8'))
    response = responseJson['result']['fulfillment']['speech']
    if response:
        bot.send_message(chat_id=message.chat.id, text=response)
    else:
        bot.send_message(chat_id=message.chat.id, text='Я Вас не совсем понял!')

bot.polling(none_stop=True, interval=0)


"""
Given the root to a binary tree, implement serialize(root), which serializes
the tree into a string, and deserialize(s), which deserializes the string
back into the tree.
"""

import json

class TreeNode(object):
    def __init__(self, value, left=None, right=None):
        """Create a new tree node

        :param value: value to store in the node
        :param left: left TreeNode. Default `None`
        :param right: right TreeNode. Default `None`

        """
        self.value = value
        self.left = left
        self.right = right

    def __repr__(self):
        """Return a string representation of `self`"""
        return '\nValue: {}\nLeft: [{}]\nRight: [{}]'.format(
            self.value, self.left, self.right)

def get_left_index(index):
    """Return index of this index's left child

    :param index: `int` index of the node to get left child's index for

    """
    return index * 2

def get_right_index(index):
    """Return index of this index's right child

    :param index: `int` index of the node to get right child's index for

    """
    return index * 2 + 1

def serialize(node, index=1, tree_dict=None):
    """Return a serialized tree

    We serialize a tree by keying a node's value by its index in the "array
    representaion" the tree, then recursively adding its children at their
    indices, if they exist.

    After all nodes have been added to the `tree_dict`, the result is
    serialized as JSON to get a string representation and returned.

    :param node: `TreeNode` to add to the `tree_dict`
    :param index: `int` index of node, used to create key and calculate
        indices of children. Defaults to 1 (the root)
    :param tree_dict: `dict` of tree, passed to child serializer function calls

    """
    # Short-circuit if no node to add to the tree
    if node is None:
        return

    # Create the `tree_dict` if needed
    tree_dict = tree_dict or {}

    # Add the node value to the `tree_dict`
    tree_dict[index] = node.value

    # Add the node's children to the tree
    serialize(node.left, get_left_index(index), tree_dict)
    serialize(node.right, get_right_index(index), tree_dict)

    # Serialize if at the root
    return json.dumps(tree_dict) if index == 1 else tree_dict

def deserialize(tree, index=1):
    """Return a deserialized tree

    We ingest a serialized version of the tree `dict` representaion,
    deserialize it, then recurseivly create its sub-trees, turning them back
    into nodes.

    :param tree: `str` or `dict` representation of the tree
    :param index: `int` index of the node being deserialized.
        Defaults to 1 (the root)

    """
    # Deserialize the tree if needed
    if type(tree) == str:
        tree = json.loads(tree)

    # Get this node's value
    value = tree.get(str(index))

    # Return `None` if no node is present
    if value is None:
        return None

    # Calculate the indices for this node's children
    left_index = get_left_index(index)
    right_index = get_right_index(index)

    # Create children recursively
    left_node = deserialize(tree, left_index)
    right_node = deserialize(tree, right_index)

    # Return a new node
    return TreeNode(value, left_node, right_node)

if __name__ == '__main__':
    # Bulid the test tree
    left_left = TreeNode('four')
    left_right = TreeNode('five')
    right_left = TreeNode('six')
    right_right = TreeNode('seven')
    left = TreeNode('two', left_left, left_right)
    right = TreeNode('three', right_left, right_right)
    root = TreeNode('one', left, right)

    # Serialize the tree
    serialized_tree = serialize(root)

    # Print the serialized and re-serialized tree, to show deserializing works
    print(serialized_tree)
    print(serialize(deserialize(serialized_tree)))
    print(deserialize(serialized_tree))


import tempfile
import os

# PIL is the Pillow library.
from PIL import Image

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

from rest_framework import status
from rest_framework.test import APIClient

from core.models import Recipe, Tag, Ingredient

from recipe.serializers import RecipeSerializer, RecipeDetailSerializer

RECIPES_URL = reverse('recipe:recipe-list')
# /api/recipe/recipes  What the RECIPES_URL might look like

def image_upload_url(recipe_id):
    # Return URL for recipe image upload
    return reverse('recipe:recipe-upload-image', args=[recipe_id])

def detail_url(recipe_id):
    # return recipe detail URL
    # /api/recipe/recipes/2/ The 2 might be what is passed into the recipe_id
    return reverse('recipe:recipe-detail', args=[recipe_id])

def sample_tag(user, name='Main Course'):
    # Create and return a sample tag
    return Tag.objects.create(user=user, name=name)

def sample_ingredient(user, name='Vanilla'):
    # Create and return a sample ingredient
    return Ingredient.objects.create(user=user, name=name)

def sample_recipe(user, **params):
    # Create and return a sample recipe
    defaults = {
        'title': 'Sample Recipe',
        'time_minutes': 10,
        'price': 5.00
    }
    # Python's update function accepts a dictionary object and will take
    # whichever keys are in the dictionary and update them. If they don't
    # exist, it will create them.
    defaults.update(params)

    return Recipe.objects.create(user=user, **defaults)

class PublicRecipeApiTests(TestCase):
    # Test unauthenticated API access

    def setUp(self):
        self.client = APIClient()

    def test_auth_required(self):
        # Test that authentication is required
        res = self.client.get(RECIPES_URL)

        self.assertEqual(res.status_code, status.HTTP_401_UNAUTHORIZED)

class PrivateRecipeApiTests(TestCase):
    # Test unauthenticated recipe API access

    def setUp(self):
        self.client = APIClient()
        self.user = get_user_model().objects.create_user(
            'test@test.com',
            'testpass'
        )
        self.client.force_authenticate(self.user)

    def test_retrieve_recipes(self):
        # Test retrieving a list of recipes
        sample_recipe(user=self.user)
        sample_recipe(user=self.user)

        res = self.client.get(RECIPES_URL)

        recipes = Recipe.objects.all().order_by('-id')
        serializer = RecipeSerializer(recipes, many=True)

        self.assertEqual(res.status_code, status.HTTP_200_OK)
        self.assertEqual(res.data, serializer.data)

    def test_recipes_limited_to_user(self):
        # Test retrieving recipes for user
        user2 = get_user_model().objects.create_user(
            'other@test.com',
            'password123'
        )
        sample_recipe(user=user2)
        sample_recipe(user=self.user)

        res = self.client.get(RECIPES_URL)

        recipes = Recipe.objects.filter(user=self.user)
        serializer = RecipeSerializer(recipes, many=True)
        self.assertEqual(res.status_code, status.HTTP_200_OK)
        self.assertEqual(len(res.data), 1)
        self.assertEqual(res.data, serializer.data)

    def test_view_recipe_detail(self):
        # Test viewing a recipe detail
        recipe = sample_recipe(user=self.user)
        # .add is how you an item on a ManyToManyField
        recipe.tags.add(sample_tag(user=self.user))
        recipe.ingredients.add(sample_ingredient(user=self.user))

        url = detail_url(recipe.id)
        res = self.client.get(url)

        serializer = RecipeDetailSerializer(recipe)
        self.assertEqual(res.data, serializer.data)

    def test_create_basic_recipe(self):
        # Test creating recipe
        payload = {
            'title': 'Ddeokkbokki',
            'time_minutes': 30,
            'price': 5.00
        }
        res = self.client.post(RECIPES_URL, payload)

        # HTTP 201 is the standard HTTP code for creating objects in an API.
        self.assertEqual(res.status_code, status.HTTP_201_CREATED)

        # When you create an object in the Django REST framework the default
        # behavior is that it will return a dictionary containing the created
        # object. Thus res.data['id'] will retrieve the id of the object.
        recipe = Recipe.objects.get(id=res.data['id'])

        for key in payload.keys():
            # looping through keys in the dictionary
            # getattr is a Python standard library function that allows you
            # to retrieve an attribute from an object by passing in a variable
            self.assertEqual(payload[key], getattr(recipe, key))

    def test_create_recipe_with_tags(self):
        # Test creating a recipe with tag
        tag1 = sample_tag(user=self.user, name='Vegan')
        tag2 = sample_tag(user=self.user, name='Desert')
        payload = {
            'title': 'Key Lime Pie',
            'tags': [tag1.id, tag2.id],
            'time_minutes': 60,
            'price': 20.00
        }
        res = self.client.post(RECIPES_URL, payload)

        self.assertEqual(res.status_code, status.HTTP_201_CREATED)
        recipe = Recipe.objects.get(id=res.data['id'])
        # Due to the fact that we have a ManyToManyField assigned to our
        # tags (from models.py), this will return all of the tags that are
        # assigned to our recipe object as a queryset.
        tags = recipe.tags.all()
        self.assertEqual(tags.count(), 2)
        self.assertIn(tag1, tags)
        self.assertIn(tag2, tags)

    def test_create_recipe_with_ingredients(self):
        # Test creating recipe with ingredients
        ingredient1 = sample_ingredient(user=self.user, name='Abalone')
        ingredient2 = sample_ingredient(user=self.user, name='Garlic')
        payload = {
            'title': 'Feast of the Sea',
            'ingredients': [ingredient1.id, ingredient2.id],
            'time_minutes': 30,
            'price': 50.00
        }
        res = self.client.post(RECIPES_URL, payload)
        self.assertEqual(res.status_code, status.HTTP_201_CREATED)
        recipe = Recipe.objects.get(id=res.data['id'])
        ingredients = recipe.ingredients.all()
        self.assertEqual(ingredients.count(), 2)
        self.assertIn(ingredient1, ingredients)
        self.assertIn(ingredient2, ingredients)

    def test_partial_update_recipe(self):
        # Test updating a recipe with 'patch'
        # 'patch' - is used to update the fields in a payload. It will only
        # change the fields that are provided. Ommitted ones will not change.
        recipe = sample_recipe(user=self.user)
        recipe.tags.add(sample_tag(user=self.user))
        new_tag = sample_tag(user=self.user, name='Curry')

        payload = {'title': 'Chicken Tikka', 'tags': [new_tag.id]}
        url = detail_url(recipe.id)
        self.client.patch(url, payload)

        recipe.refresh_from_db()
        self.assertEqual(recipe.title, payload['title'])
        tags = recipe.tags.all()
        # both tags.count() and len(tags) work
        self.assertEqual(len(tags), 1)
        self.assertIn(new_tag, tags)

    def test_full_update_recipe(self):
        # Test updating a recipe with 'put'
        # 'put'- will replace the full object that we're updating. If you
        # exclude any fields in the payload those fields will actually
        # be removed from the object that you're updating.
        recipe = sample_recipe(user=self.user)
        recipe.tags.add(sample_tag(user=self.user))
        payload = {
            'title': 'Chicken Schwarma',
            'time_minutes': 20,
            'price': 6.00
        }
        url = detail_url(recipe.id)
        self.client.put(url, payload)

        recipe.refresh_from_db()
        self.assertEqual(recipe.title, payload['title'])
        self.assertEqual(recipe.time_minutes, payload['time_minutes'])
        self.assertEqual(recipe.price, payload['price'])

        tags = recipe.tags.all()
        self.assertEqual(len(tags), 0)

class RecipeImageUploadTests(TestCase):

    def setUp(self):
        self.client = APIClient()
        self.user = get_user_model().objects.create_user(
            'user@test.com',
            'testpass'
        )
        self.client.force_authenticate(self.user)
        self.recipe = sample_recipe(user=self.user)

    def tearDown(self):
        # function to tear down and clear test files from our object.
        self.recipe.image.delete()

    def test_upload_image_to_recipe(self):
        # Test uploading an image to recipe
        url = image_upload_url(self.recipe.id)
        with tempfile.NamedTemporaryFile(suffix='.jpg') as ntf:
            # creates a temporary file on the system that we can then
            # write to and then after we exit the context manager (with)
            # it will automatically remove that file.
            img = Image.new('RGB', (10, 10))
            img.save(ntf, format='JPEG')
            # The way that python reads files. Because we saved the file
            # the seeking would be at the end of the file and we would
            # just have a blank. We use seek(0) to set it to the beginning
            # of the file again.
            ntf.seek(0)
            # We need the multipart format because we need to tell Django
            # that we want to make a multipart form request which means a
            # a form that consists of data. By default it would be a form
            # that contains a JSON object.
            res = self.client.post(url, {'image': ntf}, format='multipart')

        self.recipe.refresh_from_db()
        self.assertEqual(res.status_code, status.HTTP_200_OK)
        # check that image is in the response and that the path is saved
        self.assertIn('image', res.data)
        self.assertTrue(os.path.exists(self.recipe.image.path))

    def test_upload_image_bad_request(self):
        # Test uploading an invalid image
        url = image_upload_url(self.recipe.id)
        res = self.client.post(url, {'image': 'notimage'}, format='multipart')

        self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)

    def test_filter_recipes_by_tags(self):
        # Test returning recipes with specific tags
        recipe1 = sample_recipe(user=self.user, title='Thai Veggie Curry')
        recipe2 = sample_recipe(user=self.user, title='Yummy Chicken')
        tag1 = sample_tag(user=self.user, name='Vegan')
        tag2 = sample_tag(user=self.user, name='Meat')
        recipe1.tags.add(tag1)
        recipe2.tags.add(tag2)
        recipe3 = sample_recipe(user=self.user, title='Fish and Chips')

        res = self.client.get(
            RECIPES_URL,
            {'tags': f'{tag1.id},{tag2.id}'}
        )

        serializer1 = RecipeSerializer(recipe1)
        serializer2 = RecipeSerializer(recipe2)
        serializer3 = RecipeSerializer(recipe3)
        self.assertIn(serializer1.data, res.data)
        self.assertIn(serializer2.data, res.data)
        self.assertNotIn(serializer3.data, res.data)

    def test_filter_recipes_by_ingredients(self):
        # Test returning recipes with specific ingredients
        recipe1 = sample_recipe(user=self.user, title='Sloppy Joe')
        recipe2 = sample_recipe(user=self.user, title='Double Cooked Pork')
        ingredient1 = sample_ingredient(user=self.user, name='Ground Beef')
        ingredient2 = sample_ingredient(user=self.user, name='Pork')
        recipe1.ingredients.add(ingredient1)
        recipe2.ingredients.add(ingredient2)
        recipe3 = sample_recipe(user=self.user, title='T-Bone Steak')

        res = self.client.get(
            RECIPES_URL,
            {'ingredients': f'{ingredient1.id},{ingredient2.id}'}
        )

        serializer1 = RecipeSerializer(recipe1)
        serializer2 = RecipeSerializer(recipe2)
        serializer3 = RecipeSerializer(recipe3)
        self.assertIn(serializer1.data, res.data)
        self.assertIn(serializer2.data, res.data)
        self.assertNotIn(serializer3.data, res.data)


"""For generating logs, first we have import the logging package, call the basicConfig funtion and pass the filename,
format and datefmt arguments
    format=defines how the log will be formatted
    datefmt = we are customizing the date format for asctime
    force=True, force: If this keyword argument is specified as true, any existing handlers attached
    to the root logger are removed and closed, before carrying out the configuration as specified
    by the other arguments.
in order to set the fields, use getLogger function, it returns the logger object"""

import logging

class LogGen:
    @staticmethod
    def generate_logs():
        logging.basicConfig(filename=".\\Logs\\automation.log", format="%(asctime)s: %(levelname)s: %(message)s",
                            datefmt='%m/%d/%Y %I:%M:%S %p', force=True)
        logger = logging.getLogger()
        logger.setLevel(logging.INFO)
        return logger


from data import transforms

class DataTransform:
    def __init__(self, resolution) -> None:
        """
        Args:
            resolution (int): Resolution of the image. (320 for knee, 384 for brain)
        """
        self.resolution = resolution

    def __call__(self, k_space, mask, target, attrs, f_name, slice):
        """
        Args:
            kspace (numpy.array): Input k-space of shape (num_coils, rows, cols, 2) for multi-coil
                data or (rows, cols, 2) for single coil data.
            mask (numpy.array): Mask from the test dataset
            target (numpy.array): Target image
            attrs (dict): Acquisition related information stored in the HDF5 object.
            fname (str): File name
            slice (int): Serial number of the slice.
        Returns:
            (tuple): tuple containing:
                k_space (torch.Tensor): k-space(resolution x resolution x 2)
                target (torch.Tensor): Target image converted to a torch Tensor.
                fname (str): File name
                slice (int): Serial number of the slice.
        """
        k_space = transforms.to_tensor(k_space)
        full_image = transforms.ifft2(k_space)
        cropped_image = transforms.complex_center_crop(full_image, (self.resolution, self.resolution))
        k_space = transforms.fft2(cropped_image)
        # Normalize input
        cropped_image, mean, std = transforms.normalize_instance(cropped_image, eps=1e-11)
        cropped_image = cropped_image.clamp(-6, 6)
        # Normalize target
        target = transforms.to_tensor(target)
        target = transforms.center_crop(target, (self.resolution, self.resolution))
        target = transforms.normalize(target, mean, std, eps=1e-11)
        target = target.clamp(-6, 6)
        return k_space, target, f_name, slice


from PyQt5.QtWidgets import QApplication, QMainWindow, QStatusBar, QButtonGroup, QPushButton, QGridLayout, QLabel, \
    QRadioButton, QFileDialog, QMessageBox, QLineEdit, QTextEdit, QFrame, QWidget, QComboBox
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon

import batch_send as bps
import office.outlook_mail as outlook
from batch_send import RowNumConfig
import ctypes

class BatchSendMailWidget(QMainWindow):
    """
    批处理进行邮件发送处理处理的UI
    """
    _row_config: RowNumConfig

    def __init__(self):
        super().__init__()
        # UI处理的各种成员
        self.setCentralWidget(QWidget())
        self.setWindowIcon(QIcon('./icon/mail.png'))
        ctrl_line = []
        self._ctrl_matrix = []
        self._btn_group = []
        label = QLabel("发件人：")
        ctrl_line.append(label)
        radio1 = QRadioButton("相应EXCEL列号：")
        ctrl_line.append(radio1)
        edit = QLineEdit("1")
        ctrl_line.append(edit)
        radio2 = QRadioButton("使用填写发件人：")
        ctrl_line.append(radio2)
        edit = QLineEdit("")
        edit.setEnabled(False)
        ctrl_line.append(edit)
        group = QButtonGroup()
        group.addButton(radio1)
        group.addButton(radio2)
        radio1.clicked.connect(self.radio_clicked)
        radio2.clicked.connect(self.radio_clicked)
        group.setExclusive(True)
        radio1.setChecked(True)
        self._ctrl_matrix.append(ctrl_line)
        self._btn_group.append(group)
        ctrl_line = []
        label = QLabel("收件人：")
        ctrl_line.append(label)
        radio1 = QRadioButton("相应EXCEL列号：")
        ctrl_line.append(radio1)
        edit = QLineEdit("2")
        ctrl_line.append(edit)
        radio2 = QRadioButton("使用填写收件人：")
        ctrl_line.append(radio2)
        edit = QLineEdit("")
        edit.setEnabled(False)
        ctrl_line.append(edit)
        group = QButtonGroup()
        group.addButton(radio1)
        group.addButton(radio2)
        radio1.clicked.connect(self.radio_clicked)
        radio2.clicked.connect(self.radio_clicked)
        group.setExclusive(True)
        radio1.setChecked(True)
        self._ctrl_matrix.append(ctrl_line)
        self._btn_group.append(group)
        ctrl_line = []
        label = QLabel("抄送：")
        ctrl_line.append(label)
        radio1 = QRadioButton("相应EXCEL列号：")
        ctrl_line.append(radio1)
        edit = QLineEdit("3")
        ctrl_line.append(edit)
        radio2 = QRadioButton("使用填写抄送人：")
        ctrl_line.append(radio2)
        edit = QLineEdit("")
        edit.setEnabled(False)
        ctrl_line.append(edit)
        group = QButtonGroup()
        group.addButton(radio1)
        group.addButton(radio2)
        radio1.clicked.connect(self.radio_clicked)
        radio2.clicked.connect(self.radio_clicked)
        radio1.setChecked(True)
        self._ctrl_matrix.append(ctrl_line)
        self._btn_group.append(group)
        ctrl_line = []
        label = QLabel("邮件标题：")
        ctrl_line.append(label)
        radio1 = QRadioButton("相应EXCEL列号：")
        ctrl_line.append(radio1)
        edit = QLineEdit("4")
        ctrl_line.append(edit)
        radio2 = QRadioButton("使用填写标题：")
        ctrl_line.append(radio2)
        edit = QLineEdit("")
        edit.setEnabled(False)
        ctrl_line.append(edit)
        group = QButtonGroup()
        group.addButton(radio1)
        group.addButton(radio2)
        radio1.clicked.connect(self.radio_clicked)
        radio2.clicked.connect(self.radio_clicked)
        radio1.setChecked(True)
        self._ctrl_matrix.append(ctrl_line)
        self._btn_group.append(group)
        ctrl_line = []
        label = QLabel("邮件正文：")
        ctrl_line.append(label)
        radio1 = QRadioButton("相应EXCEL列号：")
        ctrl_line.append(radio1)
        edit = QLineEdit("5")
        ctrl_line.append(edit)
        radio2 = QRadioButton("使用填写正文：")
        ctrl_line.append(radio2)
        edit = QTextEdit("")
        edit.setEnabled(False)
        ctrl_line.append(edit)
        group = QButtonGroup()
        group.addButton(radio1)
        group.addButton(radio2)
        radio1.clicked.connect(self.radio_clicked)
        radio2.clicked.connect(self.radio_clicked)
        radio1.setChecked(True)
        self._ctrl_matrix.append(ctrl_line)
        self._btn_group.append(group)
        self._edit_open_xls = QLineEdit("")
        self._btn_open_xls = QPushButton("打开")
        self._btn_open_xls.clicked.connect(self.open_xls_clicked)
        self._combo_xls_sheet = QComboBox()
        self._combo_xls_sheet.currentIndexChanged.connect(self.combo_sheet_changed)
        self._edit_start_row = QLineEdit("2")
        self._edit_end_row = QLineEdit("5")
        self._btn_send = QPushButton("发送")
        self._btn_exit = QPushButton("退出")
        self._btn_send.clicked.connect(self.send_mail)
        self._btn_exit.clicked.connect(self.exit_app)
        self._status_bar = QStatusBar()
        self._status_bar.layout().setAlignment(Qt.AlignRight)
        # BPS批处理发送处理类
        self._batch_send = bps.BatchProcessSend()
        self._default_mail = outlook.SendMailInfo()
        self._row_config = bps.RowNumConfig()
        self._column_config = bps.ColumnNumConfig()

    def show(self):
        grid = QGridLayout()
        line = 0
        grid.setSpacing(10)
        grid.setColumnStretch(0, 1)
        grid.setColumnStretch(1, 1)
        grid.setColumnStretch(2, 1)
        grid.setColumnStretch(3, 1)
        grid.setColumnStretch(4, 10)
        grid.setColumnStretch(5, 1)
        self.centralWidget().setLayout(grid)
        grid.addWidget(QLabel("打开EXCEL配置文件："), line, 0)
        grid.addWidget(self._edit_open_xls, line, 1, 1, 5)
        grid.addWidget(self._btn_open_xls, line, 6)
        line += 1
        grid.addWidget(QLabel("EXCEL对应的Sheet："), line, 0)
        grid.addWidget(self._combo_xls_sheet, line, 1, 1, 5)
        line += 1
        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setFrameShadow(QFrame.Plain)
        grid.addWidget(separator, line, 0, 1, 8)
        line += 1
        for x in self._ctrl_matrix:
            grid.addWidget(x[0], line, 0, 1, 1)
            grid.addWidget(x[1], line, 1, 1, 1)
            grid.addWidget(x[2], line, 2, 1, 1)
            grid.addWidget(x[3], line, 3, 1, 1)
            # 给最后一个TextEdit多留一点空间
            if line == 5:
                grid.addWidget(x[4], line, 4, 3, 5)
                line += 3
            else:
                grid.addWidget(x[4], line, 4, 1, 5)
                line += 1
        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setFrameShadow(QFrame.Plain)
        grid.addWidget(separator, line, 0, 1, 8)
        line += 1
        grid.addWidget(QLabel("EXCEL起始发送行："), line, 0)
        grid.addWidget(self._edit_start_row, line, 1)
        line += 1
        grid.addWidget(QLabel("EXCEL结束发送行："), line, 0)
        grid.addWidget(self._edit_end_row, line, 1)
        line += 1
        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setFrameShadow(QFrame.Plain)
        grid.addWidget(separator, line, 0, 1, 8)
        line += 1
        grid.addWidget(self._btn_send, line, 5)
        grid.addWidget(self._btn_exit, line, 6)
        self.move(300, 150)
        self.setWindowTitle('OA Send Mail')
        self.setStatusBar(self._status_bar)
        self._status_bar.showMessage("选择配置的EXCEL文件，进行批量发送")
        QMainWindow.show(self)

    def radio_clicked(self):
        radio = self.sender()
        for x in self._ctrl_matrix:
            if x[1] == radio:
                x[1].setChecked(True)
                x[2].setEnabled(True)
                x[4].setEnabled(False)
            elif x[3] == radio:
                x[3].setChecked(True)
                x[4].setEnabled(True)
                x[2].setEnabled(False)
        pass

    def open_xls_clicked(self):
        """
        打开文件对话框，
        """
        file_name, file_type = QFileDialog.getOpenFileName(
            self,
            "选取EXCEL文件",
            "./",
            "Excel Files (*.xls *.xlsx);;All Files (*)")
        if file_name == "":
            self._status_bar.showMessage("必须选择配置的EXCEL文件，才能进行批量发送")
            pass
        self._status_bar.showMessage("已经选择了EXCEL，请选择Sheet")
        self._edit_open_xls.setText(file_name)
        success_open = self._batch_send.open_excel(file_name)
        if not success_open:
            QMessageBox.information(self,
                                    "提示",
                                    "无法打开对应的EXCEL文件。",
                                    QMessageBox.Ok)
        self._combo_xls_sheet.clear()
        self._combo_xls_sheet.addItems(self._batch_send.sheets_name)
        pass

    def send_mail(self) -> bool:
        """
        发送邮件，
        """
        if self._ctrl_matrix[0][1].isChecked():
            self._column_config.sender = int(self._ctrl_matrix[0][2].text())
            if self._column_config.sender == 0:
                self._status_bar.showMessage("没有填写默认发送人时，发送人列号不能是0")
                return False
        else:
            self._column_config.sender = 0
            self._default_mail.sender = self._ctrl_matrix[0][4].text()
        if self._ctrl_matrix[1][1].isChecked():
            self._column_config.to = int(self._ctrl_matrix[1][2].text())
            if self._column_config.to == 0:
                self._status_bar.showMessage("没有填写默认接收人时，接收人列号不能是0")
                return False
            else:
                if self._column_config.sender == self._column_config.to:
                    self._status_bar.showMessage("不能出现相同的列号")
                    return False
        else:
            self._column_config.to = 0
            self._default_mail.to = self._ctrl_matrix[1][4].text()
        if self._ctrl_matrix[2][1].isChecked():
            self._column_config.cc = int(self._ctrl_matrix[2][2].text())
            if self._column_config.cc == 0:
                self._status_bar.showMessage("没有填写默认抄送人时，抄送人列号不能是0")
                return False
            else:
                if self._column_config.sender == self._column_config.to or \
                        self._column_config.to == self._column_config.cc:
                    self._status_bar.showMessage("不能出现相同的列号")
                    return False
        else:
            self._column_config.cc = 0
            self._default_mail.cc = self._ctrl_matrix[2][4].text()
        if self._ctrl_matrix[3][1].isChecked():
            self._column_config.subject = int(self._ctrl_matrix[3][2].text())
            if self._column_config.subject == 0:
                self._status_bar.showMessage("没有填写默认标题时，标题列号不能是0")
                return False
        else:
            self._column_config.subject = 0
            self._default_mail.subject = self._ctrl_matrix[3][4].text()
        if self._ctrl_matrix[4][1].isChecked():
            self._column_config.body = int(self._ctrl_matrix[4][2].text())
            if self._column_config.body == 0:
                self._status_bar.showMessage("没有填写默认内容时，内容列号不能是0")
                return False
            else:
                if self._column_config.sender == self._column_config.to or \
                        self._column_config.to == self._column_config.cc:
                    self._status_bar.showMessage("不能出现相同的列号")
                    return False
        else:
            self._column_config.body = 0
            self._default_mail.body = self._ctrl_matrix[4][4].text()

        self._row_config.send_start = int(self._edit_start_row.text())
        self._row_config.send_end = int(self._edit_end_row.text())
        if self._row_config.send_start >= self._row_config.send_end:
            self._status_bar.showMessage("起始，结束发送行错误")
            return False
        self._row_config.mail_count = self._row_config.send_end - self._row_config.send_start + 1
        cfg_success = self._batch_send.config(self._column_config,
                                              self._row_config,
                                              self._default_mail)
        if not cfg_success:
            self._status_bar.showMessage("配置存在问题，请检查")
            return False
        else:
            self._status_bar.showMessage("配置正确，准备发送{}封邮件".format(self._row_config.mail_count))
        i = 0
        j = self._row_config.send_start
        while j <= self._row_config.send_end:
            send_result = self._batch_send.send_one(j)
            if not send_result:
                self._status_bar.showMessage(str("发送第{}封,第{}行邮件失败").format(i + 1, j))
                return False
            j += 1
            i += 1
            self._status_bar.showMessage(str("发送第{}封,第{}行邮件成功").format(i + 1, j))
        self._status_bar.showMessage("全部发送完成")
        return True

    def exit_app(self):
        self._status_bar.showMessage("下次再见")
        QApplication.quit()

    def combo_sheet_changed(self):
        load_ok = self._batch_send.load_sheet_byname(self._combo_xls_sheet.currentText())
        if not load_ok:
            self._status_bar.showMessage("加载EXCEL对应的sheet失败，请检查表格")
            return
        out_info = "加载sheet[{}]成功.起始结束行列{}".format(
            self._combo_xls_sheet.currentText(),
            self._batch_send.cur_sheet_info())
        self._status_bar.showMessage(out_info)
        s_r, s_c, e_r, e_c = self._batch_send.cur_sheet_info()
        self._row_config.caption = s_r
        self._row_config.send_start = s_r + 1
        self._row_config.send_end = e_r
        self._row_config.mail_count = e_r - s_r
        self._edit_start_row.setText(str(self._row_config.send_start))
        self._edit_end_row.setText(str(self._row_config.send_end))

    def bps_start(self) -> bool:
        start_ok = self._batch_send.start()
        if not start_ok:
            QMessageBox.information(window,
                                    "提示",
                                    "OA Send Mail需要Outlook和Excel，否则无法使用。",
                                    QMessageBox.Ok)
            QApplication.quit()
        return True

    def closeEvent(self, event):
        """
        重写closeEvent方法，实现dialog窗体关闭时执行一些代码
        """
        self._batch_send.close()

if __name__ == '__main__':
    app = QApplication([])
    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("OAMail")
    font = app.font()
    font.setPointSize(11)
    app.setFont(font)
    window = BatchSendMailWidget()
    ret = window.bps_start()
    window.show()
    app.exec()