#main file of the straight track (easy mode), all functions controlling user/computer players

from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from panda3d.core import *
from direct.gui.OnscreenText import OnscreenText
from direct.gui.OnscreenImage import OnscreenImage
import sys
import math
import random

def displayBigText(txt, position, color, shad = (0,0,0,1)):
    size = 0.25
    display = OnscreenText(text = txt, fg = color, scale = size,
                    shadow = shad, parent=base.a2dTopLeft,
                    pos=position, align=TextNode.ALeft)
    return display

def displaySmallText(txt, position, color):
    size = 0.1
    display = OnscreenText(text = txt, fg = color, scale = size,
                        parent=base.a2dTopLeft, pos=position,
                        align=TextNode.ALeft)
    return display

def roundtoThou(n):
    return math.ceil(n * 1000.0)/1000.0

class Game(ShowBase):

    def __init__(self, screen='start'):
        ShowBase.__init__(self)

        #countdown sound file from https://www.youtube.com/watch?v=KOoCEIwswYg
        self.cdMusic = loader.loadSfx("sounds/countdown.ogg")
        #race sound file from https://www.youtube.com/watch?v=eawQqkq8ROo
        self.rMusic = loader.loadSfx("sounds/race.ogg")

        #window and camera
        self.win.setClearColor((0, 0, 0, 1))
        self.startPos = LPoint3f(100, 50, 0)
        self.disableMouse()
        self.x = self.startPos.getX()
        self.y = self.startPos.getY()
        self.z = 15
        self.camera.setPos(-20, self.y/3, self.z)
        self.camera.setH(90)
        self.camMaxDist = 30
        self.camMinDist = 25

        #mario character (user player)
        #3d mario model file from past tp, with permission:
        #https://github.com/Kabartchlett/15-112-Term-Project
        self.mario = Actor("models/MarioKart")
        self.scale = 2.5
        self.mario.setScale(self.scale)
        self.mario.setH(self.mario, 90)
        self.mario.reparentTo(render)
        self.marioX = 3
        self.marioY = self.startPos.getY() /3
        self.marioZ = 11
        self.mario.setPos(self.marioX, self.marioY, self.marioZ)
        self.mario.y_speed = -50
        self.mario.h_speed = 0
        self.marioRaceTime = 0
        self.marioFinishTime = 0
        self.drifted = False
        self.marioNumFellOff = 0
        self.mario.falling = False
        self.marioStartFallTime = None
        self.marioStartFallPos = None
        self.mario.mush = False
        self.mario.megamush = False
        self.mario.bananaSpin = False
        self.mario.preSpinAngle = self.mario.getH()
        self.mario.doubleMush = False

        #floater method from roaming ralph:
        #https://github.com/panda3d/panda3d/tree/master/samples/roaming-ralph
        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(self.mario)
        self.floater.setZ(2)

        #computer player
        #3d yoshi model file from past tp, with permission:
        #https://github.com/Kabartchlett/15-112-Term-Project
        self.yoshi = Actor("models/YoshiKart")
        self.yoshi.setScale(self.scale)
        self.yoshi.setH(self.yoshi, 90)
        self.yoshi.reparentTo(render)
        self.yoshiX = 3
        self.yoshiY = self.startPos.getY() *(2/3)
        self.yoshiZ = 10
        self.yoshi.setPos(self.yoshiX, self.yoshiY, self.yoshiZ)
        self.yoshi.y_speed = -45
        self.yoshi.h_speed = 0
        self.yoshiRaceTime = 0
        self.yoshiFinishTime = 0
        self.yoshi.mush = False
        self.yoshi.megamush = False
        self.yoshi.bananaSpin = False
        self.yoshi.preSpinAngle = self.yoshi.getH()
        self.yoshi.doubleMush = False
        self.yoshi.falling = False

        #statuses
        self.gameover = False
        self.victory = False
        self.loss = False
        self.menuCount = 0
        self.paused = False
        self.addMove = 1
        self.timeDelay = 0
        self.start = True
        self.escapeStart = False
        self.menuStart = False
        self.gameMode = None
        self.startMode = True
        self.escapeMode = False
        self.charFin = True
        self.counting = True
        self.playing = True
        self.playRaceMusic = False

        #data init
        self.vDataRows = 50
        self.finalX = self.x
        self.lines = []
        self.isPaused = []
        self.wait = 11
        self.countdowns = []
        self.startText = []
        self.modeText = []
        self.OSimageText = []
        self.timeCharFin = None
        self.lives = []

        #collision with items init
        self.cTrav = CollisionTraverser()
        from itemStr import Item
        self.itemClass = Item(self)

        self.yoshiHandler = CollisionHandlerEvent()
        self.yoshiHandler.addInPattern('into-self.yoshi')

        self.collisions = self.collideInit()

        self.cTrav.addCollider(self.collisions, self.yoshiHandler)
        self.accept('into-self.yoshi', self.collideYoshi)

        #collision with road init
        self.ray = CollisionRay(0,0,self.mario.getZ(), 0,0,-1)
        self.roadCol = self.mario.attachNewNode(CollisionNode('colNode'))
        self.roadCol.node().addSolid(self.ray)
        self.roadCol.node().setCollideMask(BitMask32.allOn())
        self.mario.setCollideMask(BitMask32.allOff())
        self.lifter = CollisionHandlerFloor()
        self.cTrav.addCollider(self.roadCol, self.lifter)
        self.lifter.addCollider(self.roadCol, self.mario)

        #key events
        self.accept('escape', sys.exit)
        self.accept('q', self.menuToggle, ['display'])
        self.accept('e', self.menuToggle, ['remove'])
        self.accept('p', self.pause)

        #tasks
        if self.start and screen != 'mode':
            taskMgr.add(self.startScreen, 'start')

    def addTasks(self,task):
        self.straightRoad()
        self.displayLives()
        taskMgr.add(self.move, "move")
        taskMgr.add(self.moveSim, 'moveSim')
        taskMgr.add(self.countdown, 'countdown')
        taskMgr.add(self.mapPrev, 'map')
        taskMgr.add(self.takeLives, 'lives')
        taskMgr.add(self.moveCam, 'cam')

    def collideInit(self):
        mNode = CollisionNode('mario')
        mNode.addSolid(CollisionSphere(0,0,0,self.scale-2))
        marioCol = self.mario.attachNewNode(mNode)

        yNode = CollisionNode('yoshi')
        yNode.addSolid(CollisionSphere(0,0,0,self.scale-2))
        yoshiCol = self.yoshi.attachNewNode(yNode)

        return marioCol

    def straightRoad(self):

        #geom format and data
        z = 10

        format = GeomVertexFormat.getV3n3c4t2()
        vData = GeomVertexData('road', format, Geom.UHStatic)
        vData.setNumRows(self.vDataRows)

        vertex = GeomVertexWriter(vData, 'vertex')
        normal = GeomVertexWriter(vData, 'normal')
        color = GeomVertexWriter(vData, 'color')
        texcoord = GeomVertexWriter(vData, 'texcoord')

        for i in range(self.vDataRows):
            x = i * self.x
            vertex.addData3f(x, 0, z)
            vertex.addData3f(x, self.y, z)

            for n in range(2):
                normal.addData3f(x,self.y/2,z)

            if i > self.vDataRows-6: #finish line
                for n in range(4):
                    color.addData4f(255,0,0,1)
            elif i%2 == 0: #rainbow road
                color.addData4f(0,255,255,1)
                color.addData4f(0,255,0,1)
                color.addData4f(255,255,0,1)
                color.addData4f(255,0,255,1)

            texcoord.addData2f(x,0)
            texcoord.addData2f(x,self.y)

        self.finalX = x

        #geom primitives
        road = GeomTristrips(Geom.UHStatic)
        road.add_consecutive_vertices(0,2*self.vDataRows)
        road.close_primitive()

        #connect data and primitives
        geom = Geom(vData)
        geom.addPrimitive(road)

        node = GeomNode('geomNode')
        node.addGeom(geom)

        nodePath = render.attachNewNode(node)

        nodePath.set_two_sided(True)

    def collideYoshi(self, collEntry):
        #altered use of conservation of momentum

        if self.mario.getH() != 90:
            mario_sign = (90 - self.mario.getH()) // abs(90 - self.mario.getH())
        else: mario_sign = 0
        if self.yoshi.getH() != 90:
            yoshi_sign = (90 - self.yoshi.getH()) // abs(90 - self.yoshi.getH())
        else: yoshi_sign = 0
        mario_xSpeed = self.mario.y_speed * math.cos(self.mario.getH())
        mario_ySpeed = self.mario.y_speed * math.sin(self.mario.getH())
        yoshi_xSpeed = self.yoshi.y_speed * math.cos(self.yoshi.getH())
        yoshi_ySpeed = self.yoshi.y_speed * math.sin(self.yoshi.getH())

        x_speedAvg = ( mario_xSpeed + yoshi_xSpeed ) / 2
        y_speedAvg = ( mario_ySpeed + yoshi_ySpeed ) / 2

        marioAngle = math.degrees(math.acos( y_speedAvg/self.mario.y_speed ))
        yoshiAngle = math.degrees(math.acos( y_speedAvg/abs(self.yoshi.y_speed) ))

        self.mario.setH(self.mario.getH() + mario_sign*0.2*marioAngle)
        self.yoshi.setH(self.yoshi.getH() - mario_sign*0.2*yoshiAngle)

    def isOffRoad(self, player):
        return player.getY() < 0 or player.getY() > self.y or \
            player.getX() < 0 or player.getX() > self.finalX

    def checkItemCol(self,player):
        #checks if player ran into an item box
        for i in range(len(self.itemClass.items)):
            items = self.itemClass.items[i]
            if self.itemClass.itemsPresent[i] and abs(items.getX() - player.getX()) <= 3 and\
                    abs(items.getY() - player.getY()) <= 3 and\
                    abs(items.getZ() - player.getZ()) <= 3:
                self.itemClass.collideItem(items, i, player)

    def checkBananaCol(self,player):
        #makes player react to running into a banana

        #banana collision detection
        for i in range(len(self.itemClass.bananas)):
            banana = self.itemClass.bananas[i]
            if self.itemClass.bananaPresent[i] and abs(banana.getX() - player.getX()) <= 3 and\
                    abs(banana.getY() - player.getY()) <= 3 and\
                    abs(banana.getZ() - player.getZ()) <= 3:
                self.itemClass.bananaReact(banana, i, player)
                player.preSpinAngle = player.getH()

        #spin player
        if player.bananaSpin and globalClock.getFrameTime() < \
                            self.itemClass.beginSpin + self.itemClass.spinTime:
            player.h_speed = 700
            player.y_speed = 0

        #end spin
        if player.bananaSpin and (abs(globalClock.getFrameTime() - (self.itemClass.beginSpin + self.itemClass.spinTime)) < 1):
            player.bananaSpin = False
            player.h_speed = 0
            if player == self.yoshi: player.y_speed = -25
            elif player == self.mario: player.y_speed = -30
            player.setH(90)

    def checkMushEnd(self, player):
        #ends the effect of mushroom or mega mushroom
        if player.mush:
            if player.falling or abs(player.getX() - self.itemClass.beginBoostX) >= \
                                                self.itemClass.boostDistance:
                player.mush = False
                self.itemClass.mushroom(player)

        if player.megamush:
            if player.falling or abs(player.getX()  - self.itemClass.beginBoostX) >= \
                                                    self.itemClass.boostDistance:
                player.megamush = False
                self.itemClass.megaMushroom(player)

    def moveSim(self,task):
        dt = globalClock.getDt()
        self.timeDelay += dt

        h_speed = self.yoshi.h_speed
        y_speed = self.yoshi.y_speed
        z_speed = 10

        #chance of falling off road
        chance = 1
        maxNum = 100
        fall = random.randint(1,maxNum)
        if fall <= chance:
            h_speed = 5
        if fall >= maxNum-chance:
            h_speed = -5

        #checks for banana collision
        self.checkBananaCol(self.yoshi)

        #checks for item collision
        self.checkItemCol(self.yoshi)

        #change position, angle
        dh = h_speed * dt
        dy = y_speed * dt
        dz = z_speed * dt
        if self.timeDelay > self.wait:
            self.yoshiRaceTime += dt
            self.yoshi.setH(self.yoshi.getH() + dh)
            self.yoshi.setY(self.yoshi, dy)

        #stop at end of road
        if self.yoshi.getX() > self.finalX - 20:
            self.yoshi.y_speed = 0

        #fell off road
        if self.isOffRoad(self.yoshi):
            self.yoshi.y_speed = 0
            self.yoshi.setZ(self.yoshi.getZ() - dz)

        #crossed finish line
        if self.yoshi.getX() > (self.vDataRows-4)*self.x and self.yoshi.getZ() >= 10:
            self.yoshiFinishTime = math.ceil(self.yoshiRaceTime * 1000.0)/1000.0
            self.loss = True
            escText = displaySmallText('press esc to quit program', (0.8,-1.2), (255,255,255,0.7))
            self.OSimageText.append(escText)
            if self.gameover == False and self.victory == False:
                compwins = displayBigText('COMPUTER WINS', (0.4, -1),  (255,0,0,0.5))
                self.OSimageText.append(compwins)

        #checks for power ups
        self.checkMushEnd(self.yoshi)

        return task.cont

    def move(self, task):

        if self.playing and self.playRaceMusic:
            self.rMusic.play()
            self.playing = False

        dt = globalClock.getDt()
        self.timeDelay += dt

        #enable moving mario with direction keys
        h_speed = self.mario.h_speed
        y_speed = 0
        x_speed = 0
        z_speed = 10
        h_incr = 80
        y_incr = self.mario.y_speed
        right_down = base.mouseWatcherNode.isButtonDown(KeyboardButton.right())
        left_down = base.mouseWatcherNode.isButtonDown(KeyboardButton.left())
        forward_down = base.mouseWatcherNode.isButtonDown(KeyboardButton.up())
        backward_down = base.mouseWatcherNode.isButtonDown(KeyboardButton.down())

        if right_down:
            h_speed -= h_incr
        if left_down:
            h_speed += h_incr
        if forward_down:
            y_speed += y_incr
        if backward_down:
            y_speed -= y_incr

        #checks for banana punishment
        self.checkBananaCol(self.mario)

        #checks for collision with item boxes
        self.checkItemCol(self.mario)

        #change position, angle
        dh = h_speed * dt
        dy = y_speed * dt
        dz = z_speed * dt
        dx = x_speed * dt
        if self.timeDelay > self.wait:
            self.marioRaceTime += dt
            self.mario.setH(self.mario.getH() + dh)
            self.mario.setY(self.mario, dy)
            self.mario.setX(self.mario, dx)

        #fell off road
        if self.isOffRoad(self.mario) and not self.mario.falling:
            self.marioNumFellOff += 1
            self.marioStartFallTime = globalClock.getFrameTime()
            self.marioStartFallPos = self.mario.getPos()
            self.mario.falling = True
            if self.marioNumFellOff > 2:
                self.gameOver()

        if self.mario.falling:
            self.mario.setZ(self.mario.getZ() - dz)
            self.mario.y_speed = 0
            self.resetPreFallPos(self.mario, self.marioStartFallPos)

        #crossed finish line
        if self.mario.getX() > (self.vDataRows-4)*self.x:
            self.marioFinishTime = roundtoThou(self.marioRaceTime)
            self.victory = True
            if self.loss == False and self.gameover == False:
                vic = displayBigText('VICTORY!', (0.8, -1),  (255,255,0,1))
                self.OSimageText.append(vic)
                escText = displaySmallText('press esc to quit program', (0.8,-1.2), (255,255,255,0.7))
                self.OSimageText.append(escText)

        #show leaderboard if both players crossed finish line
        if ( self.gameover or self.victory or self.loss ) and self.charFin:
            self.timeCharFin = globalClock.getFrameTime()
            self.charFin = False

        if self.timeCharFin != None and globalClock.getFrameTime() > self.timeCharFin + self.wait/2:
            self.leaderboard()

        #checks for power ups
        self.checkMushEnd(self.mario)

        return task.cont

    def moveCam(self,task):
        dt = globalClock.getDt()

        #enable camera spinning around mario using a,s,w,x,k,l keys
        cam = 0
        camSpeed = 20
        a_down = base.mouseWatcherNode.isButtonDown(KeyboardButton.asciiKey(b"a"))
        s_down = base.mouseWatcherNode.isButtonDown(KeyboardButton.asciiKey(b"s"))

        #wxkl for debugging purposes

        if a_down:
            cam += camSpeed
        if s_down:
            cam -= camSpeed

        dCam = cam * dt
        self.camera.setX(self.camera, dCam)

        #keep camera close to mario, from roaming ralph:
        #https://github.com/panda3d/panda3d/tree/master/samples/roaming-ralph
        camVector = self.mario.getPos() - self.camera.getPos()
        camVector.setZ(0) #don't change camera's z distance
        camDist = camVector.length() #magnitude
        camVector.normalize() #unit vector (direction)

        if camDist > self.camMaxDist:
            self.camera.setPos(self.camera.getPos() + \
                                camVector * (camDist - self.camMaxDist))
            camDist = self.camMaxDist
        if camDist < self.camMinDist:
            self.camera.setPos(self.camera.getPos() - \
                                camVector * (self.camMinDist - camDist))
            camDist = self.camMinDist

        if self.camera.getZ() != self.mario.getZ() + 6.0:
            self.camera.setZ(self.mario.getZ() + 6.0)

        self.cTrav.traverse(render)

        self.camera.lookAt(self.floater)

        return task.cont

    def getPlayerProg(self, player):
        totalX = self.finalX
        x = player.getX()
        progress = x/totalX
        return progress

    def mapPrev(self,task):
        #show map in corner with all player's positions

        radius = 0.085
        length = 2*radius
        #image made in Preview
        mapP = OnscreenImage(image = 'images/straightMap.png', pos = (1,0,0.8), scale = 0.1)
        self.OSimageText.append(mapP)

        marioProg = self.getPlayerProg(self.mario)
        marioY = (0.8-radius) + length*marioProg

        yoshiProg = self.getPlayerProg(self.yoshi)
        yoshiY = (0.8-radius) + length*yoshiProg

        finishY = 0.8 + radius

        #images made in Preview
        marioSym = OnscreenImage(image = 'images/mariosym.png',
                                pos = (1,0,marioY), scale = 0.01)
        yoshiSym = OnscreenImage(image = 'images/yoshisym.png',
                                pos = (1,0,yoshiY), scale = 0.01)
        finishSym = OnscreenImage(image = 'images/finishsym.png',
                                pos = (1,0,finishY), scale = 0.01)

        self.OSimageText.append(marioSym)
        self.OSimageText.append(yoshiSym)
        self.OSimageText.append(finishSym)

        return task.cont

    def resetPreFallPos(self, player, pos):
        #only mario is replaced on the track
        if self.marioStartFallTime != None and not self.gameover and globalClock.getFrameTime() > self.marioStartFallTime + self.wait/4:
            self.mario.falling = False

            x = player.getX()
            y = self.y/2
            z = player.getY()

            player.setPos(x,y,z)
            self.mario.y_speed = -30

    def displayLives(self):
        #display the number of lives user player has left

        livesText = displaySmallText('lives:', (0.1,-0.15), (255,255,255,1))
        self.OSimageText.append(livesText)
        #images made in Preview
        self.life1 = OnscreenImage(image = 'images/life.png', pos = (-0.9,0,0.87), scale = 0.03)
        self.life2 = OnscreenImage(image = 'images/life.png', pos = (-0.8,0,0.87), scale = 0.03)
        self.life3 = OnscreenImage(image = 'images/life.png', pos = (-0.7,0,0.87), scale = 0.03)
        self.OSimageText.append(self.life1)
        self.OSimageText.append(self.life2)
        self.OSimageText.append(self.life3)

    def takeLives(self,task):
        if self.marioNumFellOff == 1: self.life3.destroy()
        if self.marioNumFellOff == 2: self.life2.destroy()
        if self.marioNumFellOff == 3: self.life1.destroy()

        return task.cont

    def startScreen(self, task):
        if self.escapeStart: return

        if self.start:
            #image from supermariorun.com
            logo = OnscreenImage(image = 'images/mario.png', pos = (0.01,0,-0.5), scale = 0.3)

            welcome = OnscreenText('Welcome to Mario Kart!', fg = (255,0,0,1), scale = 0.2,
                                    shadow = (255,255,255,1), parent = base.a2dTopLeft,
                                    pos = (0.2, -0.5), align = TextNode.ALeft)

            show_menu = OnscreenText('press Q to see key instructions', fg = (255,255,255,1),
                                    scale = 0.1, shadow = (0,255,0,1),
                                    parent = base.a2dTopLeft, pos = (0.6, -0.8),
                                    align = TextNode.ALeft)

            start_game = OnscreenText('press space to start game!', fg = (255,255,255,1),
                                    scale = 0.1, shadow = (0,0,255,1),
                                    parent = base.a2dTopLeft, pos = (0.7, -1),
                                    align = TextNode.ALeft)

            self.startText.append(logo)
            self.startText.append(welcome)
            self.startText.append(show_menu)
            self.startText.append(start_game)

        q_down = base.mouseWatcherNode.isButtonDown(KeyboardButton.asciiKey(b"q"))
        space_down = base.mouseWatcherNode.isButtonDown(KeyboardButton.space())

        self.menuStart = True
        if q_down or space_down:
            self.start = False
            self.escapeStart = True
            for text in self.startText: text.destroy()
        if space_down:
            self.menuStart = False
            self.addTasks(task)

        return task.cont

    def countdown(self, task):
        #display countdown before game starts
        taskMgr.remove('start')

        dt = globalClock.getDt()
        self.timeDelay += dt
        interval = self.wait/3

        #sound effect
        if self.counting:
            self.cdMusic.play()
            self.counting = False

        if self.timeDelay < interval*1:
            display = displayBigText('3', (1.3,-1), (255,255,255,1), (255,0,0,1))
            self.countdowns.append(display)
        elif self.timeDelay < interval*2:
            for display in self.countdowns: display.destroy()
            display = displayBigText('2', (1.3,-1), (255,255,255,1), (255,255,0,1))
            self.countdowns.append(display)
        elif self.timeDelay < interval*3:
            for display in self.countdowns: display.destroy()
            display = displayBigText('1', (1.3,-1), (255,255,255,1), (0,255,0,1))
            self.countdowns.append(display)
        elif self.timeDelay < interval*4:
            for display in self.countdowns: display.destroy()
            display = displayBigText('GO!', (1.1,-1), (255,255,255,1))
            self.countdowns.append(display)
        elif self.timeDelay > interval*4:
            for display in self.countdowns: display.destroy()
            self.playRaceMusic = True

        return task.cont

    def pause(self, fromFunction = None):
        self.paused = not self.paused

        if self.paused == True:
            if fromFunction == None: #not from menu
                displayPause = displayBigText('PAUSED', (0.85, -1),  (255,255,255,1))
                self.isPaused.append(displayPause)
                self.OSimageText.append(displayPause)

            self.addMove -= 1
            taskMgr.remove("move")
            taskMgr.remove("moveSim")
            taskMgr.remove("countdown")

        else: #unpause
            if fromFunction == None:
                for item in self.isPaused: item.destroy()

            if self.addMove > 0: return
            self.addMove += 1

            taskMgr.add(self.move, "move")
            taskMgr.add(self.moveSim, "moveSim")
            taskMgr.add(self.countdown, 'countdown')

    def menuToggle(self, keyInput):
        #display menu of key instructions

        taskMgr.remove('start')
        for text in self.startText: text.destroy()

        yIncr = -0.1
        directions = [ 'esc: quit program', 'Q: view menu', 'E: escape menu',
                        'P: pause/unpause', 'Up/Down: move Mario forward/backward',
                        "Right/Left: change Mario's direction", 'A/S: rotate camera' ]

        if keyInput == 'display':
            self.menuCount += 1
            if self.menuCount > 1: return

            self.paused = False
            self.pause('menu')

            title = displaySmallText('Mario Kart Key Legend', (0.75,-0.1),
                                    (255,255,255,1))
            self.lines = [ title ]
            self.OSimageText.append(title)

            for i in range(2,len(directions)+2):
                y = yIncr*i

                line = displaySmallText(directions[i-2], (0.1,y), (255,255,255,0.75))
                self.lines.append(line)
                self.OSimageText.append(line)

            #access menu from start screen
            if self.menuStart:
                line = displaySmallText('press E to start game!', (0.8,-1.5),
                                        (255,255,0,1))
                self.lines.append(line)
                self.OSimageText.append(line)
                self.menuStart = False

        if keyInput == 'remove':
            #escape menu
            self.menuCount = 0
            self.paused = True
            self.pause('menu')
            for line in self.lines:
                line.destroy()

            self.straightRoad()
            self.displayLives()
            # taskMgr.add(self.moveSim, 'moveSim')
            # taskMgr.add(self.countdown, 'countdown')
            # taskMgr.add(self.move, "move")
            taskMgr.add(self.mapPrev, 'map')
            taskMgr.add(self.takeLives, 'lives')
            taskMgr.add(self.moveCam, 'cam')

    def gameOver(self):
        self.gameover = True
        if self.victory == False and self.loss == False:
            gameText = displayBigText('GAME OVER', (0.65, -1),  (255,255,255,1))
            self.OSimageText.append(gameText)
            escText = displaySmallText('press esc to quit program', (0.8,-1.2), (255,255,255,0.7))
            self.OSimageText.append(escText)

    def leaderboard(self):
        #remove everything else on the screen
        taskMgr.remove('moveSim')
        taskMgr.remove("move")
        taskMgr.remove('map')
        for child in render.getChildren():
            child.removeNode()
        for item in self.OSimageText:
            item.destroy()

        #characters that didn't finish
        if self.marioFinishTime == 0:
            self.marioFinishTime = roundtoThou(self.marioRaceTime)
        if self.yoshiFinishTime == 0:
            self.yoshiFinishTime = roundtoThou(self.yoshiRaceTime)

        leaderB = displayBigText('LEADERBOARD', (0.45, -0.3), (255,255,0,1))
        firstPlace = displaySmallText('1st:', (0.5, -0.5), (255,255,0,1))
        secondPlace = displaySmallText('2nd:', (0.5, -0.6), (255,255,0,1))

        if self.marioFinishTime <= self.yoshiFinishTime:
            marioY = -0.5
            yoshiY = -0.6
        else:
            marioY = -0.6
            yoshiY = -0.5

        marioRight = displaySmallText('Mario (User)', (0.8, marioY), (255,255,0,1))
        marioLeft = displaySmallText(str(self.marioFinishTime), (1.8, marioY), (255,255,0,1))
        yoshiRight = displaySmallText('Yoshi (Computer)', (0.8, yoshiY), (255,255,0,1))
        marioLeft = displaySmallText(str(self.yoshiFinishTime), (1.8, yoshiY), (255,255,0,1))
        escText = displaySmallText('press esc to quit program', (0.7,-1.8), (255,255,255,0.7))

        #image from https://mariokart.fandom.com/wiki/Mario
        image = OnscreenImage(image = 'images/leaderboardmario.png', pos = (0.01,0,-0.2), scale = 0.4)

