''''
The modules required for the reduction of astronomical images and performing aperture photometry on the selected sources. All the modules except one use pure PYTHON packages, mostly derived from the astropy project. One of the modules still uses IRAF which we'll upgrade soon.

This program contains functions that perform:
1. BIAS correction.
2. Flat correction.
3. Cosmic Ray removal.
4. Finding the FWHM for a few sources, to determine the aperture radius.
5. Selecting the source and comparision stars for performing aperture photometry.
6. Conversion of time to Julian Dates (JD) or MJD.
7. Align the images with respect to a reference image (to make sure the co-ordinates are constant throughout the sample)
8. Calculate and subtract the global rms background.
9. Finds the sources in an image based on the DAOFIND algorithm. IRAFStarFinder, which uses IRAF algorithm can also be used.
10. Generates multiple aprtures on the locations selected by the user.
11. Generates annulus to estimate the local backgound and subtract it later.
12. Gets the total flux within the apertures and instrumental magnitudes.
13. Estimates the error on the flux and magnitudes assuming poisson noise.
14. Writes the output to ASCII file.
15. Plot the light curves using matplotlib.

written by:
Vivek, Kiran and Dimple at ARIES Nainital.

This was written keeping the 1.3m DFOT in mind and the customizations are needed for various telescopes/datasets if required.
'''
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

import numpy as np
import matplotlib as mp
import ccdproc,os,sys,time,random
from astropy import units as u
import matplotlib.pyplot as plt
from astropy.io import fits
from astropy.time import Time
from glob import glob
#from astroquery.astrometry_net import AstrometryNet
from astropy.wcs import WCS
import astroalign as aa
#from pyraf import iraf
#from iraf import noao,imred,specred
from astropy.nddata import CCDData
from astropy.stats import sigma_clipped_stats, SigmaClip
from astropy.visualization import ImageNormalize, LogStretch
from matplotlib.ticker import LogLocator
from photutils.background import Background2D, MeanBackground,SExtractorBackground
from photutils import find_peaks, CircularAperture, CircularAnnulus, aperture_photometry
from photutils.centroids import centroid_2dg
from astropy.stats import SigmaClip, mad_std
from photutils import Background2D, MedianBackground, DAOStarFinder
from photutils.utils import calc_total_error
from photutils.detection import findstars

def view_image(name,time):
    '''
    This module is meant for viewinga large number of images. The images
    can be seen as an animation. Will be helpful for large datasets as it doesn't
    require a lot of pointing and clicking.
    INPUT:
    name: The name of the image file.
    time: No. of seconds you'd like to see the image on screen

    OUTPUT:

    the images seen as animation, with a gap of 1 second between successive images.
    '''
    ims=[]
    data=fits.open(name)
    image=data[0].data
    head=data[0].header

    mean,std=np.mean(image),np.std(image)
    #image=image/np.median(image)
    im=plt.imshow(image,cmap='gray_r',origin='lower',vmin=mean-2*std,vmax=mean+2*std)
    #plt.scatter(124, 146, s=400,edgecolor='red',linewidth=1, facecolor='none')
    ims.append([im])
    plt.pause(time)
    plt.clf()

def clean_the_images(path,filename):
    '''
    This module is meant for cleaning the images. The tasks to be included are: bias correction,
    flat correction, trimming, overscan as well as the cosmic ray removal from the science cases.
    (For the time we are skipping the overscan and trimming part.

    INPUT:
    path: The directory where the images are kept (string)
    filename: The first few characters and the extension of the images (string). Example:
    j0946*fits, HD1104*fits etc.

    OUTPUT:

    cleaned images in the new directory: path/cleaned
    '''

    dir = path
    gain = 2 * u.electron / u.adu  # gain and readout noise are properties of the CCD and will change for different CCDs.
    readnoise = 7.5 * u.electron

    ra=input('Enter the RA of the source:   ')
    dec=input('Enter the DEC of the source: ')

    bias_files = sorted(glob(os.path.join(dir,'bias*.fits')))
    biaslist = []
    for i in range (0,len(bias_files)):
        data= ccdproc.CCDData.read(bias_files[i],unit='adu')
        #data = ccdproc.create_deviation(data, gain=gain, readnoise=readnoise)
        #data= data-(data.uncertainty.array)
        biaslist.append(data)
    masterbias = ccdproc.combine(biaslist,method='average',sigma_clip=True, sigma_clip_low_thresh=5, sigma_clip_high_thresh=5,
                             sigma_clip_func=np.ma.median, sigma_clip_dev_func=mad_std)
    masterbias.write('masterbias.fits', overwrite=True)
    mbias=ccdproc.CCDData.read('masterbias.fits',unit='adu')
    print('Master bias generated')
    print(" Mean and median of the masterbias: ",np.mean(masterbias), np.median(masterbias))

    flat_files=sorted(glob(os.path.join(dir,'flat*.fits')))
    flatlist = []
    for j in range(0,len(flat_files)):
        flat=ccdproc.CCDData.read(flat_files[j],unit='adu')
        flat_bias_removed=ccdproc.subtract_bias(flat,masterbias)
        flatlist.append(flat_bias_removed)

        def inv_median(a):
            return 1 / np.median(a)

    masterflat = ccdproc.combine(flatlist,method='median', scale=inv_median,
                                 sigma_clip=True, sigma_clip_low_thresh=5, sigma_clip_high_thresh=5,
                                 sigma_clip_func=np.ma.median, sigma_clip_dev_func=mad_std)
    masterflat.write('masterflat.fits', overwrite=True)
    mflat=ccdproc.CCDData.read('masterflat.fits',unit='adu')
    print('Master flat generated')
    print(" Mean and median of the masterflat: ",np.mean(masterflat), np.median(masterflat))

    file_names = sorted(glob(os.path.join(dir,filename)))
    for i in range(0,len(file_names)):
        image=ccdproc.CCDData.read(file_names[i],unit='adu')
        header=fits.getheader(file_names[i],0)
        bias_subtracted = ccdproc.subtract_bias(image, masterbias)
        flat_corrected = ccdproc.flat_correct(bias_subtracted, masterflat)
        cr_cleaned = ccdproc.cosmicray_lacosmic(flat_corrected,readnoise=7.5, sigclip=5,satlevel=65535,niter=20,cleantype='meanmask',gain_apply=True)
        #print('Cosmic rays removed')
        clean_file=file_names[i].replace('.fits','')

        fits.writeto(clean_file+'_cleaned.fits',cr_cleaned,header,overwrite=True)
        print('Image no-%i has been cleaned'%i)

def align_the_images(path,filename,ref_image):

    '''
    This function is meant for alignment of the images with respect to a reference image. To do this task we are using the astro-align package.

    INPUT:

    path: The directory where the images are kept (string)
    filename: The first few characters and the extension of the images (string). Example:
    j0946*fits, HD1104*fits etc.
    ref_image: Reference image which will be used to align the images.

    OUTPUT:

    aligned images.

    '''

    nfiles=sorted(glob(path+filename))
    image_data=fits.open(path+ref_image)
    reference_image=image_data[0].data
    for i in range(len(nfiles)):
        image_data=fits.open(nfiles[i])
        source_image=image_data[0].data
        header=image_data[0].header
        image_aligned,footprint=aa.register(source_image,reference_image)

        aligned_file=nfiles[i].replace('.fits','')
        fits.writeto(aligned_file+'_aligned'+'.fits',image_aligned,header,overwrite=True)

        print('No. %i done'%i)

def time_to_jd(path,filename):

    '''
    This function is used to take the observation epoch from the header and convert it to Julian dates. A header value 'JD' is written to the fits header. For the kinetic mode images, we take the time from the first frame and slice the images first and then add the intregration time to the frames to get the JD for each frame.

    INPUT:

    path: The directory where the images are kept (string)
    filename: The first few characters and the extension of the images (string). Example:
    j0946*fits, HD1104*fits etc.

    OUTPUT:

    1. The images sliced in the case of kinetic mode images.
    2. The Julian date updated to the header of the fits files.

    '''

    dir=path

    files=sorted(glob(os.path.join(dir,filename)))
    nof=np.zeros(len(files))
    for i in range(0,len(files)):
        data=fits.open(files[i])
        header=data[0].header
        image=data[0].data
        k=np.shape(image)
        nof[i]=k[0]

        check_header=header['ACQMODE']

        if (check_header=='Single Scan'):
            jd_up=image
            time=header['DATE']
            t=Time(time,format='isot',scale='utc')
            time_jd=t.jd
            header.insert(15,('JD',time_jd))
            files[i]
            mod_file_1=files[i].replace('.fits','')
            fits.writeto(mod_file_1+'_sliced'+'.fits',jd_up,header,overwrite=True)

            #print(files[i],t.jd,t.mjd,'single scan image')

        elif (check_header=='Kinetics'):

            print('kinetic mode image with no. of files:',nof[i])

            name_of_file=files[i]
            mod_file=name_of_file.replace('.fits','')
            time=header['DATE']
            t=Time(time,format='isot',scale='utc')
            t_jd=t.jd
            temp=int(nof[i])
            mod_jd=np.zeros(temp)
            exp_time=header['ACT']/86400  # for the 'day' from seconds calculation.
            mod_jd[0]=t_jd   # first frame time from the header and the next ones from the 'ACT'
            for j in range(1,temp):
                mod_jd[j]=mod_jd[j-1]+exp_time

            for k in range(0,len(mod_jd)):
                mod_header=header
                sliced_image=image[k]
                time_jd=mod_jd[k]
                mod_header.insert(15,('JD',time_jd))
                fits.writeto(mod_file+'_sliced_%g'%k+'.fits',sliced_image,mod_header,overwrite=True)
                print(mod_file+'_sliced_%g'%k+'.fits has been written')
                continue

        #os.system("mv *sliced* cleaned/")

