from logging import exception
import re
import threading
import time
import json
from collections import OrderedDict
from turtle import title
import pyautogui
import tkinter as tk
import tkinter.font as font
import tkinter.ttk as ttk
import keyboard
from tkinter import END, messagebox
from tkinter import filedialog

window = tk.Tk()
window.title('Tnank you for your help')
window.geometry('1000x400+0+0')
window.resizable(True,True)

font_15 = font.Font(size=15)
font_15_bold = font.Font(size=15, weight='bold')

select_func_list = ['Mouse', 'Keyboard', 'Hotkey', 'Write' 'Text']
mouse_func_list = ['L-Click', 'L-Down', 'L-Up', 'R-Click', 'R-Down', 'R-Up', 'L-Double','L-Triple', 'Move', 'Drag']
key_func_list = ['Press', 'KeyDown', 'KeyUp']
key_list = ['left', 'right', 'up', 'down','enter', 'esc', 'escape', 'space', 'backspace', 'tab', 'alt', 'altleft', 'altright', 'ctrl', 'ctrlleft', 'ctrlright', 'shift', 'shiftleft', 'shiftright', 'del', 'delete', 'end', 'hanguel', 'hangul', 'home', 'insert',
            'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12',
            'pagedown', 'pageup', 'pause', 'pgdn', 'pgup', 'printscreen', 'prntscrn', 'scrolllock',
            '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/',  ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'
            'a', 'b', 'c', 'd', 'e','f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o','p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '0', '1', '2', '3', '4', '5', '6', '7','8', '9',
            'num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7', 'num8', 'num9', 'numlock'
            ]
hotkey_list = ['Ctrl+a', 'Ctrl+c', 'Ctrl+v']

save_condition_arr=[]       #save all things, [num, act_deact, select_func, mouse_func, mouseX, mouseY, key_func, key_combo, hotkey, text_ent, delay_ent, target_win_posX, target_win_posY]

t_cnt=0                     #total target window number
target_win_arr=[]           #save target windows
target_lb1_arr=[]           #target number label
target_lb2_arr=[]           #'+' label
act_chk_btn_arr=[]          #act/deactive check box
chk_var=[]                  #save checked or not(1 or 0) for act_chk_btn
no_lb_arr=[]                #number of list
select_func_combo_arr=[]    #select function(mouse, keyboard, hotkey, text)
mouse_func_combo_arr=[]     #mouse function
key_func_combo_arr=[]
key_combo_arr=[]
hotkey_combo_arr=[]
text_entry_arr=[]
delay_entry_arr=[]

thd1_bool = False           #thread boolean

#make new target window
def makeTarget(act_deact = 1, select_func=0, mouse_func=0, key_func=0, key_combo=0, hotkey=0, text_ent='', delay_ent='1000', target_win_posX='0', target_win_posY='0'):
    t_cnt = len(target_win_arr)
    #make new window and setting
    target_win_arr.append(tk.Toplevel(window))
    target_win_arr[t_cnt].wm_attributes("-topmost", True)
    target_win_arr[t_cnt].wm_attributes("-transparentcolor", '#F0F0F0')      #to make window transparent
    target_win_arr[t_cnt].geometry('150x100' + '+' + target_win_posX + '+' + target_win_posY)
    target_win_arr[t_cnt].overrideredirect(True)

    #show target num and target point '+'
    target_lb1_arr.append(tk.Label(target_win_arr[t_cnt], borderwidth=1, relief='ridge', bg='red', text=str(t_cnt+1)))
    target_lb2_arr.append(tk.Label(target_win_arr[t_cnt], borderwidth=1, relief='ridge', width=3, font=font_15, fg='red', text='+'))
    target_lb1_arr[t_cnt].grid(row=0, column=0,sticky='ns')
    target_lb2_arr[t_cnt].grid(row=0, column=1)

    #create checkbutton, label, combobox, entry
    chk_var.append(tk.IntVar())
    no_lb_arr               .append(tk.Label(set_target_lbframe, text=str(t_cnt+1)))
    act_chk_btn_arr         .append(ttk.Checkbutton(set_target_lbframe, variable=chk_var[t_cnt]))
    select_func_combo_arr   .append(ttk.Combobox(set_target_lbframe, state='readonly', width=8, values=select_func_list))
    mouse_func_combo_arr    .append(ttk.Combobox(set_target_lbframe, state='readonly', width=8, values=mouse_func_list))
    key_func_combo_arr      .append(ttk.Combobox(set_target_lbframe, state='readonly', width=8, values=key_func_list))
    key_combo_arr           .append(ttk.Combobox(set_target_lbframe, state='readonly', width=8, values=key_list))
    hotkey_combo_arr        .append(ttk.Combobox(set_target_lbframe, state='readonly', width=8, values=hotkey_list))
    text_entry_arr          .append(tk.Entry(set_target_lbframe))
    delay_entry_arr         .append(tk.Entry(set_target_lbframe, justify='right', width=8))

    #grid checkbutton, label, combobox, entry
    no_lb_arr               [t_cnt].grid(row=t_cnt+2, column=0, padx=(5,5), pady=(5,0))
    act_chk_btn_arr         [t_cnt].grid(row=t_cnt+2, column=2, padx=(5,5), pady=(5,0))
    select_func_combo_arr   [t_cnt].grid(row=t_cnt+2, column=4, padx=(5,5), pady=(5,0))
    mouse_func_combo_arr    [t_cnt].grid(row=t_cnt+2, column=6, padx=(5,5), pady=(5,0))
    key_func_combo_arr      [t_cnt].grid(row=t_cnt+2, column=8, padx=(5,5), pady=(5,0))
    key_combo_arr           [t_cnt].grid(row=t_cnt+2, column=10, padx=(5,5), pady=(5,0))
    hotkey_combo_arr        [t_cnt].grid(row=t_cnt+2, column=12, padx=(5,5), pady=(5,0))
    text_entry_arr          [t_cnt].grid(row=t_cnt+2, column=14, padx=(5,5), pady=(5,0))
    delay_entry_arr         [t_cnt].grid(row=t_cnt+2, column=16, padx=(5,5), pady=(5,0))

    chk_var                 [t_cnt].set(act_deact)              #always checked when it created
    select_func_combo_arr   [t_cnt].current(select_func)        #select first value
    mouse_func_combo_arr    [t_cnt].current(mouse_func)         #select first value
    key_func_combo_arr      [t_cnt].current(key_func)           #select first value
    key_combo_arr           [t_cnt].current(key_combo)          #select first value
    hotkey_combo_arr        [t_cnt].current(hotkey)             #select first value
    text_entry_arr          [t_cnt].delete(0, tk.END)
    text_entry_arr          [t_cnt].insert(0, text_ent)
    delay_entry_arr         [t_cnt].delete(0, tk.END)
    delay_entry_arr         [t_cnt].insert(0, delay_ent)
    actDeactWidgets('')

    #to move target point using mouse drag
    def drag(event):
        if(not(thd1_bool)):
            width = str(target_win_arr[t_cnt].winfo_width())
            height = str(target_win_arr[t_cnt].winfo_height())
            pos_x = str(pyautogui.position().x)
            pos_y = str(pyautogui.position().y)
            target_win_arr[t_cnt].geometry(width + 'x' + height + '+' + pos_x + '+' + pos_y)

    #mouse left drag event
    target_win_arr[t_cnt].bind("<B1-Motion>", drag)
    #only digit input
    delay_entry_arr[t_cnt].bind('<KeyRelease>', onlyNumbers)
    #when combobox selected, widget condition will be change
    select_func_combo_arr[t_cnt].bind('<<ComboboxSelected>>', actDeactWidgets)
    #refresh scrollbox when item added
    canvas.configure(scrollregion=canvas.bbox("all"))

#delete target and list
def delTarget():
    t_cnt = len(target_win_arr)
    if t_cnt != 0:
        #destroy widgets
        target_lb1_arr          [t_cnt-1].destroy()
        target_lb2_arr          [t_cnt-1].destroy()
        target_win_arr          [t_cnt-1].destroy()
        act_chk_btn_arr         [t_cnt-1].destroy()
        no_lb_arr               [t_cnt-1].destroy()
        select_func_combo_arr   [t_cnt-1].destroy()
        mouse_func_combo_arr    [t_cnt-1].destroy()
        key_func_combo_arr      [t_cnt-1].destroy()
        key_combo_arr           [t_cnt-1].destroy()
        hotkey_combo_arr        [t_cnt-1].destroy()
        text_entry_arr          [t_cnt-1].destroy()
        delay_entry_arr         [t_cnt-1].destroy()

        #remove list
        target_lb1_arr          .pop()
        target_lb2_arr          .pop()
        target_win_arr          .pop()
        act_chk_btn_arr         .pop()
        no_lb_arr               .pop()
        select_func_combo_arr   .pop()
        mouse_func_combo_arr    .pop()
        key_func_combo_arr      .pop()
        key_combo_arr           .pop()
        hotkey_combo_arr        .pop()
        text_entry_arr          .pop()
        delay_entry_arr         .pop()
        chk_var                 .pop()
    else:
        print('no widget to delete')

    #refresh scrollbox when item deleted
    canvas.configure(scrollregion=canvas.bbox("all"))

#thread start
def threadStart():
    global thd1_bool
    thd1 = threading.Thread(target=saveCondition)
    if(thd1_bool):
        print('already thread started.')
        thd1_bool = False
    else:
        if(len(target_win_arr) < 1):
            messagebox.showwarning(title='Warning', message='Please add targets push the + button')
        else:
            print('thread start!')
            thd1_bool = True
            start_btn.config(bg='red')
            thd1.start()

#thread stop
def threadStop():
    global thd1_bool
    thd1_bool = False

#check incorrect values
def checkValues():
    loop = loop_entry.get()
    init_delay = init_delay_entry.get()

    #init delay empty check
    if(init_delay == ''):
        init_delay_entry.insert(0,'1000')

    #Loop empty check
    if(loop == ''):
        loop_entry.insert(0,'1')

    #delay empty check
    t_cnt = len(target_win_arr)
    for idx in range(0, t_cnt):
        delay_ent   = delay_entry_arr[idx].get()
        if(delay_ent == ''):
            delay_entry_arr[idx].insert(0,'10')

#when you push the start button, it will save target points and ect. only act checked
def saveCondition():
    try:
        checkValues()
        allWidgetsActDeact()

        save_condition_arr.clear()
        t_cnt = len(target_win_arr)
        for idx in range(0, t_cnt):
            num             = no_lb_arr[idx].cget('text')
            act_deact       = chk_var[idx].get()
            select_func     = select_func_combo_arr[idx].get()
            mouse_func      = mouse_func_combo_arr[idx].get()
            #mouse target point X,Y
            mouseX          = target_lb2_arr[idx].winfo_rootx() + (target_lb2_arr[idx].winfo_width())/2
            mouseY          = target_lb2_arr[idx].winfo_rooty() + (target_lb2_arr[idx].winfo_height())/2
            key_func        = key_func_combo_arr[idx].get()
            key_combo       = key_combo_arr[idx].get()
            hotkey          = hotkey_combo_arr[idx].get()
            text_ent        = text_entry_arr[idx].get()
            delay_ent       = delay_entry_arr[idx].get()
            delay_ent       = int(delay_ent) / 1000
            target_win_posX = str(target_win_arr[idx].winfo_rootx())
            target_win_posY = str(target_win_arr[idx].winfo_rooty())

            save_condition_arr.append([num, act_deact, select_func, mouse_func, mouseX, mouseY, key_func, key_combo, hotkey, text_ent, delay_ent, target_win_posX, target_win_posY])

        for item in save_condition_arr:
            print(item)

        if(thd1_bool):
            play()

    except Exception as e:
        print('play error : ',e)

