import numpy as np
from matplotlib import pyplot
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import glob
import cv2

# Get size of calibration image
img = cv2.imread('camera_cal/calibration1.jpg')
img_size = (img.shape[1], img.shape[0])

# 1. Camera calibration
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*9,3), np.float32)
objp[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1,2)

# Arrays to store object points and image points from all the images.
objpoints = [] # 3d points in real world space
imgpoints = [] # 2d points in image plane.

# Make a list of calibration images
images = glob.glob('camera_cal/calibration*.jpg')

# Step through the list and search for chessboard corners
for idx, fname in enumerate(images):
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Find the chessboard corners
    ret, corners = cv2.findChessboardCorners(gray, (9,6), None)

    # If found, add object points, image points
    if ret == True:
        objpoints.append(objp)
        imgpoints.append(corners)

        # Draw and display the corners
        cv2.drawChessboardCorners(img, (9,6), corners, ret)
        #write_name = 'corners_found'+str(idx)+'.jpg'
        #cv2.imwrite(write_name, img)
        #cv2.imshow('img', img)
        cv2.waitKey(500)
#print(corners[0,0])
#print(np.shape(corners))
#print(np.shape(imgpoints))
cv2.destroyAllWindows()

import pickle
# Do camera calibration given object points and image points
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size,None,None)

# Save the camera calibration result for later use (we won't worry about rvecs / tvecs)
dist_pickle = {}
dist_pickle["mtx"] = mtx
dist_pickle["dist"] = dist
pickle.dump( dist_pickle, open( "output_images/cal_dist_pickle.p", "wb" ) )

# 2. Distortion correction
# Test undistortion on an image
img = cv2.imread('test_images/test2.jpg')
# undistort test image
undist = cv2.undistort(img, mtx, dist, None, mtx)
cv2.imwrite('output_images/test2_undist.jpg',undist)
# Visualize undistortion
undist = cv2.cvtColor(undist, cv2.COLOR_BGR2RGB) #convert to RGB
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

f, (ax1, ax2) = plt.subplots(1, 2, figsize=(20,10))
ax1.imshow(img)
ax1.set_title('Original Image', fontsize=30)
ax2.imshow(undist)
ax2.set_title('Undistorted Image', fontsize=30)
pyplot.savefig('output_images/test2_vs_undistort.png')

def select_yellow(image):
    hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
    lower = np.array([20,60,60])
    upper = np.array([38,174, 250])
    mask = cv2.inRange(hsv, lower, upper)

    return mask

def select_white(image):
    lower = np.array([202,202,202])
    upper = np.array([255,255,255])
    mask = cv2.inRange(image, lower, upper)

    return mask

img = mpimg.imread('output_images/test2_undist.jpg')
def doubleThreshold(image):
    yellow = select_yellow(image)
    white = select_white(image)

    combined_binary = np.zeros_like(yellow)
    combined_binary[(yellow >= 1) | (white >= 1)] = 255

    return combined_binary
threshed=doubleThreshold(img)
cv2.imwrite('output_images/test2_undist_thresh.jpg',threshed)

# 3. Color/gradient threshold
# Read in an image and grayscale it
# img = mpimg.imread('output_images/cal_undist.jpg')
# def doubleThreshold(img):
#     # Convert to HLS color space and separate the S channel
#     # Note: img is the undistorted image
#     hls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)
#     s_channel = hls[:,:,2]
#
#     # Grayscale image
#     # NOTE: we already saw that standard grayscaling lost color information for the lane lines
#     # Explore gradients in other colors spaces / color channels to see what might work better
#     gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
#
#     # Sobel x
#     sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0) # Take the derivative in x
#     abs_sobelx = np.absolute(sobelx) # Absolute x derivative to accentuate lines away from horizontal
#     scaled_sobel = np.uint8(255*abs_sobelx/np.max(abs_sobelx))
#
#     # Threshold x gradient
#     thresh_min = 30
#     thresh_max = 100
#     sxbinary = np.zeros_like(scaled_sobel)
#     sxbinary[(scaled_sobel >= thresh_min) & (scaled_sobel <= thresh_max)] = 1
#
#     # Threshold color channel
#     s_thresh_min = 170
#     s_thresh_max = 255
#     s_binary = np.zeros_like(s_channel)
#     s_binary[(s_channel >= s_thresh_min) & (s_channel <= s_thresh_max)] = 1
#     # Combine the two binary thresholds
#     combined_binary = np.zeros_like(sxbinary)
#     combined_binary[(s_binary == 1) | (sxbinary == 1)] = 255
#     print(np.shape(combined_binary))
#     return combined_binary
#
# threshed=doubleThreshold(img)
# cv2.imwrite('output_images/cal_undist_thresh.jpg',threshed)
# #Plotting thresholded images
# fig = plt.figure()
# plt.title('Combined S channel and gradient thresholds')
# plt.imshow(threshed, cmap='gray')
# plt.show()
# pyplot.savefig('output_images/cal_undist_thresh.png')

# 4. Perspective Transform
# Read image from last step
img = mpimg.imread('output_images/undist_thresh.jpg')
def perspectiveTrans(img):
#     offset=50
#     up=np.int(0.65*img_size[1]) #vertical height
#     down=np.int(img_size[1])
#     leftup=np.int(0.45*img_size[0]) #horizontal
#     leftdown=np.int(0.2*img_size[0])
#     rightup=np.int(0.6*img_size[0])
#     rightdown=np.int(0.85*img_size[0])
#     hist_leftup = np.sum(img[up:(up+offset),leftup-offset:leftup], axis=0)
#     hist_leftdown = np.sum(img[down-offset:down,leftdown:leftdown+offset], axis=0)
#     hist_rightup = np.sum(img[up:(up+offset),rightup:rightup+offset], axis=0)
#     hist_rightdown = np.sum(img[down-offset:down,rightdown-offset:rightdown], axis=0)
#
#     leftup = np.argmax(hist_leftup)+leftup-offset #argmax returns index of max value
#     leftdown = np.argmax(hist_leftdown)+leftdown-offset
#     rightup = np.argmax(hist_rightup)+rightup-offset
#     rightdown = np.argmax(hist_rightdown)+rightdown-offset
#     #
#     src = np.float32([[leftup,up], [rightup,up], [leftdown,down], [rightdown,down]])
#     dst = np.float32([[leftdown, 0], [rightdown,0], [leftdown, down], [rightdown,down]])
    #src = np.float32([[599,447], [680,447], [227,704], [1065,704]])

    src = np.float32([[545, 460],
                    [735, 460],
                    [1280, 700],
                    [0, 700]])

    dst = np.float32([[0, 0],
                     [1280, 0],
                     [1280, 720],
                     [0, 720]])

#     print(src)
#     print(dst)
    # Given src and dst points, calculate the perspective transform matrix
    M = cv2.getPerspectiveTransform(src, dst)
    MInv = cv2.getPerspectiveTransform(dst,src)
    # Warp the image using OpenCV warpPerspective()
    warped = cv2.warpPerspective(img, M, img_size)
    org = cv2.warpPerspective(warped, MInv, img_size)
    return warped, org, MInv

warped, org, MInv = perspectiveTrans(img)
cv2.imwrite('output_images/test2_undist_thresh_transform.jpg',warped)
# cv2.imwrite('output_images/undist_thresh_invtrans.jpg',org)

# 5. Detect lane lines
# Read in an image and grayscale it
img = mpimg.imread('output_images/undist_thresh_transform.jpg')
ploty = np.linspace(0, img_size[1]-1, img_size[1] ) #args: (start,stop,elements)
def slide_window(img):
    # detect lane lines by sliding windows
    binary_warped=img
    # Assuming you have created a warped binary image called "binary_warped"
    # Take a histogram of the bottom half of the image
    histogram = np.sum(binary_warped[np.int(img_size[1]/2):,:], axis=0)
    # Create an output image to draw on and  visualize the result
    #out_img = np.dstack((binary_warped, binary_warped, binary_warped))#*255
    # Find the peak of the left and right halves of the histogram
    # These will be the starting point for the left and right lines
    midpoint = np.int(histogram.shape[0]/2)
    leftx_base = np.argmax(histogram[:midpoint]) #argmax returns index of max value
    rightx_base = np.argmax(histogram[midpoint:]) + midpoint

    # Choose the number of sliding windows
    nwindows = 9
    # Set height of windows
    window_height = np.int(img_size[1]/nwindows) # y vertical height
    # Identify the x and y positions of all nonzero pixels in the image
    nonzero = binary_warped.nonzero() #nonzero() returns tuple of arrays. e.g.coord:(3,2) (1,7),(8,6) -> ([3,1,8],[2,7,6])
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])
    # Current positions to be updated for each window
    leftx_current = leftx_base #current is center of window in x axis
    rightx_current = rightx_base
    # Set the width of the windows +/- margin
    margin = 100
    # Set minimum number of pixels found to recenter window
    minpix = 50
    # Create empty lists to receive left and right lane pixel indices
    left_lane_inds = []
    right_lane_inds = []

    # Step through the windows one by one
    for window in range(nwindows):
        # Identify window boundaries in x and y (and right and left)
        win_y_low = img_size[1] - (window+1)*window_height
        win_y_high = img_size[1] - window*window_height
        win_xleft_low = leftx_current - margin
        win_xleft_high = leftx_current + margin
        win_xright_low = rightx_current - margin
        win_xright_high = rightx_current + margin
        # Draw the windows on the visualization image
    #     cv2.rectangle(out_img,(win_xleft_low,win_y_low),(win_xleft_high,win_y_high),
    #     (0,255,0), 2)
    #     cv2.rectangle(out_img,(win_xright_low,win_y_low),(win_xright_high,win_y_high),
    #     (0,255,0), 2)
        # Identify the nonzero pixels in x and y within the window
        good_left_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) &
        (nonzerox >= win_xleft_low) &  (nonzerox < win_xleft_high)).nonzero()[0]
        good_right_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) &
        (nonzerox >= win_xright_low) &  (nonzerox < win_xright_high)).nonzero()[0]
        # Append these indices to the lists
        left_lane_inds.append(good_left_inds)
        right_lane_inds.append(good_right_inds)
        # If you found > minpix pixels, recenter next window on their mean position
        if len(good_left_inds) > minpix:
            leftx_current = np.int(np.mean(nonzerox[good_left_inds]))
        if len(good_right_inds) > minpix:
            rightx_current = np.int(np.mean(nonzerox[good_right_inds]))

    # Concatenate the arrays of indices
    left_lane_inds = np.concatenate(left_lane_inds)
    right_lane_inds = np.concatenate(right_lane_inds)

    # Extract left and right line pixel positions
    leftx = nonzerox[left_lane_inds]
    lefty = nonzeroy[left_lane_inds]
    rightx = nonzerox[right_lane_inds]
    righty = nonzeroy[right_lane_inds]

    # Fit a second order polynomial to each
    left_fit = np.polyfit(lefty, leftx, 2) #lefty in 1st place, similar to x axis as in ax^2+bx+c
    right_fit = np.polyfit(righty, rightx, 2) #returns polynomial coeffients a,b,c (xx_fit[0],[1],[2])

    # Generate x and y values for plotting
    left_fitx = left_fit[0]*ploty**2 + left_fit[1]*ploty + left_fit[2] # all pairs of (ploty,left_fitx) form a polynomial line (power 2)
    right_fitx = right_fit[0]*ploty**2 + right_fit[1]*ploty + right_fit[2] # as later plotted line in yellow color
    return leftx, lefty, rightx, righty, left_fit, right_fit, left_fitx, right_fitx

leftx, lefty, rightx, righty, left_fit, right_fit, left_fitx, right_fitx = slide_window(img)