def iraf_fwhm(filename):

    '''
    This function calculates the fwhm of the sources we'll select from the list of sources available to us. This uses a combination of IRAF and DS9 software at the moment, and we'll replace it with suitable python packages in time.

    INPUT:

    filename: The name of the file. Be sure to chose the best image among the group of images that you have. This image will also be the reference image for future alignment.

    OUTPUT:

    1.The mean  FWHM calculated from the sources selected by the user.
    2. The filename, which will be used as a reference image.

    '''
    print(' We need to get the FWHM using IRAF tasks.')
    print(' Opening DS9 for image display.')
    os.system("ds9 &")
    #filename=input('Enter the filename:')
    input('Please press enter here!!!')
    iraf.display(filename,1)
    print ('Please do the following:\n1. Press enter here and then click on any source in the DS9 window. \n2. Press (comma) in the middle of source(s) to get FWHM.\n3. Press q to quit. \n ')
    imx=iraf.imexam(Stdout=1)
    sum1=0
    if(imx[1].split()[10]=='INDEF'):
        fwhm=8.0
        print ('Fwhm:', 'INDEF', 'but taken as 8.0')
    else:
        for i in range(1,len(imx)):
            token=i
            sum1+=eval(imx[i].split()[10])
        fwhm=sum1/token
        print("Average FWHM in pixels:",fwhm, ' in arc seconds:',0.53*fwhm)
    return(fwhm,filename)

def source_list(filename):
    '''
    This function selects the sources based on the IRAF imexa task and dispayed through DS9. We point at the centre of the sources and the return is the x,y co-ordinates. Be careful to click in the centre. You can use zoom in function to get the centre. The apertures will be selected keeping the same locations as the centre.

    INPUT:
    filename: The name of the file. Be sure to chose the best image among the group of images that you have. This image will also be the reference image for future alignment.

    OUTPUT:

    The x,y co-ordinates selected by the user.

    '''
    print('*****************************')
    print('now selecting the sources')
    os.system("ds9 &")
    input("Please press enter")
    iraf.display(filename,1)
    print ('Please do the following:\n1. Press enter here and then click on any source in the DS9 window. \n2. Press (comma) in the middle of source(s) to get FWHM.\n3. Press q to quit. \n ')
    imx=iraf.imexam(Stdout=1)
    xval=np.zeros(len(imx))
    yval=np.zeros(len(imx))
    for i in range(1,len(imx)):
        xval[i]=eval(imx[i].split()[0])
        yval[i]=eval(imx[i].split()[1])
    with open('co_ordinates_list.txt', 'w') as f:
        for i in range(1,len(imx)):
            print(xval[i],'\t',yval[i],file=f)

    return(xval,yval)

def py_source_list(filename):
    '''
    This function selects the sources based on the python tasks task and dispayed through matplotlib. We point at the centre of the sources and the return is the x,y co-ordinates. Be careful to click in the centre. You can use zoom in function to get the centre. The apertures will be selected keeping the same locations as the centre.

    INPUT:
    filename: The name of the file. Be sure to chose the best image among the group of images that you have. This image will also be the reference image for future alignment.

    OUTPUT:

    The x,y co-ordinates selected by the user.
    '''

    #data1=fits.open(filename)
    #image1=data1[0].data

    def tellme(s):
        data=fits.open(filename)
        image=data[0].data
        mean,std=np.mean(image),np.std(image)
        print(s)
        plt.imshow(image,origin='lower',cmap='gray_r',vmin=mean-std,vmax=mean+std)
        plt.title(s, fontsize=16)
        #plt.draw()

    plt.clf()
    tellme('In this interactive window, we select sources')

    def input_source(val):

        val=int(val)
        print(val)
        return val

    vals=input('How many sources you want to select?')
    nsources=input_source(vals)

    plt.setp(plt.gca(), autoscale_on=True)

    plt.waitforbuttonpress()

    pts = []

    while len(pts) < nsources:
        tellme('Select 3 corners with mouse')
        pts = np.asarray(plt.ginput(nsources, timeout=-1))
        #print(pts[0],pts[1])

    #raise SystemExit
    plt.clf()
    for i in range(len(pts)):
        k=pts[i]
        x=k[0]
        y=k[1]
        new_x=np.arange(x-40,x+40,1).astype(int)
        new_y=np.arange(y-40,y+40,1).astype(int)
        mod_image=image1[new_x[0]:new_x[78],new_y[0]:new_y[78]]
        plt.plot(mod_image)
        plt.show()

def do_aperture_photometry(filename,count, fwhm,date):

    #fwhm,files=iraf_fwhm()

    #xpix,ypix=source_list(files)

    #ast=AstrometryNet()
    #ast.api_key= 'iqmqwvazpvolmjmn'
    '''
    choice=input("Enter the mode. Please use the keywords\n 'single' for single image, 'multiple'  for multiple images: \n\n")

    if (choice=='single'):
        print('single mode')
    elif (choice ==' multiple'):
        print(' multiple image mode')
    else:
        print('Enter valid choice!!!')
    '''
    data,header=fits.getdata(filename,header=True)
    #exposure=header['EXPOSURE']
    exposure=300
    #print('Exposure is',exposure)

    sigma_clip = SigmaClip(sigma=3, maxiters=10)
    bkg_estimator = SExtractorBackground()
    bkg = Background2D(data, (10,10), filter_size=(3, 3),sigma_clip=sigma_clip, bkg_estimator=bkg_estimator)
    back=bkg.background # this is the background we need for the background subtraction.
    back2=np.median(bkg.background)
    #print('median background is',back2)

    mask = data == 0
    unit = u.electron / u.s

    xdf_image = CCDData(data, unit=unit, meta=header, mask=mask)
    norm_image = ImageNormalize(vmin=1e-4, vmax=5e-2, stretch=LogStretch(), clip=False)
    xdf_image_clipped = np.clip(xdf_image, 1e-4, None)

    mean, median, std = sigma_clipped_stats(xdf_image.data, sigma=3.0, maxiters=20, mask=xdf_image.mask)

    #print('Finding the sources')

    #daofind = DAOStarFinder(fwhm=fwhm, threshold=5*std) # 3 sigma above the background.
    #sources = daofind(data-back)

    #sources_findpeaks = find_peaks(xdf_image.data, mask=xdf_image.mask, threshold=30.*std, box_size=30, centroid_func=centroid_2dg)

    #print('We have found:',len(sources),' sources')
    #print(sources)

    #print(sources['xcentroid'], sources['ycentroid'],sources['fwhm'])
    #positions=sources['xcentroid'], sources['ycentroid']
    positions=np.genfromtxt('co_ordinates_list.txt',unpack=True,usecols=(0,1))
    #print(positions)
    radii=[ fwhm,2*fwhm, 3*fwhm,4*fwhm,5*fwhm]

    #positions=(sources['xcentroid'], sources['ycentroid'])
    apertures = [CircularAperture(positions, r=r) for r in radii]

    an_ap = CircularAnnulus(positions, r_in=6*fwhm, r_out=6.2*fwhm)
    #apers = [apertures, annulus_apertures]

    #bkg_sigma=mad_std(data)
    effective_gain=exposure
    error=calc_total_error(data,back,effective_gain)

    #error=0.1*data
    phot_table = aperture_photometry(data-back, apertures,error=error)
    phot_table2=aperture_photometry(data-back,an_ap)

    bkg_mean = phot_table2['aperture_sum'] / an_ap.area
    bkg_sum = bkg_mean * an_ap.area

    final_sum0=phot_table['aperture_sum_0']-bkg_sum
    final_sum1=phot_table['aperture_sum_1']-bkg_sum
    final_sum2=phot_table['aperture_sum_2']-bkg_sum
    final_sum3=phot_table['aperture_sum_3']-bkg_sum
    final_sum4=phot_table['aperture_sum_4']-bkg_sum

    mag_back=-2.5*np.log10(bkg_mean/exposure)+22
    mag_0=-2.5*np.log10(final_sum0/exposure)+22
    mag_1=-2.5*np.log10(final_sum1/exposure)+22
    mag_2=-2.5*np.log10(final_sum2/exposure)+22
    mag_3=-2.5*np.log10(final_sum3/exposure)+22
    mag_4=-2.5*np.log10(final_sum4/exposure)+22

    #print(mag_back,mag_0,mag_1,mag_2,mag_3,mag_4)

    flux_err_0=phot_table['aperture_sum_err_0']
    mag_err_0=1.09*flux_err_0/final_sum0

    flux_err_1=phot_table['aperture_sum_err_1']
    mag_err_1=1.09*flux_err_1/final_sum1

    flux_err_2=phot_table['aperture_sum_err_2']
    mag_err_2=1.09*flux_err_2/final_sum2

    flux_err_3=phot_table['aperture_sum_err_3']
    mag_err_3=1.09*flux_err_3/final_sum3

    flux_err_4=phot_table['aperture_sum_err_4']
    mag_err_4=1.09*flux_err_4/final_sum4
    '''
    fig=plt.figure()
    plt.imshow(data,cmap='gray',origin='lower',vmin=mean-4*std,vmax=mean+4*std)
    colors=['red','salmon','yellow','blue','cyan']
    for i in range(len(apertures)):
        apertures[i].plot(color=colors[i], alpha=0.7)

    an_ap.plot(color='green', alpha=0.7)
    plt.show()
    '''
    with open ('{}_r.dat'.format(date),'w') as r:
        for i in range (len(phot_table)):
            #print(final_sum0[i],final_sum1[i],final_sum2[i],final_sum3[i],final_sum4[i],final_sum5[i],file=r)
            print(mag_back[i],mag_0[i],mag_err_0[i],mag_1[i],mag_err_1[i],mag_2[i],mag_err_2[i],mag_3[i],mag_err_3[i],mag_4[i],mag_err_4[i],file=r)

    print('No. {} file has been processed'.format(count+1))

    '''

    mag=-2.5*np.log10(final_sum/30)+25

    flux=final_sum
    flux_err=phot_table['aperture_sum_err_0']
    mag_err=1.09*flux_err/flux

    x=[phot.value for phot in phot_table['xcenter']]
    y=[phot.value for phot in phot_table['ycenter']]

    #with open('result.dat', 'w') as f:
    #with open('out.txt', 'w') as f:
    for i in range(len(x)):
        print(x[i],y[i],'\t',mag[i],mag_err[i])

    outfile=' '
    for i in range (len(phot_table)):
        outfile+=x[i]+ " "+ y[i]+" "+ mag[i]+" " +mag_err[i]
        outfile+='\n'

    out=open('result.txt','w')
    out.write(outfile,overwrite=True)
    out.close()

    '''

    '''

    phot_table['xcenter']

    wcs_header=ast.solve_from_image('bfc.fits')
    wcs=WCS(wcs_header)
    xpix=[phot.value for phot in phot_table['xcenter']]
    ypix=[phot.value for phot in phot_table['ycenter']]
    ra=np.zeros(len(xpix))
    dec=np.zeros(len(ypix))
    for i in range(len(ra)):
        ra[i],dec[i]=wcs.all_pix2world(xpix[i],ypix[i],0)

    plt.imshow(data,cmap='gray',vmin=30,vmax=300)

    plt.show()

    #print(phot_table)

    '''
