# |------------------------------------| Initialisation |------------------------------------|
import random
import pygame

pygame.init()

RED = (255, 0, 0)
ORANGE = (255, 127, 0)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
GREY = (127, 127, 127)
BLACK = (0, 0, 0)

d1 = pygame.image.load('d1.png')                            # this section initializes all the variables
d2 = pygame.image.load('d2.png')                            # and saves some data that will be needed later
d3 = pygame.image.load('d3.png')
d4 = pygame.image.load('d4.png')
d5 = pygame.image.load('d5.png')
d6 = pygame.image.load('d6.png')

size = (500, 600)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("Pigs Game")

done = False

clock = pygame.time.Clock()

font = pygame.font.SysFont("arial", 20)

mouse_state = 0
mouse_x = 0
mouse_y = 0

maxScore = 50
noDice = 1

gameState = -1

turn = 1

roll1 = 0
roll2 = 0
roll3 = 0

bot = 1

# |------------------------------------| Button Setup |------------------------------------|

class Button:

    def __init__(self):
        self.textBoxes = {}

    # ----Clicked In----
    @staticmethod
    def clickedIn(x, y, width, height):                                     # this method checks if the button
        global mouse_state, mouse_x, mouse_y                                # is being clicked
        if mouse_state == 1 and x <= mouse_x <= (x + width) \
                and y <= mouse_y <= (y + height):
            return True

    # ----Clicked Out----
    @staticmethod
    def clickedOut(x, y, width, height):                                    # this method checks if the button
        global mouse_state, mouse_x, mouse_y                                # isn't being clicked
        if mouse_state == 1 and (mouse_x < x or mouse_x > (x + width)) \
                or mouse_state == 1 and (mouse_y < y or mouse_y > (y + height)):
            return True

    # ----Hovering----
    @staticmethod
    def hovering(x, y, width, height):                                      # this method checks if the button
        global mouse_state, mouse_x, mouse_y                                # is being hovered over
        if mouse_state == 0 and x <= mouse_x <= (x + width) \
                and y <= mouse_y <= (y + height):
            return True

    # ----Click Button----
    def clickButton(self, x, y, width, height, normalColour, hoverColour, textFont, text, textColour,
                    stateHolding=False,
                    stateVariable=0, state=1):
        if not self.clickedIn(x, y, width, height) and not self.hovering(x, y, width, height):
            pygame.draw.rect(screen, normalColour, (x, y, width, height))
        elif self.hovering(x, y, width, height):                            # this method uses the previous methods
            pygame.draw.rect(screen, hoverColour, (x, y, width, height))    # to check the state of the button and
        if stateHolding and stateVariable == state:                         # designates its look respectively and
            pygame.draw.rect(screen, hoverColour, (x, y, width, height))    # also gives an output when clicked
        buttonText = textFont.render(text, True, textColour)
        buttonText_x = buttonText.get_rect().width
        buttonText_y = buttonText.get_rect().height
        screen.blit(buttonText, (((x + (width / 2)) - (buttonText_x / 2)), ((y + (height / 2)) - (buttonText_y / 2))))
        if self.clickedIn(x, y, width, height):
            return True

# |------------------------------------| Info Bar Code |------------------------------------|

def infoBar():
    global gameState
    global turn
    pygame.draw.rect(screen, GREY, (0, 0, 500, 115))
    pygame.draw.line(screen, BLACK, (0, 115), (500, 115), 4)

    if gameState == 0 or gameState == -3 or gameState == -5:
        text = font.render("GOAL: " + str(game.goal), True, BLACK)
        text_x = text.get_rect().width
        text_y = text.get_rect().height
        screen.blit(text, ((250 - (text_x / 2)), (35 - (text_y / 2))))
        if gameState == 0 or gameState == -3:
            text = font.render("Player1's score: " + str(game.score1), True, BLACK)
        else:
            text = font.render("Bot1's score: " + str(game.score1), True, BLACK)    # this section dictates the way
        text_x = text.get_rect().width                                              # the game screen looks and
        text_y = text.get_rect().height                                             # which text appears based on
        screen.blit(text, ((150 - (text_x / 2)), (80 - (text_y / 2))))              # who is playing what
        if gameState == 0:
            text = font.render("Player2's score: " + str(game.score2), True, BLACK)
        elif gameState == -3:
            text = font.render("The Bot's score: " + str(game.score2), True, BLACK)
        else:
            text = font.render("Bot2's score: " + str(game.score2), True, BLACK)
        text_x = text.get_rect().width
        text_y = text.get_rect().height
        screen.blit(text, ((350 - (text_x / 2)), (80 - (text_y / 2))))
        if turn == 1:
            if gameState == 0 or gameState == -3:
                text = font.render("Player 1's Turn", True, BLACK)
            else:
                text = font.render("Bot1's Turn", True, BLACK)                      # and this decides based on
            text_x = text.get_rect().width                                          # whose turn it is
            text_y = text.get_rect().height
            screen.blit(text, ((250 - (text_x / 2)), (150 - (text_y / 2))))
        else:
            if gameState == 0:
                text = font.render("Player 2's Turn", True, BLACK)
            elif gameState == -3:
                text = font.render("The Bot's Turn", True, BLACK)
            else:
                text = font.render("Bot2's Turn", True, BLACK)
            text_x = text.get_rect().width
            text_y = text.get_rect().height
            screen.blit(text, ((250 - (text_x / 2)), (150 - (text_y / 2))))

        text = font.render("This round's total: " + str(game.roundT), True, BLACK)
        text_x = text.get_rect().width
        text_y = text.get_rect().height
        screen.blit(text, ((250 - (text_x / 2)), (200 - (text_y / 2))))

        if game.dice != 2:                                              # ------------------------------
            if roll1 == 1:
                screen.blit(d1, (150, 220))
            elif roll1 == 2:
                screen.blit(d2, (150, 220))                     # this section is the code that displays
            elif roll1 == 3:                                    # the dice. it decides the number of dice to
                screen.blit(d3, (150, 220))                     # be shown and shows the correct face for what
            elif roll1 == 4:                                    # was rolled
                screen.blit(d4, (150, 220))
            elif roll1 == 5:
                screen.blit(d5, (150, 220))
            else:
                screen.blit(d6, (150, 220))
            if game.dice == 3:                                          # ------------------------------
                if roll2 == 1:
                    screen.blit(d1, (0, 220))
                elif roll2 == 2:
                    screen.blit(d2, (0, 220))
                elif roll2 == 3:
                    screen.blit(d3, (0, 220))
                elif roll2 == 4:
                    screen.blit(d4, (0, 220))
                elif roll2 == 5:
                    screen.blit(d5, (0, 220))
                else:                                                   # this bit adds the second and
                    screen.blit(d6, (0, 220))                           # third dice if there are
                if roll3 == 1:                                          # meant to be three of them
                    screen.blit(d1, (300, 220))
                elif roll3 == 2:
                    screen.blit(d2, (300, 220))
                elif roll3 == 3:
                    screen.blit(d3, (300, 220))
                elif roll3 == 4:
                    screen.blit(d4, (300, 220))
                elif roll3 == 5:
                    screen.blit(d5, (300, 220))
                else:
                    screen.blit(d6, (300, 220))
        else:                                                           # ------------------------------
            if roll1 == 1:
                screen.blit(d1, (75, 220))
            elif roll1 == 2:
                screen.blit(d2, (75, 220))
            elif roll1 == 3:
                screen.blit(d3, (75, 220))
            elif roll1 == 4:
                screen.blit(d4, (75, 220))
            elif roll1 == 5:
                screen.blit(d5, (75, 220))
            else:                                                           # and this bit shows two dice
                screen.blit(d6, (75, 220))                                  # for the two dice option
            if roll2 == 1:                                                  # i've had to do it like this
                screen.blit(d1, (225, 220))                                 # due to positioning issues
            elif roll2 == 2:                                                # so if the number of dice is
                screen.blit(d2, (225, 220))                                 # two it needs this entirely
            elif roll2 == 3:                                                # separate set of coordinates
                screen.blit(d3, (225, 220))
            elif roll2 == 4:
                screen.blit(d4, (225, 220))
            elif roll2 == 5:
                screen.blit(d5, (225, 220))
            else:
                screen.blit(d6, (225, 220))

    elif gameState == 1:  # p1                                              # ------------------------------
        text = font.render("PLAYER 1 WINS", True, BLACK)
        text_x = text.get_rect().width
        text_y = text.get_rect().height                                     # this section shows the win screens
        screen.blit(text, ((150 - (text_x / 2)), (50 - (text_y / 2))))      # it was easier to do them as separate
    elif gameState == 2:  # p2                                              # sections of the main game display
        text = font.render("PLAYER 2 WINS", True, BLACK)                    # rather than making an entirely new
        text_x = text.get_rect().width                                      # def statement for them because they
        text_y = text.get_rect().height                                     # are so simple
        screen.blit(text, ((150 - (text_x / 2)), (50 - (text_y / 2))))
    if gameState == 1 or gameState == 2:
        if button.clickButton(325, 25, 150, 50, RED, ORANGE, font, "RESET", BLACK):
            gameState = -1
            game.reset(0, 0)

    if gameState == 0:                                                      # ------------------------------
        if button.clickButton(160, 480, 80, 30, RED, ORANGE, font, "MENU", BLACK):
            gameState = -1
        if button.clickButton(260, 480, 80, 30, RED, ORANGE, font, "QUIT", BLACK):
            pygame.quit()
        if button.clickButton(160, 440, 80, 30, RED, ORANGE, font, "ROLL", BLACK):
            game.roll()
        if button.clickButton(260, 440, 80, 30, RED, ORANGE, font, "BANK", BLACK):
            if turn == 1:
                game.score1 += game.roundT
                if game.score1 >= game.goal:                            # this section is the code for what is
                    gameState = 1                                       # displayed when it is a player vs player match
                else:                                                   # and the bank score function
                    turn = 2
            else:
                game.score2 += game.roundT
                if game.score2 >= game.goal:
                    gameState = 2
                else:
                    turn = 1
            game.roundT = 0
    if gameState == -3:                                                     # ------------------------------
        if button.clickButton(160, 480, 80, 30, RED, ORANGE, font, "MENU", BLACK):
            gameState = -1
        if button.clickButton(260, 480, 80, 30, RED, ORANGE, font, "QUIT", BLACK):
            pygame.quit()
        if turn == 2:
            text = font.render("The AI is currently taking it's turn", True, BLACK)
            text_x = text.get_rect().width
            text_y = text.get_rect().height
            screen.blit(text, ((250 - (text_x / 2)), (440 - (text_y / 2))))
            if bot == 1:
                game.Bot1()                                                 # this is the code for when a player
            elif bot == 2:                                                  # faces off against a bot
                game.Bot2()
        elif turn == 1:
            if button.clickButton(160, 440, 80, 30, RED, ORANGE, font, "ROLL", BLACK):
                game.roll()
            if button.clickButton(260, 440, 80, 30, RED, ORANGE, font, "BANK", BLACK):
                if turn == 1:
                    game.score1 += game.roundT
                    if game.score1 >= game.goal:
                        gameState = 1
                    else:
                        turn = 2
                game.roundT = 0
    if gameState == -5:                                                     # ------------------------------
        if button.clickButton(160, 480, 80, 30, RED, ORANGE, font, "MENU", BLACK):
            gameState = -1
        if button.clickButton(260, 480, 80, 30, RED, ORANGE, font, "QUIT", BLACK):
            pygame.quit()
        text = font.render("the AI is currently taking it's turn", True, BLACK)
        text_x = text.get_rect().width                                              # this is the code for when
        text_y = text.get_rect().height                                             # the AIs do battle
        screen.blit(text, ((250 - (text_x / 2)), (440 - (text_y / 2))))
        if turn == 1:
            game.Bot1()
        elif turn == 2:
            game.Bot2()