def searchAfter(img,undist,left_fit,right_fit):
    binary_warped=img
    # Assume you now have a new warped binary image
    # from the next frame of video (also called "binary_warped")
    # It's now much easier to find line pixels!
    nonzero = binary_warped.nonzero()
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])
    margin = 100
    left_lane_inds = ((nonzerox > (left_fit[0]*(nonzeroy**2) + left_fit[1]*nonzeroy +
    left_fit[2] - margin)) & (nonzerox < (left_fit[0]*(nonzeroy**2) +
    left_fit[1]*nonzeroy + left_fit[2] + margin)))  # use +/- margin criteria for nonzero indexes be included in lane lines

    right_lane_inds = ((nonzerox > (right_fit[0]*(nonzeroy**2) + right_fit[1]*nonzeroy +
    right_fit[2] - margin)) & (nonzerox < (right_fit[0]*(nonzeroy**2) +
    right_fit[1]*nonzeroy + right_fit[2] + margin)))

    # Again, extract left and right line pixel positions
    leftx = nonzerox[left_lane_inds]
    lefty = nonzeroy[left_lane_inds]
    rightx = nonzerox[right_lane_inds]
    righty = nonzeroy[right_lane_inds]
    # Fit a second order polynomial to each
    left_fit = np.polyfit(lefty, leftx, 2)
    right_fit = np.polyfit(righty, rightx, 2)
    # Generate x and y values for plotting
    ploty = np.linspace(0, img.shape[0]-1, img.shape[0] )
    left_fitx = left_fit[0]*ploty**2 + left_fit[1]*ploty + left_fit[2]  # all x values along lane line for each y value
    right_fitx = right_fit[0]*ploty**2 + right_fit[1]*ploty + right_fit[2]

    # Project those lines onto the original image as follows:
    # Create an image to draw the lines on
    warp_zero = np.zeros_like(binary_warped).astype(np.uint8)
    color_warp = np.dstack((warp_zero, warp_zero, warp_zero))
    lane_mask=np.copy(color_warp)
    # Recast the x and y points into usable format for cv2.fillPoly()
    pts_left = np.array([np.transpose(np.vstack([left_fitx, ploty]))])
    pts_right = np.array([np.flipud(np.transpose(np.vstack([right_fitx, ploty])))])
    pts = np.hstack((pts_left, pts_right))
    # Draw the lane onto the warped blank image
    cv2.fillPoly(color_warp, np.int_([pts]), (0,255, 0))
    # mark lane points with red color on left lane and blue on right lane
    lane_mask[lefty, leftx] = [255, 0, 0]
    lane_mask[righty, rightx] = [0, 0, 255]
    color_warp = cv2.addWeighted(color_warp, 1, lane_mask, 1, 0)
    # Warp the blank back to original image space using inverse perspective matrix (Minv)
    newwarp = cv2.warpPerspective(color_warp, MInv, img_size)
    # Combine the result with the original image
    result = cv2.addWeighted(undist, 1, newwarp, 0.5, 0)
    # Color in left and right line pixels
    #result=cv2.cvtColor(result,cv2.COLOR_RGB2BGR)
    return leftx, lefty, rightx, righty, left_fit, right_fit, left_fitx, right_fitx, result

leftx, lefty, rightx, righty, left_fit, right_fit,left_fitx, right_fitx, result=searchAfter(img,undist,left_fit,right_fit)
result=cv2.cvtColor(result,cv2.COLOR_RGB2BGR)
# cv2.imwrite('output_images/shaded_lane.jpg',result)

#6. Determine the lane curvature
def laneCurvature(leftx,lefty,rightx,righty,left_fit,right_fit):
    # Define y-value where we want radius of curvature
    # I'll choose the maximum y-value, corresponding to the bottom of the image
    y_eval = np.max(ploty)
    # Define conversions in x and y from pixels space to meters
    ym_per_pix = 30/720 # meters per pixel in y dimension
    xm_per_pix = 3.7/700 # meters per pixel in x dimension

    # Fit new polynomials to x,y in world space
    left_fit_cr = np.polyfit(lefty*ym_per_pix, leftx*xm_per_pix, 2)
    right_fit_cr = np.polyfit(righty*ym_per_pix, rightx*xm_per_pix, 2)
    # Calculate the new radii of curvature
    left_curverad = ((1 + (2*left_fit_cr[0]*y_eval*ym_per_pix + left_fit_cr[1])**2)**1.5) / np.absolute(2*left_fit_cr[0])
    right_curverad = ((1 + (2*right_fit_cr[0]*y_eval*ym_per_pix + right_fit_cr[1])**2)**1.5) / np.absolute(2*right_fit_cr[0])
    # Now our radius of curvature is in meters
    left_basex = left_fit[0]*y_eval**2 + left_fit[1]*y_eval + left_fit[2]
    right_basex = right_fit[0]*y_eval**2 + right_fit[1]*y_eval + right_fit[2]

    road_center=(left_basex+right_basex)*0.5
    vehicle_center=img_size[0]*0.5
    abs_diff=np.abs(road_center-vehicle_center)*xm_per_pix
    if vehicle_center<road_center:
        shift='left'
    else:
        shift='right'
    return left_curverad,right_curverad,shift,abs_diff

left_curverad,right_curverad,shift,abs_diff=laneCurvature(leftx,lefty,rightx,righty,left_fit,right_fit)
# print(left_curverad, 'm,', right_curverad, 'm', '.vehicle is', abs_diff,'m', shift,'of center')

class Line():
    def __init__(self,recent_xfitted,recent_fit,current_fit,radius,allx,ally):
        # was the line detected in the last iteration?
        self.detected = True
        # x values of the last n fits of the line
        self.recent_xfitted = recent_xfitted
        #average x values of the fitted line over the last n iterations
        self.bestx = np.mean(self.recent_xfitted,axis=0)
        # last n fits of the line
        self.recent_fit = recent_fit
        #polynomial coefficients averaged over the last n iterations
        self.best_fit = np.mean(self.recent_fit,axis=0)
        #polynomial coefficients for the most recent fit
        self.current_fit = current_fit
        #radius of curvature of the line in some units
        self.radius_of_curvature = radius
        #distance in meters of vehicle center from the line
        self.line_base_pos = None
        #difference in fit coefficients between last and new fits
        self.diffs = np.abs(self.current_fit-self.recent_fit[-1])
        #x values for detected line pixels
        self.allx = allx
        #y values for detected line pixels
        self.ally = ally
    def sanityCheck(self):
        if (self.radius_of_curvature/1000<0.4) or (self.radius_of_curvature/1000>3):
            self.detected=False
        last_fit=self.recent_fit[-1]
        if np.any(self.diffs>[0.01*last_fit[0], 0.01*last_fit[1], 0.01*last_fit[2]]):
            self.detected=False

def process_image(img):
    global frame
    global lastFrame

    frame+=1
    # 2. Undistortion
    undist = cv2.undistort(img, mtx, dist, None, mtx)

    # 3. gradient & color thresholding
    threshed=doubleThreshold(undist)

    # 4. Perspective transform
    warped, org, MInv = perspectiveTrans(threshed)

    # 5. Detect lane lines
    # first frame use slide window to locate lanes
    if frame==1:
        leftx, lefty, rightx, righty, left_fit, right_fit, left_fitx, right_fitx = slide_window(warped)
        left_curverad,right_curverad,shift,abs_diff=laneCurvature(leftx,lefty,rightx,righty,left_fit,right_fit)
    else: # from 2nd frame onwards, search around last detected lanes for current lanes
        left_fit, right_fit = lastFrame["left_fit"],lastFrame["right_fit"]
        leftx, lefty, rightx, righty, left_fit, right_fit, left_fitx, right_fitx, result=searchAfter(warped,undist,left_fit,right_fit)
        left_curverad,right_curverad,shift,abs_diff=laneCurvature(leftx,lefty,rightx,righty,left_fit,right_fit)

    if frame<=avg_n:
        recent_left_fitx.append(left_fitx)
        recent_left_fit.append(left_fit)
        recent_right_fitx.append(right_fitx)
        recent_right_fit.append(right_fit)
        #left
        line_l_current=Line(recent_left_fitx,recent_left_fit,left_fit,left_curverad,leftx,lefty)
        #right
        line_r_current=Line(recent_right_fitx,recent_right_fit,right_fit,right_curverad,rightx,righty)

    if frame>avg_n:
        recent_left_fitx.pop(0)
        recent_left_fitx.append(left_fitx)
        recent_left_fit.pop(0)
        recent_left_fit.append(left_fit)
        recent_right_fitx.pop(0)
        recent_right_fitx.append(right_fitx)
        recent_right_fit.pop(0)
        recent_right_fit.append(right_fit)
        #left
        line_l_current=Line(recent_left_fitx,recent_left_fit,left_fit,left_curverad,leftx,lefty)
        line_l_current.sanityCheck()
        #right
        line_r_current=Line(recent_right_fitx,recent_right_fit,right_fit,right_curverad,rightx,righty)
        line_r_current.sanityCheck()
        if line_r_current.detected=='False' or line_l_current.detected=='False':
            print("sanity check failed")
            leftx, lefty, rightx, righty, left_fit, right_fit, left_fitx, right_fitx = slide_window(warped)

    #print('frame',frame,':',line_l_current.recent_fit,line_l_current.best_fit)
    #leftx, lefty, rightx, righty, left_fit, right_fit,left_fitx,right_fitx, result=searchAfter(warped,undist,left_fit,right_fit)
    leftx, lefty, rightx, righty, left_fit, right_fit,left_fitx,right_fitx, result=searchAfter(warped,undist,line_l_current.best_fit,line_r_current.best_fit)

    # 6. Determine the lane curvature and center shift
    left_curverad,right_curverad,shift,abs_diff=laneCurvature(leftx,lefty,rightx,righty,line_l_current.best_fit,line_r_current.best_fit)

    # For use in next frame
    lastFrame={"leftx":leftx,"lefty":lefty, "rightx":rightx, "righty":righty,"left_fit":left_fit,"right_fit":right_fit,
          "left_fitx":left_fitx, "right_fitx":right_fitx, "left_curve":left_curverad,"right_curve":right_curverad}

    text1= "left curvature %f m, right curvature %f m" % (left_curverad,right_curverad)
    text2= "vehicle is %f m %s of road center" % (abs_diff, shift)
    font = cv2.FONT_HERSHEY_SIMPLEX
    result=cv2.putText(result,text1,(100,100), font, 1,(0,0,255),2,cv2.LINE_AA)
    result=cv2.putText(result,text2,(100,200), font, 1,(0,0,255),2,cv2.LINE_AA)

    return result

avg_n = 20
recent_left_fitx=[]
recent_left_fit =[]
recent_right_fitx=[]
recent_right_fit =[]
frame=0
lastFrame={}

from moviepy.editor import VideoFileClip
clip1 = VideoFileClip("project_video.mp4")#.subclip(40,48)
out_clip = clip1.fl_image(process_image) #NOTE: this function expects color images!!
video_output = 'output_images/out_project_video.mp4'
out_clip.write_videofile(video_output, audio=False)


#connection
import re
import sqlalchemy as s
from connect import engine
from connect import connection
from connect import metadata

import tables_project.project as project
import tables_project.scope as scope

import algos

# create table object, called selected table, st
st = s.Table("bees", metadata, autoload=True, autoload_with=engine) #bees

# the functions that are imported into the display table and view table, adapt/change this when updating a table.
#________________________________________________________________________________________________________________________
def select_table():
    # the query object
    query = s.select([st.columns.bee_no,st.columns.first_name ,st.columns.last_name])
    # execute query
    select_result_proxy = connection.execute(query)
    return select_result_proxy

def update_table():
    bee_no = int(input("select scope id: "))
    first_name = input("Insert first name: ")
    last_name = input("Insert last name: ")
    email = input("Insert email: ")
    phone_no = input("Insert phone_no: ")
    gender = algos.gender_type()
    hire_date = algos.date_setup("hire date")
    birth_date = algos.date_setup("birth date")

    update = s.update(st).values(first_name= first_name,last_name=last_name,email=email,phone_no=phone_no,gender=gender,hire_date=hire_date,birth_date=birth_date).where(st.columns.bee_no == bee_no)
    proxy = connection.execute(update)
    ans = "selected id updated"
    return ans

