__author__ = 'aymgal'

import numpy as np
import numpy.testing as npt
import pytest
import unittest
import copy

from slitronomy.Optimization.model_operators import ModelOperators
from slitronomy.Lensing.lensing_operator import LensingOperator
from slitronomy.Util import util

from lenstronomy.Data.imaging_data import ImageData
from lenstronomy.LensModel.lens_model import LensModel
from lenstronomy.LightModel.light_model import LightModel
from lenstronomy.Data.psf import PSF
from lenstronomy.ImSim.Numerics.numerics_subframe import NumericsSubFrame
import lenstronomy.Util.util as l_util

np.random.seed(18)

class TestModelOperators(object):
    """
    tests the Lensing Operator classes
    """
    def setup(self):
        self.num_pix = 49  # cutout pixel size
        self.subgrid_res_source = 2
        self.num_pix_source = self.num_pix * self.subgrid_res_source

        delta_pix = 0.24
        _, _, ra_at_xy_0, dec_at_xy_0, _, _, Mpix2coord, _ \
            = l_util.make_grid_with_coordtransform(numPix=self.num_pix, deltapix=delta_pix, subgrid_res=1,
                                                         inverse=False, left_lower=False)

        self.image_data = np.random.rand(self.num_pix, self.num_pix)
        kwargs_data = {
            'ra_at_xy_0': ra_at_xy_0, 'dec_at_xy_0': dec_at_xy_0,
            'transform_pix2angle': Mpix2coord,
            'image_data': self.image_data,
        }
        data = ImageData(**kwargs_data)

        lens_model = LensModel(['SPEP'])
        kwargs_lens = [{'theta_E': 1, 'gamma': 2, 'center_x': 0, 'center_y': 0, 'e1': -0.05, 'e2': 0.05}]

        # PSF
        kernel_pixel = np.zeros((self.num_pix, self.num_pix))
        kernel_pixel[int(self.num_pix/2), int(self.num_pix/2)] = 1  # just a dirac here
        kwargs_psf = {'psf_type': 'PIXEL', 'kernel_point_source': kernel_pixel}
        psf = PSF(**kwargs_psf)

        # wavelets scales for lens and source
        self.n_scales_source = 4
        self.n_scales_lens = 3

        # list of source light profiles
        source_model = LightModel(['SLIT_STARLETS'])
        self.kwargs_source = [{'n_scales': self.n_scales_source}]

        # list of lens light profiles
        lens_light_model = LightModel(['SLIT_STARLETS'])
        self.kwargs_lens_light = [{'n_scales': self.n_scales_lens}]

        # define some mask
        likelihood_mask = np.ones((self.num_pix, self.num_pix))

        # get grid classes
        self.numerics = NumericsSubFrame(data, psf)
        image_grid_class = self.numerics.grid_class
        source_numerics = NumericsSubFrame(data, psf, supersampling_factor=self.subgrid_res_source)
        source_grid_class = source_numerics.grid_class

        # get a lensing operator
        self.lensing_op = LensingOperator(lens_model, image_grid_class, source_grid_class, self.num_pix)
        self.lensing_op.update_mapping(kwargs_lens)

        self.model_op = ModelOperators(data, self.lensing_op, self.numerics)
        self.model_op._set_likelihood_mask(likelihood_mask)
        self.model_op.add_source_light(source_model)
        self.model_op.add_lens_light(lens_light_model)
        self.model_op_nolens = ModelOperators(data, self.lensing_op, self.numerics)
        self.model_op_nolens._set_likelihood_mask(likelihood_mask)
        self.model_op_nolens.add_source_light(source_model)

        # define some test images in direct space
        self.X_s = np.random.rand(self.num_pix_source, self.num_pix_source)  # source light
        self.X_l = np.random.rand(self.num_pix, self.num_pix)  # lens light

        # define some test images in wavelets space
        self.alpha_s = np.random.rand(self.n_scales_source, self.num_pix_source, self.num_pix_source)  # source light
        self.alpha_l = np.random.rand(self.n_scales_lens, self.num_pix, self.num_pix)  # lens light

    def test_set_wavelet_scales(self):
        self.model_op.set_source_wavelet_scales(self.n_scales_source)
        Phi_T_s_X = self.model_op.Phi_T_s(self.X_s)
        self.model_op.set_lens_wavelet_scales(self.n_scales_lens)
        Phi_T_l_X = self.model_op.Phi_T_l(self.X_l)
        # test that transformed image has the right shape in terms of number of scales
        assert Phi_T_s_X.shape[0] == self.n_scales_source
        assert Phi_T_l_X.shape[0] == self.n_scales_lens

    def test_subtract_from_data_and_reset(self):
        image_to_subtract = np.eye(self.num_pix, self.num_pix)
        self.model_op.subtract_from_data(image_to_subtract)
        npt.assert_equal(self.model_op.Y_tilde, self.model_op.Y_eff)
        npt.assert_equal(self.model_op.Y_tilde, self.image_data)
        npt.assert_equal(self.model_op.Y_p, self.image_data - image_to_subtract)
        self.model_op.reset_partial_data()
        npt.assert_equal(self.model_op.Y_tilde, self.image_data)
        npt.assert_equal(self.model_op.Y_p, self.image_data)

    def test_spectral_norm_source(self):
        self.model_op.set_source_wavelet_scales(self.n_scales_source)
        npt.assert_almost_equal(self.model_op.spectral_norm_source, 0.97, decimal=2)

    def test_spectral_norm_lens(self):
        self.model_op.set_lens_wavelet_scales(self.n_scales_lens)
        npt.assert_almost_equal(self.model_op.spectral_norm_lens, 0.99, decimal=2)

    def test_data_terms(self):
        npt.assert_equal(self.model_op.Y_tilde, self.image_data)
        npt.assert_equal(self.model_op.Y_p, self.image_data)

    def test_convolution(self):
        H_X_s = self.model_op.H(self.X_s)
        npt.assert_equal(H_X_s, self.numerics.convolution_class.convolution2d(self.X_s))
        H_T_X_s = self.model_op.H_T(self.X_s)
        conv_transpose = self.numerics.convolution_class.copy_transpose()
        npt.assert_equal(H_T_X_s, conv_transpose.convolution2d(self.X_s))

    def test_lensing(self):
        F_X_s = self.model_op.F(self.X_s)
        npt.assert_equal(F_X_s, self.lensing_op.source2image_2d(self.X_s))
        F_T_X_l = self.model_op.F_T(self.X_l)
        npt.assert_equal(F_T_X_l, self.lensing_op.image2source_2d(self.X_l))

    def test_wavelet_transform(self):
        # TODO : do more accurate tests here
        self.model_op.set_source_wavelet_scales(self.n_scales_source)
        self.model_op.set_lens_wavelet_scales(self.n_scales_lens)
        Phi_alpha_s = self.model_op.Phi_s(self.alpha_s)
        Phi_alpha_l = self.model_op.Phi_l(self.alpha_l)
        assert Phi_alpha_s.shape == (self.num_pix*self.subgrid_res_source, self.num_pix*self.subgrid_res_source)
        assert Phi_alpha_l.shape == (self.num_pix, self.num_pix)
        Phi_T_X_s = self.model_op.Phi_T_s(self.X_s)
        Phi_T_X_l = self.model_op.Phi_T_l(self.X_l)
        assert Phi_T_X_s.shape == (self.n_scales_source, self.num_pix*self.subgrid_res_source, self.num_pix*self.subgrid_res_source)
        assert Phi_T_X_l.shape == (self.n_scales_lens, self.num_pix, self.num_pix)

