import cv2
import numpy as np
import copy
from PIL import Image

from assemble_puzzle_classes import SinglePuzzle, Trace

puzzle_solving_thread_list = []

def blur_image(image, amount=3):
    # Blurs the image
    # Does not affect the original image
    kernel = np.ones((amount, amount), np.float32) / (amount ** 2)
    return cv2.filter2D(image, -1, kernel)

def try_to_assemble_puzzles(filename, contours):
    base_image_color = cv2.imread(filename)
    single_puzzles = []
    for con in contours:
        if cv2.contourArea(con) > 3000:
            single_puzzle = mask_image(con, filename)
            single_puzzles.append(single_puzzle)

    width, height = base_image_color.shape[:2]
    assembled_puzzle = assemble_the_puzzles(single_puzzles, width, height)
    return assembled_puzzle

def mask_image(contour, image_path):
    image = cv2.imread(image_path, -1)

    rect = cv2.minAreaRect(contour)
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    x_values = [box[0][0], box[1][0], box[2][0], box[3][0]]
    y_values = [box[0][1], box[1][1], box[2][1], box[3][1]]
    x1 = min(x_values)
    x2 = max(x_values)
    y1 = min(y_values)
    y2 = max(y_values)

    cv2.drawContours(image, [box], 0, (0, 0, 255), 2)

    crop_img = image[y1:y2, x1:x2]
    single_puzzle = find_puzzle_tabs(contour, image)
    single_puzzle.crop_image = crop_img
    single_puzzle.threshold = (y2 - y1) / 10  # 10% of puzzle height
    single_puzzle.define_puzzle_tabs()

    return single_puzzle

def find_puzzle_tabs(contour, img):
    hull = cv2.convexHull(contour, returnPoints=False)

    defects = cv2.convexityDefects(contour, hull)

    single_puzzle = SinglePuzzle()

    for i in range(defects.shape[0]):
        s, e, f, d = defects[i, 0]
        start = tuple(contour[s][0])
        end = tuple(contour[e][0])
        far = tuple(contour[f][0])
        trace = Trace()
        trace.start = start
        trace.end = end
        trace.far = far
        single_puzzle.traces.append(trace)

        cv2.line(img, start, end, [0, 255, 0], 2)
        cv2.circle(img, far, 5, [255, 0, 0], -1)
    return single_puzzle

def assemble_the_puzzles(single_puzzles, width, height):
    corners_puzzles = []
    border_puzzles_top = []
    border_puzzles_down = []
    border_puzzles_left = []
    border_puzzles_right = []
    inside_puzzles = []
    for single_puzzle in single_puzzles:
        straight_tab_number = single_puzzle.get_straight_tab_number()
        if straight_tab_number == 2:
            corners_puzzles.append(single_puzzle)
        elif straight_tab_number == 1:
            if single_puzzle.top_tab == "straight":
                border_puzzles_top.append(single_puzzle)
            elif single_puzzle.down_tab == "straight":
                border_puzzles_down.append(single_puzzle)
            elif single_puzzle.right_tab == "straight":
                border_puzzles_right.append(single_puzzle)
            elif single_puzzle.left_tab == "straight":
                border_puzzles_left.append(single_puzzle)
        elif straight_tab_number == 0:
            inside_puzzles.append(single_puzzle)
    final_puzzles_width = 2 + len(border_puzzles_top)
    final_puzzles_height = 2 + len(border_puzzles_right)
    # create array of size of puzzles(one array value is one puzzle)
    final_puzzles = [[0 for x in range(final_puzzles_width)] for y in range(final_puzzles_height)]

    for corner in corners_puzzles:
        if corner.top_tab == "straight":
            if corner.right_tab == "straight":
                final_puzzles[0][final_puzzles_width - 1] = corner
            else:
                final_puzzles[0][0] = corner
        else:
            if corner.right_tab == "straight":
                final_puzzles[final_puzzles_height - 1][final_puzzles_width - 1] = corner
            else:
                final_puzzles[final_puzzles_height - 1][0] = corner

    unfitted_puzzles = []
    unfitted_puzzles.extend(border_puzzles_top)
    unfitted_puzzles.extend(border_puzzles_down)
    unfitted_puzzles.extend(border_puzzles_left)
    unfitted_puzzles.extend(border_puzzles_right)
    unfitted_puzzles.extend(inside_puzzles)

    additional_final_puzzles = []
    x = 0
    y = 0
    while x < final_puzzles_width:
        while y < final_puzzles_height:
            if final_puzzles[y][x] == 0:
                additional_final = complete_field(final_puzzles, unfitted_puzzles, x, y, final_puzzles_width,
                                                  final_puzzles_height)
                if additional_final is not None and additional_final != "ERROR":
                    additional_final_puzzles.append(additional_final)

                if additional_final == "ERROR":
                    additional_tuple = additional_final_puzzles.pop()

                    final_puzzles = additional_tuple[0]
                    x = additional_tuple[1]
                    y = additional_tuple[2]
            y += 1
        x += 1
        y = 0

    image = Image.new("RGBA", (width + 500, height))
    actual_height = 0
    actual_width = 0
    max_width = 0
    for y in range(len(final_puzzles)):
        y_values = []
        for x in range(len(final_puzzles[y])):
            temp_image = Image.fromarray(final_puzzles[y][x].crop_image)
            image.paste(temp_image, (actual_width, actual_height))
            width, height = temp_image.size
            actual_width += width

            y_values.append(height)
        if actual_width > max_width:
            max_width = actual_width
        actual_width = 0
        actual_height += max(y_values)

    open_cv_image = np.array(image)
    trimmed_image = open_cv_image[0:actual_height, 0:max_width]
    return np.array(trimmed_image)

def complete_field(final_puzzles, unfitted_puzzles, horizontal_position, perpendicularly_position, horizontal_array_size,
                   perpendicularly_array_size):
    single_puzzle_left = "straight"
    single_puzzle_right = "straight"
    single_puzzle_top = "straight"
    single_puzzle_down = "straight"

    if horizontal_position != 0:
        single_puzzle_left = final_puzzles[perpendicularly_position][horizontal_position - 1]
    if horizontal_position != (horizontal_array_size - 1):
        single_puzzle_right = final_puzzles[perpendicularly_position][horizontal_position + 1]

    if perpendicularly_position != 0:
        single_puzzle_top = final_puzzles[perpendicularly_position - 1][horizontal_position]
    if perpendicularly_position != (perpendicularly_array_size - 1):
        single_puzzle_down = final_puzzles[perpendicularly_position + 1][horizontal_position]

    puzzles_that_can_fit = []
    for puzzle in unfitted_puzzles:

        if single_puzzle_left != "straight" and single_puzzle_right != "straight" and single_puzzle_top != "straight"\
                and single_puzzle_down != "straight":
            if puzzle.left_tab == "straight" or puzzle.right_tab == "straight" or puzzle.top_tab == "straight"\
                    or puzzle.down_tab == "straight":
                continue

        if single_puzzle_left == "straight":
            if puzzle.left_tab != "straight":
                continue
        elif single_puzzle_left != 0:
            if not (
                    single_puzzle_left.right_tab != "straight" and puzzle.left_tab != "straight"
                    and single_puzzle_left.right_tab != puzzle.left_tab):
                continue

        if single_puzzle_right == "straight":
            if puzzle.right_tab != "straight":
                continue
        elif single_puzzle_right != 0:
            if not (
                    single_puzzle_right.left_tab != "straight" and puzzle.right_tab != "straight"
                    and single_puzzle_right.left_tab != puzzle.right_tab):
                continue

        if single_puzzle_top == "straight":
            if puzzle.top_tab != "straight":
                continue
        elif single_puzzle_top != 0:
            if not (
                    single_puzzle_top.down_tab != "straight" and puzzle.top_tab != "straight"
                    and single_puzzle_top.down_tab != puzzle.top_tab):
                continue

        if single_puzzle_down == "straight":
            if puzzle.down_tab != "straight":
                continue
        elif single_puzzle_down != 0:
            if not (
                    single_puzzle_down.top_tab != "straight" and puzzle.down_tab != "straight"
                    and single_puzzle_down.top_tab != puzzle.down_tab):
                continue
        puzzles_that_can_fit.append(puzzle)

    if len(puzzles_that_can_fit) == 0:
        return "ERROR"

    final_puzzles[perpendicularly_position][horizontal_position] = puzzles_that_can_fit[0]

    if len(puzzles_that_can_fit) > 1:
        additionalArray = copy.deepcopy(final_puzzles)
        additionalArray[perpendicularly_position][horizontal_position] = puzzles_that_can_fit[1]
        additional = (additionalArray, horizontal_position, perpendicularly_position)
        return additional

    return None


import subprocess
import os
import requests

def get_gpu_meta(video_path:str, log_file):

    process = subprocess.Popen(
        [exe, video_path],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        universal_newlines=True,
    )

    for output in process.stdout:

        if ("gps" in output.strip()) or ("GPS" in output.strip()):
            print(f"{video_path}: gps exists")
            log_file.write(f"{video_path}: gps exists\n")
            global num_have_gps
            num_have_gps += 1
            break

    global cnt_checked
    cnt_checked += 1
    print(f"({cnt_checked}/{cnt_total}) {video_path}: gps does not exist")

if __name__=="__main__":

    if not os.path.exists("Image-ExifTool-12.64.tar.gz"):
        print("Donwloading exiftool...")
        exiftool_url = "https://exiftool.org/Image-ExifTool-12.64.tar.gz"
        response = requests.get(exiftool_url)
        open("Image-ExifTool-12.64.tar.gz", "wb").write(response.content)
        untar_cmd = "tar -xzf Image-ExifTool-12.64.tar.gz"
        os.system(untar_cmd)

    root = "/Users/heesukim/Downloads"
    # poc_ver = "poc_01"
    # poc_ver = "poc_02"  # no gps info at all
    # poc_ver = "poc_03"
    poc_ver = "poc_04"
    # poc_ver = "poc_10"
    # poc_ver = "poc_11"
    exe = "Image-ExifTool-12.64/exiftool"

    video_root = os.path.join(root, poc_ver)
    video_files = os.listdir(video_root)
    video_paths = [os.path.join(video_root, file) for file in video_files]

    global cnt_total, cnt_checked, num_have_gps
    cnt_total = len(video_paths)
    cnt_checked = 0
    num_have_gps = 0

    # Open the log file
    with open(f'logfile_{poc_ver}.txt', 'w') as log_file:

        for video_path in video_paths:
            get_gpu_meta(video_path, log_file)

        log_file.write(f"num_have_gps: {num_have_gps}\n")
        log_file.write("done\n")

    print("num_have_gps:", num_have_gps)
    print("done")


### \file
### \ingroup BasicExamples
### | **input (alg)**                                      | other algorithms                   |                       |                                       | **output (alg)**                                |
### | ---------------------------------------------------- | ---------------------------------- | --------------------- | ------------------------------------- | ----------------------------------------------- |
### | generate Pythia events and save them to HepMC file   | convert `HepMC::GenEvent` to EDM   | filter MC Particles   | use sample jet clustering algorithm   | write the EDM output to ROOT file using PODIO   |

from Gaudi.Configuration import *

### Example of pythia configuration file to generate events
pythiafile="Generation/data/Pythia_standard.cmd"
# Example of pythia configuration file to read LH event file
#pythiafile="options/Pythia_LHEinput.cmd"