def delete_table():
    bee_no = int(input("Select scope id: "))
    query = s.delete(st).where(st.columns.bee_no == bee_no)
    proxy = connection.execute(query)
    ans = "selected id deleted"
    return ans

def insert_table():
    first_name = input("Insert first name: ")
    last_name = input("Insert last name: ")
    email = input("Insert email: ")
    phone_no = input("Insert phone_no: ")
    gender = algos.gender_type()
    hire_date = algos.date_setup("hire date")
    birth_date = algos.date_setup("birth date")

    insert = s.insert(st).values(first_name= first_name,last_name=last_name,email=email,phone_no=phone_no,gender=gender,hire_date=hire_date,birth_date=birth_date)
    proxy = connection.execute(insert)
    ans = "selected id updated"

# display functions
#________________________________________________________________________________________________________________________

def display_table(table_name):
    """View , select, update and insert the table:
    To adopt this function to suit other table; Change all occurencies for
    1) selected_table.columns.[column_a]
    2) selected_table.columns.[column_b]

    with the new column names

    To allow the user to insert into more columns (more than one),modify the insert section.

    Args:
        table_name (str): the name of the table

    Returns:
        _type_: the id of the entry selected

    """
    try_again = True
    while try_again:

        select = select_table()

        # STORING RESULTS PROXY IN A DICT
        #_______________________________________________________________________________________

        # record the output in a dict ( key and value); id and the name
        items = [result for result in select]
        items_dict = {item[0]:(item[1] + " " + item[2]) for item in items}

        # insert option to update if input is missing
        changes = {"u":"update","d":"delete","i":"insert" }

        # DISPLAY ENTRIES IN DATABASE
        #_______________________________________________________________________________________
        print()
        print(F"{table_name}")
        print("***************************************************")
        for key, value in items_dict.items():
            print(f"{key}:{str.upper(value)}")
        print("___________________________________________________")

        # DISPLAY CREATE, DELETE AND INSERT OPTIONS
        #_______________________________________________________________________________________
        for key, value in changes.items():
            print(f"<> ({key}):{value} <> ", end="")

        print()
        print("***************************************************")

        user_selection = input("select: ")

        # SELECT ENTRY IN DATABASE LOGIC
        #_______________________________________________________________________________________
        if user_selection.isdigit():
            user_selection = int(user_selection)
            if user_selection in items_dict.keys():
                try_again = False
            else:
                print("Integer selected out of range")
                try_again = True

        # UPDATE , DELETE AND INSERT LOGIC
        #_______________________________________________________________________________________
        else:
            if user_selection in changes.keys():
                #UPDATE
                if user_selection == str.lower("u"):
                    print()
                    print("_____________________________")
                    print("Updating a database entry")
                    print("_____________________________")
                    update_table()
                    print("_____________________________")
                    print()
                    try_again = True

                #DELETE

                elif user_selection == "d":
                    # delete the entries
                    print()
                    print("_____________________________")
                    print("Deleting a database entry")
                    print("_____________________________")
                    delete_table()
                    print("_____________________________")
                    print()
                    try_again = True

                # INSERT
                elif user_selection == "i":
                    # insert new entries
                    print()
                    print("_____________________________")
                    insert_table()
                    print("_____________________________")
                    try_again = True

                else:
                    print("Input selected is out of range")

    # RETURN ID(PRIMARY KEY) OF SELECTED OPTION
    #_______________________________________________________________________________________
    return user_selection

def show_table(table_name):
    try_again = True
    while try_again:

        select = select_table()

        # STORING RESULTS PROXY IN A DICT
        #_______________________________________________________________________________________

        # record the output in a dict ( key and value); id and the name
        output_dict = {}
        for result in select:
            #convert tuple output to one item
            column_a = result[0]
            column_b= result[1]

            # table_id (primary key) recorded as the key, followed by identifier name

            output_dict[column_a]=str.upper(column_b)

        # DISPLAY ENTRIES IN DATABASE
        #_______________________________________________________________________________________
        print()
        print(F"{table_name}")
        print("***************************************************")
        for key, value in output_dict.items():
            print(f"{key}:{value}")
        print("___________________________________________________")

        # SELECT ENTRY IN DATABASE LOGIC
        #_______________________________________________________________________________________
        user_selection = input("select: ")
        if user_selection.isdigit():
            user_selection = int(user_selection)
            if user_selection in output_dict.keys():
                try_again = False
            else:
                print("Integer selected out of range")
                try_again = True
        else:
            print("Wrong input")

    return user_selection

# select functions
#________________________________________________________________________________________________________________________

def show_bee_name(bee_no):
    # the query object
    query = s.select([st.columns.first_name, st.columns.last_name]).where(st.columns.bee_no==bee_no)
    # execute query
    select_result_proxy = connection.execute(query)
    full_name = [result for result in select_result_proxy]
    bee_name = str(full_name[0][0]) + " " +  str(full_name[0][1])
    return bee_name

if __name__ == "__main__":
    display_table("bees")
    #bee_no = 14
    #show_bee_name(bee_no)


import logging

import gradio as gr
from config import BaseConfig
from predict import post_to_std, post_to_lid, post_to_asr, post_to_esc

logging.basicConfig(format="[%(asctime)s] %(levelname)s: %(message)s")
config = BaseConfig()

css = "footer {display: none !important;} #sp-gradio-container {min-height: 0px !important;}"

def asr_helper(language, audio_path):
    if language == 'en: English':
        return post_to_asr(audio_path)
    else:
        return 'Predicted Language is not English'

def mic_toggle(choice):
    if choice == 'Microphone':
        return gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)
    else:
        return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=True)

def run_both_asr_esc(language, audio_path):
    return asr_helper(language, audio_path), post_to_esc(audio_path)

with gr.Blocks(css=css) as demo:

        with gr.Row(variant='compact').style(equal_height=True):
            with gr.Column(scale=1, variant='compact'):
                r: gr.Radio = gr.Radio(['Microphone', 'File'], value='Microphone', label='How would you like to upload your audio?')
                bm: gr.Button = gr.Button()
                bf: gr.Button = gr.Button(visible=False)
            with gr.Column(scale=2, variant='compact'):
                m: gr.Mic = gr.Mic(label='Input', type='filepath')
                f: gr.Audio = gr.Audio(label='Input', type='filepath', visible=False)
                std_output: gr.Audio = gr.Audio(type='filepath')

        with gr.Column(variant='compact'):
            with gr.Row():
                with gr.Box():
                    lid_output: gr.Textbox = gr.Textbox(show_label=True, label='Language Identification')
                    asr_output: gr.Textbox = gr.Textbox(show_label=True, label='Automatic Speech Recognition', visible=True)
                esc_output: gr.Label = gr.Label(label='Environment Sounds')

        r.change(mic_toggle, r, [bm, bf, m, f])

        bm.click(post_to_std, m, std_output).then(post_to_lid, std_output, lid_output).then(run_both_asr_esc, [lid_output, std_output], [asr_output, esc_output])
        bf.click(post_to_std, f, std_output).then(post_to_lid, std_output, lid_output).then(run_both_asr_esc, [lid_output, std_output], [asr_output, esc_output])

if __name__ == "__main__":

    demo.launch(
        server_name="0.0.0.0",
        server_port=config.port,
        enable_queue=True,
    )


"""

Based on code for WarpGrad:
https://github.com/flennerhag/warpgrad.

We modify the code to provide ARWarpGrad.

"""
import time
import os
from os.path import join

from options import parse_set

from data_multishot import DataLoader

import torch
from torch import nn
from torchvision import transforms

from model import get_model
from utils import build_kwargs, log_status, write_train_res, plot_train_res, write_val_res, unlink, consolidate

# for the n-way k-shot setting
# from data_loader import split_omniglot_characters
# from data_loader import load_imagenet_images
# from data_loader import OmniglotTask
# from data_loader import ImageNetTask
# from data_loader import fetch_dataloaders

os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = "1"

# Define a training image loader for ImageNet (miniImageNet, tieredImageNet)
train_transformer_ImageNet = transforms.Compose([
    transforms.Resize((84, 84)),
    transforms.ToTensor()
])

parser = parse_set()

args = parser.parse_args()

args.imsize = (28, 28)
args.cuda = True
args.batch_norm = not args.no_batch_norm
# for k-shot
# if args.num_sample ==5:
#     args.batch_norm = True
# else:
#     args.batch_norm = False

args.inner_kwargs = build_kwargs(args.inner_kwargs)
args.outer_kwargs = build_kwargs(args.outer_kwargs)
args.meta_kwargs = build_kwargs(args.meta_kwargs)
args.multi_head = args.meta_model.lower() == 'ft'

def pp(*inputs, **kwargs):
    if args.log_ival > 0:
        print(*inputs, **kwargs)

if args.cuda and not torch.cuda.is_available:
    raise ValueError("Cuda is not available. Run with --no_cuda")

torch.manual_seed(args.seed)

def main():
    data = DataLoader(
        root=args.root,
        num_pretrain_alphabets=args.num_pretrain,
        num_classes=args.classes,
        num_sample=args.task_batch_size,
        num_query=args.num_query,
        seed=args.seed,
        num_workers=args.workers,
        pin_memory=True,
    )

    # for n-way k-shot settings
    # mini-imagent
    # data = DataContainer(
    #     root=args.root,
    #     num_pretrain_alphabets=args.num_pretrain,
    #     num_classes=args.classes,
    #     num_sample=args.num_sample,
    #     num_query=args.num_query,
    #     transform=train_transformer_ImageNet,
    #     num_workers=args.workers,
    #     pin_memory=True,
    # )

    # if 'Omniglot' in args.data_dir:
    #     (meta_train_classes, meta_val_classes,
    #      meta_test_classes) = split_omniglot_characters(
    #          args.data_dir, args.SEED)
    #     task_type = OmniglotTask
    # elif ('mini-imagenet' in args.data_dir or
    #       'tieredImageNet' in args.data_dir):
    #     (meta_train_classes, meta_val_classes,
    #      meta_test_classes) = load_imagenet_images(args.data_dir)
    #     task_type = ImageNetTask
    # else:
    #     raise ValueError("I don't know your dataset")

    ###########################################################################

    log_dir = os.path.join(args.log_dir, args.meta_model, args.suffix)

    def evaluate(model: object, case: object, step: object) -> object:
        if args.write_ival > 0:
            torch.save(model, join(log_dir, 'model.pth.tar'))
        # iterator = []

        # for multi-shot
        if case == 'test':
            iterator = data.test
        else:
            iterator = data.val

        iterator = iterator(args.task_batch_size,
                            args.task_val_steps,
                            args.multi_head)

        ### for n-way k-shot
        # if case == 'test':
        #     for n_task in range(HOLD_OUT):
        #         task = task_type(meta_test_classes, args.classes,
        #                          args.num_sample, args.num_query)
        #         dataloaders = fetch_dataloaders(['train','test'], task)
        #         iterator.append(dataloaders)
        # else:
        #     for n_task in range(HOLD_OUT):
        #         task = task_type(meta_train_classes, args.classes,
        #                          args.num_sample, args.num_query)
        #         dataloaders = fetch_dataloaders(['train','test'], task)
        #         iterator.append(dataloaders)

        pp('Evaluating on {} tasks'.format(case))

        results = []
        for i, task in enumerate(iterator):
            if args.write_ival > 0:
                task_model = torch.load(join(log_dir, 'model.pth.tar'))
            else:
                task_model = model

            t = time.time()
            task_results = task_model([task], meta_train=False)
            t = time.time() - t

            results.append(task_results)

            if args.log_ival > 0:
                log_status(task_results, 'task={}'.format(i), t)

        if args.log_ival > 0:
            log_status(consolidate(results), 'Average', t)

        if args.write_ival > 0:
            write_val_res(results, step, case, log_dir)

        pp('-Done-')

    ###########################################################################

    criterion = nn.CrossEntropyLoss()

    if args.evaluate:
        model = torch.load(join(log_dir, 'model.pth.tar'))
        evaluate(model, 'test', 0)
        return

    model = get_model(args, criterion)

    ###########################################################################
    if args.write_ival > 0:
        if os.path.exists(log_dir):
            assert args.overwrite, \
                "Path exists ({}). Use --overwrite or change suffix".format(
                    log_dir)
            unlink(log_dir)
        os.makedirs(log_dir, exist_ok=True)

        with open(join(log_dir, 'args.log'), 'w') as f:
            f.write("%r" % args)

        with open(join(log_dir, 'args.log'), 'w') as f:
            f.write("%r" % args)

    pp('Initiating meta-training')
    train_step = 0
    try:
        evaluate(model, 'val', train_step)
        t = time.time()
        while True:
            # for multi-shot
            task_batch = data.train(args.meta_batch_size,
                                    args.task_batch_size,
                                    args.task_train_steps,
                                    args.multi_head)

            # for n-way k-shot
            # task_batch=[]
            # for n_task in range(args.meta_batch_size):
            #     task = task_type(meta_train_classes, args.classes,
            #                      args.num_sample, args.num_query)
            #     dataloaders = fetch_dataloaders(['train','test'], task)
            #     task_batch.append(dataloaders)

            results = model(task_batch, meta_train=True)

            train_step += 1
            if train_step % args.write_ival == 0:
                write_train_res(results, train_step, log_dir)
                # for visualization
                # plot_train_res(results, train_step, log_dir)

            if train_step % args.test_ival == 0:
                evaluate(model, 'val', train_step)
                pp("Resuming training")

            if args.log_ival > 0 and train_step % args.log_ival == 0:
                t = (time.time() - t) / args.log_ival
                log_status(results, 'step={}'.format(train_step), t)
                t = time.time()

            if results.train_loss != results.train_loss:
                break

            if train_step == args.meta_train_steps:
                break

    except KeyboardInterrupt:
        pp('Meta-training stopped.')
    else:
        pp('Meta-training complete.')