class TestRaise(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super(TestRaise, self).__init__(*args, **kwargs)
        self.num_pix = 10
        _, _, ra_at_xy_0, dec_at_xy_0, _, _, Mpix2coord, _ \
            = l_util.make_grid_with_coordtransform(numPix=self.num_pix, deltapix=0.5, subgrid_res=1,
                                                   inverse=False, left_lower=False)
        kwargs_data_nonsquare = {
            'ra_at_xy_0': ra_at_xy_0, 'dec_at_xy_0': dec_at_xy_0,
            'transform_pix2angle': Mpix2coord,
            'image_data': np.zeros((self.num_pix, self.num_pix+10)),  # non-square image
        }
        kwargs_data = {
            'ra_at_xy_0': ra_at_xy_0, 'dec_at_xy_0': dec_at_xy_0,
            'transform_pix2angle': Mpix2coord,
            'image_data': np.zeros((self.num_pix, self.num_pix)),  # non-square image
        }
        self.data_nonsquare = ImageData(**kwargs_data_nonsquare)
        self.data = ImageData(**kwargs_data)
        psf = PSF('NONE')
        self.numerics = NumericsSubFrame(self.data, psf)
        lens_model = LensModel(['SPEP'])
        self.source_model_class = LightModel(['SLIT_STARLETS'])
        self.lens_light_model_class = LightModel(['SLIT_STARLETS'])
        # get grid classes
        image_grid_class = self.numerics.grid_class
        source_numerics = NumericsSubFrame(self.data, psf, supersampling_factor=1)
        source_grid_class = source_numerics.grid_class
        self.lensing_op = LensingOperator(lens_model, image_grid_class, source_grid_class, self.num_pix)
        self.model_op = ModelOperators(self.data, self.lensing_op, self.numerics)
        self.model_op.add_lens_light(self.lens_light_model_class)
        self.model_op_nolens = ModelOperators(self.data, self.lensing_op, self.numerics)

    def test_raise(self):
        with self.assertRaises(ValueError):
            # no wavelet scales was set, so should raise errors
            X_s = np.random.rand(self.num_pix, self.num_pix)
            Phi_T_s_X = self.model_op.Phi_T_s(X_s)
        with self.assertRaises(ValueError):
            # no wavelet scales was set, so should raise errors
            X_l = np.random.rand(self.num_pix, self.num_pix)
            Phi_T_l_X = self.model_op.Phi_T_l(X_l)
        with self.assertRaises(ValueError):
            # no wavelet scales was set, so should raise errors
            alpha_s = np.random.rand(3, self.num_pix, self.num_pix)
            Phi_s_alpha = self.model_op.Phi_s(alpha_s)
        with self.assertRaises(ValueError):
            # no wavelet scales was set, so should raise errors
            alpha_l = np.random.rand(3, self.num_pix, self.num_pix)
            Phi_l_alpha = self.model_op.Phi_l(alpha_l)
        with self.assertRaises(ValueError):
            # no lens profile was set, so should raise an error
            X_l = np.random.rand(self.num_pix, self.num_pix)
            Phi_T_l_X = self.model_op_nolens.Phi_T_l(X_l)
        with self.assertRaises(ValueError):
            # no lens profile was set, so should raise an error
            alpha_l = np.random.rand(3, self.num_pix, self.num_pix)
            Phi_l_alpha = self.model_op_nolens.Phi_l(alpha_l)
        with self.assertRaises(ValueError):
            # ModelOperators init should raise an error with non square data image
            model_op_error = ModelOperators(self.data_nonsquare, self.lensing_op, self.numerics)

if __name__ == '__main__':
    pytest.main()


#setup code
alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
shift = input("What would you like the shift to be? (integer between 1 and 26): ")
msg = input("Please enter a message to be ciphered: ")
newmessagelist = []

#ciphers the message
def msgCreater(msg):
    for i in msg:
        if (int(shift) % 26 == 0):
            newmessagelist.append(i)
            continue
        if (i == " "):
            index = index + 1
            newmessagelist.append(" ")
            continue
        index = alphabet.index(i)
        newindex = index + int(shift)
        if (newindex >= 25):
            newindex = (newindex % 25) - 1
            newletter = alphabet[newindex]
            newmessagelist.append(newletter)
        else:
            newletter = alphabet[newindex]
            newmessagelist.append(newletter)

#list to string
def listToString(newmessagelist):
    str = ""
    for i in newmessagelist:
        str = str + i
    return str

#function code
print("Old message is:", msg)
msgCreater(msg)
print("Ciphered message is:", listToString(newmessagelist))


'''
Created on 29 Oct 2019

@author: oqb
'''
import numpy as np
import matplotlib.pyplot as plt
from aa import data, fetcher, js
from tzlocal import get_localzone
from datetime import datetime
from scipy.signal import savgol_filter
from scipy import interpolate

if __name__ == '__main__':
    #Set up archive
    tz = get_localzone()
    ArchiverBessy = 'archiver.bessy.de'

    jfetcher_Bessy = js.JsonFetcher(ArchiverBessy,80)
    # gather data on move to create time window
    UE112Gap = 'UE112ID7R:BasePmGap.A'
    UE112Phase = 'UE112ID7R:SBasePmGap.B'

    tuneH = 'TUNEZR:rdH'
    tuneV =  'TUNEZR:rdV'
    THzbox0 = 'THZH11FD6L:BOX0'

    Vtunetarget = 979.0
    Htunetarget = 882.0
    THztarget = 1

    toistart = tz.localize(datetime(2019, 10, 27, 8 , 25))
    toiend = tz.localize(datetime(2019, 10, 27, 8, 33))

    phasemove = jfetcher_Bessy.get_values(UE112Phase, toistart, toiend)

    #Have a quick look
    plt.plot(phasemove._timestamps, phasemove._values)
    plt.show()

    # set window

    #find _values array point wwhere the move starts and stops #can be wrapped up
    #find the stepsize of value array
    tmpphasediff = np.diff(phasemove._values[:,0])
    #create a True/False array of 'real steps' i.e. above 0.1 micron. Find the first true
    datastartindex = np.argmax(tmpphasediff>1e-4)+1
    #find the first false after the first true
    datastopindex = np.argmin(tmpphasediff[datastartindex:]>1e-4)+2
    #Local Timestamps for TuneH, Tune V and Box0
    phasemovestart = tz.localize(datetime.fromtimestamp((phasemove._timestamps[datastartindex]))) #start a second before
    phasemoveend = tz.localize(datetime.fromtimestamp((phasemove._timestamps[datastopindex]+1))) #finish a second after
    # gather more data (tune, Box0)
    tuneHdata =  jfetcher_Bessy.get_values(tuneH, phasemovestart, phasemoveend)
    tuneVdata = jfetcher_Bessy.get_values(tuneV, phasemovestart, phasemoveend)
    THzbox0data = jfetcher_Bessy.get_values(THzbox0, phasemovestart, phasemoveend)

    #gather data for detrending THz signal
    THzdetrendstart = tz.localize(datetime(2019, 10, 27, 8 , 14))
    THzdetrendend = tz.localize(datetime(2019, 10, 27, 8 , 24))
    THztrenddata = jfetcher_Bessy.get_values(THzbox0, THzdetrendstart, THzdetrendend)

    #create linear fit
    THztargetfit = np.polyfit(THztrenddata._timestamps, THztrenddata._values[:,0], 1)
    print(THztargetfit)
    THztarget = np.poly1d(THztargetfit)

    FMN = THzbox0data._values-THztarget(THzbox0data._timestamps)

    plt.figure()
    plt.plot(tuneHdata._timestamps, tuneHdata._values-np.mean(tuneHdata._values))
    plt.plot(tuneVdata._timestamps, tuneVdata._values-np.mean(tuneVdata._values))
    plt.plot(THzbox0data._timestamps, 500*(FMN))

    plt.show()
    # fit curves
    #fit gap curve also with time
    #(VTune determine main oscillation frequency and strip it out?) Worth looking at? Or comes out in the fit wash?
    #THz tune number remove linear fit from first to final point CAN ONLY BE DONE AFTER BOTH WAYS LOOKED AT OR LINEAR FIT FROM PREVIOUS MOTION FREE TIMES USED
    #HTune Smooth
    smTuneHData = savgol_filter(tuneHdata._values[:,0], 5, 2)
    #VTune Smooth
    smTuneVData = savgol_filter(tuneVdata._values[:,0], 51, 2)
    #THz Smooth
    smTHzData = savgol_filter(THzbox0data._values[:,0], 151, 2)
    tmp2 = savgol_filter(FMN, 151, 2)

    ###Here be fits
    xnew = np.linspace(phasemove._timestamps[datastartindex], (phasemove._timestamps[datastopindex]), 51)

    #Htune Fit
    #Htune = np.linspace(phasemove._timestamps[datastartindex], phasemove._timestamps[datastopindex], len(smTuneHData))
    tckHtune = interpolate.splrep(np.linspace(phasemove._timestamps[datastartindex], phasemove._timestamps[datastopindex], len(smTuneHData)),smTuneHData, s=0.0)
    Htunefit = interpolate.splev(xnew, tckHtune, der=0)

    plt.figure()
    plt.plot(tuneHdata._timestamps, tuneHdata._values,tuneHdata._timestamps, smTuneHData, xnew, Htunefit)
    #plt.plot(tuneHdata._timestamps, smTuneHData)

    plt.show()

    #Vtune Fit
    tckVtune = interpolate.splrep(np.linspace(phasemove._timestamps[datastartindex], phasemove._timestamps[datastopindex], len(smTuneVData)),smTuneVData, s=0.0)
    Vtunefit = interpolate.splev(xnew, tckVtune, der=0)

    plt.figure()
    plt.plot(tuneVdata._timestamps, tuneVdata._values,tuneVdata._timestamps, smTuneVData, xnew, Vtunefit)

    plt.show()

    #THz Fit
    tckTHz = interpolate.splrep(np.linspace(phasemove._timestamps[datastartindex], phasemove._timestamps[datastopindex], len(smTHzData)),smTHzData, s=0.0)
    THzfit = interpolate.splev(xnew, tckTHz, der=0)

    plt.figure()
    plt.plot(THzbox0data._timestamps, THzbox0data._values,THzbox0data._timestamps, smTHzData,xnew, THzfit)

    plt.show()

    #Gap Fit
    #TODO Needs Improving
    tckGap = interpolate.splrep(np.linspace(phasemove._timestamps[datastartindex], phasemove._timestamps[datastopindex], len(phasemove._values[datastartindex:datastopindex])),phasemove._values[datastartindex:datastopindex], s=0.0)
    Gapfit = interpolate.splev(xnew, tckGap, der=0)

    plt.figure()
    plt.plot(phasemove._timestamps[datastartindex:datastopindex], phasemove._values[datastartindex:datastopindex],xnew, Gapfit)

    plt.show()

    # create tables
    #Bring fitted lines together
    gapresponse = np.transpose(np.vstack((Gapfit, Htunefit-Htunetarget, Vtunefit-Vtunetarget, THzfit)))

    #Create inverse matrix
    responsematrix = np.transpose([[35.75,67.5, 600],[-157.3, -22.5, -22.75], [121,207.5, 190.5]])
    invresponse = np.linalg.inv(responsematrix)
    print (np.asmatrix(responsematrix))
    print(invresponse)

    print(np.dot(invresponse,np.transpose(-gapresponse[50,1:])))

    #create quadrupole array solution.
    quadoffsets = np.zeros(gapresponse.shape)
    quadoffsets[:,0] = gapresponse[:,0]
    quadoffsets[:,1:]=np.transpose(np.dot(invresponse,np.transpose(-gapresponse[:,1:])))

    #export tables in expected form..

    #make a quadrupole array solution based on existing points and linear interpolation
    # compare tables

    #point to stop for debug
    print (quadoffsets)


# Import required libraries
import numpy as np
from matplotlib import pyplot as plt
from scipy.sparse import spdiags, csr_matrix, linalg, lil_matrix, diags, block_diag

# Read source, target and mask for a given id
def Read(id, path = ""):
    source = plt.imread(path + "source_" + id + ".jpg")
    info = np.iinfo(source.dtype) # get information about the image type (min max values)
    source = source.astype(np.float32) / info.max # normalize the image into range 0 and 1
    target = plt.imread(path + "target_" + id + ".jpg")
    info = np.iinfo(target.dtype) # get information about the image type (min max values)
    target = target.astype(np.float32) / info.max # normalize the image into range 0 and 1
    mask   = plt.imread(path + "mask_" + id + ".jpg")
    info = np.iinfo(mask.dtype) # get information about the image type (min max values)
    mask = mask.astype(np.float32) / info.max # normalize the image into range 0 and 1

    return source, mask, target

# Adjust parameters, source and mask for negative offsets or out of bounds of offsets
def AlignImages(mask, source, target, offset):
    sourceHeight, sourceWidth, _ = source.shape
    targetHeight, targetWidth, _ = target.shape
    xOffset, yOffset = offset

    if (xOffset < 0):
        mask    = mask[abs(xOffset):, :]
        source  = source[abs(xOffset):, :]
        sourceHeight -= abs(xOffset)
        xOffset = 0
    if (yOffset < 0):
        mask    = mask[:, abs(yOffset):]
        source  = source[:, abs(yOffset):]
        sourceWidth -= abs(yOffset)
        yOffset = 0
    # Source image outside target image after applying offset
    if (targetHeight < (sourceHeight + xOffset)):
        sourceHeight = targetHeight - xOffset
        mask    = mask[:sourceHeight, :]
        source  = source[:sourceHeight, :]
    if (targetWidth < (sourceWidth + yOffset)):
        sourceWidth = targetWidth - yOffset
        mask    = mask[:, :sourceWidth]
        source  = source[:, :sourceWidth]

    maskLocal = np.zeros_like(target)
    maskLocal[xOffset:xOffset + sourceHeight, yOffset:yOffset + sourceWidth] = mask
    sourceLocal = np.zeros_like(target)
    sourceLocal[xOffset:xOffset + sourceHeight, yOffset:yOffset + sourceWidth] = source

    return sourceLocal, maskLocal

# Pyramid Blend
def PyramidBlend(source, mask, target):

    return source * mask + target * (1 - mask)

# Poisson Blend

def PoissonBlend(source, mask, target, isMix):
    # Get dimensions
    height, width, channel = target.shape
    num_pixels = height * width

    # Compute matrix A and vector b

    # Loop through the mask to find A. We will also precompute its content since traversing it is slow due to being linked list
    A_data, A_row, A_col = [], [], []
    for y in range(height):
        for x in range(width):
            i = y * width + x
            if (mask[y, x] == 0).any():
                A_data.append(1)
                A_row.append(i)
                A_col.append(i)
            else:
                A_data.append(4)
                A_row.append(i)
                A_col.append(i)
                if x > 0:               #A[i, i - 1] = -1
                    A_data.append(-1)
                    A_row.append(i)
                    A_col.append(i - 1)
                if x < width - 1:       #A[i, i + 1] = -1
                    A_data.append(-1)
                    A_row.append(i)
                    A_col.append(i + 1)
                if y > 0:               #A[i, i - width] = -1
                    A_data.append(-1)
                    A_row.append(i)
                    A_col.append(i - width)
                if y < height - 1:      #A[i, i + width] = -1
                    A_data.append(-1)
                    A_row.append(i)
                    A_col.append(i + width)

    # Construct sparse matrix A
    A = csr_matrix((A_data, (A_row, A_col)), shape=(num_pixels, num_pixels))

    # Loop through each channel to find b respective to the channel
    blend = np.zeros(target.shape)
    for c in range(channel):
        b = np.zeros((num_pixels))
        for y in range(height):
            for x in range(width):
                i = y * width + x

                if (mask[y,  x] == 0).any():
                    b[i] = target[y, x, c]
                else:
                    # use derived equation to fill in b. also need to check bounds to avoid errors
                    b[i] = 4 * source[y, x, c]
                    if x > 0:
                        b[i] -= source[y, x - 1, c]
                    if y > 0:
                        b[i] -= source[y - 1, x, c]
                    if x < width - 1:
                        b[i] -= source[y, x + 1, c]
                    if y < height - 1:
                        b[i] -= source[y + 1, x, c]

        # Solve Ax = b
        x = linalg.spsolve(A, b)

        # Reshape x to target size
        blend[:, :, c] = np.reshape(x, target[:, :, c].shape)

    return np.clip(blend, 0, 1)

if __name__ == '__main__':
    # Setting up the input output paths
    inputDir = '../Images/'
    outputDir = '../Results/'

    # False for source gradient, true for mixing gradients
    isMix = False

    # Source offsets in target
    offsets = [[210, 10], [10, 28], [140, 80], [-40, 90], [60, 100], [20, 20], [-28, 88]]

    # main area to specify files and display blended image
    for index in range(len(offsets)):
        # Read data and clean mask
        source, maskOriginal, target = Read(str(index+1).zfill(2), inputDir)

        # Cleaning up the mask
        mask = np.ones_like(maskOriginal)
        mask[maskOriginal < 0.5] = 0

        # Align the source and mask using the provided offest
        source, mask = AlignImages(mask, source, target, offsets[index])

        ### The main part of the code ###

        # Implement the PoissonBlend function

        poissonOutput = PoissonBlend(source, mask, target, isMix)

        # Writing the result

        if not isMix:
            plt.imsave("{}poisson_{}.jpg".format(outputDir, str(index+1).zfill(2)), poissonOutput)
        else:
            plt.imsave("{}poisson_{}_Mixing.jpg".format(outputDir, str(index+1).zfill(2)), poissonOutput)


from tkinter import Label, Button, BOTTOM, LEFT, RIGHT, Tk, Frame

from PIL.Image import Image

def click():
    print("Kliknięto!")

window = Tk()
window.title("Witamy w aplikacji!")
window.geometry('400x250')

# file = filedialog.askopenfilename(initialdir=path.dirname(__file__),filetypes=(("Python", "*.py"), ("Tekstowe", "*.txt")))
# file =filedialog.askdirectory()
#
# menu = Menu(window)
# new_item = Menu(menu,tearoff=0)
# new_item.add_command(label='New',command=click)
# new_item.add_command(label='Koniec')
# menu.add_cascade(label='File',menu=new_item)
# window.config(menu=menu)

# w = Frame(window, width=200, height=200, bg='yellow')
# w.pack()
# bf = Frame(window)
# bf.pack(side=BOTTOM)
# b = Button(w, text='tekst', fg="red")
# b.pack(side=LEFT)
# b2 = Button(w, text='tekst', fg="blue")
# b2.pack(side=RIGHT)
# b3 = Button(w, text='tekst', fg="green")
# b3.pack(side=LEFT)
# b1 = Button(bf, text='tekst1', fg='blue')
# b1.pack(side=BOTTOM)

obraz = Image.open("LOGO_DEVC.png")
obraz.resize((100, 100))
lbl = Label(window, image=obraz)
lbl.pack()

# tab_control = ttk.Notebook(window)
# tab1 = ttk.Frame(tab_control)
# tab2 = ttk.Frame(tab_control)
# tab_control.add(tab1, text='Pierwszy')
# tab_control.add(tab2, text='Drugi')
# lbl1 = Label(tab1, text="Treść 1")
# lbl2 = Label(tab2, text="Treść 2")
# lbl1.grid(column=0, row=0)
# lbl2.grid(column=0, row=0)
# tab_control.pack(expand=1, fill='both')
window.mainloop()


from partigames.environment.zoo_env import parallel_env
from interaction_learning.core.util import  AgentPositionGenerator
import matplotlib.pyplot as plt
import numpy as np
import pickle
import torch

plt.style.use("seaborn-whitegrid")

num_eval = 100

agent_position_generator = AgentPositionGenerator(num_eval * 10)
agent_reward = ["x"]
max_steps = 1000
ghost_agents = 0
render = False
num_agents = 2

env = parallel_env(num_agents=num_agents, agent_position_generator=agent_position_generator, agent_reward=agent_reward,
                   max_steps=max_steps, ghost_agents=ghost_agents, render=render)

obs = env.reset()
p_obs = obs["player_0"]
p_obs[3] = 0.1
p_obs[4] = 0.9

with open("impact_learner/all_ego_and_impact_task.agent", "rb") as input_file:
    interaction_agent = pickle.load(input_file)

with open(f"impact_learner/all_ego_task.agent", "rb") as input_file:
    other_agent = pickle.load(input_file)

with open(f"impact_learner/joint_learner_mis_goals.agent", "rb") as input_file:
    joint_agent = pickle.load(input_file)

task = "ta"
impact_task = "ie0"

x, y = np.meshgrid(np.arange(0.0, 1.025, .025), np.arange(0.0, 1.025, .05))

# Koordinaten und Richtungen definieren
z = np.zeros_like(x)
u = np.zeros_like(x)
v = np.zeros_like(x)
# 0 Still 1 right 2 down 3 left 4 up
for a in range(x.shape[0]):
    for b in range(x.shape[1]):
        p_obs[0] = x[a][b]
        p_obs[1] = y[a][b]
        # print(f"x: {x[a][b]} || y: {y[a][b]}")
        with torch.no_grad():
            state = torch.FloatTensor(p_obs).unsqueeze(0).to("cpu")
            q_task = interaction_agent.task_models[task].get_q(state)
            dist_task = interaction_agent.task_models[task].get_dist(q_task).cpu().numpy().flatten()
            support = len(dist_task)
            entropy = interaction_agent.calc_entropy(dist_task)
            linear_entropy = 1 - np.power(support, entropy) / support
            u[a][b] = linear_entropy

fig, ax = plt.subplots()
c = ax.pcolormesh(x, y, u, cmap='hot')
ax.axis([0, 1, 0, 1])
ax.set_title("Task Entropy")
fig.colorbar(c, ax=ax)
plt.show()


from .. import graph, using, style
from flask_babel import gettext as _l
import pygal

@graph(_l('Number of each notification type'))
@using('notifications')
def types_of_notifications(data):
    notify_table = data['notifications']

    if notify_table is None:
        return None

    not_type = notify_table.groupby(notify_table.url_type).count().time

    type_chart = pygal.Pie(style=style)
    for i, t in zip(not_type.index, list(not_type)):
        type_chart.add(i, t)

    return type_chart


"""
Provides the various special functions as UFuncs
"""
import _ufuncs
_token = "sf_"
_tokl  = len(_token)

for _name in dir(_ufuncs):
    if _name[:_tokl] == _token:
        _shortname = _name[_tokl:]
        _cmd = "%s = _ufuncs.%s" % (_shortname, _name)
        #printy cmd
        exec(_cmd)
del _token
del _tokl
del _shortname
del _name
del _cmd


class Methods:
    name_1="IBM"                                              #Static Variables
    year_1=1911

    def __init__(self,name,year):
        self.name=name                                        #Instance Variable..
        self.year=year

if __name__ == '__main__':
    m = Methods("Jala",2017)
    print(m.name_1)
    print(m.year_1)
    print(m.name)
    print(m.year)


"""
Given two arrays A and B of size N. A[i] represents the time by which you can buy ith car without paying any money.
B[i] represents the profit you can earn by buying ith car. It takes 1 minute to buy a car so, you can only buy the ith car when the current time <= A[i] - 1.
Your task is to find maximum profit one can earn by buying cars considering that you can only buy one car at a time.
"""
import heapq

class Solution:
    #A time B profit
    def solve(self, A, B):
        heap = []
        value = tuple(zip(A, B))
        value = sorted(value)
        time = 0
        for t,p in value:
            if time < t:
                heapq.heappush(heap, p)
                time+=1
            else:
                if heap[0] < p:
                    heapq.heappushpop(heap, p)
        return sum(heap)

t = Solution()
A = [ 1, 7, 6, 2, 8, 4, 4, 6, 8, 2 ]
B = [ 8, 11, 7, 7, 10, 8, 7, 5, 4, 9 ]
print(t.solve(A, B))


"""Service to deal with incidents"""
from typing import List, cast
import logging
from domain.incident import Incident, IncidentStatus
from domain.integrations.integration import Integration
from services.ticket_service import TicketService
from services.call_service import CallService
from services.integrated_service import IntegratedService
from utils.integration_enum import IntegrationType
from utils.dynamo import DynamoUtils

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

class IncidentService:
    """
    Class that handles incident actions
    """

    def __init__(self, team_id, integrated_services: List[IntegratedService]):
        self.team_id = team_id
        self.__set_integrated_services(integrated_services)

    def create_incident(self, incident_id, incident_name) -> Incident:
        """
        Creates an incident in the database
        Parameters:
            incident_id: In the slackbot context, this is the channel id
            incident_name: Name that will be set as a channel name and also saved in the db
        """
        # Create ticket
        ticket: Integration = self.create_ticket()

        # Create call
        call: Integration = self.create_call()

        incident = Incident(self.team_id, incident_id, incident_name)
        if ticket is not None:
            incident.set_ticket(ticket)
        if call is not None:
            incident.set_call(call)

        # Save incident
        DynamoUtils.create_incident(incident)
        return incident

    def get_call(self, incident_id) -> str:
        """Returns the call link for the given incident"""
        incident: Incident = DynamoUtils.get_incident(self.team_id, incident_id)
        if incident is not None and incident.has_call():
            return incident.call.get_link()
        return ""

    def get_ongoing_incidents(self) -> List[Incident]:
        """Returns all the incidents that are ongoing"""
        return DynamoUtils.get_ongoing_incidents(self.team_id)

    def get_today_incidents(self) -> List[Incident]:
        """Returns all incidents from today that are ongoing"""
        return DynamoUtils.get_today_incidents(self.team_id)

    # TODO - should also receive some text with the incident resolution and save in the DB
    def close_incident(self, incident_id):
        """Closes incident"""
        incident = Incident(self.team_id, incident_id)
        return DynamoUtils.update_incident_status(incident, IncidentStatus.CLOSED)

    def log_comment(self, incident_id: str, text: str) -> dict:
        """
        Adds a comment to the ticket associated to the incident_id received in the arguments
        """
        try:

            incident: Incident = DynamoUtils.get_incident(self.team_id, incident_id)
            if incident is None:
                return {
                    "status": "failure",
                    "failure_description": "Not ongoing incident in this channel, "
                    "please run this command in an incident channel",
                }
            if incident.has_ticket():
                issue_id = incident.ticket.get_link().split("browse/")[1]
                response = self.ticket_service.add_comment(issue_id, text)
                if (
                    response.get("success") is not None
                    and response.get("success") is True
                ):
                    return {"status": "ok"}
                else:
                    return {
                        "status": "failure",
                        "failure_description": response.get("descriptive_error"),
                    }
            else:
                return {
                    "status": "failure",
                    "failure_description": "No ticket associated to this incident",
                }
        except Exception as e:
            logger.error(f"could not log comment for team {self.team_id} - {e}")
            return {"status": "failure", "failure_description": "Could not log comment"}

    def create_ticket(self) -> Integration:
        """Creates a ticket for the incident"""
        if not hasattr(self, "ticket_service"):
            logger.error(f"no ticket integration for team {self.team_id}")
            return None
        logger.info("Creating ticket...")
        ticket: Integration = self.ticket_service.create_ticket()
        if ticket is not None:
            logger.info("Ticket created")
            return ticket
        else:
            logger.error(f"ticket could not be created for team {self.team_id}")
            return None

    def create_call(self) -> Integration:
        """Creates a call for the incident"""
        if not hasattr(self, "call_service"):
            logger.error(f"no call integration for team {self.team_id}")
            return None
        logger.info(f"Creating call... {self.team_id}")
        call: Integration = self.call_service.create_call()
        if call is not None:
            logger.info(f"call created {self.team_id}")
            return call
        else:
            logger.error(f"call could not be created {self.team_id}")
            return None

    def __set_integrated_services(self, integrated_services: List[IntegratedService]):
        """Sets in instance variables the integrated services being used"""
        for integrated_service in integrated_services:
            if integrated_service.get_type() == IntegrationType.CALL:
                self.call_service: CallService = cast(CallService, integrated_service)
            if integrated_service.get_type() == IntegrationType.TICKET:
                self.ticket_service: TicketService = cast(
                    TicketService, integrated_service
                )


# from crypt import methods
from multiprocessing import dummy
import sqlite3
from flask import *
import os
import sqlite3
from numpy import recfromcsv
import pandas as pd
from re import search
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import tensorflow as tf

from sqlalchemy import false

app = Flask(__name__)
app.static_folder = 'static'

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

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

@app.route('/thirtyGames', methods = ['POST'])
def thirtyGames():
    search = request.get_json()
    df = pd.read_json(r'static\json_files\full_json.json').dropna().drop_duplicates()

    first_run = True
    for category in search:
        # print(category)
        for term in search[f'{category}']:
            term = term.strip()
            search_result = df.loc[df[category].str.contains(f"{term}", case=False)]
            if first_run:
                complete_df = search_result
                first_run = False
            else:
                complete_df = pd.concat([complete_df,search_result]).drop_duplicates()
            # print(term)

    complete_df = complete_df.sort_values(by='userscore', ascending=False).head(100)

    #method 1
    data = complete_df.sample(n=30)['title'].to_list()

    return jsonify(data)

@app.route('/videogame_buddy.html')
def vgbuddy():
    return render_template('videogame_buddy.html')

@app.route('/browse_games.html')
def browse():
    return render_template('browse_games.html')

@app.route('/results.html')
def results():
    return render_template('results.html')

@app.route('/finalresults.html')
def finalresults():
    return render_template('finalresults.html')

@app.route('/data.html')
def data():
    return render_template('data.html')

@app.route('/trainAI', methods = ['POST'])
def train():
    input = request.get_json()

    df = pd.read_json(r'static\json_files\full_json.json').dropna().drop_duplicates(subset='title')
    df.drop('summary',axis=1,inplace=True)

    dummy_master = pd.get_dummies(df)

    train_df = pd.DataFrame()
    search_result = pd.DataFrame()

    for title in input['name']:

        try:
            search_result = dummy_master.loc[dummy_master[f'title_{title}'] == 1]
        except:
            print(f"Couldn't find {title}")

        train_df = pd.concat([search_result,train_df])

    # print(len(train_df))
    # print(len(input['status']))

    train_df['like'] = input['status']

    # print(train_df.head(5))

    train_df.drop(['Unnamed: 0'], axis=1,inplace=True)

    # dummy_df = pd.get_dummies(train_df)
    the_input_dim = train_df.shape[1] - 1

    X = train_df.drop('like',axis=1)
    y = train_df['like']

    dummy_master.drop('Unnamed: 0',axis=1,inplace=True)

    # print(f"X shape: {X.shape}")
    # print(f"dummy_master shape: {dummy_master.shape}")

    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42, stratify=y)

    scaler = StandardScaler()
    X_scaler = scaler.fit(X_train)
    X_train_scaled = X_scaler.transform(X_train)
    X_test_scaled = X_scaler.transform(X_test)