game = Game()
game.run()


import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import ExtraTreesRegressor

import pickle
import warnings
warnings.filterwarnings('ignore')

from preprocessing import preprocessing

# load data
data_raw=pd.read_csv("./data/Train.csv")

# preprocess data
data = preprocessing(data_raw, use_location=False, only_means=False)

# split data into X and Y
X = data.drop('target', axis=1)
y = data['target']

# split into train and test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42)

# save data
print("Saving data in the data folder")
X_test.to_csv("data/X_test.csv", index=False)
y_test.to_csv("data/y_test.csv", index=False)
X_train.to_csv("data/X_train.csv", index=False)
y_train.to_csv("data/y_train.csv", index=False)

# model
print("Training Extra Trees Regressor")
reg = ExtraTreesRegressor(n_estimators=400, max_depth=50, max_features= 'log2', random_state=42)
reg.fit(X_train, y_train)

#saving the model
print("Saving model in the model folder")
filename = 'models/ExtraTrees_model.sav'
pickle.dump(reg, open(filename, 'wb'))


from django.shortcuts import render
from app1.models import *
from django.http import HttpResponse
# Create your views here.
def insert_topic(request):
    if request.method=='POST':
        tn=request.POST['topic']
        print(tn)
        TO=topic.objects.create(topic_name=tn)
        TO.save()
        return HttpResponse('dfghjkl')
    return render(request,'insert_topic.html')