def targetCrossActDeact(show):
    if(show):
        cnt = len(target_lb2_arr)
        for idx_lb in range(0, cnt):
            target_lb2_arr[idx_lb]['text']='+'
    else:
        cnt = len(target_lb2_arr)
        for idx_lb in range(0, cnt):
            target_lb2_arr[idx_lb]['text']=''

def play():
    global thd1_bool
    targetCrossActDeact(False)

    loop = loop_entry.get()
    init_delay = init_delay_entry.get()
    loop = int(loop)

    #init delay
    init_delay = int(init_delay_entry.get()) /1000
    time.sleep(init_delay)

    for idx_lp in range(1, loop+1):
        #show loop progress
        prog_loop_lb2.config(text=str(idx_lp).zfill(3) + '/' + str(loop).zfill(3))
        for idx, item in enumerate(save_condition_arr):
            if(thd1_bool == False): break
            #show loop progress
            prog_element_lb2.config(text=str(idx+1).zfill(3) + '/' + str(len(save_condition_arr)).zfill(3))
            if(item[1] == 1):   #act_chkbtn is checked
                if(item[2] == 'Mouse'):
                    if(item[3] == 'L-Click'):
                        pyautogui.click(button='left', x=item[4], y=item[5])
                    elif(item[3] == 'L-Down'):
                        pyautogui.mouseDown(button='left', x=item[4], y=item[5])
                    elif(item[3] == 'L-Up'):
                        pyautogui.mouseUp(button='left', x=item[4], y=item[5])
                    elif(item[3] == 'R-Click'):
                        pyautogui.click(button='right', x=item[4], y=item[5])
                    elif(item[3] == 'R-Down'):
                        pyautogui.mouseDown(button='right', x=item[4], y=item[5])
                    elif(item[3] == 'R-Up'):
                        pyautogui.mouseUp(button='right', x=item[4], y=item[5])
                    elif(item[3] == 'L-Double'):
                        pyautogui.click(clicks=2, button='left', x=item[4], y=item[5])
                    elif(item[3] == 'L-Triple'):
                        pyautogui.click(clicks=3, button='left', x=item[4], y=item[5])
                    elif(item[3] == 'Move'):
                        pyautogui.moveTo(item[4], item[5])
                    elif(item[3] == 'Drag'):
                        pyautogui.dragTo(item[4], item[5], 0.5, button='left')
                    else:
                        print('somethings wrong (mouse)')

                elif(item[2] == 'Keyboard'):
                    if(item[6] == 'Press'):
                        pyautogui.press(item[7])
                    elif(item[6] == 'KeyDown'):
                        pyautogui.keyDown(item[7])
                    elif(item[6] == 'KeyUp'):
                        pyautogui.keyUp(item[7])
                    else:
                        print('somethings wrong (keyboard)')

                elif(item[2] == 'Hotkey'):
                    spl = item[8].split('+')
                    if(len(spl) == 2):
                        pyautogui.hotkey(spl[0], spl[1])
                    elif(len(spl) == 3):
                        pyautogui.hotkey(spl[0], spl[1], spl[2])
                    else:
                        print('somethings wrong (hotkey)')

                elif(item[2] == 'WriteText'):
                    # pyautogui.write(item[9])  #it doesn't work in 한글
                    keyboard.write(item[9])
                else:
                    print('somethings wrong 1')

                if(thd1_bool == False):
                    break
                else:
                    time.sleep(item[10])

            else:
                print('somethings wrong 2')

        if(thd1_bool == False): break

    thd1_bool = False
    allWidgetsActDeact()
    targetCrossActDeact(True)
    start_btn.config(bg='#F0F0F0')
    print('thread done!')

#targetpoint(+) show or hide
def targetShowHide():
    if(len(target_win_arr) == 0):
        pass
    else:
        if(show_hide_chk_var.get()):
            for idx, item in enumerate(target_win_arr):
                if(select_func_combo_arr[idx].get() == 'Mouse'):    #if select function is Mouse,
                    item.deiconify()     #show target window
                else:
                    pass
        else:
            for item in target_win_arr:
                item.withdraw()

#disable comboboxes not necessary
def actDeactWidgets(event):
    t_cnt = len(target_win_arr)
    for idx in range(0, t_cnt):
        if(select_func_combo_arr[idx].get() == 'Mouse'):
            act_chk_btn_arr         [idx].config(state='normal')
            select_func_combo_arr   [idx].config(state='readonly')
            mouse_func_combo_arr    [idx].config(state='readonly')
            key_func_combo_arr      [idx].config(state='disabled')
            key_combo_arr           [idx].config(state='disabled')
            hotkey_combo_arr        [idx].config(state='disabled')
            text_entry_arr          [idx].config(state='readonly')
            delay_entry_arr         [idx].config(stat='normal')
            target_win_arr          [idx].deiconify()     #show target window
        elif(select_func_combo_arr[idx].get() == 'Keyboard'):
            act_chk_btn_arr         [idx].config(state='normal')
            select_func_combo_arr   [idx].config(state='readonly')
            mouse_func_combo_arr    [idx].config(state='disabled')
            key_func_combo_arr      [idx].config(state='readonly')
            key_combo_arr           [idx].config(state='readonly')
            hotkey_combo_arr        [idx].config(state='disabled')
            text_entry_arr          [idx].config(state='readonly')
            delay_entry_arr         [idx].config(stat='normal')
            target_win_arr          [idx].withdraw()     #hide target window
        elif(select_func_combo_arr[idx].get() == 'Hotkey'):
            act_chk_btn_arr         [idx].config(state='normal')
            select_func_combo_arr   [idx].config(state='readonly')
            mouse_func_combo_arr    [idx].config(state='disabled')
            key_func_combo_arr      [idx].config(state='disabled')
            key_combo_arr           [idx].config(state='disabled')
            hotkey_combo_arr        [idx].config(state='readonly')
            text_entry_arr          [idx].config(state='readonly')
            delay_entry_arr         [idx].config(stat='normal')
            target_win_arr          [idx].withdraw()     #hide target window
        elif(select_func_combo_arr[idx].get() == 'WriteText'):
            act_chk_btn_arr         [idx].config(state='normal')
            select_func_combo_arr   [idx].config(state='readonly')
            mouse_func_combo_arr    [idx].config(state='disabled')
            key_func_combo_arr      [idx].config(state='disabled')
            key_combo_arr           [idx].config(state='disabled')
            hotkey_combo_arr        [idx].config(state='disabled')
            text_entry_arr          [idx].config(state='normal')
            delay_entry_arr         [idx].config(stat='normal')
            target_win_arr          [idx].withdraw()     #hide target window

#when thread is running, all of widgets are Deactivated
def allWidgetsActDeact():
    if(thd1_bool):
        init_delay_entry            .config(state='readonly')
        loop_entry                  .config(state='readonly')
        t_cnt = len(target_win_arr)
        for idx in range(0, t_cnt):
            act_chk_btn_arr         [idx].config(state='disabled')
            select_func_combo_arr   [idx].config(state='disabled')
            mouse_func_combo_arr    [idx].config(state='disabled')
            key_func_combo_arr      [idx].config(state='disabled')
            key_combo_arr           [idx].config(state='disabled')
            hotkey_combo_arr        [idx].config(state='disabled')
            text_entry_arr          [idx].config(state='readonly')
            delay_entry_arr         [idx].config(stat='readonly')
    else:
        init_delay_entry            .config(state='normal')
        loop_entry                  .config(state='normal')
        actDeactWidgets('')

#save to json file
def saveFile():
    try:
        filename = filedialog.asksaveasfilename(initialdir="/", title="Save File",
                                                filetypes=(("Json", "*.json")))
        saveCondition()
        if(len(save_condition_arr) == 0):
            messagebox.showwarning(title='Warning', message='Please add targets push the + button')
        else:
            file_data = OrderedDict()
            file_data['total_count']    = len(save_condition_arr)
            file_data['init_delay']     = init_delay_entry.get()
            file_data['loop']           = loop_entry.get()

            for idx, item in enumerate(save_condition_arr):
                file_data[str(idx)] ={
                    'num' : item[0],
                    'act_deact_chk'     : item[1],
                    'select_func'       : item[2],
                    'mouse_func'        : item[3],
                    'mouseX'            : item[4],
                    'mouseY'            : item[5],
                    'key_func'          : item[6],
                    'key_combo'         : item[7],
                    'hotkey'            : item[8],
                    'text_ent'          : item[9],
                    'delay_ent'         : int(item[10]*1000),
                    'target_win_posX'   : item[11],
                    'target_win_posY'   : item[12]
                    }
            with open(filename+".json", 'w', encoding='utf-8') as make_file:
                json.dump(file_data, make_file, ensure_ascii=False, indent='\t')
                messagebox.showinfo(title='Success', message='File saved successfully')
    except Exception as e:
        messagebox.showwarning(title='Warning', message=e)
        print('file save error :', e)

#load json file
def loadFile():
    try:
        filename = filedialog.askopenfilename(initialdir="/", title="Select file",
                                                filetypes=(("Json", "*.json"),
                                                ("all files", "*.*")))
        with open(filename, 'r', encoding='utf-8') as load_file:
            load_data = json.load(load_file)

        total_count = load_data["total_count"]
        init_delay  = load_data["init_delay"]
        loop        = load_data["loop"]
        init_delay_entry.delete(0, tk.END)
        init_delay_entry.insert(0, init_delay)
        loop_entry      .delete(0, tk.END)
        loop_entry      .insert(0, loop)

        t_cnt = len(target_win_arr)
        for idx in range(0, t_cnt):  delTarget()

        for idx in range(0, total_count):
            #set act_deact chk
            if(load_data[str(idx)]["act_deact_chk"]):
                act_deact=1
            else:
                act_deact=0

            #set select_func
            for idx_sel, item in enumerate(select_func_list):
                if(item == load_data[str(idx)]["select_func"]):
                    select_func = idx_sel

            #set mouse_func
            for idx_sel, item in enumerate(mouse_func_list):
                if(item == load_data[str(idx)]["mouse_func"]):
                    mouse_func = idx_sel

            #set key_func
            for idx_sel, item in enumerate(key_func_list):
                if(item == load_data[str(idx)]["key_func"]):
                    key_func = idx_sel

            # set key_combo
            key_combo=0
            for idx_sel, item in enumerate(key_list):
                if(item == load_data[str(idx)]["key_combo"]):
                    key_combo = idx_sel

            #set hotkey
            for idx_sel, item in enumerate(hotkey_list):
                if(item == load_data[str(idx)]["hotkey"]):
                    hotkey = idx_sel

            text_ent    = load_data[str(idx)]["text_ent"]
            delay_ent   = load_data[str(idx)]["delay_ent"]
            target_win_posX = load_data[str(idx)]["target_win_posX"]
            target_win_posY = load_data[str(idx)]["target_win_posY"]

            makeTarget(act_deact, select_func, mouse_func, key_func, key_combo, hotkey, text_ent, delay_ent, target_win_posX, target_win_posY)
        messagebox.showinfo(title='Success', message='File load successfully')

    except Exception as e:
        messagebox.showwarning(title='Warning', message=e)
        print('file save error :', e)

#################################  UI using tkinter
####set main_frame
main_frame = tk.Frame(window)
main_frame.pack(fill='both', expand=1)

####set canvas for y_scroll
canvas = tk.Canvas(main_frame)
canvas.pack(side='left', fill='both', expand=1)

####set scrollbar
scrollbar=ttk.Scrollbar(main_frame, orient='vertical', command=canvas.yview)
scrollbar.pack(side='right', fill='y')

#mouse wheel func
def on_MouseWheel(event):
    # block wheel func when scroll is not available
    if canvas.yview() == (0.0, 1.0):
        return
    canvas.yview_scroll(int(-1*event.delta/120), "units")