from Configurables import FCCDataSvc
#### Data service
podioevent = FCCDataSvc("EventDataSvc")

from Configurables import PythiaInterface
### PYTHIA algorithm
pythia8gen = PythiaInterface("Pythia8Interface", Filename=pythiafile)
pythia8gen.DataOutputs.hepmc.Path = "hepmcevent"

from Configurables import HepMCConverter
### Reads an HepMC::GenEvent from the data service and writes a collection of EDM Particles
hepmc_converter = HepMCConverter("Converter")
hepmc_converter.DataInputs.hepmc.Path="hepmcevent"
hepmc_converter.DataOutputs.genparticles.Path="all_genparticles"
hepmc_converter.DataOutputs.genvertices.Path="all_genvertices"

from Configurables import GenParticleFilter
### Filters generated particles
genfilter = GenParticleFilter("StableParticles")
genfilter.DataInputs.genparticles.Path = "all_genparticles"
genfilter.DataOutputs.genparticles.Path = "genparticles"

from Configurables import JetClustering_fcc__MCParticleCollection_fcc__GenJetCollection_fcc__GenJetParticleAssociationCollection_ as JetClustering
genjet_clustering = JetClustering("GenJetClustering", verbose = False)
genjet_clustering.DataInputs.particles.Path='genparticles'
genjet_clustering.DataOutputs.jets.Path='genjets'
genjet_clustering.DataOutputs.constituents.Path='genjets_particles'

from Configurables import PodioOutput
### PODIO algorithm
out = PodioOutput("out",OutputLevel=DEBUG)
out.outputCommands = ["keep *"]

from Configurables import ApplicationMgr
ApplicationMgr( TopAlg=[ pythia8gen, hepmc_converter, genfilter, genjet_clustering, out ],
                EvtSel='NONE',
                EvtMax=100,
                ExtSvc=[podioevent],
                OutputLevel=DEBUG
)


def union(A,B):
    R=[]
    for i in range(0,len(A)):
        if(A[i]>B[i]):
            R.append(A[i])
        else:
            R.append(B[i])
    return R

def intersect(A,B):
    R=[]
    for i in range(0,len(A)):
        if(A[i]<B[i]):
            R.append(A[i])
        else:
            R.append(B[i])
    return R

def complement(A):
    R=[]
    for i in range(0,len(A)):
        V=1-A[i]
        R.append(V)
    return R

def diff(A,B):
    COM_B=complement(B)
    R=[]
    for i in range(0,len(A)):
        if(A[i]<COM_B[i]):
            R.append(A[i])
        else:
            R.append(COM_B[i])
    return R

def print_fuzzy_set(A,W):
    for i in range(0,len(A)-1):
        print(f'{round(A[i],2)}/{round(W[i],1)}',end=' + ')
    print(f'{round(A[-1],2)}/{round(W[-1],1)}')

A=list(map(float,input('Enter the values of first fuzzy set : ').strip().split()))
B=list(map(float,input('Enter the values of second fuzzy set : ').strip().split()))
W=list(map(int,input('Enter the weights of each element for the fuzzy set : ').strip().split()))
OP=float(input('1.Union\n2.Intersection\n3.Complement\n4.Difference\nEnter the operation which need to be performed from above:'))

print('The first fuzzy set is :')
print_fuzzy_set(A,W)
print('The second fuzzy set is :')
print_fuzzy_set(B,W)

if (OP==1):
    R=union(A,B)
    print('The union of first and second set is :')
    print_fuzzy_set(R,W)
elif (OP==2):
    R=intersect(A,B)
    print('The intersect of first and second set is :')
    print_fuzzy_set(R,W)
elif (OP==3):
    R=complement(A)
    print('The complement of first fuzzy set is :')
    print_fuzzy_set(R,W)
    R=complement(B)
    print('The complement of second fuzzy set is :')
    print_fuzzy_set(R,W)
elif (OP==4):
    R=diff(A,B)
    print('The differnce of first and second set is :')
    print_fuzzy_set(R,W)
else:
    print('Please select any valid number : 1,2,3,4')
    exit()


import matplotlib.pyplot as plt
import numpy as np
from pca4 import euler
from pca4 import epsilon

def euler_crom(x0,v0,ti,tf,dt):
    print("in euler_crom!")
    # time, position, and velocity arrays
    t = np.arange(ti,tf,dt)
    x = np.zeros(len(t))
    v = np.zeros(len(t))
    x[0] = x0
    v[0] = v0

    for i in range(len(t)-1):
        v[i+1] = v[i] - x[i]*dt
        x[i+1] = x[i] + v[i+1]*dt # there is a hidden (k/m) = (1/1) in front of the x[i]dt term

    return x,v,t

def runga_kutta4(x0,v0,ti,tf,dt):
    print("in runga_kutta4!")
    t = np.arange(ti,tf,dt)
    x = np.zeros(len(t))
    v = np.zeros(len(t))
    x[0] = x0
    v[0] = v0

    for i in range(len(t)-1):
        x1 = x[i]
        v1 = v[i]
        a1 = -x1

        x2 = x1 + v1*dt/2.0
        v2 = v1 + a1*dt/2.0
        a2 = -x2

        x3 = x1 + v2*dt/2.0
        v3 = v1 + a2*dt/2.0
        a3 = -x3

        x4 = x1 + v3*dt
        v4 = v1 + a3*dt
        a4 = -x4

        x[i+1] = x[i] + dt*(v1 + 2.0*v2 + 2.0*v3 + v4)/6.0
        v[i+1] = v[i] + dt*(a1 + 2.0*a2 + 2.0*a3 + a4)/6.0
    return x,v,t

def epsilon_check(x0,v0,ti,tf,dt,f):
    eps = 0
    n = 0
    while eps < 1e-4:
        print('dt = ',dt)
        sol = f(x0,v0,ti,tf,dt)
        eps = epsilon(sol[0],sol[1],sol[2])
        print("epsilon = ",eps)
        if eps < 1e-4:
            dt *=1.001

    return tf/dt

q1 = euler(0,1,0,4*np.pi,0.1*np.pi)
q2 = euler(0,1,0,4*np.pi,0.01*np.pi)
q3 = euler(0,1,0,4*np.pi,0.001*np.pi)

p1 = euler_crom(0,1,0,4*np.pi,0.1*np.pi)
p2 = euler_crom(0,1,0,4*np.pi,0.01*np.pi)
p3 = euler_crom(0,1,0,4*np.pi,0.001*np.pi)

run1 = runga_kutta4(0,1,0,4*np.pi,0.1*np.pi)
run2 = runga_kutta4(0,1,0,4*np.pi,0.01*np.pi)
run3 = runga_kutta4(0,1,0,4*np.pi,0.001*np.pi)

t = [0.1*np.pi,0.01*np.pi,0.001*np.pi]

eps_run = [epsilon(run1[0],run1[1],run1[2]), epsilon(run2[0],run2[1],run2[2]), epsilon(run3[0],run3[1],run3[2])]
eps_eu = [epsilon(q1[0],q1[1],q1[2]), epsilon(q2[0],q2[1],q2[2]), epsilon(q3[0],q3[1],q3[2])]
eps_euc = [epsilon(p1[0],p1[1],p1[2]), epsilon(p2[0],p2[1],p2[2]), epsilon(p3[0],p3[1],p3[2])]

plt.figure(111)
plt.plot(t,eps_run,marker='o',linewidth=3)
plt.plot(t,eps_eu,marker='s',linewidth=3)
plt.plot(t,eps_euc,marker='^',linewidth=3)
plt.yscale('log')
plt.xlabel('dt')
plt.ylabel(r'$\epsilon$')
plt.legend(['RK4','Euler','Euler-Cromer'],loc='best')
plt.savefig('epsilon.png',dpi=600)

# Calculate the number of steps for each function to reach epsilon greater than 10-4
print("steps = ",epsilon_check(0,1,0,4*np.pi,0.000001*np.pi,euler))


# Faça um programa que leia nome e peso de várias pessoas, guardando tudo em uma lista. No final, mostre:
# A) Quantas pessoas foram cadastradas.
# B) Uma listagem com as pessoas mais pesadas.
# C) Uma listagem com as pessoas mais leves.
dado = []
lista = []
pessoaMaiorPeso = []
pessoaMenorPeso = []
maiorPeso = 0
menorPeso = 0

while True:
    dado.append(str(input('Nome: ')))
    dado.append(float(input('Peso: ')))

    lista.append(dado[:])
    dado.clear()

    escolha = input('Deseja Continuar? [S/N]: ').upper()
    while escolha not in 'SN':
        escolha = input('Tente novamente. Deseja Continuar? [S/N]: ').upper()
    if escolha == 'N':
        break

for d in range(0, len(lista)):
    if maiorPeso == 0 and menorPeso == 0:
        maiorPeso = lista[d][1]
        menorPeso = lista[d][1]

    if lista[d][1] >= maiorPeso:
        if lista[d][1] == maiorPeso:
            maiorPeso = lista[d][1]
            pessoaMaiorPeso.append(lista[d][0])
        else:
            maiorPeso = lista[d][1]
            pessoaMaiorPeso.clear()
            pessoaMaiorPeso.append(lista[d][0])

    elif lista[d][1] <= menorPeso:
        if lista[d][1] == menorPeso:
            menorPeso = lista[d][1]
            pessoaMenorPeso.append(lista[d][0])
        else:
            menorPeso = lista[d][1]
            pessoaMenorPeso.clear()
            pessoaMenorPeso.append(lista[d][0])

print(lista)
print(f"Ao todo, foram cadastrados: {len(lista)} pessoas.")
print(f"O maior peso foi de {maiorPeso}. Peso de {pessoaMaiorPeso}.")
print(f"O menor peso foi de {menorPeso}. Peso de {pessoaMenorPeso}.")


from tkinter import *
from tk_tools import TK_ROOT
from tkinter import ttk
from tkinter import filedialog

from functools import partial
from PIL import Image, ImageTk

import os.path

import img as png

from BEE2_config import ConfigFile, GEN_OPTS
from tooltip import add_tooltip
import SubPane
import utils

MAX_ENTS = 2048
MAX_OVERLAY = 512
MAX_BRUSH = 8192

# The size of PeTI screenshots
PETI_WIDTH = 555
PETI_HEIGHT = 312

COMPILE_DEFAULTS = {
    'Screenshot': {
        'Type': 'AUTO',
        'Loc': '',
        },
    'General': {
        'spawn_elev': 'True',
        'player_model': 'PETI',
        'force_final_light': '0',
        },
    'Corridor': {
        'sp_entry': '1',
        'sp_exit': '1',
        'coop': '1',
    },
    'Counts': {
        'brush': '0',
        'ent': '0',
        'overlay': '0',
        },
    }

PLAYER_MODELS = {
    'ATLAS': 'ATLAS',
    'PBODY': 'P-Body',
    'SP': 'Chell',
    'PETI': 'Bendy',
}
PLAYER_MODEL_ORDER = ['Bendy', 'Chell', 'ATLAS', 'P-Body']
PLAYER_MODELS_REV = {value: key for key, value in PLAYER_MODELS.items()}

COMPILE_CFG = ConfigFile('compile.cfg')
COMPILE_CFG.set_defaults(COMPILE_DEFAULTS)
window = None
UI = {}