def insert_webpage(request):
    TO=topic.objects.all()
    d={'topics':TO}
    if request.method=='POST':
        tn=request.POST['topic']
        n=request.POST['name']
        email=request.POST['email']
        date=request.POST['date']
        TO1=topic.objects.get_or_create(topic_name=tn)[0]
        TO1.save()
        TO3=Webpage.objects.get_or_create(topic_name=TO1,name=n,email=email,date=date)[0]
        TO3.save()
        return HttpResponse('dfghjkl')
    return render(request,'insert_webpage.html',d)

def retrieve_topic(request):
    TO=topic.objects.all()
    d={'topics':TO}
    if request.method=='POST':
        tn=request.POST.getlist('topic')
        new=Webpage.objects.none()

        for i in tn:
            new=new | Webpage.objects.filter(topic_name=i)

        d1={'topics':new}
        return render(request,'retrieve_topic.html',d1)
    return render(request,'retrieve_topics.html',d)


# -*- coding: utf-8 -*-
# __author__ = 'zhaozhigang'
# last modify: 2020.10.26

from biocluster.api.database.base import Base, report_check
import datetime
import json
from bson.son import SON
from bson.objectid import ObjectId
from pymongo import MongoClient

class GenomeQuality(Base):
    def __init__(self, bind_object):
        super(GenomeQuality, self).__init__(bind_object)
        self._project_type = "bacgenome"

    @report_check
    def add_genome_quality(self, main = None,params=None, name=None, task_id = None, project_sn = None, main_id=None):
        if main:
            if task_id is None:
                task_id = self.bind_object.sheet.id
            project_sn = project_sn if project_sn else self.bind_object.sheet.project_sn
            created_ts = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            insert_data = {
                'project_sn': project_sn,
                'task_id': task_id,
                'desc': '基因组评估主表',
                'created_ts': created_ts,
                'name': name if name else 'GenomeQuality',
                'params': json.dumps(params, sort_keys=True, separators=(',', ':')),
                'status': 'start'
            }
            collection = self.db['genome_quality']
            # 将主表名称写在这里
            main_id = collection.insert_one(insert_data).inserted_id
            collection.update({'_id': main_id}, {'$set': {'main_id': main_id}})
        else:
            if main_id is None:
                # raise Exception("main为False时需提供main_id!")
                self.bind_object.set_error("main为False时需提供main_id!", code="51401101")
            if not isinstance(main_id, ObjectId):
                main_id = ObjectId(main_id)
        return main_id

    def add_genome_quality_detail(self, task_id, type, project_sn = None, main_id=None, params=None, name=None):
        if task_id is None:
            task_id = self.bind_object.sheet.id
        params = {"task_id":task_id}
        project_sn = project_sn if project_sn else self.bind_object.sheet.project_sn
        created_ts = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        insert_data = {
                'project_sn': project_sn,
                'task_id': task_id,
                'desc': '基因组评估主表',
                'created_ts': created_ts,
                'name': name if name else 'GenomeQuality',
                'params': json.dumps(params, sort_keys=True, separators=(',', ':')),
                'status': 'start'
        }
        collection = self.db['genome_quality']
        # 将主表名称写在这里
        main_id = collection.insert_one(insert_data).inserted_id
        collection.update({'_id': main_id}, {'$set': {'main_id': main_id}})

        data_list = []
        collection_detail = self.db['genome_quality_detail']
        if type == "uncomplete":
            genome_size_main = self.db.datastat
            genome_size_detail = self.db.datastat_summary
            scaffold_num_main = self.db.assemble
            scaffold_num_detail = self.db.assemble_detail
            gene_num_main = self.db.gene_predict
            gene_num_detail = self.db.gene_predict_specimen
            genome_size_main_data = []
            genome_size_detail_data = []
            scaffold_num_main_data = []
            scaffold_num_detail_data = []
            gene_num_main_data = []
            gene_num_detail_data = []
            sample_id = []
            information = []

            # genome_size 提取
            genome_main_data = genome_size_main.find({"task_id": task_id})
            for i in genome_main_data:
                genome_size_main_data.append(i)
            genome_detail = genome_size_detail.find({"datastat_id": genome_size_main_data[0]["main_id"]})
            for i in genome_detail:
                genome_size_detail_data.append(i)
            if len(genome_size_detail_data) == 0:
                size = 0
            else:
                size = 1
            # scaffold数量提取
            scaffold_main = scaffold_num_main.find({"task_id": task_id})
            for i in scaffold_main:
                scaffold_num_main_data.append(i)
            scaffold_detail = scaffold_num_detail.find({"assemble_id": scaffold_num_main_data[-1]["main_id"]})
            for i in scaffold_detail:
                scaffold_num_detail_data.append(i)
            for i in range(len(scaffold_num_detail_data)):
                if scaffold_num_detail_data[i]["specimen_id"] not in sample_id:
                    sample_id.append(scaffold_num_detail_data[i]["specimen_id"])
            # gene 数量提取
            gene_main = gene_num_main.find({"task_id": task_id})
            for i in gene_main:
                gene_num_main_data.append(i)
            gene_detail_data = gene_num_detail.find({"predict_id": gene_num_main_data[-1]["main_id"]})
            for i in gene_detail_data:
                gene_num_detail_data.append(i)
            for i in sample_id:
                if size:
                    for a in range(len(genome_size_detail_data)):
                        if genome_size_detail_data[a]["specimen_id"] == i:
                            information.append(i)
                            information.append(str(float(genome_size_detail_data[a]["g_size"])/(1000*1000)))
                            break
                else:
                    information.append(i)
                    information.append("-")
                for b in range(len(scaffold_num_detail_data)):
                    if scaffold_num_detail_data[b]["specimen_id"] == i:
                        information.append(scaffold_num_detail_data[b]["scaf_no"])
                        break
                for c in range(len(gene_num_detail_data)):
                    if gene_num_detail_data[c]["specimen_id"] == i:
                        information.append(gene_num_detail_data[c]["num"])
                        information.append(gene_num_detail_data[c]["aver_base"])
                        break
                if (1 <= float(information[1]) <= 10) and (1 <= int(information[2]) <= 100) and (
                        1000 <= float(information[3]) <= 10000) and (800 <= float(information[4]) <= 1200):
                    information.append("yes")
                else:
                    information.append("no")
                data = {
                    "quality_id": main_id,
                    "specimen_id": information[0],
                    "genome_size": information[1],
                    "scaffold_num": information[2],
                    "cds_num": information[3],
                    "cds_average_len": information[4],
                    "normal": information[5]
                    }
                information = []
                data_son = SON(data)
                data_list.append(data_son)
        else:
            genome_size_main = self.db.datastat
            genome_size_detail = self.db.datastat_summary
            chr_num_main = self.db.assemble
            chr_num_detail = self.db.assemble_detail
            gene_num_main = self.db.gene_predict
            gene_num_detail = self.db.gene_predict_specimen
            gene0001_main = self.db.anno_summary
            gene0001_detail = self.db.anno_summary_detail
            genome_size_main_data = []
            genome_size_detail_data = []
            chr_num_main_data = []
            chr_num_detail_data = []
            gene_num_main_data = []
            gene_num_detail_data = []
            sample_id = []
            gene0001_data = []
            print(task_id)
            # genome_size 提取
            genome_main_data = genome_size_main.find({"task_id": task_id})
            for i in genome_main_data:
                genome_size_main_data.append(i)
            if genome_size_main_data:
                genome_detail = genome_size_detail.find({"datastat_id": genome_size_main_data[0]["main_id"]})
                for i in genome_detail:
                    genome_size_detail_data.append(i)
            if len(genome_size_detail_data) == 0:
                size = 0
            else:
                size = 1
            # chr和plas数量提取
            chr_main = chr_num_main.find({"task_id": task_id})
            for i in chr_main:
                chr_num_main_data.append(i)
            chr_detail = chr_num_detail.find({"assemble_id": chr_num_main_data[-1]["main_id"]})
            for i in chr_detail:
                chr_num_detail_data.append(i)
            for i in range(len(chr_num_detail_data)):
                if chr_num_detail_data[i]["specimen_id"] not in sample_id:
                    sample_id.append(chr_num_detail_data[i]["specimen_id"])
            # gene 数量提取
            gene_main = gene_num_main.find({"task_id": task_id})
            for i in gene_main:
                gene_num_main_data.append(i)
            gene_detail_data = gene_num_detail.find({"predict_id": gene_num_main_data[-1]["main_id"]})
            for i in gene_detail_data:
                gene_num_detail_data.append(i)
            for i in sample_id:
                information = []
                if size:
                    for a in range(len(genome_size_detail_data)):
                        if genome_size_detail_data[a]["specimen_id"] == i:
                            information.append(i)
                            information.append(str(float(genome_size_detail_data[a]["g_size"])/(1000*1000)))
                            break
                else:
                    information.append(i)
                    information.append("-")
                for b in range(len(chr_num_detail_data)):
                    if chr_num_detail_data[b]["specimen_id"] == i:
                        information.append(chr_num_detail_data[b]["chr_no"])
                        information.append(chr_num_detail_data[b]["pla_no"])
                        break
                for c in range(len(gene_num_detail_data)):
                    if gene_num_detail_data[c]["specimen_id"] == i:
                        information.append(gene_num_detail_data[c]["num"])
                        information.append(gene_num_detail_data[c]["aver_base"])
                        break
                result = list(gene0001_main.find({"task_id": task_id}))
                main_id1 = result[0]['main_id']
                gene0001tmp = list(gene0001_detail.find(
                    {"summary_id": main_id1, "gene_id": "gene0001", "specimen_id": i}))
                if len(gene0001tmp) >0:
                    if gene0001tmp[0]["gene_name"] == "dnaA":
                        information.append("yes")
                    else:
                        information.append("no")
                else:
                    information.append("no")
                if size:
                    if 1 <= float(information[1]) <= 10 and (0 <= int(information[2]) <= 3) and (
                            0 <= int(information[3]) <= 10) and (1000 <= float(information[4]) <= 10000) and (
                            800 <= float(information[5]) <= 1200) and information[6] == "yes":
                        information.append("yes")
                    else:
                        information.append("no")
                else:
                    if (str(information[1]) == "-" and (0 <= int(information[2]) <= 3) and (
                            0 <= int(information[3]) <= 10) and (1000 <= float(information[4]) <= 10000) and (
                            800 <= float(information[5]) <= 1200) and information[6] == "yes"):
                        information.append("yes")
                    else:
                        information.append("no")
                data = {
                    "quality_id": main_id,
                    "specimen_id": information[0],
                    "genome_size": information[1],
                    "chromosome_num": information[2],
                    "plasmid_num": information[3],
                    "cds_num": information[4],
                    "cds_average_len": information[5],
                    "first_gene": information[6],
                    "normal": information[7]
                }
                data_son = SON(data)
                data_list.append(data_son)
        try:
            collection_detail.insert_many(data_list)
            s_collection = self.db['genome_quality']
            s_collection.update({'_id': main_id}, {'$set': {'main_id': main_id, 'status': 'end'}})
        except Exception as e:
            print("error")
            self.bind_object.logger.error("导入genome_quality_detail%s信息出错:%s" % (task_id, e))
        else:
            print("ok")
            self.bind_object.logger.info("导入genome_quality_detail%s信息成功!" % task_id)