####set mouse wheel
canvas.configure(yscrollcommand=scrollbar.set)
window.bind('<MouseWheel>', on_MouseWheel)

####set second_frame on the canvas
second_frame = tk.Frame(canvas)
canvas.create_window((0,0), window=second_frame, anchor='nw')

####set_init
set_init_lbframe    = tk.LabelFrame(second_frame, text='Set Initial values')
init_delay_lb       = tk.Label(set_init_lbframe, justify='right', text='Init delay(ms):')
init_delay_entry    = tk.Entry(set_init_lbframe, justify='right', width=8)
loop_lb             = tk.Label(set_init_lbframe, justify='right', text='Loop:', anchor='e')
loop_entry          = tk.Entry(set_init_lbframe, justify='right', width=8)
####set_init GRID
set_init_lbframe    .grid(row=0, column=0, padx=(5,0), pady=(5,0), sticky='news')
init_delay_lb       .grid(row=0, column=0, padx=(5,0), pady=(5,0))
init_delay_entry    .grid(row=0, column=1, padx=(5,5), pady=(5,0))
init_delay_entry    .insert(0,'1000')
loop_lb             .grid(row=1, column=0, padx=(5,0), pady=(5,5), sticky='ew')
loop_entry          .grid(row=1, column=1, padx=(5,5), pady=(5,5))
loop_entry          .insert(0,'1')

####add_target_lbframe
add_target_lbframe  = tk.LabelFrame(second_frame, text='Add/Del Targets')
add_target_btn      = tk.Button(add_target_lbframe, text='+', width=3, height=1, font=font_15_bold, command=makeTarget) #command=lambda: command_args(arg1, arg2, arg3)
del_target_btn      = tk.Button(add_target_lbframe, text='-', width=3, height=1, font=font_15_bold, command=delTarget) #command=lambda: command_args(arg1, arg2, arg3)
####add_target_lbframe GRID
add_target_lbframe  .grid(row=0, column=1, sticky='news', padx=(5,0), pady=(5,0))
add_target_btn      .grid(row=0, column=0, sticky='news', padx=(5,0), pady=(5,0))
del_target_btn      .grid(row=0, column=1, sticky='news', padx=(5,5), pady=(5,0))

####Start_lbframe
start_lbframe       = tk.LabelFrame(second_frame, text='Start')
start_btn           = tk.Button(start_lbframe, text='▶', width=3, height=1, font=font_15_bold, command=threadStart)
####Start_lbframe GRID
start_lbframe       .grid(row=0, column=2, sticky='news', padx=(5,0), pady=(5,0))
start_btn           .grid(row=0, column=0, sticky='news', padx=(5,5), pady=(5,0))

####Stop_lbframe
stop_lbframe        = tk.LabelFrame(second_frame, text='Stop')
stop_lb1            = tk.Label(stop_lbframe, text='Push ''▶'' button OR', anchor='w')
stop_lb2            = tk.Label(stop_lbframe, text='Ctrl+Shift+Space', anchor='w')
####Stop_lbframe GRID
stop_lbframe        .grid(row=0, column=3, sticky='news', padx=(5,0), pady=(5,0))
stop_lb1            .grid(row=0, column=0, sticky='news', padx=(5,0), pady=(5,0))
stop_lb2            .grid(row=1, column=0, sticky='news', padx=(5,0), pady=(0,0))

####progress_lbframe
progress_lbframe    =tk.LabelFrame(second_frame, text='Progress')
prog_loop_lb1       =tk.Label(progress_lbframe, text='Loop :', anchor='e')
prog_loop_lb2       =tk.Label(progress_lbframe, text='000/000')
prog_element_lb1    =tk.Label(progress_lbframe, text='Element :', anchor='e')
prog_element_lb2    =tk.Label(progress_lbframe, text='000/000')
####progress_lbframe GRID
progress_lbframe    .grid(row=0, column=4, sticky='news', padx=(5,0), pady=(5,0))
prog_loop_lb1       .grid(row=0, column=0, sticky='news', padx=(5,0), pady=(5,0))
prog_loop_lb2       .grid(row=0, column=1, sticky='news', padx=(5,0), pady=(5,0))
prog_element_lb1    .grid(row=1, column=0, sticky='news', padx=(5,0), pady=(5,0))
prog_element_lb2    .grid(row=1, column=1, sticky='news', padx=(5,0), pady=(5,0))

####save_load_lbframe
save_load_lbframe   =tk.LabelFrame(second_frame, text='Save/Load')
save_btn            =tk.Button(save_load_lbframe, text='Save', width=6, height=2, command=saveFile)
load_btn            =tk.Button(save_load_lbframe, text='Load', width=6, height=2, command=loadFile)
####save_load_lbframe GRID
save_load_lbframe   .grid(row=0, column=5, sticky='news', padx=(5,0), pady=(5,0))
save_btn            .grid(row=0, column=0, sticky='news', padx=(5,0), pady=(5,0))
load_btn            .grid(row=0, column=1, sticky='news', padx=(5,5), pady=(5,0))

####show_hide_lbframe
show_hide_lbframe   =tk.LabelFrame(second_frame, text='Show/Hide')
show_hide_chk_var   =tk.IntVar()
show_hide_chk_btn   =ttk.Checkbutton(show_hide_lbframe, text='Show/Hide', variable=show_hide_chk_var, command=targetShowHide)
####show_hide_lbframe GRID
show_hide_lbframe   .grid(row=0, column=6, sticky='news', padx=(5,0), pady=(5,0))
show_hide_chk_btn   .grid(row=0, column=0, sticky='news', padx=(5,0), pady=(5,0))
show_hide_chk_var   .set(1)

####set_target_lbframe
set_target_lbframe  = tk.LabelFrame(second_frame, text='Set Targets')
#Act(chk_btn), No. , Select Func.(Mouse, Keyboard, Hotkey, Write Text), Mouse Func.(L-click, L-Down, L-Up, R-click, R-Down, R-Up, Double, Move, Drag), key Func.(Press, KeyDown, KeyUp), Key('a', 'b',...), Hotkey(Ctrl+A, Ctrl+C, Ctrl+V), Text, Delay
#it will execute just what you Act checked
#when you select Mouse, then M-Function menu will be activated
no_lb               = tk.Label(set_target_lbframe, anchor='center', text='No.')
act_lb              = tk.Label(set_target_lbframe, anchor='center', text='Act')
select_func_lb      = tk.Label(set_target_lbframe, anchor='center', width=12, text='Select Func.')
mouse_func_lb       = tk.Label(set_target_lbframe, anchor='center', width=12, text='Mouse Func.')
key_func_lb         = tk.Label(set_target_lbframe, anchor='center', width=12, text='Key Func')
key_lb              = tk.Label(set_target_lbframe, anchor='center', width=12, text='Key')
hotkey_lb           = tk.Label(set_target_lbframe, anchor='center', width=12, text='Hotkey')
text_lb             = tk.Label(set_target_lbframe, anchor='center', width=20, text='Text')
delay_lb            = tk.Label(set_target_lbframe, anchor='center', width=8, text='Delay(ms)')
#seperators
sep_v1              = ttk.Separator(set_target_lbframe, orient="vertical")
sep_v2              = ttk.Separator(set_target_lbframe, orient="vertical")
sep_v3              = ttk.Separator(set_target_lbframe, orient="vertical")
sep_v4              = ttk.Separator(set_target_lbframe, orient="vertical")
sep_v5              = ttk.Separator(set_target_lbframe, orient="vertical")
sep_v6              = ttk.Separator(set_target_lbframe, orient="vertical")
sep_v7              = ttk.Separator(set_target_lbframe, orient="vertical")
sep_v8              = ttk.Separator(set_target_lbframe, orient="vertical")
sep_h1              = ttk.Separator(set_target_lbframe, orient="horizontal")
####set_target_lbframe GRID
set_target_lbframe  .grid(row=1, column=0, padx=(5,0), pady=(5,0), sticky='news', rowspan=999, columnspan=20)
no_lb               .grid(row=0, column=0, padx=(5,5), pady=(5,0))
sep_v1              .grid(row=0, column=1, sticky='ns', rowspan=999)
act_lb              .grid(row=0, column=2, padx=(5,5), pady=(5,0))
sep_v2              .grid(row=0, column=3, sticky='ns', rowspan=999)
select_func_lb      .grid(row=0, column=4, padx=(5,5), pady=(5,0))
sep_v3              .grid(row=0, column=5, sticky='ns', rowspan=999)
mouse_func_lb       .grid(row=0, column=6, padx=(5,5), pady=(5,0))
sep_v4              .grid(row=0, column=7, sticky='ns', rowspan=999)
key_func_lb         .grid(row=0, column=8, padx=(5,5), pady=(5,0))
sep_v5              .grid(row=0, column=9, sticky='ns', rowspan=999)
key_lb              .grid(row=0, column=10, padx=(5,5), pady=(5,0))
sep_v6              .grid(row=0, column=11, sticky='ns', rowspan=999)
hotkey_lb           .grid(row=0, column=12, padx=(5,5), pady=(5,0))
sep_v7              .grid(row=0, column=13, sticky='ns', rowspan=999)
text_lb             .grid(row=0, column=14, padx=(5,5), pady=(5,0))
sep_v8              .grid(row=0, column=15, sticky='ns', rowspan=999)
delay_lb            .grid(row=0, column=16, padx=(5,5), pady=(5,0))
sep_h1              .grid(row=1, column=0, sticky='ew', columnspan=17)

#only digit input
def onlyNumbers(event):
    txt = event.widget.get()
    txt = re.sub(r"[^0-9]", "", txt)
    event.widget.delete(0,'end')
    event.widget.insert(0, txt)

#only digit input
init_delay_entry.bind('<KeyRelease>', onlyNumbers)
loop_entry      .bind('<KeyRelease>', onlyNumbers)

#thread stop hotkey
keyboard.add_hotkey('ctrl+shift+space', threadStop)

# 종료시 호출
def on_closing():
    global thd1_bool
    if messagebox.askokcancel("Quit", "Do you want to quit?"):
        thd1_bool = False
        window.destroy()

window.protocol("WM_DELETE_WINDOW", on_closing)

window.mainloop()


import os
import shutil
import glob
import re

shutil.rmtree("haha")
shutil.copytree("haha", "nop")
shutil.move()

os.listdir(".")
os.path.exists(".")
os.path.getsize(".")
os.path.isfile(".")
os.path.isdir(".")

os.mkdir("hello")
os.open() #filename flag
os.remove()
os.removedirs()
os.rename()
os.rmdir()

os.system("notepad")

for m in os.listdir("."):
	newm = re.sub(r'\d+', lambda x: str(int(x.group(0)) * 2), m)
	os.rename(m, newm)

mm = os.listdir(".")
mm.sort(key= lambda x: int(re.search("\d+").group(0)))


import os

from ament_index_python.packages import get_package_share_directory

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from nav2_common.launch import RewrittenYaml