chosen_thumb = StringVar(
    value=COMPILE_CFG.get_val('Screenshot', 'Type', 'AUTO')
)
tk_screenshot = None # The preview image shown

# Location we copy custom screenshots to
SCREENSHOT_LOC = os.path.abspath(os.path.join(
        os.getcwd(),
        '..',
        'config',
        'screenshot.jpg'
    ))

player_model_var = StringVar(
    value=PLAYER_MODELS.get(
        COMPILE_CFG.get_val('General', 'player_model', 'PETI'),
        PLAYER_MODELS['PETI'],
    )
)
start_in_elev = IntVar(
    value=COMPILE_CFG.get_bool('General', 'spawn_elev')
)
cust_file_loc = COMPILE_CFG.get_val('Screenshot', 'Loc', '')
cust_file_loc_var = StringVar(value='')

count_brush = IntVar(value=0)
count_ents = IntVar(value=0)
count_overlay = IntVar(value=0)

vrad_light_type = IntVar(
    value=COMPILE_CFG.get_bool('General', 'vrad_force_full')
)
cleanup_screenshot = IntVar(
    value=COMPILE_CFG.get_bool('Screenshot', 'del_old', True)
)

CORRIDOR = {}

def set_corr_values(group_name, props):
    """Set the corrdors according to the passed prop_block."""
    count = 7 if group_name == 'sp_entry' else 4
    group = CORRIDOR[group_name] = ['Random'] + [
        str(i) + ': Corridor'
        for i in
        range(1, count+1)
    ]
    for prop in props[group_name]:
        try:
            ind = int(prop.name)
        except ValueError:
            continue

        if 0 < ind <= count:
            group[ind] = '{!r}: {}'.format(ind, prop.value)

def make_corr_combo(frm, corr_name, width):
    set_corr_values(corr_name, {corr_name: []})
    widget = ttk.Combobox(
        frm,
        values=CORRIDOR[corr_name],
        width=width,
        exportselection=0,
    )
    widget['postcommand'] = partial(set_corr_dropdown, corr_name, widget)
    widget.state(['readonly'])
    widget.bind(
        '<<ComboboxSelected>>',
        partial(set_corr, corr_name)
    )
    widget.current(COMPILE_CFG.get_int('Corridor', corr_name))
    return widget

def refresh_counts(reload=True):
    if reload:
        COMPILE_CFG.load()
    count_brush.set(COMPILE_CFG.get_int('Counts', 'brush'))
    count_ents.set(COMPILE_CFG.get_int('Counts', 'ent'))
    count_overlay.set(COMPILE_CFG.get_int('Counts', 'overlay'))

def find_screenshot(_=None):
    """Prompt to browse for a screenshot."""
    file_name = filedialog.askopenfilename(
        title='Find Game Exe',
        filetypes=[
            ('Image File', '*.jpg *.jpeg *.jpe *.jfif *.png *.bmp'
                           '*.tiff *.tga *.ico *.psd'),
        ],
        initialdir='C:',
    )
    if file_name:
        load_screenshot(file_name)
    COMPILE_CFG.save_check()

def set_screen_type():
    """Set the type of screenshot used."""
    chosen = chosen_thumb.get()
    COMPILE_CFG['Screenshot']['type'] = chosen
    if chosen == 'CUST':
        UI['thumb_label'].grid(row=2, column=0, columnspan=2, sticky='EW')
    else:
        UI['thumb_label'].grid_forget()
    UI['thumb_label'].update()
    # Resize the pane to accommodate the shown/hidden image
    window.geometry('{}x{}'.format(
        window.winfo_width(),
        window.winfo_reqheight(),
    ))

    COMPILE_CFG.save()

def load_screenshot(path):
    """Copy the selected image, changing format if needed."""
    img = Image.open(path)
    resized_img = img.resize(
        # Resize to the same propotions as the original
        # PeTI screenshots.
        (
            img.width,
            int(img.height * PETI_WIDTH / PETI_HEIGHT)
        )
    )
    COMPILE_CFG['Screenshot']['LOC'] = SCREENSHOT_LOC
    resized_img.save(SCREENSHOT_LOC)
    set_screenshot(img)