if __name__ == "__main__":
    a = GenomeQuality(None)
    #task_id = "majorbio_236401"
    #project_sn = "test01"
    #a.add_genome_quality(main="123",project_sn="234",task_id="tsg_36243",params="123456")
    #a.add_genome_quality_detail("tsg_37312", "uncomplete")
    #a.add_genome_quality_detail("tsg_34765", "complete",main_id= insert_id)


import streamlit as st
import os
import SASM_v4 as sa
import pandas as pd
# from io import BytesIO
from io import StringIO
from file_process import import_bench
from file_process import import_last_bench
from file_process import import_labor_data
from file_process import side_bar
from file_process import import_last_sasm

def main(user, mbt):
    """
    Purpose: Sets the layout of the web app
    Params:  None
    Return:  None
    """

    # Display the header / welcome
    st.set_page_config(page_title='Bench Report Generator', layout="wide")
    st.title("_SIG Analytics Staff Management_")

    ##############################################
    #
    # CREATE CENTER OF PAGE
    #
    ##############################################
    with st.container():
        h1, h2 = st.columns(2, gap="small")
        with h1:
            st.header("**Status Tracker**")
        with h2:
            st.header("**Import Data**")

    # Asks user to select file
    with st.container():

        # Format page to three colums
        col0, col1, col2, col3 = st.columns(4,gap="small")

        # Format col1
        with col1:
            import_bench(user, mbt)
            import_last_sasm(user, mbt)

        # Format col2
        with col2:
            import_last_bench(user,mbt)

        with col3:
            import_labor_data(user,mbt)

    ##############################################
    #
    # CREATE SIDE COLUMN
    #
    ##############################################
        with col0:
            side_bar(user, mbt)

    ##############################################
    #
    # CREATE DISPLAY TABS
    #
    ##############################################
    # Create tabs to display data to user
    tab1, tab2, tab3, tab4, tab5 = st.tabs(["Current Bench", "Last Bench",
                                "Labor Data", "Last SASM Report",
                                "Generated Bench Report"])
    with tab1:
        st.header("Current Bench Data")

        if user.bench_path == "No File Selected":
            st.write(user.bench_path)
        else:
            st.write(mbt.df_bench)

    with tab2:
        st.header("Last Bench Data")

        if user.last_bench == "No File Selected":
            st.write(user.last_bench)
        else:
            st.write(mbt.df_last_bench)

    with tab3:
        st.header("Labor Data")
        if user.labor_path == "No File Selected":
            st.write(user.labor_path)
        else:
            st.write(mbt.df_labor)

    with tab4:
        st.header("Last SASM Report")
        if user.last_sasm == "No File Selected":
            st.write(user.last_sasm)
        else:
            st.write(mbt.df_last_sasm)

    with tab5:
        st.header("Generated Bench Report")
        if mbt.df_final_report.empty:
            st.write("Upload Date and Generate Report")
        else:
            st.write(mbt.df_final_report)