def generate_launch_description():

    map_name = "dmro_sim_map.yaml"
    # goal file to save goal poses to.
    save_goal_file = "goal_test.json"
    # goal file to load poses from
    load_goal_file = "goal_test.json"
    autostart = True
    use_sim_time = False

    map_yaml_file = os.path.join(
        get_package_share_directory("warehousebot_navigation"), "maps", map_name
    )
    params_file = os.path.join(
        get_package_share_directory("goal_plotter"), "param", "map_server_config.yaml"
    )
    rviz_config_dir = os.path.join(
        get_package_share_directory("goal_plotter"), "rviz", "default_plot.rviz"
    )

    save_file_path = os.path.join(
        get_package_share_directory("goal_plotter"), "goal_json", save_goal_file
    )

    load_file_path = os.path.join(
        get_package_share_directory("goal_plotter"), "goal_json", load_goal_file
    )

    param_substitutions = {"yaml_filename": map_yaml_file}

    configured_params = RewrittenYaml(
        source_file=params_file,
        root_key="",
        param_rewrites=param_substitutions,
        convert_types=True,
    )

    run_map_server = Node(
        package="nav2_map_server",
        executable="map_server",
        name="map_server",
        output="screen",
        parameters=[configured_params],
    )

    run_lifecycle_manager = Node(
        package="nav2_lifecycle_manager",
        executable="lifecycle_manager",
        name="lifecycle_manager_localization",
        output="screen",
        parameters=[{"node_names": ["map_server"]}, {"autostart": autostart}],
    )

    # run rviz2 with settings
    run_rviz2 = Node(
        package="rviz2",
        executable="rviz2",
        name="rviz2",
        arguments=["-d", rviz_config_dir],
        parameters=[{"use_sim_time": use_sim_time}],
        output="screen",
    )

    run_main_plot = Node(
        package="goal_plotter",
        executable="main_plot",
        name="main_plot",
        parameters=[
            {"save_file_path": save_file_path},
            {"load_file_path": load_file_path},
        ],
        prefix=["xterm -e gdb -ex run --args"],
        output="screen",
    )

    run_get_goal = Node(
        package="goal_plotter",
        executable="get_goal",
        name="get_goal",
        output="screen",
    )

    ld = LaunchDescription()
    ld.add_action(run_lifecycle_manager)
    ld.add_action(run_map_server)
    ld.add_action(run_rviz2)
    ld.add_action(run_main_plot)
    ld.add_action(run_get_goal)

    return ld


import re
import sys
from subprocess import check_output

from typing import Optional

def exit_with_error(message: str) -> None:
    print(message)
    sys.exit(1)

def run_command(command: str) -> str:
    return check_output(command.split()).decode('utf-8').strip()

def current_git_branch_name() -> str:
    return run_command('git symbolic-ref --short HEAD')

def extract_jira_issue_key(message: str) -> Optional[str]:
    project_key, issue_number = r'[A-Z]{2,}', r'[0-9]+'
    match = re.search(f'{project_key}-{issue_number}', message)
    return match and match.group(0)

def main():
    # Verify that the branch name is a Jira issue key.
    git_branch_name = current_git_branch_name()
    if git_branch_name == 'master':
        print('You are not allowed to commit/push to the master branch!')
        sys.exit(1)
    jira_issue_key = extract_jira_issue_key(git_branch_name)
    if not jira_issue_key:
        exit_with_error(
            f'Commit aborted! To continue, please rename your branch to a Jira '
            f'issue key with:\n$ git branch -m {git_branch_name} <KEY-123>')

    # Read the commit message.
    commit_msg_filepath = sys.argv[1]
    with open(commit_msg_filepath, 'r') as f:
        commit_msg = f.read()

    # Split the commit into a subject and body and apply some light formatting.
    commit_elements = commit_msg.split('\n', maxsplit=1)
    commit_subject = commit_elements[0].strip()
    commit_subject = commit_subject.capitalize()
    commit_subject = re.sub(r'\.+$', '', commit_subject)
    commit_body = commit_elements[1].strip()

    # Build the new commit message:

    # 1. Add the time worked to the Work Log in the commit body.
    if '#time' not in commit_msg:
        exit_with_error(f'You forgot to register the hours you worked on this commit.'
                        f'Use the #time command from JIRA smart commits to do so.')
    # 2. Make sure the subject starts with a Jira :issue key.
    if not extract_jira_issue_key(commit_subject):
        commit_subject = f'{jira_issue_key} {commit_subject}'
    # Assemble the commit message as the subject plus body.
    commit_msg = f'{commit_subject}\n\n{commit_body}'
    # 3. Override commit message.
    with open(commit_msg_filepath, 'w') as f:
        f.write(commit_msg)

if __name__ == '__main__':
    main()


import pytest

from dbt.cli.main import dbtRunner
from dbt.contracts.graph.manifest import Manifest
from dbt.exceptions import ParsingError
from dbt.tests.util import run_dbt, get_manifest

from tests.functional.metrics.fixtures import (
    mock_purchase_data_csv,
    models_people_sql,
    models_people_metrics_yml,
    invalid_models_people_metrics_yml,
    invalid_metrics_missing_model_yml,
    invalid_metrics_missing_expression_yml,
    names_with_spaces_metrics_yml,
    names_with_special_chars_metrics_yml,
    names_with_leading_numeric_metrics_yml,
    long_name_metrics_yml,
    downstream_model_sql,
    invalid_derived_metric_contains_model_yml,
    derived_metric_yml,
    invalid_metric_without_timestamp_with_time_grains_yml,
    invalid_metric_without_timestamp_with_window_yml,
    metricflow_time_spine_sql,
    semantic_model_people_yml,
    semantic_model_purchasing_yml,
    purchasing_model_sql,
)

class TestSimpleMetrics:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": models_people_metrics_yml,
            "metricflow_time_spine.sql": metricflow_time_spine_sql,
            "semantic_model_people.yml": semantic_model_people_yml,
            "people.sql": models_people_sql,
        }

    def test_simple_metric(
        self,
        project,
    ):
        runner = dbtRunner()
        result = runner.invoke(["parse"])
        assert result.success
        assert isinstance(result.result, Manifest)
        manifest = get_manifest(project.project_root)
        metric_ids = list(manifest.metrics.keys())
        expected_metric_ids = [
            "metric.test.number_of_people",
            "metric.test.collective_tenure",
            "metric.test.collective_window",
            "metric.test.average_tenure",
            "metric.test.average_tenure_minus_people",
        ]
        assert metric_ids == expected_metric_ids

        assert (
            len(manifest.metrics["metric.test.number_of_people"].type_params.input_measures) == 1
        )
        assert (
            len(manifest.metrics["metric.test.collective_tenure"].type_params.input_measures) == 1
        )
        assert (
            len(manifest.metrics["metric.test.collective_window"].type_params.input_measures) == 1
        )
        assert len(manifest.metrics["metric.test.average_tenure"].type_params.input_measures) == 2
        assert (
            len(
                manifest.metrics[
                    "metric.test.average_tenure_minus_people"
                ].type_params.input_measures
            )
            == 3
        )

class TestInvalidRefMetrics:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": invalid_models_people_metrics_yml,
            "people.sql": models_people_sql,
        }

    # tests that we get a ParsingError with an invalid model ref, where
    # the model name does not have quotes
    def test_simple_metric(
        self,
        project,
    ):
        # initial run
        with pytest.raises(ParsingError):
            run_dbt(["run"])

class TestInvalidMetricMissingModel:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": invalid_metrics_missing_model_yml,
            "people.sql": models_people_sql,
        }

    # tests that we get a ParsingError with an invalid model ref, where
    # the model name does not have quotes
    def test_simple_metric(
        self,
        project,
    ):
        # initial run
        with pytest.raises(ParsingError):
            run_dbt(["run"])

class TestInvalidMetricMissingExpression:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": invalid_metrics_missing_expression_yml,
            "people.sql": models_people_sql,
        }

    # tests that we get a ParsingError with a missing expression
    def test_simple_metric(
        self,
        project,
    ):
        # initial run
        with pytest.raises(ParsingError):
            run_dbt(["run"])

class TestNamesWithSpaces:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": names_with_spaces_metrics_yml,
            "people.sql": models_people_sql,
        }

    def test_names_with_spaces(self, project):
        with pytest.raises(ParsingError) as exc:
            run_dbt(["run"])
        assert "cannot contain spaces" in str(exc.value)

class TestNamesWithSpecialChar:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": names_with_special_chars_metrics_yml,
            "people.sql": models_people_sql,
        }

    def test_names_with_special_char(self, project):
        with pytest.raises(ParsingError) as exc:
            run_dbt(["run"])
        assert "must contain only letters, numbers and underscores" in str(exc.value)

class TestNamesWithLeandingNumber:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": names_with_leading_numeric_metrics_yml,
            "people.sql": models_people_sql,
        }

    def test_names_with_leading_number(self, project):
        with pytest.raises(ParsingError) as exc:
            run_dbt(["run"])
        assert "must begin with a letter" in str(exc.value)

class TestLongName:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": long_name_metrics_yml,
            "people.sql": models_people_sql,
        }

    def test_long_name(self, project):
        with pytest.raises(ParsingError) as exc:
            run_dbt(["run"])
        assert "cannot contain more than 250 characters" in str(exc.value)

class TestInvalidDerivedMetrics:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "derived_metric.yml": invalid_derived_metric_contains_model_yml,
            "downstream_model.sql": downstream_model_sql,
        }

    def test_invalid_derived_metrics(self, project):
        with pytest.raises(ParsingError):
            run_dbt(["run"])

class TestMetricDependsOn:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people.sql": models_people_sql,
            "metricflow_time_spine.sql": metricflow_time_spine_sql,
            "semantic_models.yml": semantic_model_people_yml,
            "people_metrics.yml": models_people_metrics_yml,
        }

    def test_metric_depends_on(self, project):
        manifest = run_dbt(["parse"])
        assert isinstance(manifest, Manifest)

        expected_depends_on_for_number_of_people = ["semantic_model.test.semantic_people"]
        expected_depends_on_for_average_tenure = [
            "metric.test.collective_tenure",
            "metric.test.number_of_people",
        ]

        number_of_people_metric = manifest.metrics["metric.test.number_of_people"]
        assert number_of_people_metric.depends_on.nodes == expected_depends_on_for_number_of_people

        average_tenure_metric = manifest.metrics["metric.test.average_tenure"]
        assert average_tenure_metric.depends_on.nodes == expected_depends_on_for_average_tenure

class TestDerivedMetric:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "downstream_model.sql": downstream_model_sql,
            "purchasing.sql": purchasing_model_sql,
            "metricflow_time_spine.sql": metricflow_time_spine_sql,
            "semantic_models.yml": semantic_model_purchasing_yml,
            "derived_metric.yml": derived_metric_yml,
        }

    # not strictly necessary to use "real" mock data for this test
    # we just want to make sure that the 'metric' calls match our expectations
    # but this sort of thing is possible, to have actual data flow through and validate results
    @pytest.fixture(scope="class")
    def seeds(self):
        return {
            "mock_purchase_data.csv": mock_purchase_data_csv,
        }

    def test_derived_metric(
        self,
        project,
    ):
        # initial parse
        results = run_dbt(["parse"])

        # make sure all the metrics are in the manifest
        manifest = get_manifest(project.project_root)
        metric_ids = list(manifest.metrics.keys())
        expected_metric_ids = [
            "metric.test.count_orders",
            "metric.test.sum_order_revenue",
            "metric.test.average_order_value",
        ]
        assert metric_ids == expected_metric_ids

        # make sure the downstream_model depends on these metrics
        metric_names = ["average_order_value", "count_orders", "sum_order_revenue"]
        downstream_model = manifest.nodes["model.test.downstream_model"]
        assert sorted(downstream_model.metrics) == [[metric_name] for metric_name in metric_names]
        assert sorted(downstream_model.depends_on.nodes) == [
            "metric.test.average_order_value",
            "metric.test.count_orders",
            "metric.test.sum_order_revenue",
        ]
        assert sorted(downstream_model.config["metric_names"]) == metric_names

        # make sure the 'expression' metric depends on the two upstream metrics
        derived_metric = manifest.metrics["metric.test.average_order_value"]
        assert sorted(derived_metric.depends_on.nodes) == [
            "metric.test.count_orders",
            "metric.test.sum_order_revenue",
        ]

        # actually compile
        results = run_dbt(["compile", "--select", "downstream_model"])
        compiled_code = results[0].node.compiled_code

        # make sure all these metrics properties show up in compiled SQL
        for metric_name in manifest.metrics:
            parsed_metric_node = manifest.metrics[metric_name]
            for property in [
                "name",
                "label",
                "type",
                "type_params",
                "filter",
            ]:
                expected_value = getattr(parsed_metric_node, property)
                assert f"{property}: {expected_value}" in compiled_code