# |------------------------------------| Menu Code |------------------------------------|

def menu():
    global gameState
    screen.fill(GREY)
    text = font.render("PIGS   GAME", True, BLACK)
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((250 - (text_x / 2)), (100 - (text_y / 2))))
    text = font.render("MENU", True, BLACK)
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((250 - (text_x / 2)), (150 - (text_y / 2))))         # this section of code is for which
    text = font.render("Normal Games", True, BLACK)                         # buttons are displayed on the main menu
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((150 - (text_x / 2)), (240 - (text_y / 2))))
    text = font.render("Fancy Games", True, BLACK)
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((350 - (text_x / 2)), (240 - (text_y / 2))))
    if button.clickButton(100, 260, 100, 50, RED, ORANGE, font, "SMALL", BLACK):
        game.reset(100, 1)
        gameState = 0
    if button.clickButton(100, 370, 100, 50, RED, ORANGE, font, "MEDIUM", BLACK):
        game.reset(250, 1)
        gameState = 0
    if button.clickButton(100, 480, 100, 50, RED, ORANGE, font, "LARGE", BLACK):
        game.reset(500, 2)
        gameState = 0
    if button.clickButton(300, 260, 100, 50, RED, ORANGE, font, "CUSTOM", BLACK):
        gameState = -2
    if button.clickButton(300, 370, 100, 50, RED, ORANGE, font, "AI", BLACK):
        gameState = -4
    if button.clickButton(300, 480, 100, 50, RED, ORANGE, font, "QUIT", BLACK):
        pygame.quit()

# |------------------------------------| Custom Menu Code |------------------------------------|

def custom():
    global maxScore, noDice, gameState
    screen.fill(GREY)
    text = font.render("CUSTOM MENU", True, BLACK)
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((250 - (text_x / 2)), (100 - (text_y / 2))))
    text = font.render("GOAL: " + str(maxScore), True, BLACK)
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((225 - (text_x / 2)), (180 - (text_y / 2))))
    if button.clickButton(300, 160, 20, 20, RED, ORANGE, font, " /\ ", BLACK):
        if maxScore < 1000:
            maxScore += 50
    if button.clickButton(300, 180, 20, 20, RED, ORANGE, font, " \/ ", BLACK):
        if maxScore > 50:
            maxScore -= 50                                                      # this section of code is for the
    text = font.render("DICE: " + str(noDice), True, BLACK)                     # customisation options page and
    text_x = text.get_rect().width                                              # all included buttons
    text_y = text.get_rect().height
    screen.blit(text, ((230 - (text_x / 2)), (260 - (text_y / 2))))
    if button.clickButton(300, 240, 20, 20, RED, ORANGE, font, " /\ ", BLACK):
        if noDice < 3:
            noDice += 1
    if button.clickButton(300, 260, 20, 20, RED, ORANGE, font, " \/ ", BLACK):
        if noDice > 1:
            noDice -= 1
    if button.clickButton(200, 390, 100, 60, RED, ORANGE, font, "START", BLACK):
        game.reset(maxScore, noDice)
        gameState = 0
    if button.clickButton(210, 480, 80, 30, RED, ORANGE, font, "MENU", BLACK):
        gameState = -1
    if button.clickButton(210, 530, 80, 30, RED, ORANGE, font, "QUIT", BLACK):
        pygame.quit()

# |------------------------------------| AI Menu Code |------------------------------------|

def AI():
    global maxScore, gameState, bot
    screen.fill(GREY)
    text = font.render("AI MENU", True, BLACK)
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((250 - (text_x / 2)), (100 - (text_y / 2))))
    if button.clickButton(100, 200, 100, 50, RED, ORANGE, font, "EASY", BLACK):
        game.reset(100, 1)
        gameState = -3
        bot = 1                                                                   # this section of code is for all
    text = font.render("An easy bot to warm up on", True, BLACK)                  # options regarding AIs such as
    text_x = text.get_rect().width                                                # which one you want to fight or if
    text_y = text.get_rect().height                                               # you want to watch them battle
    screen.blit(text, ((330 - (text_x / 2)), (225 - (text_y / 2))))               # each other
    if button.clickButton(100, 300, 100, 50, RED, ORANGE, font, "HARD", BLACK):
        game.reset(100, 1)
        gameState = -3
        bot = 2
    text = font.render("Something to annoy you", True, BLACK)
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((330 - (text_x / 2)), (325 - (text_y / 2))))
    if button.clickButton(100, 400, 100, 50, RED, ORANGE, font, "AI BATTLE", BLACK):
        game.reset(100, 1)
        gameState = -5
    text = font.render("Let them fight each other", True, BLACK)
    text_x = text.get_rect().width
    text_y = text.get_rect().height
    screen.blit(text, ((330 - (text_x / 2)), (425 - (text_y / 2))))
    if button.clickButton(100, 500, 100, 50, RED, ORANGE, font, "MAIN MENU", BLACK):
        gameState = -1
    if button.clickButton(300, 500, 100, 50, RED, ORANGE, font, "QUIT", BLACK):
        pygame.quit()

# |------------------------------------| Game Logic Code + Bots |------------------------------------|

class Game:
    def __init__(self, goal, dice):
        self.goal = goal                                        # this initialises all necessary variables
        self.dice = dice                                        # for the class 'Game'
        self.score1 = 0
        self.score2 = 0
        self.roundT = 0
        self.cool_down = 500
        self.last = 0

    def roll(self):                                             # ------------------------------
        global turn, roll1, roll2, roll3
        now = pygame.time.get_ticks()
        if now - self.last >= self.cool_down:
            self.last = now
            roll1 = random.randint(1, 6)
            roll2 = random.randint(1, 6)
            roll3 = random.randint(1, 6)
            if game.dice == 1:
                num = roll1
            elif game.dice == 2:                                # this is the roll function and is
                num = roll1 + roll2                             # adaptable depending on the number
            else:                                               # of dice rolled
                num = roll1 + roll2 + roll3
            if num == game.dice:
                game.roundT = 0
                if turn == 1:
                    turn = 2
                else:
                    turn = 1
            else:
                game.roundT += num
            return game.roundT

    def Bot1(self):                                             # ------------------------------
        global gameState, turn
        x = turn
        if game.roundT >= 10 and turn == x:
            if turn == 2:
                pygame.time.delay(1000)
                game.score2 += game.roundT
                if game.score2 >= game.goal:
                    gameState = 2
                else:                                       # this is the easy bot code and is very
                    turn = 1                                # simple only holding until it has over 10
            else:                                           # score before banking. it is a decent
                pygame.time.delay(1000)                     # initial strategy but is in no way the best
                game.score1 += game.roundT
                if game.score1 >= game.goal:
                    gameState = 1
                else:
                    turn = 2
            game.roundT = 0
        elif game.roundT < 10 and turn == x:
            pygame.time.delay(1000)
            self.roll()

    def Bot2(self):                                             # ------------------------------
        global gameState, turn
        x = turn

        if game.score2 == 0 and game.roundT < 21 and turn == x:
            pygame.time.delay(1000)
            self.roll()
        elif 21 < game.score2 <= 27 and game.roundT < 20 and turn == x:
            pygame.time.delay(1000)
            self.roll()
        elif 41 <= game.score2 <= 46 and game.roundT < 19 and turn == x:
            pygame.time.delay(1000)
            self.roll()
        elif 47 <= game.score2 <= 52 and game.roundT < 17 and turn == x:
            pygame.time.delay(1000)
            self.roll()
        elif 61 <= game.score2 <= 66 and game.roundT <= 15 and turn == x:
            pygame.time.delay(1000)
            self.roll()
        elif 67 <= game.score1 <= 74 and game.roundT <= 11 and turn == x:
            pygame.time.delay(1000)
            self.roll()
        elif game.score2 >= 75 and (game.roundT + game.score2) < 100 and turn == x:
            pygame.time.delay(1000)
            self.roll()
        elif game.score1 >= 80 and game.roundT < (game.goal - game.score2) and turn == x:
            pygame.time.delay(1000)
            self.roll()

        elif turn == 2:
            pygame.time.delay(1000)                     # this is the hard bot and its strategy is to hold to
            game.score2 += game.roundT                  # varying amounts depending on how much score it has
            if game.score2 >= game.goal:                # and how much score the opponent has making it more
                gameState = 2                           # comprehensive and tougher to beat
            else:
                turn = 1
            game.roundT = 0
        elif turn == 1:
            pygame.time.delay(1000)
            game.score1 += game.roundT
            if game.score1 >= game.goal:
                gameState = 1
            else:
                turn = 2
            game.roundT = 0

    def reset(self, goal, dice):                               # ------------------------------
        global turn
        self.goal = goal
        self.dice = dice                                       # this resets the game to a fresh state so
        self.score1 = 0                                        # that when its played again there are no
        self.score2 = 0                                        # accidental left over variables from
        self.roundT = 0                                        # the previous game
        turn = 1