# stop = False
# while stop == False:
    nn = tf.keras.models.Sequential()
    nn.add(tf.keras.layers.Dense(units=60, activation='relu', input_dim=the_input_dim))
    nn.add(tf.keras.layers.Dense(units=30, activation='relu'))
    nn.add(tf.keras.layers.Dense(units=1, activation='sigmoid'))
    nn.summary()

    nn.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])

    # fit_model = nn.fit(X_train_scaled, y_train, epochs=10)

    model_loss, model_accuracy = nn.evaluate(X_test_scaled,y_test,verbose=2)
    print(f"Loss: {model_loss}, Accuracy: {model_accuracy}")

    dummy_master['like'] = nn.predict(x=dummy_master)

    predict_result = dummy_master.loc[dummy_master['like'] > .3].sample(n=5)

    for column in predict_result.columns:
        empty = True
        for value in predict_result[column]:
            if(value == 1):
                empty=False
        if(empty == True):
            predict_result.drop(column,axis=1,inplace=True)

    recommended_games = predict_result.columns[0:5].to_list()

    for x in range(5):
        recommended_games[x] = recommended_games[x].split('_',1)[1]
        print(recommended_games[x])

    # print(predict_result)
    # print(predict_result.shape)
    return jsonify(recommended_games)

@app.route('/gameSearch', methods = ['POST'])
def search():
    input = request.get_json()
    df = pd.read_json(r'static\json_files\full_json.json').dropna().drop_duplicates(subset='title')

    search_result = df.loc[df['title'].str.contains(f"{input}", case=False)]

    return (search_result.to_html())

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