class TestInvalidTimestampTimeGrainsMetrics:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": invalid_metric_without_timestamp_with_time_grains_yml,
            "people.sql": models_people_sql,
        }

    # Tests that we get a ParsingError with an invalid metric definition.
    # This metric definition is missing timestamp but HAS a time_grains property
    def test_simple_metric(
        self,
        project,
    ):
        # initial run
        with pytest.raises(ParsingError):
            run_dbt(["run"])

class TestInvalidTimestampWindowMetrics:
    @pytest.fixture(scope="class")
    def models(self):
        return {
            "people_metrics.yml": invalid_metric_without_timestamp_with_window_yml,
            "people.sql": models_people_sql,
        }

    # Tests that we get a ParsingError with an invalid metric definition.
    # This metric definition is missing timestamp but HAS a window property
    def test_simple_metric(
        self,
        project,
    ):
        # initial run
        with pytest.raises(ParsingError):
            run_dbt(["run"])


from urllib.error import ContentTooShortError
import torch
from tqdm import tqdm
import torch.nn.functional as F
from model import CLIPModel

def get_model(path, device):
    model = CLIPModel(config)
    model.load_state_dict(torch.load(path, map_location=device))
    model.eval()
    return model

def numpy_to_tensor(numpy_array, dim):
    tmp = []
    for tensor in numpy_array:
        tmp.append(tensor)
    return torch.stack(tmp, dim=dim)

def dict_to_tensor(indices, dict, trained_model, content_type, clip_version='clip-ViT-L-14'):
    collection = []
    for id in tqdm(indices):
        try:
            emb = torch.from_numpy(dict['data'][id][clip_version])
            emb = torch.unsqueeze(emb, 0)
            if trained_model:
                if content_type == 'text':
                    emb = trained_model.text_projection_head(emb)
                elif content_type == 'image':
                    emb = trained_model.image_projection_head(emb)
                else:
                    raise NotImplementedError
            collection.append(emb)
        except Exception as e:
            print('Problem with ', id)
            print('Exception: ', e)
            break
    # final_tensor = numpy_to_tensor(collection, dim=dim)
    final_tensor = torch.stack(collection, dim=1)
    final_tensor = torch.squeeze(final_tensor, dim=0)
    print('Final tensor shape:', final_tensor.shape)
    return final_tensor

def dict_to_list(indices, dict, trained_model, content_type, clip_version='clip-ViT-L-14'):
    list = []
    for id in tqdm(indices):
        try:
            emb = torch.from_numpy(dict['data'][id][clip_version])
            emb = torch.unsqueeze(emb, 0)
            if trained_model:
                if content_type == 'text':
                    emb = trained_model.text_projection_head(emb)
                elif content_type == 'image':
                    emb = trained_model.image_projection_head(emb)
                else:
                    raise NotImplementedError
            list.append(emb)
        except Exception as e:
            print('Problem with ', id)
            print('Exception: ', e)
    assert len(indices) == len(list)
    print(f'Got a list of {len(list)} embeddings')
    return list

def get_top_k_indices(query_emb, index, target_embeddings, k=5, normalized=True):
    """
    Given a query embedding, an index, a tensor of target embeddings, and a k
    Retrieve indices of top-k items in target embeddings
    """
    if normalized:
        # normalize query and image embeddings
        query_embedding_n = F.normalize(query_emb, p=2, dim=-1)
        target_embeddings_n = F.normalize(target_embeddings, p=2, dim=-1)
    else:
        query_embedding_n = query_emb
        target_embeddings_n = target_embeddings

    # find top-k image embeddings
    dot_similarity = query_embedding_n @ target_embeddings_n.T
    values, indices = torch.topk(dot_similarity.squeeze(0), k)

    # map matches to indices
    matches = [index[idx] for idx in indices]

    return matches, values

def get_top_k_matches_for_queries(indices, queries_embs, target_tensor, k, normalized):
    retrieved_results = []
    for id, query_emb in tqdm(zip(indices, queries_embs)):
        matches, values = get_top_k_indices(
            query_emb=query_emb,
            index=indices,
            k=k,
            normalized=normalized,
            target_embeddings=target_tensor
        )
        retrieved_results.append(matches)
    assert len(indices) == len(queries_embs) == len(target_tensor)
    return retrieved_results

def recall_at_k(target, predictions, k=5, total_in_collection=1):
    if len(predictions) < k:
        print(f'Watch out, len(predictions) < k: {len(predictions)} < {k}')
    recall_at_k = predictions[:k].count(target) / total_in_collection
    return 100 * recall_at_k

def get_recalls(target, predictions, k_list=[1,5,10,50,100]):
    recalls = []
    for k in k_list:
        tmp_recall = recall_at_k(
            target=target,
            predictions=predictions,
            k=k,
            total_in_collection=1
            )
        recalls.append(tmp_recall)
    return recalls

def iterate_over_results_to_compute_recalls(ids, results, k_list=[1, 5, 10, 50, 100]):
    query_ids = []
    top_k_results = []
    recalls_at_1 = []
    recalls_at_5 = []
    recalls_at_10 = []
    recalls_at_50 = []
    recalls_at_100 = []
    for id, result in tqdm(zip(ids, results)):
        r_at_1, r_at_5, r_at_10, r_at_50, r_at_100 = get_recalls(
            id,
            result,
            k_list=k_list
            )
        query_ids.append(id)
        top_k_results.append(result)
        recalls_at_1.append(r_at_1)
        recalls_at_5.append(r_at_5)
        recalls_at_10.append(r_at_10)
        recalls_at_50.append(r_at_50)
        recalls_at_100.append(r_at_100)
    return query_ids, top_k_results, recalls_at_1, recalls_at_5, recalls_at_10, recalls_at_50, recalls_at_100

def get_mrr(target, predictions):
    if target in predictions:
        index = predictions.index(target) + 1
        mrr = 1 / index
        return mrr
    else:
        return 0.0


#!/usr/bin/env python
import os
from tempfile import TemporaryDirectory
from subprocess import check_output, check_call

from sys import argv, stderr, exit
import hashlib

def local_test():
    out = check_output([test_bin + "/testLocal", test_bw])
    md5sum = hashlib.md5(out).hexdigest()
    assert md5sum == "1c52065211fdc44eea45751a9cbfffe0"

def remote_http_test():
    if not os.path.exists(test_bin + "/testRemote"):
        print("libBigWig was compiled without CURL. Skipping test with testRemote!", file=stderr)
        return

    out = check_output([test_bin + "/testRemote",
                        "http://hgdownload.cse.ucsc.edu/goldenPath/hg19/encodeDCC/wgEncodeMapability/wgEncodeCrgMapabilityAlign50mer.bigWig"])
    md5sum = hashlib.md5(out).hexdigest()
    assert md5sum == "9ccecd6c32ff31042714c1da3c0d0eba"

def test_recreating_file():
    with TemporaryDirectory(prefix="libbigwig-test") as tmpdir:
        tmpout = os.path.join(tmpdir, "output.bw")
        p1 = check_call([test_bin + "/testWrite", test_bw, tmpout])
        assert p1 == 0
        with open(tmpout, mode="rb") as f:
            md5sum = hashlib.md5(f.read()).hexdigest()
            assert md5sum == "8e116bd114ffd2eb625011d451329c03"

def test_creation_from_scratch():
    with TemporaryDirectory(prefix="libbigwig-test") as tmpdir:
        tmpout = os.path.join(tmpdir, "test", "example_output.bw")
        os.mkdir(os.path.dirname(tmpout))
        p1 = check_call([test_bin + "/exampleWrite"], cwd=tmpdir)
        assert p1 == 0
        with open(tmpout, mode="rb") as f:
            md5sum = hashlib.md5(f.read()).hexdigest()
            assert md5sum == "ef104f198c6ce8310acc149d0377fc16"

def remote_test2():
    ## Ensure that we can properly parse chromosome trees with non-leaf nodes
    # The UCSC FTP site is timing out for OSX!
    if not os.path.exists(test_bin + "/testRemoteManyContigs"):
        print("libBigWig was compiled without CURL. Skipping test with testRemoteManyContigs!", file=stderr)
        return

    out = check_output([test_bin + "/testRemoteManyContigs",
                        "http://hgdownload.cse.ucsc.edu/gbdb/dm6/bbi/gc5BaseBw/gc5Base.bw"])

    md5sum = hashlib.md5(out).hexdigest()
    assert md5sum == "a15a3120c03ba44a81b025ebd411966c"

def test_bigbed():
    if not os.path.exists(test_bin + "/testBigBed"):
        print("libBigWig was compiled without CURL. Skipping test with testBigBed!", file=stderr)
        return

    out = check_output([test_bin + "/testBigBed",
                        "https://www.encodeproject.org/files/ENCFF001JBR/@@download/ENCFF001JBR.bigBed"])

    md5sum = hashlib.md5(out).hexdigest()
    assert md5sum == "33ef99571bdaa8c9130149e99332b17b"

if __name__ == "__main__":
    if len(argv) != 3:
        print("Usage: {} path/to/test/bin path/to/test.bw".format(argv[0]), file=stderr)
        print("Example: {} build/test/ test/test.bw".format(argv[0]), file=stderr)
        exit(1)

    test_bin = os.path.abspath(argv[1])
    test_bw = os.path.abspath(argv[2])

    local_test()
    remote_http_test()
    test_recreating_file()
    test_creation_from_scratch()
    remote_test2()
    test_bigbed()

    print("success", file=stderr)


import numpy as np
from scipy import signal,stats

from helpers import parabolic_interpolation
from is_silent import is_silent
from harmonic_change import harmonic_change_detector

def eckf_pitch_modified(y, fs, blockSize, c, numBufToWait, npeaks, nsemitones=2):

#y - incoming noisy signal (row vector)
#fs - sampling rate
#f0 - estimated pitch
#c - parameter for determining process noise
#numBufToWait - number of buffers to wait after note onset - this will vary
#from instrument to instrument, depending on how strong its attack is
#nsemitones - number of semitones of pitch change to occur for a new note
#to be detected
#amp - estimated amplitude of fundamental
#phi - estimated phase of fundamental
#x_est - estimated fundamental component of signal

    nframes = int(np.ceil((y.size)/blockSize))
    padding = np.zeros((nframes*blockSize - y.size))

    y = np.concatenate((y, padding))

    blocks = 1
    onset_pos = []

    Ts = 1/fs
    H =[0,0.5,0.5]
    H = np.array(H).reshape(len(H), 1)

    K = np.zeros((3,1))
    flag = -1
    Kthres = 0.01
    x_est = np.zeros(len(y))
    amp = np.zeros(len(y))
    phase = np.zeros(len(y))
    Q = np.zeros(len(y))
    f0 = np.zeros(len(y))

    P_track = np.zeros(len(y))
    K_track = np.zeros(len(y))
    n = 0
    start_pos = 0