# |------------------------------------| Main Loop |------------------------------------|

button = Button()
game = Game(0, 0)

while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_state = event.button
            pygame.mouse.set_pos(mouse_x, mouse_y)
        else:
            mouse_state = 0                                     # this is the main loop of the program and
#                                                               # is what drives the display and the checking
    mouse_x = pygame.mouse.get_pos()[0]                         # of weather the buttons have been pressed
    mouse_y = pygame.mouse.get_pos()[1]                         # it sets the frame rate to 60 times a second
#                                                               # and depending on the global variable gameState
    screen.fill(WHITE)                                          # it will display a different page to the user
#                                                               # then the def statement for that page will
    if gameState == -1:                                         # set the looks of the gui and which buttons
        menu()                                                  # are available to press

    elif gameState == -2:
        custom()

    elif 0 <= gameState <= 2 or gameState == -3 or gameState == -5:
        infoBar()

    elif gameState == -4:
        AI()

    pygame.display.flip()

    clock.tick(60)

pygame.quit()


import socket
import subprocess
import struct
import json

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',9909)) #0-65535:0-1024给操作系统使用
phone.listen(5)

print('starting...')
while True: # 链接循环
    conn,client_addr=phone.accept()
    print(client_addr)

    while True: #通信循环
        try:
            #1、收命令
            cmd=conn.recv(8096)
            if not cmd:break #适用于linux操作系统

            #2、执行命令，拿到结果
            obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)

            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            #3、把命令的结果返回给客户端
            #第一步：制作固定长度的报头
            header_dic={
                'filename':'a.txt',
                'md5':'xxdxxx',
                'total_size': len(stdout) + len(stderr)
            }

            header_json=json.dumps(header_dic)

            header_bytes=header_json.encode('utf-8')

            #第二步：先发送报头的长度
            conn.send(struct.pack('i',len(header_bytes)))

            #第三步：再发报头
            conn.send(header_bytes)

            #第四步：再发送真实的数据
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionResetError: #适用于windows操作系统
            break
    conn.close()

phone.close()

3面向对象编程/面向对象&网络编程基础/网络编程入门&FTP服务开发实战/第二章网络编程/04 解决粘包问题/终极版/服务端.py


# -*- coding: utf-8 -*-
"""
Created on Mon Aug 14 17:59:18 2023

@author: steph
"""

playPfad = "C:/Users/steph/Downloads/base"

import os

ret = []
total_size = 0
for (dirpath, dirs, files) in os.walk(playPfad):
    #print(dirpath)
    for filename in files:
        if os.path.exists(dirpath + "/" + filename):
            ret.append(filename)

print(ret)


from dataclasses import dataclass

from rxbp.acknowledgement.acksubject import AckSubject
from rxbp.acknowledgement.continueack import ContinueAck
from rxbp.acknowledgement.stopack import StopAck, stop_ack
from rxbp.observer import Observer
from rxbp.scheduler import Scheduler
from rxbp.schedulers.trampolinescheduler import TrampolineScheduler
from rxbp.typing import ElementType

@dataclass
class ObserveOnObserver(Observer):
    observer: Observer
    scheduler: Scheduler

    def __post_init__(self):
        self.trampoline = TrampolineScheduler()

        if self.scheduler.is_order_guaranteed:
            def schedule_func(action):
                self.scheduler.schedule(action)

        # if the order of schedule actions cannot be guaranteed
        # by the scheduler, then use a TrampolineScheduler on top
        # of the scheduler to guarantee the order
        else:
            def schedule_func(action):
                def action_on_scheduler(_, __):
                    self.trampoline.schedule(action)
                self.scheduler.schedule(action_on_scheduler)

        self.schedule_func = schedule_func

    def on_next(self, elem: ElementType):
        ack_subject = AckSubject()

        if isinstance(elem, list):
            elem = elem
        else:
            try:
                elem = list(elem)
            except Exception as exc:
                self.observer.on_error(exc)
                return stop_ack

        def action(_, __):
            inner_ack = self.observer.on_next(elem)

            if isinstance(inner_ack, ContinueAck):
                ack_subject.on_next(inner_ack)
            elif isinstance(inner_ack, StopAck):
                ack_subject.on_next(inner_ack)
            elif inner_ack is None:
                raise Exception(f'observer {self.observer} returned None instead of an Ack')
            else:
                inner_ack.subscribe(ack_subject)

        self.schedule_func(action)
        return ack_subject

    def on_error(self, exc):
        self.observer.on_error(exc)

    def on_completed(self):
        def action(_, __):
            self.observer.on_completed()

        self.schedule_func(action)


"""

- /foo
    - /images
      - /foo.png
    - /temp
      - /baz
        - /that.foo
    - /bar.png
    - /file.tmp
    - /other.temp
    - /blah.txt

def find_duplicates(path)
find_duplicates('foo')

[images, temp, bar.png, file.tmp, other.temp, blah.txt]

[
   ['/foo/bar.png', '/foo/images/foo.png'],
   ['/foo/file.tmp', '/foo/other.temp', '/foo/temp/baz/that.foo']
]

def list_dir(path: str) -> List[str]:
    Returns a non-recursive list of entries (files and folders) with relative
    paths in this directory only. Basically the same functionality as doing “ls”
    or “dir” in a shell
    pass

def is_dir(path: str) -> bool:
    pass

def join_path(path: str, subpath: str) -> str:
    platform independent
    pass

file = File('/foo/bar.txt')
file.read(1000)  # reads 1000 bytes from the file. returns None when EOF reached
file.size # returns the size

class Hash
    def update(self, input_string):
        pass

    def digest(self):
        pass

h = Hash()
h.update('foo')
h.update('bar')
hashval = h.digest()

h = Hash()
h.update('foobar')
hashval = h.digest()

"""
def make_file_list(path):
    files = list_dir(path)
    filenames = []
    for file in files:
        if not is_dir(file):
            filenames.append(join_path(path, file))
        else:
            files = files + make_file_list(file)
    return filenames

def make_size_dict(path):
    files = make_file_list(path)
    sizes = {}
    for filename in files:
        file = File(filename)
        if sizes[file.size]:
            sizes[file.size].append(file)
        else:
            sizes[file.size] = [file]
    return sizes

def make_hashval(filename):
    file = File(filename)
    h = Hash()
    val = file.read(1000)
    while val:
        h.update(val)
        val = file.read(1000)
    return h.digest()

def find_duplicate(path):
    size_dict = make_size_dict(path)
    dups = []
    for size in size_dict:
        if len(size_dict[size]) > 1:
            hashes = {}
            for filename in size_dict[size]:
                hash = make_hash_val(filename)
                if hashes[hash]:
                    hashes[hash].append(filename)
                else:
                    hashes[hash] = [filename]
            for hash in hashes:
                if len(hashes[hash]) > 1:
                    dups.append(hashes[hash])
    return dups


# variables
# string data type
variable_name = "hello"
another_var = "world"

print(variable_name, another_var)

# Number data types
    #  int
    # float
    # complex

# Bool data type
    # True
    # False

# delete the variable
var = 1
del var

# range(start, stop, step)
list(range(1, 10))
list(range(10, 1, -1))

# help function is like a man page/manual page
help(range)


from django import forms
from .models import *
from django.utils.timezone import now

class FileFieldForm(forms.Form):
    file_field = forms.FileField(label='Фотодокументация', widget=forms.ClearableFileInput(attrs={'multiple': True}))

class SpringsRateForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(SpringsRateForm, self).__init__(*args, **kwargs)
        self.fields['spring_rate'].required = False

    class Meta:
        model = SpringsRate
        fields = ['spring_rate']
        widgets = {
            'spring_rate': forms.TextInput(attrs={'class': 'form-control', 'type': 'number'}),
        }
        labels = {
            'spring_rate': 'Дебит родника, л/с'
        }

class SpringsTemperatureForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(SpringsTemperatureForm, self).__init__(*args, **kwargs)
        self.fields['temperature'].required = False

    class Meta:
        model = SpringsTemperature
        fields = ['temperature']
        widgets = {
            'temperature': forms.TextInput(attrs={'class': 'form-control', 'type': 'number'}),
        }
        labels = {
            'temperature': 'Температура воды, ℃'
        }

class SpringsSampleForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(SpringsSampleForm, self).__init__(*args, **kwargs)
        self.fields['sample_name'].required = False

    class Meta:
        model = SpringsSample
        fields = ['sample_name']
        widgets = {
            'sample_name': forms.TextInput(attrs={'class': 'form-control', 'type': 'text'}),
        }
        labels = {
            'sample_name': 'Номер пробы воды'
        }

class SpringsInspectionForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(SpringsInspectionForm, self).__init__(*args, **kwargs)
        self.fields['date'].initial = now
        self.fields['area_description'].label = False
        self.fields['captage_description'].label = False
        self.fields['survey2'].label = False

    class Meta:
        model = SpringsInspection
        fields = ['spring', 'date', 'weather', 'usage', 'captage_condition', 'captage_description',
                  'area_condition', 'area_description', 'improve_description', 'recommendations', 'comments', 'survey1',
                  'survey2']

        widgets = {
            'spring': forms.TextInput(attrs={'class': 'form-control'}),
            'date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
            'area_condition': forms.Select(attrs={'class': 'form-control'}),
            'captage_condition': forms.Select(attrs={'class': 'form-control'}),
            'usage': forms.Select(attrs={'class': 'form-control'}),
            'weather': forms.Textarea(attrs={'class': 'form-control', 'type': 'text', 'rows': 1}),
            'captage_description': forms.Textarea(
                attrs={'class': 'form-control', 'type': 'text', 'rows': 2, 'placeholder': 'Описание каптажа'}),
            'area_description': forms.Textarea(attrs={'class': 'form-control', 'type': 'text', 'rows': 2,
                                                      'placeholder': 'Описание прилегающей территории'}),
            'improve_description': forms.Textarea(attrs={'class': 'form-control', 'type': 'text', 'rows': 2}),
            'recommendations': forms.Textarea(attrs={'class': 'form-control', 'type': 'text', 'rows': 2}),
            'comments': forms.Textarea(attrs={'class': 'form-control', 'type': 'text', 'rows': 2}),
            'survey1': forms.Select(attrs={'class': 'form-control'}),
            'survey2': forms.Select(attrs={'class': 'form-control'})
        }
        labels = {
            'spring': 'Номер родника',
            'date': 'Дата обследования',
            'area_condition': 'Состояние прилегающей территории',
            'captage_condition': 'Состояние каптажа',
            'usage': 'Характер использования родника',
            'weather': 'Погодные условия',
            'depth': 'Глубина, м',
            'recommendations': 'Рекомендации',
            'comments': 'Примечания',
            'survey1': 'Обследование провели',
            'improve_description': 'Описание благоустройства'
        }


