import sys
from os import walk

import pygame as pg
import pygame.image

from camera import Camera
from events import save_event, load_event
from joueur import Joueur
from resource_manager import ResourceManager
from unite import Villageois
from unite import Unite
from buildings import Batiment
from animal import Animal
from utils import draw_text, scale_image
from bouton import Button, ButtonVide

class Hud:

    def __init__(self, resource_manager: ResourceManager, width, height, nb_joueur):

        self.resource_manager = resource_manager

        self.width = width
        self.height = height
        self.hud_colour = (198, 155, 93, 175)

        self.hud_haut_surface = pg.Surface((width * 0.484, height * 0.08), pg.SRCALPHA).convert_alpha()
        self.hud_haut_rect = self.hud_haut_surface.get_rect(topleft=(0, 0))
        # self.hud_haut_surface.fill(self.hud_colour)
        self.hud_haut = pg.image.load("assets/hud/hud_haut.png").convert_alpha()

        self.hud_age_surface = pg.Surface((width * 0.30, height * 0.08), pg.SRCALPHA).convert_alpha()
        self.hud_age2_surface = pg.Surface((width * 0.30, height * 0.08), pg.SRCALPHA).convert_alpha()
        self.hud_age3_surface = pg.Surface((width * 0.30, height * 0.08), pg.SRCALPHA).convert_alpha()
        self.hud_age_rect = self.hud_age_surface.get_rect(topleft=(self.width - 290, 0))
        self.hud_age2_rect = self.hud_age2_surface.get_rect(topleft=(self.width - 290, 0))
        self.hud_age3_rect = self.hud_age3_surface.get_rect(topleft=(self.width - 290, 0))
        # self.hud_age_surface.fill(self.hud_colour)
        self.hud_age = pg.image.load("assets/hud/hud_age.png").convert_alpha()
        self.hud_age2 = pg.image.load("assets/hud/hud_age2.png").convert_alpha()
        self.hud_age3 = pg.image.load("assets/hud/hud_age3.png").convert_alpha()

        self.hud_action_surface = pg.Surface((width * 0.35, height * 0.29), pg.SRCALPHA).convert_alpha()
        self.hud_action_rect = self.hud_action_surface.get_rect(topleft=(self.width - 413, self.height - 205))
        # self.hud_action_surface.fill(self.hud_colour)
        self.hud_action = pg.image.load("assets/hud/hud_action.png").convert_alpha()

        self.hud_info_surface = pg.Surface((width * 0.6, height * 0.29), pg.SRCALPHA).convert_alpha()
        self.hud_info_rect = self.hud_info_surface.get_rect(topleft=(self.width - 1180, self.height - 205))
        # self.hud_info_surface.fill(self.hud_colour)
        self.hud_info = pg.image.load("assets/hud/hud_info.png").convert_alpha()

        # diplomatie
        self.diplo_surface = pg.Surface((450, 100 * nb_joueur + 50), pg.SRCALPHA)
        self.diplo_surface.fill((255, 255, 255))
        self.option_diplo_bouton = [[Button(None, self.width / 2 - 225 + 300,
                                            self.height / 2 - self.diplo_surface.get_height() / 2 - 50 + 25 + 25 + 100 * i,
                                            'Allié'),
                                     Button(None, self.width / 2 - 225 + 300,
                                            self.height / 2 - self.diplo_surface.get_height() / 2 - 50 + 25 + 50 + 100 * i,
                                            'Neutre'),
                                     Button(None, self.width / 2 - 225 + 300,
                                            self.height / 2 - self.diplo_surface.get_height() / 2 - 50 + 25 + 75 + 100 * i,
                                            'Ennemi')] for i in range(nb_joueur)]

        # pause
        self.pause_surface = pg.image.load("assets/hud/pause.png")
        self.save_button = ButtonVide(None, (self.width / 2) - 249 + 115, (self.height / 2) - 349 + 206, 'inv', 264, 48)
        self.load_button = ButtonVide(None, (self.width / 2) - 249 + 161, (self.height / 2) - 349 + 336, 'inv', 171, 48)
        self.continue_button = ButtonVide(None, (self.width / 2) - 249 + 143, (self.height / 2) - 349 + 461, 'inv', 210, 48)
        self.exit_button = ButtonVide(None, (self.width / 2) - 249 + 171, (self.height / 2) - 349 + 589, 'inv', 149, 48)

        self.images = self.load_images()
        self.tiles = self.create_build_hud()
        self.images_examined = self.load_images_examined()
        self.images_terre = self.load_image_terre()

        self.tp_villageois = [Button(None, self.hud_haut.get_width() // 5.8 * (i + 1) - 80, 7, 'inv') for i in range(5)]
        self.tp_villageois[4].x += 110

        self.selected_tile = None
        self.examined_tile = None
        self.unite_recrut = None
        self.action_age = None
        self.can_pass_age = None

        self.villageois_bouton = Button((0, 255, 0), self.width - 550, self.height - 100, 'villageois_recrut')
        self.age_feodal_bouton = Button((0, 255, 0), self.width - 500, self.height - 100, 'age_feodal')
        self.age_castel_bouton = Button((0, 255, 0), self.width - 500, self.height - 150, 'age_castle')
        self.clubman_bouton = Button((0, 255, 0), self.width - 550, self.height - 100, 'clubman_recrut')

        self.diplo_bouton = Button(None, self.hud_haut.get_width() + 30, 10, 'diplomatie')

        self.diplo_actif = False
        self.pause = False

    def create_build_hud(self):

        render_pos = [self.hud_action_rect.x + 30, self.hud_action_rect.y + 40]
        # 1280 / self.width
        # (867 * 1.035 / self.hud_action_rect.x)
        # (515 * 1.08 / self.hud_action_rect.y)
        object_width = self.hud_action_surface.get_width() // 15

        self.hud_haut_surface.blit(self.hud_haut, (0, 0))
        self.hud_age_surface.blit(self.hud_age, (0, 0))
        self.hud_age2_surface.blit(self.hud_age2, (0, 0))
        self.hud_age3_surface.blit(self.hud_age3, (0, 0))
        self.hud_action_surface.blit(self.hud_action, (0, 0))
        self.hud_info_surface.blit(self.hud_info, (0, 0))

        tiles = []
        i = 0
        for image_name, image in self.images.items():
            i += 1
            pos = render_pos.copy()
            image_tmp = image.copy()
            image_scale = scale_image(image_tmp, w=object_width)
            rect = image_scale.get_rect(topleft=pos)

            tiles.append(
                {
                    "name": image_name,
                    "icon": image_scale,
                    "image": self.images[image_name],
                    "rect": rect,
                    "affordable": True
                }
            )

            if i % 3 == 0:
                render_pos[0] += image_scale.get_width() + 9 * 1280 / self.width

        return tiles

    def update(self, joueurs: list[Joueur], camera: Camera, world):

        mouse_pos = pg.mouse.get_pos()
        mouse_action = pg.mouse.get_pressed(3)

        if self.examined_tile is not None:
            if self.villageois_bouton.is_over(mouse_pos) and self.villageois_bouton.can_press and \
                    not self.villageois_bouton.is_press:
                if mouse_action[0]:
                    self.unite_recrut = self.villageois_bouton.text[:-7]
                    self.villageois_bouton.is_press = True

            if self.clubman_bouton.is_over(mouse_pos) and self.clubman_bouton.can_press and \
                    not self.clubman_bouton.is_press:

                if mouse_action[0]:
                    self.unite_recrut = self.clubman_bouton.text[:-7]
                    self.clubman_bouton.is_press = True
            if self.villageois_bouton.is_press and not mouse_action[0]:
                self.villageois_bouton.is_press = False
            if self.clubman_bouton.is_press and not mouse_action[0]:
                self.clubman_bouton.is_press = False

            if isinstance(self.examined_tile, Batiment):
                if self.age_feodal_bouton.is_over(mouse_pos) and not self.age_feodal_bouton.is_press and\
                        self.examined_tile.name == "hdv":
                    if mouse_action[0]:
                        self.action_age = "feodal"
                        self.age_feodal_bouton.is_press = True
                elif self.age_castel_bouton.is_over(mouse_pos) and not self.age_castel_bouton.is_press and\
                        self.examined_tile.name == "hdv":
                    if mouse_action[0]:
                        self.action_age = "castle"
                        self.age_castel_bouton.is_press = True
                else:
                    self.action_age = None

        if mouse_action[0] and self.diplo_bouton.is_over(mouse_pos) and not self.diplo_bouton.is_press:
            self.diplo_actif = not self.diplo_actif
            self.diplo_bouton.is_press = True

        if mouse_action[2]:
            self.selected_tile = None

        for tile in self.tiles:
            if self.resource_manager.is_affordable(tile["name"][:-1]):
                tile["affordable"] = True
            else:
                tile["affordable"] = False
            if tile["rect"].collidepoint(mouse_pos) and tile["affordable"]:
                if mouse_action[0]:
                    self.selected_tile = tile

        if camera:
            for i in range(5):
                if mouse_action[0] and self.tp_villageois[i].is_over(mouse_pos) and \
                        not self.tp_villageois[i].is_press:
                    self.tp_villageois[i].is_press = True
                    camera.tp_villageois(joueurs[0], i, world, self)
                if self.tp_villageois[i].is_press and not mouse_action[0]:
                    self.tp_villageois[i].is_press = False

        if self.diplo_bouton.is_press and not mouse_action[0]:
            self.diplo_bouton.is_press = False

        if self.diplo_actif:
            for i in range(len(self.option_diplo_bouton)):
                for j in range(3):
                    if mouse_action[0] and self.option_diplo_bouton[i][j].is_over(mouse_pos) and \
                            not self.option_diplo_bouton[i][j].is_press:
                        self.option_diplo_bouton[i][j].is_press = True
                        if j == 0:
                            joueurs[0].diplomatie[i + 1] = "allié"
                            joueurs[i + 1].diplomatie[0] = "allié"
                        elif j == 1:
                            joueurs[0].diplomatie[i + 1] = "neutre"
                            joueurs[i + 1].diplomatie[0] = "neutre"
                        else:
                            joueurs[0].diplomatie[i + 1] = "ennemi"
                            joueurs[i + 1].diplomatie[0] = "ennemi"

                    if self.option_diplo_bouton[i][j].is_press and not mouse_action[0]:
                        self.option_diplo_bouton[i][j].is_press = False

    def update_pause(self):

        mouse_pos = pg.mouse.get_pos()
        mouse_action = pg.mouse.get_pressed(3)

        if mouse_action[0] and self.save_button.is_over(mouse_pos) and not self.save_button.is_press:
            pygame.time.set_timer(save_event, 10, loops=1)
            self.save_button.is_press = True

        if self.save_button.is_press and not mouse_action[0]:
            self.save_button.is_press = False

        if mouse_action[0] and self.load_button.is_over(mouse_pos) and not self.load_button.is_press:
            pygame.time.set_timer(load_event, 10, loops=1)
            self.load_button.is_press = True

        if self.load_button.is_press and not mouse_action[0]:
            self.load_button.is_press = False

        if mouse_action[0] and self.continue_button.is_over(mouse_pos) and not self.continue_button.is_press:
            self.pause = False
            self.continue_button.is_press = True

        if self.continue_button.is_press and not mouse_action[0]:
            self.continue_button.is_press = False

        if mouse_action[0] and self.exit_button.is_over(mouse_pos) and not self.exit_button.is_press:
            pygame.quit()
            sys.exit()

    def draw(self, screen, joueurs: list[Joueur]):
        mouse_pos = pg.mouse.get_pos()

        screen.blit(self.hud_haut_surface, (0, 0))
        if joueurs[0].age.numero == "1":
            screen.blit(self.hud_age_surface, (self.width - 290, 0))
        elif joueurs[0].age.numero == "2":
            screen.blit(self.hud_age2_surface, (self.width - 290, 0))
        else:
            screen.blit(self.hud_age3_surface, (self.width - 290, 0))

        screen.blit(self.hud_action_surface, (self.width - 413, self.height - 205))
        # a voir
        draw_text(screen, str(len(joueurs[0].resource_manager.villageois["wood"])), 20, "#ffffff",
                  (self.hud_haut_rect.bottomleft[0] + 45, self.hud_haut_rect.bottomleft[1]-45))
        draw_text(screen, str(len(joueurs[0].resource_manager.villageois["food"])), 20, "#ffffff",
                  (self.hud_haut_rect.bottomleft[0] + 44 + 110, self.hud_haut_rect.bottomleft[1]-45))
        draw_text(screen, str(len(joueurs[0].resource_manager.villageois["gold"])), 20, "#ffffff",
                  (self.hud_haut_rect.bottomleft[0] + 43 + 220, self.hud_haut_rect.bottomleft[1]-45))
        draw_text(screen, str(len(joueurs[0].resource_manager.villageois["stone"])), 20, "#ffffff",
                  (self.hud_haut_rect.bottomleft[0] + 42 + 330, self.hud_haut_rect.bottomleft[1]-45))
        draw_text(screen, str(len(joueurs[0].resource_manager.villageois["rien"])), 20, "#ffffff",
                  (self.hud_haut_rect.bottomleft[0] + 42 + 530, self.hud_haut_rect.bottomleft[1]-45))

        self.diplo_bouton.draw(screen)

        if self.diplo_actif:
            # diplomatie
            screen.blit(self.diplo_surface,
                        (self.width / 2 - 225, self.height / 2 - self.diplo_surface.get_height() / 2 - 50))
            draw_text(screen, "Diplomatie", 50, "#ff0000",
                      (self.width / 2 - 225 + 125, self.height / 2 - self.diplo_surface.get_height() / 2 - 50 + 10))
            for i in range(1, len(joueurs)):
                draw_text(screen, joueurs[i].name + " :", 25, "#000000",
                          (self.width / 2 - 225 + 25,
                           self.height / 2 - self.diplo_surface.get_height() / 2 - 100 + 25 + i * 100))
            for i in range(1, len(joueurs)):
                if joueurs[0].diplomatie[i] == "ennemi":
                    color = "#ff0000"
                elif joueurs[0].diplomatie[i] == "allié":
                    color = "#228B22"
                else:
                    color = "#000000"
                draw_text(screen, joueurs[0].diplomatie[i], 25, color,
                          (self.width / 2 - 225 + 175, self.height / 2 -
                           self.diplo_surface.get_height() / 2 - 100 + 25 + i * 100))
            for boutons in self.option_diplo_bouton:
                for bouton in boutons:
                    bouton.draw(screen)

        if self.pause:
            screen.blit(self.pause_surface, (self.width / 2 - 249, self.height / 2 - 349))

        # si un objet est selectionné
        if self.examined_tile is not None:
            screen.blit(self.hud_info_surface, (self.width - 1180, self.height - 205))

            # affichage de l'image du batiment avec son nom et son nombre de vie
            if isinstance(self.examined_tile, Batiment) or isinstance(self.examined_tile, Unite) or \
                    isinstance(self.examined_tile, Animal):
                name_image = self.examined_tile.name + joueurs[0].age.numero if isinstance(self.examined_tile, Batiment) \
                    else self.examined_tile.name
                img = self.images_examined[name_image].convert_alpha()
                draw_text(screen, self.examined_tile.name, 50, "#ff0000",
                          (self.hud_info_rect.midtop[0], self.hud_info_rect.midtop[1] + 40))
                draw_text(screen, str(self.examined_tile.health), 30, (255, 255, 255),
                          (self.hud_info_rect.center[0], self.hud_info_rect.center[1]))
                if isinstance(self.examined_tile, Animal):
                    draw_text(screen, str(self.examined_tile.ressource), 30, (255, 255, 255),
                              (self.hud_info_rect.center[0], self.hud_info_rect.center[1] + 20))

                if self.examined_tile is not None and self.examined_tile.name == "hdv" and \
                        self.examined_tile.joueur.name == "joueur 1":
                    self.clubman_bouton.can_press = False
                    if self.resource_manager.stay_place():
                        self.villageois_bouton.draw(screen)
                        self.villageois_bouton.can_press = True

                    if self.examined_tile.joueur.age.name == "sombre":
                        self.age_feodal_bouton.draw(screen)
                    if self.examined_tile.joueur.age.name == "feodal":
                        self.age_castel_bouton.draw(screen)

                if self.examined_tile is not None and self.examined_tile.name == "caserne" and \
                        self.examined_tile.joueur.name == "joueur 1" and self.examined_tile.construit:
                    self.villageois_bouton.can_press = False
                    if self.resource_manager.stay_place():
                        self.clubman_bouton.draw(screen)
                        self.clubman_bouton.can_press = True

            else:
                if self.examined_tile["tile"]:
                    img = self.images_terre[
                        self.examined_tile["tile"] + "_" + str(self.examined_tile["frame"]) + ".png"].convert_alpha()
                    draw_text(screen, str(self.examined_tile["ressource"]), 30, (255, 255, 255),
                              (self.hud_info_rect.center[0], self.hud_info_rect.center[1]))
                    draw_text(screen, str(self.examined_tile["tile"]), 50, (0, 255, 255),
                              (self.hud_info_rect.center[0] - 50, self.hud_info_rect.center[1] - 90))
                else:
                    img = None
            if img:
                screen.blit(img, (self.width - 1150, self.height - 205 + 40))

            if isinstance(self.examined_tile, Villageois):
                draw_text(screen, str(round(self.examined_tile.stockage)), 30, (255, 255, 255),
                          (self.hud_info_rect.center[0], self.hud_info_rect.center[1] - 20))

        for tile in self.tiles:
            if tile["name"][-1:] == joueurs[0].age.numero:
                icon = tile["icon"].copy()
                if not tile["affordable"]:
                    icon.set_alpha(100)
                screen.blit(icon, tile["rect"].topleft)

                if tile["rect"].collidepoint(mouse_pos):
                    ressource = self.resource_manager.get_cost(tile["name"][:-1])
                    pygame.draw.rect(screen, (255, 255, 255),
                                     pygame.Rect(mouse_pos[0], mouse_pos[1] - len(ressource) * 40, 150, len(ressource)*40))
                    pos = (mouse_pos[0]+10, mouse_pos[1] - len(ressource) * 40 + 10)
                    for cle, valeur in ressource.items():
                        if self.resource_manager.resources[cle] >= valeur:
                            color = (0, 255, 0)
                        else:
                            color = (255, 0, 0)
                        draw_text(screen, '{} : {}'.format(cle, valeur), 30, color, pos)
                        pos = (pos[0], pos[1] + 40)

        pos = 75
        for resource, resource_value in self.resource_manager.resources.items():
            txt = str(resource_value)
            draw_text(screen, txt, 30, (255, 255, 255), (pos, 20))
            pos += 110
        txt_units = str(self.resource_manager.population["population_actuelle"]) + "/" + str(
            self.resource_manager.population["population_maximale"])
        draw_text(screen, txt_units, 30, (255, 255, 255), (pos, 20))

    def load_images(self):
        caserne1 = pygame.image.load("assets/batiments/caserne.png").convert_alpha()
        caserne2 = pygame.image.load("assets/batiments/caserne2.png").convert_alpha()
        caserne3 = pygame.image.load("assets/batiments/caserne3.png").convert_alpha()
        grenier1 = pygame.image.load("assets/batiments/strorage.png").convert_alpha()
        grenier2 = pygame.image.load("assets/batiments/storage2.png").convert_alpha()
        grenier3 = pygame.image.load("assets/batiments/storage3.png").convert_alpha()
        house1 = pygame.image.load("assets/batiments/house.png").convert_alpha()
        house2 = pygame.image.load("assets/batiments/house2.png").convert_alpha()
        house3 = pygame.image.load("assets/batiments/house3.png").convert_alpha()
        tower1 = pg.image.load("assets/batiments/tower1.png").convert_alpha()
        tower2 = pg.image.load("assets/batiments/tower2.png").convert_alpha()
        tower3 = pg.image.load("assets/batiments/tower3.png").convert_alpha()

        images = {
            "caserne1": caserne1,
            "caserne2": caserne2,
            "caserne3": caserne3,
            "house1": house1,
            "house2": house2,
            "house3": house3,
            "grenier1": grenier1,
            "grenier2": grenier2,
            "grenier3": grenier3,
            "tower1": tower1,
            "tower2": tower2,
            "tower3": tower3,
        }

        return images

    def load_images_examined(self):
        caserne1 = pygame.image.load("assets/batiments/caserne.png").convert_alpha()
        caserne2 = pygame.image.load("assets/batiments/caserne2.png").convert_alpha()
        caserne3 = pygame.image.load("assets/batiments/caserne3.png").convert_alpha()
        grenier1 = pygame.image.load("assets/batiments/strorage.png").convert_alpha()
        grenier2 = pygame.image.load("assets/batiments/storage2.png").convert_alpha()
        grenier3 = pygame.image.load("assets/batiments/storage3.png").convert_alpha()
        hdv1 = pygame.image.load("assets/batiments/hdv.png").convert_alpha()
        hdv2 = pygame.image.load("assets/batiments/hdv2.png").convert_alpha()
        hdv3 = pygame.image.load("assets/batiments/hdv3.png").convert_alpha()
        house1 = pygame.image.load("assets/batiments/house.png").convert_alpha()
        house2 = pygame.image.load("assets/batiments/house2.png").convert_alpha()
        house3 = pygame.image.load("assets/batiments/house3.png").convert_alpha()
        tower1 = pg.image.load("assets/batiments/tower1.png").convert_alpha()
        tower2 = pg.image.load("assets/batiments/tower2.png").convert_alpha()
        tower3 = pg.image.load("assets/batiments/tower3.png").convert_alpha()
        villageois = pygame.image.load("assets/hud/examined_title/villageois.png").convert_alpha()
        gazelle = pygame.image.load("assets/hud/examined_title/gazelle.png").convert_alpha()
        gazelle_mort = pygame.image.load("assets/hud/examined_title/gazelle_mort.png").convert_alpha()
        clubman = pygame.image.load("assets/hud/examined_title/clubman.png").convert_alpha()
        bigdaddy = pygame.image.load("assets/hud/examined_title/bigdaddy.png").convert_alpha()

        images = {
            "caserne1": caserne1,
            "caserne2": caserne2,
            "caserne3": caserne3,
            "grenier1": grenier1,
            "grenier2": grenier2,
            "grenier3": grenier3,
            "hdv1": hdv1,
            "hdv2": hdv2,
            "hdv3": hdv3,
            "house1": house1,
            "house2": house2,
            "house3": house3,
            "tower1": tower1,
            "tower2": tower2,
            "tower3": tower3,
            "villageois": villageois,
            "gazelle": gazelle,
            "gazelle_mort": gazelle_mort,
            "clubman": clubman,
            "bigdaddy": bigdaddy,
        }

        w, h = self.hud_info_rect.width, self.hud_info_rect.height
        for i in images:
            images[i] = scale_image(images[i], h=h * 0.7)

        return images

    def load_image_terre(self):
        images = {}
        for (repertoire, sousRepertoires, fichiers) in walk("assets/ressource"):
            for nom in fichiers:
                image = pygame.image.load(repertoire + "/" + nom).convert_alpha()
                rect = image.get_rect(topleft=(0, 0))
                images[nom] = scale_image(image, h=rect.height * 1.8, w=rect.width * 1.8)

        return images

    def end(self, end, screen):
        if end == 1:
            image = pygame.image.load("assets/hud/victoire.png").convert_alpha()
        else:
            image = pygame.image.load("assets/hud/defaite.png").convert_alpha()
        screen.blit(image, (self.width / 2 - image.get_width() / 2, self.height / 2 - image.get_height() / 2))
        pygame.display.flip()


import os

from utils import parse_xml

def copy_folder_structure(source_dir: str, destination_dir: str) -> None:
    """Создает исходную структуру папок в новой папке."""
    for root, _, files in os.walk(source_dir):
        relative_path = os.path.relpath(root, source_dir)
        new_dir = os.path.join(destination_dir, relative_path)
        os.makedirs(new_dir, exist_ok=True)
        for file in files:
            filename, extension = os.path.splitext(file)
            if extension.lower() == '.xml':
                source_file = os.path.join(root, file)
                destination_file = os.path.join(new_dir, f'{filename}.txt')
                parse_xml(source_file, destination_file)

if __name__ == '__main__':
    source_dir = 'data'
    destination_dir = 'output'
    copy_folder_structure(source_dir, destination_dir)


import xlrd
import random
from random import shuffle
from random import randint

best_value_arr = []
best_route = []
savedIteration = 0
best_value = 10000
something = False
population = 10
cities_List = [2, 3, 4, 5, 6, 7, 8]
#creating a 2d array and filling it with 'x'
rand_pop = [['x' for i in range(7)] for j in range(10)]
#when getting the values the first item is row and the 2nd is column
data = xlrd.open_workbook('/Users/prajwalshrestha/Desktop/travelling_salesman/travelling_sales.xlsx')
worksheet = data.sheet_by_name('Sheet1')
#print(worksheet.cell(1,0).value)
#print(data.sheet_names())
for iteration in range(0,2000):
#function to calculate the initial total of the first generation population
	def calc_init_distance():
		#array to store the total distances calculated
		dist_Total = []
		#dict to store the total distance and array which holds the path it tool
		keyValue = {}
		#dict which is sorted according to the distances
		sortedKeyVal = {}
		#for loop calculate the distances from the array that is provided
		for i in range(0,population):
			total = 0
			for j in range (0,7):
				#to prevent Index Error
				if(j + 1 < len(rand_pop[1])):
					total = total + worksheet.cell(rand_pop[i][j],rand_pop[i][j + 1]).value
			#adding the distance of london to the first value of the rand_pop array because every journey starts from london and
			#also adding the distance from last destination to london
			lonToX = worksheet.cell(1, rand_pop[i][0]).value
			#storing the distance from x to london as the journey needs to end in london
			XtoLon = worksheet.cell(rand_pop[i][6],1).value
			#adding the distances from london to x and x to london
			total += (lonToX + XtoLon)
			keyValue[total] = rand_pop[i]
			#print(total)
			#creating an array to store the total distances calculated for each array in the rand_pop array
		topFiveDist = 0
		#loop that only gets the first five of the key:value pair list
		for key in sorted(keyValue.keys()):
			if(topFiveDist < 5):
				sortedKeyVal[key] = keyValue[key]
			else:
				break
			topFiveDist += 1
		best_value_arr.append(min(int(key) for key in sortedKeyVal))
		best_value = min(best_value_arr)
		if(iteration == 1999):
			print('best_value = ' + str(best_value))
		if(sortedKeyVal.get(best_value) != None):
			global string
			global best_route
			best_route = sortedKeyVal.get(best_value)
			print('Best route = ' + str(sortedKeyVal.get(best_value)))
		if iteration == 1999:
			print('The best route to take is: London -> ' + str(worksheet.cell(0, best_route[0]).value) + ' -> ' + str(worksheet.cell(0,best_route[1]).value) + ' -> ' + str(worksheet.cell(0,best_route[2]).value) + ' -> ' + str(worksheet.cell(0,best_route[3]).value) + ' -> ' + str(worksheet.cell(0,best_route[4]).value) + ' -> ' + str(worksheet.cell(0,best_route[5]).value) + ' -> ' + str(worksheet.cell(0,best_route[6]).value) + ' -> London')

	def mutation():
		counter = 0
		mutation_rate = 0.05
		mutated = []
		#the key:value pair list from stored in a different variable as a duplicate
		keyValuePair = calc_init_distance()
		#the values for the keyValuePair dict stored in a list
		for values in keyValuePair.values():
			mutated.append(values)
		#for loop to make sure that all the arrays go through a check for mutation
		for x in range(0,len(keyValuePair.values())-1):
			#random value created to check if the mutation is to be applied
			randomVal = random.random()
			#if statement that checks if the value of the randomVal is greater than the mutation rate and if it is then mutation is applied
			if(randomVal < mutation_rate):
				# two different random values that are the indexes that needs to be exchanged
				randomInList1 = randint(0,len(list(keyValuePair.values())[0])-1)
				randomInList2 = randint(0,len(list(keyValuePair.values())[0])-1)
				#print(list(keyValuePair.values())[x])
				#while loop so that the random indexes are not the same
				while(randomInList1 == randomInList2):
					randomInList2 = randint(0,len(list(keyValuePair.values())[0])-1)
				#performing the swap of random indexes
				list(keyValuePair.values())[x][randomInList1], list(keyValuePair.values())[x][randomInList2] = list(keyValuePair.values())[x][randomInList2], list(keyValuePair.values())[x][randomInList1]
				#items being added to the mutated list once the mutation is done
				mutated[x] = list(keyValuePair.values())[x]
				#print(list(keyValuePair.values())[x])
		return mutated

	def crossOver():
		#storing the array returned from the mutation function in a new array
		crossOvered = mutation()
		#making a new array with 0s inside
		newArray = [0] * 7
		#creating two random values between 0 and 4 for selecting random arrays on the Map variable
		randArr1 = randint(0,len(crossOvered)-1)
		randArr2 = randint(0,len(crossOvered)-1)
		#while loop so check and change if the two random values are the same as we dont want to select the same two arrays
		while(randArr1 == randArr2):
			randArr2 = randint(0,len(crossOvered)-1)
		array1 = crossOvered[randArr1]
		array2 = crossOvered[randArr2]
		#random index is picked from an array
		getRindex = randint(0,len(crossOvered[0])-2)
		#replacing the value of one of the 0s in the position getRindex to the value of array1[getRindex]
		newArray[getRindex] = array1[getRindex]
		#increasing the value of getRindex by one so that the 2nd 0 value can be replace by the next value of array1
		getRindex += 1
		newArray[getRindex] = array1[getRindex]
		#increasing the value again so that the replacing of the 0s starts from the index next to the value that has already been filled
		getRindex += 1
		#preventing index error
		if(getRindex > 6):
			getRindex = 0
		#skips valriable is used in the loop below
		skips = 0
		#while loop that continues until all the 0 values are filled
		while(0 in newArray):
			#while loop that checks if there are any duplicates given therere are no index errors
			while(array2[getRindex] in newArray):
				#preventing index error
				if(getRindex + 1 == 7):
					getRindex = 0
				else:
					getRindex += 1
				#skips variable is used to keep the index of the newArray the same when the index of the array2 changes
				skips += 1
			#replacing the 0s after the duplicate value has been skipped
			newArray[getRindex - skips] = array2[getRindex]
			#increasing the index so that other 0s can be replaced
			getRindex += 1
			if(getRindex > 6):
				getRindex = 0
		#print(crossOvered)
		#print('new Array = ' + str(newArray))
		crossOvered.append(newArray)
		return crossOvered

	# if the first iteration has finished then the new population needs to be allocated
	if something == False:
			#nested loop that runs to create a 2d array with shuffled numbers
		for x in range(population):
			#shuffling the list to find the first 10 inidividuals
			shuffle(cities_List)
			shuffle_it = cities_List
			for y in range(len(cities_List)):
				#creating 2d array to save all the random value list created so that the total distances can be calculated for each list
				rand_pop[x][y] = shuffle_it[y]
	#when the first iteration has finished create population of 10 with the ones that were the best from the last generation
	else:
		nextGen = crossOver()
		for x in range(0,3):
			#shuffling the list to find the first 10 inidividuals
			shuffle(cities_List)
			shuffle_it = cities_List
			nextGen.append(shuffle_it)
		rand_pop = nextGen
	calc_init_distance()


from datetime import datetime
from app import db

# from sqlalchemy import BigInteger

class BaseModel(object):
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

    @classmethod
    def create(cls, *periods: tuple[dict, dict]):

        try:
            for period in periods:
                obj = cls(**period)
                db.session.add(obj)
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            obj = getattr(e, "message", str(e))

        return obj

class Timeshits(BaseModel, db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    email = db.Column(db.String(120), nullable=False)
    start = db.Column(db.DateTime, default=datetime.now())
    end = db.Column(db.DateTime, default=datetime.now())


import numpy as np
from typing import List, Tuple

def get_betas_coeff_N_unit_acts_ultimate(delta_theta_connect_idls : List[Tuple[int, int]],
                                         delta_theta_obs_start,
                                         p_or_connect_idls,
                                         p_or_obs_start,
                                         delta_theta_obs_target=None,
                                         p_or_obs_target=None,
                                         idls=None,
                                         verbose=False):

    """
        Compute the coefficients to apply the superposition theorem with each unitary action of any kind,
        using p_or for disconnect or split actions, or delta_thetas for reconnect or merge action

        Parameters
        ----------
        idls: :class:`list`, dtype:pair(int,int)
            List of grid lines or "virtual" lines corresponding to the elements on which each unitary action intervenes.
            The pair is the ids of each extremity node
        delta_theta_connect_idls: :class:`numpy.ndarray`, dtype:float64
            array of dim n_unitary_actions*n_unitary_actions. For each observation of unitary action intervention,
            it records the delta theta elemnts of every grid lines or "virtual lines" on which any considered unitary action could intervene
        delta_theta_obs_start: :class:`numpy.ndarray`, dtype:float64
             array of dim n_unitary_actions. For the base observation without any action,
             it records the delta theta elements of every grid lines or "virtual lines" on which any considered unitary action could intervene
        p_or_connect_idls: :class:`numpy.ndarray`, dtype:float64
            array of dim n_unitary_actions*n_unitary_actions. For each observation of unitary action intervention,
            it records the p_or elements of every grid lines or "virtual lines" on which any considered unitary action could intervene
        p_or_obs_start: :class:`numpy.ndarray`, dtype:float64
            array of dim n_unitary_actions. For the base observation without any action,
            it records the p_or elements of every grid lines or "virtual lines" on which any considered unitary action could intervene
        delta_theta_obs_target: :class:`numpy.ndarray`, dtype:float64
            array of dim n_unitary_actions. If computed for live assert of proper computation, for the target observation with all combined actions,
            it records the delta theta elements of every grid lines or "virtual lines" on which any considered unitary action could intervene
        p_or_obs_target: :class:`numpy.ndarray`, dtype:float64`
            array of dim n_unitary_actions. If computed for live assert of proper computation, for the target observation with all combined actions,
            it records the p_or elements of every grid lines or "virtual lines" on which any considered unitary action could intervene
        verbose: ``bool``
            Whether to print some informations or not

        Returns
        -------
        betas: :class:`numpy.ndarray`, dtype:float64
            the superposition theorem coefficient value to assign to each observation of unitary action intervention

        """

    if (p_or_obs_target is not None) and (delta_theta_obs_target is not None):
        expected_betas =[p_or_obs_target[i ] /p_or_connect_idls[i][i] if
                    ((p_or_connect_idls[i][i] != 0) and (p_or_obs_target[i])) else
                          delta_theta_obs_target[i] / delta_theta_connect_idls[i][i] for i, id_l in enumerate(idls)]
        if verbose:
            print("expected_betas :" + str(expected_betas))

    n_lines_connect = len(idls)

    a = np.array([[1 - p_or_connect_idls[i][j] / p_or_obs_start[j] if (
                (p_or_connect_idls[i][j] != 0) and (p_or_obs_start[j])) else
                   1 - delta_theta_connect_idls[i][j] / delta_theta_obs_start[j] \
                   for i in range(len(idls))] for j in range(len(idls))])
    np.fill_diagonal(a, 1)
    b = np.ones(n_lines_connect)
    if verbose:
        print(a)
        print(b)

    betas = np.linalg.solve(a, b)
    if verbose:
        print("computed betas: " + str(betas))
    return betas

# old functions using LODF matrix, still useful for demonstrations
def get_betas_coeff_N2_reconnect(id_l1, id_l2, delta_theta_line_start, delta_theta_line_unit_acts, env, verbose=False):
    init_obs, *_ = env.simulate(env.action_space())
    obs_disconnected_l1_l2, *_ = env.simulate(env.action_space({"set_line_status": [(id_l1, -1), (id_l2, -1)]}))
    obs_disconnect_l1, *_ = env.simulate(env.action_space({"set_line_status": [(id_l1, -1)]}))
    obs_disconnect_l2, *_ = env.simulate(env.action_space({"set_line_status": [(id_l2, -1)]}))

    expected_betas = [init_obs.p_or[id_l1] / obs_disconnect_l2.p_or[id_l1],
                      init_obs.p_or[id_l2] / obs_disconnect_l1.p_or[id_l2]]
    if verbose:
        print("expected_betas :" + str(expected_betas))

    a = [[1, 1 - delta_theta_line_unit_acts[1][id_l1] / delta_theta_line_start[id_l1]],
         [1 - delta_theta_line_unit_acts[0][id_l2] / delta_theta_line_start[id_l2], 1]
         ]
    b = [1, 1]

    if verbose:
        print(a)
        print(b)

    betas = np.linalg.solve(a, b)
    if verbose:
        print("computed betas: " + str(betas))
    return betas

def get_DeltaVirtual_Flows_NK(il_connect, p_il_connect, A, ilds, verbose=False):
    a = []
    for idl in ilds:
        a_row = np.array([A[il_connect][idl] * A[idlj][il_connect] + A[idlj][idl] for idlj in ilds])
        a.append(a_row)

    b = np.array([-p_il_connect * A[il_connect][idl] for idl in ilds])
    pls_virtual = np.linalg.solve(a, b)
    if verbose:
        print(pls_virtual)

    por_virtual_il_connect = p_il_connect
    for i in range(len(ilds)):
        por_virtual_il_connect += A[ilds[i]][il_connect] * pls_virtual[i]

    return por_virtual_il_connect

def get_Flows_NPlusK(p_init, A, ilds, p_ilds_connect, verbose=False):
    nl_connect = len(p_ilds_connect)
    por_virtual_ilds = []
    for idx, il_connect in enumerate(ilds):
        p_il_connect = p_ilds_connect[idx]
        ilds_il_connect = [idl for idl in ilds if idl != il_connect]
        por_virtual_ilds.append(get_DeltaVirtual_Flows_NK(il_connect, p_il_connect, A, ilds_il_connect))

    if verbose:
        print(por_virtual_ilds)

    por_connected = p_init
    for i in range(len(ilds)):
        por_connected -= A[ilds[i]] * por_virtual_ilds[i]

    return por_connected

def get_Virtual_Flows_N2(por_init, A, idl1, idl2, verbose=False):
    a = np.array([[A[idl1][idl1], A[idl2][idl1]], [A[idl1][idl2], A[idl2][idl2]]])
    b = np.array([-por_init[idl1], -por_init[idl2]])
    [pl1_virtual, pl2_virtual] = np.linalg.solve(a, b)
    if verbose:
        print(pl1_virtual)
        print(pl2_virtual)

    por_virtual = por_init + A[idl1] * pl1_virtual + A[idl2] * pl2_virtual

    return por_virtual

# a generic version for n-K
def get_Virtual_Flows_NK(por_init, A, ilds, verbose=False):
    a = []
    for idl in ilds:
        a_row = np.array([A[idlj][idl] for idlj in ilds])
        a.append(a_row)

    b = np.array([-por_init[idl] for idl in ilds])
    pls_virtual = np.linalg.solve(a, b)
    if verbose:
        print(pls_virtual)

    por_virtual = por_init
    for i in range(len(ilds)):
        por_virtual += A[ilds[i]] * pls_virtual[i]

    return por_virtual


import logging
import sys

from pyaedt import is_ironpython
from pyaedt import log_handler
from pyaedt import settings

message_levels = {"Global": 0, "Project": 1, "Design": 2}

class Msg:
    (INFO, WARNING, ERROR, FATAL) = range(4)

class MessageList:
    """
    Collects and returns messages from the AEDT message manager for a specified project name and design name.

    Parameters
    ---------
    msg_list : list
        List of messages extracted from AEDT.
    project_name : str
        Name of the project. The default is the active project.
    design_name : str
        Name of the design within the specified project. The default is the active design.

    Attributes
    ----------
    global_level : list of str
        List of strings representing the message content at the global level of the message manager

    project_level : list of str
        List of strings representing the message content of the specifiec project

    design_level : list of str
        List of strings representing the message content for the specified design within the specified project

    """

    def __init__(self, msg_list, project_name, design_name):
        self.global_level = []
        self.project_level = []
        self.design_level = []
        global_label = "Project: *Global - Messages, "
        project_label = "Project: {}, ".format(project_name)
        design_label = "Project: {}, Design: {} (".format(project_name, design_name)
        for line in msg_list:
            # Find the first instance of '[' to get the message context
            loc = line.find("[")
            if loc < 0:
                # Format is not clear - append to global
                self.global_level.append(line)
            else:
                line_label = line[0:loc]
                msg_txt = line[loc:].strip()
                if design_label in line_label:
                    self.design_level.append(msg_txt)
                elif project_label in line_label:
                    self.project_level.append(msg_txt)
                elif global_label in line_label:
                    self.global_level.append(msg_txt)

class AppFilter(logging.Filter):
    """Specifies the destination of the logger.

    AEDT exposes three different loggers, which are the global, project, and design loggers.

    Parameters
    ----------
    destination : str, optional
        Logger to write to. Options are ``"Global"`, ``"Project"``, and ``"Design"``.
        The default is ``"Global"``.
    extra : str, optional
        Name of the design or project. The default is ``""``.
    """

    def __init__(self, destination="Global", extra=""):
        self._destination = destination
        self._extra = extra

    def filter(self, record):
        """
        Modify the record sent to the logger.

        Parameters
        ----------
        record : class:`logging.LogRecord`
            Contains information related to the event being logged.
        """
        record.destination = self._destination

        # This will avoid the extra '::' for Global that does not have any extra info.
        if not self._extra:
            record.extra = self._extra
        else:
            record.extra = self._extra + ":"
        return True

class AedtLogger(object):
    """
    Specifies the logger to use for each AEDT logger.

    This class allows you to add a handler to write messages to a file and to indicate
    whether to write mnessages to the standard output (stdout).

    Parameters
    ----------
    level : int, optional
        Logging level to filter the message severity allowed in the logger.
        The default is ``logging.DEBUG``.
    filename : str, optional
        Name of the file to write messages to. The default is ``None``.
    to_stdout : bool, optional
        Whether to write log messages to stdout. The default is ``False``.
    """

    def __init__(self, level=logging.DEBUG, filename=None, to_stdout=False):
        main = sys.modules["__main__"]

        self.level = level
        self.filename = filename or settings.logger_file_path
        settings.logger_file_path = self.filename
        if is_ironpython:
            logging.basicConfig()
        self._global = logging.getLogger("Global")
        self._file_handler = None
        self._std_out_handler = None
        self.formatter = logging.Formatter(settings.logger_formatter, datefmt=settings.logger_datefmt)

        if not settings.enable_logger:
            self._global.addHandler(logging.NullHandler())
            return

        if self._global.handlers:
            if "messenger" in dir(self._global.handlers[0]):
                self._global.removeHandler(self._global.handlers[0])
                if self._global.handlers:
                    self._global.removeHandler(self._global.handlers[0])
        if not self._global.handlers:
            self._global.addHandler(log_handler.LogHandler(self, "Global", logging.DEBUG))
            main._aedt_handler = self._global.handlers
            self._global.setLevel(level)
            self._global.addFilter(AppFilter())

        if self.filename:
            self._file_handler = logging.FileHandler(self.filename)
            self._file_handler.setLevel(level)
            self._file_handler.setFormatter(self.formatter)
            self._global.addHandler(self._file_handler)

        if to_stdout:
            self._std_out_handler = logging.StreamHandler()
            self._std_out_handler.setLevel(level)

            self._std_out_handler.setFormatter(self.formatter)
            self._global.addHandler(self._std_out_handler)

    @property
    def _desktop(self):
        if "oDesktop" in dir(sys.modules["__main__"]):
            MainModule = sys.modules["__main__"]
            return MainModule.oDesktop
        return None  # pragma: no cover

    @property
    def _log_on_desktop(self):
        if self._desktop and settings.enable_desktop_logs:
            return True
        else:
            return False

    @_log_on_desktop.setter
    def _log_on_desktop(self, val):
        settings.enable_desktop_logs = val

    @property
    def _log_on_file(self):
        return settings.enable_file_logs

    @_log_on_file.setter
    def _log_on_file(self, val):
        settings.enable_file_logs = val

    @property
    def _log_on_screen(self):
        return settings.enable_screen_logs

    @_log_on_screen.setter
    def _log_on_screen(self, val):
        settings.enable_screen_logs = val

    @property
    def logger(self):
        """Aedt Logger object."""
        if self._log_on_file:
            return logging.getLogger(__name__)
        else:
            return None  # pragma: no cover

    @property
    def messages(self):
        """Message manager content for the active project and design.

        Returns
        -------
        list of str
           List of messages for the active project and design.

        """
        return self.get_messages(self._project_name, self._design_name)

    def get_messages(self, project_name=None, design_name=None):
        """
        Retrieve the message manager content for a specified project and design.

        If the specified project and design names are invalid, they are ignored.

        Parameters
        ----------
        project_name : str
            Name of the project.
        design_name : str
            Name of the design within the specified project.

        Returns
        -------
        list of str
            List of messages for the specified project and design.

        """
        if not project_name:
            project_name = self._project_name
        if not design_name:
            design_name = self._design_name
        if self._log_on_desktop:
            global_message_data = self._desktop.GetMessages("", "", 0)
            message_data = MessageList(global_message_data, project_name, design_name)
            return message_data
        return MessageList([], project_name, design_name)

    def add_error_message(self, message_text, level=None):
        """
        Add a type 2 "Error" message to the message manager tree.

        Also add an error message to the logger if the handler is present.

        Parameters
        ----------
        message_text : str
            Text to display as the error message.
        level : str, optional
            Level to add the error message to. Options are ``"Global"``,
            ``"Project"``, and ``"Design"``. The default is ``None``,
            in which case the error message gets added to the ``"Design"``
            level.

        Examples
        --------
        Add an error message to the AEDT message manager.

        >>> hfss.logger.project.error("Project Error Message", "Project")

        """
        self.add_message(2, message_text, level)

    def add_warning_message(self, message_text, level=None):
        """
        Add a type 1 "Warning" message to the message manager tree.

        Also add a warning message to the logger if the handler is present.

        Parameters
        ----------
        message_text : str
            Text to display as the warning message.
        level : str, optional
            Level to add the warning message to. Options are ``"Global"``,
            ``"Project"``, and ``"Design"``. The default is ``None``,
            in which case the warning message gets added to the ``"Design"``
            level.

        Examples
        --------
        Add a warning message to the AEDT message manager.

        >>> hfss.logger.warning("Global warning message")

        """
        self.add_message(1, message_text, level)

    def add_info_message(self, message_text, level=None):
        """Add a type 0 "Info" message to the active design level of the message manager tree.

        Also add an info message to the logger if the handler is present.

        Parameters
        ----------
        message_text : str
            Text to display as the info message.
        level : str, optional
            Level to add the info message to. Options are ``"Global"``,
            ``"Project"``, and ``"Design"``. The default is ``None``,
            in which case the info message gets added to the ``"Design"``
            level.

        Examples
        --------
        Add an info message at the global level.

        >>> hfss.logger.info("Global warning message", "Global")

        """
        self.add_message(0, message_text, level)

    def add_debug_message(self, message_type, message_text):
        """
        Parameterized message to the message manager to specify the type and project or design level.

        Parameters
        ----------
        message_type : int
            Type of the message. Options are:

            * ``0`` : Info
            * ``1`` : Warning
            * ``2`` : Error

        message_text : str
            Text to display as the message.

        """

        if len(message_text) > 250:
            message_text = message_text[:250] + "..."

        # Print to stdout and to logger
        if self._log_on_file:
            if message_type == 0 and self.logger:
                self.logger.debug(message_text)
            elif message_type == 1 and self.logger:
                self.logger.warning(message_text)
            elif message_type == 2 and self.logger:
                self.logger.error(message_text)

    def add_message(self, message_type, message_text, level=None, proj_name=None, des_name=None):
        """Add a message to the message manager to specify the type and project or design level.

        Parameters
        ----------
        message_type : int
            Type of the message. Options are:
            * ``0`` : Info
            * ``1`` : Warning
            * ``2`` : Error
        message_text : str
            Text to display as the message.
        level : str, optional
            Level to add the message to. Options are ``"Global"``,
            ``"Project"``, and ``"Design"``. The default is ``None``,
            in which case the message gets added to the
            ``"Design"`` level.
        proj_name : str, optional
            Name of the project.
        des_name : str, optional
            Name of the design.
        """
        if not proj_name:
            proj_name = ""

        if not des_name:
            des_name = ""

        if not level:
            level = "Design"

        assert level in message_levels, "Message level must be `Design', 'Project', or 'Global'."

        if self._log_on_desktop and self._desktop:
            if not proj_name and message_levels[level] > 0:
                proj_name = self._project_name
            if not des_name and message_levels[level] > 1:
                des_name = self._design_name
            if des_name and ";" in des_name:
                des_name = des_name[des_name.find(";") + 1 :]
            try:
                self._desktop.AddMessage(proj_name, des_name, message_type, message_text)
            except:
                print("pyaedt info: Failed in Adding Desktop Message")

        if len(message_text) > 250:
            message_text = message_text[:250] + "..."

        # Print to stdout and to logger
        if self._log_on_screen:
            if message_type == 0:
                print("pyaedt info: {}".format(message_text))
            elif message_type == 1:
                print("pyaedt warning: {}".format(message_text))
            elif message_type == 2:
                print("pyaedt error: {}".format(message_text))
        if self._log_on_file:
            if message_type == 0 and self.logger:
                self.logger.debug(message_text)
            elif message_type == 1 and self.logger:
                self.logger.warning(message_text)
            elif message_type == 2 and self.logger:
                self.logger.error(message_text)

    def clear_messages(self, proj_name=None, des_name=None, level=2):
        """Clear all messages.

        Parameters
        ----------
        proj_name : str, optional
            Name of project. The default is ``None``, in which case messages
            are cleared for the current project. If blank, messages are cleared
            for all projects.
        des_name : str, optional
            Name of the design within the specified project. The default
            is ``None,`` in which case the current design is used.
            If blank, all designs are used.
        level : int, optional
            Level of the messages to clear. Options are:

            * ``0`` : Clear all info messages.
            * ``1`` : Clear all info and warning messages.
            * ``2`` : Clear all info, warning, and error messages.
            * ``3`` : Clear all messages, which include info, warning,
              error, and fatal-error messages.

            The default is ``2.``

        Examples
        --------
        Clear all messages in the current design and project.

        >>> hfss.clear_messages(level=3)

        """
        if self._log_on_desktop:
            if proj_name is None:
                proj_name = self._project_name
            if des_name is None:
                des_name = self._design_name
            self._desktop.ClearMessages(proj_name, des_name, level)

    @property
    def _oproject(self):
        if self._desktop:
            return self._desktop.GetActiveProject()

    @property
    def _odesign(self):
        if self._oproject:
            return self._oproject.GetActiveDesign()

    @property
    def _design_name(self):
        try:
            return self._odesign.GetName()
        except AttributeError:
            return ""

    @property
    def _project_name(self):
        try:
            return self._oproject.GetName()
        except AttributeError:
            return ""

    def add_logger(self, destination, level=logging.DEBUG):
        """
        Add a logger for either the active project or active design.

        Parameters
        ----------
        destination : str
            Logger to write to. Options are ``"Project"`` and ``"Design"``.
        level : int, optional
            Logging level enum. The default is ``logging.DEBUG``.
        """

        if destination == "Project":
            project_name = self._project_name
            self._project = logging.getLogger(project_name)
            self._project.addHandler(log_handler.LogHandler(self, "Project", level))
            self._project.setLevel(level)
            self._project.addFilter(AppFilter("Project", project_name))
            if self._file_handler is not None:
                self._project.addHandler(self._file_handler)
            if self._std_out_handler is not None:
                self._project.addHandler(self._std_out_handler)
            return self._project
        elif destination == "Design":
            project_name = self._project_name
            design_name = self._design_name
            self._design = logging.getLogger(project_name + ":" + design_name)
            self._design.addHandler(log_handler.LogHandler(self, "Design", level))
            self._design.setLevel(level)
            self._design.addFilter(AppFilter("Design", design_name))
            if self._file_handler is not None:
                self._design.addHandler(self._file_handler)
            if self._std_out_handler is not None:
                self._design.addHandler(self._std_out_handler)
            return self._design
        else:
            raise ValueError("The destination must be either 'Project' or 'Design'.")

    def disable_desktop_log(self):
        """Disable the log in AEDT."""
        self._log_on_desktop = False

    def enable_desktop_log(self):
        """Enable the log in AEDT."""
        self._log_on_desktop = True

    def disable_stdout_log(self):
        """Disable printing log messages to stdout."""
        self._log_on_screen = False
        self._global.removeHandler(self._std_out_handler)

    def enable_stdout_log(self):
        """Enable printing log messages to stdout."""
        self._log_on_screen = True

    def disable_log_on_file(self):
        """Disable writing log messages to an output file."""
        self._log_on_file = False
        self._file_handler.close()
        self._global.removeHandler(self._file_handler)

    def enable_log_on_file(self):
        """Enable writing log messages to an output file."""
        self._log_on_file = True
        self._file_handler = logging.FileHandler(self.filename)
        self._file_handler.setLevel(self.level)
        self._file_handler.setFormatter(self.formatter)
        self._global.addHandler(self._file_handler)

    def info(self, msg, *args, **kwargs):
        """Write an info message to the global logger."""
        return self._global.info(msg, *args, **kwargs)

    def warning(self, msg, *args, **kwargs):
        """Write a warning message to the global logger."""
        return self._global.warning(msg, *args, **kwargs)

    def error(self, msg, *args, **kwargs):
        """Write an error message to the global logger."""
        return self._global.error(msg, *args, **kwargs)

    def debug(self, msg, *args, **kwargs):
        """Write a debug message to the global logger."""
        return self._global.debug(msg, *args, **kwargs)

    @property
    def glb(self):
        """Global logger."""
        self._global = logging.getLogger("Global")
        return self._global

    @property
    def project(self):
        """Project logger."""
        self._project = logging.getLogger(self._project_name)
        if not self._project.handlers:
            self.add_logger("Project")
        return self._project

    @property
    def design(self):
        """Design logger."""
        self._design = logging.getLogger(self._project_name + ":" + self._design_name)
        if not self._design.handlers:
            self.add_logger("Design")
        return self._design


import MySQLdb

def exdbsql(sql):
    # 连接数据库
    db = MySQLdb.connect('localhost', 'root', '123456', 'dbname')
    # 创建一个游标对象
    cursor = db.cursor()
    # 执行sql语句
    cursor.execute(sql)
    # 得到查询结果
    datas = cursor.fetchall()
    # 关闭连接
    db.close()
    return datas

if __name__ == '__main__':
    datas = exdbsql('select * from student')


import io
import sys

_INPUT = """\
6
1
1
1 1
"""

sys.stdin = io.StringIO(_INPUT)
case_no=int(input())
for __ in range(case_no):
  N=int(input())
  M=0
  d={}
  w=1
  ans=[]
  while w<=N:
    for i in range(N-w+1):
      M+=1
      d[(i,i+w-1)]=M
      ans.append((i+1,i+w))
    w*=2
  print(M)
  for i in range(M):
    print(*ans[i])
  Q=int(input())
  for _ in range(Q):
    L,R=map(lambda x: int(x)-1, input().split())
    w=1
    while w<=R-L+1: w*=2
    w//=2
    print(d[(L,L+w-1)],d[(R-w+1,R)])


# -*- coding: utf-8 -*-
"""
Created on Fri Jul 07 16:33:52 2017

@author: marco
"""
from typing import Optional

from yaocptool.methods.base.solutionmethodinterface import SolutionMethodInterface
from yaocptool.modelling.ocp import OptimalControlProblem
from yaocptool.modelling.system_model import SystemModel

class Node:
    def __init__(
        self,
        node_id: int = None,
        name: Optional[str] = None,
        model: Optional[SystemModel] = None,
        problem: Optional[OptimalControlProblem] = None,
        solution_method: Optional[SolutionMethodInterface] = None,
        color: float = 0.25,
    ):
        self.name = name
        self.node_id = node_id
        self.problem: OptimalControlProblem = problem  # type: ignore
        self.solution_method = solution_method
        self.color = color

        self.model: SystemModel = problem.model if model is None and problem is not None else model  # type: ignore

        if name is None and self.model is not None:
            self.name = self.model.name

    def __repr__(self) -> str:
        return "<Node name: {}, node_id: {}>".format(self.name, self.node_id)


# 413. 等差数列划分

# 如果一个数列 至少有三个元素 ，并且任意两个相邻元素之差相同，则称该数列为等差数列。

# 例如，[1,3,5,7,9]、[7,7,7,7] 和 [3,-1,-5,-9] 都是等差数列。
# 给你一个整数数组 nums ，返回数组 nums 中所有为等差数组的 子数组 个数。

# 子数组 是数组中的一个连续序列。
from typing import List

class Solution:
    def numberOfArithmeticSlices(self, nums: List[int]) -> int:
        res = 0
        n = len(nums)
        diff = []
        for i in range(1,n):
            diff.append(nums[i]-nums[i-1])
        begin = 0
        for i in range(1,len(diff)):
            if diff[i]!=diff[begin]:
                tmp = i-begin
                if tmp>=2:
                    res += (tmp-1)*tmp//2
                begin=i
        else:
            tmp = len(diff)-begin
            if tmp>=2:
                res += (tmp-1)*tmp//2
        return res

sol = Solution()
nums = [1,2,3,4]
print(sol.numberOfArithmeticSlices(nums))


# Shivani Bhatia
# EC500 C1 Building Software
# API Project - Twitter Image Downloader
# image_download.py

import configparser
import wget
import sys
import os

from tweepy import API, OAuthHandler, Stream

def config_parse(filename):
    cparse = configparser.ConfigParser()
    cparse.read(filename)
    return cparse

def authorization(cparse):
    auth = OAuthHandler(cparse['API']['consumerKey'], cparse['API']['consumerSecret'])
    auth.set_access_token(cparse['API']['accessToken'], cparse['API']['accessTokenSecret'])
    api = API(auth)
    return api

def downloadTweets(username, api, max_img, output):

    media_tweets = set()
    downloaded =  0
    back_count = max_img

    try:
        tweets = api.user_timeline(screen_name=username,
                                   include_rts=False,
                                   exclude_replies=True)

    except Exception as e:
        print(e)
        sys.exit()

    for posts in tweets:
        media = posts.entities.get('media', [])
        if (len(media) > 0):
            if (media[0]['type'] == 'photo'):
                media_tweets.add(media[0]['media_url'])
                downloaded = downloaded + 1
                if (downloaded == max_img):
                    break

    if not os.path.exists(output):
        os.makedirs(output)

    last_id = int(tweets[-1].id - 1)

    while (downloaded < max_img):
        temp_tweets = api.user_timeline(screen_name=username,
                                        max_id=last_id,
                                        include_rts = False,
                                        exclude_replies = True)

        if len(temp_tweets) == 0:
            break
        else:
            for posts in temp_tweets:
                media = posts.entities.get('media', [])
                if (len(media) > 0):
                    if (media[0]['type'] == 'photo'):
                        media_tweets.add(media[0]['media_url'])
                        downloaded = downloaded + 1
                        if (downloaded == max_img):
                            break

            last_id = int(temp_tweets[-1].id - 1)

    for files in media_tweets:
        if (back_count != 0):
            wget.download(files,out=output)
            back_count = back_count - 1

def renameImages(output):
    for i, filename in enumerate(os.listdir(output)):
        os.rename(output + "/" + filename, output + "/image" + str(i+1) + ".jpg")


#!/usr/bin/env python
import sys
import getopt
import struct
import time
import socket
import random
import json

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor

from pyrip_lib import *

'''
    Provides methods to handle RIP routes
'''
class IRoute(object):
    def __init__(self, prefix=0, prefixLen=0, nextHop=0, afi=0):
        self.afi = afi
        self.prefix = prefix
        self.prefixLen = prefixLen
        self.nextHop = nextHop

    def __cmp__(self, other):
        if self.afi < other.afi:
            return -1
        elif self.afi > other.afi:
            return 1

        if self.prefix < other.prefix:
            return -1
        elif self.prefix > other.prefix:
            return 1

        if self.prefixLen < other.prefixLen:
            return -1
        elif self.prefixLen > other.prefixLen:
            return 1

        if self.nextHop < other.nextHop:
            return -1
        elif self.nextHop > other.nextHop:
            return 1

        return 0

    def __lt__(self, other):
        return self.__cmp__(other)<0
    def __le__(self, other):
        return self.__cmp__(other)<=0
    def __eq__(self, other):
        return self.__cmp__(other)==0
    def __ne__(self, other):
        return self.__cmp__(other)!=0
    def __gt__(self, other):
        return self.__cmp__(other)>0
    def __ge__(self, other):
        return self.__cmp__(other)>=0

class RipRoute(IRoute):
    def __init__(self, prefix, prefixLen, nextHop, metric=RIP_METRIC_MIN, routeTag=0, family=RIP_ADDRESS_FAMILY):
        self.family = family
        self.routeTag = routeTag
        self.prefix = prefix
        self.prefixLen = prefixLen
        self.nextHop = nextHop
        self.afi = 1 # IPv4

        if metric >= RIP_METRIC_INFINITY:
            self.metric = RIP_METRIC_INFINITY
        elif metric <= RIP_METRIC_MIN:
            self.metric = RIP_METRIC_MIN
        else:
            self.metric = metric

    def pack(self):
        mask = PrefixLen2MaskInt(self.prefixLen)
        return struct.pack(RIP_ENTRY_PACK_FORMAT,
            self.family, self.routeTag, self.prefix, mask, self.nextHop, self.metric)

    def __str__(self):
        return '{'+'{:s}/{:d}, {:s}, {:d}, {:d}'.format(Int2IP(self.prefix), self.prefixLen, Int2IP(self.nextHop), self.metric, self.routeTag)+'}'

    def __repr__(self):
        return '{:08x}/{:d}->{:08x} m{:d} t{:d}'.format(self.prefix, self.prefixLen, self.nextHop, self.metric, self.routeTag)

'''
    Provides methods to handle RIP packets
'''
class RipPacket(object):
    def __init__(self, cmd = RIP_COMMAND_RESPONSE, version = 2):
        self.command = cmd
        self.version = version
        self.entry = []

    @property
    def size(self):
        return RIP_HEADER_SIZE + len(self.entry)*RIP_ENTRY_SIZE

    def pack(self):
        header = struct.pack(RIP_HEADER_PACK_FORMAT,self.command,self.version,0)
        entries = b''
        for ent in self.entry:
            entries = entries + ent.pack()
        packet = header + entries
        return packet

    def unpack(data):
        pkt = RipPacket()
        hdr = data[:RIP_HEADER_SIZE]
        data = data[RIP_HEADER_SIZE:]
        pkt.command, pkt.version, zero = struct.unpack(RIP_HEADER_PACK_FORMAT, hdr)
        while len(data) > 0 and len(data)%RIP_ENTRY_SIZE == 0:
            ent = data[:RIP_ENTRY_SIZE]
            data = data[RIP_ENTRY_SIZE:]
            family, tag, prefix, mask, nextHop, metric = struct.unpack(RIP_ENTRY_PACK_FORMAT, ent)
            pkt.entry.append(RipRoute(
                prefix, MaskInt2PrefixLen(mask), nextHop, metric, tag, family))
        return pkt

    def addEntry(self, prefix, prefixLen, nextHop, metric, routeTag=0, family=RIP_ADDRESS_FAMILY):
        self.entry.append(RipRoute(prefix, prefixLen, nextHop, metric, routeTag, family))
        return True

    def removeEntry(self, prefix, prefixLen, nextHop):
        for ent in self.entry:
            if ent.prefix == prefix and ent.prefixLen == prefixLen and ent.nextHop == nextHop:
                self.entry.remove(ent)
                return True
        return False

    def __getitem__(self, key):
        return self.entry[key]

    def __setitem__(self, key, value):
        self.entry[key] = value

    def __repr__(self):
        if self.command == RIP_COMMAND_REQUEST:
            cmd = 'request'
        elif self.command == RIP_COMMAND_RESPONSE:
            cmd = 'response'

        ret = 'v{:d}, {:s}, {:d} entries'.format(self.version,cmd,len(self.entry))
        return ret

'''
    main RIP process. RIP version 2.
    version 1 should be added in future.
'''
class RIP(DatagramProtocol):
    def __init__(self, inputDict):
        self.ttl = 1 # link-local. Not going to follow history ttl=2.
        self.updateTime = RIP_DEFAULT_UPDATE
        self.timeoutTime = RIP_DEFAULT_TIMEOUT
        self.garbageTime = RIP_DEFAULT_GARBAGE
        self.jitterScale = RIP_DEFAULT_JITTER_SCALE
        self.RIB = []
        self.loadConfigurationFile(inputDict['configFileName'])

    ''' Twisted Functions '''
    def startProtocol(self):
        self.transport.setTTL(self.ttl)
        self.transport.joinGroup(RIP_MULTICAST_ADDR)
        self.transport.setLoopbackMode(False)
        self.transport.setBroadcastAllowed(True)
        #
        self.requestAllRoutes()
        reactor.callLater(self.getUpdateTime(), self.sendRegularUpdate)

    def datagramReceived(self, datagram, addrPort):
        pkt = RipPacket.unpack(datagram)
        print('r', pkt, repr(addrPort))

        # we only deal with RIPv2 now.
        if pkt.version != 2:
            return

        if pkt.command == RIP_COMMAND_REQUEST:
            self.respondToRequest(pkt.entry, addrPort[0])
        elif pkt.command == RIP_COMMAND_RESPONSE:
            self.updateRIB(pkt.entry, addrPort[0])

    def connectionRefused(self):
        pass

    ''' RIP Functions '''
    def loadConfigurationFile(self, filename):
        with open(filename, 'r') as f:
            conf = json.load(f)

        if 'updateTime' in conf:
            self.updateTime = conf['updateTime']
        if 'timeoutTime' in conf:
            self.timeoutTime = conf['timeoutTime']
        if 'garbageTime' in conf:
            self.garbageTime = conf['garbageTime']

        if 'routes' in conf:
            for r in conf['routes']:
                if set(('prefix', 'prefixLen', 'nextHop')) <= set(r.keys()):
                    prefix = IP2Int(r['prefix'])
                    prefixLen = r['prefixLen']
                    nextHop = IP2Int(r['nextHop'])
                    metric = RIP_METRIC_MIN if 'metric' not in r.keys() else r['metric']
                    rtag = 0 if 'routeTag' not in r.keys() else r['routeTag']
                    self.addRouteToRIB(RipRoute(prefix, prefixLen, nextHop, metric, rtag))

    def getUpdateTime(self):
        return self.updateTime + (random.random()*2-1)*self.updateTime*self.jitterScale

    # TODO
    def respondToRequest(self, routes, addr):
        # use unicast to respond
        pass

    def updateRIB(self, routes, addr):
        for r in routes:
            if r.nextHop == 0:
                r.nextHop = IP2Int(addr)
            self.addRouteToRIB(r)
        self.refreshRIB()

    def sendRegularUpdate(self):
        pkt = RipPacket(RIP_COMMAND_RESPONSE, 2)
        for r in self.RIB:
            if r.metric < RIP_METRIC_MAX:
                pkt.addEntry(r.prefix, r.prefixLen, r.nextHop, r.metric)

        if len(pkt.entry) > 0:
            print('s', pkt, pkt.size)
            self.transport.write(pkt.pack(), (RIP_MULTICAST_ADDR, RIP_UDP_PORT))
        else:
            print('s No valid entry to send update packet.')

        reactor.callLater(self.getUpdateTime(), self.sendRegularUpdate)

    # TODO
    def sendRequest(self, route):
        # construct request packet
        pass

    def requestAllRoutes(self):
        pkt = RipPacket(RIP_COMMAND_REQUEST, 2)
        pkt.addEntry(0, 0, 0, 16, 0, 0) # a special entry to request all routing table from neighbors.
        print('s', pkt)
        self.transport.write(pkt.pack(), (RIP_MULTICAST_ADDR, RIP_UDP_PORT))

    '''
        RIB database functions
    '''
    def routeTimerSetup(self, route):
        if route.metric == RIP_METRIC_INFINITY:
            self.garbageRoute(route)
        else:
            route.timeoutTimer = reactor.callLater(self.timeoutTime, self.garbageRoute, route)
            route.garbageTimer = None

    def routeTimerReset(self, route):
        if route.timeoutTimer:
            route.timeoutTimer.reset(self.timeoutTime)
        else:
            route.timeoutTimer = reactor.callLater(self.timeoutTime, self.garbageRoute, route)
            route.garbageTimer.cancel()
            route.garbageTimer = None

    def routeTimerTimeoutGet(self, route):
        if route.timeoutTimer:
            return int(route.timeoutTimer.getTime()-time.time())
        return -1

    def routeTimerGarbageGet(self, route):
        if route.garbageTimer:
            return int(route.garbageTimer.getTime()-time.time())
        return -1

    def garbageRoute(self, route):
        route.timeoutTimer = None
        route.metric = RIP_METRIC_INFINITY
        route.garbageTimer = reactor.callLater(self.garbageTime, self.deleteRouteFromRIB, route)

    def verifyRoute(self, route):
        # check netmask
        # check prefix
        # check next hop

        if route.metric < RIP_METRIC_MIN:
            return False

        if not route.family == RIP_ADDRESS_FAMILY:
            return False

        return True

    def showRIB(self):
        for r in self.RIB:
            print(str(r)+' '+str(self.routeTimerTimeoutGet(r))+' '+str(self.routeTimerGarbageGet(r)))

    def addRouteToRIB(self, route):
        if self.verifyRoute(route) == False:
            return

        for r in self.RIB:
            if r == route:
                return

        self.routeTimerSetup(route)

        for i in range(0,len(self.RIB)):
            if self.RIB[i] >= route:
                self.RIB.insert(i, route)
                return
        self.RIB.append(route)

    def deleteRouteFromRIB(self, route):
        if len(self.RIB) == 0:
            warn('Nothing to delete in RIB.')
            return

        for r in self.RIB:
            if r == route:
                self.RIB.remove(r)

    def refreshRIB(self):
        for r in self.RIB:
            if r.metric >= RIP_METRIC_INFINITY:
                r.metric = RIP_METRIC_INFINITY

'''
    Show available options and details about this program.
'''
def showHelp():
    print('pyrip.py <configFile>')

'''
    Parse input arguments into dictionary.
'''
def inputParser(argv):
    inputDict = {}

    try:
        opts, args = getopt.getopt(argv,'h',['help'])
    except getopt.GetoptError:
        showHelp()
        sys.exit()

    for opt, arg in opts:
        if opt == '-h':
            showHelp()
            sys.exit()

    if len(args) == 0:
        showHelp()
        sys.exit()

    inputDict['configFileName'] = args[0]
    return inputDict

def main(argv):
    reactor.listenMulticast(RIP_UDP_PORT, RIP(inputParser(argv)), listenMultiple=True)
    reactor.run()

if __name__ == '__main__':
    main(sys.argv[1:])


import argparse
from scripts import json_to_md, repo_scraper, save_to_file, start_loading_animation
from utils import (
    allowed_file_extensions,
    excluded_folders,
    allowed_filenames,
)

try:
    loading_spinner = start_loading_animation("🔮 Inscribing data to the arcane JSON scroll and casting spells to convert JSON to MD... 🌟📜🔮🧙♂")
    parser = argparse.ArgumentParser(description="Repository Scraper Tool")
    parser.add_argument("--owner", type=str, help="Owner of the repository", required=True)
    parser.add_argument("--repo", type=str, help="Name of the repository", required=True)
    parser.add_argument("--branch", type=str, help="Branch to scrape", default="main")
    parser.add_argument(
        "--generate-readme", action="store_true", help="Generate a README.md file"
    )
    args = parser.parse_args()

    scraper = repo_scraper(
        args.owner,
        args.repo,
        args.branch,
        allowed_file_extensions,
        excluded_folders,
        allowed_filenames,
    )

    repository_data = scraper.scrape_repository()

    filename_json = f"{args.owner}-{args.repo}_tree.json"
    json_filepath = f"data/results_json/{filename_json}"

    loading_spinner.stop()

    save_to_file(json_filepath, repository_data)

    if args.generate_readme:
        filename_readme = f"{args.owner}-{args.repo}.md"
        output_dir = f"data/results_md/{filename_readme}"
        json_to_md(json_filepath, output_dir)

    else:
        print("No README.md file will be generated.")

except Exception as e:
    print(f"An magical error occurred: {str(e)}")


from .pblefdef_pb2 import LibraryExchangeFormat as PbLef
from collections import OrderedDict

class LibraryExchangeFormat:
    def __init__(self, filename) -> None:
        self.lef = PbLef()
        with open(filename, 'rb') as fp:
            self.lef.ParseFromString(fp.read())
        self.macros = OrderedDict()
        for m in self.lef.macros:
            self.macros[m.name] = m


# user/models.py
from django.db import models
from django.contrib.auth import get_user_model

class UserProfile(models.Model):
    """User profile model for users"""
    user = models.OneToOneField(
        get_user_model(), related_name='userprofile', on_delete=models.CASCADE)
    image_url = models.CharField(max_length=200, null=True, blank=True)
    company_name = models.CharField(max_length=100, null=True, blank=True)
    is_admin = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.user.username


import pandas as pd
from scipy.stats import shapiro
from scipy import stats

pd.set_option('display.max_columns', None)
pd.set_option('display.float_format', lambda x: '%.5f' % x)

# Reading Datasets. There are two different datasets to compare.
df_control = pd.read_excel("Week_05/AB_Test_Project/ab_testing_data.xlsx", sheet_name="Control Group")
df_test = pd.read_excel("Week_05/AB_Test_Project/ab_testing_data.xlsx", sheet_name="Test Group")

df_control.head()
df_test.head()

df_control.shape
df_test.shape

df_control.info()
df_test.info()

# Setting outliers, if there are any.
def outlier_thresholds(dataframe, variable, low_quantile=0.05, up_quantile=0.95):
    quantile_one = dataframe[variable].quantile(low_quantile)
    quantile_three = dataframe[variable].quantile(up_quantile)
    interquantile_range = quantile_three - quantile_one
    up_limit = quantile_three + 1.5 * interquantile_range
    low_limit = quantile_one - 1.5 * interquantile_range
    return low_limit, up_limit

low_limit_click, up_limit_click = outlier_thresholds(df_control, "Impression")
low_limit_click, up_limit_click = outlier_thresholds(df_control, "Click")
low_limit_purchase, up_limit_purchase = outlier_thresholds(df_control, "Purchase")
low_limit_earning, up_limit_earning = outlier_thresholds(df_control, "Earning")

def replace_with_thresholds(dataframe, variable):
    low_limit, up_limit = outlier_thresholds(dataframe, variable)
    # dataframe.loc[(dataframe[variable] < low_limit), variable] = low_limit
    dataframe.loc[(dataframe[variable] > up_limit), variable] = up_limit

column_names_to_set = ["Impression", "Click", "Purchase", "Earning"]

for variable in column_names_to_set:
    replace_with_thresholds(df_control, variable)
    replace_with_thresholds(df_test, variable)

# Defining the AB Testing hypothesis
# H0: There is no statistical difference between the control and test groups in terms of the average number of purchases
# H1: There is statistical difference between the control and test groups in terms of the average number of purchases.
df_control["Purchase"].mean()
df_test["Purchase"].mean()

# Target variables here is "Purchase".
control_purchase = df_control["Purchase"]
test_purchase = df_test["Purchase"]

# There are 2 prerequisites for the AB test: Assumption of Normality and Homogeneity of Variance.

# Normality Test
# H0: Assumption of Normality is provided.
# H1: Assumption of Normality is not provided.
test_statistic_control, p_value_control = shapiro(control_purchase)
print('Test Statistic = %.4f, p-value = %.4f' % (test_statistic_control, p_value_control))
p_value_control < 0.05
test_statistic_test, p_value_test = shapiro(test_purchase)
print('Test Statistic = %.4f, p-value = %.4f' % (test_statistic_test, p_value_test))
p_value_test < 0.05
# Both p values are higher than 0.05 so we cant reject H0, assumption of Normality is provided.

# Variance Homogeneity Test - Levene's test
# H0: Homogeneity of Variance is provided.
# H1: Homogeneity of Variance is not provided.
stats.levene(control_purchase, test_purchase)
# p value is higher than 0.05 so we cant reject H0, Homogeneity of Variance is provided.
# In this case, both two conditions are provided.

# AB Testing hypothesis
# H0: There is no statistical difference between the control and test groups in terms of the average number of purchases
# H1: There is statistical difference between the control and test groups in terms of the average number of purchases.
test_statistic, p_value = stats.ttest_ind(control_purchase, test_purchase, equal_var=True)

print('Test Statistic = %.4f, p-value = %.4f' % (test_statistic, p_value))
# p value is higher than 0.05 so we can't reject H0.
# There is no statistical difference between the control and test groups in terms of the average number of purchases.


# This Program to insert Random Data in all six tables
import cx_Oracle
import random

def connectdatabase():
    fp = open("database.txt", 'r')
    if fp:
        c = fp.read()
        fp.close()
        return c
    else:
        print("File is not opened!")

firstname = ['Dipak', 'Atharv', 'Bhupendra',  'Rohit', 'Saurabh', 'Jayesh', 'Shivam', 'Kalpesh',
             'Atul',  'Yash', 'Ashwini', 'Rina', 'Anuja', 'Diksha', 'Khushi', 'Neha', 'Sarika', 'Puja']

g = ['Ashwini', 'Rina', 'Anuja', 'Diksha', 'Khushi', 'Neha', 'Sarika', 'Puja']

lastname = ['Mali', 'Mahajan', 'Patil', 'Garud', 'Salunke', 'Yogi', 'Koli', 'Thakur', 'Magare',
            'Nikam', 'Hire', 'Wakade', 'Tawade', 'Deshmukh', 'More', 'Lokhande', 'Chauhan', 'Mahale']

a = [18, 19, 20, 21]

address = ['Chopda', 'Jalgaon', 'Dhule', 'Nundurbar', 'Shirpur', 'Chalisgaon']

c = cx_Oracle.connect(connectdatabase())
if c != None:
    print("Connected")
    cur = c.cursor()
    branch = ['com', 'ds', 'entc', 'cevil', 'mech', 'ele']
    for j in range(6):
        for i in range(1, 51):
            rn = i
            name = random.choice(firstname)+' '+random.choice(lastname)
            prn = 20110603+i
            age = random.choice(a)
            if name.split()[0] in g:
                gen = 'F'
            else:
                gen = 'M'
            d = random.randint(1, 28)
            m = random.randint(1, 12)
            y = 2021-age
            dob = f'{str(d)}/{str(m)}/{str(y)}'
            phn = '80555*****'
            email = f'{name.split()[0]}{str(rn)}@gmail.com'
            add = random.choice(address)
            cgpa = random.uniform(2.5, 10.0)
            q = f"INSERT INTO {branch[j]} VALUES({int(rn)}, '{name}', {int(prn)}, {int(age)}, '{gen}', '{dob}', '{phn}', '{email}', '{add}',{cgpa})"
            cur.execute(q)
    c.commit()
    c.close()
else:
    print(c)
    print('Failed...')
print('Random data of 50 students has been added for each branch :)')


"""
Title: Trial Num Bot Endpoint
Date Started: Feb 8, 2022
Version: 1
Version Start: Feb 8, 2022
Author: David Hyongsik Choi
Legal:  All rights reserved.  This code may not be used, distributed, or copied without the express written consent of David Hyongsik Choi.
Purpose:  To provide API endpoint for Age bot.
"""
# IMPORT TOOLS
#   STANDARD LIBRARY IMPORTS
#   THIRD PARTY IMPORTS
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
from dashappobject import app
import plotly.express as px
import pandas as pd
#   LOCAL APPLICATION IMPORTS
from Modules.stats_profilers import stat_profilerv3
from ..dashinputs import prompt_builder, gen_tablecontents, dash_inputbuilder
from ..botclasses import BotParams
from Modules.referencetools.statresearch.trialnumbot import num_trial_bot, num_trial_bot_report
from ..os_functions import get_currentscript_filename
from formatting import formatting_schema

dccgraph_config = formatting_schema['dccgraph_config']
figure_layout_mastertemplate = formatting_schema['figure_layout_mastertemplate']

bp = BotParams(
    get_currentscript_filename(__file__),
    'Trial Num Bot',
    "The Trial Num Bot returns the number of trials recommended to run based on how much an effect you desire an additional sample should have on the overall mean of the population.",
    num_trial_bot
)
tbodydata = [
    {
        'id': f'randmin_{bp.botid}',
        'placeholdertext': 'min value',
        'prompt': 'Enter the smallest value a random sample can have',
        'inputtype': 'number'
        },
    {
        'id': f'randmax_{bp.botid}',
        'placeholdertext': 'max value',
        'prompt': 'Enter the largest value a random sample can have',
        'inputtype': 'number'
        },
    {
        'placeholdertext': 'tolerance level',
        'id': f'tolerance_{bp.botid}',
        'prompt': "Set the tolerance.  This is the number an additional sample's effect on the overall mean of the population must stay below for that sample's effect to be considered statistically insignificant.",
        'details': 'For example, entering 0.025 means that for an additional sample to have no significant impact on the overall population, it would have to have an impact of less than 2.5%, meaning it may only move the overall population average by an amount less than that.',
        'inputtype': 'number',
        'min': 0
        },
    {
        'placeholdertext': 'window proportion',
        'id': f'win_prop_{bp.botid}',
        'prompt': 'Set the proportion of all samples starting with most recent that you want to include when considering the effect a sample has on the overall mean.',
        'details': 'For example, entering 0.5 means that the bot will use the most recent 50% of all trial samples to determine what is the recent maximum effect an additional sample had on the overall population.',
        'inputtype': 'number',
        'min': 0,
        'max': 1
        }
]

layout = html.Div([
    html.Div([
        html.Table(gen_tablecontents(tbodydata)),
        prompt_builder({
            'id': f'submitbutton_{bp.botid}',
            'inputtype': 'button_submit'
            })
        ], id=f'input_{bp.botid}'),
    html.Div(id=f'output_{bp.botid}')
])

@app.callback(
    Output(f'output_{bp.botid}', 'children'),
    Input(f'submitbutton_{bp.botid}', 'n_clicks'),
    State(f'randmin_{bp.botid}', "value"),
    State(f'randmax_{bp.botid}', "value"),
    State(f'tolerance_{bp.botid}', "value"),
    State(f'win_prop_{bp.botid}', "value"),
    prevent_initial_call=True
    )
def calc_numtrialbot(
        n_clicks,
        randmin,
        randmax,
        tolerance,
        win_prop
        ):
    s = num_trial_bot(randmin, randmax, tolerance, win_prop)
    report = num_trial_bot_report(s)
    df = pd.DataFrame(data=s)
    statdf = pd.DataFrame(data=[stat_profilerv3(s[-1]['data'])])
    return [
        html.Div([html.P(i) for i in report]),
        html.Div([
            html.P('Stat summary of all trial samples:'),
            dash_inputbuilder({
                'data': statdf.to_dict('records'),
                'inputtype': 'table',
                'id': f"statsumtable_{bp.botid}"
                })
            ]),
        dcc.Graph(figure=px.line(
            df.iloc[:, :-2],
            x='trial',
            y=['sample', 'avg_before', 'avg_after', 'sample_effect', 'recent_max_effect', 'tolerance'],
            markers=False,
            template=figure_layout_mastertemplate
            ), config=dccgraph_config)
    ]


from pypet import ComputedLevel, Hierarchy, Dimension, Query, Member
from sqlalchemy.sql import func, extract, cast, select
from sqlalchemy import types

to_char = func.to_char

FORMAT_FUNCTIONS = {
        'year': lambda x: to_char(x, 'YYYY'),
        'month': lambda x: to_char(x, 'YYYY-MM'),
        'day': lambda x: to_char(x, 'YYYY-MM-DD'),
}

class TimeLevel(ComputedLevel):

    def __init__(self, name, column, time_slice=None):
        if time_slice is None:
            time_slice = name

        def partial_trunc(column):
            return func.date_trunc(time_slice, column)

        def partial_extract(column):
            return extract(time_slice, column)
        label_expression = FORMAT_FUNCTIONS.get(time_slice, partial_extract)
        super(TimeLevel, self).__init__(name, column,
                function=partial_trunc, label_expression=label_expression)

    def __getitem__(self, key):
        bind = self.column.table.bind
        values = list(bind.execute(select([
                self.function(cast(key, types.Date)).label('id'),
                self.label_expression(cast(key, types.Date)).label('label')])
                ))[0]
        return Member(self, values.id, values.label)

class TimeDimension(Dimension):

    def __init__(self, name, column, time_levels):
        levels = [TimeLevel(level, column) for level in time_levels]
        super(TimeDimension, self).__init__(name, [Hierarchy('default',
            levels)])