#     %state of current frame - initially silent
    silent_cur = 1
    harm_prev = 0
    spf = []

    hcd = harmonic_change_detector()

    while(blocks <= nframes):

        end_pos = start_pos + blockSize - 1
        if(end_pos >= max(np.shape(y))):
            break
        y_frame = y[start_pos:start_pos + blockSize]

        silent_prev = silent_cur
        silent_cur, cur_spf = is_silent(y_frame)

        spf = np.matrix([[spf],[cur_spf]])
    #     %if current frame is silent, then continue
        if(silent_cur == 1):
            flag = 0
            start_pos = start_pos+blockSize
            n = start_pos
            continue
        else:

            if (start_pos > blockSize-1):
               y_prev = y[start_pos-blockSize:start_pos]
            else:
                y_prev = []

            harm_cur,_,_,_ = hcd(y_prev,y_frame,fs,npeaks,nsemitones)

            if ((silent_prev == 1 and silent_cur == 0) or (harm_prev == 0 and harm_cur == 1)):
                onset_pos = [onset_pos, [start_pos]]
               # onset_pos = [onset_pos, start_pos]
                count = 0

                while(count < numBufToWait):
                    count = count+1
                    start_pos = start_pos+blockSize

                if(start_pos + blockSize  < y.shape[0]):
                    y_frame = y[start_pos:start_pos+blockSize]
                    if(n>0):
                        f0[n:start_pos] = f0[n-1]
                        amp[n:start_pos] = amp[n-1]
                        phase[n:start_pos] = phase[n-1]
                    flag = 1
                    n = start_pos
                else:
                    break

        if(flag == 1):
            if (start_pos > blockSize-1):
                y_prev = y[start_pos-blockSize:start_pos]
            else:
                y_prev = []

            harm_cur, f1, a1, phi1 = hcd(y_prev.T,y_frame.T,fs,npeaks,nsemitones)
            # print('{}   {}  {}'.format(f1,a1,phi1))
            x0 = [0+0j,0+0j,0+0j]
            x0[0] = np.exp(1j*2*np.pi*f1*Ts)
            x0[1] = a1*np.exp(1j*2*np.pi*f1*n*Ts + 1j*phi1)
            x0[2] = a1*np.exp(-1j*2*np.pi*f1*n*Ts - 1j*phi1)
            x0 = np.array(x0).reshape(3, 1)

            P0 = np.zeros((3,3))

            if(abs(min(K)) < Kthres):
                # print('Covariance matrix reset at time {} seconds'.format((n-1)/fs))
                P_last = P0
                x_last = x0
                flag = 0
                start_pos = start_pos - count*blockSize
                n = start_pos

        k = 0
        while (k < blockSize-1):
            K = (np.dot(P_last, H))/(np.dot(H.reshape(1, len(H)), np.dot(P_last, H).reshape(3, 1))[0][0] + 1)
            P = P_last - np.matmul(np.dot(K, H.reshape(1, len(H))), P_last)
            x = x_last + K*(y_frame[k] - np.dot(H.reshape(1, len(H)), x_last))
            x_next = np.array([x[0][0],x[0][0]*x[1][0],x[2][0]/x[0][0]]).reshape(3, 1)
            F = [[1,0,0],[x[1][0],x[0][0],0],[-x[2][0]/(x[0][0]**2),0,1/x[0][0]]]
            Q[n] = 10**-(c-np.abs(y_frame[k] - np.dot(H.reshape(1, len(H)), x)[0][0]))
            P_next = F*P*np.array(F).T + Q[n]*np.eye(3)

            # with open('ppospy.txt', 'a') as f:
            #         f.write('{}+{}i\n'.format(x[0][0].real,x[0][0].imag))

            ####### WRONG VALUE to temp
            temp = np.log(x[0][0])/(1j*Ts*2*np.pi)
            f0[n] = np.abs(temp)

            amp[n] = np.abs(x[1])
            phase[n] = np.abs(-1j * (np.log(x[1]/amp[n])-(2*np.pi*f0[n]*Ts*n)))
           # x_est[n] = np.dot(H.reshape(1, len(H)), x)[0][0]
            x_est[n] = np.abs(np.vdot(H,x))
            P_last = P_next
            x_last = x_next
            P_track[n] = np.linalg.norm(P)
            K_track[n] = np.linalg.norm(K)
            n = n + 1
            k = k + 1

        start_pos = start_pos + blockSize
        harm_prev = harm_cur

    return f0,amp,phase,x_est,onset_pos


import random
import cv2
import numpy as np
import torch

class Compose:
    def __init__(self, transforms):
        self.transforms = transforms

    def __call__(self, image, targets):
        for transform in self.transforms:
            image, targets = transform(image, targets)
        return image, targets

class FlipColorChannelOrder:
    def __call__(self, image, targets=None):
        image = image[:, :, ::-1]
        return image, targets

class Normalize:
    def __call__(self, image, targets=None):
        image = image.astype(np.float32) / 255
        return image, targets

class NormalizeTargets:
    def __call__(self, image, targets=None):
        size = max(image.shape[:2])
        if targets is not None:
            targets[:, :4] /= size
        return image, targets

class Pad:
    def __init__(self, random_padding):
        self.random_padding = random_padding

    def __call__(self, image, targets=None):
        h, w = image.shape[:2]
        offset = self._compute_offset(np.array([w, h]))
        left, top = offset
        out_size = max(w, h)
        new_image = np.ones((out_size, out_size, 3), dtype=np.uint8) * 127
        new_image[top:top + h, left:left + w] = image
        if targets is not None:
            targets[:, :2] += offset
        return new_image, targets

    def _compute_offset(self, image_size):
        out_size = max(image_size)
        w, h = image_size
        if self.random_padding:
            dx = np.random.randint(out_size - w + 1)
            dy = np.random.randint(out_size - h + 1)
        else:
            dx = (out_size - w) // 2
            dy = (out_size - h) // 2
        return np.array([dx, dy])

class RandomDistort:
    def __init__(self, hue, saturation, exposure):
        self.hue = hue
        self.saturation = saturation
        self.exposure = exposure

    def __call__(self, image, targets=None):
        image = self._randomly_distort(image)
        return image, targets

    @staticmethod
    def _compute_random_scale(max_scale):
        scale = np.random.uniform(1, max_scale)
        return scale if random.random() < 0.5 else 1 / scale

    def _randomly_distort(self, image):
        diff_hue = np.random.uniform(-self.hue, self.hue)
        sat_scale = self._compute_random_scale(self.saturation)
        exp_scale = self._compute_random_scale(self.exposure)

        hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
        hsv = hsv.astype(np.float32)
        hsv[:, :, 0] /= 179
        hsv[:, :, 1:] /= 255
        hue = hsv[:, :, 0] + diff_hue
        hsv[:, :, 1] *= sat_scale
        hsv[:, :, 2] *= exp_scale
        if diff_hue > 0:
            hue[hue > 1] -= 1
        else:
            hue[hue < 0] += 1
        hsv[:, :, 0] = np.round((hue * 179).clip(0, 179))
        hsv[:, :, 1:] = np.round((hsv[:, :, 1:] * 255).clip(0, 255))
        hsv = hsv.astype(np.uint8)
        image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
        return image

class RandomHorizontalFlip:
    def __call__(self, image, targets=None):
        if random.random() < 0.5:
            image = image[:, ::-1]
            if targets is not None:
                targets[:, 0] = 1 - targets[:, 0]
        return image, targets

class Resize:
    def __init__(self, out_size, random_aspect_ratio_jitter):
        self.out_size = out_size
        self.jitter = random_aspect_ratio_jitter

    def __call__(self, image, targets=None):
        h, w = image.shape[:2]
        org_size = np.array([w, h])
        new_size = self.jitter_and_scale_size(org_size)
        image = cv2.resize(image, tuple(new_size))
        if targets is not None:
            scale = new_size / org_size
            targets[:, :4] *= np.concatenate([scale, scale])
        return image, targets

    def jitter_and_scale_size(self, image_size):
        w, h = self._jitter_size(image_size, self.jitter)
        new_size = self._scale_size_based_on_longer_edge(self.out_size, w / h)
        return new_size

    @staticmethod
    def _jitter_size(image_size, jitter):
        max_dw, max_dh = image_size * jitter
        dw = np.random.uniform(-max_dw, max_dw)
        dh = np.random.uniform(-max_dh, max_dh)
        return image_size + np.array([dw, dh])

    @staticmethod
    def _scale_size_based_on_longer_edge(out_size, new_aspect_ratio):
        if new_aspect_ratio < 1:
            new_h = out_size
            new_w = new_h * new_aspect_ratio
        else:
            new_w = out_size
            new_h = new_w / new_aspect_ratio
        return np.array([new_w, new_h]).astype(np.int)

class ToFixedSizeTargets:
    def __init__(self, max_targets):
        self.max_targets = max_targets

    def __call__(self, image, targets=None):
        padded_targets = np.zeros((self.max_targets, 5))
        if targets is not None:
            targets = targets[:self.max_targets]
            padded_targets[:len(targets)] = targets
        return image, padded_targets

class ToTensor:
    def __call__(self, image, targets):
        image = torch.from_numpy(image.transpose(2, 0, 1)).float()
        if targets is not None:
            targets = torch.from_numpy(targets).float()
        return image, targets

class ToYOLOTargets:
    def __call__(self, image, targets):
        """Convert COCO annotation to target

        Args:
            image (np.ndarray): image
            targets (dict): COCO annotation
                bounding box is represented as (x0, y0, w, h) where
                    (x0, y0): coordinates of top-left corner of it
                    (w, h): size

        Returns:
            (tuple): (image, targets)
                image (np.ndarray): image
                targets (np.ndarray or None): (shape: Nx5)
                    each row is (bbox, category_index) where
                        bbox: (shape: 4) (xc, yc, w, h) where
                            (xc, yc): coordinates of center of the
                                bounding box
                            (w, h): size of the bounding box
                        category_index: category index in the list
                            of categories considered

        """
        converted_targets = []
        for annotation in targets:
            bbox = np.asarray(annotation['bbox'])
            bbox[:2] = bbox[:2] + bbox[2:] / 2
            converted_targets.append([*bbox, annotation['category_index']])
        if len(converted_targets) > 0:
            converted_targets = np.vstack(converted_targets)
        else:
            converted_targets = None
        return image, converted_targets


from kafka import KafkaConsumer
import json
from pymongo import MongoClient
from bson.objectid import ObjectId
from op_handler import OpHandler
from config import DB,MONGO_URI,TOPICS,KAFKA_URL
import time

from elasticsearch_wrapper import create_doc,delete_doc,update_doc
from mongo_aggregations import projects_aggregation,users_aggregation,discussions_aggregation,tasks_aggregation

UPDATE = 'u'
DELETE = 'd'
CREATE = 'c'
READ = 'r' # initial sync

ops={
'c':'create',
'd':'delete',
'u':'update'
}

"""
returns tuple (op,collection,Id)
"""
def get_params_from_message(msg):
    value_payload = msg.value['payload']
    # print("////////////////////msg:////////////////")
    # print(msg)
    # print("////////////////////payload:////////////////")
    # print(value_payload)
    if value_payload:
        op = value_payload['op']
        key = json.loads(msg.key)
        topic = msg.topic
        collection = topic[(topic.rfind('.')+1):]
        id = None
        if op==UPDATE:
            # print(key)
            #id = json.loads(key['payload']['_id'])['_id']['$oid']#azure version
            id = json.loads(key['payload']['id'])['$oid']
            #check if deleted/recycled by root
            op = DELETE if 'recycled' in json.loads(value_payload['patch'])['$set'].keys() else UPDATE
            # print('updated op///////////////////////////')
            # print(op)
        elif op == READ:
            op = UPDATE #update/create
            print("in READ////////////////// Debug:")
            payload = key['payload']
            print("payload:")
            print(payload)
            print("id://///////////////////////////////////////////")
            id = json.loads(payload['id'])['$oid']
            print(id)
            print("value_payload://////////////////////////////////")
            print(value_payload)
            after = json.loads(value_payload['after'])
            print("DEbug after.keys():///////////")
            print(after.keys())
            if 'recycled' in json.loads(value_payload['after']).keys():
                return 'junk'
            #TODO: check if deleted has patched
            #check if deleted/recycled by root TODO: check where the recycled is when its read
            #op = DELETE if 'recycled' in json.loads(value_payload['patch'])['$set'].keys() else UPDATE

        else:
            # print(key)
            #id = key['payload']['_id'] #azure version
            id = json.loads(key['payload']['id'])['$oid']
        return (ops[op],collection,id)
    else:
        return 'junk'