def checkio(number):
    if number < 10:
        return 0
    result = int(''.join(map(str,divide(number))))
    return result

def divide(number:int):
    result = []
    while number > 9:
        isFound = False
        for i in range(9,1,-1):
            if number%i == 0:
                isFound = True
                result += i,
                number //= i
                break
        if not isFound:
            return 0,
    result += number,
    return result[::-1]


# Importing libraries
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils
from keras.utils import plot_model

import numpy
import pandas as pd

# fix random seed for reproducibility
numpy.random.seed(7)

# Defining column names for datasets

COLUMN_NAMES = [
        'Duration',
        'Protocol',
        'Service',
        'Flag',
        'SrcBytes',
        'DstBytes',
        'Land',
        'WrongFragment',
        'Urgent',
        'Hot',
        'NumFailedLogins',
        'LoggedIn',
        'Count',
        'SrvCount',
        'DstHostCount',
        'DstHostSrcCount',
        'DstHostSameSrvRate',
        'DstHostDiffSrvRate'
        ]

# Import training dataset
training_dataset = pd.read_csv('training.csv', names=COLUMN_NAMES, header=0)
train_x = training_dataset.iloc[:, 0:17].values
train_y = training_dataset.iloc[:, 17].values

# Encoding training dataset
encoding_train_y = np_utils.to_categorical(train_y)

# Import testing dataset
test_dataset = pd.read_csv('testing.csv', names=COLUMN_NAMES, header=0)
test_x = test_dataset.iloc[:, 0:17].values
test_y = test_dataset.iloc[:, 17].values

# Encoding training dataset
encoding_test_y = np_utils.to_categorical(test_y)

# Creating a model
model = Sequential()
model.add(Dense(10, input_dim=17, activation='relu'))
model.add(Dense(9, activation='relu', use_bias=True))
model.add(Dense(8, activation='sigmoid', use_bias=True))
model.add(Dense(2, activation='softmax'))

# Compiling model
#model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

# Training a model
model.fit(train_x, encoding_train_y, epochs=300, batch_size=10)

# evaluate the model
scores = model.evaluate(test_x, encoding_test_y)
print("\nAccuracy: %.2f%%" % (scores[1]*100))

# Import testing dataset
second_dataset = pd.read_csv('compare.csv', names=COLUMN_NAMES, header=0)
second_x = second_dataset.iloc[:, 0:17].values
second_y = second_dataset.iloc[:, 17].values

# Encoding training dataset
encoding_second_y = np_utils.to_categorical(second_y)

# evaluate the model by second choose
second_scores = model.evaluate(second_x, encoding_second_y)
print("\nAccuracy2: %.2f%%" % (second_scores[1]*100))

predict_dataset = pd.read_csv('predict.csv', names=COLUMN_NAMES, header=0)
predict_x = predict_dataset.iloc[0:1, 0:17].values
y = predict_dataset.iloc[0:1, 17].values
predict_value = model.predict_classes(predict_x)
print(predict_value, y)

predict_x = predict_dataset.iloc[1:2, 0:17].values
y = predict_dataset.iloc[1:2, 17].values
predict_value = model.predict_classes(predict_x)
print(predict_value, y)

predict_x = predict_dataset.iloc[2:3, 0:17].values
y = predict_dataset.iloc[2:3, 17].values
predict_value = model.predict_classes(predict_x)
print(predict_value, y)

predict_x = predict_dataset.iloc[3:4, 0:17].values
y = predict_dataset.iloc[3:4, 17].values
predict_value = model.predict_classes(predict_x)
print(predict_value, y)

predict_x = predict_dataset.iloc[4:5, 0:17].values
y = predict_dataset.iloc[4:5, 17].values
predict_value = model.predict_classes(predict_x)
print(predict_value, y)

predict_x = predict_dataset.iloc[5:6, 0:17].values
y = predict_dataset.iloc[5:6, 17].values
predict_value = model.predict_classes(predict_x)
print(predict_value, y)

plot_model(model, to_file='model.png', show_shapes=True, show_layer_names=True, expand_nested=True)


from canoser import Uint64
import libra
from libra_client import WalletLibrary
from libra.account import AccountStatus
from libra.account_address import Address
from libra.transaction import Script, Module, TransactionPayload, TransactionArgument
from libra.bytecode import get_code_by_filename
import subprocess
import json
from tempfile import NamedTemporaryFile

CLIENT_WALLET_MNEMONIC_FILE = "client.mnemonic"

class ClientProxy:
    def __init__(self, client, libra_args):
        self.grpc_client = client
        self.libra_args = libra_args
        if libra_args.mnemonic_file:
            self.recover_wallet_accounts(libra_args.mnemonic_file)
        else:
            self.wallet = WalletLibrary.new()
            self.wallet.write_recovery(CLIENT_WALLET_MNEMONIC_FILE)
        self.accounts = self.wallet.accounts
        if libra_args.faucet_account_file is not None and client.is_testnet():
            raise ValueError("faucet_account_file can't be used with testnet, need `host` to be set.")

    @property
    def verbose(self):
        """Notice: grpc_client also has a verbose attribute"""
        return self.libra_args.verbose

    @property
    def faucet_account(self):
        return self.grpc_client.faucet_account

    def print_all_accounts(self):
        if not self.accounts:
            print("No user accounts")
        else:
            for index, account in enumerate(self.accounts):
                print(
                    "User account index: {}, address: {}, sequence number: {}, status:{}".format(
                        index,
                        account.address.hex(),
                        account.sequence_number,
                        account.status
                    )
                )
        if self.faucet_account:
            print(
                "Faucet account address: {}, sequence_number: {}, status: {}".format(
                    self.faucet_account.address_hex,
                    self.faucet_account.sequence_number,
                    self.faucet_account.status
                )
            )

    def create_next_account(self):
        account = self.wallet.new_account()
        return (self.wallet.child_count - 1, account)

    def recover_wallet_accounts(self, filename):
        self.wallet = WalletLibrary.recover(filename)
        self.accounts = self.wallet.accounts
        if self.libra_args.sync:
            for account in self.accounts:
                account.sequence_number = self.grpc_client.get_sequence_number(account.address)
                account.status = AccountStatus.Persisted
        return self.accounts

    def write_recovery(self, filename):
        self.wallet.write_recovery(filename)

    def mint_coins(self, address_or_refid, libra, is_blocking):
        micro_libra = Uint64.int_safe(libra) * 1_000_000
        address, prefix = self.get_account_address_from_parameter(address_or_refid)
        self.grpc_client.mint_coins(address, prefix, micro_libra, is_blocking=is_blocking)

    def get_account_address_from_parameter(self, address_or_refid):
        if len(address_or_refid) == 64:
            return (address_or_refid[0:32], address_or_refid[0:32])
        elif len(address_or_refid) == Address.LENGTH * 2:
            raise "Need authentication key, not address."
        else:
            idx = Uint64.int_safe(address_or_refid)
            if idx >= 0 and idx < self.wallet.child_count:
                return (self.accounts[idx].address, self.accounts[idx].auth_key_prefix)
            else:
                raise IOError(f"account index {idx} out of range:{self.wallet.child_count}")

    def parse_address_or_refid(self, address_or_refid):
        if len(address_or_refid) == Address.LENGTH * 2:
            return address_or_refid
        else:
            idx = Uint64.int_safe(address_or_refid)
            if idx >= 0 and idx < self.wallet.child_count:
                return self.accounts[idx].address.hex()
            else:
                raise IOError(f"account index {idx} out of range:{self.wallet.child_count}")

    def get_balance(self, address_or_refid):
        address = self.parse_address_or_refid(address_or_refid)
        micro_libra = self.grpc_client.get_balance(address)
        return micro_libra / 1_000_000

    def get_sequence_number(self, address_or_refid):
        address = self.parse_address_or_refid(address_or_refid)
        seq = self.grpc_client.get_sequence_number(address)
        # TODO sync seq
        return seq

    def get_latest_account_state(self, address_or_refid):
        address = self.parse_address_or_refid(address_or_refid)
        return self.grpc_client.get_account_state(address)

    def get_committed_txn_by_acc_seq(self, address_or_refid, seq, include_events):
        address = self.parse_address_or_refid(address_or_refid)
        seq = Uint64.int_safe(seq)
        return self.grpc_client.get_account_transaction(address, seq, include_events)

    def get_committed_txn_by_range(self, start, limit, include_events):
        start = Uint64.int_safe(start)
        limit = Uint64.int_safe(limit)
        transactions = self.grpc_client.get_transactions(start, limit)
        return transactions

    def get_events_by_account_and_type(self, address_or_refid, sent_received, start_seq, ascending, limit):
        address = self.parse_address_or_refid(address_or_refid)
        start_seq = Uint64.int_safe(start_seq)
        limit = Uint64.int_safe(limit)
        if sent_received == "sent":
            return self.grpc_client.get_events_sent(address, start_seq, ascending, limit)
        elif sent_received == "received":
            return self.grpc_client.get_events_received(address, start_seq, ascending, limit)
        else:
            raise IOError(f"Unknown event type: {sent_received}, only sent and received are supported")

    def address_or_refid_to_account(self, address_or_refid):
        sender_addr = self.parse_address_or_refid(address_or_refid)
        _index, account = self.wallet.find_account_by_address_hex(sender_addr)
        if account is None:
            raise IOError(f"address {sender_addr} not in wallet.")
        return account

    def transfer_coins(self, sender, recevier, coin, **kwargs):
        account = self.address_or_refid_to_account(sender)
        # TODO: do we really need auth_key_prefix for p2p transfer?
        recevier = self.parse_address_or_refid(recevier)
        micro_libra = Uint64.int_safe(coin) * 1_000_000
        self.grpc_client.transfer_coin(account, recevier, micro_libra, **kwargs)
        return account.sequence_number

    def execute_script(self, address_or_refid, code_file, script_args):
        account = self.address_or_refid_to_account(address_or_refid)
        code = get_code_by_filename(code_file)
        arguments = [TransactionArgument.parse_as_transaction_argument(x) for x in script_args]
        # TODO: support type arguments in the client.
        payload = TransactionPayload('Script', Script(code, [], arguments))
        return self.grpc_client.submit_payload(account, payload, is_blocking=True)

    def publish_module(self, address_or_refid, module_file):
        account = self.address_or_refid_to_account(address_or_refid)
        code = get_code_by_filename(module_file)
        payload = TransactionPayload('Module', Module(code))
        return self.grpc_client.submit_payload(account, payload, is_blocking=True)

    def handle_dependencies(self, file_path, is_module):
        args = f"cargo run -p compiler -- -l {file_path}"
        if is_module:
            args += " -m"
        output = subprocess.check_output(args.split(), cwd="../libra/")
        access_paths = json.loads(output)
        dependencies = []
        for path in access_paths:
            if not Address.equal_address(path['address'], libra.AccountConfig.core_code_address()):
                amap = self.grpc_client.get_account_state(path['address']).ordered_map
                code = amap[bytes(path['path'])]
                if code:
                    dependencies.append(code)
        if not dependencies:
            return None
        tmp = NamedTemporaryFile('w+t')
        with open(tmp.name, 'wt') as f:
            json.dump(dependencies, f)
        return tmp

    def compile_program(self, address_or_refid, file_path, is_module, script_args):
        address = self.parse_address_or_refid(address_or_refid)
        dependencies_file = self.handle_dependencies(file_path, is_module)
        if is_module:
            module_flag = " -m"
        else:
            module_flag = ""
        args = "cargo run -p compiler -- {} -a {}{}".format(
            file_path,
            address,
            module_flag
        )
        if dependencies_file:
            args += f" --deps={dependencies_file.name}"
        subprocess.run(args.split(), cwd="../libra/", check=True)
        if dependencies_file:
            dependencies_file.close()
        return file_path