from models.player import Player
from controllers.grab import Input
from views.view import View
from views.home_menu_view import HomeMenuView
from utils.menus import Menu

class PlayerController:

    """
        Création de l'instance d'un joueur.
    """

    def __init__(self):
        self.player = None
        self.view = View()
        self.input = Input()
        self.menu = Menu()
        self.viewmenu = HomeMenuView(self.menu)

        self.display_information = {"first_name": "Le prénom du joueur :",
                                    "last_name": "Le nom :",
                                    "gender": "Le genre : 'H' si c'est un homme, 'F' si c'est une femme :",
                                    "elo": "les points ELO du joueur :",
                                    "birthday": "Veuillez saisir la date de naissance dans le format 01/01/1901",
                                    "check": "les informations sont-elles correctes ? \n 1 pour oui, 2 pour non :",
                                    "invalid_info": "Qu'elles informations ne sont pas valide ?",
                                    "info": "'1' le prénom, '2' le nom, '3' le genre, '4' l'anniversaire, '5' l'elo "
                                    }

    def add_new_player(self):

        """
            Méthode qui demande à l'utilisateur de rentrer les informations d'un joueur lors de sa création.
            Renvoie une instance de joueur.
        """

        self.view.show(self.display_information["first_name"])
        first_name = self.input.get_input_str()
        self.view.show(self.display_information["last_name"])
        last_name = self.input.get_input_str()
        self.view.show(self.display_information["gender"])
        gender = self.input.get_gender()
        self.view.show(self.display_information["elo"])
        elo = self.input.get_input_int()
        self.view.show(self.display_information["birthday"])
        birthday = self.input.get_input_date()

        self.player = Player(first_name=first_name,
                             last_name=last_name,
                             gender=gender,
                             elo=elo,
                             ranking_points=0,
                             birthday=str(birthday),
                             ID=Player.player_id
                             )
        Player.player_id += 1
        self.check_player_info()
        self.player.serialize()
        return self.player

    def add_database_player(self, player_id):

        """
            Méthode qui récupère l'ID d'un joueur, pour créer une instance de joueur à partir de la base de donnée.
            Cette méthode est utilisée pour ajouter un joueur à un tournoi.
            Renvoie une instance de joueur.

            Argument:
                player_id = L'id du joueur qui correspond à son doc_id de la base de donnée.
        """

        player = Player().add_player_from_database(player_id=player_id)
        return player

    def check_player_info(self):

        """
            Méthode qui affiche les attributs d'un joueur pour que l'utilisateur valide
            ou modifie si besoin.
        """

        self.view.check_info_player(ID=self.player.ID,
                                    first_name=self.player.first_name,
                                    last_name=self.player.last_name,
                                    gender=self.player.gender,
                                    birthday=self.player.birthday,
                                    elo=self.player.elo,
                                    )

        self.view.show(self.display_information["check"])
        valid_info = self.input.get_input_int()
        if valid_info == 1:
            pass
        if valid_info == 2:
            self.view.show(self.display_information["invalid_info"])
            self.view.show(self.display_information["info"])
            info = self.input.get_input_int()
            if info == 1:
                self.view.show(self.display_information["first_name"])
                self.player.first_name = self.input.get_input_str()
            elif info == 2:
                self.view.show(self.display_information["last_name"])
                self.player.last_name = self.input.get_input_str()
            elif info == 3:
                self.view.show(self.display_information["gender"])
                self.player.gender = self.input.get_gender()
            elif info == 4:
                self.view.show(self.display_information["birthday"])
                self.player.birthday = self.input.get_input_date()
            elif info == 5:
                self.view.show(self.display_information["elo"])
                self.player.elo = self.input.get_input_int()

    def display_players_from_database(self):

        """
            Méthode qui affiche la liste de tous les joueurs de la base de donnée.
        """

        for player in Player().deserialize_players_in_database():
            self.view.check_info_player(ID=player.ID,
                                        first_name=player.first_name,
                                        last_name=player.last_name,
                                        gender=player.gender,
                                        birthday=player.birthday,
                                        elo=player.elo,
                                        )

    def deserialize__players_from_tournament(self, player_list):

        """
            Méthode qui crée les instances de joueurs d'un tournoi à partir de la liste
            de joueurs serialisés du tournoi.
            Renvoie une liste d'instances de joueurs

            Argument:
                player_list = liste des joueurs serialisés d'un tournoi
        """

        all_player_instance = list()
        for elem in player_list:
            player = Player(ID=elem['ID'],
                            first_name=elem['prenom'],
                            last_name=elem['nom'],
                            gender=elem['genre'],
                            birthday=elem['anniversaire'],
                            elo=elem['ELO'],
                            ranking_points=elem['points de tournoi']
                            )
            all_player_instance.append(player)
        return all_player_instance