def file_selector(folder_path='.'):
    filenames = os.listdir(folder_path)
    selected_filename = st.selectbox('Most recent bench.csv file', filenames)

    file = st.file_uploader("Please choose a file")

    return os.path.join(selected_filename)

if __name__ == "__main__":
    # Initialize objects

    if 'user' not in st.session_state:
        st.session_state.user = sa.User_Data()
        user = st.session_state.user
    else:
        user = st.session_state.user

    if 'mbt' not in st.session_state:
        st.session_state.mbt = sa.Billable_Hour_Tracker()
        mbt = st.session_state.mbt
    else:
        mbt = st.session_state.mbt

    main(user, mbt)


from discord.ext import commands
from discord.ext.commands import Cog

import ml
import utils

class Moderate(Cog):
    """
    Automatic moderation
    """

    def __init__(self, bot):
        self.bot = bot

    async def handle_message(self, message):
        if message.author.bot:
            return
        if message.channel.name == self.bot.config['log_channel']:
            return
        content = utils.ascii_filter(utils.clean_message(message.content))
        prediction = await ml.predict(content)  # Get prediction from API
        print(f'{message.content} - {prediction}')
        classification = prediction[0]
        score = prediction[1]
        if classification == 'notok':
            if score > 0.8:  # Delete if score is good
                await message.delete()
            await self.send_log(message, score)
        elif classification == "ok":
            if score < 0.9:
                await self.send_log(message, 0)

    @commands.Cog.listener()
    async def on_message(self, message):
        await self.handle_message(message)

    @commands.Cog.listener()
    async def on_message_edit(self, before, after):
        await self.handle_message(after)

    async def send_log(self, message, score):
        channel = [c for c in message.guild.text_channels if c.name == self.bot.config['log_channel'] and c.permissions_for(message.guild.me).send_messages]
        if channel:
            channel = channel[0]
            if score > 0.8:
                await channel.send(
                    f"\N{LARGE RED CIRCLE} `[{utils.get_time_string()}]` {message.channel.mention} **Harmful message by** `{message.author.name}#{message.author.discriminator} ({message.author.id})` **was deleted:** `{utils.clean_message(message.content)}`"
                )
            else:
                await channel.send(
                    f"\N{LARGE ORANGE DIAMOND} `[{utils.get_time_string()}]` {message.channel.mention} **Potentially harmful message by** `{message.author.name}#{message.author.discriminator} ({message.author.id})` **Content:** `{utils.clean_message(message.content)}`"
                )

def setup(bot):
    bot.add_cog(Moderate(bot))


from __future__ import print_function, division
import os
import torch
from torch import nn,optim
import torch.nn.functional as F
import pandas as pd
import numpy as np
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms, utils

import pretrainedmodels
import torch
import pretrainedmodels.utils as utils

from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import roc_curve,auc
import matplotlib.pyplot as plt
from PIL import Image

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

#----------------model define-----------------
model_name = 'pnasnet5large'
print(pretrainedmodels.pretrained_settings['pnasnet5large'])
model = pretrainedmodels.__dict__[model_name](num_classes=6, pretrained=None)
model.to(device)
print(model)

# model.load_state_dict(torch.load("./checkpoint/pnasnet_100_0.8644578313253012.pth"))
model.load_state_dict(torch.load("./checkpoint/pnasnet_100_0.8644578313253012.pth"))
model.eval()

def get_label(file):
    if "mc" in file:
        label = 0
    elif "sj" in file:
        label = 1
    elif "hj" in file:
        label = 2
    elif "jj" in file:
        label = 3
    elif "zc" in file:
        label = 4
    elif "wz" in file:
        label = 5

    return label

files = os.listdir("./data/test")

real_labels = []
pred_labels = []
pred_probs = []

for file in files:
    real_labels.append(get_label(file))

    path_img = "./data/test/"+file

    img_1 = Image.open(path_img)
    longer_side = max(img_1.size)
    horizontal_padding = (longer_side - img_1.size[0]) / 2
    vertical_padding = (longer_side - img_1.size[1]) / 2
    img_1 = img_1.crop((-horizontal_padding,
        -vertical_padding,
        img_1.size[0] + horizontal_padding,
        img_1.size[1] + vertical_padding))
    img_1 = img_1.resize((331,331),Image.BICUBIC)
    img_2 = transforms.ToTensor()(img_1)
    img_2 = transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))(img_2)

    input_tensor = img_2.unsqueeze(0) # 3x331x331 -> 1x3x331x331
    input_tensor = input_tensor.to(device)

    output_logits = model(input_tensor) # 1x6

    output_prob = F.softmax(output_logits,dim =1).detach().cpu() #对每一行进行softmax
    output_prob =output_prob.numpy() #对每一行进行softmax

    pred_label = np.argmax(output_logits.detach().cpu()).item()
    pred_labels.append(pred_label)
    pred_probs.append(np.max(output_prob))
    print("{} | {} | {}".format(file,pred_label,np.max(output_prob)))

target_names = ["盲肠","升结肠","横结肠","降结肠","直肠","未知"]

print("-----classification_report-----")
print(classification_report(real_labels,pred_labels,target_names=target_names))

print("----confusion_matrix-----")
cm = confusion_matrix(y_true=real_labels,y_pred=pred_labels)
print(cm)

print("------acc---------")
totalPic = np.sum(cm)
for i in range(6):
    print("%10s = %.4f"%(target_names[i]+" acc",(totalPic-np.sum(cm[:,i])-np.sum(cm[i,:])+2*cm[i,i]) / totalPic))

print("----------Sensitivity / Specificity-----------")
print("%10s%15s%15s"%("","Sensitivity","Specificity"))
for i in range(6):
    rsum = np.sum(cm[i,:])
    print("%10s%12.2f\t%12.2f"%(target_names[i],cm[i,i]/rsum,1-(np.sum(cm[:,i])-cm[i,i])/(totalPic-rsum)))

print("------阴/阳性预测值----------")
print("%10s%10s%10s"%("","阳性预测值","阴性预测值"))
for i in range(len(target_names)):
    pN = totalPic - np.sum(cm[:,i])
    print("%10s%12.2f\t%12.2f"%(target_names[i],cm[i,i]/np.sum(cm[:,i]),(pN-np.sum(cm[i,:])+cm[i,i])/pN))


from datetime import datetime
import heapq
import itertools
import math
import operator
import string
import sys
import typing as tp
from abc import abstractmethod, ABC
from math import radians
from math import log, sin, cos, radians, acos

from dateutil import parser as dateutil_parser

TRow = tp.Dict[str, tp.Any]
TRowsIterable = tp.Iterable[TRow]
TRowsGenerator = tp.Generator[TRow, None, None]

class Operation(ABC):
    @abstractmethod
    def __call__(self, rows: TRowsIterable, *args: tp.Any, **kwargs: tp.Any) -> TRowsGenerator:
        pass

# Operations

class Mapper(ABC):
    """Base class for mappers"""

    @abstractmethod
    def __call__(self, row: TRow) -> TRowsGenerator:
        """
        :param row: one table row
        """
        pass

class Map(Operation):
    """Map operation"""

    def __init__(self, mapper: Mapper) -> None:
        self.mapper = mapper

    def __call__(self, rows: TRowsIterable, *args: tp.Any, **kwargs: tp.Any) -> TRowsGenerator:
        """
        :param rows: iterator over TRow
        :return: Generator of mapped TRow
        """
        for row in rows:
            yield from self.mapper(row)

class Reducer(ABC):
    """Base class for reducers"""

    @abstractmethod
    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        """
        :param rows: table rows
        """
        pass

class Reduce(Operation):
    """Reduce operation"""

    def __init__(self, reducer: Reducer, keys: tp.Tuple[str, ...]) -> None:
        self.reducer = reducer
        self.keys = keys

    def __call__(self, rows: TRowsIterable, *args: tp.Any, **kwargs: tp.Any) -> TRowsGenerator:
        """
        :param rows: iterator over TRow, sorted by self.keys
        :return: reduced groups of TRow
        """

        def get_value(row: TRow) -> tp.Optional[tp.Iterable[str]]:
            """get values in TRow pointed by key"""
            return [row[key] for key in self.keys]

        for _, g in itertools.groupby(rows, get_value):
            yield from self.reducer(self.keys, g)

class Joiner(ABC):
    """Base class for joiners"""

    def __init__(self, suffix_a: str = "_1", suffix_b: str = "_2") -> None:
        self._a_suffix = suffix_a
        self._b_suffix = suffix_b

    @abstractmethod
    def __call__(
            self, keys: tp.Sequence[str], rows_a: TRowsIterable, rows_b: TRowsIterable
    ) -> TRowsGenerator:
        """
        :param keys: join keys
        :param rows_a: left table rows
        :param rows_b: right table rows
        """
        pass

    def _inner_join(
            self, keys: tp.Sequence[str], rows_a: TRowsIterable, rows_b: TRowsIterable
    ) -> TRowsIterable:
        rows_b_cp = list(rows_b)
        suffix_a, suffix_b = None, None

        for row_a in rows_a:
            for row_b in rows_b_cp:

                if suffix_a is None:
                    suffix_a = {k for k in row_a.keys() if k in row_b and k not in keys}

                if suffix_b is None:
                    suffix_b = {k for k in row_b.keys() if k in row_a and k not in keys}

                res = {
                          k + self._a_suffix if k in suffix_a else k: row_a[k]
                          for k in row_a.keys()
                      } | {
                          k + self._b_suffix if k in suffix_b else k: row_b[k]
                          for k in row_b.keys()
                      }

                yield res