def set_screenshot(img=None):
    # Make the visible screenshot small
    global tk_screenshot
    if img is None:
        try:
            img = Image.open(SCREENSHOT_LOC)
        except IOError:  # Image doesn't exist!
            # In that case, use a black image
            img = Image.new('RGB', (1, 1), color=(0, 0, 0))
    tk_img = img.resize(
        (
            int(PETI_WIDTH // 3.5),
            int(PETI_HEIGHT // 3.5),
        ),
        Image.LANCZOS
    )
    tk_screenshot = ImageTk.PhotoImage(tk_img)
    UI['thumb_label']['image'] = tk_screenshot

def set_screenshot_cleanup():
    COMPILE_CFG['Screenshot']['del_old'] = str(cleanup_screenshot.get())

def set_elev_type():
    COMPILE_CFG['General']['spawn_elev'] = str(start_in_elev.get())
    COMPILE_CFG.save()

def set_model(_=None):
    """Save the selected player model."""
    text = player_model_var.get()
    COMPILE_CFG['General']['player_model'] = PLAYER_MODELS_REV[text]
    COMPILE_CFG.save()

def set_corr(corr_name, e):
    """Save the chosen corridor when it's changed.

    This is shared by all three dropdowns.
    """
    COMPILE_CFG['Corridor'][corr_name] = str(e.widget.current())
    COMPILE_CFG.save()

def set_corr_dropdown(corr_name, widget):
    """Set the values in the dropdown when it's opened."""
    widget['values'] = CORRIDOR[corr_name]

def set_vrad_type():
    """Set the compile type override for VRAD."""
    COMPILE_CFG['General']['vrad_force_full'] = str(vrad_light_type.get())
    COMPILE_CFG.save()

def make_pane(tool_frame):
    """Create the compiler options pane.

    """
    global window
    window = SubPane.SubPane(
        TK_ROOT,
        options=GEN_OPTS,
        title='Compile Options',
        name='compiler',
        resize_x=True,
        resize_y=False,
        tool_frame=tool_frame,
        tool_img=png.png('icons/win_compiler'),
        tool_col=4,
    )
    window.columnconfigure(0, weight=1)

    thumb_frame = ttk.LabelFrame(
        window,
        text='Thumbnail',
        labelanchor=N,
    )
    thumb_frame.grid(row=0, column=0, sticky=EW)
    thumb_frame.columnconfigure(0, weight=1)

    UI['thumb_auto'] = ttk.Radiobutton(
        thumb_frame,
        text='Auto',
        value='AUTO',
        variable=chosen_thumb,
        command=set_screen_type,
    )

    UI['thumb_peti'] = ttk.Radiobutton(
        thumb_frame,
        text='PeTI',
        value='PETI',
        variable=chosen_thumb,
        command=set_screen_type,
    )

    UI['thumb_custom'] = ttk.Radiobutton(
        thumb_frame,
        text='Custom:',
        value='CUST',
        variable=chosen_thumb,
        command=set_screen_type,
    )

    UI['thumb_label'] = ttk.Label(
        thumb_frame,
        anchor=CENTER,
        cursor=utils.CURSORS['link'],
    )
    UI['thumb_label'].bind(
        utils.EVENTS['LEFT'],
        find_screenshot,
    )

    UI['thumb_cleanup'] = ttk.Checkbutton(
        thumb_frame,
        text='Cleanup old screenshots',
        variable=cleanup_screenshot,
        command=set_screenshot_cleanup,
    )

    UI['thumb_auto'].grid(row=0, column=0, sticky='W')
    UI['thumb_peti'].grid(row=0, column=1, sticky='W')
    UI['thumb_custom'].grid(row=1, column=0, columnspan=2, sticky='NEW')
    UI['thumb_cleanup'].grid(row=3, columnspan=2, sticky='W')
    add_tooltip(
        UI['thumb_auto'],
        "Override the map image to use a screenshot automatically taken"
        "from the beginning of a chamber. Press F5 to take a new "
        "screenshot. If the map has not been previewed recently "
        "(within the last few hours), the default PeTI screenshot "
        "will be used instead."
    )
    add_tooltip(
        UI['thumb_peti'],
        "Use the normal editor view for the map preview image."
    )
    custom_tooltip = (
        "Use a custom image for the map preview image. Click the "
        "screenshot to select.\n"
        "Images will be converted to JPEGs if needed."
    )
    add_tooltip(
        UI['thumb_custom'],
        custom_tooltip,
    )

    add_tooltip(
        UI['thumb_label'],
        custom_tooltip
    )

    add_tooltip(
        UI['thumb_cleanup'],
        'Automatically delete unused Automatic screenshots. '
        'Disable if you want to keep things in "portal2/screenshots". '
    )

    if chosen_thumb.get() == 'CUST':
        # Show this if the user has set it before
        UI['thumb_label'].grid(row=2, column=0, columnspan=2, sticky='EW')
    set_screenshot()  # Load the last saved screenshot

    vrad_frame = ttk.LabelFrame(
        window,
        text='Lighting:',
        labelanchor=N,
    )
    vrad_frame.grid(row=1, column=0, sticky=EW)

    UI['light_fast'] = ttk.Radiobutton(
        vrad_frame,
        text='Fast',
        value=0,
        variable=vrad_light_type,
        command=set_vrad_type,
    )
    UI['light_fast'].grid(row=0, column=0)
    UI['light_full'] = ttk.Radiobutton(
        vrad_frame,
        text='Full',
        value=1,
        variable=vrad_light_type,
        command=set_vrad_type,
    )
    UI['light_full'].grid(row=0, column=1)

    add_tooltip(
        UI['light_fast'],
        "Compile with lower-quality, fast lighting. This speeds "
        "up compile times, but does not appear as good. Some "
        "shadows may appear wrong.\n"
        "When publishing, this is ignored."
    )
    add_tooltip(
        UI['light_full'],
        "Compile with high-quality lighting. This looks correct, "
        "but takes longer to compute. Use if you're arranging lights."
        "When publishing, this is always used."
    )

    elev_frame = ttk.LabelFrame(
        window,
        text='Spawn at:',
        labelanchor=N,
    )

    elev_frame.grid(row=2, column=0, sticky=EW)
    elev_frame.columnconfigure(0, weight=1)
    elev_frame.columnconfigure(1, weight=1)

    UI['elev_preview'] = ttk.Radiobutton(
        elev_frame,
        text='Entry Door',
        value=0,
        variable=start_in_elev,
        command=set_elev_type,
    )

    UI['elev_elevator'] = ttk.Radiobutton(
        elev_frame,
        text='Elevator',
        value=1,
        variable=start_in_elev,
        command=set_elev_type,
    )

    UI['elev_preview'].grid(row=0, column=0, sticky=W)
    UI['elev_elevator'].grid(row=0, column=1, sticky=W)

    add_tooltip(
        UI['elev_elevator'],
        "When previewing in SP, spawn inside the entry elevator. "
        "This also disables the map restarts when you reach the "
        "exit door. Use this to examine the entry and exit corridors."
    )
    add_tooltip(
        UI['elev_preview'],
        "When previewing in SP, spawn just before the entry door. "
        "When you reach the exit door, the map will restart."
    )

    corr_frame = ttk.LabelFrame(
        window,
        text='Corridor:',
        labelanchor=N,
    )
    corr_frame.grid(row=3, column=0, sticky=EW)
    corr_frame.columnconfigure(0, weight=1)
    corr_frame.columnconfigure(1, weight=1)

    UI['corr_sp_entry'] = make_corr_combo(
        corr_frame,
        'sp_entry',
        width=9,
    )

    UI['corr_sp_exit'] = make_corr_combo(
        corr_frame,
        'sp_exit',
        width=9,
    )

    UI['corr_coop'] = make_corr_combo(
        corr_frame,
        'coop',
        width=9,
    )

    UI['corr_sp_entry'].grid(row=1, column=0, sticky=EW)
    UI['corr_sp_exit'].grid(row=1, column=1, sticky=EW)
    UI['corr_coop'].grid(row=2, column=1, sticky=EW)
    ttk.Label(
        corr_frame,
        text='SP Entry:',
        anchor=CENTER,
    ).grid(row=0, column=0, sticky=EW)
    ttk.Label(
        corr_frame,
        text='SP Exit:',
        anchor=CENTER,
    ).grid(row=0, column=1, sticky=EW)
    ttk.Label(
        corr_frame,
        text='Coop:',
        anchor=CENTER,
    ).grid(row=2, column=0, sticky=EW)

    model_frame = ttk.LabelFrame(
        window,
        text='Player Model (SP):',
        labelanchor=N,
    )
    model_frame.grid(row=4, column=0, sticky=EW)
    UI['player_mdl'] = ttk.Combobox(
        model_frame,
        exportselection=0,
        textvariable=player_model_var,
        values=PLAYER_MODEL_ORDER,
        width=20,
    )
    # Users can only use the dropdown
    UI['player_mdl'].state(['readonly'])
    UI['player_mdl'].grid(row=0, column=0, sticky=EW)

    UI['player_mdl'].bind('<<ComboboxSelected>>', set_model)
    model_frame.columnconfigure(0, weight=1)

    count_frame = ttk.LabelFrame(
        window,
        text='Last Compile:',
        labelanchor=N,
    )

    count_frame.grid(row=5, column=0, sticky=EW)
    count_frame.columnconfigure(0, weight=1)
    count_frame.columnconfigure(2, weight=1)

    ttk.Label(
        count_frame,
        text='Entity',
        anchor=N,
    ).grid(row=0, column=0, columnspan=3, sticky=EW)

    UI['count_ent'] = ttk.Progressbar(
        count_frame,
        maximum=MAX_ENTS,
        variable=count_ents,
        length=120,
    )
    UI['count_ent'].grid(
        row=1,
        column=0,
        columnspan=3,
        sticky=EW,
        padx=5,
    )

    ttk.Label(
        count_frame,
        text='Overlay',
        anchor=CENTER,
    ).grid(row=2, column=0, sticky=EW)
    UI['count_over'] = ttk.Progressbar(
        count_frame,
        maximum=MAX_OVERLAY,
        variable=count_overlay,
        length=50,
    )
    UI['count_over'].grid(row=3, column=0, sticky=EW, padx=5)

    UI['refresh_counts'] = SubPane.make_tool_button(
        count_frame,
        png.png('icons/tool_sub', resize_to=16),
        refresh_counts,
    )
    UI['refresh_counts'].grid(row=3, column=1)

    ttk.Label(
        count_frame,
        text='Brush',
        anchor=CENTER,
    ).grid(row=2, column=2, sticky=EW)
    UI['count_brush'] = ttk.Progressbar(
        count_frame,
        maximum=MAX_BRUSH,
        variable=count_brush,
        length=50,
    )
    UI['count_brush'].grid(row=3, column=2, sticky=EW, padx=5)

    UI['view_logs'] = ttk.Button(
        count_frame,
        text='View Logs',
    )
    UI['view_logs'].grid(row=4, column=0, columnspan=3, sticky=EW)

    refresh_counts(reload=False)


import secrets
from collections import namedtuple
from importlib import import_module

import pytest

PreparedDbInfo = namedtuple("PreparedDbInfo", ["full_name", "schema", "table"])

@pytest.fixture()
def processing(request, spark):
    processing_classes = {
        "clickhouse": ("tests.fixtures.processing.clickhouse", "ClickhouseProcessing"),
        "greenplum": ("tests.fixtures.processing.greenplum", "GreenplumProcessing"),
        "hive": ("tests.fixtures.processing.hive", "HiveProcessing"),
        "mongodb": ("tests.fixtures.processing.mongodb", "MongoDBProcessing"),
        "mssql": ("tests.fixtures.processing.mssql", "MSSQLProcessing"),
        "mysql": ("tests.fixtures.processing.mysql", "MySQLProcessing"),
        "oracle": ("tests.fixtures.processing.oracle", "OracleProcessing"),
        "postgres": ("tests.fixtures.processing.postgres", "PostgresProcessing"),
        "kafka": ("tests.fixtures.processing.kafka", "KafkaProcessing"),
    }

    db_storage_name = request.function.__name__.split("_")[1]
    if db_storage_name not in processing_classes:
        raise ValueError(f"Wrong name. Please use one of: {list(processing_classes.keys())}")

    module_name, class_name = processing_classes[db_storage_name]
    module = import_module(module_name)
    db_processing = getattr(module, class_name)

    if db_storage_name == "hive":
        yield db_processing(spark)
    else:
        with db_processing() as result:
            yield result

@pytest.fixture
def get_schema_table(processing):
    schema = processing.schema
    processing.create_schema(schema=schema)

    table = f"test_{secrets.token_hex(5)}"
    full_name = f"{schema}.{table}"

    yield PreparedDbInfo(full_name=full_name, schema=schema, table=table)

    try:
        processing.drop_table(
            table=table,
            schema=schema,
        )
    except Exception:  # noqa: S110
        pass

@pytest.fixture
def prepare_schema_table(processing, get_schema_table):
    fields = {column_name: processing.get_column_type(column_name) for column_name in processing.column_names}
    _, schema, table = get_schema_table

    processing.create_table(schema=schema, table=table, fields=fields)

    return get_schema_table

@pytest.fixture
def load_table_data(prepare_schema_table, processing):
    _, schema, table = prepare_schema_table

    processing.insert_data(
        schema=schema,
        table=table,
        values=processing.create_pandas_df(),
    )

    return prepare_schema_table


#!/usr/bin/env python3
import sys

def get_possibilities_recursive(input: list, start_idx=0) -> int:
    """
    Recusive solution
    O(3 ^ |input|)
    """
    if start_idx == len(input) - 1:
        return 1

    res = 0
    for i in range(1, 4):
        try:
            if input[start_idx + i] - input[start_idx] <= 3:
                res += get_possibilities_recursive(input, start_idx + i)
            else:
                break
        except IndexError:
            break

    return res

def get_possibilities_dp(input: list) -> int:
    """
    Dynamic Programming based solution
    O(|input|)
    """
    # possibilities from some idx onwards
    possibilities = [ 0 for _ in range(len(input)) ]
    possibilities[-1] = 1
    for idx in range(len(input) - 2, -1, -1):
        for i in range(1, 4):
            try:
                if input[idx + i] - input[idx] <= 3:
                    possibilities[idx] += possibilities[idx + i]
                else:
                    break
            except IndexError:
                break

    return possibilities[0]

def main():
    if len(sys.argv) < 2:
        print(f"usage: {sys.argv[0]} <input-fname>", file=sys.stderr)
        sys.exit(1)
    with open(sys.argv[1], 'r') as f:
        input = [int(val) for val in f.read().splitlines()]

    input.append(0)
    input.sort()

    # recusive solution is too time consuming for `input`
    # possibilities = get_possibilities_recursive(input)

    possibilities = get_possibilities_dp(input)
    print(possibilities)

if __name__ == "__main__":
    main()

"""
for sample1: 8
for sample2: 19208
for input: 13816758796288
"""


import pygame
from pygame.locals import *
import time
pygame.init()
white = (255, 255, 255)
green = (0, 255, 0)
blue = (0, 0, 128)
RED=(255,0,0)
X = 800
Y = 400
color=(230,230,0)
# def make_button():
#     text = pygame.font.Font('freesansbold.ttf', 25).render('Next', True, (0,0,255),(255,165,0))
#     textRect = text.get_rect()
#     textRect.center = (500,200)
#     display_surface.blit(text, textRect)
#     if event.type == pygame.MOUSEBUTTONDOWN:
#         if textRect.collidepoint(event.pos):
#             pass

display_surface = pygame.display.set_mode((X, Y))
text = pygame.font.Font('freesansbold.ttf', 25).render('Next', True, (0,0,255),(255,165,0))
textRect = text.get_rect()
textRect.center = (500,200)
run=True
while run:#main loop for running of game until pressed close

    for event in pygame.event.get():#for taking all the input inside of game
        if event.type==pygame.QUIT:
            run=False
        if event.type == pygame.MOUSEBUTTONDOWN:
                if textRect.collidepoint(event.pos):
                    color=(230,230,230)
        display_surface.fill(color)
    display_surface.blit(text, textRect)
    pygame.display.update()
pygame.quit()


import asyncio
from decimal import Decimal
import logging
from operator import itemgetter, setitem
from typing import Any, Optional
from unittest.mock import ANY

import pytest
import sqlalchemy
from app.tests.utility import (aplly_func_to_dict_and_return_dict,
                               copy_list_update_key_in_dict,
                               get_dict_from_RowProxy)
from gino import Gino
from pytest_dictsdiff import check_objects
from sqlalchemy import select
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.engine.result import RowProxy
from sqlalchemy.sql.functions import sum as sql_sum
from app.tests.tree.tree_implement import Tree
from asyncpg.exceptions import DeadlockDetectedError

pytestmark = pytest.mark.usefixtures("clear_db_async_pg")

class TestGinoQuery():

    @pytest.mark.asyncio
    async def test_insert_value(self, table_tasks, async_list_dict_5:list[dict], dicts_are_same)->None:
        stmt = insert(table_tasks).values(async_list_dict_5)
        result = await stmt.returning(table_tasks.__table__).gino.all()
        assert isinstance(result, list)
        assert isinstance(result[0], sqlalchemy.engine.result.RowProxy)
        list_wich_task_id = copy_list_update_key_in_dict(async_list_dict_5, {"task_id":ANY})
        assert dicts_are_same(list_wich_task_id[0] , get_dict_from_RowProxy(result[0]))

    @pytest.mark.asyncio
    async def test_create_(self, table_tasks, async_list_dict_5:list[dict], dicts_are_same)->None:
        teask_object = await table_tasks.create(**async_list_dict_5[0])
        assert isinstance(teask_object, table_tasks)
        dict_task_object = teask_object.__dict__["__values__"]
        list_wich_task_id = copy_list_update_key_in_dict(async_list_dict_5, {"task_id":ANY})
        assert check_objects(list_wich_task_id[0], dict_task_object, verbose= 1)

    @pytest.mark.asyncio
    async def test_insert_many(self, table_tasks, async_list_dict_5:list[dict])->None:
        result = await table_tasks.insert().returning(table_tasks.__table__).gino.all(async_list_dict_5)
        assert result is None
        list_result = await table_tasks.query.gino.all()
        assert len(list_result) == len(async_list_dict_5)
        assert isinstance(list_result[0], table_tasks)

    @pytest.mark.asyncio
    async def test_select_queyr(self, table_tasks, gino_list_dict_5:list[Any], async_list_dict_5:list[dict])->None:
        # all() returns all results in a list, which may be empty when the query has no result, empty but still a list.
        # first() returns the first result directly, or None if there is no result at all. There is usually some optimization behind the scene to efficiently get only the first result, instead of loading the full result set into memory.
        # one() returns exactly one result. If there is no result at all or if there are multiple results, an exception is raised.
        # one_or_none() is similar to one(), but it returns None if there is no result instead or raising an exception.
        # scalar() is similar to first(), it returns the first value of the first result. Quite convenient to just retrieve a scalar value from database, like NOW(), MAX(), COUNT() or whatever generates a single value. None is also returned when there is no result, it is up to you how to distinguish no result and the first value is NULL.
        # status() executes the query and discard all the query results at all. Instead it returns the execution status line as it is, usually a textual string. Note, there may be no optimization to only return the status without loading the results, so make your query generate nothing if you don’t want any result.
        task_list  = await table_tasks.query.order_by(table_tasks.task_id).gino.all()
        assert isinstance(task_list, list)
        assert isinstance(task_list[0], table_tasks)
        list_wich_task_id = copy_list_update_key_in_dict(async_list_dict_5, {"task_id":ANY})
        for elem_etalon, elem_db in zip(list_wich_task_id, task_list):
            assert check_objects(elem_etalon, elem_db.__dict__["__values__"], verbose= 1)

    @pytest.mark.asyncio
    async def test_select_iterate(self, db:Gino, table_tasks, gino_list_dict_5:list[Any], async_list_dict_5:list[dict])->None:
        # https://python-gino.org/docs/en/master/reference/api/gino.engine.html#gino.engine.GinoConnection.iterate
        # Cursors must work within transactions:
        async with db.transaction():
            cursor = await db.iterate(table_tasks.query)
            user = await cursor.next()
            assert isinstance(user, table_tasks)
            users = await cursor.many(3)
            assert 3 == len(users)

    @pytest.mark.asyncio
    async def test_delete_query(self, table_tasks, gino_list_dict_5:list[Any], async_list_dict_5:list[dict])->None:
        # status() executes the query and discard all the query results at all. Instead it returns the execution status line as it is, usually a textual string. Note, there may be no optimization to only return the status without loading the results, so make your query generate nothing if you don’t want any result.
        list_id_delete = [gino_list_dict_5[0]["task_id"], gino_list_dict_5[1]["task_id"]]
        result = await table_tasks.delete.where(table_tasks.task_id.in_(list_id_delete)).gino.status()
        assert f'DELETE {len(list_id_delete)}' == result[0]
        list_wich_task_id = copy_list_update_key_in_dict(async_list_dict_5[2:], {"task_id":ANY})
        list_db = await table_tasks.query.gino.all()
        assert len(list_wich_task_id) == len(list_db)
        for elem_etalon, elem_db in zip(list_wich_task_id, list_db):
            assert check_objects(elem_etalon, elem_db.__dict__["__values__"], verbose= 1)

    @pytest.mark.asyncio
    async def test_delete_query_returning(self, table_tasks, gino_list_dict_5:list[Any], async_list_dict_5:list[dict])->None:
        # all() returns all results in a list, which may be empty when the query has no result, empty but still a list.
        list_id_delete = [gino_list_dict_5[0]["task_id"], gino_list_dict_5[1]["task_id"]]
        delete_items = await table_tasks.delete.where(table_tasks.task_id.in_(list_id_delete)).returning(table_tasks.__table__).gino.all()
        assert len(list_id_delete) == len(delete_items)
        list_wich_task_id = copy_list_update_key_in_dict(async_list_dict_5[:2], {"task_id":ANY})
        for elem_etalon, elem_db in zip(list_wich_task_id, delete_items):
            assert check_objects(elem_etalon, elem_db.__dict__["__values__"], verbose= 1)

    @pytest.mark.asyncio
    async def test_update_query(self, table_tasks, gino_list_dict_5:list[Any], async_list_dict_5:list[dict])->None:
        list_of_id = [gino_list_dict_5[0]["task_id"], gino_list_dict_5[1]["task_id"]]
        task_list = table_tasks.update.values(Num_of_executors = table_tasks.Num_of_executors +5).where(table_tasks.task_id.in_(list_of_id))
        result_list = await task_list.returning(table_tasks.task_name, table_tasks.Num_of_executors).gino.all()
        assert isinstance(result_list[0], table_tasks)
        list_etalon = async_list_dict_5[:2]
        func_input = (lambda x: setitem(x, "Num_of_executors", x["Num_of_executors"] +5))
        list_etalon = [aplly_func_to_dict_and_return_dict(func=func_input, dict_input=elem) for elem in list_etalon]
        assert len(list_etalon) == len(result_list)

        for elem_etalon, elem_db in zip(list_etalon, result_list):
            assert check_objects(elem_etalon, elem_db.__dict__["__values__"], verbose= 1)

    @pytest.mark.asyncio
    async def test_raw_query_get_first_and_one_param(self, db:Gino, table_tasks, gino_list_dict_5:list[Any], async_list_dict_5:list[dict])->None:
        # https://github.com/python-gino/gino/blob/83e60e80e43f7714a626590c3c61385c735457bc/tests/test_crud.py
        #
        # read Using Textual SQL
        # https://docs.sqlalchemy.org/en/14/core/tutorial.html
        engine = db.bind
        u2 = await engine.first(
                db.text("SELECT * FROM tasks WHERE task_id = :uid")
                .bindparams(uid=gino_list_dict_5[0]["task_id"])
                .columns(*table_tasks)
                .execution_options(model=table_tasks)
            )
        assert isinstance(u2, table_tasks)
        assert check_objects(gino_list_dict_5[0], u2.__dict__["__values__"])

    @pytest.mark.asyncio
    async def test_raw_query_get_all_and_list_param(self, db:Gino, table_tasks, gino_list_dict_5:list[Any], async_list_dict_5:list[dict])->None:
        # https://github.com/python-gino/gino/blob/83e60e80e43f7714a626590c3c61385c735457bc/tests/test_crud.py
        sql_exp = db.text("""SELECT * FROM tasks WHERE task_id = ANY (:list_uid)""")
        sql_exp = sql_exp.bindparams(sqlalchemy.bindparam('list_uid'))
        params = {
            "list_uid": [gino_list_dict_5[0]["task_id"], gino_list_dict_5[1]["task_id"]],
        }
        list_row_proxy = await db.all(sql_exp, params)
        assert isinstance(list_row_proxy, list)
        assert isinstance(list_row_proxy[0], RowProxy)
        assert check_objects(gino_list_dict_5[0], get_dict_from_RowProxy(list_row_proxy[0]))

class TestLoaders():
# https://github.com/python-gino/gino/blob/eb15470eafec1ed4ea180cd825f168c3635541de/tests/test_loader.py
    @pytest.mark.asyncio
    async def test_basic_lodaerd(self, db:Gino, table_tasks, table_tasks_time_track, gino_list_tack_dict_5:dict) -> None:
        query = table_tasks_time_track.outerjoin(table_tasks).select()
        tasks_wich_time = await query.gino.load(
            table_tasks.distinct(table_tasks.task_id).load(add_time_tracks=table_tasks_time_track)).all()

        assert isinstance(tasks_wich_time, list)
        assert isinstance(tasks_wich_time[0], table_tasks)
        func_sort = lambda x: x["time_track_id"]
        for elem in tasks_wich_time:
            list_etalon = [item for item in gino_list_tack_dict_5["time_tracks"] if item["task_id"] == elem.task_id]
            list_bd = [item.__dict__["__values__"] for item in elem.time_tracks]

            assert len(list_etalon) == len(list_bd)
            assert sorted(list_etalon, key=func_sort) == sorted(list_bd, key=func_sort)

    @pytest.mark.asyncio
    async def test_join_and_load_to_tuple(self, db:Gino, table_tasks, table_tasks_time_track, gino_list_tack_dict_5:dict)->None:
        summ_sec = sql_sum(table_tasks_time_track.count_secodns).label("sum_sec")
        time_track_summ = select([table_tasks_time_track.task_id, summ_sec]).group_by(table_tasks_time_track.task_id).alias()
        query = table_tasks.outerjoin(time_track_summ).select()
        result = await query.gino.load(
                (table_tasks.task_id, table_tasks.task_name, summ_sec)
            ).all()
        assert len(gino_list_tack_dict_5["tasks"]) == len(result)
        assert isinstance(result, list)
        for task_id, task_name, summ_sec in result:
            summ_etalon = sum((elem["count_secodns"] for elem in gino_list_tack_dict_5["time_tracks"] if elem["task_id"] == task_id))
            assert summ_etalon if summ_etalon else None == summ_sec

class TestTreeStructure():
# work wich recursion in postgres https://habr.com/ru/post/269497/?ysclid=l8fw4mx46588764965

    async def get_child_for_node_raw_query(self,db:Gino, id:str, list_coumnt:list[str],
                name_column_id:str, name_column_parent_id:str) -> Optional[list[dict]]:
        # https://bitworks.software/2017-10-20-storing-trees-in-rdbms.html
        str_column = ",".join(list_coumnt)
        str_column_files = ",".join((f'f.{elem}' for elem in list_coumnt))
        result =  await db.status(db.text(f'''
            WITH RECURSIVE sub_category({str_column}, level) AS (
            SELECT {str_column}, 1 FROM files WHERE {name_column_id} = :id
            UNION ALL
            SELECT {str_column_files}, level+1
            FROM files f, sub_category sc
            WHERE f.{name_column_parent_id} = sc.{name_column_id}
                )
            SELECT {str_column} FROM sub_category ;
            '''), {
                "id": id,
            })
        return [get_dict_from_RowProxy(elem) for elem in result[1]]

    async def get_parent_for_node_raw_query(self, db:Gino, id:str, list_coumnt:list[str],
                name_column_id:str, name_column_parent_id:str) -> Optional[list[dict]]:
        # https://bitworks.software/2017-10-20-storing-trees-in-rdbms.html
        str_column = ",".join(list_coumnt)
        str_column_files = ",".join((f'f.{elem}' for elem in list_coumnt))
        result =  await db.status(db.text(f'''
                WITH RECURSIVE sub_category({str_column}, level) AS (
                    SELECT {str_column}, 1 FROM files WHERE {name_column_id} = :id
                    UNION ALL
                    SELECT {str_column_files}, level+1
                    FROM files f, sub_category sc
                    WHERE f.{name_column_id} = sc.{name_column_parent_id}
                )
                SELECT {str_column}, level, (SELECT max(level) FROM sub_category) - level AS distance FROM sub_category ORDER BY distance DESC;
            '''),
            {"id":id}
            )
        set_of_del_key = set(['level', 'distance'])
        return [{key : val for key, val in sub.items() if key not in set_of_del_key} for sub in result[1]]

    # async def get_parent_for_node_gino_core(self, db:Gino, id:str, table)->None:
    #     Parent = table.alias()
    #     parents = db.select([table.parent_id])
    #     query = table.load(parent=Parent.on(
    #         table.parent_id == Parent.file_id
    #     )).where(
    #         table.file_id.in_(db.select([table.alias().parent_id]))
    #     )
    #     list_of_result:list[dict] = []
    #     list_of_result  = await query.gino.all()
    #         # list_of_result.append(c)
    #     a = 4

    @pytest.mark.asyncio
    async def test_get_child_raw_query(self, subtests, db:Gino, table_files, all_tree_in_one_list)->None:
        stmt = insert(table_files).values(all_tree_in_one_list[0])
        await stmt.gino.status()
        tree = Tree(key_id = "file_id", key_parent_id = "parent_id")
        tree.add_data(all_tree_in_one_list[0])

        func_sort = itemgetter("file_id")
        for id in (elem["file_id"] for elem in all_tree_in_one_list[0]):
            with subtests.test(msg=f'Get child for id {id}'):
                list_of_child_from_db = await self.get_child_for_node_raw_query(db=db, id=id, list_coumnt=["file_id", "type_file", "parent_id", "size"],
                        name_column_id="file_id", name_column_parent_id="parent_id")
                list_of_child_etalon = tree.get_child(id_node=id)

                assert len(list_of_child_etalon) == len(list_of_child_from_db)

                for elem_etalon, elem_db in zip(sorted(list_of_child_etalon, key=func_sort),sorted(list_of_child_from_db, key=func_sort)):
                    assert elem_etalon, elem_db

    @pytest.mark.asyncio
    async def test_get_parent_raw_query(self, subtests, db:Gino, table_files, all_tree_in_one_list)->None:
        stmt = insert(table_files).values(all_tree_in_one_list[0])
        await stmt.gino.status()
        tree = Tree(key_id = "file_id", key_parent_id = "parent_id")
        tree.add_data(all_tree_in_one_list[0])

        func_sort = itemgetter("file_id")
        for id in (elem["file_id"] for elem in all_tree_in_one_list[0]):
            with subtests.test(msg=f'Get child for id {id}'):
                list_of_child_from_db = await self.get_parent_for_node_raw_query(db=db, id=id, list_coumnt=["file_id", "type_file", "parent_id", "size"],
                        name_column_id="file_id", name_column_parent_id="parent_id")
                list_of_child_etalon = tree.get_parent(id_node=id)

                assert len(list_of_child_etalon) == len(list_of_child_from_db)

                for elem_etalon, elem_db in zip(sorted(list_of_child_etalon, key=func_sort),sorted(list_of_child_from_db, key=func_sort)):
                    assert elem_etalon, elem_db

    # @pytest.mark.asyncio
    # async def test_get_parent_gino_core(self, subtests, db:Gino, table_files, all_tree_in_one_list)->None:
    #     stmt = insert(table_files).values(all_tree_in_one_list[0])
    #     await stmt.gino.status()
    #     tree = Tree(key_id = "file_id", key_parent_id = "parent_id")
    #     tree.add_data(all_tree_in_one_list[0])

    #     func_sort = itemgetter("file_id")
    #     for id in (elem["file_id"] for elem in all_tree_in_one_list[0]):
    #         with subtests.test(msg=f'Get child for id {id}'):
    #             list_of_child_from_db = await self.get_parent_for_node_gino_core(db=db, id=id, table=table_files)
    #             list_of_child_etalon = tree.get_parent(id_node=id)

    #             assert len(list_of_child_etalon) == len(list_of_child_from_db)

    #             for elem_etalon, elem_db in zip(sorted(list_of_child_etalon, key=func_sort),sorted(list_of_child_from_db, key=func_sort)):
    #                 assert elem_etalon, elem_db

class TestBlocking():
    # https://habr.com/ru/company/postgrespro/blog/462877/?ysclid=l8dwud10nd529920752
    # Хороший цикл статей по блокировкам
    # async def service_update_task(self, db, table_tasks, id_task:int, add_number:int):
    #     task_list = table_tasks.update.values(Num_of_executors = table_tasks.Num_of_executors +add_number)
    #     task_list = task_list.where(task_id = id_task)
    #     await task_list.gino.status()

    async def update_wichout_lock(self, db, table_tasks, id_task:int, add_number:int)->None:
        # так делать нельзя, т.к. между чтением данных и их обновлением происходит их изменение в коркурируещем процесе.
        task_data = await table_tasks.query.where(table_tasks.task_id == id_task).gino.first()
        await asyncio.sleep(1)
        stms = table_tasks.update.values(Num_of_executors = task_data.Num_of_executors +add_number)
        result = await stms.where(table_tasks.task_id == id_task).gino.status()
        await asyncio.sleep(1)

    async def update_lock_for_update(self, db, table_tasks, id_task:int, add_number:int, name_task:str)->None:
        # блокируем запросом для обновления, в данном случаи ключи не меняются поэтому использует FOR NO KEY UPDATE
        logger = logging.getLogger()
        logger.debug(f"Login inside {name_task}")
        async with db.transaction():
            engine = db.bind
            task_data = await engine.first(
                db.text("SELECT * FROM tasks WHERE task_id = :uid FOR NO KEY UPDATE")
                .bindparams(uid=id_task)
                .columns(*table_tasks)
                .execution_options(model=table_tasks)
            )
            await asyncio.sleep(1)
            stms = table_tasks.update.values(Num_of_executors = task_data.Num_of_executors +add_number)
            result = await stms.where(table_tasks.task_id == id_task).gino.status()
            await asyncio.sleep(1)
        logger.debug(f"logout form {name_task}")

    @pytest.mark.asyncio
    async def test_not_correct_update(self, db, table_tasks, gino_list_dict_5:dict):
        task_id = gino_list_dict_5[0]["task_id"]
        task_one = self.update_wichout_lock(db, table_tasks, task_id, 10)
        task_two = self.update_wichout_lock(db, table_tasks, task_id, 10)
        reslt = await asyncio.gather(*[task_one, task_two])
        task_data = await table_tasks.query.where(table_tasks.task_id == task_id).gino.first()
        assert task_data.Num_of_executors == (gino_list_dict_5[0]["Num_of_executors"] +10)

    @pytest.mark.asyncio
    async def test_correct_select_for_update(self, db, table_tasks, gino_list_dict_5:dict):
        task_id = gino_list_dict_5[0]["task_id"]
        task_one = self.update_lock_for_update(db, table_tasks, task_id, 10, "Task one")
        task_two = self.update_lock_for_update(db, table_tasks, task_id, 10, "Task two")
        reslt = await asyncio.gather(*[task_one, task_two])
        task_data = await table_tasks.query.where(table_tasks.task_id == task_id).gino.first()
        assert task_data.Num_of_executors == (gino_list_dict_5[0]["Num_of_executors"] +10*2)

    async def make_update_two_row(self, db, table_tasks, first_id:int, two_id:int, name_task:str)->None:
        add_number = 1
        logger = logging.getLogger()
        logger.debug(f"Login inside {name_task}")
        async with db.transaction():
            engine = db.bind
            task_data = await engine.first(
                db.text("SELECT * FROM tasks WHERE task_id = :uid FOR NO KEY UPDATE")
                .bindparams(uid=first_id)
                .columns(*table_tasks)
                .execution_options(model=table_tasks)
            )
            await asyncio.sleep(1)
            stms = table_tasks.update.values(Num_of_executors = task_data.Num_of_executors +add_number)
            result = await stms.where(table_tasks.task_id == two_id).gino.status()
            await asyncio.sleep(1)
        logger.debug(f"logout form {name_task}")

    async def get_curr_count_deadlock(self, db)->Decimal:
        engine = db.bind
        deadlock_data = await engine.first(db.text("SELECT SUM(deadlocks) FROM pg_stat_database ;"))
        return deadlock_data.values()[0]

    @pytest.mark.asyncio
    async def test_deadlock(self, db, table_tasks, gino_list_dict_5:dict):
        deadlock_before = await self.get_curr_count_deadlock(db)
        first_task_id = gino_list_dict_5[0]["task_id"]
        two_task_id = gino_list_dict_5[1]["task_id"]
        task_one = self.make_update_two_row(db, table_tasks, first_task_id, two_task_id, "Task one")
        task_two = self.make_update_two_row(db, table_tasks, two_task_id, first_task_id, "Task two")
        result = await asyncio.gather(*[task_one, task_two],return_exceptions=True)
        first_task_id = await table_tasks.query.where(table_tasks.task_id == first_task_id).gino.first()
        two_task_id = await table_tasks.query.where(table_tasks.task_id == two_task_id).gino.first()
        assert isinstance(result[0], DeadlockDetectedError) or isinstance(result[1], DeadlockDetectedError)
        assert deadlock_before+1 == await self.get_curr_count_deadlock(db)

    async def work_advisory_lock(self, db, table_tasks, name_for_blocking:str, name_task:str)->None:
        # В отличие от других блокировок (таких, как блокировки отношений), рекомендательные блокировки (advisory locks)
        # никогда не устанавливаются автоматически, ими управляет разработчик приложения. Их удобно использовать, например,
        # если приложению для каких-то целей требуется логика блокирования, не вписывающаяся в стандартную логику обычных блокировок.
        # Существуют большой набор функций для работы с рекомендательными блокировками на все случаи жизни:

        #     pg_advisory_lock_shared получает разделяемую блокировку,
        #     pg_advisory_xact_lock (и pg_advisory_xact_lock_shared) получает блокировку до конца транзакции,
        #     pg_try_advisory_lock (а также pg_try_advisory_xact_lock и pg_try_advisory_xact_lock_shared) не ожидает получения блокировки, а возвращает ложное значение, если блокировку не удалось получить немедленно.
        logger = logging.getLogger()
        async with db.transaction():
            logger.debug(f"Begin transaction  {name_task} lock name {name_for_blocking}")
            engine = db.bind
            await engine.first(
                    db.text("SELECT pg_advisory_xact_lock(hashtext(:name_block));").bindparams(name_block=name_for_blocking)
                    )
            await asyncio.sleep(2)
            await table_tasks.create(task_name=name_task)

            logger.debug(f"End transaction  {name_task} lock name {name_for_blocking}")

    @pytest.mark.asyncio
    async def test_deadlock(self, db, table_tasks):
        task_one = self.work_advisory_lock(db, table_tasks, "Block one", "Task one")
        task_two = self.work_advisory_lock(db, table_tasks, "Block one", "Task two")
        task_tree = self.work_advisory_lock(db, table_tasks, "Block two", "Task tree")
        result = await asyncio.gather(*[task_one, task_two, task_tree])
        all_tasks = await table_tasks.query.order_by(table_tasks.task_id).gino.all()
        assert all_tasks[0].task_name == "Task one"
        assert all_tasks[1].task_name == "Task tree"
        assert all_tasks[2].task_name == "Task two"


from django.contrib.auth.decorators import login_required
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse

from .models import Favorite
from products.models import Product

@login_required(login_url='login')
def add_product_to_favorite(request, product_id):
    try:
        favorite, _ = Favorite.objects.get_or_create(user=request.user)
        product = get_object_or_404(Product, id=product_id)

        favorite.products.add(product)

        redirect_url = request.META.get('HTTP_REFERER', reverse('catalog'))

        return HttpResponseRedirect(redirect_url)

    except (Favorite.DoesNotExist, Product.DoesNotExist):
        return render(request, 'main/400.html', {'error': 'Что-то пошло не так'}, status=400)

def remove_favorite(request, product_id):
    if request.user.is_authenticated:
        favorite = get_object_or_404(Favorite, user=request.user)
        product = get_object_or_404(Product, id=product_id)
        favorite.products.remove(product)

        redirect_url = request.META.get('HTTP_REFERER', reverse('personal_data'))

        return HttpResponseRedirect(redirect_url)


from setuptools import setup

with open('README.rst', 'r') as f:
    README = f.read()

setup(name='aftershocks',
      version='0.1.0',
      description='Plot recent earthquakes in Japan by region',
      long_description=README,
      long_description_content_type='text/x-rst',
      url='http://github.com/juseg/aftershocks',
      author='Julien Seguinot',
      license='gpl-3.0',
      py_modules=['aftershocks'],
      install_requires=['matplotlib', 'pandas'],
      scripts=['aftershocks.py'],
      zip_safe=False)


import asyncio
from Modules.Module_Basic import *
# from Modules.Module_API import *
from Modules.Module_SQL import *
from Utils.sendLog import sendLogging

class question_btn(discord.ui.View):
    def __init__(self):
        super().__init__(timeout=None)

    @discord.ui.button(label="문의 글 작성하기", style=discord.ButtonStyle.green, custom_id="ok_btn")
    async def donation_from(
        self, interaction: discord.Interaction, button: discord.ui.Button
    ):
        class modal(ui.Modal, title="아래의 항목에 정보를 작성해주세요."):
            question_title = ui.TextInput(
                label="문의 글 제목",
                style=discord.TextStyle.long,
                placeholder="",
                default="",
                required=True,
                max_length=50,
            )

            question_content = ui.TextInput(
                label="문의 내용",
                style=discord.TextStyle.paragraph,
                placeholder="",
                default="",
                required=True,
                max_length=100,
            )

            async def on_submit(self, interaction: Interaction) -> None:
                category: CategoryChannel = interaction.guild.get_channel(1167826323939532881)
                overwrites = {
                    interaction.guild.default_role: PermissionOverwrite(read_messages=False, view_channel=False, send_messages=False),
                    interaction.user: PermissionOverwrite(read_messages=True, view_channel=True, send_messages=True)
                }
                textChannel = await interaction.guild.create_text_channel(name=f"문의-{interaction.user.display_name}", category=category, overwrites=overwrites)

                await interaction.guild.get_channel(textChannel.id).send(f"작성자 : {interaction.user.mention}({interaction.user.id})\n질문 제목 : {self.question_title} \n### 질문 내용\n{self.question_content}")
                await interaction.response.send_message(
                    f"질문을 정상적으로 작성 하였습니다. \n질문 제목 : {self.question_title} \n### 질문 내용\n{self.question_content}",
                    ephemeral=True,
                )

        await interaction.response.send_modal(modal())

class users_cs(commands.Cog):
    def __init__(self, client: commands.Bot) -> None:
        self.client = client

    # @app_commands.command(name="팀정보", description="팀 정보를 확인 할 수 있습니다.")
    # async def teamCoinInfo(self, interaction: Interaction):
    #     db, SQL = get_SQL()
    #     if db == False:
    #         return await interaction.response.send_message("서버와의 연결이 불안정 합니다.", ephemeral=True)

    #     result, teamData = await API_teamInfo(get_teamId(interaction.user.id))
    #     if result:
    #         users = str(teamData['member']).split(', ')
    #         await interaction.response.send_message(f"## {teamData['name']}팀의 정보 \n- 보유 코인: {teamData['coin']} \n- 보유 머니: {teamData['money']} \n## 팀원정보\n- {users[0]} (팀장)\n- {users[1]}\n- {users[2]}\n- {users[3]}", ephemeral=True)

    #     else:
    #         await sendLogging(self.client, f"팀정보 조회 ERROR - {interaction.user.mention} \n{teamData}")
    #         await interaction.response.send_message("팀을 조회 할 수 업습니다. 스태프에게 문의 해주세요.", ephemeral=True)

    # @app_commands.command(name="우리팀일어남", description="스태프에게 기상 하였다는 것을 알려줍니다.")
    # async def wakeUp(self, interaction: Interaction):
    #     teamName = get_teamName(get_teamId(interaction.user.id))
    #     if teamName == "False" or teamName is False or teamName == 'false':
    #         await sendLogging(self.client, f"ERROR 우리팀일어남 - 참가한 팀이 없습니다. \n사용자 : {interaction.user.mention}")
    #         interaction.response.send_message(f"## {teamName}팀 모두 기상하셨군요! 스태프에게 전달 해놓을게요~~", ephemeral=True)
    #     await sendLogging(self.client, f"## {teamName}팀이 기상하였습니다.")
    #     await interaction.response.send_message(f"## {teamName}팀 모두 기상하셨군요! 스태프에게 전달 해놓을게요~~", ephemeral=True)

    # @app_commands.command(name="신청곡", description="1코인으로 신청곡을 신청 합니다.")
    # @app_commands.describe(value="신청곡 URL 또는 제목과 가수명을 작성해 주세요.")
    # async def musicStaffSend(self, interaction: Interaction, value: str):
    #     await interaction.response.send_message(f"현재 준비중 입니다.", ephemeral=True)
    #     sendLogging(self.client, f"## 신청곡 \n {value}")
    #     await interaction.response.send_message(f"## 정상적으로 신청곡 신청이 완료 되었습니다! 스태프에게 전달 해놓을게요~~")

    @app_commands.command(name="문의하기", description="운영팀에게 질문 할 수 있습니다.")
    async def donationSend(self, interaction: Interaction):
        # await interaction.response.send_message(f"현재 준비중 입니다.", ephemeral=True)
        view = question_btn()
        await interaction.response.send_message(f"## 질문 유의 사항 \n- 운영팀 모두가 보는 질문 입니다. \n- 문의 작성자와 운영팀이 있는 채널이 생성 됩니다.", ephemeral=True, view=view)

    @app_commands.command(name="문의_종료", description="질문 채널에서 종료가 가능 합니다.")
    async def testBoardList(self, interaction: Interaction):
        if not interaction.user.guild_permissions.administrator:
            await interaction.response.send_message("운영팀이 아닙니다.", ephemeral=True)
        try:
            await interaction.response.send_message(f"문의가 3초 뒤 종료 됩니다.")
            await asyncio.sleep(3)
            await interaction.channel.delete()
        except:
            pass

    # @app_commands.command(name='테스트')
    # async def adminTest(self, interaction: Interaction):
    #     if not interaction.user.guild_permissions.administrator:
    #         await interaction.response.send_message("운영팀이 아닙니다.", ephemeral=True)

    #     for user in interaction.guild.members:
    #         try:
    #             print(user.display_name.split('_'))
    #             name = user.display_name.split('_')
    #         except:
    #             pass
    #         db, SQL = get_SQL()
    #         if db == False:
    #             print("DB CONNECT ERROR")
    #         try:
    #             SQL.execute(
    #                 f"UPDATE USER_DATA SET DISCORD_ID = '{user.id}' WHERE USER_NAME = '{name[1]}' and SCHOOL_TYPE = '{name[0]}'"
    #             )
    #             db.commit()
    #             print(f'{name[0]} - {name[1]} 디스코드 아이디 업데이트 완료')
    #         except Exception as e:
    #             print(e)

async def setup(client: commands.Bot):
    await client.add_cog(users_cs(client))


import math

class Car:
# wheel base is the distance between front and back wheels, d is the distance between the back wheel and the back of the car, bumper is the length of the protrusion in the front of the car(The triangle)
# The center of the car is defined as the middle point of the two back wheels
  def __init__(self, wheelbase, d, length, width, bumper, center_x, center_y, orientation=0):
    self.geometry = CarGeometry(wheelbase, d, length, width, bumper)
    self.center_x = center_x
    self.center_y = center_y
    self.orientation = orientation

  def setPosAndOrient(self, CenterAndOrientInfo):
    self.orientation = CenterAndOrientInfo[1]
    l = (self.geometry.bumper + self.geometry.length) * 1.0/2 - self.geometry.d
    # l = 35
    self.center_x = CenterAndOrientInfo[0][0] - l * math.cos(self.orientation)
    self.center_y = CenterAndOrientInfo[0][1] - l * math.sin(self.orientation)

  def setPosAndOrientReverse(self, CenterAndOrientInfo):
    if CenterAndOrientInfo[1] >=0:
        self.orientation = CenterAndOrientInfo[1] - 3.14159
    else:
        self.orientation = CenterAndOrientInfo[1] + 3.14159
    l = (self.geometry.bumper + self.geometry.length) * 1.0/2 - self.geometry.d
    self.center_x = CenterAndOrientInfo[0][0] - l * math.cos(self.orientation)
    self.center_y = CenterAndOrientInfo[0][1] - l * math.sin(self.orientation)

  def getSize(self):
    return (self.geometry.length, self.geometry.width)

  def getPosAndOrient(self):
    return ((self.center_x, self.center_y), self.orientation)

  def getCenterAndOrient(self):
    l = (self.geometry.bumper + self.geometry.length) * 1.0/2 - self.geometry.d
    # l = 39
    return ((self.center_x +  l * math.cos(self.orientation), self.center_y + l * math.sin(self.orientation)), self.orientation)

  def getVertices(self):
    x1 = self.center_x - self.geometry.d * math.cos(self.orientation)
    y1 = self.center_y - self.geometry.d * math.sin(self.orientation)
    x2 = self.center_x + (self.geometry.length - self.geometry.d) * math.cos(self.orientation)
    y2 = self.center_y + (self.geometry.length - self.geometry.d) * math.sin(self.orientation)
    dx = self.geometry.width * math.sin(self.orientation)/2
    dy = self.geometry.width * math.cos(self.orientation)/2
    V = [(x1 + dx, y1 - dy), (x1 - dx, y1 + dy),
         (x2 - dx, y2 + dy), (x2 + self.geometry.bumper * math.cos(self.orientation), y2 + self.geometry.bumper * math.sin(self.orientation)), (x2 + dx, y2 - dy)]
    return V

  def update(self, action):
    theta = self.orientation
    phi = action[0]
    ds = action[1]
    dx = ds * math.cos(theta)
    dy = ds * math.sin(theta)
    dtheta = ds * math.tan(phi) / self.geometry.wheelbase

    self.center_x += dx
    self.center_y += dy
    self.orientation += dtheta
    if self.orientation > math.pi:
      self.orientation -= 2 * math.pi
    if self.orientation < -math.pi:
      self.orientation += 2 * math.pi

  def contains(self, v):
    # The true center of the car with speed bumper considered
    center_x_ = self.center_x + ((self.geometry.length + self.geometry.bumper)*1.0/2 - self.geometry.d) * math.cos(self.orientation)
    center_y_ = self.center_y + ((self.geometry.length + self.geometry.bumper)*1.0/2 - self.geometry.d) * math.sin(self.orientation)

    x, y = v
    x = x - center_x_
    y = y - center_y_
    x_ = math.cos(self.orientation) * x + math.sin(self.orientation) * y
    y_ = - math.sin(self.orientation) * x + math.cos(self.orientation) * y

    if (2 * abs(x_) < self.geometry.length + self.geometry.bumper) and (2 * abs(y_) < self.geometry.width):
        return 1
    else:
        return 0

# The copiedCar is used when searching, the only difference from car is that it they refer to a same geometry and thus save space
class CopiedCar(Car):
  def __init__(self, car):
    self.geometry = car.geometry
    self.center_x = car.center_x
    self.center_y = car.center_y
    self.orientation = car.orientation

class CarGeometry:
  def __init__(self, wheelbase, d, length, width, bumper):
    self.wheelbase = wheelbase
    self.d = d
    self.length = length
    self.width = width
    self.bumper = bumper


import naver_shopping.stage_naver_shopping as ns
import libs.naver_shopping.parser as par
import sys
# GUI 를 구성하기 위한 라이브러리
from PyQt5.QtWidgets import *
# GUI 에 아이콘을 넣기 위한 라이브러리
from PyQt5.QtGui import QIcon
# UI 가 없는 응용프로그램의 상태를 조작하기 위한 라이브러리
from PyQt5.QtCore import QCoreApplication

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 조건에 따른 라디오 버튼 및 그룹 생성
        global choose

        self.radioButton1 = QRadioButton('기본')
        self.radioButton1.setChecked(True)
        self.radioButton1.clicked.connect(self.radioFunction)
        self.radioButton2 = QRadioButton('낮은 가격순')
        self.radioButton2.clicked.connect(self.radioFunction)
        self.radioButton3 = QRadioButton('등록일순')
        self.radioButton3.clicked.connect(self.radioFunction)
        self.radioButton4 = QRadioButton('리뷰 많은순')
        self.radioButton4.clicked.connect(self.radioFunction)

        self.vBox = QVBoxLayout()
        self.vBox.addWidget(self.radioButton1)
        self.vBox.addWidget(self.radioButton2)
        self.vBox.addWidget(self.radioButton3)
        self.vBox.addWidget(self.radioButton4)

        self.radioGroup = QGroupBox('조건선택', self)
        self.radioGroup.setLayout(self.vBox)

        # 검색어 입력칸 생성
        self.inputKeyword = QLineEdit()

        # 검색어 출력칸 생성
        self.outputKeyword = QTextBrowser()
        self.outputKeyword.setAcceptRichText(True)
        self.outputKeyword.setOpenExternalLinks(True)

        # 개발자 정보칸 생성
        self.infoKeyword = QTextBrowser()
        self.infoKeyword.setAcceptRichText(True)
        self.infoKeyword.setOpenExternalLinks(True)

        info1 = "개발자 메일 : wjehd9@naver.com\n"
        info2 = "<다음과 같은 순서를 반드시 지켜주세요!>\n"
        info3 = "1. 저장할 파일의 위치 지정을 먼저합니다.\n"
        info4 = "2. 검색어에 원하는 내용을 입력합니다.\n"
        info5 = "3. 검색할 조건을 지정합니다.\n"
        info6 = "4. 검색 및 저장 버튼을 누릅니다.\n"
        info7 = "# 초기화 버튼으로 연관 검색어 기록을 없앨 수 있습니다.\n"
        info8 = "사용 환경에 따라 멈춤 현상이 일어날 수 있습니다.\n"
        info9 = "기타 문의 사항은 위 메일로 보내주시기 바랍니다.\n"
        info10 = "*너무 자주 실행 시 네이버에서 DDOS로 인식될 수 있습니다.*\n"
        info11 = "제작일 : 2020.04.06\n"

        self.infoKeyword.append(info1)
        self.infoKeyword.append(info2)
        self.infoKeyword.append(info3)
        self.infoKeyword.append(info4)
        self.infoKeyword.append(info5)
        self.infoKeyword.append(info6)
        self.infoKeyword.append(info7)
        self.infoKeyword.append(info8)
        self.infoKeyword.append(info9)
        self.infoKeyword.append(info10)
        self.infoKeyword.append(info11)

        # Quit 버튼 생성
        self.quitButton = QPushButton('종료', self)
        self.quitButton.resize(self.quitButton.sizeHint())
        self.quitButton.clicked.connect(QCoreApplication.instance().quit)

        # Search 버튼 생성
        self.searchButton = QPushButton('검색 및 저장', self)
        self.searchButton.resize(self.searchButton.sizeHint())
        self.searchButton.pressed.connect(self.search_object)
        self.searchButton.setEnabled(False)

        # clear 버튼 생성
        self.clearButton = QPushButton('초기화', self)
        self.clearButton.resize(self.clearButton.sizeHint())
        self.clearButton.pressed.connect(self.clear_object)

        # 저장 버튼 생성
        self.saveButton = QPushButton('위치 지정', self)
        self.saveButton.resize(self.saveButton.sizeHint())
        self.saveButton.pressed.connect(self.save_directory)

        # Grid 레이아웃 생성
        self.grid = QGridLayout()
        self.setLayout(self.grid)

        self.grid.addWidget(QLabel('검색어'), 0, 0)
        self.grid.addWidget(QLabel('검색 조건'), 1, 0)
        self.grid.addWidget(QLabel('연관 검색어'), 2, 0)
        self.grid.addWidget(self.quitButton, 3, 0)

        self.grid.addWidget(self.inputKeyword, 0, 1)
        self.grid.addWidget(self.radioGroup, 1, 1)
        self.grid.addWidget(self.outputKeyword, 2, 1)
        self.grid.addWidget(self.searchButton, 3, 1)

        self.grid.addWidget(self.saveButton, 0, 2)
        self.grid.addWidget(self.infoKeyword, 1, 2, 2, 2)
        self.grid.addWidget(self.clearButton, 3, 2)

        # GUI 의 크기와 위치, 이름을 정의
        self.setWindowTitle('Naver Shopping Crawler (made by Aerotic)')
        self.setWindowIcon(QIcon('A.png'))
        self.setGeometry(300, 300, 700, 500)
        self.show()

    def radioFunction(self):
        if self.radioButton1.isChecked():
            self.choose = 1
        if self.radioButton2.isChecked():
            self.choose = 2
        if self.radioButton3.isChecked():
            self.choose = 3
        if self.radioButton4.isChecked():
            self.choose = 4

    def search_object(self):
        self.outputKeyword.clear()
        text = self.inputKeyword.text()
        condition = self.choose
        ns.doing(text, condition, position)
        for p in par.relation:
            self.outputKeyword.append(p)
        self.inputKeyword.clear()

    def clear_object(self):
        self.outputKeyword.clear()

    def save_directory(self):
        global position
        position = QFileDialog.getExistingDirectory(self, self.tr("Open"), "./", QFileDialog.ShowDirsOnly)
        self.searchButton.setEnabled(True)

# __main__ :  현재 모듈의 이름이 저장되는 내장 변수
# 다른 파일의 코드를 가져오기 위해서는 import 작업이 우선되어야 한다.
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

    # pyinstaller -w -F --icon=test.ico --onefile test.py


from aiogram import types
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery

from data.peewee import Miner
from loader import dp

def settings_kb():
    buttons = [
        InlineKeyboardButton(text='🔊Уведомления', callback_data='🔊'),
        InlineKeyboardButton(text='🤖Другое', callback_data='🤖')
    ]
    keyboard = InlineKeyboardMarkup(row_width=3)
    keyboard.add(*buttons)
    return keyboard

def notifications(minerid):
    miner = Miner.get(minerid=minerid)

    notify_balance = 'О бунтах и забастовках'
    notify_courses = 'О смене курса'
    notify_reset = 'О перезапуске бота'

    if miner.notify_balance is True:
        notify_balance += '✅'
    elif miner.notify_balance is False:
        notify_balance += '❌'
    if miner.notify_courses is True:
        notify_courses += '✅'
    elif miner.notify_courses is False:
        notify_courses += '❌'
    if miner.notify_reset is True:
        notify_reset += '✅'
    elif miner.notify_reset is False:
        notify_reset += '❌'

    buttons = [
        InlineKeyboardButton(text=notify_balance, callback_data='balance_end_notify'),
        InlineKeyboardButton(text=notify_courses, callback_data='change_courses_notify'),
        InlineKeyboardButton(text=notify_reset, callback_data='reset_bot_notify'),
        InlineKeyboardButton(text='Назад', callback_data='in_settings'),
    ]
    keyboard = InlineKeyboardMarkup(row_width=1)
    keyboard.add(*buttons)
    return keyboard

def other_settings(minerid):
    miner = Miner.get(minerid=minerid)
    fast_sell_text = 'Быстрая продажа'

    if miner.fast_sell is True:
        fast_sell_text += '✅'
    elif miner.fast_sell is False:
        fast_sell_text += '❌'

    buttons = [
        InlineKeyboardButton(text=fast_sell_text, callback_data='fast_sell_activate'),
        InlineKeyboardButton(text='Назад', callback_data='in_settings'),
    ]
    keyboard = InlineKeyboardMarkup(row_width=1)
    keyboard.add(*buttons)
    return keyboard

@dp.message_handler(text='⚙ Настройки')
async def settings(message: types.Message):
    await message.answer(text='⚙Открываем настройки...', reply_markup=settings_kb())

@dp.callback_query_handler(text='🔊')
async def notify(call: CallbackQuery):
    miner = Miner.get(minerid=call.from_user.id)
    await call.message.edit_text(text='Нажмите на кнопку, чтобы настроить уведомления: ', reply_markup=notifications(miner.minerid))

@dp.callback_query_handler(text='balance_end_notify')
async def notify(call: CallbackQuery):
    miner = Miner.get(minerid=call.from_user.id)
    if miner.notify_balance is False:
        miner.notify_balance = True
        miner.save()
    elif miner.notify_balance is True:
        miner.notify_balance = False
        miner.save()
    await call.message.edit_text(text='Изменения баланса приняты. Нажмите на кнопку, чтобы настроить уведомления',
                           reply_markup=notifications(miner.minerid))

@dp.callback_query_handler(text='change_courses_notify')
async def notify(call: CallbackQuery):
    miner = Miner.get(minerid=call.from_user.id)
    if miner.notify_courses is False:
        miner.notify_courses = True
        miner.save()
    elif miner.notify_courses is True:
        miner.notify_courses = False
        miner.save()
    await call.message.edit_text(text='Изменения курса приняты. Нажмите на кнопку, чтобы настроить уведомления',
                           reply_markup=notifications(miner.minerid))

@dp.callback_query_handler(text='reset_bot_notify')
async def notify(call: CallbackQuery):
    miner = Miner.get(minerid=call.from_user.id)
    if miner.notify_reset is False:
        miner.notify_reset = True
        miner.save()
    elif miner.notify_reset is True:
        miner.notify_reset = False
        miner.save()
    await call.message.edit_text(text='Изменения перезапуска приняты. Нажмите на кнопку, чтобы настроить уведомления',
                           reply_markup=notifications(miner.minerid))

@dp.callback_query_handler(text='🤖')
async def notify(call: CallbackQuery):
    await call.message.edit_text(text='Нажмите на кнопку, чтобы применить настройку: ',
                                 reply_markup=other_settings(call.from_user.id))

@dp.callback_query_handler(text='fast_sell_activate')
async def activate(call: CallbackQuery):
    miner = Miner.get(minerid=call.from_user.id)
    if miner.fast_sell is False:
        miner.fast_sell = True
        miner.save()
    elif miner.fast_sell is True:
        miner.fast_sell = False
        miner.save()
    await call.message.edit_text(text='Кнопка Быстрая продажа добавлена в раздел ⛏Добыча. '
                                      'Нажмите на кнопку, чтобы применить настройку:',
                                 reply_markup=other_settings(call.from_user.id))

@dp.callback_query_handler(text='in_settings')
async def back(call: CallbackQuery):
    await call.message.edit_text(text='⚙ Вернулись...', reply_markup=settings_kb())