import json

import torch
from torchvision import datasets, transforms

from omniglot_one_shot_dataset import OmniglotTransformation, OmniglotOneShotDataset

from cls_module.memory.ltm.visual_component import VisualComponent
from cls_module.memory.stm.aha import AHA

with open('aha_config.json') as config_file:
  config = json.load(config_file)

batch_size = 20
image_tfms = transforms.Compose([
    transforms.ToTensor(),
    OmniglotTransformation(resize_factor=0.5)
])

study_loader = torch.utils.data.DataLoader(
    OmniglotOneShotDataset('./data', train=True, download=True,
                           transform=image_tfms, target_transform=None),
    batch_size=batch_size, shuffle=False)

x, _ = next(iter(study_loader))

ltm = VisualComponent(config=config['ltm'], input_shape=x.shape, target_shape=None)
ltm.eval()
_, outputs = ltm(x, targets=x)
encoding = outputs['memory']['output']

stm = AHA(config=config['stm'], input_shape=encoding.shape, target_shape=x.shape)

stm.train()
stm(inputs=encoding, targets=x, labels=None)

stm.eval()
stm(inputs=encoding, targets=x, labels=None)


from django import forms
from .models import Files

class FilesForm(forms.ModelForm):
    class Meta:
      model = Files
      fields = ['file_name', 'file_docs']
      widgets = {
            'file_name': forms.TextInput(),
            'file_docs': forms.FileInput(),
        }


#!/usr/bin/env python

import os
import numpy as np
import U_Net_function_list as ff
import nibabel as nb
import pandas as pd
import segcnn

cg = segcnn.Experiment()

# find patient list
patient_list = ff.find_all_target_files(['*/*'],cg.seg_data_dir)
print(patient_list.shape)

result = []
for p in patient_list:
    patient_id = os.path.basename(p)
    patient_class = os.path.basename(os.path.dirname(p))
    print(patient_class,patient_id)

    # read time frame file
    f = open(os.path.join(p,'time_frame_picked_for_pretrained_AI.txt'),"r")
    t = int(f.read())

    # read threshold setting:
    threshold_file = os.path.join(p,'threshold.txt')
    if os.path.isfile(threshold_file) == 0:
        raise ValueError('no threshold txt file')
    else:
        threshold = open(threshold_file,"r")
        threshold = int(threshold.read())
        print(threshold)

    # load image and seg
    imgfile = os.path.join(cg.image_data_dir,patient_class,patient_id,'img-nii-0.625',str(t)+'.nii.gz')
    segfile = os.path.join(p,'seg-pred-1.5-upsample-retouch','pred_s_'+str(t)+'.nii.gz')
    if os.path.isfile(imgfile) == 1 and os.path.isfile(segfile) == 1:
        img = nb.load(imgfile).get_fdata()
        seg = nb.load(segfile).get_fdata()
        _,LV_pixels = ff.count_pixel(seg,1)
        LV_pixels = np.asarray(LV_pixels)

        # find the center of geometry
        x = LV_pixels[:,0]; x_mean = int(np.mean(x))
        y = LV_pixels[:,1]; y_mean = int(np.mean(y))
        z = LV_pixels[:,2]; z_mean = int(np.mean(z))

        # set a small ROI
        x_range = 40; z_range = 20
        seg_roi = seg[x_mean-x_range:x_mean+x_range, y_mean-x_range:y_mean+x_range, z_mean-z_range:z_mean+z_range]
        img_roi = img[x_mean-x_range:x_mean+x_range, y_mean-x_range:y_mean+x_range, z_mean-z_range:z_mean+z_range]

        # calculate intensity stat
        _,LV_pixels = ff.count_pixel(seg_roi,1)
        intensity_list = []
        for L in LV_pixels:
            intensity_list.append(img_roi[L[0],L[1],L[2]])
        mean_value = np.mean(np.asarray(intensity_list))
        std_value = np.std(np.asarray(intensity_list))
    else:
        print('no image or segmentation')
        mean_value =''
        std_value = ''

    result.append([patient_class,patient_id,threshold,mean_value,std_value])