class Join(Operation):
    def __init__(self, joiner: Joiner, keys: tp.Sequence[str]):
        self.keys = keys
        self.joiner = joiner

    def __call__(
            self, rows: TRowsIterable, *args: tp.Any, **kwargs: tp.Any
    ) -> TRowsGenerator:
        def grouper(row):  # type: ignore
            return tuple(row[key] for key in self.keys)

        def take_next_from_iterable(iterable_obj: tp.Iterator[tp.Any]) -> tp.Any:
            try:
                return next(iterable_obj)
            except StopIteration:
                return None, None

        rows_a, rows_b = rows, args[0]

        groups_a = itertools.groupby(rows_a, key=grouper)
        groups_b = itertools.groupby(rows_b, key=grouper)

        need_take_a = True
        need_take_b = True

        key_a, group_a = None, None
        key_b, group_b = None, None

        while True:
            if need_take_a:
                key_a, group_a = take_next_from_iterable(groups_a)
            if need_take_b:
                key_b, group_b = take_next_from_iterable(groups_b)

            if group_b is None and group_a is None:
                break

            if key_a == key_b:
                need_take_a = True
                need_take_b = True

                yield from self.joiner(self.keys, group_a or [], group_b or [])
            # elif key_a is None or (key_b is not None and key_b < key_a):
            elif key_a is not None and (key_b is None or key_b >= key_a):
                need_take_a = True
                need_take_b = False
                yield from self.joiner(self.keys, group_a or [], [])
            else:
                need_take_a = False
                need_take_b = True
                yield from self.joiner(self.keys, [], group_b or [])

# Dummy operators

class DummyMapper(Mapper):
    """Yield exactly the row passed"""

    def __call__(self, row: TRow) -> TRowsGenerator:
        yield row

class FirstReducer(Reducer):
    """Yield only first row from passed ones"""

    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        first_row: tp.Union[TRow, None] = None
        for row in rows:
            if first_row is None:
                first_row = row
        assert first_row is not None
        yield first_row

# Mappers

class FilterPunctuation(Mapper):
    """Left only non-punctuation symbols"""

    def __init__(self, column: str):
        """
        :param column: name of column to process
        """
        self.column = column

    def __call__(self, row: TRow) -> TRowsGenerator:
        row[self.column] = row[self.column].translate(str.maketrans('', '', string.punctuation))
        yield row

class LowerCase(Mapper):
    """Replace column value with value in lower case"""

    def __init__(self, column: str):
        """
        :param column: name of column to process
        """
        self.column = column

    def __call__(self, row: TRow) -> TRowsGenerator:
        row[self.column] = row[self.column].lower()
        yield row

class Split(Mapper):
    """Split row on multiple rows by separator"""

    def __init__(self, column: str, separator: tp.Optional[str] = None) -> None:
        """
        :param column: name of column to split
        :param separator: string to separate by
        """
        self.column = column
        self.separator = separator

    def __call__(self, row: TRow) -> TRowsGenerator:
        for part in row[self.column].split(self.separator):
            initial_row = row.copy()
            initial_row[self.column] = part
            yield initial_row

class Product(Mapper):
    """Calculates product of multiple columns"""

    def __init__(self, columns: tp.Sequence[str], result_column: str = 'product') -> None:
        """
        :param columns: column names to product
        :param result_column: column name to save product in
        """
        self.columns = columns
        self.result_column = result_column

    def __call__(self, row: TRow) -> TRowsGenerator:
        res = 1
        for col in self.columns:
            res *= row[col]
        row[self.result_column] = res
        yield row

class Filter(Mapper):
    """Remove records that don't satisfy some condition"""

    def __init__(self, condition: tp.Callable[[TRow], bool]) -> None:
        """
        :param condition: if condition is not true - remove record
        """
        self.condition = condition

    def __call__(self, row: TRow) -> TRowsGenerator:
        if self.condition(row):
            yield row

class Project(Mapper):
    """Leave only mentioned columns"""

    def __init__(self, columns: tp.Sequence[str]) -> None:
        """
        :param columns: names of columns
        """
        self.columns = columns

    def __call__(self, row: TRow) -> TRowsGenerator:
        yield {key: row[key] for key in self.columns}

class Devide_mapper(Mapper):
    """Divide value from one column to number"""

    def __init__(self, numerator: str, denominator: str, result_column: str) -> None:
        self.numerator = numerator
        self.denominator = denominator
        self.result_column = result_column

    def __call__(self, row: TRow) -> TRowsGenerator:
        new_row = row.copy()
        new_row[self.result_column] = row[self.numerator] / row[self.denominator]
        yield new_row

class Idf(Mapper):
    """Count idf metrics based on number of docs and \
            number of docs containing the word"""

    def __init__(self, doc_count: str, num_word_entries: str, text_column: str, result_column: str) -> None:
        """
        :param doc_count: name of doc_count column
        :param num_word_entries: name of word entries number column
        :param text_column: name of column with word
        :param result_colum: name of column for idf
        """
        self.doc_count = doc_count
        self.num_word_entries = num_word_entries
        self.text_column = text_column
        self.result_column = result_column

    def __call__(self, row: TRow) -> TRowsGenerator:
        total_doc = row[self.doc_count]
        entries_count = row[self.num_word_entries]
        word = row[self.text_column]

        result = dict()
        result[self.text_column] = word
        result[self.result_column] = math.log(total_doc / entries_count)
        yield result

class Pmi(Mapper):
    """Count pmi metrics based on frequency of word \
            in docs and total"""

    def __init__(self, doc_freq: str, total_freq: str, result_column: str) -> None:
        """
        :param doc_freq: name of doc frequency column
        :param total_freq: name of total frequency column
        :param result_colum: name of column for pmi
        """
        self.doc_freq = doc_freq
        self.total_freq = total_freq
        self.result_column = result_column

    def __call__(self, row: TRow) -> TRowsGenerator:
        doc_freq = row[self.doc_freq]
        total_freq = row[self.total_freq]
        row[self.result_column] = math.log(doc_freq / total_freq)
        yield row

class Len_mapper(Mapper):
    """Calc len among two points"""

    def __init__(self, start: str, stop: str, len: str) -> None:
        self.start = start
        self.stop = stop
        self.len = len

    def __call__(self, row: TRow) -> TRowsGenerator:
        row_new = row.copy()
        start_ = row[self.start]
        stop_ = row[self.stop]
        d_ = 6371. * acos(
            sin(radians(start_[1])) * sin(radians(stop_[1])) + cos(radians(start_[1])) * cos(radians(stop_[1]))
            * (cos(radians(stop_[0]) - radians(start_[0]))))
        row_new[self.len] = d_
        yield row_new

class Week_Day_Hour(Mapper):
    """Day of the week and hour from Date"""

    def __init__(self, date_col: str, day_of_week: str, hour_col: str):
        self.date_col = date_col
        self.day_of_week = day_of_week
        self.hour_col = hour_col

    def __call__(self, row: TRow) -> TRowsGenerator:
        row_new = row.copy()
        if "." in row[self.date_col]:
            row_new[self.day_of_week] = datetime.strptime(row[self.date_col], "%Y%m%dT%H%M%S.%f").strftime("%A")[:3]
            row_new[self.hour_col] = datetime.strptime(row[self.date_col], "%Y%m%dT%H%M%S.%f").hour
        else:
            row_new[self.day_of_week] = datetime.strptime(row[self.date_col], "%Y%m%dT%H%M%S").strftime("%A")[:3]
            row_new[self.hour_col] = datetime.strptime(row[self.date_col], "%Y%m%dT%H%M%S").hour

        yield row_new

class Time_diff(Mapper):
    """Calc time-delta between two dates"""

    def __init__(self, start_date: str, end_date: str, time_delta: str):
        self.start_date = start_date
        self.end_date = end_date
        self.time_delta = time_delta

    def __call__(self, row: TRow) -> TRowsGenerator:
        if "." in row[self.start_date]:
            time1 = datetime.strptime(row[self.start_date], "%Y%m%dT%H%M%S.%f")
        else:
            time1 = datetime.strptime(row[self.start_date], "%Y%m%dT%H%M%S")
        if "." in row[self.end_date]:
            time2 = datetime.strptime(row[self.end_date], "%Y%m%dT%H%M%S.%f")
        else:
            time2 = datetime.strptime(row[self.end_date], "%Y%m%dT%H%M%S")

        row_new = row.copy()
        d_ = (time2 - time1).total_seconds() / 3600
        row_new[self.time_delta] = d_
        yield row_new

class ProcessLength(Mapper):
    """Get edge length"""

    def __init__(self, start_coord_column: str, end_coord_column: str, length_column: str) -> None:
        """
        :param start_coord_column: name of start column
        :param end_coord_column: name of end column
        :param length_column: name of column for length
        """
        self.start = start_coord_column
        self.end = end_coord_column
        self.length = length_column

    def __call__(self, row: TRow) -> TRowsGenerator:
        l1, f1 = row[self.start]
        l2, f2 = row[self.end]
        l1 = radians(l1)
        l2 = radians(l2)
        f1 = radians(f1)
        f2 = radians(f2)
        row[self.length] = 6371 * 2 * math.asin(math.sqrt(math.sin(f2 / 2 - f1 / 2) * math.sin(f2 / 2 - f1 / 2) +
                                                          math.cos(f1) * math.cos(f2) * math.sin(l2 / 2 - l1 / 2) *
                                                          math.sin(l2 / 2 - l1 / 2)))
        yield row

class ProcessTime(Mapper):
    """Get edge length"""

    def __init__(self, enter_time_column: str, leave_time_column: str, time_column: str, weekday_column: str,
                 hour_column: str) -> None:
        """
        :param enter_time_column: name of enter time column
        :param leave_time_column: name of leave time column
        :param time_column: name of column for time
        :param weekday_column: name of column for week day
        :param hour_column: name of column for hour
        """
        self.enter = enter_time_column
        self.leave = leave_time_column
        self.time = time_column
        self.day = weekday_column
        self.hour = hour_column

    def __call__(self, row: TRow) -> TRowsGenerator:
        date1 = dateutil_parser.parse(row[self.enter])
        date2 = dateutil_parser.parse(row[self.leave])

        row[self.day] = date1.strftime('%a')
        row[self.hour] = date1.hour
        row[self.time] = (date2 - date1).total_seconds()
        yield row