if __name__ == '__main__':
    if args.cuda:
        with torch.cuda.device(args.device):
            main()
    else:
        pp('Warning: No GPU is selected!')
        main()


""" Migrate Certs from source key vault to destination key vault.
   input parameter json = > cert url => key vault and cert name/version => get cert and import to dest cert """

import re
import base64
import json

from azure.identity import AzureCliCredential
from azure.keyvault.certificates import CertificateClient
from azure.keyvault.secrets import SecretClient
from azure.mgmt.keyvault import KeyVaultManagementClient
from azure.mgmt.resource import ResourceManagementClient
from azure.common.credentials import get_azure_cli_credentials
from azure.core.exceptions import ResourceNotFoundError

# The following parameters need to be updated by region/service.
SUB_SERIES = "MAML-2"
FOLDER_NAME = "SnrExp"
DEST_LOCATION = "southeastasia"
INPUT_PARAMETER_JSON = \
    fr".\{FOLDER_NAME}\snr-exp-prod-asiasoutheast-001-Production-Southeast Asia_CloudService.Parameters.json"
#############################################################################################

LOCATION_TO_SUB_IDs = {
    "centraluseuap": {
        "MAML-1": "1220ed94-c61b-4690-b5c6-acc242a69250",
        "MAML-2": "8402e523-a30f-4d05-917e-fb5a74b3ff5a",
    },
    "westcentralus": {
        "MAML-1": "c1f017cf-6887-4b7b-901b-efd8647d04c6",
        "MAML-2": "4ee2639f-70ee-4e6f-99f6-b4361dbe4ce1",
    },
    "westeurope": {
        "MAML-1": "db252142-caa2-4ea2-83cb-04677720fe1f",
        "MAML-2": "cdb0a04a-e5cd-4cfa-8dcf-fd5678880640",
    },
    "southcentralus": {
        "MAML-1": "a0e9f47a-2fbb-4dca-8071-169298ff7f6a",
        "MAML-2": "3a4d3ed0-61ec-41ab-956b-91f3168c1e0f",
    },
    "southeastasia": {
        "MAML-1": "6c54c4ca-e743-45a6-ba22-c4ecff8c06d1",
        "MAML-2": "212b350a-9910-41d7-906d-4689ed4977db",
    },
    "japaneast": {
        "MAML-1": "697346b9-61f2-4685-9c98-5703480f5370",
        "MAML-2": "d56631dc-e554-4a36-8096-ab25a16cb612",
    },
    "eastus2": {
        "MAML-2": "3a70124c-988d-4a70-ae83-ddc4c203da75"
    }
}
MAML_Production_4_Community_ID = "6c868b6a-900a-4b5d-8c03-2d5506291b33"
MAML_Production_13_Web_Service_Management_UX_ID = "4d3bdb9c-a8d4-4a75-9ce4-cd4244c00d64"
LOCATION_SHORT_NAME = {
    "centraluseuap": "usce",
    "westcentralus": "wcus",
    "westeurope": "weu",
    "southcentralus": "scus",
    "southeastasia": "sea",
    "japaneast": "jae",
    "eastus2": "eus2",
    "eastus": "eus",
    "eastasia": "ea",
    "northeurope": "neu",
    "westus": "wus"
}
SUB_SERIES_WHITE_LIST = ["MAML-1", "MAML-2", "MAML-4-comm", "MAML-13-UX"]

# Object id of your own microsoft account.
OBJECT_ID = "48d778e1-dc10-421b-8a65-4ac90dc3c21b"

DEST_RESOURCE_GROUP_NAME = "studio-migration"
TENANT_ID = "72f988bf-86f1-41af-91ab-2d7cd011db47"
# Application used for ev2 deployment in release pipeline.
DEPLOYMENT_APP_OBJECT_ID = "cbdda706-d154-4831-85c5-58f6a3765b3f"

def get_subscription_id(location: str, sub_series: str) -> str:
    if sub_series not in SUB_SERIES_WHITE_LIST:
        raise ValueError(f"Parameter sub_series must be one of {SUB_SERIES_WHITE_LIST}. Got {sub_series}")

    if sub_series in ["MAML-1", "MAML-2"]:
        sub_id = LOCATION_TO_SUB_IDs.get(location).get(sub_series)
        if not sub_id:
            raise Exception(f"Subscription not found for location {location} and sub_series {sub_series}")
        return sub_id

    elif sub_series == "MAML-4-comm":
        return MAML_Production_4_Community_ID

    elif sub_series == "MAML-13-UX":
        return MAML_Production_13_Web_Service_Management_UX_ID

def get_dest_vault_name(location: str, sub_series: str) -> str:
    if sub_series not in SUB_SERIES_WHITE_LIST:
        raise ValueError(f"Parameter sub_series must be one of {SUB_SERIES_WHITE_LIST}. Got {sub_series}")

    location_short_name = LOCATION_SHORT_NAME.get(location)
    if not location_short_name:
        raise ValueError(f"Location short name not found for location {location}")

    return f"studio-{location_short_name}-{sub_series}"

DEST_VAULT_NAME = get_dest_vault_name(DEST_LOCATION, SUB_SERIES)
DEST_VAULT_URL = f"https://{DEST_VAULT_NAME}.vault.azure.net"
OUTPUT_CERT_MAPPING_JSON = rf".\{FOLDER_NAME}\cert_mapping_{DEST_LOCATION}.json"

secret_client_cache_dict = dict()
cert_client_cache_dict = dict()
SUBSCRIPTION_ID = get_subscription_id(DEST_LOCATION, SUB_SERIES)

def _create_dest_key_vault():
    """Create destination key vault. If exists, do nothing."""
    credential, _ = get_azure_cli_credentials()
    # Create or update resource group.
    client = ResourceManagementClient(credential, SUBSCRIPTION_ID)
    try:
        client.resource_groups.get(DEST_RESOURCE_GROUP_NAME)
    except:
        print(f"Creating resource group {DEST_RESOURCE_GROUP_NAME} "
              f"in location {DEST_LOCATION} in sub {SUBSCRIPTION_ID}")
        client.resource_groups.create_or_update(DEST_RESOURCE_GROUP_NAME, {'location': DEST_LOCATION})

    # Create or update destination key vault.
    client = KeyVaultManagementClient(credential, SUBSCRIPTION_ID)
    try:
        client.vaults.get(DEST_RESOURCE_GROUP_NAME, DEST_VAULT_NAME)
    except:
        print(f"Creating key vault {DEST_VAULT_NAME} in resource group {DEST_RESOURCE_GROUP_NAME} "
              f"in location {DEST_LOCATION} in sub {SUBSCRIPTION_ID}")
        operation = client.vaults.create_or_update(
            DEST_RESOURCE_GROUP_NAME,
            DEST_VAULT_NAME,
            {
                'location': DEST_LOCATION,
                'properties': {
                    'sku': {
                        'name': 'standard'
                    },
                    'tenant_id': TENANT_ID,
                    'access_policies': [
                        {
                            'object_id': OBJECT_ID,
                            'tenant_id': TENANT_ID,
                            'permissions': {
                                'certificates': ['all'],
                                'secrets': ['all']
                            }
                        },
                        {
                            'object_id': DEPLOYMENT_APP_OBJECT_ID,
                            'tenant_id': TENANT_ID,
                            'permissions': {
                                'certificates': ['all'],
                                'secrets': ['all']
                            }
                        }
                    ],
                    "enabledForDeployment": True
                }
            }
        )
        return operation.result()
    return client.vaults.get(DEST_RESOURCE_GROUP_NAME, DEST_VAULT_NAME)

def _get_certificate_section(complete_json_str: str) -> list:
    """Get certificate section from the input parameter json file."""
    start_target = '"Certificates": '
    start_index = complete_json_str.find(start_target) + len(start_target)
    end_index = complete_json_str.find(']', start_index) + 1  # +1 to include the ] string.
    cert_str = complete_json_str[start_index: end_index]

    try:
        return json.loads(cert_str)
    # Possibly caused because of the comma after the last element
    except json.decoder.JSONDecodeError:
        # Remove the last comma
        cert_str = cert_str[::-1].replace(",", "", 1)[::-1]
        return json.loads(cert_str)

def _create_certificate_client(vault_url: str) -> CertificateClient:
    """Create and cache certificate client."""
    if cert_client_cache_dict.get(vault_url) is not None:
        return cert_client_cache_dict.get(vault_url)

    credential = AzureCliCredential()
    certificate_client = CertificateClient(vault_url=vault_url, credential=credential)
    cert_client_cache_dict.update({vault_url: certificate_client})
    return certificate_client

def _create_secret_client(vault_url: str) -> SecretClient:
    """Create and cache secret client."""
    if secret_client_cache_dict.get(vault_url) is not None:
        return secret_client_cache_dict.get(vault_url)

    credential = AzureCliCredential()
    client = SecretClient(vault_url=vault_url, credential=credential)
    secret_client_cache_dict.update({vault_url: client})
    return client

def _parse_vault_url(url: str) -> (str, str, str):
    """Get base vault url, cert name and cert version from cert url."""
    vault_url_pattern = "^(.+?)(:443|/secrets)"
    cert_name_version_pattern = "/secrets/(.*)"
    try:
        vault_url = re.search(vault_url_pattern, url).group(1)
        cert_name_version = re.search(cert_name_version_pattern, url).group(1)
        cert_name_version = cert_name_version.split("/")
        if len(cert_name_version) == 2:
            return vault_url, cert_name_version[0], cert_name_version[1]

        else:
            return vault_url, cert_name_version[0], None

    except:
        raise ValueError(f"Failed to parse url: {url}")

def _parse_vault_name(vault_url: str) -> str:
    p = "https://(.*).vault"
    return re.search(p, vault_url).group(1)

def _get_cert_section_from_input_file(file_path: str) -> dict:
    with open(file_path, "r") as f:
        d = f.read()
    return _get_certificate_section(d)