def plot_light_curve(filename,list):
#~~~~~~~~~~~~~~~~~~~~~~for plotting purposes~~~~~~~~#
    mp.rcParams['font.family']='serif'
    mp.rcParams['xtick.major.size']=10
    mp.rcParams['xtick.major.width']=2
    mp.rcParams['xtick.minor.size']=7
    mp.rcParams['xtick.minor.width']=2
    mp.rcParams['ytick.major.size']=10
    mp.rcParams['ytick.major.width']=2
    mp.rcParams['ytick.minor.size']=7
    mp.rcParams['ytick.minor.width']=2
    mp.rcParams['axes.linewidth']=1.5
    mp.rcParams['xtick.labelsize']=36
    mp.rcParams['ytick.labelsize']=36
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#

    filelist=list
    files=sorted(glob(filelist))
    julian_date=np.zeros(len(files))
    for i in range(0,len(files)):
        data=fits.open(files[i])
        header=data[0].header
        julian_date[i]=header['JD']

    # This is a dummy for flux
    julian_date-=2458870
    fluxd=np.zeros(len(files))
    for i in range(len(files)):
        fluxd[i]=random.randint(5,8)

    flux_errd=0.05*fluxd
    #################

    fig,ax=plt.subplots()
    plt.plot(julian_date,fluxd,'ko',markersize=25,label='I band light curve')
    plt.errorbar(julian_date,fluxd,yerr=flux_errd,capsize=5,fmt=' ')
    plt.ylabel("Magnitudes (instrumental)",fontsize=36)
    plt.xlabel("Julian Dates (2458870+)",fontsize=36)
    #plt.title("Deviation in lag values with respect to the variability",fontsize=36)
    ax.tick_params(axis="both",which='minor',direction="in")
    ax.tick_params(axis="both",which='major',direction="in")
    ax.yaxis.set_ticks_position('both')
    ax.xaxis.set_ticks_position('both')
    ax.minorticks_on()
    ax.legend(fontsize=30)
    plt.show()


from sys import stdin
from collections import deque

n, m = map(int, stdin.readline().split())

graph = []
visit_graph = [[0 for _ in range(m)] for _ in range(n)]
position = [[0, 1], [1, 0], [-1, 0], [0, -1]]

for _ in range(n):
    graph.append(list(map(str, list(stdin.readline().strip()))))

count = 1
result = 0

for row in range(n):
    for col in range(m):
        if graph[row][col] == 'L':
            queue = deque([[row, col]])
            queue.append([-1, -1])  # 하나의 순환의 끝을 표시하기위한 좌표
            time = -1
            count += 1
            visit_graph[row][col] = count

            while queue:
                x, y = queue.popleft()

                # 하나의 순환이 끝나면 time 증가
                if [x, y] == [-1, -1]:
                    if queue:
                        queue.append([-1, -1])

                    time += 1
                    continue

                # 상하좌우 탐색하면서 방문 처리, 덱에 추가
                for p_x, p_y in position:
                    n_x = x + p_x
                    n_y = y + p_y

                    if 0 <= n_x < n and 0 <= n_y < m:
                        if graph[n_x][n_y] == 'L' and visit_graph[n_x][n_y] != count:
                            visit_graph[n_x][n_y] = count
                            queue.append([n_x, n_y])

            result = max(result, time)

print(result)


from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Firefox(executable_path="C://Users//seker//Documents//driver//geckodriver.exe")
driver.get("https://www.amazon.com")
driver.maximize_window()
driver.implicitly_wait(10)

#Capture all the cookies created by browser and number
cookies = driver.get_cookies()
print('Number of cookies:',len(cookies))
print("Before adding cookie:",cookies)

# How to add a new cookie to the browser. Cookies are stored in key-value form
cookie = {'name':'MyCookie','value':'13579'}
driver.add_cookie(cookie)
cookies = driver.get_cookies()
print('Number of cookies:',len(cookies))
print("After adding cookie:",cookies)

#To delete the cookie you need to know the cookie name
driver.delete_cookie("MyCookie")
cookies = driver.get_cookies()
print('After deleting a cookie:',len(cookies))

driver.close()


import itertools
import math

