import pygame
from Chess_logic import logic
from GUI import menu
#from Speech_to_moves import VoiceControl
from Speech_to_moves_2 import SpeechToMoves
from Chess_AI import ChessAI
from Moves_to_commands import moves_to_commands
from Commands_to_serial import Serial_sender
from Moves_to_commands import solver
import time

"""Test function before chess logic is done"""

promotion_map = {(0, 0): "r", (0, 1): "n", (1, 0): "b", (1, 1): "q"}
_STOCKFISH_ENGINE_PATH = "/opt/homebrew/bin/stockfish"
_SERIAL_PORT = "/dev/tty.usbserial-1130"
_SERVO_OFF = 60
_SERVO_ON = 0

# HARDCODED CASTLE MOVES 0 - WHITE SHORT CASTLE, 1 - WHITE LONG, 2 - BLACK SHORT, 3 - BLACK LONG
_CASTLE_MOVES = [[[(7, 0), (6.5, 0.5)], [(4, 0), (6, 0)], [(6.5, 0.5), (5, 0.5), (5, 0)]],
                 # WHITE SHORT CASLTE
                 [[(0, 0), (0.5, 0.5)], [(4, 0), (2, 0)], [(0.5, 0.5), (3, 0.5), (3, 0)]],
                 # WHITE LONG CASTLE
                 [[(7, 7), (6.5, 6.5)], [(4, 7), (6, 7)], [(6.5, 6.5), (5, 6.5), (5, 7)]],
                 # BLACK SHORT CASTLE
                 [[(0, 7), (0.5, 6.5)], [(4, 7), (2, 7)], [(0.5, 6.5), (3, 6.5), (3, 7)]],
                 # BLACK LONG CASTLE
                 ]

_WHITE_SHORT = 0
_WHITE_LONG = 1
_BLACK_SHORT = 2
_BLACK_LONG = 3
_SERVO_WAIT = 0.3
_MOTOR_SPEED = 50
_MOTOR_ACCEL = 200
_MOTORS_ON = 5
_MOTOR_OFF = 4