def _get_thumbprint(vault_base_url: str, cert_name: str, cert_version: str) -> bytes:
    """Get x509 thumbrprint."""
    client = _create_certificate_client(vault_base_url)
    if cert_version:
        cert = client.get_certificate_version(certificate_name=cert_name, version=cert_version)
    else:
        cert = client.get_certificate(certificate_name=cert_name)
    return cert.properties.x509_thumbprint

def _check_if_certificate_in_secrets_section(vault_base_url: str, cert_name: str, cert_version: str) -> bool:
    """Check if the certificate is stored in key vault's secret section."""
    cert_client = _create_certificate_client(vault_base_url)
    try:
        if cert_version:
            cert_client.get_certificate_version(certificate_name=cert_name, version=cert_version)
        else:
            cert_client.get_certificate(certificate_name=cert_name)
    except ResourceNotFoundError:
        secret_client = _create_secret_client(vault_base_url)
        versions = secret_client.list_properties_of_secret_versions(name=cert_name)
        if any(cert_version in v.version for v in versions):
            return True
    return False

def _check_if_certificate_has_imported(source_vault_url, source_cert_name, source_cert_version, dest_cert_name) -> bool:
    """Check if the certificate has already been imported into destination key vault."""
    dest_cert_client = _create_certificate_client(DEST_VAULT_URL)
    cert_versions = dest_cert_client.list_properties_of_certificate_versions(certificate_name=dest_cert_name)

    if cert_versions:
        source_cert_thumbprint = _get_thumbprint(source_vault_url, source_cert_name, source_cert_version)
        for cv in cert_versions:
            if cv.x509_thumbprint == source_cert_thumbprint:
                return True
    return False

def _get_certificate_version_in_dest_key_vault(
        source_vault_url, source_cert_name, source_cert_version, dest_cert_name) -> str:
    """Get cert version in destination key vault."""
    dest_cert_client = _create_certificate_client(DEST_VAULT_URL)
    cert_versions = dest_cert_client.list_properties_of_certificate_versions(certificate_name=dest_cert_name)
    if cert_versions:
        source_cert_thumbprint = _get_thumbprint(source_vault_url, source_cert_name, source_cert_version)
        for cv in cert_versions:
            if cv.x509_thumbprint == source_cert_thumbprint:
                return cv.version
    raise Exception(f"Failed to find certificate {source_cert_name} in key vault {DEST_VAULT_NAME}")

def main():
    source_dest_url_mapping = list()
    cert_section = _get_cert_section_from_input_file(INPUT_PARAMETER_JSON)

    for c in cert_section:
        mapping_dict = {
            "name": c["Name"],
            "sourceUrl": c["ContentReference"],
        }

        cert_url = c["ContentReference"]
        print(f"Retrieving cert from {cert_url}...")

        source_vault_url, source_cert_name, source_cert_version = _parse_vault_url(cert_url)

        source_secret_client = _create_secret_client(source_vault_url)
        source_cert = source_secret_client.get_secret(name=source_cert_name, version=source_cert_version)

        # Put source vault name into the cert name in destination vault.
        # This helps cert rotation.
        source_vault_name = _parse_vault_name(source_vault_url)
        dest_cert_name = source_vault_name + "--" + source_cert_name

        if _check_if_certificate_in_secrets_section(source_vault_url, source_cert_name, source_cert_version):
            print(f"Cert name {source_cert_name} and version {source_cert_version} is stored in key vault "
                  f"{source_vault_url} as a secret. Need to import manually.")

            mapping_dict.update({"destUrl": "Cert is in key vaults's secret section. Import manually."})
            source_dest_url_mapping.append(mapping_dict)
            continue

        if _check_if_certificate_has_imported(source_vault_url, source_cert_name, source_cert_version, dest_cert_name):
            print(f"Skip import. Cert name {source_cert_name} and version {source_cert_version} has already been "
                  f"imported into key vault {DEST_VAULT_NAME}.")
        else:
            print(f"Importing cert name {source_cert_name} and version {source_cert_version} into key vault "
                  f"{DEST_VAULT_NAME}...")
            dest_cert_client = _create_certificate_client(DEST_VAULT_URL)
            dest_cert_client.import_certificate(certificate_name=dest_cert_name,
                                                certificate_bytes=base64.b64decode(source_cert.value))

        # Get the cert url of imported cert.
        dest_cert_version = _get_certificate_version_in_dest_key_vault(
            source_vault_url, source_cert_name, source_cert_version, dest_cert_name)
        dest_cert_url = DEST_VAULT_URL + ":443/secrets/" + dest_cert_name + "/" + dest_cert_version

        # Update the mapping dict.
        mapping_dict.update({"destUrl": dest_cert_url})
        source_dest_url_mapping.append(mapping_dict)

    with open(OUTPUT_CERT_MAPPING_JSON, "w") as f:
        print(f"Writing mapping json into file {OUTPUT_CERT_MAPPING_JSON}...")
        json.dump(source_dest_url_mapping, f, indent=4)

if __name__ == "__main__":
    main()


import random
import math
import json
import time
import os

import pyglet
from pyglet.graphics import OrderedGroup

from . import guiobject
from . import resources
from . import physicalobject as po
from . import asteroid
from .util import distance

def asteroids(num_asteroids, player_position, screen_size, batch):
    """Creates a list of sprites to draw asteroids,
        making sure they aren't on top of the player"""
    asteroids = []
    for i in range(num_asteroids):

        asteroid_x, asteroid_y = player_position
        while distance((asteroid_x, asteroid_y), player_position) < 100:
            asteroid_x = random.randint(0, screen_size[0])
            asteroid_y = random.randint(0, screen_size[1])

        new_asteroid = asteroid.Asteroid(screen_size=screen_size,
                                    x=asteroid_x, y=asteroid_y, batch=batch)
        new_asteroid.rotation = random.randint(0, 360)
        new_asteroid.velocity_x = random.random() * 40
        new_asteroid.velocity_y = random.random() * 40
        asteroids.append(new_asteroid)

    return asteroids

def player_lives(screen_size, num_icons, batch):
    """Creates a list of sprites to draw amount of player lives."""
    player_lives = []
    for i in range(num_icons):
        x = screen_size[0] - 25 - i * 30
        y = screen_size[1] - 25

        new_sprite = pyglet.sprite.Sprite(img=resources.player_image,
            x=x, y=y, batch=batch)
        new_sprite.scale = 0.5
        player_lives.append(new_sprite)

    return player_lives

def scores(score=None):
    """Reads, appends, and returns scores

    Leaving no score argument will return current score
    Entering score argument will return updated score

    Doesn't save score if 0
    """
    if not os.path.exists('./data'):
        os.makedirs('./data')

    try:
        with open('./data/score.json', 'r') as stream:
            score_data = json.load(stream)
    except:
        score_data = None

    if score not in (0, None):
        # adding score
        data = {'score':score, 'date':time.time()}

        if score_data:
            # data exists
            score_data.append(data)

        else:
            # data doesn't exist
            score_data = [data]

    if score_data:
        with open('./data/score.json', 'w') as stream:
            stream.write(json.dumps(score_data))

        return score_data

    else:
        return [{'date':0, 'score':0}]


import pygame, random
from game.components.enemies.enemy import Enemy

from game.utils.constants import ENEMY_2, SCREEN_HEIGHT

class Enemy2(Enemy):
    SPEED_X = 11
    SPEED_Y = 6
    def __init__(self):
        super().__init__()
        self.image = pygame.transform.scale(ENEMY_2, (50, 50))
        self.count = 0
        self.number_random = random.randint(1, 4)
        self.number = self.number_random

    def update(self, enemies, bullet_manager):
        super().update(enemies, bullet_manager)
        if self.number % 2 == 0:
            if self.rect.y > SCREEN_HEIGHT // 2:
                if self.count < 3:
                    self.rect.y = 20
                    self.count += 1

    def shoot(self, bullet_manager):
        super().shoot(bullet_manager)


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

# # coding: utf-8
#
# # ## 性能度量
# # ### 错误率：分类错误的样本数占总体样本数的比例
# # ### 精度：分类正确的样本数占总体样本的比例
# # ### 查准率：真正例占预测样本正例的比例   P = TP/TP+FP
# # ### 查全率：真正例占实际样本正例的比例   R = TP/TP+FN
# # ### 查准率与查全率是一对矛盾，trade-off。
# # ### PR曲线：查准率为纵轴，查全率为横轴。
# # ### BEP：平衡点（查准率=查全率）
# # ### F1 = 2*P*R/(P+R)
# # ### Fβ = (1+β^2)*P*R/(β^2*P)+R  考虑β趋于无穷时只有R  考虑β趋于0时只有P
# # ### 真正例率 = 查全率 = TP/(TP+FN)
# # ### 假正例率 = FP/（FP+TN）
# # ### ROC曲线：真正例率为纵轴，假正例率为横轴。
# # ### AUC：ROC曲线下方面积
# #

# In[1]:

import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score,confusion_matrix,f1_score,precision_recall_curve
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve,plot_precision_recall_curve,average_precision_score

# In[2]:

data,label = load_iris().data[50:150],load_iris().target[50:150]
label

# In[3]:

x_train,x_test,y_train,y_test = train_test_split(data,label,test_size = 0.3,random_state = 66)

# In[4]:

lr = LogisticRegression()
lr.fit(x_train,y_train)
y_hat = lr.predict(x_test)
diff = y_hat - y_test
acc_score = len(diff[diff==0])/len(diff)
#精度
print('计算的精度为%.5f'%acc_score)
print('sklearn的精度为%.5f'%accuracy_score(y_test,y_hat))

# In[5]:

#计算混淆矩阵
TP,FN,FP,TN = 0,0,0,0
# print(y_hat[0])
for i in range(len(y_test)):
    if y_test[i] == y_hat[i] and y_test[i] == 1:
        TP += 1
    elif y_test[i] == y_hat[i] and y_test[i] ==2:
        TN += 1
    elif y_test[i] != y_hat[i] and y_test[i] == 1:
        FN += 1
    elif y_test[i] != y_hat[i] and y_test[i] == 2:
        FP += 1

p = TP/(TP+FP)
r = TP/(TP+FN)
F1 = 2*p*r/(p+r)

#混淆矩阵
print('计算混淆矩阵:',[TP ,FN ,FP ,TN],'\n')
print('sklearn 混淆矩阵:' , confusion_matrix(y_test,y_hat).ravel() ,'\n') #sklearn

print('sklearn F1score = ',f1_score(y_test,y_hat,average=None))
print('查准率= %.5f,查全率= %.5f,F1score= %.5f'%(p,r,F1))

# In[6]:

y_scores = lr.predict_proba(x_test)[:,1]
y_test.dtype,y_scores.dtype
# y_test.reshape(-1,1)

# In[7]:

p,r,th = precision_recall_curve(y_test,y_scores,pos_label=1)

pr_curve = plot_precision_recall_curve(lr, x_test, y_test)
average_precision = average_precision_score(y_test, y_scores)
pr_curve.ax_.set_title('Precision-Recall curve: '
                   '平均精度={0:0.2f}'.format(average_precision))


import os

import requests

from flask import Flask, request, redirect
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

def render_rows(form):
    return "".join(
        map(lambda f: f"""

                {f[0]}
                {f[1]}

        """, form.items())
    )

def create_message(form):
    subject = "New submission"
    for key in "name", "Name", "email", "Email":
        name = form.get(key)
        if name:
            subject += f" from {name}"
            break
    html = f"""

New submission

    {render_rows(form)}

"""
    return {
        "from": os.environ["EMAIL_FROM"],
        "to": os.environ["EMAIL_TO"],
        "subject": subject,
        "html": html,
    }