def opHandler(op_collection_id_tuple):
    # m = globals()['OpHandler']()
    # func_name = "{0}_{1}".format(op_collection_id_tuple[0].lower(),op_collection_id_tuple[1].lower())
    # func = getattr(m, func_name)
    # return func(op_collection_id_tuple[2])
    func = OpHandler.execute((op_collection_id_tuple[0].lower(),op_collection_id_tuple[1].lower()))
    print("1////////////////////")
    print(func)
    return func(op_collection_id_tuple[2])

def fix(e):
    #check if kafka fell -> take last seriallized msg and run fix from mongo by querying all objects in all collections created after the timestamp from objectid
    #else just try and execute flow on last serialized msg until succesful
    #after a succesfull fix return to reading messages from kafka and executing flow
    pass

def main():
    # counter = 0
    print('Welcome!')
    consumer = KafkaConsumer(*TOPICS,value_deserializer=lambda m: json.loads(m.decode('utf-8')) if m is not None else False,bootstrap_servers=[KAFKA_URL], group_id='my_favorite_group',auto_offset_reset='earliest',enable_auto_commit=False)
    for msg in consumer:
        print("this is a message yo, read the message:")
        print(msg)
        # TODO:
        #serialize message
        if msg and msg.value:
            op_collection_userId_tuple = get_params_from_message(msg)
            print(op_collection_userId_tuple)
            print('\n')
            # counter = counter + 1
            # if counter > 3:
            #     raise Exception('This is the exception you expect to handle')
            if(op_collection_userId_tuple!='junk'):
                opHandler(op_collection_userId_tuple)
            consumer.commit() # commits the current msg(offset/partition), on restart will take this msg

if __name__ == "__main__":
    main()
    # while True:
    #     try:
    #         main()
    #     except  Exception as e:
    #         print("Runtime Error:")
    #         print(e)
    #         # time.sleep(5) ?
    #         #while true fix serialized message
    #         while True:
    #             try:
    #                 fix(e) #run flow with last serialized msg
    #             except Exception as e2:
    #                 print("Runtime Fix Error:")
    #                 print(e2)
    #                 continue
    #             break # on success return to read messages from kafka

            # while True retry last serialized message before continuing
            # need to check if we can get the creation date of the object from the mongo object id
            # and then maybe try and rerun all everything from mongo created after 1 hour before the last object


from flask import Flask, render_template, request, session, url_for, redirect
import pymysql.cursors
import datetime
from pyecharts.charts import Bar
from pyecharts import options as opts

from appdef import *

def authenticateCustomer():
    username = ""
    try:
        #could be that there is no user, make sure
        username = session['username']
    except:
        return False

    cursor = conn.cursor()
    query = 'select * from customer where email=%s'
    cursor.execute(query, (username))
    data = cursor.fetchall()
    cursor.close()
    if data:
        return True
    else:
        #Logout before returning error message
        session.pop('username')
        return False

@app.route('/customerHome')
def customerHome():
  if not authenticateCustomer():
    error = 'Invalid Credentials'
    return redirect(url_for('errorpage', error=error))
  else:
    username = session['username']
    cursor = conn.cursor()
    query = 'SELECT purchases.ticket_id, ticket.airline_name, ticket.flight_num, departure_airport, departure_time, arrival_airport, arrival_time \
    FROM purchases, ticket, flight \
    WHERE purchases.ticket_id = ticket.ticket_id \
    AND ticket.airline_name = flight.airline_name \
    AND ticket.flight_num = flight.flight_num \
    AND customer_email = %s AND departure_time > curdate()'
    cursor.execute(query, (username))
    data = cursor.fetchall()
    cursor.close()
    return render_template('customer.html', username=username, posts=data)

@app.route('/viewUpcomingFlights', methods=['GET', 'POST'])
def viewUpcomingFlights():
  if authenticateCustomer():
    username = session['username']
    cursor = conn.cursor()
    if request.method == 'POST':
      city = request.form.get('citysearchbox', False)
      if city:
        query='SELECT purchases.ticket_id, ticket.airline_name, ticket.flight_num, departure_airport, departure_time, arrival_airport, arrival_time \
                FROM purchases, ticket, flight, airport \
                WHERE purchases.ticket_id = ticket.ticket_id \
                AND ticket.airline_name = flight.airline_name \
                AND ticket.flight_num = flight.flight_num \
                AND (flight.arrival_airport = airport_name or flight.departure_airport = airport_name)\
                AND airport.airport_city = %s\
                AND customer_email = %s AND departure_time > curdate();'
        cursor.execute(query, (city, username))
        data = cursor.fetchall()
        cursor.close()
        return render_template('viewUpcomingFlights.html', username=username, posts=data)

      airport = request.form.get('airportsearchbox', False)
      ##########################################################################
      error = request.args.get('error')
      if len(airport)>3:
        error = "Please enter the abbreviation of airport name."
        return render_template('viewUpcomingFlights.html',error = error)
      cursor = conn.cursor()
      query1 = "select * from airport where airport_name = %s"
      cursor.execute(query1,airport)
      data1 = cursor.fetchall()
      cursor.close()
      if not data1:
        error = " The airport doesn't exist."
        return render_template('viewUpcomingFlights.html',error = error)
      ##########################################################################
      if airport:
        query='SELECT purchases.ticket_id, ticket.airline_name, ticket.flight_num, departure_airport, departure_time, arrival_airport, arrival_time \
                FROM purchases, ticket, flight\
                WHERE purchases.ticket_id = ticket.ticket_id \
                AND ticket.airline_name = flight.airline_name \
                AND ticket.flight_num = flight.flight_num \
                AND (flight.arrival_airport = %s or flight.departure_airport = %s)\
                AND customer_email = %s AND departure_time > curdate();'
        cursor.execute(query, (airport, airport, username))
        data = cursor.fetchall()
        cursor.close()
        return render_template('viewUpcomingFlights.html', username=username, posts=data)

      begintime = request.form["begintime"]
      endtime = request.form["endtime"]

      if datetime.datetime.strptime(begintime, "%Y-%m-%d") > datetime.datetime.strptime(endtime, "%Y-%m-%d"):
        return render_template('viewUpcomingFlights.html', username=username, error="The dates you entered are invalid.")
      else:
        query="SELECT distinct purchases.ticket_id, ticket.airline_name, ticket.flight_num, departure_airport, departure_time, arrival_airport, arrival_time \
              FROM purchases, ticket, flight\
              WHERE purchases.ticket_id = ticket.ticket_id \
              AND ticket.airline_name = flight.airline_name \
              AND ticket.flight_num = flight.flight_num \
              AND purchases.customer_email = %s \
              AND flight.departure_time > curdate()\
              and ((date(flight.departure_time) between %s and %s) or (date(flight.arrival_time) between %s and %s));"
        cursor.execute(query, (username, begintime, endtime, begintime, endtime))
        data = cursor.fetchall()
        cursor.close()
        return render_template('viewUpcomingFlights.html', username=username, posts=data)

    query = 'SELECT purchases.ticket_id, ticket.airline_name, ticket.flight_num, departure_airport, departure_time, arrival_airport, arrival_time \
            FROM purchases, ticket, flight \
            WHERE purchases.ticket_id = ticket.ticket_id \
            AND ticket.airline_name = flight.airline_name \
            AND ticket.flight_num = flight.flight_num \
            AND customer_email = %s AND departure_time > curdate();'
    cursor.execute(query, (username))
    data = cursor.fetchall()
    cursor.close()
    return render_template('viewUpcomingFlights.html', username=username, posts=data)
  else:
    error = 'Invalid Credentials'
    return redirect(url_for('errorpage', error=error))

@app.route('/searchPageCustomer')
def searchPageCustomer():
  if authenticateCustomer():
    return render_template('searchCustomer.html')
  else:
    error = 'Invalid Credentials'
    return redirect(url_for('errorpage', error=error))

@app.route('/searchCustomer', methods=['POST'])
def searchCustomer():
  if authenticateCustomer():
    username = session['username']
    cursor = conn.cursor()
    fromcity = request.form['fromcity']
    fromairport = request.form['fromairport']
    fromdate = request.form['fromdate']
    tocity = request.form['tocity']
    toairport = request.form['toairport']
    todate = request.form['todate']
    ##############################################################
    if datetime.datetime.strptime(fromdate, "%Y-%m-%d") > datetime.datetime.strptime(todate, "%Y-%m-%d"):
        return render_template('searchCustomer.html', error="The dates you entered are invalid.")
    ##############################################################
    query = 'SELECT * FROM flight, airport, purchases, ticket \
            WHERE airport.airport_name=flight.departure_airport \
            AND flight.flight_num = ticket.flight_num AND flight.airline_name = ticket.airline_name\
            AND ticket.ticket_id = purchases.ticket_id\
            AND purchases.customer_email = %s\
            AND airport.airport_city = %s \
            AND airport.airport_name = %s \
            # AND flight.status = "Upcoming"\
            AND %s BETWEEN DATE_SUB(flight.departure_time, INTERVAL 2 DAY) AND DATE_ADD(flight.departure_time, INTERVAL 2 DAY) \
            AND %s BETWEEN DATE_SUB(flight.arrival_time, INTERVAL 2 DAY) AND DATE_ADD(flight.arrival_time, INTERVAL 2 DAY) \
            AND (flight.airline_name, flight.flight_num) in \
              (SELECT flight.airline_name, flight.flight_num FROM flight, airport \
              WHERE airport.airport_name=flight.arrival_airport \
              AND airport.airport_city = %s \
              AND airport.airport_name = %s)'
    cursor.execute(query, (username, fromcity, fromairport, fromdate, todate, tocity, toairport))
    data = cursor.fetchall()
    cursor.close()
    error = None
    if(data):
      return render_template('searchCustomer.html', username=username, results=data)
    else:
      #returns an error message to the html page
      error = 'No results found'
      return render_template('searchCustomer.html', error=error)
  else:
    error = 'Invalid Credentials'
    return redirect(url_for('errorpage', error=error))