class ProcessSpeed(Mapper):
    """Get speed based on length and time"""

    def __init__(self, length_column: str, time_column: str, speed_column: str) -> None:
        """
        :param length_column: column for total length
        :param time_column: column for total time
        :param speed_column: column for redult speed
        """
        self.length = length_column
        self.time = time_column
        self.speed = speed_column

    def __call__(self, row: TRow) -> TRowsGenerator:
        row[self.speed] = row[self.length] / row[self.time] * 3600
        yield row

# Reducers

class TopN(Reducer):
    """Calculate top N by value"""

    def __init__(self, column: str, n: int) -> None:
        """
        :param column: column name to get top by
        :param n: number of top values to extract
        """
        self.column_max = column
        self.n = n

    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        for row in heapq.nlargest(self.n, rows, key=operator.itemgetter(self.column_max)):
            yield row

class TermFrequency(Reducer):
    """Calculate frequency of values in column"""

    def __init__(self, words_column: str, result_column: str = 'tf') -> None:
        """
        :param words_column: name for column with words
        :param result_column: name for result column
        """
        self.words_column = words_column
        self.result_column = result_column

    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        counts: tp.Dict[tp.Any, int] = dict()
        length = 0
        row_sample = None
        for row in rows:
            if row_sample is None:
                row_sample = row
            word = row[self.words_column]
            if word not in counts:
                counts[word] = 1
            else:
                counts[word] += 1
            length += 1

        assert row_sample is not None
        for word, count in counts.items():
            result: TRow = dict()
            for key in group_key:
                result[key] = row_sample[key]
            result[self.words_column] = word
            result[self.result_column] = count / length
            yield result

class Count(Reducer):
    """Count rows passed and yield single row as a result"""

    def __init__(self, column: str) -> None:
        """
        :param column: name of column to count
        """
        self.column = column

    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        result: TRow = dict()
        row_sample = None
        length = 0
        for row in rows:
            if row_sample is None:
                row_sample = row
            length += 1
        assert row_sample is not None
        for key in group_key:
            result[key] = row_sample[key]
        result[self.column] = length
        yield result

class SafeCount(Reducer):
    """Count rows passed and yield multiple row as a result"""

    def __init__(self, column: str) -> None:
        """
        :param column: name of column to count
        """
        self.column = column

    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        result = dict()
        row_sample = None
        length = 0
        for row in rows:
            if row_sample is None:
                row_sample = row
            length += 1

        assert row_sample is not None
        for key in group_key:
            result[key] = row_sample[key]
        result[self.column] = length
        for i in range(length):
            yield result

class Sum(Reducer):
    """
    Sum values aggregated by key
    Example for key=('a',) and column='b'
        {'a': 1, 'b': 2, 'c': 4}
        {'a': 1, 'b': 3, 'c': 5}
        =>
        {'a': 1, 'b': 5}
    """

    def __init__(self, column: str) -> None:
        """
        :param column: name for sum column
        """
        self.column = column

    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        sum_ = 0
        keys: TRow = {}
        print('lolo', self.column, file=sys.stderr)

        for r in rows:
            sum_ += r[self.column]
            if not keys:
                keys = {k: r[k] for k in group_key}

        yield keys | {self.column: sum_}

class MultipleSum(Reducer):
    """Sum values in columns passed and yield single row as a result"""

    def __init__(self, columns: tp.Iterable[str]) -> None:
        """
        :param column: name of columns to sum
        """
        self.columns = columns

    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        result = dict()
        row_sample = None
        s = dict()

        for col in self.columns:
            s[col] = 0

        for row in rows:
            if row_sample is None:
                row_sample = row
            for col in self.columns:
                s[col] += row[col]

        assert row_sample is not None
        for key in group_key:
            result[key] = row_sample[key]
        for col in self.columns:
            result[col] = s[col]
        yield result

class CalcMean(Reducer):
    """Calc mean of a group"""

    def __init__(self, column: str, mean_val: str) -> None:
        self.column = column
        self.mean_val = mean_val
        self.sum = 0

    def __call__(self, group_key: tp.Tuple[str, ...], rows: TRowsIterable) -> TRowsGenerator:
        self.sum = 0
        num_of_rows = 0
        for row in rows:
            num_of_rows += 1
            self.sum += row[self.column]

        row_new = {key: row[key] for key in row if key in group_key}
        value = self.sum / num_of_rows
        row_new[self.mean_val] = value
        yield row_new

# Joiners

class InnerJoiner(Joiner):
    """Join with inner strategy"""

    def __call__(
            self, keys: tp.Sequence[str], rows_a: TRowsIterable, rows_b: TRowsIterable
    ) -> TRowsGenerator:
        if rows_a or rows_b:
            yield from self._inner_join(keys, rows_a, rows_b)
        else:
            return

class OuterJoiner(Joiner):
    """Join with outer strategy"""

    def __call__(
            self, keys: tp.Sequence[str], rows_a: TRowsIterable, rows_b: TRowsIterable
    ) -> TRowsGenerator:
        rows_b_cp = list(rows_b)
        if not rows_a:
            yield from rows_b_cp
        elif not rows_b_cp:
            yield from rows_a
        else:
            yield from self._inner_join(keys, rows_a, rows_b_cp)

class LeftJoiner(Joiner):
    """Join with left strategy"""

    def __call__(
            self, keys: tp.Sequence[str], rows_a: TRowsIterable, rows_b: TRowsIterable
    ) -> TRowsGenerator:
        rows_b_cp = list(rows_b)
        if not rows_a:
            return
        if not rows_b_cp:
            yield from rows_a
        else:
            yield from self._inner_join(keys, rows_a, rows_b_cp)

class RightJoiner(Joiner):
    """Join with right strategy"""

    def __call__(
            self, keys: tp.Sequence[str], rows_a: TRowsIterable, rows_b: TRowsIterable
    ) -> TRowsGenerator:
        if not rows_b:
            return
        if not rows_a:
            yield from rows_b
        else:
            yield from self._inner_join(keys, rows_a, rows_b)


import pdb
from data import datasets

from data.datasets.evaluation.kitti.kitti_eval import kitti_evaluation
from .kitti_object_eval_python.evaluate import evaluate as _evaluate_python
from .kitti_object_eval_python.evaluate import generate_kitti_3d_detection, check_last_line_break

def evaluate(eval_type, dataset, predictions, output_folder):
    """evaluate dataset using different methods based on dataset type.
    Args:
        eval_type:
        dataset: Dataset object
        predictions(list[BoxList]): each item in the list represents the
            prediction results for one image.
        output_folder: output folder, to save evaluation files or results.
        **kwargs: other args.
    Returns:
        evaluation result
    """
    args = dict(
        eval_type=eval_type,
        dataset=dataset,
        predictions=predictions,
        output_folder=output_folder,

    )
    if isinstance(dataset, datasets.KITTIDataset):
        return kitti_evaluation(**args)
    else:
        dataset_name = dataset.__class__.__name__
        raise NotImplementedError("Unsupported dataset type {}.".format(dataset_name))

def evaluate_python(label_path, result_path, label_split_file, current_class, metric):
    result, ret_dict = _evaluate_python(label_path, result_path, label_split_file, current_class, metric=metric)

    return result, ret_dict

if __name__ == '__main__':
    pass


""" Pyhton API client for Bing Webmaster Tools

Method description: https://msdn.microsoft.com/en-us/library/jj572365.aspx
API Reference: https://msdn.microsoft.com/en-us/library/hh969386.aspx

"""
import httplib2
import json
try:
    import socks
except ImportError:
    socks = None
from urllib import urlencode

__author__    = "Karoly 'Charles' Nagy"
__copyright__ = "Copyright 2015, Karoly Nagy"
__licence__   = "GPL"
__version__	  = "0.0.1"
__contact__   = "dr.karoly.nagy@gmail.com"

API_KEY = 'YOUR-API-KEY'

class BingWebmasterApi(object):

    def __init__(self, api_key=None, timeout=3, proxy=None):
        self.api_key = api_key or API_KEY
        self.endpoint = 'https://www.bing.com/webmaster/api.svc/json/'
        if proxy:
            if socks:
                if not (isinstance(proxy, tuple) or isinstance(proxy, list)):
                    proxy = proxy.replace('http://', '').split(':')
                _proxy_info = httplib2.ProxyInfo(socks.PROXY_TYPE_HTTP, proxy[0], int(proxy[-1]))
            else:
                raise ImportError('You need socks module to use proxy. Please run `pip install PySocks`')
        else:
            _proxy_info = None
        self.h = httplib2.Http("/tmp/.cache", timeout=timeout, proxy_info=_proxy_info)

    def __getattr__(self, item):
        def call(**kwargs):
            kwargs.update({
                'apikey': self.api_key
            })
            _uri = '{endpoint}{function}?{query}'.format(
                endpoint=self.endpoint,
                function=item,
                query=urlencode(kwargs)
            )
            resp, content = self.h.request(_uri, 'GET')
            if not resp.status == 200:
                if resp.status == 404:
                    raise BingMethodNotImplemented('Method does not exists!')
                elif resp.status == 400:
                    _c = json.loads(content)
                    _ec = _c.get('ErrorCode')
                    if _ec == 8:
                        raise BingInvalidParameter('Invalid parameter given to %s function.' % item)
                    elif _ec == 14:
                        raise BingAuthorizationError('Not authorized.')
                raise BingError('Unknown error occured [status code: %d] with response: %s' % (resp.status, content))
            else:
                return json.loads(content)

        return call


'''
Вам дано описание наследования классов в следующем формате.
<имя класса 1> : <имя класса 2> <имя класса 3> ... <имя класса k>
Это означает, что класс 1 отнаследован от класса 2, класса 3, и т. д.
Или эквивалентно записи:
class Class1(Class2, Class3 ... ClassK):
    pass
Класс A является прямым предком класса B, если B отнаследован от A:
class B(A):
    pass

Класс A является предком класса B, если
    A = B;
    A - прямой предок B
    существует такой класс C, что C - прямой предок B и A - предок C

Например:
class B(A):
    pass
class C(B):
    pass
# A -- предок С

Вам необходимо отвечать на запросы, является ли один класс предком другого класса

Важное примечание:
Создавать классы не требуется.
Мы просим вас промоделировать этот процесс, и понять существует ли путь от одного класса до другого.
Формат входных данных
В первой строке входных данных содержится целое число n - число классов.
В следующих n строках содержится описание наследования классов. В i-й строке указано от каких классов наследуется i-й класс. Обратите внимание, что класс может ни от кого не наследоваться. Гарантируется, что класс не наследуется сам от себя (прямо или косвенно), что класс не наследуется явно от одного класса более одного раза.
В следующей строке содержится число q - количество запросов.
В следующих q строках содержится описание запросов в формате <имя класса 1> <имя класса 2>.
Имя класса – строка, состоящая из символов латинского алфавита, длины не более 50.
Формат выходных данных
Для каждого запроса выведите в отдельной строке слово "Yes", если класс 1 является предком класса 2, и "No", если не является.

Sample Input:
4
A
B : A
C : A
D : B C
4
A B
B D
C D
D A
Sample Output:
Yes
Yes
Yes
No'''