@app.route("/", methods=["POST"])
def send_email():
    domain = os.environ["MG_DOMAIN"]
    key = os.environ["MG_API_KEY"]
    url = f"https://api.mailgun.net/v3/{domain}/messages"
    form = request.form
    resp = requests.post(url, data=create_message(form), auth=("api", key))
    resp.raise_for_status()
    return redirect(os.environ["REDIRECT_URL"])


from django.shortcuts import render, redirect
from django.http import HttpResponse
import json
from accounts.models import User
from friends.models import FriendRequest, FriendsList
from django.contrib.auth.decorators import login_required

# Send Friends Request
@login_required
def send_friend_request(request, *args, **kwargs):
	user = request.user
	payload = {}

	if request.method == 'POST': # This's a post request
		user_id = request.POST.get("receiver_user_id")
		if user_id:
			receiver = User.objects.get(id=user_id)

			# Return the queryset of friend request
			friend_requests = FriendRequest.objects.filter(
				sender=user, receiver=receiver
			)

			# check for an active friend request
			for request in friend_requests:
				if request.is_active:
					# display a message
					payload['response'] = 'You have already send them a friend request.'

			# Create and send a friend request
			friend_requests = FriendRequest(
				sender=user, receiver=receiver
			)
			friend_requests.save() # save the friend request to the database
			# display a message
			payload['response'] = 'Friend request sent.'

			if payload['response'] == None:
				payload['response'] = 'Something went wrong.'
	else:
		payload['response'] = 'Unable to send them a friend request'
	return HttpResponse(json.dumps(payload), content_type='application/json')
#End

# Cancel friend request
@login_required
def cancel_friend_request(request, *args, **kwargs):
	user = request.user
	payload = {}

	if request.method == 'POST':
		user_id = request.POST['receiver_user_id']
		if user_id:
			receiver = User.objects.get(pk=user_id)

			try:
				# Return the queryset of friend request
				friend_requests = FriendRequest.objects.filter(
					sender=user,
					receiver=receiver,
					is_active=True
				)
			except:
				# dispaly a message
				payload['response'] = 'Nothing to cancel. Does not exist'

			# Check for the len of friend request
			if len(friend_requests) > 1:
				# loop to find the friend request and cancelled it
				for request in friend_requests:
					request.cancel()
				payload['response'] = 'Friend request cancelled.'
			else:
				# cancel the first friend request
				friend_requests.first().cancel()
				payload['response'] = 'Friend request cancelled.'
		else:
			# display a messages when the user id is invalid
			payload['response'] = 'Unable to cancel that request, invalid id'

	# This's a httpresponse
	# We're updating the page without loading it using Ajax
	return HttpResponse(json.dumps(payload), content_type='application/json')
#End

# Friend request view
@login_required
def friend_request_view(request, *args, **kwargs):
	context = {} # Empty dictionary
	user = request.user # assign variable user to request.user
	user_id = kwargs.get("user_id") # get the user id

	# assign variable account to User models
	# and get the user model id and assign it to user_id
	try:
		account = User.objects.get(pk=user_id)
	except:
		return HttpResponse('Something went wrong.')

	# make both account user model and user request.user equal
	if account == user:

		# return friend request queryset
		friend_requests = FriendRequest.objects.filter(
			# user id is receiving friend request
			receiver=account,
			is_active=True
		)
		# defined template variable
		# access the Friend request variable in the template using context
		context = {
			'friend_requests':friend_requests
		}
	else:
		# Unable to view another person request
		return HttpResponse("You can't view another person friend request")
	return render(request, 'friends/friend_request.html', context)
#End

# Accepting Friend request
@login_required
def accept_friend_request(request, *args, **kwargs):
	user = request.user
	payload = {}
	if request.method == 'GET':
		friend_request_id = kwargs.get("friend_request_id")
		if friend_request_id:
			friend_request = FriendRequest.objects.get(
				pk=friend_request_id
			)

			if friend_request.receiver == user:
				if friend_request:
					update_notification = friend_request.accept()
					payload['response'] = 'Friend request accepted.'
				else:
					payload['response'] = 'Something went wrong.'
			else:
				payload['response'] = 'Not your friend request to accept'
		else:
			payload['response'] = 'Unable to accept that friend request'
	return HttpResponse(json.dumps(payload), content_type='application/json')

# End

# Decline Friend request
@login_required
def decline_friend_request(request, *args, **kwargs):
	user = request.user
	payload = {}
	if request.method == "GET":
		friend_request_id = kwargs.get("friend_request_id")
		if friend_request_id:
			friend_request = FriendRequest.objects.get(
				pk=friend_request_id
			)
			if friend_request.receiver == user:
				if friend_request:
					update_notification = friend_request.decline()
					payload['response'] = 'Friend request declined.'

				else:
					payload['response'] = 'Something went wrong.'

			else:
				payload['response'] = "Not your friend request to decline."

		else:
			payload['response'] = 'Unable to decline that friend request.'

	return HttpResponse(json.dumps(payload), content_type='application/json')
# End

# unfriend a user
@login_required
def unfriend(request, *args, **kwargs):
	user = request.user
	payload = {}
	if request.method == 'POST':
		user_id = request.POST.get('receiver_user_id')
		if user_id:
			try:
				removee = User.objects.get(pk=user_id)
				friend_list = FriendsList.objects.get(user=user)
				friend_list.unfriend(removee)
				payload['response'] = 'Successfully remove a friend.'
			except:
				payload['response'] = 'Somethin went wrong.'
		else:
			payload['response'] = 'There was an error, unable to remove a friend.'
	return HttpResponse(json.dumps(payload), content_type='application/json')
# End


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

import os

# if MTA database already exisits, delete to avoid duplicating data
if os.path.exists('mta_data.db'):
    os.remove('mta_data.db')

# get MTA data from same year as satellite image
os.system('python get_mta.py "18"')

# import SQLAlchemy
from sqlalchemy import create_engine, inspect