class ChessGUI(object):
    pygame.init()
    pygame.mixer.init()
    _WIDTH = 480
    _HEIGHT = 480
    _NUM_PIECES = 12
    _RECT_SIZE = 60
    _PIECE_IMG_SIZE = 45

    _LIGHT_CHESS_COLOR = "#eeeed2"
    _DARK_CHESS_COLOR = "#769656"
    _BG_COLOR = "#2d3436"

    _VIABLE_MOVE_COLOR = (116, 125, 140, 75)
    _VIABLE_MOVE_SURF = pygame.Surface([_RECT_SIZE, _RECT_SIZE], pygame.SRCALPHA)
    pygame.draw.circle(_VIABLE_MOVE_SURF, _VIABLE_MOVE_COLOR, [_RECT_SIZE * 0.5, _RECT_SIZE * 0.5], _RECT_SIZE * 0.20)

    _BOARD_IMG_OFFSET = [0, 0]

    _SELECTED_RECT_COLOR = (186, 202, 68)

    _HOVER_COLOR = (241, 242, 246, 200)
    _HOVER_SURF = pygame.Surface([_RECT_SIZE, _RECT_SIZE], pygame.SRCALPHA)
    pygame.draw.rect(_HOVER_SURF, _HOVER_COLOR, [0, 0, _RECT_SIZE, _RECT_SIZE], 5)

    """---AUDIO STUFF---"""
    _NORMAL_MOVE_SOUND = pygame.mixer.Sound("GUI/src/normal_move.mp3")
    _CAPTURE_MOVE_SOUND = pygame.mixer.Sound("GUI/src/capture.mp3")
    _CHECK_MOVE_SOUND = pygame.mixer.Sound("GUI/src/move-check.mp3")
    _PROMOTION_MOVE_SOUND = pygame.mixer.Sound("GUI/src/promote.mp3")
    _CASTLE_MOVE_SOUND = pygame.mixer.Sound("GUI/src/castle.mp3")
    """--- GAME MODES ---"""
    _PVP = 1
    _PVAI = 2
    _AIVAI = 3

    promotion_x, promotion_y = _WIDTH / 2 - _PIECE_IMG_SIZE * 1.5, _HEIGHT / 2 - _PIECE_IMG_SIZE * 1.5

    _font = pygame.font.SysFont("Arial", 15, False)
    _font.render("a", True, "White")
    _letters_surf = pygame.Surface([_WIDTH, 20])
    _letters_surf.fill("#2d3436")
    _nums_surf = pygame.Surface([20, _HEIGHT])
    _nums_surf.fill("#2d3436")
    _letters = []
    for i in range(8):
        _letters.append(_font.render(chr(i + 97), True, "White"))
    _nums = []
    for i in range(8):
        _nums.append(_font.render(str(8 - i), True, "White"))
    for i, letter in enumerate(_letters):
        _letters_surf.blit(letter,
                           (i * _WIDTH / 8 + _WIDTH / 16 - letter.get_width() / 2, 10 - letter.get_height() / 2))
    for i, num in enumerate(_nums):
        _nums_surf.blit(num, (10 - num.get_width() / 2, i * _HEIGHT / 8 + _HEIGHT / 16 - num.get_height() / 2))

    def __init__(self):
        """---------- VISUAL STUFF --------"""
        self._PROMOTION_POPUP_WHITE = pygame.Surface([3 * self._PIECE_IMG_SIZE, 3 * self._PIECE_IMG_SIZE])
        self._PROMOTION_POPUP_BLACK = pygame.Surface([3 * self._PIECE_IMG_SIZE, 3 * self._PIECE_IMG_SIZE])
        self.piece_imgs = self.__load_images()
        self.chess_board = pygame.Surface([8 * self._RECT_SIZE, 8 * self._RECT_SIZE])
        self._mouse_down = False
        self._clicked_pos = None
        self._hover_pos = None
        self._choosing_promotion = False
        self._choosing_promotion_color = None
        self._promotion_move = None
        """----------- FUNCTIONAL STUFF -----------"""
        self.__logic = logic.ChessBoard()
        self.valid_moves = []
        self._speech_recog = None
        self._stockfish = None
        self._stockfish2 = None
        self.my_color = -1
        self._recently_played_move = ""
        self._recent_promotion = ""
        self._was_castle = False
        self._castle_type = 0
        self._was_capture = False
        self._captured_pos = None
        self._mode = self._PVP
        self._move_calc = moves_to_commands.MoveToCmds()
        self._move_calc_optimal = solver.A_star_solver()
        self._sender = None
        self._use_serial = False
        self.game_ended = False

    def __load_images(self):
        """
        Loads images from "/src" directory
        :return: dictionary of white and black images
        """
        images = {'b': dict(), 'w': dict()}
        for i in range(1, self._NUM_PIECES + 1):
            if i <= 6:
                img = pygame.image.load("GUI/src/w" + str(i) + ".svg")
                images["w"][i] = img
            else:
                img = pygame.image.load("GUI/src/b" + str(i - 6) + ".svg")
                images["b"][i - 6] = img

        self._PROMOTION_POPUP_WHITE.fill((45, 52, 54))
        self._PROMOTION_POPUP_BLACK.fill((241, 242, 246))
        self._PROMOTION_POPUP_WHITE.blit(images["w"][2], (0.5 * self._PIECE_IMG_SIZE, 0.5 * self._PIECE_IMG_SIZE))
        self._PROMOTION_POPUP_WHITE.blit(images["w"][3], (1.5 * self._PIECE_IMG_SIZE, 0.5 * self._PIECE_IMG_SIZE))
        self._PROMOTION_POPUP_WHITE.blit(images["w"][4], (0.5 * self._PIECE_IMG_SIZE, 1.5 * self._PIECE_IMG_SIZE))
        self._PROMOTION_POPUP_WHITE.blit(images["w"][5], (1.5 * self._PIECE_IMG_SIZE, 1.5 * self._PIECE_IMG_SIZE))

        self._PROMOTION_POPUP_BLACK.blit(images["b"][2], (0.5 * self._PIECE_IMG_SIZE, 0.5 * self._PIECE_IMG_SIZE))
        self._PROMOTION_POPUP_BLACK.blit(images["b"][3], (1.5 * self._PIECE_IMG_SIZE, 0.5 * self._PIECE_IMG_SIZE))
        self._PROMOTION_POPUP_BLACK.blit(images["b"][4], (0.5 * self._PIECE_IMG_SIZE, 1.5 * self._PIECE_IMG_SIZE))
        self._PROMOTION_POPUP_BLACK.blit(images["b"][5], (1.5 * self._PIECE_IMG_SIZE, 1.5 * self._PIECE_IMG_SIZE))
        return images

    @staticmethod
    def __get_piece_color(chess_piece):
        piece_color = None
        if chess_piece != 0:
            piece_color = "b"
            if chess_piece < 0:
                piece_color = "w"
        return piece_color

    def __draw_chess_board(self, surf: pygame.Surface):
        """
        Draws chess board with pieces
        :param surf: pygame.Surface where the board will be drawn
        :return:
        """
        for row in range(8):
            for col in range(8):
                square_col = self._DARK_CHESS_COLOR
                if (row + col) % 2 == 0:
                    square_col = self._LIGHT_CHESS_COLOR
                if [row, col] == self._clicked_pos:
                    square_col = self._SELECTED_RECT_COLOR
                pygame.draw.rect(surf, square_col,
                                 [col * self._RECT_SIZE, row * self._RECT_SIZE, self._RECT_SIZE, self._RECT_SIZE])
                chess_piece = self.__logic.get_piece(row, col)
                piece_color = self.__get_piece_color(chess_piece)
                if piece_color:
                    x = col * self._RECT_SIZE + self._RECT_SIZE * 0.5 - self.piece_imgs[piece_color][
                        abs(chess_piece)].get_width() * 0.5
                    y = row * self._RECT_SIZE + self._RECT_SIZE * 0.5 - self.piece_imgs[piece_color][
                        abs(chess_piece)].get_height() * 0.5
                    if [row, col] == self._clicked_pos and self._mouse_down:
                        pass
                    else:
                        surf.blit(self.piece_imgs[piece_color][abs(chess_piece)], (x, y))
            """--- DRAW IT SEPARATELY ON TOP TO NOT BE OBSTRUCTED ---"""
            if self._mouse_down:
                chess_piece = self.__logic.get_piece(*self._clicked_pos)
                piece_color = self.__get_piece_color(chess_piece)
                row, col = self.__get_row_col(self._hover_pos[0], self._hover_pos[1])
                surf.blit(self._HOVER_SURF, (col * self._RECT_SIZE, row * self._RECT_SIZE))
                if piece_color:
                    surf.blit(self.piece_imgs[piece_color][abs(chess_piece)],
                              [self._hover_pos[0] - self._RECT_SIZE / 2, self._hover_pos[1] - self._RECT_SIZE / 2])
                """--- DRAW ALL VIABLE MOVES ---"""
                for move in self.valid_moves:
                    surf.blit(self._VIABLE_MOVE_SURF, [move[1] * self._RECT_SIZE, move[0] * self._RECT_SIZE])
            if self._choosing_promotion:

                if self._choosing_promotion_color == 1:
                    surf.blit(self._PROMOTION_POPUP_BLACK, (self.promotion_x, self.promotion_y))
                else:
                    surf.blit(self._PROMOTION_POPUP_WHITE, (self.promotion_x, self.promotion_y))

    def __get_row_col(self, x, y):
        return [y // self._RECT_SIZE, x // self._RECT_SIZE]

    def __detect_special_moves(self, move, promotion):
        self._was_castle = False
        self._was_capture = False
        move_start_piece = self.__logic.get_piece(*move[0])
        move_end_piece = self.__logic.get_piece(*move[1])
        dr, dc = move[0][0] - move[1][0], move[0][1] - move[1][1]
        # castle detection
        if abs(move_start_piece) == 6 and (abs(dr) > 1 or abs(dc) > 1):
            self._was_castle = True
            if logic.sign(move_start_piece) == -1:  # white
                if move[1][1] < 3:
                    self._castle_type = _WHITE_LONG  # long
                elif move[1][1] > 5:
                    self._castle_type = _WHITE_SHORT  # short
            else:  # black
                if move[1][1] < 3:
                    self._castle_type = _BLACK_LONG  # long
                elif move[1][1] > 5:
                    self._castle_type = _BLACK_SHORT  # short
        # en passant detection
        elif abs(move_start_piece) == 1 and dc != 0 and move_end_piece == 0:
            self._was_capture = True
            self._captured_pos = move[1]
        else:
            if move_end_piece != 0:
                self._was_capture = True
                self._captured_pos = move[1]

    def __play_sound(self, promotion):
        if self._was_castle:
            pygame.mixer.Sound.play(self._CASTLE_MOVE_SOUND)
        elif promotion != "":
            pygame.mixer.Sound.play(self._PROMOTION_MOVE_SOUND)
        elif self._was_capture:
            pygame.mixer.Sound.play(self._CAPTURE_MOVE_SOUND)
        else:
            pygame.mixer.Sound.play(self._NORMAL_MOVE_SOUND)

    def _determine_move(self):
        new_row, new_col = self.__get_row_col(*self._hover_pos)
        move = [self._clicked_pos, [new_row, new_col]]
        if self.__logic.is_valid_move(move):
            if (self.__logic.get_piece(*self._clicked_pos) == 1 and new_row == 7) or (
                    self.__logic.get_piece(*self._clicked_pos) == -1 and new_row == 0):
                # SHOW POPUP
                self._choosing_promotion = True
                self._choosing_promotion_color = logic.sign(self.__logic.get_piece(*self._clicked_pos))
                self._mouse_down = False
                self._promotion_move = move
                return
            self.play_move(move)
        self._mouse_down = False
        self._clicked_pos = None

    def __init_speech(self) -> None:
        """
        Initializes speech recognition module and starts new thread
        :return: None
        """
        #self._speech_recog = VoiceControl.VoiceControl(debug=True, info=True, recordTime=4, lan="en-US")
        self._speech_recog = SpeechToMoves.SpeechToMoves("Speech_to_moves_2/coordinates.dict")
        self._speech_recog.start()

    def __init_stockfish(self, stockfish=1) -> None:
        """
        Initializes stockfish engine and sets parameters
        :return: None
        """
        if stockfish == 1:
            self._stockfish = ChessAI.ChessAI(_STOCKFISH_ENGINE_PATH)
            self._stockfish.set_parameters(skill_level=0)
        elif stockfish == 2:
            self._stockfish2 = ChessAI.ChessAI(_STOCKFISH_ENGINE_PATH)
            self._stockfish2.set_parameters(skill_level=20)

    def __init_serial(self) -> None:
        """
        Init serial obj for sending data over serial
        :return:
        """
        self._sender = Serial_sender.SerialSender(_SERIAL_PORT)
        self._sender.send_bare_command(_MOTORS_ON)
        self._sender.send_set_speed(_MOTOR_SPEED)
        self._sender.send_set_acceleraton(_MOTOR_ACCEL)
        self._sender.send_set_servo(_SERVO_OFF)

    def _eval_player_move(self, event):
        """
        Evaluates what move is being played based on users input
        :param event: pygame.event
        :return:
        """
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == pygame.BUTTON_LEFT:
                pos = pygame.mouse.get_pos()
                if not self._choosing_promotion:
                    self._clicked_pos = self.__get_row_col(pos[0] - self._BOARD_IMG_OFFSET[0],
                                                           pos[1] - self._BOARD_IMG_OFFSET[1])
                    if self.__logic.get_piece(*self._clicked_pos) != 0:
                        self._mouse_down = True
                        self.valid_moves = self.__logic.get_valid_moves(self._clicked_pos)
                    else:
                        self._clicked_pos = None

        if event.type == pygame.MOUSEBUTTONUP:
            if not self._choosing_promotion:
                if event.button == pygame.BUTTON_LEFT and self._mouse_down:
                    self._determine_move()
            else:
                if event.button == pygame.BUTTON_LEFT:
                    pos = pygame.mouse.get_pos()
                    x = round((pos[0] - self.promotion_x - self._PIECE_IMG_SIZE / 2 - self._BOARD_IMG_OFFSET[
                        0]) // self._PIECE_IMG_SIZE)
                    y = round((pos[1] - self.promotion_y - self._PIECE_IMG_SIZE / 2 - self._BOARD_IMG_OFFSET[
                        1]) // self._PIECE_IMG_SIZE)
                    if (x in [0, 1]) and (y in [0, 1]):
                        chosen_promotion = promotion_map[(y, x)]
                        self.play_move(self._promotion_move, chosen_promotion)
                        self._choosing_promotion = False

    def _play_stockfish_move(self, move, promotion="", stockfish=1):
        if self._was_castle:
            # castle
            if move[1][1] == 7:
                move[1][1] = 6
            elif move[1][1] == 0:
                move[1][1] = 2

        if stockfish == 1:
            if move:
                move = self._stockfish.get_move(logic.row_col_to_cmd(*move[0]) + logic.row_col_to_cmd(*move[1]) + promotion)
            else:
                move = self._stockfish.get_move("")
        elif stockfish == 2:
            if move:
                move = self._stockfish2.get_move(logic.row_col_to_cmd(*move[0]) + logic.row_col_to_cmd(*move[1]) + promotion)
            else:
                move = self._stockfish2.get_move("")
        move_rc = [logic.cmd_to_row_col(move[0:2]), logic.cmd_to_row_col(move[2:4])]

        if len(move) == 5:
            promotion = move[4]
        else:
            promotion = ""
        self.play_move(move_rc, promotion)

    def _get_coords(self, move):
        """
        # OLD VERSION - NOT OPTIMAL
        full_squares = []
        for row in range(8):
            for col in range(8):
                if self.__logic.get_piece(row, col) != 0:
                    full_squares.append([col, 7 - row])
        move = [[move[0][1], 7 - move[0][0]], [move[1][1], 7 - move[1][0]]]
        return self._move_calc.move(full_squares, move)
        """
        full_squares = []
        for row in range(8):
            for col in range(8):
                if self.__logic.get_piece(row, col) != 0:
                    full_squares.append((row, col))
        return self._move_calc_optimal.calc_path(full_squares, move)

    def __reset_board(self) -> None:
        print("PLAYER: ", self.__logic.get_player_playing() * -1, " WON")
        self.__logic.reset_board()
        self._recently_played_move = ""
        self._recent_promotion = ""
        if self._mode == self._PVAI:
            self.__init_stockfish()
        if self._mode == self._AIVAI:
            self.__init_stockfish(1)
            self.__init_stockfish(2)

    def _serial_capture(self):
        """
        Sends capture commands over serial
        :return:
        """
        """

        if move_recalc[1] > 3:
            dy = -0.5
        else:
            dy = 0.5
        if move_recalc[0] > 3:
            final_x = 0
        else:
            final_x = 7
        self._sender.send_move(move_recalc)
        self._sender.send_set_servo(_SERVO_ON)
        self._sender.wait_for_empty_buffer()
        time.sleep(_SERVO_WAIT)
        self._sender.send_move((move_recalc[0], move_recalc[1] + dy))
        # TODO: special capture command, currently only normal move
        self._sender.send_move((final_x, move_recalc[1] + dy))
        self._sender.send_set_servo(_SERVO_OFF)
        self._sender.wait_for_empty_buffer()
        time.sleep(_SERVO_WAIT)
        """
        coords = self._get_coords([(self._captured_pos[0], self._captured_pos[1]), (3.5, 7)])
        print("capture coords: ", coords)
        for i, coord in enumerate(coords):
            if i == 1:
                self._sender.send_set_servo(_SERVO_ON)
                self._sender.wait_for_empty_buffer()
                time.sleep(_SERVO_WAIT)
            self._sender.send_move(coord)

        self._sender.send_set_servo(_SERVO_OFF)
        self._sender.wait_for_empty_buffer()
        time.sleep(_SERVO_WAIT)
        self._sender.send_move((3.5, 3.5))
        self._sender.wait_for_empty_buffer()
        #Clear captured figure
        self._sender.send_remover()
        self._sender.wait_for_empty_buffer()
        time.sleep(0.5)
        print("move done")

    def _serial_castle(self):
        """
        Sends castle commands over serials
        :return:
        """
        for command_sequence in _CASTLE_MOVES[self._castle_type]:
            for i, command in enumerate(command_sequence):
                if i == 1:
                    self._sender.send_set_servo(_SERVO_ON)
                    self._sender.wait_for_empty_buffer()
                    time.sleep(_SERVO_WAIT)
                self._sender.send_move(command)
            self._sender.send_set_servo(_SERVO_OFF)
            self._sender.wait_for_empty_buffer()
            time.sleep(_SERVO_WAIT)

    def play_move(self, move, promotion="") -> None:
        """
        Plays move and handles all of the logic (sending to Arduino, updating board etc...)
        :param: move = [[start row, start col], [end row, end col]]
        :return: None
        """
        if self.__logic.is_valid_move(move, promotion=promotion):
            # play sound
            self.__detect_special_moves(move, promotion)
            self.__play_sound(promotion)
            print("playing move: ", move)
            if self._use_serial:
                cnc_coords = []
                if self._was_capture:
                    print("Capture move")
                    self._serial_capture()
                    cnc_coords = self._get_coords(move)
                elif self._was_castle:
                    print("Castle move: ", self._castle_type)
                    self._serial_castle()
                else:
                    print("move:", move)
                    cnc_coords = self._get_coords(move)

                if cnc_coords:
                    print("cnc coords:", cnc_coords)
                    for i, coord in enumerate(cnc_coords):
                        if i == 1:
                            self._sender.send_set_servo(_SERVO_ON)
                            self._sender.wait_for_empty_buffer()
                            time.sleep(_SERVO_WAIT)
                        self._sender.send_move(coord)

                    self._sender.send_set_servo(_SERVO_OFF)
                    self._sender.wait_for_empty_buffer()
                    time.sleep(_SERVO_WAIT)
                    print("move done")
            #print(self._get_coords(move))
            # Do the move
            self.__logic.play_move(move, promotion)
            self._recently_played_move = move
            self._recent_promotion = promotion
            if self.__logic.is_checkmate():
                print("PLAYER: ", "BLACK" if self.__logic.get_player_playing() * -1 == 1 else "WHITE", " WON")
                self.game_ended = True
                self._sender.send_set_servo(_SERVO_ON)
                self._sender.send_set_servo(_SERVO_OFF)
                self._sender.send_set_servo(_SERVO_ON)
                self._sender.send_set_servo(_SERVO_OFF)
                self._sender.send_set_servo(_SERVO_ON)
                self._sender.send_set_servo(_SERVO_OFF)
                self._sender.wait_for_empty_buffer()
                #self.__reset_board()
        else:
            print("Move not valid:", move, promotion)

    def draw(self, win: pygame.Surface) -> None:
        """
        Draws chess board to win
        :param win: pygame.Surface
        :return: None
        """
        win.fill(self._BG_COLOR)
        self.__draw_chess_board(self.chess_board)
        win.blit(self.chess_board, [0, 0])
        win.blit(self._letters_surf, [0, 480])
        win.blit(self._nums_surf, [480, 0])
        pygame.display.flip()

    def main(self) -> None:
        """---- SETUP ----"""
        win = pygame.display.set_mode((self._WIDTH + 20, self._HEIGHT + 20))
        pygame.display.set_caption("Speech Chess")
        """ --- SETTINGS SCREEN ---"""
        run = True
        q = False
        _menu = menu.Menu([self._WIDTH, self._HEIGHT])
        _speech_commands = False
        _serial_comm = False
        mode = self._PVP
        clock = pygame.time.Clock()
        while run:
            clock.tick(30)
            events = pygame.event.get()
            for event in events:
                if event.type == pygame.QUIT:
                    run = False
                    q = True
            if _menu.update(events):
                settings = _menu.get_settings()
                # [1v1, 1vAI, speech_commands]
                mode = None
                for i in range(3):
                    if settings[i]:
                        mode = i + 1

                _speech_commands = settings[3]
                _serial_comm = settings[4]
                print(_serial_comm)
                if mode is not None:
                    run = False
            win.fill("#2d3436")
            win.blit(_menu.screen, [10, 10])
            pygame.display.flip()

        """---- Start speech recog thread"""
        self._mode = mode
        if _speech_commands:
            self.__init_speech()
        if mode == self._PVAI:
            self.__init_stockfish()
        elif mode == self._AIVAI:
            self.__init_stockfish(1)
            self.__init_stockfish(2)
        if _serial_comm:
            self.__init_serial()
            self._use_serial = True
        """---- Main Loop ----"""
        run = not q

        """run = False
        self._castle_type = _WHITE_LONG
        self._serial_castle()
        """
        while run:
            clock.tick(30)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    run = False
                    if _speech_commands:
                        self._speech_recog.close()
                if not self.game_ended:
                    if (mode == self._PVAI and self.__logic.get_player_playing() != self.my_color) or mode == self._AIVAI:
                        pass
                    else:
                        self._eval_player_move(event)

            if not self.game_ended:

                if mode == self._PVAI and self.__logic.get_player_playing() != self.my_color:
                    self._play_stockfish_move(self._recently_played_move, self._recent_promotion)

                if mode == self._AIVAI:
                    if self.__logic.get_player_playing() == self.my_color:
                        self._play_stockfish_move(self._recently_played_move, self._recent_promotion, 1)
                    else:
                        self._play_stockfish_move(self._recently_played_move, self._recent_promotion, 2)

                if _speech_commands:
                    if self._speech_recog.available():
                        move = self._speech_recog.get_next_coordinate()
                        if move:
                            print(move)
                            move = [(7 - move[0][1], move[0][0]), (7 - move[1][1], move[1][0])]
                            if mode == self._PVAI and self.__logic.get_player_playing() != self.my_color:
                                pass
                            elif mode == self._PVAI and self.__logic.get_player_playing() == self.my_color:
                                if self.__logic.is_valid_move(move):
                                    print("Voice command valid- playing move: ", move)
                                    self.play_move(move)
                                else:
                                    print("voice command not valid!")

                            elif mode == self._PVP:
                                if self.__logic.is_valid_move(move):
                                    print("Voice command valid- playing move: ", move)
                                    self.play_move(move)
                                else:
                                    print("voice command not valid!")

                if self._mouse_down:
                    pos = pygame.mouse.get_pos()
                    self._hover_pos = [pos[0] - self._BOARD_IMG_OFFSET[0], pos[1] - self._BOARD_IMG_OFFSET[1]]
                self.draw(win)

        if self._use_serial:
            self._sender.send_move((0, 0))
            self._sender.wait_for_empty_buffer()
            self._sender.send_bare_command(4)
            self._sender.Serial.close()

if __name__ == "__main__":
    gui = ChessGUI()
    gui.main()


# Create the list of known users
known_users = ["Alice", "Bob", "Claire", "Dan", "Emma", "Fred", "Georgie", "Harry"]

while True:
    print("Hi! My Name is Travis")
    #use the strip funtion to remove the space after the text and capitalize
    name = input("What is your name?: ").strip().capitalize()

    if name in known_users:
        print("Hello {}!".format(name))
        remove = input("Would you like to be removed from the system? (y/n): ").strip().lower()

        if remove == "y":
            known_users.remove(name)
        elif remove == "n":
            print ("No problem, I didn't want you to leave anyway!")

    else:
        print("Hmmm I don't think I have met you yet {}".format(name))
        add_user = input("Would you like to be added to the system {}? (y/n): ").strip().lower()

        if add_user == "y":
            known_users.append(name)
        elif remove == "n":
            print ("No problem, I didn't want to add you anyway!")


from fastapi import APIRouter
from models.portfolio import Portfolio

router = APIRouter(
    prefix="/api/portfolio",
    tags=["portfolio"]
)

@router.get("/positions")
async def get_positions():
    positions = await Portfolio.get_positions()
    return positions

@router.get("/activities")
async def get_activities():
    activities = await Portfolio.get_activities()
    return activities

@router.get("/deposits")
async def get_deposits():
    deposits = await Portfolio.get_deposits()
    return deposits

@router.get("/orders")
async def get_orders():
    orders = await Portfolio.get_orders()
    return orders

@router.get("/history")
async def get_history():
    history = await Portfolio.get_history()
    return history

@router.get("/metrics")
async def get_metrics():
    metrics = await Portfolio.get_metrics()
    return metrics


# Code is being added tentatively. I need to fix a bunch of security holes.

from flask import Flask, render_template
from gmail import gmail, summarise_text
from build_email import Object, unpack_data
from flask_mail import Mail, Message

from aws import upload_file_to_s3

import os

from datetime import datetime, timedelta
from test import email_test

app = Flask(__name__)
mail = Mail(app)

# Configure the mail settings
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = os.environ.get("MAIL_USERNAME")
app.config['MAIL_PASSWORD'] = os.environ.get("MAIL_PASSWORD")
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True

mail = Mail(app)

def the_week_of():
    """
    Ensure we're only ever looking at the week that passed.

    :return: A string showing the date of today and 7 days prior in the format
        [SEVEN_DAYS_AGO] - [TODAY], [MONTH(s)]
    """
    # Get today's date
    today = datetime.now()

    # Get the date 7 days ago
    seven_days_ago = today - timedelta(days=7)

    # Format the dates
    today_str = today.strftime('%d %b')
    seven_days_ago_str = seven_days_ago.strftime('%d %b')

    # Get the months
    month_today = today.strftime('%b')
    month_seven_days_ago = seven_days_ago.strftime('%b')

    # Format the month(s)
    if month_today == month_seven_days_ago:
        month_str = month_today
    else:
        month_str = month_seven_days_ago + "/" + month_today

    return f"{seven_days_ago_str} - {today_str}, {month_str}"

def send_mail(email_list, url):
    """
    Send the newsletter after mail's been summarized.
    :param: email_list: The list of email objects that contain
        the heading, summary, sender and date
    :param: url: The storage location of the email banner.
    """

    msg = Message("This Week's Noise In Your Inbox",
        sender=os.environ.get("MAIL_USERNAME"),
        recipients=os.environ.get("RECIPIENT_EMAIL"))

    msg.html = render_template('email.html',
        email_list=email_list,
        name=os.environ.get("RECIPIENT_NAME"),
        week_of=the_week_of(),
        logo=url
    )
    mail.send(msg)

def index():
    """
    Sends a weekly newsletter about what's going on in your inbox.
    """
    # Get all emails
    email_list = gmail()

    # GPT that shit
    email = summarise_text(email_list)

    # Prepare the banner / this can be set to https://github.com/AlysonNumberFIVE/NoiseCancellingInbox/blob/main/resources/noiselogo.png
    url = os.environ.get("BANNER_LOCATION")

    # Extract that into the list of email summaries.
    email_objects = unpack_data(email)

    # Send the mail.
    send_mail(email_objects, url)

if __name__ == '__main__':
    index()


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1 or not l2:
            return None
        carry = 0
        dummy = ListNode(0)
        cur = dummy
        while l1 or l2:
            sum = carry
            if l1:
                sum += l1.val
                l1 = l1.next
            if l2:
                sum += l2.val
                l2 = l2.next
            carry = sum // 10
            digit = sum % 10
            cur.next = ListNode(digit)
            cur = cur.next
        if carry != 0:
            cur.next = ListNode(carry)
        return dummy.next


import numpy as np
import tensorflow as tf
from skimage.data import chelsea

from gcvit import GCViTXXTiny

def test_inference():
    # load test image, resize, and create batch
    img = tf.keras.applications.imagenet_utils.preprocess_input(
        chelsea(), mode="torch"
    )
    img = tf.image.resize(img, (224, 224))[
        None,
    ]

    model = GCViTXXTiny(pretrain=True)

    pred = model(img).numpy()
    result = tf.keras.applications.imagenet_utils.decode_predictions(pred, top=5)[0]
    result = np.array([x[2] for x in result])

    # should be five predictions for this pretrained model and image
    assert len(result) == 5

    # check if confidence values match with expected results
    expected_result = [
        ("n02124075", "Egyptian_cat", 0.7402193),
        ("n02123045", "tabby", 0.07757583),
        ("n02123159", "tiger_cat", 0.07006007),
        ("n02127052", "lynx", 0.0043750308),
        ("n04040759", "radiator", 0.00091264246),
    ]
    expected_result = np.array([x[2] for x in expected_result])
    np.allclose(result, expected_result, atol=1e-4)

def test_feature_extraction():
    # load test image, resize, and create batch
    img = tf.keras.applications.imagenet_utils.preprocess_input(
        chelsea(), mode="torch"
    )
    img = tf.image.resize(img, (224, 224))[
        None,
    ]

    model = GCViTXXTiny(pretrain=True)
    model.reset_classifier(num_classes=0, head_act=None)
    feature = model(img)
    shape_ = feature.shape
    print(shape_)

    assert shape_ == (1, 512)


import numpy as np

class Bunch(dict):
    """Dictionnary-like object thatexposes its keys as attributes."""

    def __init__(self, **kwargs):  # noqa: D102
        dict.__init__(self, kwargs)
        self.__dict__ = self

def _dol_to_lod(dol):
    """Convert a dict of lists to a list of dicts."""
    return [dict((key, dol[key][ii]) for key in dol.keys())
            for ii in range(len(dol[list(dol.keys())[0]]))]

def _bunchify(items):
    if isinstance(items, dict):
        items = _dol_to_lod(items)
    if len(items) > 0 and isinstance(items[0], dict):
        items = [Bunch(**item) for item in items]
    return items

def prepare_events_like_mne(eeg):
    eeg = Bunch(**eeg['EEG'])

    if not hasattr(eeg, 'event'):
        events = []
    elif isinstance(eeg.event, dict) and \
            np.array(eeg.event['latency']).ndim > 0:
        events = _dol_to_lod(eeg.event)
    elif not isinstance(eeg.event, np.ndarray):
        events = [eeg.event]
    else:
        events = eeg.event
    events = _bunchify(events)

    return events


# -*- coding: utf-8 -*-
import scrapy
import requests
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from scrapy.selector import Selector
from scrapy.http.request import Request
from MVID.settings import ANY_URL_REGEX_NEW
import re
from math import ceil
import json

from MVID.items import MvidItem

# def make_request():
#     domen = 'https://ru.investing.com'
#     url = "https://ru.investing.com/stock-screener/Service/SearchStocks"
#     referrer = [
#         #"https://ru.investing.com/stock-screener/?sp=country::5|sector::a|industry::a|equityType::a|exchange::a%3Ceq_market_cap;1",
#         "https://ru.investing.com/stock-screener/?sp=country::56|sector::a|industry::a|equityType::a%3Ceq_market_cap;1",
#     ]

#     bodies = [
#         #'country%5B%5D=56&sector=6%2C11%2C12%2C8%2C5%2C7%2C4%2C10%2C2%2C1%2C3%2C9&industry=72%2C51%2C88%2C56%2C8%2C19%2C67%2C43%2C89%2C15%2C80%2C59%2C41%2C37%2C55%2C52%2C2%2C93%2C48%2C57%2C1%2C22%2C71%2C82%2C76%2C46%2C94%2C95%2C102%2C58%2C101%2C30%2C29%2C35%2C53%2C16%2C34%2C47%2C12%2C66%2C90%2C14%2C49%2C17%2C44%2C68%2C27%2C11%2C74%2C85%2C63%2C83%2C84%2C26%2C21%2C79%2C36%2C64%2C62%2C60%2C24%2C20%2C54%2C33%2C4%2C81%2C40%2C98%2C70%2C99%2C42%2C39%2C92%2C65%2C18%2C73%2C77%2C25%2C7%2C10%2C78%2C86%2C87%2C38%2C31%2C6%2C91%2C61%2C97%2C45%2C13%2C100%2C23%2C96%2C9%2C69%2C32%2C75%2C5%2C28%2C50%2C3&equityType=ORD%2CDRC%2CPreferred%2CUnit%2CClosedEnd%2CREIT%2CELKS%2COpenEnd%2CRight%2CParticipationShare%2CCapitalSecurity%2CPerpetualCapitalSecurity%2CGuaranteeCertificate%2CIGC%2CWarrant%2CSeniorNote%2CDebenture%2CETF%2CADR%2CETC%2CETN&exchange%5B%5D=40&pn={}&order%5Bcol%5D=eq_market_cap&order%5Bdir%5D=d',
#         'country%5B%5D=5&sector=6%2C11%2C12%2C8%2C5%2C7%2C4%2C10%2C2%2C1%2C3%2C9&industry=72%2"C51%2C88%2C56%2C8%2C19%2C67%2C43%2C89%2C15%2C80%2C59%2C41%2C37%2C55%2C52%2C2%2C93%2C48%2C57%2C1%2C22%2C71%2C82%2C76%2C46%2C94%2C95%2C102%2C58%2C101%2C30%2C29%2C35%2C53%2C16%2C34%2C47%2C12%2C66%2C90%2C14%2C49%2C17%2C44%2C68%2C27%2C11%2C74%2C85%2C63%2C83%2C84%2C26%2C21%2C79%2C36%2C64%2C62%2C60%2C24%2C20%2C54%2C33%2C4%2C81%2C40%2C98%2C70%2C99%2C42%2C39%2C92%2C65%2C18%2C73%2C77%2C25%2C7%2C10%2C78%2C86%2C87%2C38%2C31%2C6%2C91%2C61%2C97%2C45%2C13%2C100%2C23%2C96%2C9%2C69%2C32%2C75%2C5%2C28%2C50%2C3&equityType=ORD%2CDRC%2CPreferred%2CUnit%2CClosedEnd%2CREIT%2CELKS%2COpenEnd%2CRight%2CParticipationShare%2CCapitalSecurity%2CPerpetualCapitalSecurity%2CGuaranteeCertificate%2CIGC%2CWarrant%2CSeniorNote%2CDebenture%2CETF%2CADR%2CETC%2CETN&exchange%5B%5D=2&exchange%5B%5D=95&exchange%5B%5D=1&pn={}&order%5Bcol%5D=eq_market_cap&order%5Bdir%5D=d',
#     ]

#     headers = {
#         "Host": "ru.investing.com",
#         "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:66.0) Gecko/20100101 Firefox/66.0",
#         "Accept": "application/json, text/javascript, */*; q=0.01",
#         "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3",
#         "Accept-Encoding": "gzip, deflate, br",
#         "Referer": "{}",
#         "Content-Type": "application/x-www-form-urlencoded",
#         "X-Requested-With": "XMLHttpRequest",
#     }

#     #scrapy.FormRequest( url = url, headers= headers, method= "POST", body = bodies)

#     links = []
#     for b , r in zip(bodies, referrer):
#         i = 1
#         while True:
#                 headers['Refferer'] = r
#                 req = requests.post( url = url, headers = headers, data= b.format(i))
#                 data = req.json()
#                 if 'error' in data  :
#                     break
#                 numPages = ceil(data['totalCount'] / 50) - 1

#                 print(data['pageNumber'],'/',numPages)
#                 for d in data['hits']:
#                     link = d['viewData']['link']
#                     if '?' in link:
#                         redirect_link = domen + link.replace('?', '-commentary?') + '\n'
#                     else:
#                         redirect_link = domen + link + '-commentary' + '\n'
#                     #print(redirect_link)
#                     links.append(redirect_link)
#                 if data['pageNumber'] == numPages:
#                     break

#                 i = i + 1
#     return links

# def change_link(value):
#     return value + "-commentary/"

class InvestingComSpider(CrawlSpider):
    name = 'investing_com'
    allowed_domains = ['ru.investing.com']
    #my_urls = make_request()
    #start_urls =  my_urls
    # ['https://ru.investing.com/stock-screener/?sp=country::56|sector::a|industry::a|equityType::a|exchange::40%3Ceq_market_cap;1',
    #               #'https://ru.investing.com/stock-screener/?sp=country::5|sector::a|industry::a|equityType::a%3Ceq_market_cap;1',
    #             ]
    rules = (
        #Rule(LinkExtractor( restrict_xpaths= ("//div[@class = 'resultsContainer stockScreener']"), process_value= change_link, allow='/equities/', unique=True), follow=True),
		Rule(LinkExtractor( restrict_xpaths= ("//div[@class = 'midDiv inlineblock']"), unique= True), callback='parse_page', follow=True),   #//div[contains(@class , 'sideDiv inlineblock text_align_lang_base_2')]//a[contains(.,@href])]
	)

    def parse_start_url(self, response):
        return self.parse_page(response)

    # def parse(self, response):
    #     headers= {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:48.0) Gecko/20100101 Firefox/48.0'}
    #     self.parse_item(response)
    #     #yield Request(self.start_urls[0], headers=headers, callback= self.parse_item)

    def parse_page(self, response):

        sel = Selector(response)

        comment_set = sel.xpath("//div[@class = 'comment js-comment']")
        for comment in comment_set:
            item = MvidItem()
            item['position'] = sel.xpath("//div[@class = 'instrumentHead']/h1/text()").extract_first()
            item['source'] = self.allowed_domains[0]
            main_comment = comment.xpath(".//div[@class = 'mainComment js-content']")
            item['main_comment_id'] = comment.xpath("./@data-comment-id").extract_first()
            item['comment_text'] = main_comment.xpath(".//span[@class = 'js-text']/text()").extract()
            item['comment_id'] = comment.xpath("./@data-comment-id").extract_first()
            item['user_id'] = comment.xpath("./@data-user-id").extract_first()
            item['user_name'] = main_comment.xpath(".//a[@class = 'js-user-link']//@alt").extract_first()
            item['date'] = main_comment.xpath(".//span[@class = 'js-date']/@comment-date-formatted").extract_first()
            item['selfurl'] = response.url
            if not item['comment_id'] or not item['user_name'] or not item['comment_text']:
                continue
            item['link'] = re.findall(ANY_URL_REGEX_NEW, main_comment.xpath(".//span[@class = 'js-text']/text()").extract()[0])

            reply_comments = comment.xpath(".//div[@class = 'commentReply js-comment js-comment-reply']")
            for reply in reply_comments:
                 self.parse_details(reply, item['comment_id'], item['position'], item['selfurl']) # comment.xpath("./@data-comment-id/text()")
            yield item

    def parse_details(self, response, main_comment_id, position, url):

        sel = response
        item = MvidItem()

        item['position'] = position
        item['source'] = self.allowed_domains[0]
        item['main_comment_id'] = main_comment_id
        item['comment_text'] = sel.xpath(".//span[@class = 'js-text']/text()").extract_first()
        item['comment_id'] = sel.xpath("./@data-comment-id").extract_first()
        item['user_id'] = sel.xpath("./@data-user-id").extract_first()
        item['user_name'] = sel.xpath(".//a[@class = 'js-user-link']//@alt").extract_first()
        item['date'] = sel.xpath(".//span[@class = 'js-date']/@comment-date-formatted").extract_first()
        item['selfurl'] = url
        if not item['comment_id'] or not item['user_name'] or not item['comment_text']:
            return
        item['link'] = re.findall(ANY_URL_REGEX_NEW, sel.xpath(".//span[@class = 'js-text']/text()").extract()[0]) #r'(https?://[^\s]+)'

        itemproc = self.crawler.engine.scraper.itemproc
        itemproc.process_item(item, self)

        return item


import torch.nn as nn
from torchvision.models import densenet
from models.boundary_aware_densenet import DenseNet
import torch.utils.model_zoo as model_zoo
import re
import numpy as np
import torch

class Dense201(DenseNet):
    def __init__(self, pretrained=True, num_classes=212, fmf_pretrain=None, **kwargs):
        super(Dense201, self).__init__(num_init_features=64, growth_rate=32, block_config=(6, 12, 48, 32),
                         num_classes=num_classes, **kwargs)
        if pretrained:
            pretrained_dict = dict(model_zoo.load_url(densenet.model_urls['densenet201']))
            tmp = pretrained_dict['features.conv0.weight']
            tmp = [tmp] * 16
            tmp = np.concatenate(tmp, axis=1)
            pretrained_dict['features.conv0.weight'] = torch.tensor(tmp)
            del pretrained_dict['classifier.weight']
            del pretrained_dict['classifier.bias']
            model_dict = self.state_dict()
            pattern = re.compile(
                r'^(.*denselayer\d+\.(?:norm|relu|conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$')
            for key in list(pretrained_dict.keys()):
                res = pattern.match(key)
                if res:
                    new_key = res.group(1) + res.group(2)
                    pretrained_dict[new_key] = pretrained_dict[key]
                    del pretrained_dict[key]

            model_dict.update(pretrained_dict)
            if not fmf_pretrain is None:
                used = {}
                state_dict = torch.load(fmf_pretrain)
                for k, v in state_dict.items():
                    if k == 'features.conv0.weight':
                        used[k] = v
                    elif 'fmf' in k:
                        used[k] = v
                model_dict.update(used)
            self.load_state_dict(model_dict)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x, heatmap):
        out = super(Dense201, self).forward(x, heatmap)
        out = self.sigmoid(out)
        return out

if __name__ == '__main__':
    import numpy as np
    import torch
    # heatmap =np.zeros((128, 128, 3), np.uint8)
    heatmap = torch.FloatTensor(np.zeros((1, 15, 128, 128), np.uint8))
    # print(heatmap)
    # print(heatmap.shape)
    featuremap = torch.FloatTensor(np.random.random((1, 48, 256, 256)))
    #假设featuremap是64通道的32*32
    net = Dense201(num_classes=212)

    out = net(featuremap, heatmap)


#!/usr/bin/env python3

# tidalnorway-py
# Quick and dirty scraper for water level data in Norway
# Simple version for water level only: http://api.sehavniva.no/tidepos_en.html
# More fancy one is here: http://api.sehavniva.no/tideapi_en.html
# Garten Position 63.642022, 9.498116
# A full request for one day looks like this:
# http://api.sehavniva.no/tideapi.php?lat=63.642022&lon=9.498116&fromtime=2017-07-05T00%3A00&totime=2017-07-06T00%3A00&datatype=tab&refcode=cd&place=&file=&lang=en&interval=10&dst=1&tzone=1&tide_request=locationdata

from lxml import html
from bs4 import BeautifulSoup
import requests
import datetime
from pytz import timezone

if __name__ == '__main__':
    print("tidalnorway-py supposed to be a class in the future")

    #t = timezone(settings.TIME_ZONE).localize(datetime.now()).replace(microsecond=0)
    current_time_utc_iso = datetime.datetime.utcnow().replace(microsecond=0).isoformat()
    print("Current datetime =", current_time_utc_iso)
    print("### RESPONSE FROM SERVER ###")
    #page = requests.get("http://forecast.weather.gov/MapClick.php?lat=37.7772&lon=-122.4168")
    lat = "63.642022"
    lon = "9.498116"
    fromtime = "2017-07-05T00:00"
    totime = "2017-07-06T00:00"
    datatype = "tab"
    refcode = "cd"
    place = ""
    file = ""
    lang = "en"
    interval = "10"
    dst = "1"
    tzone = "0"
    tide_request = "locationdata"

    payload = {'lat': lat, 'lon': lon, 'fromtime': fromtime, 'totime': totime, 'datatype': datatype, 'refcode': refcode, 'place': place, 'file': file, 'lang': lang, 'interval': interval, 'dst': dst, 'tzone': tzone, 'tide_request': tide_request}

    page = requests.get("http://api.sehavniva.no/tideapi.php", params=payload)
    soup = BeautifulSoup(page.content, 'lxml-xml')
    print(soup.prettify())


"""Contain set of CLI applications."""
from collections.abc import Callable
from pathlib import Path
from re import IGNORECASE
from re import search
from sys import argv
from typing import Any

from click import argument
from click import command
from click import option

from shell_tools.dirs.search import find_empty_dirs
from shell_tools.files.random import make_random_file
from shell_tools.git.misc import find_repositories
from shell_tools.git.misc import update_repository

def determine_file_size(size_string: str) -> int:
    multiplier = {"gb": 1024 * 1024 * 1024, "mb": 1024 * 1024, "kb": 1024}

    result = search(r"^(?P<number>\d+\.?\d*)(?P<multiplier>gb|mb|kb)?$", size_string, IGNORECASE)
    if result:
        number = float(result.group("number"))
        ratio = 1 if not result.group("multiplier") else multiplier[result.group("multiplier").lower()]
        return int(number * ratio)
    return 0

def get_program_name() -> str:
    return Path(argv[0]).stem

Processor = Callable[[list[Path]], None]

class ProcessorFactory:
    _state: dict[str, Any] = {}

    def __init__(self) -> None:
        self.__dict__ = self._state
        if not hasattr(self, "_registry"):
            self._registry: dict[str, Processor] = {}

    def register(self, name: str, processor: Processor) -> Processor:
        self._registry[name] = processor
        return processor

    @property
    def processors(self) -> dict[str, Processor]:
        return self._registry

    def get_processor(self, name: str) -> Processor:
        processor = self._registry.get(name)
        if not processor:
            raise KeyError(f"Unknown processor '{name}'")

        return processor

def dirs_processor(name: str) -> Callable[[Processor], Processor]:
    factory = ProcessorFactory()

    def decorator(processor: Processor) -> Processor:
        return factory.register(name, processor)

    return decorator

@dirs_processor(name="print")
def print_dirs(dirs: list[Path]) -> None:
    print(*dirs, sep="\n")

@dirs_processor(name="remove")
def remove_dirs(dirs: list[Path]) -> None:
    for path in dirs:
        print(f"removing '{path}'")
        path.rmdir()

def get_dirs_processor(name: str) -> Processor:
    factory = ProcessorFactory()
    return factory.get_processor(name)

@command()
@argument("path", type=Path)
@option("--size", type=str, default="10mb", help="File size in bytes[kb|mb|gb].", show_default=True)
@option("--line-size", type=int, default=64, help="Length of line/chunk.", show_default=True)
def generate_file(path: Path, size: str, line_size: int) -> None:
    path = path.absolute()
    if path.is_dir():
        print(f"Script aborted: '{path}' exists and it's directory")
        return

    if path.is_file():
        answer = input(f"file '{path}' already exists. override? [yes/no]: ")
        if not answer.lower().startswith("y"):
            return

    file_size = determine_file_size(size)
    if not file_size:
        print(f"Invalid file size was requested (file-size={size}).")
        print(f"Use '{get_program_name()} --help' to see allowed options.")
        return

    print(f"The file of {size} will be generated at '{path}'.")
    make_random_file(path, file_size, line_size)
    print("The generation is finished.")

@command()
@argument("root-dir", type=Path, default=Path.cwd())
@option("--ignore-empty-files", is_flag=True, help="Treat empty files as absent.")
@option("--remove/--no-remove", is_flag=True, help="Indicate remove or not empty dirs")
def discover_empty_dirs(root_dir: Path, ignore_empty_files: bool, remove: bool) -> None:
    root_dir = root_dir.absolute()
    if not root_dir.exists():
        print("The root directory is not set or doesn't exists.")

    empty_dirs = find_empty_dirs(root_dir, ignore_empty_files)
    processor = get_dirs_processor("remove" if remove else "print")
    processor(empty_dirs)

@command()
@argument("root-dir", type=Path, default=Path.cwd())
@option("-r", "--recursive", is_flag=True, help="Search git repos recursively")
@option("--submodules/--no-submodules", is_flag=True, help="Update or not submodules")
def sync_repos(root_dir: Path, recursive: bool, submodules: bool) -> None:
    root_dir = root_dir.absolute()
    suffix = "recursively " if recursive else ""
    print(f"Scanning '{root_dir}' for git repositories {suffix}...")

    repos = find_repositories(root_dir, recursive)
    for repo in repos:
        update_repository(repo, submodules)
        print(f"{repo.relative_to(root_dir)} was synced")

    input("Press Enter to exit...")


from viewclust.job_use import job_use
import pandas as pd

def get_users_run(jobs, d_from, target, d_to='', use_unit='cpu',
                  serialize_running=''):
    """Takes a DataFrame full of job information and
    returns usage for each "user"
    uniquely based on specified unit.

    This function operates as a stepping stone for plotting usage figures
    and returns various series and frames for several different uses.

    Parameters
    -------
    jobs: DataFrame
        Job DataFrame typically generated by slurm/sacct_jobs
        or the ccmnt package.
    use_unit: str, optional
        Usage unit to examine. One of: {'cpu', 'cpu-eqv', 'gpu', 'gpu-eqv'}.
        Defaults to 'cpu'.
    d_from: date str
        Beginning of the query period, e.g. '2019-04-01T00:00:00'.
    target: int-like
        Typically a cpu allocation or core eqv value for a particular acount.
        Often 50.
    d_to: date str, optional
        End of the query period, e.g. '2020-01-01T00:00:00'.
        Defaults to now if empty.
    serialize_running: str, optional
        Pickle given structure with argument as a name.
        If left empty, pickle procedure is skipped.
        Defaults to empty.

    Returns
    -------
    user_running_cat:
        Frame of running resources for each of the unique
        "users" in the jobs data frame.
    """

    users = jobs.user.unique()

    user_count = 0
    for user in users:
        user_mask = jobs['user'].str.match(user)
        user_jobs = jobs[user_mask].copy()
        _, user_queued, user_running, _ = job_use(user_jobs, d_from,
                                                  target, d_to=d_to,
                                                  use_unit=use_unit)
        user_queued=user_queued[d_from:d_to]
        user_running=user_running[d_from:d_to]

        if user_count == 0:
            user_running_cat = pd.Series(user_running,
                                         index=user_running.index,
                                         name=user)
        else:
            user_running_ser = pd.Series(user_running,
                                         index=user_running.index,
                                         name=user)
            user_running_cat = pd.concat([user_running_cat, user_running_ser],
                                         axis=1)

        user_count = user_count + 1

    if user_count == 1:
        user_running_cat = user_running_cat.to_frame()

    if serialize_running != '':
        user_running_cat.to_pickle(serialize_running)

    return user_running_cat


import calendar
from datetime import date

days = list(calendar.day_name)
def meetup_day(year, month, day_of_the_week, which):
    day_index = days.index(day_of_the_week)
    dates = [
        week[day_index]
        for week in calendar.monthcalendar(year, month)
        if week[day_index]]
    if which == 'teenth':
        for day_num in dates:
            if 13 <= day_num <= 19:
                return date(year, month, day_num)
    elif which == 'last':
        day_index = -1
    elif which == 'first':
        day_index = 0
    else:
        day_index = int(which[0]) - 1
    return date(year, month, dates[day_index])


from flask import Flask, render_template, request
from sklearn.cluster import KMeans
import ast
import json
import pandas as pd
from flask_cors import CORS
import plotly
import plotly.graph_objs as go
import plotly.express as px

app = Flask(__name__, template_folder="templates")
cors = CORS(app)
df = pd.read_csv('./winequality-red.csv')

@app.route('/')
def home():
    json_response = json.loads(df.to_json(orient='records'))
    print(json_response)
    return render_template('home.html', data=json.dumps(json_response))

@app.route('/displaycsv', methods=['POST', 'GET'])
def displaycsv():
    global df
    df = pd.read_csv('./winequality-white.csv')
    json_response = json.loads(df.to_json(orient='records'))
    return json.dumps(json_response)

@app.route('/labelUpdate', methods=['POST', 'GET'])
def updateLabel():
    arguments = request.args
    df_a1 = pd.read_csv('./dataset1_processed_updated.csv')
    numcols =['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week']
    labelcol = arguments['label']
    numcols.append(labelcol)
    global df_al
    df_a1_filtered = df_a1[numcols]
    json_response = json.loads(df_a1_filtered.to_json(orient='records'))
    return json.dumps(json_response)

@app.route('/applyAlgoParameters', methods=['POST', 'GET'])
def applyAlgoParamsKMeans():
    arguments = request.args
    rstate = arguments['randomstate']
    init = str(arguments['init'])
    if('means' in init):
        init =init.strip()
        init+="++"
    csvname = arguments['name']
    colormode = arguments['mode']
    print(csvname+":"+colormode)

    global df
    df = pd.read_csv('./' + csvname + '.csv')
    labelcol = df.columns
    labelcol = labelcol[-1]
    classes = df[labelcol].unique()

    if colormode != "Class-Based":
        del df[labelcol]
        kmeans = KMeans(n_clusters=len(classes),init=init,random_state=int(rstate))
        kmeans.fit(df)
        y_kmeans = kmeans.predict(df)
        labels = list(y_kmeans)
        labels = kmeans.labels_
        df['labels'] = labels
    json_response = json.loads(df.to_json(orient='records'))
    return json.dumps(json_response)

@app.route('/load', methods=['POST', 'GET'])
def readcsv():
    arguments = request.args
    csvname = arguments['name']
    print(csvname)
    global df
    df = pd.read_csv('./' + csvname + '.csv')
    json_response = json.loads(df.to_json(orient='records'))
    return json.dumps(json_response)

@app.route('/correlationMatrix', methods=['POST', 'GET'])
def calculateCorrelationMatrix():
    arguments = request.args
    try:
        cid = int(arguments['clusterid'])
    except ValueError:
        cid = arguments['clusterid']

    global df
    labelcol = df.columns
    labelcol = labelcol[-1]
    df_filtered = df[df[labelcol] == cid]
    x = list(df_filtered.columns)
    x.pop()
    fig = go.Figure(data=go.Heatmap(
        z=df_filtered.corr(),
        x=x,
        y=x,
        colorscale='Reds'
    ))

    fig['layout'].update(title="Correlation Matrix")
    json_response=json.loads(fig.to_json())
    return json.dumps(json_response)

@app.route('/visualizeA1Assignment', methods=['POST', 'GET'])
def getAssignment1Data():

    df_a1 = pd.read_csv('./dataset1_processed_updated.csv')
    df_a1_filtered = df_a1[['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week', 'salary']]
    json_response = json.loads(df_a1_filtered.to_json(orient='records'))
    return render_template('a1_dataset.html', data=json.dumps(json_response))

@app.route('/clusterizeA1', methods=['POST', 'GET'])
def clusterizeA1Data():
    df_a1 = pd.read_csv('./dataset1_processed_updated.csv')
    arguments = request.args
    lbl = arguments['label']
    numcols = ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week', lbl]
    colormode = arguments['mode']
    df_a1_filtered = df_a1[numcols]
    classes = df_a1_filtered[lbl].unique()

    if colormode != "Class-Based":
        del df_a1_filtered[lbl]
        kmeans = KMeans(n_clusters=len(classes))
        kmeans.fit(df_a1_filtered)
        y_kmeans = kmeans.predict(df_a1_filtered)
        labels =list(y_kmeans)
        labels = kmeans.labels_
        df_a1_filtered['labels'] = labels
    json_response = json.loads(df_a1_filtered.to_json(orient='records'))
    return json.dumps(json_response)

@app.route('/clusterize', methods=['POST', 'GET'])
def clusterizeData():
    arguments = request.args
    csvname = arguments['name']
    colormode = arguments['mode']
    print(csvname+":"+colormode)
    global df
    df = pd.read_csv('./' + csvname + '.csv')
    labelcol = df.columns
    labelcol = labelcol[-1]
    classes = df[labelcol].unique()

    if colormode != "Class-Based":
        del df[labelcol]
        kmeans = KMeans(n_clusters=len(classes))
        kmeans.fit(df)
        y_kmeans = kmeans.predict(df)
        labels =list(y_kmeans)
        labels = kmeans.labels_
        df['labels'] = labels
    json_response = json.loads(df.to_json(orient='records'))
    return json.dumps(json_response)

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


from dataclasses import dataclass
from ipaddress import IPv4Address, IPv6Address
from itertools import chain, islice
from typing import (
    AbstractSet,
    Iterable,
    Iterator,
    Mapping,
    MutableMapping,
    MutableSet,
    Sequence,
    Tuple,
    cast,
)

from std2.ipaddress import IPAddress

from .consts import SERVER_NAME
from .leases import leases
from .options.parser import settings
from .options.types import Accessible, PortForward, Protocol
from .types import DualStack, Networks

@dataclass(frozen=True)
class _Addrs:
    V4: IPv4Address
    V6: IPv6Address

@dataclass(frozen=True)
class _Dest:
    NAME: str
    ADDR: _Addrs

@dataclass(frozen=True)
class _Ported(_Dest):
    PROTO: Protocol
    PORT: int

@dataclass(frozen=True)
class _Forwarded(_Ported):
    FROM_PORT: int

@dataclass(frozen=True)
class _Available(_Ported):
    ...

@dataclass(frozen=True)
class Split(_Dest):
    DOMAIN: str

def _leased(networks: Networks) -> MutableMapping[str, MutableSet[IPAddress]]:
    trusted_ifs = len(settings().interfaces.trusted)
    guest_ifs = len(settings().interfaces.guest)

    leased: MutableMapping[str, MutableSet[IPAddress]] = {}
    for name, addr in leases():
        addrs = leased.setdefault(name, set())
        addrs.add(addr)

    addrs = leased.setdefault(SERVER_NAME, set())
    for addr in cast(
        Iterator[IPAddress],
        chain(
            islice(networks.guest.v4.hosts(), guest_ifs),
            islice(networks.guest.v6.hosts(), guest_ifs),
            islice(networks.trusted.v4.hosts(), trusted_ifs),
            islice(networks.trusted.v6.hosts(), trusted_ifs),
        ),
    ):
        addrs.add(addr)
    return leased

def _pick(
    leased: MutableMapping[str, MutableSet[IPAddress]], stack: DualStack, hostname: str
) -> Tuple[IPv4Address, IPv6Address]:
    lease_addrs = leased.setdefault(hostname, set())
    others = {addr for hn, addrs in leased.items() if hn != hostname for addr in addrs}

    v4 = next(
        chain(
            (addr for addr in lease_addrs if isinstance(addr, IPv4Address)),
            (addr for addr in stack.v4.hosts() if addr not in others),
        )
    )
    v6 = next(
        chain(
            (addr for addr in lease_addrs if isinstance(addr, IPv6Address)),
            (addr for addr in stack.v6.hosts() if addr not in others),
        )
    )

    lease_addrs.add(v4)
    lease_addrs.add(v6)
    return v4, v6

def forwarded_ports(
    networks: Networks,
) -> Tuple[AbstractSet[_Forwarded], AbstractSet[_Available], AbstractSet[Split]]:
    leased = _leased(networks)

    def c1(
        stack: DualStack, forwards: Mapping[str, Sequence[PortForward]]
    ) -> Iterator[_Forwarded]:
        for hostname, fws in forwards.items():
            for fwd in fws:
                for proto in fwd.protocols:
                    v4, v6 = _pick(leased, stack=stack, hostname=hostname)
                    spec = _Forwarded(
                        NAME=hostname,
                        PROTO=proto,
                        FROM_PORT=fwd.from_port or fwd.port,
                        PORT=fwd.port,
                        ADDR=_Addrs(V4=v4, V6=v6),
                    )
                    yield spec

    def c2(
        stack: DualStack, available: Mapping[str, Sequence[Accessible]]
    ) -> Iterator[_Available]:
        for hostname, accessible in available.items():
            for acc in accessible:
                for proto in acc.protocols:
                    v4, v6 = _pick(leased, stack=stack, hostname=hostname)
                    spec = _Available(
                        NAME=hostname,
                        PROTO=proto,
                        PORT=acc.port,
                        ADDR=_Addrs(V4=v4, V6=v6),
                    )
                    yield spec

    def c3(stack: DualStack, split: Mapping[str, AbstractSet[str]]) -> Iterator[Split]:
        for hostname, domains in split.items():
            for domain in domains:
                v4, v6 = _pick(leased, stack=stack, hostname=hostname)
                spec = Split(
                    NAME=hostname,
                    DOMAIN=domain,
                    ADDR=_Addrs(V4=v4, V6=v6),
                )
                yield spec

    fwd = {
        *c1(networks.wireguard, forwards=settings().port_forwards.wireguard),
        *c1(networks.trusted, forwards=settings().port_forwards.trusted),
        *c1(networks.guest, forwards=settings().port_forwards.guest),
    }
    available = {
        *c2(networks.wireguard, available=settings().guest_accessible.wireguard),
        *c2(networks.trusted, available=settings().guest_accessible.trusted),
    }
    split = {
        *c3(networks.wireguard, split=settings().dns.split_horizon.wireguard),
        *c3(networks.trusted, split=settings().dns.split_horizon.trusted),
        *c3(networks.guest, split=settings().dns.split_horizon.guest),
    }
    return fwd, available, split

def dhcp_fixed(fwds: Iterable[_Dest]) -> Iterator[_Dest]:
    seen: MutableSet[str] = set()
    for fwd in fwds:
        name = fwd.NAME
        if name not in seen:
            seen.add(name)
            yield fwd


from globalparameters import *
from global0 import *
import matplotlib.pyplot as plt

def get_trader(request_type):
    channel = grpc.insecure_channel(TRADING_CHANNEL)
    stub = broker_pb2_grpc.BrokerStub(channel)
    response = stub.get_trader(broker_pb2.TraderRequest(trader_id=TRADER_ID,
                                                       trader_pin=TRADER_PIN,
                                                        request_type=request_type))
    return response

def new_order(request_type, order_id, side, symbol, volume, price, is_market, pos_type):
    channel = grpc.insecure_channel(TRADING_CHANNEL)
    stub = broker_pb2_grpc.BrokerStub(channel)
    response = stub.new_order(broker_pb2.TraderRequest(trader_id=TRADER_ID,
                                                       trader_pin=TRADER_PIN,
                                                       request_type=request_type,
                                                       order_id=order_id,
                                                       side=side,
                                                       symbol=symbol,
                                                       volume=volume,
                                                       price=price,
                                                       is_market=is_market,
                                                       pos_type=pos_type))

    if list(response.orders.orders)!=[]:
        return list(response.orders.orders)[0]

def cancel_order(order_id):
    channel = grpc.insecure_channel(TRADING_CHANNEL)
    stub = broker_pb2_grpc.BrokerStub(channel)
    response = stub.cancel_order(broker_pb2.TraderRequest(trader_id=TRADER_ID,
                                                          trader_pin=TRADER_PIN,
                                                          request_type=common_pb2.CANCEL_ORDER,
                                                          order_id=order_id))
    return response

def record_performance(un_PNL):
    tmp = get_trader(common_pb2.FULL_INFO).positions
    positions = tmp.positions
    timestamp = tmp.timestamp

    tmp_PNL = {}
    for symbol in SYMBOLS:
        tmp_PNL[symbol] = 0

    if un_PNL is None:
        for symbol in SYMBOLS:
            un_PNL[symbol] = {'t':[],'value':[]}

    if hasattr(positions,'long_positions'):
        for symbol, value in enumerate(positions.long_positions):
            if hasattr(value,'realized_pnl'):
                tmp_PNL[symbol] = tmp_PNL[symbol] + value.realized_pnl
    if hasattr(positions,'short_positions'):
        for symbol, value in enumerate(positions.short_positions):
            if hasattr(value,'realized_pnl'):
                tmp_PNL[symbol] = tmp_PNL[symbol] + value.realized_pnl

    for symbol in SYMBOLS:
        un_PNL[symbol]['t'].append(timestamp)
        un_PNL[symbol]['value'].append(tmp_PNL[symbol])

def plotperformance(symbol, un_PNL):
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    plt.ion()
    plt.show()
    try:
        ax.lines.remove(lines[0])

    except Exception:
        pass

    lines = ax.plot(un_PNL[symbol]['t'], un_PNL[symbol]['value'] , "r-", lw=5)
    plt.pause(0.5)

# print(utils.get_trader(common_pb2.FULL_INFO))

def check_market_position(symbol):
    market_position = 0
    positions = get_trader(common_pb2.FULL_INFO).positions
    if hasattr(positions, 'long_positions') and positions.long_positions[symbol] is not None:
        if hasattr(positions.long_positions[symbol], 'volume'):
            if positions.long_positions[symbol].volume > 0:
                market_position = positions.long_positions[symbol].volume

    if hasattr(positions, 'short_positions') and positions.short_positions[symbol] is not None:
        if hasattr(positions.short_positions[symbol], 'volume'):
            if positions.short_positions[symbol].volume > 0:
                market_position = - positions.short_positions[symbol].volume
    return market_position


import turtle

screen=turtle.Screen()

start_button=turtle.Turtle()

start_button.color("green")
start_button.shape("square")
start_button.speed(0)
start_button.turtlesize(3)
start_button.penup()

reset_button=turtle.Turtle()

reset_button.color("yellow")
reset_button.shape("square")
reset_button.speed(0)
reset_button.turtlesize(3)
reset_button.penup()

stop_button=turtle.Turtle()

stop_button.color("red")
stop_button.shape("square")
stop_button.speed(0)
stop_button.turtlesize(3)
stop_button.penup()

start_button.goto(-120,-50)
reset_button.goto(-25,-50)
stop_button.goto(80,-50)

start_button.color("Black")
start_button.write("Start", move=False, align="left", font=("Arial", 20, "normal"))
start_button.goto(-100,-100)
start_button.color("Green")

reset_button.color("Black")
reset_button.write("Reset", move=False, align="left", font=("Arial", 20, "normal"))
reset_button.goto(0,-100)
reset_button.color("Yellow")

stop_button.color("Black")
stop_button.write("Stop", move=False, align="left", font=("Arial", 20, "normal"))
stop_button.goto(100,-100)
stop_button.color("Red")

global clock_interval , clock_on , current_count
clock_interval=1000
clock_on=False
current_count=0

time_turtle=turtle.Turtle()
time_turtle.hideturtle()
time_turtle.penup()
time_turtle.goto(-50,100)

def clock_change():
    global clock_on , current_count

    if clock_on==True:
        print("Got Here")
        time_turtle.clear()
        time_turtle.write("Timer: " + str(current_count))
        current_count+=1
        time_turtle.getscreen().ontimer(clock_change, clock_interval)

def clock_start(x,y):
    global clock_on , current_count
    print("timer started")
    clock_on=True

def clock_reset(x,y):
    global clock_on , current_count
    print("timer reset")
    current_count=0
    clock_on=True

def clock_stop(x,y):
    global clock_on , current_count
    print("timer stopped")
    clock_on=False

start_button.onclick(clock_start)
reset_button.onclick(clock_reset)
stop_button.onclick(clock_stop)

screen.ontimer(clock_change, clock_interval)

screen.mainloop()


import os

import matplotlib.pyplot as plt

def load_instance_from_file(filename):
  with open(filename, 'r') as file:
    instance_data = file.readlines()

    w = int(instance_data[0])
    n = int(instance_data[1])

    M = []
    for line in instance_data[2:]:
      row = list(map(int, line.split()))
      M.append(row)

    return w, n, M

def write_file_times(times, path):
  with open(path+".txt", 'w') as f:
    f.writelines("COMPUTATION TIME")
    f.writelines("\n%s" % t for t in times)
    f.writelines("\n"+"-"*5)
  f.close()

def write_file_output(solution, path):
  f = open(path, "w+")
  if not solution=={}:
    f.write("{} {}\n".format(*solution['sizes_plate']))
    f.write("{}\n".format(solution['n_circuits']))
    for i in range(len(solution['pos_circuits'])):
      f.write("{} {} {} {}\n".format(*solution['sizes_circuits'][i], *solution['pos_circuits'][i]))
  f.close()

def display_solution(solution, instance_index, folder):
  fig, ax = plt.subplots()
  ax.set_aspect('equal')
  cmap = plt.colormaps['rainbow']
  ax = plt.gca()
  plt.title(f"Solution {instance_index}")
  if len(solution['pos_circuits']) > 0:
    for i in range(solution['n_circuits']):
      rect = plt.Rectangle(solution['pos_circuits'][i], *solution['sizes_circuits'][i], edgecolor="#333", facecolor=cmap(i / (solution['n_circuits'] - 1)))
      ax.add_patch(rect)
  ax.set_xlim(0, solution['sizes_plate'][0])
  ax.set_ylim(0, solution['sizes_plate'][1] + 1)
  ax.set_xticks(range(solution['sizes_plate'][0] + 1))
  ax.set_yticks(range(solution['sizes_plate'][1] + 1))
  ax.set_xlabel('width_plate')
  ax.set_ylabel('height_plate')

  if not os.path.exists(folder):
    os.makedirs(folder)
  plt.savefig(f"{folder}/solution_{instance_index}.png")
  #plt.show()
  plt.close()

def format_solution(W, N, H, X, Y, widths, heights):
  solution = {}
  solution['sizes_plate'] = (W, H)
  solution['n_circuits'] = N
  solution['sizes_circuits'] = list(zip(widths, heights))
  solution['pos_circuits'] = list(zip(X, Y))
  return solution


import subprocess
import time
import shutil
import argparse

time_sta = time.perf_counter()
pro_sta = time.process_time()

parser = argparse.ArgumentParser()
parser.add_argument("--workers", type=int, required=False, default=1)
parser.add_argument("--testdir", type=str, required=False, default="/mnt/okinawa/test_dir")
args = parser.parse_args()

try :
    cmd = f"python3 danbooru.py {args.testdir} --start 5000000 --end 5000500 --workers {args.workers}"
    subprocess.call(cmd.split())
except:
    print("途中終了")
finally:
    time_end = time.perf_counter()
    pro_end = time.process_time()

    total = time_end - time_sta
    pro_total = pro_end - pro_sta

    print(f"total = {total} , process_time = {pro_total}")

    shutil.rmtree(args.testdir)


import itertools

def memoize(timeout, cache=None):
    '''
    Memoize the function with the given timeout and cache. If no cache is
    given, the 'default' cache is used.

    Calls are cached under keys based on their module "path" followed by
    the args/kwargs passed to them. For example:

        # foo/bar.py in the PYTHONPATH, i.e. module foo.bar
        @cache.memoize(300)
        def baz(a, **kwargs):
            ...

        baz(1, qux=2) # Result is cached under the key "foo.bar.baz(1,qux=2)"

    Note that kwargs are sorted so that f(a=1,b=2) and f(b=2,a=1) are given
    the same key. Note, however, that there's a bug when positional arguments
    are passed in by name, i.e. for the function...

        def f(arg):
            ...

    The calls f(1) and f(arg=1) will receive separate keys.
    There's a way to fix that by introspecting the arguments to f--I'll fix it
    if anyone reports it.

    Also note the use of repr(). As far as I can tell this will be unique for
    simple builtin types (float, int, str, etc.) and list/dict objects which
    contain those types. I'm leaving open the possibility of user-defined types
    being cached here because I can't see a strong reason not to, but if anyone
    overrides repr() in such a way that it doesn't return something unique, and
    reports it to me as a cache collision bug, I'll deprecate this
    functionality because it's too hard to support well. Don't be the person
    who screws it up for everyone.
    '''
    if cache is None:
        from django.core.cache import cache

    def decorator(f):
        key_prefix = '{}.{}'.format(f.__module__, f.__name__)

        def get_key(*args, **kwargs):
            return '{}({})'.format(
                key_prefix,
                ','.join(itertools.chain(
                    (repr(arg) for arg in args),
                    ('{}={}'.format(k, repr(v)) for k,v in sorted(kwargs.items())),
                )),
            )

        def memoized(*args, **kwargs):
            sentinel = object()
            key = get_key(*args, **kwargs)

            result = cache.get(key, sentinel)

            if result is sentinel:
                result = f(*args, **kwargs)
                cache.set(key, result, timeout)

            return result

        return memoized

    return decorator