df = pd.DataFrame(result,columns = ['Patient_Class','Patient_ID','threshold_set','mean_I','std_I'])
df.to_excel(os.path.join(cg.spreadsheet_dir,'LV_intensity_list.xlsx'),index = True)


print('Welcone to see your HAMMER FINANICAL SCORE')

income = input("What's your total yearly income? ")

income = int(income)

spending = input("Do you go over your budget? If so, how much? ")

spending = int(spending)

if spending > 0:
    spendingScore = 0
    print('Spending score is 0/10')
else:
    spendingScore = 10
    print('Spending score is 10/10')

debt = input("How much are you in debt? ")

debt = int(debt)

if debt > 0:
    debtScore = 0
    print('Debt score is 0/10')
else:
    debtScore = 10
    print('Debt score is 10/10')

emergency = input("How much do you have in your emergency fund? ")

emergency = int(emergency)

if emergency > 0:
    emergencyScore = 10
    print('Emergency score is 10/10')
else:
    emergencyScore = 0
    print('Emergency score is 0/10')

retirement = input("Whats in your retirement? ")

retirement = int(retirement)

if retirement > 0:
    retirementScore = 10
    print('Retirement score is 10/10')
else:
    retirementScore = 0
    print('Retirement Score is 0/10')

realEstate = input("Do you own any real estate? ")

print(type(realEstate))

if realEstate == 'yes' or 'Yes':
    realEstateScore = 10
    print("Real Estate score is 10/10")
else:
    realEstateScore = 0
    print('Real Estate score is 0/10')


from heapq import heappush, heappop
import sys
input = sys.stdin.readline

for _ in range(int(input())):
    max_heap, min_heap, tmp_max, tmp_min = [], [], set(), set()

    for __ in range(int(input())):
        arg = input().rstrip()

        if arg == "D 1":
            if max_heap != []:
                tmp = heappop(max_heap)
                tmp_max.add(-tmp)

                if max_heap == [] or -max_heap[0] < min_heap[0]:
                    max_heap, min_heap, tmp_max, tmp_min = [], [], set(), set()
                if len(min_heap) and -tmp == min_heap[0]:
                    heappop(min_heap)
                    tmp_max.remove(-tmp)
                    if not len(min_heap):
                        max_heap, min_heap, tmp_max, tmp_min = [], [], set(), set()
                while max_heap[0] in tmp_min:
                    tmp = heappop(max_heap)
                    tmp_min.remove(tmp)
        elif arg == "D -1":
            if min_heap != []:
                tmp = heappop(min_heap)
                tmp_min.add(-tmp)
                if min_heap == [] or -max_heap[0] < min_heap[0]:
                    max_heap, min_heap, tmp_max, tmp_min = [], [], set(), set()
                if len(max_heap) and -tmp == max_heap[0]:
                    heappop(max_heap)
                    tmp_min.remove(-tmp)
                    if not len(max_heap):
                        max_heap, min_heap, tmp_max, tmp_min = [], [], set(), set()
                while min_heap[0] in tmp_max:
                    tmp = heappop(min_heap)
                    tmp_max.remove(tmp)
        else:
            num = int(arg[2:])
            heappush(max_heap, -num)
            heappush(min_heap, num)

    if min_heap == [] and max_heap == []:
        print("EMPTY")
    elif max_heap != [] and min_heap == []:
        print(-max_heap[0], -max_heap[0])
    elif max_heap == [] and min_heap != []:
        print(min_heap[0], min_heap[0])
    else:
        print(-heappop(max_heap), heappop(min_heap))


import datetime as dt

class TokenData:

    """
    Class to save token data coming from an oauth request.
    It represents an oauth response. Contains access_token, refresh_token, and expiry date.
    """

    datetime_format = "%Y-%m-%d %H:%M:%S"

    def __init__(self, access_token, refresh_token, expiry_date):
        self.access_token = access_token
        self.refresh_token = refresh_token
        self.expiry_date = expiry_date

    def is_access_token_expired(self) -> bool:
        datetime_formatted = dt.datetime.strptime(
            self.expiry_date, self.datetime_format
        )
        return datetime_formatted <= dt.datetime.now()

    def is_valid(self) -> bool:
        return (
            bool(self.access_token)
            and bool(self.refresh_token)
            and bool(self.expiry_date)
        )


height = [1.87, 1.75, 1.2]
weight = [56, 58, 59]

import numpy as np

np_height = np.array(height)
np_weight = np.array(weight)

print(np_height)
print(np_weight)

bmi = np_weight / np_height * 2

print(bmi > 60)

# Students
# School@2021

import pandas as pd

dict = {
    "country": ["Burundi", "Tanzania", "Rwanda"],
    "capital": ["Bujumbura", "Dar-es-Salaam", "Kigali"],
    "population": [6.3, 15.4, 9.2],
    "area": [100.2, 303.1, 124.5]
}

brics = pd.DataFrame(dict)
print(brics)


from typing import List, Dict

N: int = int(input())
A: List[int] = list(map(int, input().split()))
B: List[int] = list(map(int, input().split()))

C: List[int] = A.copy()
for i in range(N):
    if C[i] - B[i] > 0:
        C[i] -= B[i]
    else:
        C[i+1] = C[i+1] - (B[i] - C[i]) if C[i+1] - (B[i] - C[i]) > 0 else 0
        C[i] = 0

beaten_monster: int = sum(A) - sum(C)
print(beaten_monster)


import pyaudio
import numpy
import math
from scipy.signal import lfilter
import spl_lib as spl

#Each chuck of array recorded
CHUNK = 9600
FORMAT = pyaudio.paInt16
#mono
CHANNELS = 1
#fs must be 44100 because of mic
RATE = 44100
#A-weighting
NUMERATOR, DENOMINATOR = spl.A_weighting(RATE)

class Recorder(object):

    def __init__(self):
        self.p = pyaudio.PyAudio()
        self.stream1 = self.open_pyaudio_stream(2)
        self.stream2 = self.open_pyaudio_stream(3)

    def open_pyaudio_stream(self,index):
        steam = self.p.open(format=FORMAT,
                            channels=CHANNELS,
                            rate=RATE,
                            input_device_index=index,
                            input=True,
                            frames_per_buffer=CHUNK)

        return steam

    def list_all_device(self):
        for i in range(self.p.get_device_count()):
            dev = self.p.get_device_info_by_index(i)
            print((i, dev['name'], dev['maxInputChannels']))

    def record(self,record_seconds):
        # RATE => frame per second
        # CHUNK => frame per buffer
        # RATE /CHUNK => buffer per second
        # RATE / CHUNK * SECOND => buffer

        for i in range(0, int(RATE / CHUNK * record_seconds)):
            data1 = self.stream1.read(CHUNK, exception_on_overflow=False)
            data2 = self.stream2.read(CHUNK, exception_on_overflow=False)
            decibel1 = self.to_decibel(data1)
            decibel2 = self.to_decibel(data2)
        return decibel1,decibel2

    def terminate(self):
        self.stream1.stop_stream()
        self.stream1.close()
        self.stream2.stop_stream()
        self.stream2.close()
        self.p.terminate()

    def to_decibel(self,data_chunk):

        # Int16 is a numpy data type which is Integer (-32768 to 32767)
        # If you put Int8 or Int32, the result numbers will be ridiculous
        decoded_block = numpy.fromstring(data_chunk, 'Int16')

        # This is where you apply A-weighted filter
        y = lfilter(NUMERATOR, DENOMINATOR, decoded_block)
        new_decibel = 20 * numpy.log10(spl.rms_flat(y))
        return new_decibel

    def sum_decibel(self,d1,d2):
        #sum of decibel cannot just sum
        return 10 * math.log10(10 ** (d1 / 10) + 10 ** (d2 / 10))

    def avg_decibel(self,d1,d2):
        #decibel division cannot just divide
        return self.sum_decibel(d1,d2) - (10 * math.log10(2))


# python-telegram-bot==13.15
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ConversationHandler
# /start, /komanda
from googletrans import Translator

tarjimon = Translator()

def tarjima(matn, b_til, i_til):
    t_matn = tarjimon.translate(matn, src=b_til, dest=i_til)
    return t_matn

TOKEN = '6044418045:AAGsrn5tB8ZHLw2I8bhWl0uNzzVEhmQqRPo'

def start(update, context):
    ism = update.message.from_user.first_name
    update.message.reply_html(f'<b>{ism}</b>, botimizga xush kelibsiz')
    return 1

def salom(update, context):
    update.message.reply_text('Assalomu aleykum')
    return 2

def translate(update, context):
    matn = update.message.text
    if matn == '/start':
        start(update, context)
        return 1
    else:
        r = matn.split(',')  # uz,ru,matn
        t_matn = tarjima(r[2], r[0], r[1])
        update.message.reply_text(t_matn.text)
        return 2

def main():
    updater = Updater(TOKEN, use_context=True)

    disp = updater.dispatcher

    conv_hand = ConversationHandler(
        entry_points=[CommandHandler('start', start)],
        states={
            1: [
                CommandHandler('salom', salom)
            ],
            2:[
                MessageHandler(Filters.text, translate)
            ]
        },
        fallbacks=[CommandHandler('start', start)]
    )
    disp.add_handler(conv_hand)
    # disp.add_handler(CommandHandler('start', start))
    # disp.add_handler(CommandHandler('salom', salom))
    # disp.add_handler(MessageHandler(Filters.text, translate))

    updater.start_polling()
    updater.idle()