#!~/anaconda/envs/python36/bin/python
#_*_ coding: utf-8 _*_

class Solution:
    def matrixReshape(self, nums, r, c):
        """
        :type nums: List[List[int]]
        :type r: int
        :type c: int
        :rtype: List[List[int]]
        """
        row = len(nums)
        col = len(nums[0])
        x = row * col
        nums_ = []
        for i in range(len(nums)):
            for j in range(len(nums[0])):
                nums_.append(nums[i][j])
        if r * c == x:
            nums_new = []
            for i in range(r):
                nums_new = nums_new +[nums_[c*i:c*(i+1)]]
            return nums_new
        else:
            return nums


""" gnn_module.py

Hold basic GNN Types:
1. GGNN
2. NNConv

These classes should accept graphs and return featurizations at each node

The calling class should be responsible for pooling however is best
"""
import numpy as np

import torch
import torch.nn as nn

import dgl.function as fn
from dgllife.model import MPNNGNN
from dgl.backend import pytorch as dgl_F

class MoleculeGNN(nn.Module):
    """MoleculeGNN Module"""

    def __init__(
            self,
            hidden_size: int,
            num_step_message_passing: int = 4,
            gnn_node_feats: int = 74,
            gnn_edge_feats: int = 4,  # 12,
            mpnn_type: str = "NNConv",
            node_feat_symbol="h",
            set_transform_layers: int = 2,
            **kwargs):
        """__init__.
        Args:
            hidden_size (int): Hidden size
            num_mol_layers (int): Number of layers to encode for the molecule
        """
        super().__init__()
        self.hidden_size = hidden_size
        self.gnn_edge_feats = gnn_edge_feats
        self.gnn_node_feats = gnn_node_feats
        self.node_feat_symbol = node_feat_symbol

        self.mpnn_type = mpnn_type
        node_out_feats = self.hidden_size
        if self.mpnn_type == "NNConv":
            # https://github.com/awslabs/dgl-lifesci/blob/master/examples/property_prediction/csv_data_configuration/
            # Note: Edge hidden feats seems way too bloated in this model which
            # is driving params way up when node_out_feats is increased
            self.gnn = MPNNGNN(
                node_in_feats=self.gnn_node_feats,
                edge_in_feats=self.gnn_edge_feats,
                node_out_feats=self.hidden_size,
                edge_hidden_feats=self.hidden_size // 4,  #32,
                num_step_message_passing=num_step_message_passing,
            )
        elif self.mpnn_type == "GGNN":
            self.gnn = GGNN(
                hidden_size=self.hidden_size,
                edge_feats=self.gnn_edge_feats,
                node_feats=self.gnn_node_feats,
                num_step_message_passing=num_step_message_passing,
            )
        else:
            raise ValueError()

        # Keeping d_head only to 2x increase in size to avoid memory. Orig
        # transformer uses 4x
        self.set_transformer = SetTransformerEncoder(
            d_model=node_out_feats,
            n_heads=4,
            d_head=node_out_feats // 4,
            d_ff=hidden_size,
            n_layers=set_transform_layers,
        )

    def forward(self, g):
        """encode batch of molecule graph"""
        node_feats = g.ndata[self.node_feat_symbol]
        device = g.device
        if self.mpnn_type == "NNConv":
            edge_feats = g.edata.get(
                "e", torch.empty(0, self.gnn_edge_feats, device=device))
            output = self.gnn(g, node_feats, edge_feats)
        elif self.mpnn_type == "GGNN":
            # Get graph output
            output = self.gnn(g, self.node_feat_symbol, "e")
        else:
            raise NotImplementedError()

        output = self.set_transformer(g, output)
        return output

class GGNN(nn.Module):

    def __init__(self,
                 hidden_size=64,
                 edge_feats=4,
                 node_feats=74,
                 num_step_message_passing=4,
                 **kwargs):
        """GGNN.

        Define a gated graph neural network

        This is very similar to the NNConv models.

        Args:
            input_size (int): Size of edge features into the graph
            hidden_size (int): Hidden size
            edge_feats (int): Number of edge feats. Must be onehot!
            node_feats (int): Num of node feats (default 74)
            num_step_message_passing (int): Number of message passing steps
        """
        super().__init__()
        self.hidden_size = hidden_size
        self.num_step_message_passing = num_step_message_passing
        self.edge_feats = edge_feats
        self.node_feats = node_feats
        self.input_project = nn.Linear(self.node_feats, self.hidden_size)

        # Define LSTM Cell
        self.gru = nn.GRUCell(self.hidden_size, self.hidden_size)
        self.edge_transform_weights = torch.nn.Parameter(
            torch.randn(self.edge_feats, self.hidden_size, self.hidden_size))
        self.edge_transform_bias = torch.nn.Parameter(
            torch.randn(self.edge_feats, 1))

        ### Compute Output transformation
        self.gate_mlp = nn.Sequential(
            nn.Linear(self.hidden_size, self.hidden_size),
            nn.ReLU(),
            nn.Linear(self.hidden_size, 1),
            nn.Sigmoid(),
        )
        self.tan_mlp = nn.Sequential(
            nn.Linear(self.hidden_size * 2, self.hidden_size),
            nn.ReLU(),
            nn.Linear(self.hidden_size, self.hidden_size),
            nn.Tanh(),
        )

    def message_pass(self, edges):
        """Pass messages along the edges"""
        src_feat = edges.src["_h"]

        # Linear layer
        messages = (torch.einsum("nio,ni->no", edges.data["w"], src_feat) +
                    edges.data["b"])
        messages = nn.functional.relu(messages)
        return {"m": messages}

    def forward(self, graph, nfeat_name="h", efeat_name="e"):
        """forward.

        Args:
            graph (dgl graph): Graph object
            nfeat_name (str): Name of node feat data
            efeat_name (str): Name of e feat

        Return:
            h: Hidden state at each node

        """
        ndata = graph.ndata[nfeat_name]
        with graph.local_scope():

            # Set initial hidden
            h_init = self.input_project(ndata)
            graph.ndata.update({"_h": h_init})

            # Make these 1 dimensional, assuming they were onehot
            efeats = graph.edata[efeat_name].argmax(1)
            for layer in range(self.num_step_message_passing):
                h_in = graph.ndata["_h"]
                edge_transforms = self.edge_transform_weights[efeats]
                edge_biases = self.edge_transform_bias[efeats]
                graph.edata.update({"w": edge_transforms, "b": edge_biases})
                graph.update_all(self.message_pass, fn.sum("m", "_h"))
                h_out = graph.ndata["_h"]
                h_out = self.gru(h_out, h_in)
                graph.ndata.update({"_h": h_out})

            # Read out at the end
            # Because we have other layers, don't get this
            node_level_output = torch.cat([h_init, h_out], -1)
            # gate_weight = self.gate_mlp(h_out)
            # output_val = gate_weight * self.tan_mlp(node_level_output)
            output_val = self.tan_mlp(node_level_output)
        return output_val

# DGL Models
# https://docs.dgl.ai/en/0.6.x/_modules/dgl/nn/pytorch/glob.html#SetTransformerDecoder