class Grid(list):

    def __init__(self, max_number_of_nodes=100, min_balance=0.5):
        """Create a list of couples of dimensions.

        The nth couple is the tightest balanced rectangle having at least n cells.
        A balanced rectangle satisfies: length / width > min_balance.
        [None, (1, 1), (2, 2), (2, 2), (2, 2), (3, 2), (3, 2), (3, 3), ...]
        """
        list.__init__(self)
        max_square_side = int(math.ceil(math.sqrt(max_number_of_nodes)))
        for n in range(max_square_side * max_square_side, 0, -1):
            for i in range(n, int(math.ceil(math.sqrt(n))) - 1, -1):
                if n % i == 0 and n / i / i > min_balance:
                    self.insert(0, (i, n // i))
                    break
            else:
                self.insert(0, self[0])
        self.insert(0, None)
        self[2:4] = [(2, 1), (3, 1)] # tweak the 2- and 3-box grids

    def get_nth_next(self, index, nth):
        """Return the nth next distinct rectangle after the index-th one."""
        acc = set()
        for i in itertools.count(index):
            acc.add(self[i])
            if len(acc) > nth:
                return self[i]

    def get_markdown(self):
        """Return the grid as a nicely formatted table. For the docs only."""
        (w, h) = map(max, zip(*self[1:]))
        result = \
            [
                [""] +
                list(map(lambda i: "**%s**" % i, range(1, w + 1)))
            ] + \
            [["---"] * (w + 1)] + \
            [
                ["**%s**" % row] +
                [""] * w for row in range(1, h + 1)
            ]
        for (n, (col, row)) in enumerate(self[1:], 1):
            result[row + 1][col] += (", %s" % n if result[row + 1][col] else str(n))
        return "\n".join(["| %s |" % " | ".join(row) for row in result])

if __name__ == "__main__":
    grid = Grid()
    print(grid.get_markdown())


# %% codecell
import os
import requests
from dotenv import load_dotenv

try:
    from scripts.dev.multiuse.help_class import help_print_arg
    from scripts.dev.telegram.methods.helpers import TelegramHelpers
    from scripts.dev.telegram.telegram_keys import return_telegram_keys
except ModuleNotFoundError:
    from multiuse.help_class import help_print_arg
    from telegram.methods.helpers import TelegramHelpers
    from telegram.telegram_keys import return_telegram_keys
# %% codecell

def telegram_push_message(text, sec_forms=False, **kwargs):
    """Push message to telegram chat."""
    method = kwargs.get('method', 'testing')
    testing = kwargs.get('testing', None)
    return_on_failure = kwargs.get('return_on_failure')
    if sec_forms:
        method = 'sec_forms'

    bot_api, chat_id = return_telegram_keys(method, **kwargs)

    if not text:
        help_print_arg('Text needs to be supplied to the function')
        raise Exception
    if not bot_api or not chat_id:
        help_print_arg("No bot_api or chat_id passed to telegram_push_message")
        raise Exception
    if testing:
        help_print_arg(f"telegram_push_message: bot_api: {str(bot_api)} chat_id: {str(chat_id)}")

    u1 = 'https://api.telegram.org/bot'
    u2 = f"{u1}{bot_api}/sendMessage?chat_id={chat_id}&text={text}"

    rp = requests.post(u2)

    if sec_forms:
        TelegramHelpers.write_sec_msg_log(rp)
    if return_on_failure and rp.status_code != 200:
        return rp
    if rp.status_code != 200:
        (help_print_arg(f"telegram_push_message: {str(rp.status_code)}"
                        f"{str(rp.content)} chat_id {str(chat_id)}"))

        return False
    if rp.status_code == 200:
        return rp

# %% codecell


# -*- coding: utf-8 -*-
"""
    CreatedDate: 2022-03-14
    FileName   : lengthOfLongestSubstring.py
    Author     : Honghe
    Descreption: 3. 无重复字符的最长子串
"""
class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        sub_str = ""
        left = 0
        right = 0
        max_length = 0
        for i in range(len(s)):
            if s[i] not in sub_str:
                right +=1
                max_length = max(max_length,right-left)
            else:
                index = sub_str.find(s[i])
                left += index+1
                right +=1
            sub_str = s[left:right]
        return max_length

if __name__ == '__main__':
    sol = Solution()
    s = "pwwkew"
    print(sol.lengthOfLongestSubstring(s))


import json

from absl.testing import absltest
import jax  # pylint: disable=unused-import
import jax.numpy as jnp
import numpy as np
from orbax.export.validate import validation_utils
import tensorflow as tf

class ValidationUtilsTest(tf.test.TestCase):

  def test_enhanced_json_encoder(self):
    """Make sure json.dumps support tf tensor, np array, bytes format."""
    input_dict = {
        "tf": tf.convert_to_tensor(np.arange(8)),
        "np": np.arange(8),
        "bytes": bytes("test message", "utf-8")
    }
    json.dumps(input_dict, cls=validation_utils.EnhancedJSONEncoder)

  def test_get_latency_stat(self):
    latencies = [88.0, 88.0, 90.0, 91.0, 88.5, 88.7, 87.0]
    num_batches, avg_in_ms, p90_in_ms, p99_in_ms = (
        validation_utils.get_latency_stat(latencies=latencies)
    )
    self.assertEqual(num_batches, 7)
    self.assertAlmostEqual(avg_in_ms, 88742.85714285714)
    self.assertAlmostEqual(p90_in_ms, 90400.0)
    self.assertAlmostEqual(p99_in_ms, 90940.0)

  def test_split_tf_floating_and_discrete_groups(self):
    """Test split_tf_floating_and_discrete_groups accept np,tf and jax array."""
    arrays = [np.array([0.1, 0.2, 0.3], dtype=np.float64), np.array([1, 2, 3])]
    tf_arrays = jax.tree_util.tree_map(tf.convert_to_tensor, arrays)
    jax_array = jax.tree_util.tree_map(jnp.array, arrays)
    expected_float_vals = [np.array([0.1, 0.2, 0.3], dtype=np.float64)]
    for arr in (arrays, tf_arrays, jax_array):
      float_vals, _ = validation_utils.split_tf_floating_and_discrete_groups(
          arr
      )
      self.assertAllClose(float_vals, expected_float_vals)

if __name__ == "__main__":
  absltest.main()


def bfs(start, end):
    q=[]
    q.append(start)
    v[start] = 1
    while q:
        t = q.pop(0)
        for i in G[t]:
            if v[i] == 0:
                q.append(i)
                v[i] = v[t] + 1

num = int(input())
con = int(input())
hihi = 0
G = [[] for _ in range(num+1)]
v = [0] * (num+1)
for i in range(con):
    start, end = map(int, input().split())
    G[start].append(end)
    G[end].append(start)
bfs(1, end)
for i in v:
    if i != 0 and i != 1:
        hihi += 1
print(hihi)


"""
CP1404/CP5632 - Practical
Various examples of using Python string formatting with the str.format() method
Want to read more about it? https://docs.python.org/3/library/string.html#formatstrings
"""

name = "Gibson L-5 CES"
year = 1922
cost = 16035.4

# 1922 Gibson L-5 CES for about $16,035!
print("{} {} for about ${:.0f}!".format(year, name, cost))

for i in range(0, 151, 50):
    print(f"{i:>3}")


import numpy as np
import pymc3 as pm
from scipy import stats

def from_posterior(param, samples):
    smin, smax = np.min(samples), np.max(samples)
    x = np.linspace(smin, smax, 100)
    y = stats.gaussian_kde(samples)(x)

    # what was never sampled should have a small probability but not 0,
    # so we'll extend the domain and use linear approximation of density on it
    # Note that the domain can not be extended beyond the interval [0,1].
    x = np.concatenate([[0], x, [1]])
    y = np.concatenate([[0], y, [0]])
    return pm.Interpolated(param, x, y)


# access each wheel and the camera onboard of PenguinPi

import numpy as np
import requests
import cv2

class PenguinPi:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port
        self.wheel_vel = [0, 0]

    ##########################################
    # Change the robot velocity here
    # tick = forward speed
    # turning_tick = turning speed
    ##########################################
    def set_velocity(self, command, tick=20, turning_tick=5, time=0):
        l_vel = command[0]*tick - command[1]*turning_tick
        r_vel = command[0]*tick + command[1]*turning_tick
        self.wheel_vel = [l_vel, r_vel]
        if time == 0:
            requests.get(
                f"http://{self.ip}:{self.port}/robot/set/velocity?value="+str(l_vel)+","+str(r_vel))
        else:
            assert (time > 0), "Time must be positive."
            assert (time < 30), "Time must be less than network timeout (20s)."
            requests.get(
                "http://"+self.ip+":"+str(self.port)+"/robot/set/velocity?value="+str(l_vel)+","+str(r_vel)
                            +"&time="+str(time))
        return l_vel, r_vel

    def get_image(self):
        try:
            r = requests.get(f"http://{self.ip}:{self.port}/camera/get")
            img = cv2.imdecode(np.frombuffer(r.content,np.uint8), cv2.IMREAD_COLOR)
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout) as e:
            print("Image retrieval timed out.")
            img = np.zeros((240,320,3), dtype=np.uint8)
        return img


def transform(num):
    ls = []
    s = ""
    while num > 0:
        temp = num % 2
        ls.append(temp)
        num = num // 2
    while len(ls) > 0:
        s += str(ls.pop())
    return s

if __name__ == "__main__":
    print(transform(10))


class BarrenLandSimple:

    def __init__(self, coord_arr: list, x_max=400, y_max=600):
        self.x_max = x_max
        self.y_max = y_max
        self.plot = self._create_plot(x_max, y_max)
        self._coord_list = self._build_coord_list(coord_arr)
        self._populate_barren_land()

    def find_all_fertile_areas(self) -> list:
        """
        This is the callable method of the class to start the
        algorithm. It starts at (0,0) on the array and starts
        incrementing x until it finds a 0 entry (fertile land).
        When it does, it calls a bfs starting at this point.
        It collects the results of each bfs, and sorts them in
        a list to return as the available plot sizes

        :return: list of plot sizes from smallest to largest
        """
        x, y = 0, 0
        result_list = []

        while x < self.x_max and y < self.y_max:
            # if the land is fertile, start a BFS
            if self._get_plot_point(x, y) == 0:
                result_list.append(self._bfs(x, y))

            # Traverse through every entry to find new fertile land
            if x == self.x_max - 1:
                x = 0
                y += 1
            else:
                x += 1

        return sorted(result_list)

    def _bfs(self, x: int, y: int) -> int:
        """
        This is the bulk of the work - a BFS that looks
        at every single entry in the array. If it hasn't been
        seen (value is 0), it marks the entry with a 1 and adds
        all surrounding entries to the queue. Each time it does
        this, it increments the plot_size count.
        Entries with a 1 (already seen) or -1 (barren land) are
        ignored

        :param x: starting x coordinate for the bfs
        :param y: starting y coordinate for the bfs
        :return: size of the plot integer
        """
        queue = []            # queue to hold neighbor nodes
        queue.append((x, y))  # starting node, stored as a tuple (x,y)
        plot_size = 0         # increments to count plot size

        while not queue == []:
            node = queue.pop(0)
            x, y = node

            if self._get_plot_point(x, y) == 0:
                plot_size += 1
                self._put_plot_point(x, y, 1)

                # Add neighbors to the queue if they're in boundaries
                if x > 0:
                    queue.append((x-1, y))
                if x < self.x_max - 1:
                    queue.append((x+1, y))
                if y > 0:
                    queue.append((x, y-1))
                if y < self.y_max - 1:
                    queue.append((x, y+1))

        return plot_size

    def _get_plot_point(self, x: int, y: int) -> int:
        """
        Returns the entry / value at point x,y from the
        2d array of land plots

        :param x: x coordinate of value
        :param y: y coordinate of value
        :return: value at (x,y) in array
        """
        return self.plot[y][x]

    def _put_plot_point(self, x: int, y: int, val: int):
        """
        Sets the entry in the 2d array to val

        :param x: x coordinate of value to set
        :param y: y coordinate of value to set
        :param val: value to set the entry to
        :return: None
        """
        self.plot[y][x] = val

    @staticmethod
    def _create_plot(x_max: int, y_max: int) -> list:
        """
        Static method to create 2d array of size x_max * y_max

        :param x_max: max x value
        :param y_max: max y_value
        :return: list of 2d array size x_max * y_max of all 0's
        """
        return [[0 for x in range(x_max)] for y in range(y_max)]

    def _populate_barren_land(self):
        """
        Marks the land barren (entry of -1) for
        all rectangles input by the user

        :return: None
        """
        for coords in self._coord_list:
            for y in range(coords["y0"], coords["y1"]+1):
                for x in range(coords["x0"], coords["x1"]+1):
                    self._put_plot_point(x, y, -1)  # Mark as barren

    @staticmethod
    def _build_coord_list(raw_coord_list: list) -> list:
        """
        Given a list of strings of form
        "x0 y0 x1 y1", ...
        transforms into a dictionary of form
        {
          "x0": x0,
          "y0": y0,
          ...
        }
        where the values are integers
        and returns a list of dictionaries

        :param raw_coord_list:
        :return:
        """
        coord_list = []
        for coords in raw_coord_list:
            coord_list.append(
                {
                    "x0": coords[0],
                    "y0": coords[1],
                    "x1": coords[2],
                    "y1": coords[3]
                }
            )
        return coord_list


def menu(request):
    menu = {
        'menu':{
            'administration':['Administración','administrations.index'],
            'clients':['Clientes','clients.index'],
            'inventories': ['Inventario','inventories.index'],
            'nodos': ['Nodos', 'nodos.index'],
            'plans':['Planes', 'routers.plans.index'],
            'routers':['Routers', 'routers.index'] ,
            'supports':['Soporte', 'supports.index'],
            'ventas':['Ventas', 'ventas.index'],
        }
    }
    return menu


"""Drawing flower"""

from turtle import *

def task_9():
    shape('turtle')
    radius=50
    count=3
    angle_to_turn=360/count
    while count>0:
        circle(radius)
        circle(-radius)
        left(angle_to_turn)
        count-=1
    done()

if __name__ == '__main__':
    task_9()


from django.conf.urls import include,url
# from . import views
from . import views
from django.views.generic import TemplateView

urlpatterns = [
	#url(r'^search/?$', views.MySearchView.as_view(), name='mysearch'),
	url(r'^search/?$', views.my_basic_search, name="mysearch"),
	# url(r'^search/', include('haystack.urls')),
	url(r'^viewcompany/(?P<goal>[\w|\W]+)/(?P<company_id>[0-9]+)/$',views.viewCompany, name='viewCompany'),
	url(r'^investor/(?P<user_id>[0-9]+)/$',views.viewInvestor, name='viewInvestor'),
	url(r'^investors/(?P<type_name>[\w|\W]+)/$',views.browseInvestor, name='browseInvestor'),
	url(r'^companies/(?P<goal_name>[\w|\W]+)/$',views.browseCompany, name='browseCompany'),
	url(r'^dashboard/?$', views.dashboard, name="dashboard"),
	url(r'^dashboard_investor/?$', views.dashboard_investor, name="dashboard_investor"),
	url(r'^dashboard_admin/?$', views.dashboard_admin, name="dashboard_admin"),
	url(r'^editprofile/?$', views.editProfile_company, name="editProfile_company"),
	url(r'^editprofile_investor/?$', views.editProfile_investor, name="editProfile_investor"),
	url(r'^investor_admin/(?P<investor_id>[0-9]+)/$', views.editInvestors_admin, name="editInvestors_admin"),
	url(r'^addinvestor_admin/?$', views.addInvestors_admin, name="addInvestors_admin"),
	url(r'^addcompany_admin/(?P<goal>[\w|\W]+)/$', views.addCompany_admin, name="addCompany_admin"),
	url(r'^company_admin/(?P<goal>[\w|\W]+)/(?P<company_id>[0-9]+)/$', views.editCompany_admin, name="editCompany_admin"),
	url(r'^viewmyprofile/?$', views.viewMyprofile_company, name="viewmyProfile_company"),
	url(r'^viewmyprofile_investor/?$', views.viewMyprofile_investor, name="viewmyProfile_investor"),
	url(r'^goal/?$', views.goalChoose, name="goalChoose"),
	url(r'^deletefile/(?P<goal>[\w|\W]+)/(?P<fid>[0-9]+)/$', views.deleteFile, name="deleteFile"),
	url(r'^google22cffa4b9737b2ae.html',TemplateView.as_view(template_name='mainsite/google22cffa4b9737b2ae.html'),)
]


class OrderedStream:

    def __init__(self, n: int):
        self.data = [None]*n
        self.ptr = 0

    def insert(self, idKey: int, value: str) -> List[str]:
        idKey-=1
        self.data[idKey]=value

        if idKey>self.ptr:
            return []

        while self.ptr < len(self.data) and self.data[self.ptr]:
            self.ptr+=1
        return self.data[idKey:self.ptr]

# Your OrderedStream object will be instantiated and called as such:
# obj = OrderedStream(n)
# param_1 = obj.insert(idKey,value)


#!/usr/bin/env python3

from pathlib import Path
import yaml
import re
import pandas as pd

ROOT = Path(__file__).resolve().parent.parent.parent
mkdocs_file = ROOT / "mkdocs.yml"
pattern_files = (ROOT / "src/patterns/dosdp-patterns").glob("*.yaml")
pattern_doc_dir = ROOT / "docs/editors-guide/patterns"
sample_data_dir = ROOT / "src/patterns/data/matches"
pattern_matches_location_raw = "https://raw.githubusercontent.com/monarch-initiative/mondo/master/src/patterns/data/matches"
pattern_matches_location_gh = (
    "https://github.com/monarch-initiative/mondo/blob/master/src/patterns/data/matches"
)

def curie_to_uri(curie):
    prefix, identifier = curie.split(":")
    if prefix == "owl":
        return f"http://www.w3.org/2002/07/owl#{identifier}"
    elif prefix == "oio":
        return f"http://www.geneontology.org/formats/oboInOwl#{identifier}"
    else:
        return f"http://purl.obolibrary.org/obo/{prefix}_{identifier}"

def curie_to_link(curie):
    return f"[{curie}]({curie_to_uri(curie)})"

def is_curie(s):
    return ":" in s

def render_token(var, mapping):
    var_str = mapping[var]
    if is_curie(var_str):
        return f"[{var}]({curie_to_uri(var_str)})"
    # check format ''class''
    p = re.compile(r"'([^']*)'")
    if p.match(var_str) and len(re.findall(r"'([^']*)'", var_str)) > 1:
        link_str = re.sub(
            r"'([^']*)'",
            lambda m: curie_to_link(pattern["classes"][m.group(1)]),
            var_str,
        )
        return f"{var}\({link_str}\)"
    else:
        if p.match(var_str):
            link_str = re.sub(
                r"'([^']*)'",
                lambda m: curie_to_uri(pattern["classes"][m.group(1)]),
                var_str,
            )
        else:
            link_str = curie_to_uri(pattern["classes"][var_str])

        return f"[{var}]({link_str})"

def render_equivalent(text, vars, pattern):
    ret = text % tuple("{" + render_token(var, pattern["vars"]) + "}" for var in vars)
    mapping = {}
    mapping.update(pattern["classes"])
    if "relations" in pattern:
        mapping.update(pattern["relations"])
    p = re.compile(r"'[^']*'")
    ret = re.sub(
        r"'([^']*)'",
        lambda m: "[" + m.group(1) + "](" + curie_to_uri(mapping[m.group(1)]) + ")",
        ret,
    )
    return ret

def render_str(text, vars, pattern):
    ret = text % tuple("{" + render_token(var, pattern["vars"]) + "}" for var in vars)
    return ret

# We might need to make changes to the mkdocs.yaml file
# mkdocs_yaml = yaml.load(mkdocs_file.read_text(), Loader=yaml.FullLoader)

pattern_lst = []

for pattern_file in pattern_files:
    print(pattern_file)
    pattern = yaml.load(pattern_file.read_text(), Loader=yaml.FullLoader)
    md_file_path = pattern_doc_dir / (pattern_file.stem + ".md")
    with md_file_path.open("w") as fout:
        fout.write(f"# {pattern['pattern_name']} \n\n")
        fout.write(f"[{pattern['pattern_iri']}]({pattern['pattern_iri']})\n")
        fout.write("## Description \n\n")
        fout.write(pattern["description"].replace("\n", "\n\n") + "\n")
        if "contributors" in pattern:
            fout.write("## Contributors \n")
            for contributor in pattern["contributors"]:
                fout.write(f"* [{contributor}]({contributor}) \n")
        if "name" in pattern:
            fout.write("## Name \n\n")
            fout.write(
                render_str(pattern["name"]["text"], pattern["name"]["vars"], pattern)
            )
            fout.write("\n\n")
        if "annotations" in pattern:
            fout.write("## Annotations \n\n")
            for anno in pattern["annotations"]:
                fout.write("* ")
                fout.write(
                    render_token(
                        anno["annotationProperty"], pattern["annotationProperties"]
                    )
                    + ": "
                    + render_str(anno["text"], anno["vars"], pattern)
                )
                fout.write("\n\n")
        if "def" in pattern:
            fout.write("## Definition \n\n")
            fout.write(
                render_str(pattern["def"]["text"], pattern["def"]["vars"], pattern)
            )
            fout.write("\n\n")
        if "equivalentTo" in pattern:
            fout.write("## Equivalent to \n\n")
            fout.write(
                render_equivalent(
                    pattern["equivalentTo"]["text"],
                    pattern["equivalentTo"]["vars"],
                    pattern,
                )
            )
            fout.write("\n\n")

        # Create sample table
        tsv_file = sample_data_dir / (pattern_file.stem + ".tsv")
        if tsv_file.is_file():
            examples = []
            try:
                df = pd.read_csv(tsv_file, sep="\t")
                ghurl = f"{pattern_matches_location_gh}/{pattern_file.stem}.tsv"
                if not df.empty:
                    examples.append("[mondo]({})".format(ghurl))
                    example = ghurl
                    dfh = df.head()
                    sample_table = dfh.to_markdown(index=False)
                    fout.write("## Data preview \n")
                    oboiri = "http://purl.obolibrary.org/obo/"
                    fout.write(
                        re.sub(
                            r"http://purl.obolibrary.org/obo/([^_]+)_([^\s]+)",
                            lambda m: f"[{m.group(1)}:{m.group(2)}]({m.group(0)})",
                            sample_table,
                        )
                    )
                    fout.write("\n\n")
                    fout.write(f"See full table [here]({example}) \n")
                else:
                    print("No matches!")
            except Exception as e:
                print("Error processing the tsv file!", e)
        else:
            print(str(tsv_file) + " does not exist!")
    pattern_lst.append((pattern_file.stem, pattern))

# create the index.md file
pattern_lst.sort(key=lambda x: x[1]["pattern_name"].lower())
index_md_path = pattern_doc_dir / "index.md"
with index_md_path.open("w") as fout:
    fout.write("# Design Patterns \n\n")
    fout.write("\n")
    fout.write("| Pattern | Description | \n")
    fout.write("|:---|:---|\n")
    for pattern_file_name, pattern in pattern_lst:
        description = pattern["description"].replace("\n", "<br/>")
        description = re.sub(
            r"(http://purl[^\s]*\.[yaml|sparql]+)",
            lambda m: f"[{m.group(1).split('/')[-1]}]({m.group(1)})",
            description,
        )
        fout.write(
            f"| [{pattern['pattern_name']}]({pattern_file_name}/) | {description} | \n"
        )


# flake8: noqa
# pylint: disable=invalid-name
# pylint: disable=line-too-long
# pylint: disable=missing-docstring
# pylint: disable=too-many-arguments
# pylint: disable=too-many-locals
"""
Auto-correlation Matrix
===============================================================================

Returns an auto-correlation matrix.

>>> from techminer2.analyze.correlation import auto_correlation_matrix
>>> matrix = auto_correlation_matrix(
...     #
...     # FUNCTION PARAMS:
...     rows_and_columns='authors',
...     method="pearson",
...     #
...     # ITEM PARAMS:
...     top_n=10,
...     occ_range=(None, None),
...     gc_range=(None, None),
...     custom_items=None,
...     #
...     # DATABASE PARAMS:
...     root_dir="example/",
...     database="main",
...     year_filter=(None, None),
...     cited_by_filter=(None, None),
... )
>>> matrix.df_.round(3)
                      Jagtiani J. 3:0317  ...  Zavolokina L. 2:0181
Jagtiani J. 3:0317                  1.00  ...                   0.0
Gomber P. 2:1065                    0.00  ...                   0.0
Hornuf L. 2:0358                    0.00  ...                   0.0
Gai K. 2:0323                       0.00  ...                   0.0
Qiu M. 2:0323                       0.00  ...                   0.0
Sun X./3 2:0323                     0.00  ...                   0.0
Lemieux C. 2:0253                   0.77  ...                   0.0
Dolata M. 2:0181                    0.00  ...                   1.0
Schwabe G. 2:0181                   0.00  ...                   1.0
Zavolokina L. 2:0181                0.00  ...                   1.0
<BLANKLINE>
[10 rows x 10 columns]

>>> matrix.list_cells_.head()
                  row              column  matrix_value
0  Jagtiani J. 3:0317  Jagtiani J. 3:0317           1.0
1  Jagtiani J. 3:0317    Gomber P. 2:1065           0.0
2  Jagtiani J. 3:0317    Hornuf L. 2:0358           0.0
3  Jagtiani J. 3:0317       Gai K. 2:0323           0.0
4  Jagtiani J. 3:0317       Qiu M. 2:0323           0.0

>>> print(matrix.prompt_) # doctest: +ELLIPSIS
Your task is ...

"""
from dataclasses import dataclass

from ..._common.format_prompt_for_dataframes import format_prompt_for_dataframes
from ..tfidf import tfidf
from .compute_corr_matrix import compute_corr_matrix

def auto_correlation_matrix(
    #
    # FUNCTION PARAMS:
    rows_and_columns,
    method="pearson",
    #
    # ITEM PARAMS:
    top_n=None,
    occ_range=(None, None),
    gc_range=(None, None),
    custom_items=None,
    #
    # DATABASE PARAMS:
    root_dir="./",
    database="main",
    year_filter=(None, None),
    cited_by_filter=(None, None),
    **filters,
):
    """Returns an auto-correlation.

    :meta private:
    """

    data_matrix = tfidf(
        #
        # TF PARAMS:
        field=rows_and_columns,
        #
        # ITEM FILTERS:
        top_n=top_n,
        occ_range=occ_range,
        gc_range=gc_range,
        custom_items=custom_items,
        #
        # DATABASE PARAMS:
        root_dir=root_dir,
        database=database,
        year_filter=year_filter,
        cited_by_filter=cited_by_filter,
        **filters,
    )

    custom_items = [
        " ".join(col.split(" ")[:-1]) for col in data_matrix.columns.tolist()
    ]

    corr_matrix = compute_corr_matrix(method=method, data_matrix=data_matrix)

    prompt = __prompt(corr_matrix, rows_and_columns)

    @dataclass
    class Results:
        df_ = corr_matrix
        prompt_ = prompt

        @property
        def list_cells_(self):
            matrix = self.df_.copy()
            matrix = matrix.melt(
                value_name="matrix_value",
                var_name="row",
                ignore_index=False,
            )
            matrix["column"] = matrix.index.to_list()
            matrix = matrix.reset_index(drop=True)
            matrix = matrix[["row", "column", "matrix_value"]]

            return matrix

        @property
        def heat_map_(self):
            #
            def make_heat_map(styler):
                styler.background_gradient(axis=None, vmin=1, vmax=5, cmap="Oranges")
                return styler

            return self.df_.style.pipe(make_heat_map)

    return Results()

def __prompt(corr_matrix, rows_and_columns):
    """Creates a co-occurrence matrix prompt."""

    corr_matrix = corr_matrix.copy()
    corr_matrix[corr_matrix < 0] = 0

    main_text = (
        "Your task is to generate a short paragraph of a research paper "
        "analyzing the auto-correlation values between the items of the "
        f"column '{rows_and_columns}' of a bibliographic dataset.\n\n"
        "Analyze the table below which contains the auto-correlation "
        f"values for the '{rows_and_columns}'. High correlation values "
        "indicate that the items tends to appear together in the same "
        "document and forms a group. Identify any notable patterns, "
        "trends, or outliers in the data, and discuss their implications "
        "for the research field. Be sure to provide a concise summary of "
        "your findings, in at most 50 words."
    )

    return format_prompt_for_dataframes(main_text, corr_matrix.round(3).to_markdown())


# python standard library
from abc import ABCMeta, abstractproperty, abstractmethod
from contextlib import closing
import shelve

# this package
from apcommand.baseclass import BaseClass

# this package
from commons import action_dict, radio_page
from commons import BroadcomWirelessData, BroadcomRadioData
from commons import BroadcomPages, set_page
from commons import BandEnumeration
from querier import BroadcomRadioQuerier, BroadcomSSIDQuerier

class BroadcomBaseData(object):
    """
    The base-data to hold the data-dict
    """
    def __init__(self):
        """
        Base Data constructor
        """
        super(BroadcomBaseData, self).__init__()
        return

    @classmethod
    def base_data(cls):
        """
        Returns data dictionary that only has action:Apply
        """
        return action_dict()

    @classmethod
    def base_24_ghz_data(cls):
        """
        data-dictionary to choose the 2.4 GHz interface
        """
        bwd = BroadcomWirelessData
        base_24_ghz_data = {bwd.wireless_interface: bwd.interface_24_ghz}
        return base_24_ghz_data

    @classmethod
    def base_5_ghz_data(cls):
        """
        data-dictionary to chose the 5 Ghz interface
        """
        bwd = BroadcomWirelessData
        base_5_ghz_data = {bwd.wireless_interface:bwd.interface_5_ghz}
        return base_5_ghz_data

    @classmethod
    def data(cls, action=True, band=None):
        """
        Builds a data-dict based on action and band

        :param:

         - `action`: if True, add action:Apply to the dict
         - `band`: if 2.4 or 5 add the interface selection data

        :return: base data-dict
        """
        band = str(band)
        if action:
            base_data = cls.base_data()
        else:
            base_data = {}

        if band.startswith('2'):
            base_data.update(cls.base_24_ghz_data())
        elif band.startswith('5'):
            base_data.update(cls.base_5_ghz_data())
        return base_data

    def __repr__(self):
        """
        Returns a representation of the data
        """
        return "{0}: Base: {1} 5GHz: {1} 2.4GHz: {2}".format(self.__class__.__name__,
                                                             self.base_data(),
                                                             self.base_5_ghz_data(),
                                                             self.base_24_ghz_data())

class BroadcomBaseCommand(BaseClass):
    """
    A base-class for the commands that change settings
    """
    __metaclass__ = ABCMeta
    def __init__(self, connection, band=None):
        """
        BroadcomBaseCommand constructor

        :param:

         - `connection`: A connection to the AP (HTTPConnection)
         - `band`: 2.4, 5, or None (chooses the data-dictionary)
        """
        super(BroadcomBaseCommand, self).__init__()
        self._logger = None
        self.connection = connection
        self._band = band
        self._base_data = None
        self._singular_data = None
        self._added_data = None
        self._non_base_data = None
        self._data = None
        self.previous_state = None
        self._querier = None
        self._shelf_name = None
        self._shelf_objects = None
        self._shelf_key = None
        self._asp_page = None
        return

    @abstractproperty
    def asp_page(self):
        """
        The page for the decorators to use (e.g. 'radio.asp')
        """

    @property
    def shelf_key(self):
        """
        a key to use for the cases where there's only one object to shelve
        """
        if self._shelf_key is None:
            self._shelf_key = "{0}_{1}".format(self.__module__,
                                               self.__class__.__name__)

    @property
    def shelf_objects(self):
        """
        The key:object dictionary to save (defaults to current singular-data)
        """
        return {self.shelf_key:self.singular_data}

    @property
    def shelf_name(self):
        """
        A name to use for any shelves created
        """
        if self._shelf_name is None:
            self._shelf_name = "{0}.shelve".format(__package__.split('.')[0])
        return self._shelf_name

    @property
    def querier(self):
        """
        A querier to get the previous state for the undo.

        This assumes radio.asp right now
        """
        if self._querier is None:
            self._querier = BroadcomRadioQuerier(connection=self.connection,
                                                 refresh=False,
                                                 band=self.band)
        return self._querier

    @property
    def band(self):
        """
        the band (2.4, 5, or None)
        """
        return self._band

    @band.setter
    def band(self, new_band):
        """
        sets the band and sets base_data, non_base_data to None
        """
        self.logger.debug('Setting to band: {0}'.format(new_band))
        self._band = str(new_band)
        self._data = None
        self._base_data = self._non_base_data = None
        self._querier = None
        return

    @property
    def base_data(self):
        """
        A data-dictionary to add commands to
        """
        if self._base_data is None:
            self.logger.debug("setting the base-data using band {0}".format(self.band))
            self._base_data = BroadcomBaseData.data(band=self.band)
            self.logger.debug('base-data: {0}'.format(self._base_data))
        return self._base_data

    @abstractproperty
    def singular_data(self):
        """
        A data dictionary with the command-specific data (or empty if querying)
        """
        if self._singular_data is None:
            self._singular_data = {}
        return self._singular_data

    @property
    def added_data(self):
        """
        Maintains data added from other commands
        """
        if self._added_data is None:
            self._added_data = {}
        return self._added_data

    @property
    def non_base_data(self):
        """
        The singular data updated with any added data
        """
        if self._non_base_data is None:
            self._non_base_data = self.singular_data.copy()
            self._non_base_data.update(self.added_data)
        return self._non_base_data

    @property
    def data(self):
        """
        The data dictionary to send to the server
        """
        if self._data is None:
            self._data = self.base_data.copy()
            self._data.update(self.non_base_data)
        return self._data

    def __iadd__(self, other):
        """
        Adds the other's non_base_data to this added_data

        """
        self.added_data.update(other.non_base_data)
        self._data = self._non_base_data = None
        return self

    def __add__(self, other):
        """
        Adds the other's non_base_data to this added_data

        **For this to work the sub-classes can't change the constructor interface**
        """
        new_object = self.__class__(connection=self.connection,
                                    band=self.band)
        new_object.added_data.update(other.non_base_data)
        return new_object

    def __sub__(self, other):
        """
        Removes the other's non-base data from added_data in new object

        :return: object with this data minus other's non_base_data
        """
        new_object = self.__class__(connection=self.connection,
                                    band=self.band)
        new_object._added_data = self.added_data.copy()

        for key in other.non_base_data.iterkeys():
            if key in new_object.added_data:
                del new_object.added_data[key]
        return new_object

    @set_page
    def __call__(self):
        """
        The main method to change settings
        """
        self.store()
        self.connection(data=self.data)
        return

    def undo(self):
        """
        This is an attempt to start bringing this into line with the Command Pattern

        But it isn't clear that it will be easy (or possible) in every case -
        so it isn't made a requirement
        """
        raise NotImplemented("undo not supported for this command")

    def store(self):
        """
        shelve data from this class (use in __call__, not meant for public)

        :precondition: self.shelf_objects is a dictionary of things to shelve
        :postocondition: objects is shelf_objects is shelved
        """
        with closing(shelve.open(self.shelf_name, writeback=True)) as open_shelf:
            for key, value in self.shelf_objects.iteritems():
                open_shelf[key] = value
        return

    def load(self):
        """
        Loads the shelved data for the undo method
        (to prevent circular calls using shelf_objects' keys, just uses shelf_key)

        :return: dictionary of objects using shelf_objects' keys
        :raise: KeyError if an item in shelf_objects isn't on the shelf
        """
        with closing(shelve.open(self.shelf_name, flag='r')) as open_shelf:
            data = open_shelf[self.shelf_key]
        return data

# end class BroadcomBaseCommand

class StateData(object):
    __slots__ = ()
    state = 'state'
    band = 'band'

class EnableInterface(BroadcomBaseCommand):
    """
    An interface enabler
    """
    def __init__(self, *args, **kwargs):
        super(EnableInterface, self).__init__(*args, **kwargs)
        self._enable_24_data = None
        self._enable_5_data = None
        self._disable = None
        return

    @property
    def asp_page(self):
        """
        radio.asp
        """
        if self._asp_page is None:
            self._asp_page = BroadcomPages.radio
        return self._asp_page

    @property
    def disable(self):
        """
        An interface disabler
        """
        if self._disable is None:
            self._disable = DisableInterface(connection=self.connection,
                                             band=self.band)
        return self._disable

    @property
    def shelf_objects(self):
        """
        A dictionary of shelf_key: band
        """
        previous_state = self.querier.state
        return {self.shelf_key:self.band}

    def undo(self):
        """
        Disable the interface

        This is actually just a call to DisableInterface...
        """
        try:
            self.band = self.load()
            self.disable()
        except KeyError as error:
            self.logger.debug(error)
            self.logger.debug('No entry on the shelf for {0}'.format(self.shelf_key))
        return

    @property
    def singular_data(self):
        """
        The data to enable the interface
        """
        band = str(self.band)
        if band.startswith('2'):
            return self.enable_24_data
        elif band.startswith('5'):
            return self.enable_5_data

    @property
    def enable_5_data(self):
        """
        The data to send to the connection to enable 5 GHz

        :return: dict of data-values for the connection
        """
        if self._enable_5_data is None:
            radio_data = BroadcomRadioData
            self._enable_5_data = {radio_data.interface :radio_data.radio_on}
        return self._enable_5_data

    @property
    def enable_24_data(self):
        """
        The data to send to the connection to enable 2.4 GHz

        :return: dict of data-values for the connection
        """
        if self._enable_24_data is None:
            radio_data = BroadcomRadioData
            self._enable_24_data = {radio_data.interface:radio_data.radio_on}
        return self._enable_24_data

class DisableInterface(BroadcomBaseCommand):
    """
    An interface enabler
    """
    def __init__(self, *args, **kwargs):
        super(DisableInterface, self).__init__(*args, **kwargs)
        self._disable_24_data = None
        self._disable_5_data = None
        self._enable = None
        return

    @property
    def asp_page(self):
        """
        radio.asp
        """
        if self._asp_page is None:
            self._asp_page = BroadcomPages.radio
        return self._asp_page

    @property
    def enable(self):
        """
        An interface enabler for the undo
        """
        if self._enable == None:
            self._enable = EnableInterface(connection=self.connection,
                                           band=self.band)
        return self._enable

    @property
    def querier(self):
        """
        A querier to get the previous state
        """
        if self._querier is None:
            self._querier = BroadcomQuerier(connection=self.connection,
                                            refresh=True,
                                            band=self.band)
        return self._querier

    @property
    def shelf_objects(self):
        """
        dictionary of shelf_key: band
        """
        return {self.shelf_key: self.band}

    def undo(self):
        """
        enables the interface
        """
        try:
            self.band = self.load()
            self.enable()
        except KeyError as error:
            self.logger.debug(error)
            self.logger.debug('No entry on the shelf for {0}'.format(self.shelf_key))
        return

    @property
    def singular_data(self):
        """
        The data to enable the interface

        This isn't persistent, as changing the band changes the data
        """
        band = str(self.band)
        if band.startswith('2'):
            return self.disable_24_data
        elif band.startswith('5'):
            return self.disable_5_data

    @property
    def disable_5_data(self):
        """
        The data to send to the connection to disable 5 GHz

        :return: dict of data-values for the connection
        """
        if self._disable_5_data is None:
            radio_data = BroadcomRadioData
            self._disable_5_data = {radio_data.interface: radio_data.radio_off}
        return self._disable_5_data

    @property
    def disable_24_data(self):
        """
        The data to send to the connection to enable 2.4 GHz

        :return: dict of data-values for the connection
        """
        if self._disable_24_data is None:
            radio_data = BroadcomRadioData
            self._disable_24_data = {radio_data.interface: radio_data.radio_off}
        return self._disable_24_data

class SetChannel(BroadcomBaseCommand):
    """
    A channel setter for the AP
    """
    def __init__(self, *args, **kwargs):
        super(SetChannel, self).__init__(*args, **kwargs)
        self._channel_map = None
        self._channel = None
        return

    @property
    def asp_page(self):
        """
        radio.asp
        """
        if self._asp_page is None:
            self._asp_page = BroadcomPages.radio
        return self._asp_page

    @property
    def shelf_objects(self):
        """
        A dictionary of shelf_key: channel for current band
        """
        self.querier.band = self.band
        channel = self.querier.channel
        return {self.shelf_key: channel}

    @property
    def channel(self):
        """
        returns the channel
        """
        return self._channel

    @channel.setter
    def channel(self, new_channel):
        """
        Sets the channel, the band, and singular_data based on the channel.
        """
        channel = str(new_channel)
        self.band = self.channel_map[channel]
        self._singular_data = {BroadcomRadioData.control_channel:channel}
        return

    @property
    def channel_map(self):
        """
        Map of channel to data-dictionary
        """
        if self._channel_map is None:
            channel_24 = [str(channel) for channel in range(1,12)]
            channel_24_data = ['2.4'] * len(channel_24)
            # these are the only channels that match the Atheros channels we chose
            channel_5 = BroadcomRadioData.channels_5ghz
            channel_5_data = ['5'] * len(channel_5)
            channels = channel_24 + channel_5
            data = channel_24_data + channel_5_data
            self._channel_map = dict(zip(channels, data))
        return self._channel_map

    @property
    def singular_data(self):
        """
        This is a pass-through (it has to be set when given a channel)
        """
        return self._singular_data

    def undo(self):
        """
        Sets the channels found on the shelve
        """
        try:
            self.channel = self.load()
            self()
        except KeyError as error:
            self.logger.debug(error)
            self.logger.debug("No entry on shelf for {0}".format(self.shelf_key))
        return

class SetSideband(BroadcomBaseCommand):
    """
    A side-band ('upper' or 'lower') setter
    """
    def __init__(self, *args, **kwargs):
        super(SetSideband, self).__init__(*args, **kwargs)
        self.band = '5'
        self._direction = None
        return

    @property
    def asp_page(self):
        """
        radio.asp
        """
        if self._asp_page is None:
            self._asp_page = BroadcomPages.radio
        return self._asp_page

    @property
    def singular_data(self):
        """
        This has to be set in the call when you know what the direction is
        """
        return self._singular_data

    @property
    def direction(self):
        """
        upper or lower
        """
        return self._direction

    @direction.setter
    def direction(self, new_direction):
        """
        sets the direction, band, and singular_data
        """
        direction = new_direction.lower()
        if direction.startswith('l'):
            self._direction = 'lower'
        elif direction.startswith('u'):
            self._direction = 'upper'
        #else raise some kind of error
        self._singular_data = {BroadcomRadioData.sideband:self._direction}
        return

    def __call__(self):
        """
        Sets the sideband ('upper' or 'lower') for 5GHz
        """
        self.connection(data=self.data)
        return
# end SetSideband

# python standard library
import unittest
import random
import string

# third-party
from mock import MagicMock, call, patch

random_letters = lambda: ",".join((random.choice(string.letters) for char in xrange(random.randint(1,5))))

class BadChild(BroadcomBaseCommand):
    def query(self):
        return

class BadChild2(BroadcomBaseCommand):
    def base_data(self):
        return

class EvilChild(BroadcomBaseCommand):
    def asp_page(self):
        return

    def base_data(self):
        return

    def query(self):
        return

    @property
    def singular_data(self):
        return

    def __call__(self):
        return

    def path(self):
        return

# this can't be random in the class for testing to work
# Because __add__  creates a new object
test_data = {random_letters(): random_letters()}
test_data_2 = {random_letters(): random_letters()}
test_data_3 = {random_letters(): random_letters()}
class TestChild(BroadcomBaseCommand):
    def __init__(self, *args, **kwargs):
        super(TestChild, self).__init__(*args, **kwargs)
        return

    def asp_page(self):
        return BroadcomPages.radio

    @property
    def singular_data(self):
        if self._singular_data is None:
            self._singular_data = test_data
        return self._singular_data

    def __call__(self):
        return

    def __add__(self, other):
        t = TestChild(self.connection)
        t.added_data.update(other.non_base_data)
        return t

    def path(self):
        return

class TestChild2(TestChild):
    def __init__(self, *args, **kwargs):
        super(TestChild2, self).__init__(*args, **kwargs)
        return

    @property
    def singular_data(self):
        if self._singular_data is None:
            self._singular_data = test_data_2
        return self._singular_data

class TestChild3(TestChild):
    def __init__(self, *args, **kwargs):
        super(TestChild3, self).__init__(*args, **kwargs)
        return

    @property
    def singular_data(self):
        if self._singular_data is None:
            self._singular_data = test_data_3
        return self._singular_data

class TestBroadcomCommands(unittest.TestCase):
    def setUp(self):
        self.connection = MagicMock()
        return

    def test_abstract_property(self):
        """
        Does a class without the properties defined crash on creation?
        """
        self.assertRaises(TypeError, BroadcomBaseCommand, args=(self.connection,))
        self.assertRaises(TypeError, BadChild, args=(self.connection,))
        self.assertRaises(TypeError, BadChild2, args=(self.connection,))
        EvilChild(self.connection)
        return

    def test_add(self):
        """
        Can you add two commands?
        """
        command_1 = TestChild(self.connection)
        command_2 = TestChild2(self.connection)

        data_1, data_2 = command_1.data, command_2.data

        data_changed = data_1.copy()
        data_changed.update(data_2)

        # first __add__
        command_3 = command_1 + command_2

        # creates a new object with the combined data
        self.assertEqual(data_changed, command_3.data)
        self.assertEqual(self.connection, command_3.connection)
        # leaves the originals alone
        self.assertEqual(command_1.data, data_1)
        self.assertEqual(command_2.data, data_2)

        # now __iadd__
        command_1 += command_2
        # changes LHS in place
        self.assertEqual(command_1.data, data_changed)
        self.assertEqual(command_2.data, data_2)
        return

    def test_subtract(self):
        """
        Can you subtract two commands?
        """
        command_1 = TestChild(self.connection)
        command_2 = TestChild2(self.connection)
        command_3 = TestChild3(self.connection)

        data_1, data_2, data_3 = command_1.data, command_2.data, command_3.data
        data_changed = data_1.copy()
        data_changed.update(data_2)
        data_changed.update(data_3)

        # first add so the commands are different
        command_1 += command_2
        command_1 += command_3

        # First __sub__
        command_4 = command_1 - command_2

        data_1_3 = data_1.copy()
        data_1_3.update(data_3)
        # new cleaned object
        self.assertEqual(command_4.data, data_1_3)

        command_4 = command_1 - command_2 - command_3
        self.assertEqual(command_4.data, data_1)

        # old objects unchanged
        self.assertEqual(command_1.data, data_changed)
        self.assertEqual(command_2.data, data_2)

        # now __isub__
        self.assertNotEqual(command_1.data, data_1)
        command_1 -= command_2
        self.assertEqual(command_1.data, data_1_3)
        self.assertEqual(command_2.data, data_2)

        command_1 -= command_3
        self.assertEqual(command_1.data, data_1)
        return

class TestEnableInterface(unittest.TestCase):
    def setUp(self):
        self.connection = MagicMock(name='connection')
        self.html = MagicMock(name='html')
        self.connection.return_value = self.html
        self.html.text = open('radio_asp.html').read()
        self.command = EnableInterface(connection=self.connection)
        return

    def test_previous_state(self):
        """
        Does it get the previous state correctly?
        """
        command = EnableInterface(connection=self.connection,
                                  band='2')
        command()

        command.band = '5'
        command()
        return

    def test_24ghz(self):
        """
        Does it construct the right set of data to Enable the interface?
        """
        command = EnableInterface(connection=self.connection,
                                  band='2.4')
        expected_data =  {'wl_unit':'0',
                          'action':'Apply',
                          'wl_radio':'1'}

        self.assertEqual(command.data, expected_data)
        command()
        self.assertEqual(self.connection.path, 'radio.asp')
        self.connection.assert_called_with(data=expected_data)
        return

    def test_5_ghz(self):
        """
        Does it send the 5 GHz data to the connection?
        """
        command = EnableInterface(connection=self.connection,
                                  band='5')
        expected_data ={'action':'Apply',
                        'wl_unit':'1',
                        'wl_radio':'1'}
        self.assertEqual(command.data, expected_data)

        # what happens if you change it to 2.4?
        command.band = '2.4'
        expected_data ={'action':'Apply',
                        'wl_unit':'0',
                        'wl_radio':'1'}
        self.assertEqual(command.data, expected_data)
        return

    def test_undo(self):
        """
        Does the undo disable the interface?
        """
        connection = MagicMock(name='connection')
        html = MagicMock(name='html')
        connection.return_value = html
        html.text = open('radio_asp.html').read()
        command = EnableInterface(connection=connection)

        command.band = 5
        command()

        html.text = open('radio_5_asp.html').read()

        command.undo()
        query_call = call(data={'wl_unit':'1'})
        calls = [query_call,
                 call(data={'action':'Apply',
                        'wl_unit':'1',
                        'wl_radio':'1'}),
                            call(data ={'action':'Apply',
                        'wl_unit':'1',
                        'wl_radio':'0'})]
        self.assertEqual(connection.mock_calls, calls)
        self.command.previous_state = 'Enabled'

        # the disable checks the state so we need to change the html so it is Disabled
        #self.html.text = open('radio_5_asp.html').read()
        #self.command.undo()
        #self.assertEqual(self.connection.mock_calls, calls)
        return

class TestDisableInterface(unittest.TestCase):
    def setUp(self):
        self.connection = MagicMock(name='connection')
        self.html = MagicMock(name='html')
        self.connection.return_value = self.html
        self.html.text = open('radio_5_asp.html').read()
        return

    def test_24_ghz(self):
        """
        Does it setup the right data dictionary and make the right call?
        """
        command = DisableInterface(connection=self.connection,
                                   band='2.4')
        expected_data = {'action':'Apply',
                        'wl_unit':'0',
                        'wl_radio':'0'}
        self.assertEqual(command.data, expected_data)
        command()
        self.connection.assert_called_with(data=expected_data)
        self.assertEqual(self.connection.path, 'radio.asp')
        return

    def test_5_ghz(self):
        """
        Does it set up the right data dictionary to disable the 5 Ghz interface?
        """
        command = DisableInterface(connection=self.connection,
                                   band='5')
        expected_data = {'action':'Apply',
                        'wl_unit':'1',
                        'wl_radio':'0'}
        self.assertEqual(command.data, expected_data)

class TestSetChannel(unittest.TestCase):
    def setUp(self):
        self.fake_channel = random_letters()
        self.querier = MagicMock()
        self.querier.channel = self.fake_channel
        self.connection = MagicMock()
        self.command = SetChannel(connection=self.connection)
        self.command._querier = self.querier
        return

    def test_24_ghz(self):
        """
        Does it pass in the correct data to set the (2.4 GHz) channel?
        """
        channel = random.choice(BroadcomRadioData.channels_24ghz)
        self.command.channel = channel
        expected_data = {'action':'Apply',
                        'wl_unit':'0',
                        'wl_channel':str(channel)}
        # setup the shelving
        self.command._querier = self.querier
        mock_shelf = MagicMock(spec_set=dict)
        mock_closing = MagicMock()
        mock_closing.return_value = mock_shelf
        with patch('contextlib.closing', mock_closing):
            #with patch('shelve', mock_shelf):
            self.command()
        self.connection.assert_called_with(data=expected_data)
        return

    def test_5_ghz(self):
        """
        Does it pass in the correct data to set the (5GHz) channel?
        """
        channel = random.choice(BroadcomRadioData.channels_5ghz)
        self.command.channel = channel
        expected_data = {'action':'Apply',
                        'wl_unit':'1',
                        'wl_channel':str(channel)}
        # setup the shelving
        self.command._querier = self.querier
        mock_shelf = MagicMock(spec_set=dict)
        mock_closing = MagicMock()
        with patch('contextlib.closing', mock_closing):
            #with patch('shelve', mock_shelf):
            self.command()
        self.connection.assert_called_with(data=expected_data)
        return

    def test_shelf_objects(self):
        """
        Is shelf_objects a dict of shelf_key:previous-channels dict?
        """
        querier = MagicMock()
        command = SetChannel(connection=self.connection)
        command._querier = self.querier
        shelf_objects = command.shelf_objects
        self.assertEqual(command.shelf_key, shelf_objects.keys()[0])
        self.assertEqual(self.fake_channel,
                          shelf_objects[command.shelf_key])
        return
# end class TestSetChannel

class TestSetSideband(unittest.TestCase):
    def setUp(self):
        self.connection = MagicMock()
        return

    def test_set_sideband(self):
        """
        Does it set the sideband correctly?
        """
        command = SetSideband(connection=self.connection)
        command.direction = 'lower'
        expected_data = {'action':'Apply',
                        'wl_unit':'1',
                        'wl_nctrlsb':'lower'}
        command()
        self.connection.assert_called_with(data=expected_data)
        return


from flask import Flask, request, render_template
import serial
import time

app = Flask(__name__)

# Подключение к устройству через COM-порт
ser = serial.Serial(
    port='COM6',
    baudrate=9600,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS
)

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

@app.route('/send_command', methods=['POST'])
def send_command():
    command = request.form['command']
    ser.write(command.encode() + b'\r\n')
    time.sleep(1)
    data = ser.read(ser.inWaiting())
    return data

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


import PhysX4 as px

v = px.PxVec3(2, 2, 2)
v *= 0.5
assert v.x == 1.0

tm = px.PxTransform(px.PxVec3(1, 1, 1))
assert tm.p.x == 1

tm2 = tm.transform(px.PxTransform(px.PxVec3(2, 2, 2)))
assert tm2.p.x == 3