import re
from collections import deque

def readGraph():
    graph = {}
    for e in range (int(input())):
        path = re.split('\W+', input().strip())
        value = path.pop(0)
        if value not in graph: graph[value] = []
        for i in path:
            if i not in graph: graph[i] = [value,]
            else:
                if value not in graph[i]: graph[i].append(value)
    return graph

def bfs(graph, start, goal):
    if start == goal: return True
    queue = deque([start])
    parent = {}
    parent[start] = start
    while queue:
        currNode = queue.popleft()
        if currNode in graph:
            for neighbor in graph[currNode]:
                if neighbor == goal: return True
                if neighbor not in parent:
                    parent[neighbor] = currNode
                    queue.append(neighbor)
    return False

graph = readGraph()
#print(graph)

for element in range (int(input())):
    list = input().split()
    start = list[0]
    if len(list) == 2: end = list[1]
    else: end = 'Nan'
    if start in graph and (bfs(graph, start, end) or end == 'Nan'): print('Yes')
    else: print('No')

''' Tests
12
G : F
A
B : A
C : A
D : B C
E : D
F : D
X
Y : X A
Z : X
V : Z Y
W : V
8
A G
A Z
A W
X W
X QWE
A X
X X
1 1
Y
N
Y
Y
N
N
Y
N
{'V': ['W'], 'Y': ['V'], 'A': ['B', 'C', 'Y'], 'B': ['D'], 'F': ['G'], 'C': ['D'], 'Z': ['V'], 'X': ['Y', 'Z'], 'D': ['E', 'F']}

10
classA : classB classC classD classG classH
classB : classC classE classG classH classK classL
classC : classE classD classH classK classL
classE : classD classF classK classL
classD : classG classH
classF : classK
classG : classF
classH : classL
classK : classH classL
classL
38
classK classD
classD classA
classG classD
classH classA
classE classE
classH classG
classE classL
classB classD
classD classL
classD classG
classD classE
classA classF
classA classC
classK classA
classA classH
classK classD
classH classB
classK classB
classD classL
classG classG
classA classH
classK classL
classG classE
classB classA
classC classK
classK classL
classC classL
classG classC
classD classD
classC classG
classE classA
classF classK
classB classG
classH classL
classL classF
classH classG
classD classA
classH classL
{'classE': ['classD', 'classF', 'classK', 'classL'], 'classH': ['classL'], 'classK': ['classH', 'classL'], 'classG': ['classF'], 'classC': ['classE', 'classD', 'classH', 'classK', 'classL'], 'classB': ['classC', 'classE', 'classG', 'classH', 'classK', 'classL'], 'classA': ['classB', 'classC', 'classD', 'classG', 'classH'], 'classF': ['classK'], 'classD': ['classG', 'classH'], 'classL': []}
Yes
Yes
Yes
Yes
Yes
Yes
No
No
No
No
Yes
No
No
Yes
No
Yes
Yes
Yes
No
Yes
No
No
Yes
Yes
No
No
No
Yes
Yes
No
Yes
No
No
No
Yes
Yes
Yes
No

15
G : F
A
B : A
C : A
D : B C
E : D
F : D
X
Y : X A
Z : X
V : Z Y
W : V
Q : P
Q : R
Q : S
9
A G
A Z
A W
X W
X QWE
A X
X X
1 1
Q

Yes
No
Yes
Yes
No
No
Yes
No
Yes
'''


"""Plot the results.
"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.style.use('seaborn')
# https://stackoverflow.com/questions/43080259/
# no-outlines-on-bins-of-matplotlib-histograms-or-seaborn-distplots/43080772
plt.rcParams["patch.force_edgecolor"] = True

import os
import sys
import random
import argparse
import numpy as np
import pickle
import utils
from os.path import join

# Matplotlib stuff
titlesize = 32
xsize = 30
ysize = 30
ticksize = 28
legendsize = 24
er_alpha = 0.25
lw = 3

def plot_losses(args, directory):
    """Plot losses from the saved pickle file."""
    nrows, ncols = 1, 2
    fig, ax = plt.subplots(nrows, ncols, squeeze=False, figsize=(11*ncols, 8*nrows))

    pkl_file = join(directory, 'losses.pkl')
    assert os.path.exists(pkl_file), pkl_file
    with open(pkl_file, 'rb') as fh:
        data = pickle.load(fh)

    xs = data['epoch']
    cumsum = data['tot_train'][-1]
    print(f'Plotting over {len(xs)} epochs, total number of training samples consumed: {cumsum}')
    loss_mse = data['mse_loss']
    loss_kld = data['kld_loss']
    label0 = f'Train MSE; Min: {np.min(loss_mse):0.4f}'
    label1 = f'Train KLD; Min: {np.min(loss_kld):0.3f}'
    ax[0,0].plot(xs, loss_mse, lw=lw, label=label0)
    ax[0,1].plot(xs, loss_kld, lw=lw, label=label1)

    # As of Jan 19 we also have validation.
    if 'valid_mse_loss_rec' in data:
        loss_mse_v = data['valid_mse_loss_rec']
        label_v = f'Valid MSE; Min: {np.min(loss_mse_v):0.4f}'
        ax[0,0].plot(xs, loss_mse_v, lw=lw, label=label_v)

    # Set appropriate y-axis range for MSE.
    eps = 0.0005
    min_y = 0.0 - eps
    max_y = np.max(loss_mse) + eps
    ax[0,0].set_ylim([min_y, max_y])

    # Bells and whistles.
    ax[0,0].set_title(f'MSE Loss', size=titlesize)
    ax[0,1].set_title(f'KL-Div Loss', size=titlesize)
    ax[0,0].set_xlabel('Train Epochs', size=xsize)
    ax[0,1].set_xlabel('Train Epochs', size=xsize)
    ax[0,0].set_ylabel('Loss (MSE)', size=ysize)
    ax[0,1].set_ylabel('Loss (KL Div)', size=ysize)
    for r in range(nrows):
        for c in range(ncols):
            leg = ax[r,c].legend(loc="best", ncol=1, prop={'size':legendsize})
            for legobj in leg.legendHandles:
                legobj.set_linewidth(5.0)
            ax[r,c].tick_params(axis='x', labelsize=ticksize)
            ax[r,c].tick_params(axis='y', labelsize=ticksize)
    plt.tight_layout()

    # Save .png in the same log directory.
    fig_suffix = 'plot_losses.png'.format()
    figname = join(directory, fig_suffix)
    plt.savefig(figname)
    print("Just saved:\n\t{}\n".format(figname))

if __name__ == "__main__":
    # Note: actually path should be one level BEFORE the very long 'model=[...]' phrasing
    # otherwise it'd be a pain to type this in.
    pp = argparse.ArgumentParser()
    pp.add_argument('path', type=str, default='Path to stored file')
    args = pp.parse_args()
    assert os.path.exists(args.path), args.path

    # Let's extract data directories.
    dirs = sorted([join(args.path, x) for x in os.listdir(args.path)])
    print(f'\nNow plotting over {len(dirs)} directories.')
    for d in dirs:
        plot_losses(args, directory=d)


import requests
from bs4 import BeautifulSoup
from requests_ntlm import HttpNtlmAuth
import ssl
import urllib
import re
from fast_app.models import tenure_raw

#### To overcome SSL error
ssl._create_default_https_context = ssl._create_unverified_context

#### request.get doesnt work
#### New link is inserted into the page everyday. To get the latest link,
#### I first find all the link in the page
with urllib.request.urlopen("https://fast.bnm.gov.my/fastweb/public/FastPublicBrowseServlet.do?mode=MAIN&taskId=PB030100") as response:
    html = response.read()
soup = BeautifulSoup(html, 'lxml')
links = []
for link in soup.findAll('a',attrs={'href':re.compile("^Public")}):
    links.append(link.get('href'))
    # print(link.get('href'))
#### The latest updated page always appears to be the first link (Hopefully)
latest_page = links[0]

#### Read the latest html after getting the link
with urllib.request.urlopen("https://fast.bnm.gov.my/fastweb/public/"+ latest_page) as responds:
    html = responds.read()
    # print (html)

soup = BeautifulSoup(html, 'lxml')
tables = soup.find("table")

#### Best filter so far to get the table I want
tables_data = tables.findAll(lambda tag: tag.name =='tr' and tag.has_attr('class'))

tenure_rate = []
for tr in tables_data:
    td = tr.find_all('td')
    row = [tr.text for tr in td]
    tenure_rate.append(row)
print(tenure_rate)

#### Below is the output of tenure_rate
#  ['24/01/20203-yr Reopening of MGS 03/23 3.480%'],
#  ['\n\n'],
#  ['Government Securities (Conventional) New as at 21/01/2020 '],
#  ['BNMN BAND 1', '2.980'],
#  ['BNMN BAND 4', '2.972'],
#  ['BNMN BAND 7', '2.975'],
#  ['BNMN BAND 10', '2.979'],
#  ['MTB BAND 1', '2.980'],
#  ['MTB BAND 4', '2.972'],
#  ['MTB BAND 7', '2.975'],
#  ['MTB BAND 10', '2.979'],
#  ['MGS 1 YEAR', '2.960'],
#  ['MGS 2 YEARS', '3.020'],
#  ['MGS 3 YEARS', '3.042'],
#  ['MGS 4 YEARS', '3.124'],
#  ['MGS 5 YEARS', '3.157'],
#  ['MGS 6 YEARS', '3.216'],
#  ['MGS 7 YEARS', '3.265'],
#  ['MGS 8 YEARS', '3.276'],
#  ['MGS 9 YEARS', '3.293'],
#  ['MGS 10 YEARS', '3.291'],
#  ['MGS 15 YEARS', '3.490'],
#  ['MGS 20 YEARS', '3.593'],
#  ['MGS 30 YEARS', '4.010']]

#### Remove unrelevant items in list
tenure_rate = [items for items in tenure_rate if len(items)>1]
print(tenure_rate)

for each_tenure in tenure_rate:
    new_item = tenure_raw.objects.create(tenure=each_tenure[0],rate=each_tenure[1])