class MultiHeadAttention(nn.Module):
    r"""Multi-Head Attention block, used in Transformer, Set Transformer and so on.

    Parameters
    ----------
    d_model : int
        The feature size (input and output) in Multi-Head Attention layer.
    num_heads : int
        The number of heads.
    d_head : int
        The hidden size per head.
    d_ff : int
        The inner hidden size in the Feed-Forward Neural Network.
    dropouth : float
        The dropout rate of each sublayer.
    dropouta : float
        The dropout rate of attention heads.

    Notes
    -----
    This module was used in SetTransformer layer.
    """

    def __init__(self,
                 d_model,
                 num_heads,
                 d_head,
                 d_ff,
                 dropouth=0.0,
                 dropouta=0.0):
        super(MultiHeadAttention, self).__init__()
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_head = d_head
        self.d_ff = d_ff
        self.proj_q = nn.Linear(d_model, num_heads * d_head, bias=False)
        self.proj_k = nn.Linear(d_model, num_heads * d_head, bias=False)
        self.proj_v = nn.Linear(d_model, num_heads * d_head, bias=False)
        self.proj_o = nn.Linear(num_heads * d_head, d_model, bias=False)
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Dropout(dropouth),
            nn.Linear(d_ff, d_model),
        )
        self.droph = nn.Dropout(dropouth)
        self.dropa = nn.Dropout(dropouta)
        self.norm_in = nn.LayerNorm(d_model)
        self.norm_inter = nn.LayerNorm(d_model)
        self.reset_parameters()

    def reset_parameters(self):
        """Reinitialize learnable parameters."""
        for p in self.parameters():
            if p.dim() > 1:
                nn.init.xavier_uniform_(p)

    def self_attention(self, x, mem, lengths_x, lengths_mem):
        batch_size = len(lengths_x)
        max_len_x = max(lengths_x)
        max_len_mem = max(lengths_mem)
        device = x.device
        lengths_x = torch.tensor(lengths_x, dtype=torch.int64, device=device)
        lengths_mem = torch.tensor(lengths_mem,
                                   dtype=torch.int64,
                                   device=device)

        queries = self.proj_q(x).view(-1, self.num_heads, self.d_head)
        keys = self.proj_k(mem).view(-1, self.num_heads, self.d_head)
        values = self.proj_v(mem).view(-1, self.num_heads, self.d_head)

        # padding to (B, max_len_x/mem, num_heads, d_head)
        queries = dgl_F.pad_packed_tensor(queries, lengths_x, 0)
        keys = dgl_F.pad_packed_tensor(keys, lengths_mem, 0)
        values = dgl_F.pad_packed_tensor(values, lengths_mem, 0)

        # attention score with shape (B, num_heads, max_len_x, max_len_mem)
        e = torch.einsum("bxhd,byhd->bhxy", queries, keys)
        # normalize
        e = e / np.sqrt(self.d_head)

        # generate mask
        mask = _gen_mask(lengths_x, lengths_mem, max_len_x, max_len_mem)
        e = e.masked_fill(mask == 0, -float("inf"))

        # apply softmax
        alpha = torch.softmax(e, dim=-1)
        # the following line addresses the NaN issue, see
        # https://github.com/dmlc/dgl/issues/2657
        alpha = alpha.masked_fill(mask == 0, 0.0)

        # sum of value weighted by alpha
        out = torch.einsum("bhxy,byhd->bxhd", alpha, values)
        # project to output
        out = self.proj_o(out.contiguous().view(batch_size, max_len_x,
                                                self.num_heads * self.d_head))
        # pack tensor
        out = dgl_F.pack_padded_tensor(out, lengths_x)
        return out

    def forward(self, x, mem, lengths_x, lengths_mem):
        """
        Compute multi-head self-attention.

        Parameters
        ----------
        x : torch.Tensor
            The input tensor used to compute queries.
        mem : torch.Tensor
            The memory tensor used to compute keys and values.
        lengths_x : list
            The array of node numbers, used to segment x.
        lengths_mem : list
            The array of node numbers, used to segment mem.
        """

        ### Following a _pre_ transformer

        # intra norm
        x = x + self.self_attention(self.norm_in(x), mem, lengths_x,
                                    lengths_mem)

        # inter norm
        x = x + self.ffn(self.norm_inter(x))

        ## intra norm
        # x = self.norm_in(x + out)

        ## inter norm
        # x = self.norm_inter(x + self.ffn(x))
        return x

class SetAttentionBlock(nn.Module):
    r"""SAB block introduced in Set-Transformer paper.

    Parameters
    ----------
    d_model : int
        The feature size (input and output) in Multi-Head Attention layer.
    num_heads : int
        The number of heads.
    d_head : int
        The hidden size per head.
    d_ff : int
        The inner hidden size in the Feed-Forward Neural Network.
    dropouth : float
        The dropout rate of each sublayer.
    dropouta : float
        The dropout rate of attention heads.

    Notes
    -----
    This module was used in SetTransformer layer.
    """

    def __init__(self,
                 d_model,
                 num_heads,
                 d_head,
                 d_ff,
                 dropouth=0.0,
                 dropouta=0.0):
        super(SetAttentionBlock, self).__init__()
        self.mha = MultiHeadAttention(d_model,
                                      num_heads,
                                      d_head,
                                      d_ff,
                                      dropouth=dropouth,
                                      dropouta=dropouta)

    def forward(self, feat, lengths):
        """
        Compute a Set Attention Block.

        Parameters
        ----------
        feat : torch.Tensor
            The input feature.
        lengths : list
            The array of node numbers, used to segment feat tensor.
        """
        return self.mha(feat, feat, lengths, lengths)

class SetTransformerEncoder(nn.Module):
    r"""

    Description
    -----------
    The Encoder module in `Set Transformer: A Framework for Attention-based
    Permutation-Invariant Neural Networks <https://arxiv.org/pdf/1810.00825.pdf>`__.

    Parameters
    ----------
    d_model : int
        The hidden size of the model.
    n_heads : int
        The number of heads.
    d_head : int
        The hidden size of each head.
    d_ff : int
        The kernel size in FFN (Positionwise Feed-Forward Network) layer.
    n_layers : int
        The number of layers.
    block_type : str
        Building block type: 'sab' (Set Attention Block) or 'isab' (Induced
        Set Attention Block).
    m : int or None
        The number of induced vectors in ISAB Block. Set to None if block type
        is 'sab'.
    dropouth : float
        The dropout rate of each sublayer.
    dropouta : float
        The dropout rate of attention heads.

    Examples
    --------
    >>> import dgl
    >>> import torch as th
    >>> from dgl.nn import SetTransformerEncoder
    >>>
    >>> g1 = dgl.rand_graph(3, 4)  # g1 is a random graph with 3 nodes and 4 edges
    >>> g1_node_feats = torch.rand(3, 5)  # feature size is 5
    >>> g1_node_feats
    tensor([[0.8948, 0.0699, 0.9137, 0.7567, 0.3637],
            [0.8137, 0.8938, 0.8377, 0.4249, 0.6118],
            [0.5197, 0.9030, 0.6825, 0.5725, 0.4755]])
    >>>
    >>> g2 = dgl.rand_graph(4, 6)  # g2 is a random graph with 4 nodes and 6 edges
    >>> g2_node_feats = torch.rand(4, 5)  # feature size is 5
    >>> g2_node_feats
    tensor([[0.2053, 0.2426, 0.4111, 0.9028, 0.5658],
            [0.5278, 0.6365, 0.9990, 0.2351, 0.8945],
            [0.3134, 0.0580, 0.4349, 0.7949, 0.3891],
            [0.0142, 0.2709, 0.3330, 0.8521, 0.6925]])
    >>>
    >>> set_trans_enc = SetTransformerEncoder(5, 4, 4, 20)  # create a settrans encoder.

    Case 1: Input a single graph

    >>> set_trans_enc(g1, g1_node_feats)
    tensor([[ 0.1262, -1.9081,  0.7287,  0.1678,  0.8854],
            [-0.0634, -1.1996,  0.6955, -0.9230,  1.4904],
            [-0.9972, -0.7924,  0.6907, -0.5221,  1.6211]],
           grad_fn=<NativeLayerNormBackward>)

    Case 2: Input a batch of graphs

    Build a batch of DGL graphs and concatenate all graphs' node features into one tensor.

    >>> batch_g = dgl.batch([g1, g2])
    >>> batch_f = torch.cat([g1_node_feats, g2_node_feats])
    >>>
    >>> set_trans_enc(batch_g, batch_f)
    tensor([[ 0.1262, -1.9081,  0.7287,  0.1678,  0.8854],
            [-0.0634, -1.1996,  0.6955, -0.9230,  1.4904],
            [-0.9972, -0.7924,  0.6907, -0.5221,  1.6211],
            [-0.7973, -1.3203,  0.0634,  0.5237,  1.5306],
            [-0.4497, -1.0920,  0.8470, -0.8030,  1.4977],
            [-0.4940, -1.6045,  0.2363,  0.4885,  1.3737],
            [-0.9840, -1.0913, -0.0099,  0.4653,  1.6199]],
           grad_fn=<NativeLayerNormBackward>)

    See Also
    --------
    SetTransformerDecoder

    Notes
    -----
    SetTransformerEncoder is not a readout layer, the tensor it returned is nodewise
    representation instead out graphwise representation, and the SetTransformerDecoder
    would return a graph readout tensor.
    """

    def __init__(
        self,
        d_model,
        n_heads,
        d_head,
        d_ff,
        n_layers=1,
        block_type="sab",
        m=None,
        dropouth=0.0,
        dropouta=0.0,
    ):
        super(SetTransformerEncoder, self).__init__()
        self.n_layers = n_layers
        self.block_type = block_type
        self.m = m
        layers = []
        if block_type == "isab" and m is None:
            raise KeyError(
                "The number of inducing points is not specified in ISAB block."
            )

        for _ in range(n_layers):
            if block_type == "sab":
                layers.append(
                    SetAttentionBlock(
                        d_model,
                        n_heads,
                        d_head,
                        d_ff,
                        dropouth=dropouth,
                        dropouta=dropouta,
                    ))
            elif block_type == "isab":
                # layers.append(
                #    InducedSetAttentionBlock(m, d_model, n_heads, d_head, d_ff,
                #                             dropouth=dropouth, dropouta=dropouta))
                raise NotImplementedError()
            else:
                raise KeyError(
                    "Unrecognized block type {}: we only support sab/isab")

        self.layers = nn.ModuleList(layers)

    def forward(self, graph, feat):
        """
        Compute the Encoder part of Set Transformer.

        Parameters
        ----------
        graph : DGLGraph
            The input graph.
        feat : torch.Tensor
            The input feature with shape :math:`(N, D)`, where :math:`N` is the
            number of nodes in the graph.

        Returns
        -------
        torch.Tensor
            The output feature with shape :math:`(N, D)`.
        """
        lengths = graph.batch_num_nodes()
        for layer in self.layers:
            feat = layer(feat, lengths)
        return feat