main()


import random
import numpy as np
import matplotlib
from matplotlib import pyplot as plt
import seaborn as sns
from collections import deque
import networkx as nx
from networkx.algorithms.flow import shortest_augmenting_path

def create_random_graph(n, p):
    G = []
    skipDict = {}
    for i in range(n):
        adjList = []
        skipDict[i] = []
        currentVerticesLen = len(G)
        for m in range(n):
            if i == m:
                continue
            else:
                if m < currentVerticesLen and i in G[m]:
                    adjList.append(m)
                else:
                    if skipDict.get(m) is None or (skipDict.get(m) is not None and i not in skipDict[m]):
                        randVal = np.random.uniform(0.0, 1.0, 1)
                        if randVal <= p:
                            adjList.append(m)
                        else:
                            skipDict[i].append(m)
        G.append(adjList)
    return G

def search(G, visited, vertIndex):
    visited[vertIndex] = 1
    neighbors = G[vertIndex]
    for nIndex in neighbors:
        if visited[nIndex] == 0:
            search(G, visited, nIndex)

def find_components(G):
    numConnectedComponents = 0
    n = len(G)
    visited = [0 for i in range(n)]
    for vertIndex in range(n):
        if visited[vertIndex] == 0:
            numConnectedComponents += 1
            search(G, visited, vertIndex)

    return numConnectedComponents

def simulate_connectivity(n, p, nTrials):
    probabilityMap = {}
    for pi in range(nTrials):
        nTotalComps = 0
        nConnectedGraphs = 0
        for i in range(nTrials):
            G = create_random_graph(n, p)
            nComps = find_components(G)
            nTotalComps += nComps
            if nComps == 1:
                nConnectedGraphs += 1
        print(f'Parameter p: [{p}]')
        averageConnComps = nTotalComps/nTrials
        empProbability = nConnectedGraphs/nTrials
        print(f'Average number of CC: {averageConnComps}')
        print(f'Connectivity: {empProbability}')
        probabilityMap[pi] = [averageConnComps, empProbability]
        p += 1.0e-3
    return probabilityMap

def main():

    Graph = create_random_graph(50, 0.05)
    numComponents = find_components(Graph)
    print(f'Number of connected components for n=50, p=0.5: {numComponents}')

    n = 100
    p = 0.001
    nTrials = 100
    probabilityMap = simulate_connectivity(n, p, nTrials)
    x_values = [0.001+i*0.001 for i in range(nTrials)]
    y1_values = [probabilityMap[i][0] for i in range(nTrials)]
    y2_values = [probabilityMap[i][1] for i in range(nTrials)]

    plt.ylabel('Average connected components')
    plt.xlabel('probability')
    plt.title(f'Average # of connected components vs Probability')
    plt.plot(x_values, y1_values)
    plt.show()

    plt.ylabel('Empirical probability')
    plt.xlabel('probability')
    plt.title(f'Empirical probability vs Probability')
    plt.plot(x_values, y2_values)
    plt.show()

if __name__ == '__main__': main()


# Maximum Width Ramp
from typing import List

class Solution:
    def maxWidthRamp(self, A: List[int]) -> int:
        if not A: return 0
        idx = [i for i in range(len(A))]
        idx.sort(key=lambda x: A[x])
        m = idx[0]
        ans = 0
        for i in idx:
            if i < m:
                m = i
                continue
            ans = max(ans, i - m)
        return ans

s = Solution()
print(s.maxWidthRamp([9,8,1,0,1,9,4,0,4,1]))


import numpy

from pandas import DataFrame
from pandas import concat

from sklearn.preprocessing import MinMaxScaler

def extract_file_content_binary_float64(filename, shape, offset=0):
    """
    extract the content of a binary file which contains 64-bit float data into a 2-d array
    :param filename: the name of the file that needs to be extracted
    :param shape: the shape of the 2-d array
    :param offset: the offset of the file from where the content is read
    :return: a 2-d array contains the file content
    """
    dt = numpy.dtype(numpy.float64).newbyteorder('>')
    with open(filename, 'rb') as bytestream:
        if offset != 0:
            bytestream.seek(offset)
        buf = bytestream.read(shape[0] * shape[1] * 8)
        data = numpy.frombuffer(buf, dtype=dt)
        data = data.reshape(shape)
        return data

def extract_file_content_float64(filename, shape):
    """
    extract the content of a csv file which contains 64-bit float data into a 2-d array
    :param filename: the name of the file that needs to be extracted
    :param shape: the shape of the 2-d array
    :return: a 2-d array contains the file content
    """
    dt = numpy.dtype(numpy.float64).newbyteorder('>')
    data = numpy.ndarray(shape=(0, 0), dtype=dt)
    with open(filename, 'r') as file_stream:
        for line in file_stream:
            data = numpy.append(data, numpy.array(line.split(',')).astype(dt))
    data = data.reshape(shape)
    return data

def convert2csv_float64(src_filename, dest_filename, shape):
    """
    convert a binary file which contains 64-bit float data into a csv file
    :param src_filename: the name of the file to be converted
    :param dest_filename: the name of the file to be generated
    :param shape: the shape of the data set
    :return:
    """
    data = extract_file_content_binary_float64(src_filename, shape)

    with open(dest_filename, 'w') as file_stream:
        for i in range(0, shape[0]):
            file_stream.write(",".join(data[i, ...].astype('str')) + "\n")

def write_csv_file(file_name, data):
    with open(file_name, 'w') as file_stream:
        for row in data:
            file_stream.write(','.join(row.astype(numpy.str)) + '\n')

def modify_x(train_x, num):
    x = train_x.copy()
    for i in range(num - 1):
        data = numpy.roll(train_x, -(i + 1), axis=0)
        x = numpy.concatenate((x, data), axis=1)
    return x.reshape(train_x.shape[0], num, train_x.shape[1])

def time_series_to_supervised(data_frame, lag=1):
    """
    frame a sequence as a supervised learning problem
    :param data_frame:
    :param lag:
    :return:
    """
    df = DataFrame(data_frame)
    columns = [df.shift(i) for i in range(1, lag + 1)]
    columns.append(df)
    df = concat(columns, axis=1)
    df.fillna(0, inplace=True)
    return df

def difference(data_set, interval=1):
    """
    create a difference series
    :param data_set:
    :param interval:
    :return:
    """
    diff = list()
    for i in range(interval, len(data_set)):
        value = data_set[i] - data_set[i - interval]
        diff.append(value)
    return numpy.array(diff).reshape(data_set.shape[0] - interval, -1)

def inverse_difference(history, y_hat, interval=1):
    """
    invert difference value
    :param history:
    :param y_hat:
    :param interval:
    :return:
    """
    return y_hat + history[-interval]

def gen_scaler(min_, max_):
    scaler = MinMaxScaler(feature_range=(-1, 1))
    scaler.fit(numpy.concatenate((numpy.ones((1, 2)) * min_, numpy.ones((1, 2)) * max_), axis=0))
    return scaler


from modules.printutils import *

big_banner("""
    OOPS: Polymorism
    -------------------------------------

    An object can take on many (poly) forms (morph).

    Single class working in a similar way for
    multiple classes.

    i.e.: len: Can work with lists, tuples, strings.
""")

banner("""1st Type: Common Method:
Base class that has a method that's
overridden by a subclass""")
# ------------------------------

class Animal:

    def speak(self):
        raise NotImplementedError("Subclass needs to implement this method.")

class Dog( Animal ):

    def speak(self):
        return "woof"

class Cat( Animal ):

    def speak(self):
        return "meow"

margie = Cat()
albert = Dog()
nada = Animal()

print(margie.speak())
print(albert.speak())
# Error thrown here, nada is just Animal
# print(nada.speak())

banner("""2nd type: Special Methods:
The same operation works for
different types of objects. AKA:
magic methods.
---
Example: + is shorthand for __add__()
method. Works on numbers or strings
and does 2 different things to each
of those.""")
# ------------------------------

class Human:

    def __init__(self, first, last, age):
        self.first = first
        self.last = last
        self.age = age

    def __repr__(self):
        return f"{self.first} {self.last}"

    # entry for calling len(Human)
    def __len__(self):
        return self.age

    def __add__(self, partner):
        if isinstance(partner, Human):
            return Human(first='Newborn', last=self.last, age=0)
        return "You can't add that!"

    # this is multiply
    def __mul__(self, other):
        if isinstance(other, int):
            return [self for i in range(other)]
            # this would actually make a copy instead of
            # references to the original…
            #
            # import copy from copy
            # return [copy(self) for i in range(other)]
        return "You can't multiply that!"

j = Human("Jenny", "Larsen", 47)
k = Human("Kevin", "Jones", 49)

pl(j)
pl(len(j))

l = j+k
pl(l)

m = j * 3
pl(m)

banner("""Age changed to 22 on one,
you can see that they all share the
same class members.""")
# ------------------------------
m[0].age = 22
pl(m)
[print(obj.age) for obj in m]

banner("""Having quadruplets…""")
# ----------------------------------------
quadruplets =  (k + j) * 4
pl(quadruplets)


# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/stable/config

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))

import os, codecs

# -*- coding: utf-8 -*-
#
# AWS Sphinx configuration file.
#

#
# General information about the project.
#

# The long version of the service or SDK name, such as "Amazon Simple Workflow
# Service", "AWS Flow Framework for Ruby" or "AWS SDK for Java"
service_name_long = u'AWS Cloud Development Kit (BETA)'
service_docs_home = u'http://aws.amazon.com/documentation/CDK/'

project = u'User Guide'
project_desc = u'User Guide'
project_basename = u'CDK/ug'