# import data analysis, plotting packages
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# set default plot params
plt.rcParams['ytick.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 14
plt.rcParams['axes.labelsize'] = 16
plt.rcParams['axes.titlesize'] = 20
plt.rcParams['figure.titlesize'] = 22
plt.rcParams['lines.linewidth'] = 2
plt.rcParams["font.sans-serif"] = 'Tahoma'

def insert_table_from_csv(csv_list, engine):
    """
    Adds a .csv as a table in an SQL database
    """
    for file in csv_list:
        with open(file, 'r') as f:
            data = pd.read_csv(f)
            data.columns = data.columns.str.strip()
        data.to_sql(os.path.splitext(file)[0], con=engine,
                    index=False, if_exists='replace')

def check_missing_coord(df):
    n_missing = max(len(df[np.isnan(df['LAT'])]), len(df[np.isnan(df['LON'])]))
    if n_missing != 0:
        print(f"\n{n_missing} stations are missing spatial coordinates:")
        print(df[np.isnan(df['LAT']) | np.isnan(df['LON'])])
        print('\n')
    else:
        print('\nNo stations are missing spatial coordinates!\n')

# create SQL database engine
mta_data_engine = create_engine("sqlite:///mta_data.db")

# add the geocoded station locations as a table in the MTA database
insert_table_from_csv(['geocoded.csv'], mta_data_engine)

# make sure that the geocoded table was added
insp = inspect(mta_data_engine)
print('\nTables in database:', insp.get_table_names(), '\n')

# rename column 'C/A' in the mta_data table to 'BOOTH' for consistency with the
# geocoded table
pd.read_sql('''
            ALTER TABLE mta_data
            RENAME COLUMN "C/A" TO "BOOTH";
            ''',
            mta_data_engine);

# join mta_data and geocoded tables on the booth and unit numbers
# select turnstile-level information, include entries and exits, date, time,
# latitude, longitude
# add 'SYSTEM' to differentiate turstiles in the PATH vs. MTA systems
# -- note: many PATH systems are missing lat/lon coordinates, so will likely
#    need to drop them later
# keep only summer (June, July, August) data and 'REGULAR' scheduled audit
# events, not 'RECOVR AUD' entries to avoid duplicates
mta_df_read = pd.read_sql('''
            SELECT a.booth, a.unit, a.scp, a.station, a.linename, a.division,
              a.date, a.time,
              (a.date || ' ' || a.time) AS DATE_TIME,
              a.entries, a.exits,
              b.lat AS LAT, b.lon AS LON,
              (CASE
                WHEN b.booth LIKE '%PTH%' THEN 'PATH'
                ELSE 'MTA'
              END) AS SYSTEM
            FROM mta_data a
            LEFT JOIN geocoded b
            ON a.booth = b.booth AND a.unit = b.unit
            WHERE a.date >= '06/01/2018'
              AND a.date <= '08/31/2018'
              AND a.desc = 'REGULAR';
            ''',
            mta_data_engine)

mta_df = mta_df_read.copy(deep=True)

# convert 'DATE_TIME' column to datetime format
mta_df['DATE_TIME'] = pd.to_datetime(mta_df.DATE_TIME,
                                     format = '%m/%d/%Y %H:%M:%S')

# sort linenames
mta_df['LINENAME'] = mta_df['LINENAME'].apply(lambda x: ''.join(sorted(x)))

mta_df.sort_values('DATE_TIME', ascending=True, inplace=True)

# new columns with the datetime of the previous measurement, and entries
# measurement at the previous measurement
mta_df[['PREV_DATE_TIME', 'PREV_ENTRIES']] = mta_df.groupby(['BOOTH', 'UNIT', 'SCP', 'STATION'])[['DATE_TIME', 'ENTRIES']].shift(1)

# drop the rows for the first measurement in the df because there is no
# 'PREV_DATE_TIME', and reset the index
mta_df.dropna(subset=['PREV_DATE_TIME'], axis=0, inplace=True)
mta_df.reset_index(drop=True, inplace=True)

# calculate the amount of time (in seconds) between measurements
mta_df['DELTA_DATE_TIME_SEC'] = np.abs(mta_df['DATE_TIME'] - mta_df['PREV_DATE_TIME']) / pd.Timedelta(seconds=1)

# to deal with turnstiles that are counting in reverse, always take the abs
# value of the difference between current entries and previous entries
# for hourly entries that are very high, as an upper limit, use 3 people per
# turnstile per second
pps = 3
mta_df['HOURLY_ENTRIES'] = [np.abs(entries - mta_df['PREV_ENTRIES'][i]) \
                            if (np.abs(entries - mta_df['PREV_ENTRIES'][i]) < pps*mta_df['DELTA_DATE_TIME_SEC'][i]) \
                            else (pps*mta_df['DELTA_DATE_TIME_SEC'][i]) \
                            for i,entries in enumerate(mta_df['ENTRIES'])]

# group unique stations (some stations have the same name, but are on different
# lines)
# sum the hourly entries to get the total number of entries over the time period
# sort by net entries
station_df = mta_df.groupby(['STATION','LINENAME'], as_index=False).agg({'LAT':'first', 'LON':'first', 'SYSTEM':'first', 'HOURLY_ENTRIES':'sum'}).sort_values('HOURLY_ENTRIES', ascending=False)

# rename the 'HOURLY_ENTRIES' column to 'NET_ENTRIES'
station_df.rename(columns={'HOURLY_ENTRIES': 'NET_ENTRIES'}, inplace=True)

# check which stations are missing lat/lon coordinates
check_missing_coord(station_df)

# drop the few stations that are missing lat/lon coordinates, ONLY IF they
# are also PATH stations
station_df = station_df[~((np.isnan(station_df['LAT']) | np.isnan(station_df['LON'])) & (station_df['SYSTEM'] == 'PATH'))]

# make sure there are no remaining stations that are missing lat/lon coordinates
check_missing_coord(station_df)

# reset index so that index corresponds to ranked net entries
station_df.reset_index(drop=True, inplace=True)

# create 'CROWD_INDEX'
min_entries = np.min(station_df['NET_ENTRIES'])
max_entries = np.max(station_df['NET_ENTRIES'])
# scale the log of the net entries of each station from 1 to 10 (10 being the
# most crowded, 1 being the least)
station_df['CROWD_INDEX'] = station_df['NET_ENTRIES'].apply(lambda x: 1.0 + 9.0 * np.log10(x/min_entries) / np.log10(max_entries/min_entries))

# save final dataframe
# if saved file already exisits, delete
if os.path.exists('mta_data_analyzed.csv'):
    os.remove('mta_data_analyzed.csv')

station_df.to_csv('mta_data_analyzed.csv')


from os import name
import discord
from discord import player
from discord import member
from discord import embeds
from discord.embeds import Embed
from discord.ext import tasks, commands
import json
import math
from time import sleep

"""Help messages"""
help_dict = {
    'level': discord.Embed(title="Command : level",
                           description="Give your level and experience if no user is specified. \n \
                To know someone else type : `$level @User#0001`",
                           color=0xff0000),

    'coins': discord.Embed(title="Command : coins",
                           description="Gives your amount of coins if no user is specified. \n \
               To know someone else type : `$coins @User#0001`",
                           color=0xff0000),

    'give-coins': discord.Embed(title="Command : give-coins",
                                description="This command is used to give (or remove) coins from the mentionned player. \n \
               Usage is `$give-coins @User#0001 amount` the amount can be negative to remove coins",
                                color=0xff0000),

    'richest': discord.Embed(title="Command : richest",
                             description="Sends the top 10 richest players on the server and the total amount of coins. \n \
               Usage : `$richest`, no parameter is required (or expected)",
                             color=0xff0000),

    'buy': discord.Embed(title="Command : buy",
                         description="You can buy items from the shop with this function. \n \
               It accepts the item ID or its name : `$buy 001` or `$buy Beautiful Role`",
                         color=0xff0000),

    'shop': discord.Embed(title="Command : shop",
                          description="To see the entire shop simple type `$shop`. \n \
               If you want to see only a specific item you can use `$shop item_id` or `$shop item_name`",
                          color=0xff0000),

    'inventory': discord.Embed(title="Command : inventory",
                               description="Lists all the items you own : `$inventory`. \n",
                               color=0xff0000),

    'use': discord.Embed(title="Command : shop",
                         description="Uses the item specified : `$use item_id` or `$use item_name`. \n \
               It will notify staff, they will quickly get in touch with you",
                         color=0xff0000),

    'add-item': discord.Embed(title="Command : shop",
                              description="Restricted to staff. \
               The expected arguments (in order and separated by '$') are : item_id, item_name, price, item_description, item_image. \n \
               Example : `$add-item 0001 $Role#1 $1000 $This role is important $https://imgur.com/y3ls2gX`",
                              color=0xff0000),

    'modify-item': discord.Embed(title="Command : shop",
                                 description="Restricted to staff. \
               Works similar to add-item except that the item with the id item_id will be changed with the new info provided. \n \
               Example : `$modify-item 0001 $RoleChange#1 $2000 $This role is even more important $https://imgur.com/y3ls2gX`",
                                 color=0xff0000),

    'remove-item': discord.Embed(title="Command : shop",
                                 description="Restricted to staff. \
               The item corresponding to the given item id will be deleted. \n \
               Example : `$remove-item 0001`",
                                 color=0xff0000),

    'help': discord.Embed(title='**All available commands**',
                          description='Some displayed are restricted to staff (and will not allow you to execute them)',
                          color=0xff0000),

}
fields_values = ['coins', '`$help coins`', True], ['give-coins', '`$help give-coins`', True], ['richest', '`$help richest`', True],\
                ['buy', '`$help buy`', True], ['shop', '`$help shop`', True], ['inventory', '`$help inventory`', True],\
                ['use', '`$help use`', True], ['level', '`$help level`', True], ['help', '`$help help`', True], \
                ['add-item (staff-only)', '`$help add-item`', True], ['modify-item (staff-only)', '`$help modify-item`', True], \
                ['remove-item (staff-only)', '`$help remove-item`', True]
for val in fields_values:
    help_dict['help'].add_field(name=val[0], value=val[1], inline=val[2])

allow_userIDs = [425337707825201152, 892805391870726164, 508950406076825621, 367344593911545856,
                 198373115892465664, 257501547246780416, 511165011637436416, 533584123600830466]

client = commands.Bot(command_prefix='$', help_command=None)

"""Functions"""

def update_money(change_dict: dict):
    """
    change_dict = {userID : amount,}
    with amount being positive or negative
    """
    pass

    with open('data.json', 'r') as f:
        current_info = json.load(f)

    for userID, amount in change_dict.items():
        userID = str(userID)
        if userID not in current_info:
            current_info[userID] = amount
        else:
            if current_info[userID] + amount < 30000:
                current_info[userID] += amount
            else:
                current_info[userID] = 30000

    with open('data.json', 'w') as f:
        json.dump(current_info, f, indent=4)

def get_amounts():
    """
    :return: dict with {userID : amount,}
    """
    with open('data.json', 'r') as f:
        current_info = json.load(f)

    return current_info

"""Auto-Giving Coins (activity)"""
info = {"messages": {},
        "vocal": {},
        "user_vocal": []}

#   Message Counter #
@client.event
async def on_message(msg):
    await client.process_commands(msg)
    authorID = msg.author.id

    member = await FNTK_guild.fetch_member(int(authorID))

    multiplier = 1
    for role in member.roles:
        if role.id == 923161171744411668:
            multiplier = 1.5
            break
    if authorID not in info['messages']:
        info['messages'][authorID] = 1*multiplier
    else:
        info['messages'][authorID] += 1*multiplier

#   Vocal Counter   #

@client.event
async def on_voice_state_update(user, before, after):
    userID = user.id

    if after.channel == None or after.channel.id == 893189817322602537:
        info['user_vocal'].remove(userID)
    else:
        # in vc and not afk vc
        if userID not in info['user_vocal'] and after.channel.id != 893189817322602537:
            info['user_vocal'].append(userID)
        else:  # he muted/unmuted deafened/undeafened himself/herself
            pass

@tasks.loop(seconds=10)
async def update():

    #   Update vocal amount #
    for userID in info['user_vocal']:
        member = await FNTK_guild.fetch_member(int(userID))

        multiplier = 1
        for role in member.roles:
            if role.id == 923161171744411668:
                multiplier = 1.5
                break
        if userID not in info['vocal']:
            info['vocal'][userID] = 10*multiplier
        else:
            info['vocal'][userID] += 10*multiplier

    give_dict = {}

    #   10 messages = 1 coin    #
    for userID, amount in info['messages'].items():
        #   Calculating coins   #
        if amount >= 10:  # user getting at least 1 coin
            fntk_coins = amount // 10
            info['messages'][userID] = amount % 10
        else:
            fntk_coins = 0

        if userID not in give_dict:
            give_dict[userID] = fntk_coins
        else:
            give_dict[userID] += fntk_coins

    #   1 hour of vc = 20 coins #
    for userID, time in info['vocal'].items():
        #   Calculating coins   #
        if time >= 3600:  # user getting at least 20 coins
            fntk_coins = (time // 3600)*20
            info['vocal'][userID] = time % 3600
        else:
            fntk_coins = 0

        if userID not in give_dict:
            give_dict[userID] = fntk_coins
        else:
            give_dict[userID] += fntk_coins
    # updating the levels
    with open('level.json', 'r') as f:
        lvl = json.load(f)

    for user, xp in give_dict.items():
        user = str(user)
        if user not in lvl:
            lvl[user] = {'xp': xp, 'lvl': 0}

        else:
            lvl[user]['xp'] += xp
            if lvl[user]['lvl'] < 10:
                if lvl[user]['xp'] >= (lvl[user]['lvl']+1)*1000:
                    lvl[user]['xp'] -= (lvl[user]['lvl']+1)*1000
                    lvl[user]['lvl'] += 1
            elif lvl[user]['lvl'] < 16:
                if lvl[user]['xp'] >= 10000:
                    lvl[user]['xp'] -= 10000
                    lvl[user]['lvl'] += 1
            elif lvl[user]['lvl'] < 21:
                if lvl[user]['xp'] >= 20000:
                    lvl[user]['xp'] -= 20000
                    lvl[user]['lvl'] += 1
            name = f"Level {lvl[user]['lvl']}"
            if lvl[user]['lvl'] != 0:
                roles = FNTK_guild.roles
                role = discord.utils.get(roles, name=name)
                member = await FNTK_guild.fetch_member(int(user))
                await member.add_roles(role)

    with open('level.json', 'w') as f:
        json.dump(lvl, f, indent=4)

    update_money(give_dict)

"""User Commands"""

@client.event
async def on_ready():
    global FNTK_guild
    print('Ready !')
    FNTK_guild = await client.fetch_guild(692749537219051531)
    update.start()

@client.command(name='coins')
async def coins(ctx, *user):

    current_info = get_amounts()
    return_embed = discord.Embed(description='Empty', color=6345206)

    if not user:  # wants his/her amount of coins
        authorID = ctx.message.author.id
        for userID, amount in current_info.items():
            if int(userID) == authorID:
                return_embed.description = f"**You** have {amount} coins !"

        if return_embed.description == 'Empty':
            return_embed.description = f"**You** don't have **any** coins"
        await ctx.channel.send(embed=return_embed)
    else:  # wants someone else's amount of coins
        memberID = user[0]
        for char in '<@!>':
            memberID = memberID.replace(char, '')
        member = await client.fetch_user(memberID)

        for userID, amount in current_info.items():
            if int(memberID) == int(userID):

                return_embed.description = f"**{member.name}** has {amount} coins !"

        if return_embed.description == 'Empty':
            return_embed.description = f"**{member.name}** doesn't  have **any** coins !"
        await ctx.channel.send(embed=return_embed)

@client.command(name='give-coins')
async def give_coins(ctx, user_id, amount):
    userID = ctx.message.author.id
    if userID not in allow_userIDs:
        await ctx.channel.send(embed=discord.Embed(description='You are not part of the whitelist :x:', color=6345206))
    else:

        for char in '<@!>':
            user_id = user_id.replace(char, '')

        update_money({user_id: int(amount)})
        pseudo = await client.fetch_user(user_id)
        amount = int(amount)
        if 1 < amount:
            description = f":white_check_mark: {amount} <:fanatikCoins:894584412166058014> have been given to {pseudo.mention}"
        if 1 == amount:
            description = f":white_check_mark: {amount} <:fanatikCoins:894584412166058014> has been given to {pseudo.mention}"
        elif 0 == amount:
            description = "I cannot give 0 coins."
        elif 1 == amount:
            description = f":white_check_mark: {amount-(2*amount)} <:fanatikCoins:894584412166058014> has been removed from {pseudo.mention}"
        elif 1 > amount:
            description = f":white_check_mark: {amount-(2*amount)} <:fanatikCoins:894584412166058014> have been removed from {pseudo.mention}"
        color = 6345206
        embed = discord.Embed(description=description, color=color)
        await ctx.channel.send(embed=embed)

@client.command(name='richest')
async def richest(ctx):
    dico = get_amounts()
    valeur = []
    user_list = []
    am_total = 0
    for userID, amount in dico.items():
        valeur.append(amount)
        user_list.append(userID)
        am_total += amount

    embed = discord.Embed(
        title="Richest", description="List of the richest people on the server", color=6345206)
    embed.set_thumbnail(
        url="https://cdn.discordapp.com/icons/692749537219051531/0c98647b4dd76cdecc2c670556c75f7e.png?size=4096")
    embed.add_field(name=f':bank: - Server Total',
                    value=f"{am_total} coins", inline=False)
    am_total = 0
    for i in range(10):
        rech_am = max(valeur)
        j = valeur.index(rech_am)
        if rech_am == 0:
            rech_us = None
        else:
            rech_us = await client.fetch_user(user_list[j])
            rech_us = rech_us.name
        valeur[j] = 0
        if i == 0:
            embed.add_field(
                name=f':first_place: - {rech_us}', value=f"<:fanatikCoins:894584412166058014> {rech_am} coins", inline=False)
        elif i == 1:
            embed.add_field(
                name=f':second_place: - {rech_us}', value=f"<:fanatikCoins:894584412166058014> {rech_am} coins", inline=False)
        elif i == 2:
            embed.add_field(
                name=f':third_place: - {rech_us}', value=f"<:fanatikCoins:894584412166058014> {rech_am} coins", inline=False)
        else:
            embed.add_field(
                name=f'{i+1} - {rech_us}', value=f"<:fanatikCoins:894584412166058014> {rech_am} coins", inline=False)
    await ctx.channel.send(embed=embed)

@client.command(name='buy')
async def buy(ctx, *item_id):
    with open('inventory.json', 'r') as f:
        inv = json.load(f)

    with open('shop.json', 'r') as f:
        shop = json.load(f)

    item_id = ' '.join(item_id).lower()

    authorID = ctx.message.author.id
    current_info = get_amounts()
    for userID, amount in current_info.items():
        if int(userID) == authorID:
            try:
                shop[item_id]
            except:
                for i in shop.keys():
                    if item_id == shop[i][0]:
                        item_id = i
            if amount >= shop[item_id][2]:
                if userID not in inv:
                    inv[userID] = {item_id: 1}
                else:
                    if str(item_id) in inv[userID]:
                        inv[userID][str(item_id)] += 1
                    else:
                        inv[userID][str(item_id)] = 1
                update_money({userID: -shop[item_id][2]})
                pseudo = await client.fetch_user(authorID)
                description = f'**{pseudo.name}** bought **{shop[item_id][0]}**'
            else:
                description = f"You don't have enough coins"
    embed = discord.Embed(description=description, color=6345206)
    await ctx.channel.send(embed=embed)

    with open('inventory.json', 'w') as f:
        json.dump(inv, f, indent=4)

@client.command(name='shop')
async def shop(ctx, *id):
    with open('shop.json', 'r') as f:
        shop = json.load(f)

    title = '**Boutique de TEAM FANATIK**'
    description = "Use `$shop <item name>` or `$shop <item id>` to get more details about an item.\nUse `$buy <item name>` or `$buy <item id>`to buy an item\n\n:money_with_wings: **Here are our items:**"
    color = 6345206

    new_embed = discord.Embed(
        title=title, description=description, color=color)

    msg = ""
    if id == ():
        for i in shop.items():
            new_embed.add_field(name=f"{str(i[1][0])} - <:fanatikCoins:894584412166058014> {str(i[1][2])}:",
                                value=f"{str(i[1][1])} \n`id : {str(i[0])}`", inline=False)
            msg += "ok"
    else:
        id = ' '.join(id).lower()
        id = str(id)
        description2 = "Use `$shop <item name>` or `$shop <item id>` to get more details about an item.\nUse `$buy <item name>` or `$buy <item id>`to buy an item\n\n:money_with_wings: **Here is the item you requested:**"
        new_embed = discord.Embed(
            title=title, description=description2, color=color)
        for i in shop.items():
            if i[1][0] == id or i[0] == id:
                new_embed.add_field(name=f"{str(i[1][0])} - <:fanatikCoins:894584412166058014> {str(i[1][2])}:",
                                    value=f"{str(i[1][1])} \nid : `{str(i[0])}`", inline=False)
                new_embed.set_image(url=str(i[1][3]))
                msg += "ok"
        if msg == "":
            msg = "bug"
            new_embed = discord.Embed(
                title="Error !", description="The item you asked for does not exist.", color=color)
    await ctx.channel.send(embed=new_embed)

@client.command(name='inventory')
async def inventory(ctx):
    with open('inventory.json', 'r') as f:
        inv = json.load(f)

    with open('shop.json', 'r') as f:
        shop = json.load(f)

    authorID = str(ctx.message.author.id)
    try:
        playerInv = inv[authorID]
    except KeyError:  # user has no inventory
        playerInv = {}

    member = await client.fetch_user(authorID)

    title = f"{member.name}'s inventory"
    description = "Use `$shop <item name>` or `$shop <item id>` to get more details about an item.\nUse `$buy <item name>` or `$buy <item id>`to buy an item\nUse `$use <item name>` or `$use <item id>` to use an item\n\n**You have :**"
    color = 6345206

    new_embed = discord.Embed(
        title=title, description=description, color=color)

    for i in playerInv.items():
        for y in shop.keys():
            if str(shop[i[0]][0]) == shop[y][0]:
                item_id = y
        new_embed.add_field(name=f"{str(i[1])} {str(shop[i[0]][0])}:",
                            value=f"{str(shop[item_id][1])}\nid : `{item_id}`", inline=False)
    await ctx.channel.send(embed=new_embed)

@client.command(name='help')
async def help(ctx, *cmd):
    if not cmd:
        await ctx.channel.send(embed=help_dict['help'])
    else:
        if cmd[0] in help_dict:

            await ctx.channel.send(embed=help_dict[cmd[0]])
        else:
            await ctx.channel.send(embed=discord.Embed(title='**Comand not found**', description=f'{cmd[0]} is not a valid command, type `$help` for a complete list'))

@client.command(name='use')
async def use(ctx, *item_id):
    with open('inventory.json', 'r') as f:
        inv = json.load(f)

    with open('shop.json', 'r') as f:
        shop = json.load(f)

    item_id = ' '.join(item_id).lower()
    item_id = str(item_id)
    authorID = str(ctx.message.author.id)
    color = 6345206
    if authorID in inv:
        try:
            shop[item_id]
        except:
            for i in shop.keys():
                if item_id == shop[i][0]:
                    item_id = i
        if item_id in inv[authorID]:
            inv[authorID][item_id] -= 1
            if shop[item_id][0] == "banana":
                title = "Stop everything"
                embed = discord.Embed(title=title, color=color)
                await ctx.channel.send(embed=embed)
                sleep(0.5)
                title = "Kris get the banana!"
                embed = discord.Embed(title=title, color=color)
                await ctx.channel.send(embed=embed)
                sleep(0.5)
                title = "The banana !"
                embed = discord.Embed(title=title, color=color)
                await ctx.channel.send(embed=embed)
                sleep(0.5)
                title = "**POTASSIUM**"
                description = "Kris got the banana"
                await ctx.channel.send("https://images-ext-1.discordapp.net/external/7XU8lcCaOUnWQYywFUHc4mLDqDAIYTC5Bu6MVQNVIF4/https/i.kym-cdn.com/photos/images/newsfeed/002/206/266/b53.gif")
            else:
                if inv[authorID][item_id] == 0:
                    del inv[authorID][item_id]
                member = await client.fetch_user(authorID)
                title = "Use"
                description = f"**{member.name}** used **{shop[item_id][0]}**"
                await ctx.channel.send(f"<@892805391870726164>")
        else:
            title = "Error !"
            description = "You don't own this item"
    else:
        title = "Error !"
        description = "You don't own this item"

    new_embed = discord.Embed(
        title=title, description=description, color=color)
    await ctx.channel.send(embed=new_embed)

    with open('inventory.json', 'w') as f:
        json.dump(inv, f, indent=4)

@client.command(name='add-item')
async def add_item(ctx, *msg):
    userID = ctx.message.author.id
    if userID not in allow_userIDs:
        await ctx.channel.send(embed=discord.Embed(description='You are not part of the whitelist :x:', color=6345206))
    else:
        msg = ' '.join(msg)
        msg = msg.split(" $")
        item_id, item_name, price, item_description, item_image = msg[0], msg[1].lower(
        ), msg[2], msg[3], str(msg[4])
        try:
            int(price)
        except ValueError:
            await ctx.channel.send(f"{price} isn't a valid price")
            return(None)  # To prevent the function from creating an invalid item

        try:
            int(item_id)
        except ValueError:
            await ctx.channel.send(f"{item_id}isn't a valid id")
            return(None)  # To prevent the function from creating an invalid item

        with open('shop.json', 'r') as f:
            shop = json.load(f)

        if type(item_description) == list or type(item_description) == tuple:
            item_description = ' '.join(item_description)

        shop[item_id] = [item_name, item_description, int(price), item_image]

        with open('shop.json', 'w') as f:
            json.dump(shop, f, indent=4)

        title = "Added to shop :"
        color = 6345206
        name = f"{item_name} - <:fanatikCoins:894584412166058014> {price}:"
        value = f"{item_description} \n`id:{item_id}`"
        embed = discord.Embed(title=title, color=color)
        embed.add_field(name=name, value=value)
        embed.set_image(url=item_image)
        await ctx.channel.send(embed=embed)

@client.command(name='modify-item')
async def modify_item(ctx, *msg):
    userID = ctx.message.author.id
    if userID not in allow_userIDs:
        await ctx.channel.send(embed=discord.Embed(description='You are not part of the whitelist :x:', color=6345206))
    else:
        msg = ' '.join(msg)
        msg = msg.split(" $")
        item_id, item_name, price, item_description, item_image = msg[0], msg[1].lower(
        ), msg[2], msg[3], str(msg[4])
        try:
            int(price)
        except ValueError:
            await ctx.channel.send(f"{price} isn't a valid price")
            return(None)  # To prevent the function from creating an invalid item

        try:
            int(item_id)
        except ValueError:
            await ctx.channel.send(f"{item_id}isn't a valid id")
            return(None)  # To prevent the function from creating an invalid item

        with open('shop.json', 'r') as f:
            shop = json.load(f)

        if type(item_description) == list or type(item_description) == tuple:
            item_description = ' '.join(item_description)

        shop[item_id] = [item_name, item_description, int(price), item_image]

        with open('shop.json', 'w') as f:
            json.dump(shop, f, indent=4)

        title = "Modified from shop :"
        color = 6345206
        name = f"{item_name} - <:fanatikCoins:894584412166058014> {price}:"
        value = f"{item_description} \n`id:{item_id}`"
        embed = discord.Embed(title=title, color=color)
        embed.add_field(name=name, value=value)
        embed.set_image(url=item_image)
        await ctx.channel.send(embed=embed)

@client.command(name='remove-item')
async def remove_item(ctx, item_id):
    userID = ctx.message.author.id
    if userID not in allow_userIDs:
        await ctx.channel.send(embed=discord.Embed(description='You are not part of the whitelist :x:', color=6345206))
    else:
        with open('shop.json', 'r') as f:
            shop = json.load(f)

        name = shop[item_id][0]

        del shop[item_id]

        with open('shop.json', 'w') as f:
            json.dump(shop, f, indent=4)

        title = "Removed from shop :"
        color = 6345206
        description = f"{name} was successfully removed from shop"
        embed = discord.Embed(
            title=title, description=description, color=color)
        await ctx.channel.send(embed=embed)

@client.command(name='level')
async def level(ctx, *user):
    with open('level.json', 'r') as f:
        current_info = json.load(f)

    if not user:  # wants his/her level
        authorID = ctx.message.author.id

    else:  # wants someone else's level
        authorID = user[0]
        for char in '<@!>':
            authorID = authorID.replace(char, '')
    member = await client.fetch_user(authorID)

    title = f"{member.name}'s level"
    description = "You can earn levels by sending messages or spending time in a voice channel. \nBe careful, You won't earn any xp in afk channel."
    color = 6345206
    return_embed = discord.Embed(
        title=title, description=description, color=color)

    for userID, infos in current_info.items():

        if int(userID) == int(authorID):
            name = f"Level {infos['lvl']}"
            if infos['lvl'] < 10:
                needed = (infos['lvl']+1)*1000
            elif infos['lvl'] < 16:
                needed = 10000
            elif infos['lvl'] < 21:
                needed = 20000
            value = f"xp : {infos['xp']}/{needed}\n {infos['xp']*100/needed}% to the next level"
            return_embed.add_field(name=name, value=value)

    await ctx.channel.send(embed=return_embed)

client.run('ODYxNTI5MjM5Njc3NTY2OTg2.YOLHoQ.s-UFtmIyvXQfpyd5l93o8YprY_w')