def _gen_mask(lengths_x, lengths_y, max_len_x, max_len_y):
    """Generate binary mask array for given x and y input pairs.

    Parameters
    ----------
    lengths_x : Tensor
        The int tensor indicates the segment information of x.
    lengths_y : Tensor
        The int tensor indicates the segment information of y.
    max_len_x : int
        The maximum element in lengths_x.
    max_len_y : int
        The maximum element in lengths_y.

    Returns
    -------
    Tensor
        the mask tensor with shape (batch_size, 1, max_len_x, max_len_y)
    """
    device = lengths_x.device
    # x_mask: (batch_size, max_len_x)
    x_mask = torch.arange(max_len_x,
                          device=device).unsqueeze(0) < lengths_x.unsqueeze(1)
    # y_mask: (batch_size, max_len_y)
    y_mask = torch.arange(max_len_y,
                          device=device).unsqueeze(0) < lengths_y.unsqueeze(1)
    # mask: (batch_size, 1, max_len_x, max_len_y)
    mask = (x_mask.unsqueeze(-1) & y_mask.unsqueeze(-2)).unsqueeze(1)
    return mask

def pad_packed_tensor(input, lengths, value):
    """pad_packed_tensor"""
    old_shape = input.shape
    device = input.device
    if not isinstance(lengths, torch.Tensor):
        lengths = torch.tensor(lengths, dtype=torch.int64, device=device)
    else:
        lengths = lengths.to(device)
    max_len = (lengths.max()).item()

    batch_size = len(lengths)
    x = input.new(batch_size * max_len, *old_shape[1:])
    x.fill_(value)

    # Initialize a tensor with an index for every value in the array
    index = torch.ones(len(input), dtype=torch.int64, device=device)

    # Row shifts
    row_shifts = torch.cumsum(max_len - lengths, 0)

    # Calculate shifts for second row, third row... nth row (not the n+1th row)
    # Expand this out to match the shape of all entries after the first row
    row_shifts_expanded = row_shifts[:-1].repeat_interleave(lengths[1:])

    # Add this to the list of inds _after_ the first row
    cumsum_inds = torch.cumsum(index, 0) - 1
    cumsum_inds[lengths[0]:] += row_shifts_expanded
    x[cumsum_inds] = input
    return x.view(batch_size, max_len, *old_shape[1:])


import network
import numpy as np
import tensorflow as tf
import model3 as M
import cv2
import matplotlib.pyplot as plt

def get_prediction(out, scale=4):
	shape = out.shape
	out = out.reshape([shape[0], shape[1]*shape[2], shape[3]])
	scr = np.max(out, axis=1)
	out = np.argmax(out, axis=1)

	res = np.zeros([shape[0], shape[3], 2])
	for i in range(shape[0]):
		for j in range(shape[3]):
			res[i,j,0] = out[i,j] % shape[2]
			res[i,j,1] = out[i,j] // shape[1]
	res = res * scale
	return res, scr

def plot(pts):
	for i in range(len(pts)):
		x = pts[i,0]
		y = pts[i,1]
		plt.plot(x,y,'o')

tf.keras.backend.set_learning_phase(False)
net = network.PosePredNet(19)
saver = M.Saver(net)
saver.restore('./model/')

img2 = cv2.imread('00000000.jpg')
img = np.float32([img2])
out = net(img).numpy()
pts = get_prediction(out)[0]
# print(pts.shape)

plt.imshow(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))
plot(pts[0])
plt.show()


from django.urls import path

from . import views

urlpatterns = [
    path("feed/", views.ListNearEarthObjects.as_view(), name="feed-endpoint"),
    path(
        r"detail/<int:pk>",
        views.DetailNearEarthObject.as_view(),
        name="detail-endpoint",
    ),
]


import unittest
from discount_strategies import PercentageDiscount, BuyOneGetOneFree

class TestDiscountStrategies(unittest.TestCase):

    def test_percentage_discount(self):
        percentage_discount = PercentageDiscount(20)  # this is at 20% discount

        discounted_price = percentage_discount.apply_discount(100)
        self.assertEqual(discounted_price, 80)  # this is at 20% off on actual

    def test_buy_one_get_one_free(self):
        buy_one_get_one_free = BuyOneGetOneFree()

        discounted_price = buy_one_get_one_free.apply_discount(50)
        self.assertEqual(discounted_price, 25)  # this is offer buy one, get one free

if __name__ == "__main__":
    unittest.main()


from django.db import models
from django.conf import settings
# from django.contrib.auth.models import AbstractUser

class Post(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)
    image = models.ImageField(null=True, blank=True)

    def __str__(self):
        return self.title

class Comment(models.Model):
    post = models.ForeignKey(Post,
                             on_delete=models.CASCADE,
                             related_name='commentsFromModel')
    # TH có related_name thì shell dùng Post.commentsFromModel.all()
    # TH KO có related_name thì shell dùng Post.comment_set.all()
    author = models.ForeignKey(settings.AUTH_USER_MODEL,
                               default='',
                               on_delete=models.CASCADE)
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)

class Choice(models.Model):
    question = models.ForeignKey(Post, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=100)
    vote = models.IntegerField(default=0)

    def __str__(self):
        return '{}, {}'.format(self.question, self.choice_text)

# class CustomUser(AbstractUser):
#     age = models.IntegerField(default=0)
#     gender_type = ((0, "Nữ"), (1, "Nam"), (2, "Unknown"))
#     gender = models.IntegerField(choices=gender_type, default=0)
#     address = models.CharField(default='', max_length=255)


import random
import tkinter as tk

# Configuração da janela
janela = tk.Tk()
janela.geometry("400x200")
janela.title("Jogo de Adivinhação")
janela.configure(bg="#1c1c1c")

# Configuração dos widgets
titulo = tk.Label(janela, text="Adivinhe o número entre 1 e 10", font=("Helvetica", 12), fg="white", bg="#1c1c1c")
titulo.pack(pady=10)

entrada = tk.Entry(janela, width=3, font=("Helvetica", 12))
entrada.pack(pady=5)

texto_resultado = tk.Label(janela, font=("Helvetica", 12), fg="white", bg="#1c1c1c")
texto_resultado.pack(pady=5)

botao_testar = tk.Button(janela, text="Testar", font=("Helvetica", 12), command=lambda: testar_numero())
botao_testar.pack(pady=5)

botao_reiniciar = tk.Button(janela, text="Jogar Novamente", font=("Helvetica", 12), command=lambda: reiniciar_jogo())
botao_reiniciar.pack(pady=5)

# Variáveis do jogo
numero_secreto = random.randint(1, 10)
tentativas_restantes = 5

# Funções do jogo
def testar_numero():
    global tentativas_restantes

    # Verifica se o jogador ainda tem tentativas
    if tentativas_restantes > 1:
        # Decrementa o número de tentativas
        tentativas_restantes -= 1

        # Obtém o número inserido pelo jogador
        numero_inserido = int(entrada.get())
        entrada.delete(0, tk.END)

        # Verifica se o número inserido está correto
        if numero_inserido == numero_secreto:
            texto_resultado.config(text="Parabéns! Você acertou!")
            botao_testar.pack_forget()
            botao_reiniciar.pack(pady=10)
            entrada.config(state=tk.DISABLED)

        elif numero_inserido > numero_secreto:
            texto_resultado.config(text="O número secreto é menor. Tentativas restantes: {}".format(tentativas_restantes))

        else:
            texto_resultado.config(text="O número secreto é maior. Tentativas restantes: {}".format(tentativas_restantes))

    # Jogador não tem mais tentativas
    else:
        texto_resultado.config(text="Suas tentativas acabaram. O número era {}".format(numero_secreto))
        botao_testar.pack_forget()
        botao_reiniciar.pack(pady=10)
        entrada.config(state=tk.DISABLED)

def reiniciar_jogo():
    global numero_secreto, tentativas_restantes

    numero_secreto = random.randint(1, 10)
    tentativas_restantes = 5

    texto_resultado.config(text="")
    botao_testar.pack(pady=5)
    botao_reiniciar.pack_forget()
    entrada.config(state=tk.NORMAL)

# Inicialização do jogo
janela.mainloop()


from ut_01_namefunc import get_formatted_name

print('Info: Press "q" to quit anytime\n')
while True:
    first = input('First Name: ')
    if first == 'q':
        break
    last = input('Last Name: ')
    if last == 'q':
        break
    full_name = get_formatted_name(first, last)
    print(f"{full_name} \n")


import attr

from exam_gen.property.has_dir_path import HasDirPath
from exam_gen.util.with_options import WithOptions

import exam_gen.util.logging as logging

# TODO : Check if
from attr._make import (_CountingAttr)

log = logging.new(__name__, level="DEBUG")

@attr.s(init=False)
class Classroom(HasDirPath, WithOptions):

    roster = attr.ib(default=None, kw_only=True)
    """
    Rosters are mandatory entries that let us retrieve a list of students,
    relevant metadata and the like.
    """

    answers = attr.ib(default=None, kw_only=True)
    """
    Answers are optional entries that let us import answers students have
    given to each question.
    """

    scores  = attr.ib(default=None, kw_only=True)
    """
    Score are optional entries that let us import manually scored or annotated
    results for each question.
    """

    grades  = attr.ib(default=None, kw_only=True)
    """
    Grades are optional entries that describe how to output and tweak grade
    information for each student.
    """

    exam = attr.ib()
    """
    The exam object this classroom is attached to.
    """

    students = attr.ib(factory=dict)
    """
    Cache where we store the generated student data.
    """

    # def __init__(self,

    def __init__(self, exam, **kwargs):

        key_attribs = ['roster', 'answers', 'scores', 'grades']

        new_kwargs = dict()

        for k in key_attribs:
            attr = getattr(type(self), k, None)
            if (k not in kwargs and attr != None
               and not isinstance(attr,_CountingAttr)):
                new_kwargs[k] = attr

        kwargs |= new_kwargs

        self.__attrs_init__(exam, **kwargs)

    def __attrs_post_init__(self):

        if hasattr(super(),'__attrs_post_init__'):
            super().__attrs_post_init__()

        self.roster = self.roster(parent_obj = self,
                                  exam = self.exam)

        if self.answers != None:
            self.answers = self.answers(parent_obj = self, exam = self.exam)
        if self.scores  != None:
            self.scores = self.scores( parent_obj = self, exam = self.exam)
        if self.grades  != None:
            self.grades = self.grades( parent_obj = self,exam = self.exam)

    def __getitem__(self, name):
        return self.students[item]

    def load_students(self):
        self.students |= self.roster.load_roster()

    def load_answers(self):

        assert self.students != None, (
            "Classroom must load students before loading answers.")

        answers = self.answers.load_answers(self.students)

        for (ident, answer) in answers.items():
            if self.students[ident].answer_data == None:
                self.students[ident].answer_data = answer
            else:
                self.students[ident].answer_data.merge(answer)

    def load_scores(self):
        scores = self.scored.load_scores()

        for (ident, score) in scores.items():
            if self.students[ident].score_data == None:
                self.students[ident].score_data = score
            else:
                self.students[ident].score_data.merge(score)

    def assign_grades(self, ident, grade_data):
        self.students[ident].grade_data = grade_data

    def print_grades(self, out_dir):
        self.grades.print_grades(self.students, out_dir)