# This name is used as the manual / PDF name. Don't include the extension
# (.pdf) here.
man_name = u'cdk-ug'

# Optional forum ID. If there's a relevant forum at forums.aws.amazon.com, then
# set the ID here. If not set, then no forum ID link will be generated.
forum_id = u'0'

# For the url docs.aws.amazon.com/docset-root/version/guide-name
docset_path_slug = u'CDK'
version_path_slug = u'latest'
guide_path_slug = u'ug'

# Add beta banner
guide_banner = """
This documentation is for the beta release of the AWS Cloud Development Kit (CDK).
"""

build_html = True
build_pdf = True
build_mobi = False #Or the Kindle ASIN if you need a Kindle build

feedback_folder_id = u'ceb51e73-f57c-4911-8bb5-f95408af889a'

extra_navlinks = []

# EXTRA_CONF_CONTENT -- don't change, move or remove this line!

# -- Project information -----------------------------------------------------

copyright = u'2018, Amazon Web Services'
author = u'Amazon Web Services'

# CDK_VERSION automatically replaced by build script
version = os.environ['CDK_VERSION']
release = version

# -- General configuration ---------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'm2r'
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = ['.rst', '.md']

# The master toctree document.
master_doc = 'index'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = [
    '**/_*.md'
]

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"
html_theme_path = ["_themes",]

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself.  Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
html_sidebars = {
    '**': [ 'globaltoc.html', 'searchbox.html' ]
}

rst_epilog = '\n'

common_includes = [
    '_includes/common_includes.txt',
    '_includes.txt'
]

for i in common_includes:
    if os.path.exists(i):
        f = codecs.open(i, 'r', 'utf-8')
        rst_epilog += f.read()
        f.close()

rst_epilog += '\n.. |cdk-version| replace:: ' + version + '\n'

# -- Options for HTMLHelp output ---------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'AWSCloudDevelopmentKitdoc'

show_sphinx = False

# -- Options for LaTeX output ------------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'AWSCloudDevelopmentKit.tex', u'AWS Cloud Development Kit Documentation',
     u'Amazon Web Service', 'manual'),
]

# -- Options for manual page output ------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'awsclouddevelopmentkit', u'AWS Cloud Development Kit Documentation',
     [author], 1)
]

# -- Options for Texinfo output ----------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'AWSCloudDevelopmentKit', u'AWS Cloud Development Kit Documentation',
     author, 'AWSCloudDevelopmentKit', 'One line description of project.',
     'Miscellaneous'),
]


"""
提问：测试文件过多时可以自动执行吗？
一个项目中往往会存在大量的程序测试文件，那么这些测试文件需要一个一个执行吗？Python有没有提供自动执行全部测试文件的支持

回答：未提供原生支持，可以自行实现
在编写屙屎代码时往往会将测试文件保存在同一路径下，这样就可以利用通配符加载与执行测试文件

本程序利用os模块获取了当前程序的工作目录，随后加载并执行了所有以“test”前缀命名的测试文件进行测试功能的说明
"""
# coding : UTF-8
import os, unittest			# 模块导入
class RunAllTest(unittest.TestCase): 	# 定义测试类
    def test_run(self): 		# 测试函数
        case_path = os.getcwd()	# 获取目录
        # 目录进行测试文件的名称匹配，以“test_”开头的均为测试文件
        discover = unittest.defaultTestLoader.discover(
                case_path, pattern="test_*.py")
        # 详细程度控制，内容包括：0（简单）、1（默认值）、2（详细）
        runner = unittest.TextTestRunner(verbosity=2)
        runner.run(discover) 		# 运行测试
if __name__=='__main__': 		# 判断程序执行名称
    unittest.main()			# 启动测试用例

基础篇/第14章功能测试/14.1.2unittest用例测试/加载当前目录中的测试文件.py

加载当前目录中的测试文件.py


#!/usr/bin/env python3

from datetime import datetime

from sqlalchemy import Table, Column, Integer, String, DateTime, Boolean, ForeignKey, and_,or_
from sqlalchemy.orm import relationship, joinedload, backref
from sqlalchemy.ext.declarative import declared_attr

from .basemodel import BaseModel
from .startstop_mixin import StartStopMixin
# from .user import User
# from .post import Post

import logging

MAX_LEN = dict(
    title=300
)

THREAD_TYPE = dict(
    PUBLIC=1,
    COMMENT=2,
    PRIVATE_CHAT=3,
    GROUP_CHAT=4,
    CHAT_OFFER=5,
)

class PostThread(BaseModel):

    __tablename__ = 'threads'

    id = Column('thread_id', Integer, primary_key=True)
    title = Column(String(MAX_LEN['title']))
    type = Column(Integer, default=THREAD_TYPE['PUBLIC'])

    posts = relationship('Post', primaryjoin = 'PostThread.id == Post.thread_id', lazy='noload')

    def add_post(self, post, users_read, last=False):
        self.posts.append(post)
        if users_read or last:
            for u2t in self.user2thread:
                if u2t.is_current():
                    if last:
                        u2t.last_post = post
                    if u2t.user_id in users_read:
                        u2t.last_readed_post = post

    @staticmethod
    def users_id_to_key(users_id):
        '''
            [3,1,54] => '1,3,54'
        '''
        return ','.join(map(str, sorted(set(users_id))))

    @staticmethod
    def key_to_users_id(key):
        '''
            '1,3,54' => [1,3,54]
        '''
        return [int(i) for i in key.split(',')]

    @staticmethod
    def get_chat_between_users(users, session):
        return session.query(
                PostThread
            ).join(
                User2Thread
            ).options(
                joinedload(PostThread.user2thread)
            ).filter(
                User2Thread.key == PostThread.users_id_to_key(users)
            ).filter(
                PostThread.type == THREAD_TYPE['PRIVATE_CHAT']
            ).filter(
                User2Thread.is_current()
            )

    @staticmethod
    def get_actual_chat(users, session, create=False):
        '''
            creates and returns a chat thread between users
            if the thread does already exist just returns
        '''
        thread = None
        key = PostThread.users_id_to_key(users)
        user2threads = {u_id: None for u_id in users}
        logging.debug(users)
        logging.debug(user2threads)
        # get thread if exists
        result = PostThread.get_chat_between_users(
            users,
            session
        ).all()
        # the thread was found
        if len(result):
            # thread = result[0].thread
            thread = result[0]
            # write user2thread instance to thread_members if they are current
            logging.error('hello')
            for u2t in thread.user2thread:
                logging.error(u2t._start_date)
                logging.error(u2t._stop_date)
                if u2t.is_current():
                    user2threads[u2t.user_id] = u2t
        # the thread wasn't found
        else:
            # if create flag wasn't set, returns nothing
            if not create:
                return
            # creating thread
            thread = PostThread(type=THREAD_TYPE['PRIVATE_CHAT'])
            session.add(thread)
        # create new user2thread isinstances if it's needed
        for u_id, u2t in user2threads.items():
            if not u2t and create:
                thread.user2thread.append(
                    User2Thread(
                        user_id=u_id,
                        key=key,
                        _start_date = datetime.utcnow(),
                        _stop_date = User2Thread.max_date()
                    )
                )
        return thread

class User2ThreadAbstract(BaseModel, StartStopMixin):

    __abstract__ = True

    id = Column('u2t_id', Integer, primary_key=True)

    @declared_attr
    def user_id(self):
        return Column(Integer, ForeignKey('users.user_id'))

    @declared_attr
    def thread_id(self):
        return Column(Integer, ForeignKey('threads.thread_id'))

    @declared_attr
    def user(cls):
        return relationship(
            'User',
            primaryjoin='%s.user_id == User.id' % cls.__name__,
            backref=cls.__name__.lower()
        )

    @declared_attr
    def thread(cls):
        return relationship(
            'PostThread',
            backref=cls.__name__.lower()
        )

class User2Thread(User2ThreadAbstract):

    __tablename__ = 'user2thread'

    key = Column(String, index=True)
    last_post_id = Column(Integer, ForeignKey('posts.post_id'))
    private_with_id = Column(Integer, ForeignKey('users.user_id'), index=True)
    last_readed_post_id = Column(Integer, ForeignKey('posts.post_id'), nullable=True)

    last_post = relationship(
        'Post',
        primaryjoin = 'User2Thread.last_post_id == Post.id',
    )
    last_readed_post = relationship(
        'Post',
        primaryjoin = 'User2Thread.last_readed_post_id == Post.id'
    )
    private_with = relationship(
        'User',
        primaryjoin="User2Thread.private_with_id == User.id",
    )

    allow_add_posts = Column(Boolean, default=True)
    allow_del_posts = Column(Boolean, default=False)
    allow_add_comments = Column(Boolean, default=True)
    allow_show_comments = Column(Boolean, default=True)

    @staticmethod
    def users_id_to_key(users_id):
        '''
            [3,1,54] => '1,3,54'
        '''
        return ','.join(map(str, sorted(set(users_id))))

    @property
    def chat_members(self):
        '''
            parse users_id from self.key
        '''
        logging.debug(self.key)
        # logging.error([self.user_id == int(i) for i in self.key])
        return [int(i) for i in self.key.split(',') if int(i) != self.user_id]

class IgnoredPosts(BaseModel):

    __tablename__ = 'ignored_posts'

    u2t_id = Column(Integer, ForeignKey('user2thread.u2t_id'), primary_key=True)
    post_id = Column(Integer, ForeignKey('posts.post_id'), primary_key=True)
    del_time = Column(DateTime(timezone=False), nullable=False, default=datetime.utcnow)
    del_user_id = Column(Integer, nullable=False)

    user2thread = relationship('User2Thread', backref='ignored_posts')
    post = relationship('Post', backref='ignored_posts')