@app.route('/trackMySpending', methods=['GET', 'POST'])
def trackMySpending():
  if not authenticateCustomer():
    error = 'Invalid Credentials'
    return redirect(url_for('errorpage', error=error))
  else:
    username = session['username']
    if request.method == 'POST':
          to_date = request.form['to_date']
          from_date = request.form['from_date']
          if datetime.datetime.strptime(from_date, "%Y-%m-%d") > datetime.datetime.strptime(to_date, "%Y-%m-%d"):
              return render_template('viewCustomerSpending.html', error="The dates you entered are invalid.")
          to_date_format = datetime.datetime.strptime(to_date, '%Y-%m-%d')
          from_date_format = datetime.datetime.strptime(from_date, '%Y-%m-%d')
          year = to_date_format.year
          month = to_date_format.month
          date = to_date_format.day
          from_year = from_date_format.year
          from_month = from_date_format.month
          from_date_date = from_date_format.day
          from_date_string = '{}-{}-{}'.format(from_year, from_month, from_date_date)
          to_date_string = '{}-{}-{}'.format(year, month, date)
          monthnum = (year - from_year)*12 + month - from_month
    else:
          to_date = datetime.datetime.now()
          year = to_date.year
          month = to_date.month
          if month-6 < 0:
            from_month = 12 + (month-6)
            from_year = to_date.year - 1
          else:
            from_month = month-6
            from_year = year
          date = to_date.day
          monthnum = 5
          string = '{} 1 {} 00:00'.format(month, from_year)
          from_date = datetime.datetime.strptime(string, '%m %d %Y %H:%M')
          from_date_string = '{}-{}-{}'.format(from_year, from_month, date)
          to_date_string = '{}-{}-{}'.format(year, month, date)

    cursor = conn.cursor()
    sumPrice = 'SELECT COALESCE(sum(flight.price),0) as sum\
              FROM purchases,ticket,flight\
              WHERE purchases.customer_email = %s\
              AND ticket.ticket_id = purchases.ticket_id\
              and ticket.flight_num = flight.flight_num\
              and ticket.airline_name = flight.airline_name\
              and purchases.purchase_date >= %s\
              and purchases.purchase_date <= %s;'
    cursor.execute(sumPrice, (username, from_date_string, to_date_string ))
    sumSpending = float(cursor.fetchone()['sum'])

    if month < 12:
        string = '{} 1 {} 00:00'.format(month+1, year + 1)
    else:
        string = '{} 1 {} 00:00'.format(1, year + 2)
    temp_date = datetime.datetime.strptime(string, '%m %d %Y %H:%M')

    labels = []
    values = []
    temp_year = year
    temp_month = month
    for i in range(0,monthnum + 1):
        this_date = temp_date
        this_month = temp_month
        temp_month = (month-i)%12
        if temp_month == 0:
            temp_month = 12
        if temp_month > this_month:
            temp_year = temp_year - 1
        string = '{} 1 {} 00:00'.format(temp_month, temp_year)
        temp_date = datetime.datetime.strptime(string, '%m %d %Y %H:%M')
        query = 'SELECT COALESCE(sum(flight.price),0) as sum_month\
              FROM purchases,ticket,flight\
              WHERE purchases.customer_email = %s\
              AND ticket.ticket_id = purchases.ticket_id\
              and ticket.flight_num = flight.flight_num\
              and ticket.airline_name = flight.airline_name\
              and purchases.purchase_date > %s\
              and purchases.purchase_date < %s;'
        cursor.execute(query, (username, temp_date, this_date, ))
        data = cursor.fetchone()
        label = '{}-{}'.format(temp_year, temp_month)
        labels.append(label)
        values.append(float(data['sum_month']))
    cursor.close()
    labels.reverse()
    values.reverse()
    try:
        mymax = max(values)
    except:
        mymax = 100
    return render_template('viewCustomerSpending.html', username=username, sumSpending = sumSpending, max = mymax, from_date = from_date_string, to_date = to_date_string, labels=labels, values=values)


from django.db import models

from zgw_consumers.models import ServiceUrlField

# set up for ServiceUrl test
class Case(models.Model):
    _casetype_api = models.ForeignKey(
        "zgw_consumers.Service", on_delete=models.SET_NULL, null=True, blank=True
    )
    _casetype_relative = models.CharField(max_length=200, blank=True, null=True)
    casetype = ServiceUrlField(
        base_field="_casetype_api",
        relative_field="_casetype_relative",
        blank=True,
        null=True,
    )

class ZgwConfig(models.Model):
    zaaktype = models.URLField(
        "zaaktype",
        max_length=1000,
        blank=True,
    )


from datetime import date, datetime

from jinja2 import Markup
from wtforms.widgets.core import html_params

from pycroft.helpers import utc
from web.template_filters import date_filter, datetime_filter

class Column:
    """A class representing a bootstrap-table column

    This class bundles all the attributes commonly needed to represent
    a table column as used in a bootstrap-table.  It defines a method
    :py:meth:`build_col_args` for building the args needed in the
    corresponding ```` element for the table header.

    For documentation concerning bootstrap-tables in general, see
    http://bootstrap-table.wenzhixin.net.cn/documentation/

    :param name: The name of the column.  This must coincide with the
        key given in the JSON endpoint's response, e.g. if this
        column's name is ``user_id``, the response JSON has to be of
        the form ``{"items": [{"user_id": value, …}, …]}``.
    :param title: The title to be displayed in the column header.
    :param formatter: An optional formatter to use.  The referenced
        value must be available as a javascript function for the
        column's value parsing to work properly.  See the
        ``data-formatter`` attribute in the bootstrap-table docs for
        details.
    :param width: An optional width, being translated to the
        ``col-sm-{width}`` bootstrap class.
    :param cell_style: Similar to :param:`formatter`, an optional
        javascript function's name to be passed to
        ``data-cell-style``.
    """
    def __init__(self, name, title, formatter=None, width=0, cell_style=None):
        self.name = name
        self.title = title
        self.formatter = formatter if formatter is not None else False
        self.cell_style = cell_style if cell_style is not None else False
        self.width = width

    def __repr__(self):
        return "<{cls} {name!r} title={title!r}>".format(
            cls=type(self).__name__,
            name=self.name,
            title=self.title,
        )

    def build_col_args(self, **kwargs):
        """Build th html-style attribute string for this column.

        This string can be used to add this column to a table in the
        header: ``"	".format(col.build_col_args())``.
        Attributes are utilized as described in :meth:`__init__`.

        :param kwargs: Keyword arguments which are merged into the
            dict of html attributes.
        """
        html_args = {
            'class': "col-sm-{}".format(self.width) if self.width else False,
            'data-sortable': "true",
            'data-field': self.name,
            'data-formatter': self.formatter,
            'data-cell-style': self.cell_style,
        }
        html_args.update(kwargs)
        return html_params(**html_args)

    def render(self):
        """Render this column as a ```` html tag.

        This uses the arguments provided by :meth:`build_col_args` and
        the :attr:`title` for the inner HTML.
        """
        return "{}	".format(self.build_col_args(), self.title)

    __str__ = render
    __html__ = render

class BootstrapTable:
    """An extendable, HTML-renderable bootstrap-table

    The table's HTML can be rendered using :meth:`render`.  NB:
    :meth:`__str__` and :meth:`__html__` are NOT provided, since
    :meth:`render` expects an obligatory `table_id`!

    :param columns: A list of :py:cls:`Column` objects defining the
        columns.
    :param data_url: The URL to be used as a JSON endpoint.  The JSON
        endpoint must provide the table data in the scheme defaulting
        to ``{"rows": [], "total": …}``.  In this instance, a custom
        response handler is used, which accesses the table contents on
        the sub-key ``items``: ``{"items": {"rows": …, "total": …}}``.
        The endpoint should also support the parameters limit, offset,
        search, sort, order to make server-side pagination work.
    :param table_args: Additional things to be passed to table_args.
    """
    def __init__(self, columns, data_url, table_args=None):
        self.columns = columns
        self.data_url = data_url
        self.table_args = table_args if table_args is not None else {}
        self._init_table_args()

    def __repr__(self):
        return "<{cls} cols={numcols} data_url={data_url!r}>".format(
            cls=type(self).__name__,
            numcols=len(self.columns),
            data_url=self.data_url,
        )

    def _init_table_args(self):
        """Set the defaults of :py:attr:`table_args`"""
        default_args = {
            'data-toggle': "table",
            'data-url': self.data_url,
        }
        for key, val in default_args.items():
            self.table_args.setdefault(key, val)

    def generate_table_header(self):
        """Generate the table header from :py:attr:`columns`.

        :rtype: generator
        """
        yield "
"
        yield "
"
        for col in self.columns:
            yield str(col)
        yield "
"
        yield "
"

    @staticmethod
    def generate_toolbar():
        """Return an empty iterator.

        Used to generate the inner HTML contents of the toolbar.

        :rtype: iterator
        """
        return iter(())

    @staticmethod
    def generate_table_footer():
        """Return an empty iterator.

        Used to generate the outer HTML contents of the footer.  Must
        yield the ``
`` tag as well.

        :rtype: iterator
        """
        return iter(())

    def render(self, table_id):
        """Render the table
        """
        # NB: in html_args, setting an argument to `False` makes it
        # disappear.
        html = []

        toolbar_args = html_params(id="{}-toolbar".format(table_id),
                                   class_="btn-toolbar",
                                   role="toolbar")
        html.append("
".format(toolbar_args))
        html += list(self.generate_toolbar())
        html.append("
")

        table_args = self.table_args
        table_args.update({
            'id': table_id,
            'data-toolbar': "#{}-toolbar".format(table_id),
        })

        html.append("
".format(html_params(**table_args)))
        html += list(self.generate_table_header())
        html += list(self.generate_table_footer())
        html.append("
")

        return Markup("\n".join(html))

class SplittedTable(BootstrapTable):
    def __init__(self, *a, splits, **kw):
        """Initialize a new Splitted Table

        :param splits: Split definitions of the format
            (('split_1_prefix', "Display_Name"), …).  The format will
            be checked
        :param kwargs: Passed to super()
        """
        super().__init__(*a, **kw)
        # each split shall have the format:
        # ('split_prefix', "Name to be displayed")
        if any(len(x) != 2 for x in splits):
            raise ValueError("`splits` must be a tuple of 2-tuples")
        self.splits = splits

    def generate_table_header(self):
        yield "
"
        yield "
"
        for _, split_name in self.splits:
            yield ("{}	"
                   .format(len(self.columns), split_name))
        yield "
"

        yield "
"
        def prefixed_col(prefix, col_name):
            return "{prefix}_{col_name}".format(prefix=prefix, col_name=col_name)
        for split_prefix, _ in self.splits:
            for col in self.columns:
                new_name = prefixed_col(split_prefix, col.name)
                yield "{}	".format(
                    col.build_col_args(**{'data-field': new_name}),
                    col.title
                )
        yield "
"
        yield "
"

def date_format(dt, default=None):
    """
    Format date or datetime objects for `table.dateFormatter`.
    :param datetime|date|None dt: a date or datetime object or None
    :param str|None default: formatted value to use if `dt` is None
    :return:
    """
    if dt is not None:
        return {
            'formatted': date_filter(dt),
            'timestamp': int(datetime.combine(dt, utc.time_min()).timestamp()),
        }
    else:
        return {
            'formatted': default if default is not None else date_filter(None),
            'timestamp': None,
        }

def datetime_format(dt, default=None):
    """
    Format datetime objects for `table.dateFormatter`.
    :param datetime|None dt: a datetime object or None
    :param str|None default: formatted value to use if `dt` is None
    :return:
    """
    if dt is not None:
        return {
            'formatted': datetime_filter(dt),
            'timestamp': int(dt.timestamp()),
        }
    else:
        return {
            'formatted': default if default is not None else datetime_filter(None),
            'timestamp': None,
        }


from ..util import _convert_quarterly_minutes
from ..PeaqErrors import PeaqValueError

class PredictionBase:
    @staticmethod
    def predicted_energy(
            now_min: int,
            now_sec: int,
            power_avg: float,
            total_hourly_energy: float,
            is_quarterly:bool=False
    ) -> float:
        if now_min not in range(0, 60):
            raise PeaqValueError(f"Value 'now_min' ({now_min}) must be between (0..60]")
        if now_sec not in range(0, 60):
            raise PeaqValueError(f"Value 'now_max' ({now_sec}) must be between (0..60]")
        if power_avg < 0 or total_hourly_energy < 0:
            raise PeaqValueError(f"Value 'power_avg' ({power_avg}) or 'total_hourly_energy' ({total_hourly_energy}) must be greater than or equal to 0")

        minute = _convert_quarterly_minutes(now_min, is_quarterly)

        if total_hourly_energy > 0 and (minute > 0 or (minute + now_sec) > 30):
            ret = (((power_avg / 60 / 60) * (3600 - ((minute * 60) + now_sec)) + total_hourly_energy * 1000) / 1000)
        else:
            ret = power_avg / 1000
        return round(ret, 3)

    @staticmethod
    def predicted_percentage_of_peak(
            peak: float,
            predicted_energy: float
    ) -> float:
        if peak == 0.0 or peak is None:
            return 0
        elif predicted_energy == 0.0 or predicted_energy is None:
            return 0
        return round((predicted_energy / peak) * 100, 2)