#
# Author: John Tran <jhtran@att.com>
#         Julien Danjou <julien@danjou.info>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Tests for ceilometer/storage/impl_sqlalchemy.py

.. note::
  In order to run the tests against real SQL server set the environment
  variable CEILOMETER_TEST_SQL_URL to point to a SQL server before running
  the tests.

"""

import datetime
import repr

import mock
from oslo.utils import timeutils

from ceilometer.alarm.storage import impl_sqlalchemy as impl_sqla_alarm
from ceilometer.storage import impl_sqlalchemy
from ceilometer.storage import models
from ceilometer.storage.sqlalchemy import models as sql_models
from ceilometer.tests import base as test_base
from ceilometer.tests import db as tests_db
from ceilometer.tests.storage import test_storage_scenarios as scenarios

@tests_db.run_with('sqlite')
class CeilometerBaseTest(tests_db.TestBase):

    def test_ceilometer_base(self):
        base = sql_models.CeilometerBase()
        base['key'] = 'value'
        self.assertEqual('value', base['key'])

@tests_db.run_with('sqlite')
class TraitTypeTest(tests_db.TestBase):
    # TraitType is a construct specific to sqlalchemy.
    # Not applicable to other drivers.

    def test_trait_type_exists(self):
        tt1 = self.conn._get_or_create_trait_type("foo", 0)
        self.assertTrue(tt1.id >= 0)
        tt2 = self.conn._get_or_create_trait_type("foo", 0)
        self.assertEqual(tt2.id, tt1.id)
        self.assertEqual(tt2.desc, tt1.desc)
        self.assertEqual(tt2.data_type, tt1.data_type)

    def test_new_trait_type(self):
        tt1 = self.conn._get_or_create_trait_type("foo", 0)
        self.assertTrue(tt1.id >= 0)
        tt2 = self.conn._get_or_create_trait_type("blah", 0)
        self.assertNotEqual(tt1.id, tt2.id)
        self.assertNotEqual(tt1.desc, tt2.desc)
        # Test the method __repr__ returns a string
        self.assertTrue(repr.repr(tt2))

    def test_trait_different_data_type(self):
        tt1 = self.conn._get_or_create_trait_type("foo", 0)
        self.assertTrue(tt1.id >= 0)
        tt2 = self.conn._get_or_create_trait_type("foo", 1)
        self.assertNotEqual(tt1.id, tt2.id)
        self.assertEqual(tt2.desc, tt1.desc)
        self.assertNotEqual(tt1.data_type, tt2.data_type)
        # Test the method __repr__ returns a string
        self.assertTrue(repr.repr(tt2))

@tests_db.run_with('sqlite')
class EventTypeTest(tests_db.TestBase):
    # EventType is a construct specific to sqlalchemy
    # Not applicable to other drivers.

    def test_event_type_exists(self):
        et1 = self.conn._get_or_create_event_type("foo")
        self.assertTrue(et1.id >= 0)
        et2 = self.conn._get_or_create_event_type("foo")
        self.assertEqual(et2.id, et1.id)
        self.assertEqual(et2.desc, et1.desc)

    def test_event_type_unique(self):
        et1 = self.conn._get_or_create_event_type("foo")
        self.assertTrue(et1.id >= 0)
        et2 = self.conn._get_or_create_event_type("blah")
        self.assertNotEqual(et1.id, et2.id)
        self.assertNotEqual(et1.desc, et2.desc)
        # Test the method __repr__ returns a string
        self.assertTrue(repr.repr(et2))

class MyException(Exception):
    pass

@tests_db.run_with('sqlite')
class EventTest(tests_db.TestBase):
    def test_string_traits(self):
        model = models.Trait("Foo", models.Trait.TEXT_TYPE, "my_text")
        trait = self.conn._make_trait(model, None)
        self.assertEqual(models.Trait.TEXT_TYPE, trait.trait_type.data_type)
        self.assertIsNone(trait.t_float)
        self.assertIsNone(trait.t_int)
        self.assertIsNone(trait.t_datetime)
        self.assertEqual("my_text", trait.t_string)
        self.assertIsNotNone(trait.trait_type.desc)

    def test_int_traits(self):
        model = models.Trait("Foo", models.Trait.INT_TYPE, 100)
        trait = self.conn._make_trait(model, None)
        self.assertEqual(models.Trait.INT_TYPE, trait.trait_type.data_type)
        self.assertIsNone(trait.t_float)
        self.assertIsNone(trait.t_string)
        self.assertIsNone(trait.t_datetime)
        self.assertEqual(100, trait.t_int)
        self.assertIsNotNone(trait.trait_type.desc)

    def test_float_traits(self):
        model = models.Trait("Foo", models.Trait.FLOAT_TYPE, 123.456)
        trait = self.conn._make_trait(model, None)
        self.assertEqual(models.Trait.FLOAT_TYPE, trait.trait_type.data_type)
        self.assertIsNone(trait.t_int)
        self.assertIsNone(trait.t_string)
        self.assertIsNone(trait.t_datetime)
        self.assertEqual(123.456, trait.t_float)
        self.assertIsNotNone(trait.trait_type.desc)

    def test_datetime_traits(self):
        now = datetime.datetime.utcnow()
        model = models.Trait("Foo", models.Trait.DATETIME_TYPE, now)
        trait = self.conn._make_trait(model, None)
        self.assertEqual(models.Trait.DATETIME_TYPE,
                         trait.trait_type.data_type)
        self.assertIsNone(trait.t_int)
        self.assertIsNone(trait.t_string)
        self.assertIsNone(trait.t_float)
        self.assertEqual(now, trait.t_datetime)
        self.assertIsNotNone(trait.trait_type.desc)

    def test_bad_event(self):
        now = datetime.datetime.utcnow()
        m = [models.Event("1", "Foo", now, []),
             models.Event("2", "Zoo", now, [])]

        with mock.patch.object(self.conn, "_record_event") as mock_save:
            mock_save.side_effect = MyException("Boom")
            problem_events = self.conn.record_events(m)
        self.assertEqual(2, len(problem_events))
        for bad, event in problem_events:
            self.assertEqual(bad, models.Event.UNKNOWN_PROBLEM)

    def test_get_none_value_traits(self):
        model = sql_models.Trait(None, None, 5)
        self.assertIsNone(model.get_value())
        self.assertTrue(repr.repr(model))

    def test_event_repr(self):
        ev = sql_models.Event('msg_id', None, False)
        ev.id = 100
        self.assertTrue(repr.repr(ev))

@tests_db.run_with('sqlite')
class RelationshipTest(scenarios.DBTestBase):
    # Note: Do not derive from SQLAlchemyEngineTestBase, since we
    # don't want to automatically inherit all the Meter setup.

    @mock.patch.object(timeutils, 'utcnow')
    def test_clear_metering_data_meta_tables(self, mock_utcnow):
        mock_utcnow.return_value = datetime.datetime(2012, 7, 2, 10, 45)
        self.conn.clear_expired_metering_data(3 * 60)

        session = self.conn._engine_facade.get_session()
        meta_tables = [sql_models.MetaText, sql_models.MetaFloat,
                       sql_models.MetaBigInt, sql_models.MetaBool]
        for table in meta_tables:
            self.assertEqual(0, (session.query(table)
                                 .filter(~table.id.in_(
                                     session.query(sql_models.Sample.id)
                                     .group_by(sql_models.Sample.id))).count()
                                 ))

class CapabilitiesTest(test_base.BaseTestCase):
    # Check the returned capabilities list, which is specific to each DB
    # driver

    def test_capabilities(self):
        expected_capabilities = {
            'meters': {'pagination': False,
                       'query': {'simple': True,
                                 'metadata': True,
                                 'complex': False}},
            'resources': {'pagination': False,
                          'query': {'simple': True,
                                    'metadata': True,
                                    'complex': False}},
            'samples': {'pagination': True,
                        'groupby': True,
                        'query': {'simple': True,
                                  'metadata': True,
                                  'complex': True}},
            'statistics': {'pagination': False,
                           'groupby': True,
                           'query': {'simple': True,
                                     'metadata': True,
                                     'complex': False},
                           'aggregation': {'standard': True,
                                           'selectable': {
                                               'max': True,
                                               'min': True,
                                               'sum': True,
                                               'avg': True,
                                               'count': True,
                                               'stddev': True,
                                               'cardinality': True}}
                           },
            'events': {'query': {'simple': True}}
        }

        actual_capabilities = impl_sqlalchemy.Connection.get_capabilities()
        self.assertEqual(expected_capabilities, actual_capabilities)

    def test_alarm_capabilities(self):
        expected_capabilities = {
            'alarms': {'query': {'simple': True,
                                 'complex': True},
                       'history': {'query': {'simple': True,
                                             'complex': True}}},
        }

        actual_capabilities = impl_sqla_alarm.Connection.get_capabilities()
        self.assertEqual(expected_capabilities, actual_capabilities)

    def test_storage_capabilities(self):
        expected_capabilities = {
            'storage': {'production_ready': True},
        }
        actual_capabilities = (impl_sqlalchemy.
                               Connection.get_storage_capabilities())
        self.assertEqual(expected_capabilities, actual_capabilities)