#!/usr/bin/env

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
import os
import overseer
import webbrowser
import string

class App(QApplication):
    def __init__(self, db, battleRoyale, resultsWebpage, roundResultsDriver, roundDescDriver):
        """
        Main application
        """
        QApplication.__init__(self, sys.argv)
        self.db = db
        self.setApplicationName("Battle Royale")
        self.mainWindow = MainWindow(self.db, battleRoyale, resultsWebpage, roundResultsDriver, roundDescDriver)
        self.mainWindow.show()

class MainWindow(QMainWindow):
    def __init__(self, db, battleRoyale, resultsWebpage, roundResultsDriver, roundDescDriver):
        """
        Main Window w/ option to enter a new player and start the battle"
        """

        QMainWindow.__init__(self)
        self.setWindowTitle("Main Menu")

        self.db = db

        self.mainWidget = MainWidget(self.db, battleRoyale, resultsWebpage, roundResultsDriver, roundDescDriver)
        self.setCentralWidget(self.mainWidget)

        self.resize(200, 500)

class MainWidget(QWidget):
    def __init__(self, db, battleRoyale, resultsWebpage, roundResultsDriver, roundDescDriver):
        """
        Lets user manipulate player database and start the battle
        """
        QWidget.__init__(self)

        self.mainLayout = QVBoxLayout(self)

        self.buttonLayout = QVBoxLayout()

        self.appFont = QFont("Arial", 14)

        self.db = db

        self.battleRoyale = battleRoyale

        self.results = resultsWebpage

        self.searchResults = self.db.retrievePlayerInfo()

        self.resultsDriver = roundResultsDriver
        self.descDriver = roundDescDriver

        #Path to find picture
        path = os.path.join(sys.path[0], "public", "images", "shield.png")

        #Welcome label
        self.welcomeLabel = QLabel("Welcome to the Battle Royale!")
        self.welcomeLabel.setFont(QFont("Arial", 20))

        #Button that opens quiz window for player to take
        self.newPlayerPushButton = QPushButton("Create a New Player")
        self.buttonLayout.addWidget(self.newPlayerPushButton)
        self.newPlayerPushButton.clicked.connect(self.showAttributes)

        #Starts battle w/ characters that were entered
        self.startPushButton = QPushButton("Start battle!")
        self.startPushButton.clicked.connect(self.startBattle)

        #Shows table of all players currently in db
        self.viewPushButton = QPushButton("View All Players")
        self.buttonLayout.addWidget(self.viewPushButton)
        self.viewPushButton.clicked.connect(self.showView)

        #Show all players that can be deleted and delete
        self.deletePushButton = QPushButton("Delete Player")
        self.buttonLayout.addWidget(self.deletePushButton)
        self.deletePushButton.clicked.connect(self.deletePlayer)

        #Show all players that can be edited and edit
        self.editPushButton = QPushButton("Edit Player")
        self.buttonLayout.addWidget(self.editPushButton)
        self.editPushButton.clicked.connect(self.editPlayer)

        #Opens webpage
        self.openResultsPushButton = QPushButton("View Scoreboard")
        self.buttonLayout.addWidget(self.openResultsPushButton)
        self.openResultsPushButton.clicked.connect(self.openWebBrowser)
        self.openResultsPushButton.setEnabled(False)

        #Shows events of past battle
        self.historyPushButton = QPushButton("View Past Battle History")
        self.buttonLayout.addWidget(self.historyPushButton)
        self.historyPushButton.clicked.connect(self.showHistory)

        #Creating menu bar
        self.menuBar = QMenuBar()

        #Creating player menu
        self.playerMenu=QMenu("Player")
        self.menuBar.addMenu(self.playerMenu)

        #Create new player option
        self.newPlayerAction = QAction("New...", self.playerMenu)
        self.playerMenu.addAction(self.newPlayerAction)
        self.newPlayerAction.triggered.connect(self.showAttributes)

        #Delete player option
        self.deletePlayerAction = QAction("Delete...", self.playerMenu)
        self.playerMenu.addAction(self.deletePlayerAction)
        self.deletePlayerAction.triggered.connect(self.deletePlayer)

        #View all players option
        self.viewPlayersAction = QAction("View...", self.playerMenu)
        self.playerMenu.addAction(self.viewPlayersAction)
        self.viewPlayersAction.triggered.connect(self.showView)

        #Creating edit players option
        self.editPlayersAction = QAction("Edit...", self.playerMenu)
        self.playerMenu.addAction(self.editPlayersAction)
        self.editPlayersAction.triggered.connect(self.editPlayer)

        #Creatings results menu
        self.resultsMenu = QMenu("Results")
        self.menuBar.addMenu(self.resultsMenu)

        #Creating view home option
        self.homeAction = QAction("Help...", self.resultsMenu)
        self.resultsMenu.addAction(self.homeAction)
        self.homeAction.triggered.connect(self.openHomePage)

        #Creating view scoreboad option
        self.scoreboardAction = QAction("View scoreboard...", self.resultsMenu)
        self.resultsMenu.addAction(self.scoreboardAction)
        self.scoreboardAction.triggered.connect(self.openWebBrowser)
        self.scoreboardAction.setEnabled(False)

        #Adding Picture
        self.shieldLabel = QLabel()
        self.shieldPixmap = QPixmap(path)
        self.shieldLabel.setPixmap(self.shieldPixmap)

        self.mainLayout.addWidget(self.menuBar)
        self.mainLayout.addWidget(self.welcomeLabel, alignment=Qt.AlignCenter)
        self.mainLayout.addStretch(1)
        self.mainLayout.addLayout(self.buttonLayout)
        self.mainLayout.addStretch(1)
        self.mainLayout.addWidget(self.shieldLabel, alignment = Qt.AlignCenter)
        self.mainLayout.addWidget(self.startPushButton)

    def showHistory(self):
        """
        Opens dialog window that lets user enter a battle id and get results from that battle
        """

        self.historyDialog = HistoryDialog(self.db)
        self.historyDialog.exec_()

    def editPlayer(self):
        """
        Opens dialog window that lets user select player to edit
        """
        self.editDialog = EditDialog(self.searchResults, self.db,)
        self.editDialog.exec_()

    def openHomePage(self):
        """
        Opens the home page of the results webpage
        """

        self.results.showHomePage()
    def openWebBrowser(self):
        """
        Opens results of each round and the battle in a webpage
        """
        self.results.showScoreboard()

    def showAttributes(self):
        """
        Opens dialog window that lets user select attributes for their player
        """
        self.attributeDialog = AttributeDialog(self.db)
        self.attributeDialog.exec_()

    def showView(self):
        """
        Opens dialog window that displays all players in the database
        """
        self.searchResults = self.db.retrievePlayerInfo()

        self.viewDialog = ViewDialog(self.searchResults)
        self.viewDialog.exec_()

    def deletePlayer(self):
        """
        Opens dialog window that lets user select player(s) to delete
        """
        self.searchResults = self.db.retrievePlayerInfo()

        self.deleteDialog = DeleteDialog(self.searchResults, self.db)
        self.deleteDialog.exec_()

    def startBattle(self):
        """
        Opens dialog window that lets user view players before starting battle
        """
        self.startDialog = StartDialog(self.searchResults, self.db, self.battleRoyale, self.results, self.resultsDriver, self.descDriver, self.openResultsPushButton, self.scoreboardAction)
        self.startDialog.exec_()

class ResponseLabel(QLabel):
    def __init__(self, text, layout, row):
        """
        Custom class for labels that go w/ attribute combo box
        """
        QLabel.__init__(self, text)
        self.baseLayout=layout
        self.row=row
        self.column=0

        self.baseLayout.addWidget(self, self.row, self.column)

class QuestionLabel(QLabel):
    def __init__(self, text, layout, row):
        """
        Custom class for labeled descriptions
        """
        QLabel.__init__(self, text)
        self.setFont(QFont("Arial", 14))
        self.baseLayout=layout
        self.row=row
        self.column=0
        #self.scrollArea = scroll
        self.baseLayout.addWidget(self, self.row, self.column)
        #self.scrollArea.setWidget(self)

class NumberComboBox(QComboBox):
    def __init__(self, layout, row,):
        """
        Custom class for combo boxes used to select attributes
        """
        QComboBox.__init__(self)

        self.baseLayout = layout
        self.numberData =  []

        self.addItem("0", self.numberData)
        self.addItem("1", self.numberData)
        self.addItem("2", self.numberData)
        self.addItem("3", self.numberData)
        self.addItem("4", self.numberData)
        self.addItem("5", self.numberData)
        self.addItem("6", self.numberData)
        self.addItem("7", self.numberData)
        self.addItem("8", self.numberData)
        self.addItem("9", self.numberData)
        self.addItem("10", self.numberData)
        self.addItem("11", self.numberData)
        self.addItem("12", self.numberData)
        self.addItem("13", self.numberData)
        self.addItem("14", self.numberData)
        self.addItem("15", self.numberData)
        self.addItem("16", self.numberData)
        self.addItem("17", self.numberData)
        self.addItem("18", self.numberData)
        self.addItem("19", self.numberData)
        self.addItem("20", self.numberData)

        self.row=row
        self.column=1

        self.baseLayout.addWidget(self, self.row, 1)

    def currentRating(self):
        return self.currentIndex()

class ViewKillDetailsDialog(QDialog):
    def __init__(self, db, historyID, roundNumber, buttonList):
        """
        Shows the details of a battle (what specific events occured)
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)
        self.detailsLayout = QVBoxLayout()

        self.db = db
        self.historyID = historyID
        self.buttonList = buttonList

        self.scrollWidget = QWidget()
        self.scrollLayout = QVBoxLayout(self.scrollWidget)

        self.scrollArea = QScrollArea()

        self.scrollArea.setWidget(self.scrollWidget)
        self.scrollArea.setWidgetResizable(True)

        self.infoLabel = QLabel("Round " + roundNumber  + " from Battle " + str(historyID))
        self.infoLabel.setFont(QFont("Arial", 16))

        self.moreInfoLabel = QLabel("Close Window to Select Another Round\n")
        self.moreInfoLabel.setFont(QFont("Arial", 14))

        self.detailsLayout.addWidget(self.infoLabel, alignment = Qt.AlignCenter)
        self.detailsLayout.addWidget(self.moreInfoLabel, alignment=Qt.AlignCenter)

        self.history = self.db.retrieveSingleRoundInfo(historyID, int(roundNumber))

        for event in self.history:
            self.eventLabel = QLabel(event[0])
            self.detailsLayout.addWidget(self.eventLabel)

        self.roundLabel = QLabel(roundNumber)

        self.closePushButton = QPushButton("Close")
        self.closePushButton.clicked.connect(self.close)

        #Adding scrolls layout to the dialog window
        self.scrollLayout.addLayout(self.detailsLayout)
        self.mainLayout.addWidget(self.scrollArea)

        self.mainLayout.addWidget(self.closePushButton)

    def close(self):
        self.accept()

class ViewKillHistoryDialog(QDialog):
    def __init__(self, db, historyID):
        """
        Shows record of who killed who during a given battle
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)
        self.killLayout = QVBoxLayout()

        self.scrollWidget = QWidget()
        self.scrollLayout = QVBoxLayout(self.scrollWidget)

        self.scrollArea = QScrollArea()

        self.scrollArea.setWidget(self.scrollWidget)
        self.scrollArea.setWidgetResizable(True)

        self.letters = list(string.ascii_uppercase)

        self.winner = ""

        self.db = db
        self.historyID = historyID

        self.trackRound = 1

        self.infoLabel = QLabel("Click the buttons for more details\nfrom a specific round\n")
        self.infoLabel.setFont(QFont("Arial", 16))
        self.killLayout.addWidget(self.infoLabel)

        self.closePushButton = QPushButton("Close")
        self.closePushButton.clicked.connect(self.close)

        #Getting battle kill and play-by-play info
        self.battleKills = self.db.retrieveKillInfo(self.historyID)

        self.buttonList = []

        self.battleCount = 0

        self.battleLabel = QLabel("Battle A\n")
        self.battleLabel.setFont(QFont("Arial", 18))

        self.killLayout.addWidget(self.battleLabel, alignment=Qt.AlignCenter)

        #Round labels are buttons that can be clicked to provide details about that particular round
        self.roundPushButton = QPushButton("Round " + str(self.trackRound))
        self.roundPushButton.setFont(QFont("Arial", 16))
        self.roundPushButton.clicked.connect(self.seeDetails)
        self.buttonList.append(self.roundPushButton)

        self.killLayout.addWidget(self.roundPushButton, alignment = Qt.AlignCenter)

        #Adding kill information to labels for the kill tab
        for row in self.battleKills:
            #Accounting for round label
            if row[1] != self.trackRound:
                self.trackRound=self.trackRound+1
                self.roundPushButton = QPushButton("Round " + str(self.trackRound))
                self.roundPushButton.setFont(QFont("Arial", 16))
                self.roundPushButton.clicked.connect(self.seeDetails)
                self.buttonList.append(self.roundPushButton)
                self.killLayout.addWidget(self.roundPushButton, alignment = Qt.AlignCenter)

            self.killLabel = QLabel(row[3] + " killed " + row[4] + "\n")
            self.killLayout.addWidget(self.killLabel)

            if self.trackRound%4 == 0:
                self.winner = row[3]
                self.winnerLabel = QLabel("Winner: " + self.winner + "\n")
                self.winnerLabel.setFont(QFont("Arial", 14))
                self.killLayout.addWidget(self.winnerLabel, alignment = Qt.AlignCenter)

            if self.trackRound%4 == 0 and row != self.battleKills[-1]:
                self.battleCount = self.battleCount+1
                self.battleLabel = QLabel("Battle " + self.letters[self.battleCount] + "\n")
                self.battleLabel.setFont(QFont("Arial", 18))
                self.killLayout.addWidget(self.battleLabel, alignment = Qt.AlignCenter)

        #Adding kills to the scroll layout
        self.scrollLayout.addLayout(self.killLayout)

        self.mainLayout.addWidget(self.scrollArea)

        self.mainLayout.addWidget(self.closePushButton)

    def seeDetails(self):
        sender = self.sender()

        self.roundNumber = sender.text().split(" ")[1]
        self.detailsDialog = ViewKillDetailsDialog(self.db, self.historyID, self.roundNumber, self.buttonList)
        self.detailsDialog.exec_()

    def close(self):
        self.accept()

class HistoryDialog(QDialog):
    def __init__(self, db):
        """
        Prompts user to enter a battle ID and show record of kills from that battle
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)
        self.historyLayout = QVBoxLayout()

        self.db = db

        self.historyLabel = QLabel("Enter your Battle ID to view results")
        self.historyLabel.setFont(QFont("Arial, 16"))
        self.historyLayout.addWidget(self.historyLabel)

        self.battleIDLineEdit = QLineEdit("Enter ID")
        self.historyLayout.addWidget(self.battleIDLineEdit)

        self.seeHistoryPushButton = QPushButton("See Battle History")
        self.seeHistoryPushButton.clicked.connect(self.seeHistory)

        self.closePushButton=QPushButton("Close")
        self.closePushButton.clicked.connect(self.close)

        self.mainLayout.addLayout(self.historyLayout)
        self.mainLayout.addWidget(self.seeHistoryPushButton)
        self.mainLayout.addWidget(self.closePushButton)

    def close(self):
        self.accept()

    def seeHistory(self):
        #try:
        self.historyID = int(self.battleIDLineEdit.text())

        if self.db.checkBattleID(self.historyID):
            self.viewKillHistoryDialog  =  ViewKillHistoryDialog(self.db, self.historyID)
            self.viewKillHistoryDialog.exec_()

        elif not self.db.checkBattleID(self.historyID):
            title="Error: Battle ID Doesn't Exist"
            msg="Please Enter a Valid Battle ID and Try Again"

            QMessageBox.information(self, title, msg)
        """
        except:
            errorTitle = "Error: Invalid Input"
            errorMsg = "Please Enter a Valid Battle ID and Try Again"

            QMessageBox.information(self, errorTitle, errorMsg)
        """
class BattleDialog(QDialog):
    def __init__(self, battleRoyale, resultsWebpage, db, resultsDriver, descDriver):
        """
        Dialog box that allows the user to proceed through battle
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)

        self.battleRoyale = battleRoyale

        self.resultsWebpage = resultsWebpage

        self.resultsDriver = resultsDriver

        self.descDriver = descDriver

        self.db = db

        #Path to find picture
        path = os.path.join(sys.path[0], "public", "images", "shield.png")

        self.welcomeLabel = QLabel("Battle has started! Click on the button below to advance to the next round.\nResults can be viewed in real time in the two open windows.")
        self.battleIDLabel = QLabel("\nYour Battle ID is %s" % battleRoyale.battleID)
        self.welcomeLabel.setFont(QFont("Arial", 16))
        self.battleIDLabel.setFont(QFont("Arial", 16))
        self.mainLayout.addWidget(self.welcomeLabel, alignment = Qt.AlignCenter)
        self.mainLayout.addWidget(self.battleIDLabel, alignment = Qt.AlignCenter)

        #Generates matchups using player database
        self.match, self.playersRemaining = self.battleRoyale.createMatch()

        #Button to advance to the next round -> prompts app to read from db
        self.nextRoundPushButton = QPushButton("Next Round")
        self.nextRoundPushButton.clicked.connect(self.nextRound)
        self.mainLayout.addWidget(self.nextRoundPushButton)

        #Shield pic
        self.shieldLabel = QLabel()
        self.shieldLabel.resize(50, 50)
        self.shieldPixmap = QPixmap(path)
        self.shieldPixmap.scaledToHeight(50)
        self.shieldPixmap.scaledToWidth(50)
        self.shieldLabel.setPixmap(self.shieldPixmap)
        self.mainLayout.addWidget(self.shieldLabel, alignment=Qt.AlignCenter)

        self.closePushButton = QPushButton("Close")
        self.closePushButton.clicked.connect(self.close)
        self.mainLayout.addWidget(self.closePushButton)

    def nextRound(self):
        """
        Retrieves information as the database is updated with results from a given round
        """
        #Each time the button is hit, a new round is created
        self.battleRoyale.roundCounter=self.battleRoyale.roundCounter+1

        #Players are eliminated until there is one winner
        self.playersRemaining = self.match.battleCycle(self.battleRoyale.roundCounter)

        #DB change prompts webpage to refresh
        self.match.addRoundResultsDB()
        self.resultsWebpage.advance()

        #Winner is reached
        if self.playersRemaining == 1:
            #Adds kill counts to db in order to update the scoreboard
            self.match.addBattleResultsDB()
            self.nextRoundPushButton.setEnabled(False)

            title="Battle Complete!"
            msg="Close this window to start another battle"

            QMessageBox.information(self, title, msg)

    def close(self):
        self.accept()

class StartDialog(QDialog):
    def __init__(self, results, db, battleRoyale, resultsWebpage, resultsDriver, descDriver, openButton, scoreboardAction):
        """
        Dialog box that shows user players before being able to start battle
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)

        self.startPushButton  = QPushButton("Start")
        self.closePushButton = QPushButton("Close")

        #Player information retrieved from db
        self.results = results

        self.db = db

        self.battleRoyale = battleRoyale

        #Instance of the webpage
        self.resultsWebpage = resultsWebpage

        #Display general results
        self.resultsDriver = resultsDriver

        #Displays descriptive results
        self.descDriver = descDriver

        #Button that opens the scoreboard from the main widget
        self.openButton = openButton

        #Menu action that opsn the same scoreborad as the openButton
        self.scoreboardAction = scoreboardAction

        #Creating Label
        self.questionLabel = QLabel("Start Battle With Following Players?")
        self.mainLayout.addWidget(self.questionLabel)

        #Creating table with player information displayed
        self.playerTable = QTableWidget()

        self.playerTable.setColumnCount(5)
        self.playerTable.setHorizontalHeaderLabels(["Name", "Strength", "Charisma", "Intelligence", "Dexterity"])

        self.playerTable.setRowCount(len(self.results))

        self.mainLayout.addWidget(self.playerTable)

        rowCount=0

        #Creating table
        for row in self.results:
            colCount=0
            for col in row:
                #Do not want to include pid in the table, want each row to start with the player name
                if colCount==0 and type(col) is not str :
                    continue

                self.cellWidget=QTableWidgetItem()
                self.cellWidget.setText(str(col))
                self.playerTable.setItem(rowCount, colCount, self.cellWidget)

                colCount=colCount+1

                #Reached end of row
                if colCount==6:
                    rowCount=rowCount+1
                    break

        self.startPushButton.clicked.connect(self.start)
        self.closePushButton.clicked.connect(self.close)
        self.mainLayout.addWidget(self.startPushButton, alignment=Qt.AlignCenter)
        self.mainLayout.addWidget(self.closePushButton, alignment = Qt.AlignCenter)

    def close(self):
        self.accept()

    def start(self):
        """
        Starts match
        """
        #Retrieves the webpage
        self.resultsDriver.get("http://127.0.0.1:8080/roundResults")
        self.descDriver.get("http://127.0.0.1:8080/roundDescriptions")

        #Dialog window for user to advance the battle
        self.battleDialog = BattleDialog(self.battleRoyale, self.resultsWebpage, self.db, self.resultsDriver, self.descDriver)
        self.battleDialog.exec_()
        self.openButton.setEnabled(True)

        #Now that page is initialized, scoreboard can be viewed
        self.scoreboardAction.setEnabled(True)
        self.accept()

class PlayerAttDialog(QDialog):
    def __init__(self, selectedPlayer, db):
        """
        Shows player attributes and allows for edits to be made
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)
        self.editLayout = QVBoxLayout()

        self.selectedPlayerPID = selectedPlayer

        self.db = db

        self.playerInfo = self.db.retrieveSinglePlayerInfo(self.selectedPlayerPID)

        self.genLabel = QLabel("Edit Attributes")
        self.genLabel.setFont(QFont("Arial", 16))
        self.editLayout.addWidget(self.genLabel)

        #Edit Name
        self.nameLabel = QLabel("Name")
        self.nameLineEdit = QLineEdit(self.playerInfo[1])
        self.editLayout.addWidget(self.nameLabel)
        self.editLayout.addWidget(self.nameLineEdit)

        #Edit Strength
        self.strengthLabel = QLabel("Strength")
        self.strengthLineEdit = QLineEdit(str(self.playerInfo[2]))
        self.editLayout.addWidget(self.strengthLabel)
        self.editLayout.addWidget(self.strengthLineEdit)

        #Edit Charisma
        self.charismaLabel = QLabel("Charisma")
        self.charismaLineEdit = QLineEdit(str(self.playerInfo[3]))
        self.editLayout.addWidget(self.charismaLabel)
        self.editLayout.addWidget(self.charismaLineEdit)

        #Edit Intelligence
        self.intelligenceLabel = QLabel("Intelligence")
        self.intelligenceLineEdit = QLineEdit(str(self.playerInfo[4]))
        self.editLayout.addWidget(self.intelligenceLabel)
        self.editLayout.addWidget(self.intelligenceLineEdit)

        #Edit Dexterity
        self.dexterityLabel = QLabel("Dexterity")
        self.dexterityLineEdit  = QLineEdit(str(self.playerInfo[5]))
        self.editLayout.addWidget(self.dexterityLabel)
        self.editLayout.addWidget(self.dexterityLineEdit)

        self.saveEditsPushButton = QPushButton("Save Changes")
        self.saveEditsPushButton.clicked.connect(self.saveEdits)
        self.saveEditsPushButton.clicked.connect(self.close)

        self.closePushButton = QPushButton("Close")
        self.closePushButton.clicked.connect(self.close)

        self.mainLayout.addLayout(self.editLayout)
        self.mainLayout.addWidget(self.saveEditsPushButton)
        self.mainLayout.addWidget(self.closePushButton)

    def close(self):
        self.accept()

    def saveEdits(self):
        title="Save Changes"
        msg="Save current changes?"

        reply=QMessageBox.question(self, title, msg)

        attList=[]
        changedValueList=[]

        if reply == QMessageBox.Yes:

            if self.nameLineEdit.isModified():
                changedValue = self.nameLineEdit.text()
                att = "PlayerName"

                attList.append(att)
                changedValueList.append(changedValue)

            if self.strengthLineEdit.isModified():
                changedValue =int(self.strengthLineEdit.text())
                att = "Strength"

                attList.append(att)
                changedValueList.append(changedValue)

            if self.charismaLineEdit.isModified():
                changedValue = int(self.charismaLineEdit.text())
                att = "Charisma"

                attList.append(att)
                changedValueList.append(changedValue)

            if self.intelligenceLineEdit.isModified():
                changedValue = int(self.intelligenceLineEdit.text())
                att= "Intelligence"

                attList.append(att)
                changedValueList.append(changedValue)

            if self.dexterityLineEdit.isModified():
                changedValue = int(self.dexterityLineEdit.text())
                att= "Dexterity"

                attList.append(att)
                changedValueList.append(changedValue)

            totalPoints = int(self.strengthLineEdit.text())+int(self.charismaLineEdit.text())+int(self.intelligenceLineEdit.text())+int(self.dexterityLineEdit.text())

            if totalPoints != 20:
                pointTitle = "Points not allocated correctly"
                msg = "Error: Total points do not equal 20. Please reallocate points."

                QMessageBox.information(self, pointTitle, msg)
            else:
                for index in range(len(attList)):
                    self.db.editPlayer(attList[index], changedValueList[index], self.selectedPlayerPID)

class EditDialog(QDialog):
    def __init__(self, searchResults, db):
        """
        Dialog window for user to select player to edit
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)
        self.selectionLayout = QVBoxLayout()

        self.results = searchResults

        self.db = db

        self.genLabel = QLabel("Select Player You Would Like to Edit")
        self.mainLayout.addWidget(self.genLabel)

        self.closePushButton = QPushButton("Close")

        self.editPushButton=QPushButton("Edit")

        self.boxList = []

        #Creating message box
        for row in self.results:
            checkBoxTxt=""
            checkBoxTxt=str(row[0])+ " " + str(row[1])
            self.checkBox=QCheckBox(checkBoxTxt)
            self.boxList.append(self.checkBox)

        for self.box in self.boxList:
            self.selectionLayout.addWidget(self.box)
            self.box.toggled.connect(self.showAtt)

        self.mainLayout.addLayout(self.selectionLayout)
        #self.mainLayout.addWidget(self.editPushButton, alignment=Qt.AlignCenter)
        self.mainLayout.addWidget(self.closePushButton, alignment=Qt.AlignCenter)

        self.closePushButton.clicked.connect(self.closeMe)
        self.editPushButton.clicked.connect(self.showAtt)
        self.editPushButton.clicked.connect(self.closeMe)

    def closeMe(self):
        self.accept()

    def showAtt(self):
        """
        Show selected players attributes to be edited
        """
        title="Edit Player?"
        name=""

        sender = self.sender()

        if sender.isChecked():
            msg="Edit " + sender.text() + "?"
            reply=QMessageBox.question(self, title, msg)

            if reply == QMessageBox.Yes:
                pid = int(sender.text().split(" ")[0])
                self.playerAttDialog = PlayerAttDialog(pid, self.db)
                self.playerAttDialog.exec_()

class DeleteDialog(QDialog):
    def __init__(self, searchResults, db):
        """
        Dialog window for user to select players to delete
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)
        self.selectionLayout = QVBoxLayout()

        self.results = searchResults

        self.db = db

        self.genLabel=QLabel("Select Player(s) You Would Like to Delete\n")
        self.mainLayout.addWidget(self.genLabel)

        self.closePushButton=QPushButton("Close")

        self.deletePushButton=QPushButton("Delete")

        self.boxList=[]

        #Creating message box
        for row in self.results:
            checkBoxTxt=""
            checkBoxTxt=str(row[0])+ " " + str(row[1])
            self.checkBox=QCheckBox(checkBoxTxt)
            self.boxList.append(self.checkBox)

        for self.box in self.boxList:
            self.selectionLayout.addWidget(self.box)

        self.mainLayout.addLayout(self.selectionLayout)
        self.mainLayout.addWidget(self.deletePushButton, alignment=Qt.AlignCenter)
        self.mainLayout.addWidget(self.closePushButton, alignment=Qt.AlignCenter)

        self.closePushButton.clicked.connect(self.closeMe)
        self.deletePushButton.clicked.connect(self.deleteSelections)

    def closeMe(self):
        self.accept()

    def deleteSelections(self):
        """
        Message Box that allows user to delete the player
        """
        title="Delete Player?"

        for self.box in self.boxList:
            if self.box.isChecked():
                msg="Delete " + self.box.text() +"?"
                reply=QMessageBox.question(self, title, msg)

                if reply == QMessageBox.Yes:
                    pid = int(self.box.text().split(" ")[0])
                    self.db.deletePlayer(pid)
        self.close()

class ViewDialog(QDialog):
    def __init__(self, searchResults):
        """
        Dialog window containing table that shows all players currently in the database
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)
        self.results = searchResults

        self.closePushButton = QPushButton("Close")

        self.playerTable = QTableWidget()

        self.playerTable.setColumnCount(5)
        self.playerTable.setHorizontalHeaderLabels(["Name", "Strength", "Charisma", "Intelligence", "Dexterity"])

        self.playerTable.setRowCount(len(self.results))

        self.mainLayout.addWidget(self.playerTable)

        rowCount=0

        #Creating table
        for row in self.results:
            colCount=0
            for col in row:
                if colCount==0 and type(col) is not str :
                    continue

                self.cellWidget=QTableWidgetItem()
                self.cellWidget.setText(str(col))
                self.playerTable.setItem(rowCount, colCount, self.cellWidget)

                colCount=colCount+1

                #Reached end of row
                if colCount==6:
                    rowCount=rowCount+1
                    break

        self.closePushButton.clicked.connect(self.close)
        self.mainLayout.addWidget(self.closePushButton, alignment=Qt. AlignCenter)

    def close(self):
        self.accept()

class AttributeDialog(QDialog):
    def __init__(self, db):
        """
        Dialog box that allows user to select attributes for player
        """
        QDialog.__init__(self)
        self.mainLayout = QVBoxLayout(self)
        self.mainLayout.setSpacing(25)

        self.db  = db

        #Layout w/ attributes
        self.attLayout = QGridLayout()
        self.attLayout.setVerticalSpacing(25)

        self.strengthCounter = 0;
        self.charismaCounter= 0;
        self.intelligenceCounter = 0;
        self.dexterityCounter = 0;

        self.appFont = QFont("Arial", 14)

        self.nameLineEdit = QLineEdit("Enter Name");

        self.infoLabel = QLabel("Distribute your attribute points!\nYou are given 20 points to assign to four categories.\n")
        self.infoLabel.setFont(QFont("Arial", 14))

        self.mainLayout.addStretch()

        #######################
        #Strength
        ########################
        #Creating attribute labels
        self.strengthLabel = QuestionLabel("Strength", self.attLayout, 0)

        #Creating Response labels and their combo boxes
        self.r1Label = ResponseLabel("Points", self.attLayout, 1)
        self.numberComboBox1 = NumberComboBox(self.attLayout, 1)

        #######################
        #Charisma
        ########################
        #Creating attribute labels
        self.qTwoLabel = QuestionLabel("Charisma", self.attLayout, 5)

        #Creating Response labels and their combo boxes
        self.r2Label = ResponseLabel("Points", self.attLayout, 6)
        self.numberComboBox2 = NumberComboBox(self.attLayout, 6)

        #######################
        #Intelligence
        ########################
        #Creating attribute labels
        self.qThreeLabel = QuestionLabel("Intelligence", self.attLayout, 12)

        #Creating Response labels and their combo boxes
        self.r3Label = ResponseLabel("Points", self.attLayout, 13)
        self.numberComboBox3 = NumberComboBox(self.attLayout, 13)

        ####################
        #Dexterity
        ####################
        #Creating attribute labels
        self.qFourLabel = QuestionLabel("Dexterity", self.attLayout, 14)

        #Creating Response labels and their combo boxes
        self.r4Label = ResponseLabel("Points", self.attLayout, 15)
        self.numberComboBox4 = NumberComboBox(self.attLayout, 15)

        self.closePushButton = QPushButton("Close")
        self.closePushButton.clicked.connect(self.closeMe)

        self.finishPushButton = QPushButton("Finish and Submit")
        self.finishPushButton.clicked.connect(self.submitResponseAndWrite)
        self.finishPushButton.clicked.connect(self.closeMe)

        self.mainLayout.addWidget(self.infoLabel)
        self.mainLayout.addWidget(self.nameLineEdit)
        self.mainLayout.addLayout(self.attLayout)
        self.mainLayout.addWidget(self.finishPushButton, alignment=Qt.AlignCenter)
        self.mainLayout.addWidget(self.closePushButton, alignment=Qt.AlignCenter)

    def closeMe(self):
        self.accept()

    def submitResponseAndWrite(self):
        """
        Uses attributes to enter new player into the database
        """
        self.name = self.nameLineEdit.text()

        self.strengthCounter+=self.numberComboBox1.currentRating()
        self.charismaCounter+=self.numberComboBox2.currentRating()
        self.intelligenceCounter+=self.numberComboBox3.currentRating()
        self.dexterityCounter+=self.numberComboBox4.currentRating()

        self.totalCounter=int(self.strengthCounter+self.charismaCounter+self.intelligenceCounter+self.dexterityCounter)

        #Checking that only 20 points were allocated among the four attribute categories
        if self.totalCounter != 20:
            msg = "Error: Total points do not equal 20. Please reallocate points.\n"
            title = "Points not allocated correctly"
            QMessageBox.information(self, title, msg)

        #Adds player to the database by writing the information to a file that will then be read by the overseer
        else:
            msg = "Adding %s to player database...\n" % self.name
            title = "Player added!"
            QMessageBox.information(self, title, msg)

            self.db.insertPlayer(self.name, self.strengthCounter, self.charismaCounter, self.intelligenceCounter, self.dexterityCounter)

def main():
    app=App()
    sys.exit(app.exec_())

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


"""
Evaluate and plot time it takes for each GAIA CSV file to be downloaded
and pickled, using inter-.gaiapickle file modification times

Usage:

  ls -lrt --full gaiapickles/ | grep gaiapickle\$ | python timings.py

[ls -lrt --full ...] output looks like this:

  -rw-rw-r-- 1 dad dad 1461685 2020-07-15 08:28:18.243727631 -0400 GaiaSource_3007619637220870144_3007779895338408704.gaiapickle

"""
import os
import sys
import gzip
import numpy
from datetime import datetime,timedelta

### Initialize last file's datetime, list of timedeltas, loop over STDIN
dt,tds = None,list()
for line in sys.stdin:

  ### Move last file's datetime to olddt, split input line into tokens
  olddt = dt
  toks = line.strip().split()

  ### Get year-month-day and hour-minute-second.fraction tokens
  ymd,hmsf = toks[5:7]

  ### Split off fraction seconds, parse time
  hms,f = hmsf.split('.')
  dt = datetime.strptime(' '.join([ymd,hms]),'%Y-%m-%d %H:%M:%S'
       ) + timedelta(microseconds=1e6*float('.{0}'.format(f)))

  if None is olddt: continue

  ### Calculate timedelta, ignore if > 120s, else add to list
  td = (dt - olddt).total_seconds()
  if td > 120: continue
  tds.append(td)

### Convert timedelta list to numpy array
tdsnp = numpy.array(tds)

### Sort for cumulative freq. distribution, calculate mean and median
tds.sort()
L = len(tds)
tdsmean,tdsmedian = tdsnp.mean(),tds[L>>1]

### - Sum number of bytes in CSV GZ files to calculate download bitrate
with gzip.open('csv/csv_gaia_index.html.gz','rt') as fgz:
  allbytes = [int(s.strip().split()[-1]) for s in fgz if 'GaiaSource_' in s]
  nbits = sum(allbytes) << 3
  Mbps = 1e-6 * nbits / (tdsmean * len(allbytes))

print(dict(mean=tdsmean,median=tdsmedian,Mbps=Mbps))

########################################################################
### Start plotting
import matplotlib.pyplot as plt

### figure with two plots, 2 high by 1 wide
fig,(ax0,ax1,) = plt.subplots(2,1)

### Figure title
fig.suptitle('After processing {0} CSVs; {1:.1f}Mbps'.format(L,Mbps))

### Plot raw data
ax0.set_yscale('log')
ax0.set_xlabel('CSV #')
ax0.set_ylabel('s/CSV')
ax0.plot(tdsnp,'k,')

### Plot cumulative frequency distribution
ax1.set_xscale('log')
ax1.set_xlabel('s/CSV')
ax1.set_ylabel('Cumulative CSV count')
ax1.axvline(tdsmean,color='r',label='Mean {0:.1f}s/CSV'.format(tdsmean))
ax1.axvline(tdsmedian,color='g',label='Median {0:.1f}s/CSV'.format(tdsmedian))
ax1.plot(tds,range(L),'k',label='Cum. freq. s/CSV')
ax1.legend(loc='upper left')

### Show plot
plt.show()


from turtle import Screen
from ball import Ball
from brick import Brick
from paddle import Paddle
from scoreboard import Scoreboard
import time

x_positions = range(-450, 510, 80)
y_positions = range(160, 300, 40)
colors = ['yellow', 'blue', 'green', 'red']

screen = Screen()
screen.setup(width = 1000, height = 800)
screen.bgcolor("black")
screen.title("Breakout")
screen.tracer(0)

paddle = Paddle((0, -370))
ball = Ball()
scoreboard = Scoreboard()

screen.listen()
screen.onkeypress(paddle.go_left, "Left")
screen.onkeypress(paddle.go_right, "Right")

bricks = []

i = 0
for y in y_positions:
    for x in x_positions:
        brick = Brick((x, y))
        brick.color(colors[i])
        bricks.append(brick)
    i += 1

game_is_on = True
count = 0
speed = 0.02

while game_is_on:
    time.sleep(speed)
    screen.update()
    ball.move()

    # detect collision with side wall
    if ball.xcor() > 480 or ball.xcor() < -480:
        ball.bounce_from_side_wall()
    # detect collision with top wall
    if ball.ycor() > 380:
        ball.bounce()
    # detect collision with paddle
    if ball.distance(paddle) < 50 and ball.ycor() < -365:
        ball.bounce()

    # detect collision with brick
    for brick in bricks:
        if ball.distance(brick) < 35:
            ball.bounce()
            brick.destroy()
            bricks.remove(brick)

            if brick.color()[0] == 'yellow':
                scoreboard.score += 5

            if brick.color()[0] == 'blue':
                scoreboard.score += 10

            if brick.color()[0] == 'green':
                scoreboard.score += 15

            if brick.color()[0] == 'red':
                scoreboard.score += 20

            scoreboard.update_scoreboard()
    if len(bricks) == 0:
        scoreboard.win_game()
        game_is_on = False

    # game end
    if ball.ycor() < -400:
        scoreboard.game_over()
        game_is_on = False

screen.exitonclick()


#!/usr/bin/python3

import sys
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

# Custom modules
from metrics import MSE
from encoding import OneHotEncoder, categorize
from splitting import train_test_split
from activation_function import sigmoid

np.random.seed(4)

class Perceptron():
    """
        An implementation of a single layer perceptron.
    """

    def __init__(self, in_layer, out_layer, learning_rate=0.01, variant='random'):
        self.model_ran = False
        self.input = in_layer
        self.output = out_layer
        self.lr = learning_rate
        self.epochs = 10
        self.zero_grad()
        self.__initialization(self.input, self.output, variant=variant)

    def __str__(self):
        about_hyperparameters = "Epochs: "+str(self.epochs)+" Learning Rate: "+str(self.lr)
        return "Perceptron("+about_hyperparameters+")"

    def __initialization(self, row, col, variant='he', bias=False):
        # Initializing the weight matrix with random values.

        if variant == 'he':
            self.W = np.random.randn(row, col) * np.sqrt(2 / row)
        elif variant == 'xavier':
            self.W = np.random.randn(row, col) * np.sqrt(1 / row)
        elif variant == 'random':
            self.W = np.random.randn(row, col)
        elif variant == 'zero':
            self.W = np.zeros((row, col))

        if bias:
            self.b = np.random.randn(self.output).reshape((self.output, -1))
        else:
            self.b = np.zeros(self.output).reshape((self.output, -1))

        return

    def __reshape_input(self, vector):
        # Transforms a vector into the required shape

        if isinstance(vector, list):
            vector = np.array(vector)

        vector_in = vector.reshape((self.input, -1))
        return vector_in

    def __reshape_output(self, vector):
        # Transforms a vector into the required shape

        if isinstance(vector, list):
            vector = np.array(vector)

        vector_in = vector.reshape((self.output, -1))
        return vector_in

    def forward(self, x):
        """
        Forward pass in the Neural network

        Parameters
        ----------
        x : ndarray, list
            An input training example

        Return
        ------
        output : ndarray
            An output vector
        """

        output = sigmoid(np.dot(self.W.T, x) + self.b)

        return output

    def backward(self, in_value, output, actual):
        """
        One backward propagation

        Parameters
        ----------
        in_value : ndarray, list
            An input training example
        output : ndarray
            An output vector obtained from forward pass
        actual : ndarray, list
            The Actual label for the training example

        Return
        ------
        None : NoneType
        """

        self.dW += self.lr * np.dot(((actual - output) * output * (1 - output)), in_value.reshape((-1, self.input))).T
        self.db += self.lr * (actual - output) * output * (1 - output)

        if self.dW.dtype == 'O':
            self.dW = self.dW.astype("float64")

        return

    def update_weights(self):
        """
        Update the weights
        """

        self.W += self.dW
        self.b += self.db

        return

    def model(self, x, y, epochs=100, debug=False, debug_verbose=False):
        """
        Find the weights

        Parameters
        ----------
        x : ndarray, list
            Features of the training data
        y : ndarray, list
            Labels of the training data
        epochs : int, optional
            Number of epochs to run
        learning : str, optional
            Type of the learning

        Return
        ------
        None : NoneType
        """

        self.epochs = epochs
        self.model_ran = True
        self.data_length = len(x)
        self.training_loss = list()

        for e in range(self.epochs):
            predict = list()
            self.zero_grad()
            for i in range(self.data_length):
                x_tmp = self.__reshape_input(x[i])
                output = self.forward(x_tmp)
                y_tmp = self.__reshape_output(y[i])
                self.backward(x_tmp, output, y_tmp)

                predict.append(output.tolist())
            self.update_weights()

            if debug:
                self.debug(more_verbose=debug_verbose)

            self.training_loss.append(MSE(y, predict))
            print(f"Epochs: {e+1}/{epochs} Training Loss: {self.training_loss[e]}..")

        return

    def zero_grad(self):
        self.dW = 0
        self.db = 0

        return

    def debug(self, more_verbose=False):
        print("Weights are: {}".format(self.W))
        print("Biases are: {}".format(self.b))

        if more_verbose:
            print("dW is: {}".format(self.dW))
            print("db is: {}".format(self.db))

    def get_weights(self):
        return self.W

    def get_biases(self):
        return self.b

    def summary(self):
        if self.model_ran is False:
            print("Run the model before viewing summary statistics")
            return

        print("=============================")
        print("\tSummary\t")
        print("=============================")
        print("Number of features are {}\t".format(self.input))
        print("Number of targets are {}\t".format(self.output))
        print("Model ran for {} epochs\t".format(self.epochs))
        print("Trained on {} examples\t".format(self.data_length))
        print("With a learning rate of\t{}".format(self.lr))
        print("Dimensions of weights {}\t".format(self.W.shape))
        print("Dimensions of biases {}\t".format(self.b.shape))
        print("=============================")

    def plotting(self):
        """
        Parameters
        ----------
        train : ndarray, list
            Training data
        test : ndarray, list
            Testing data

        Return
        ------
        None : NoneType
        """

        plt.figure()
        plt.grid(True)
        plt.title("Training Loss")
        plt.xlabel("Epochs")
        plt.ylabel("Loss(MSE)")
        plt.plot(range(self.epochs), self.training_loss)
        plt.show()

        return

    def predict(self, y_new):
        """
        Takes an observation as input and classifies it

        Parameters
        ----------
        y_new : ndarray, list
            An observation

        Returns
        -------
        predicted : ndarray
            The output vector
        """

        y_tmp = self.__reshape_input(y_new)
        predicted = self.forward(y_tmp)
        print(predicted)

        return predicted

if __name__ == '__main__':
    # fname = input("Enter the filename: ")
    fname = 'dataset.csv'
    data = pd.read_csv(fname)

    cat = OneHotEncoder()
    # cat = categorize()
    cat.fit(data['Class'].values)
    y = cat.transform(data['Class'].values)
    x = data[['Mass', 'Speed']].values

    ## Run the model
    epochs = 10000
    learning_rate = 0.1
    # epochs = int(input("Enter the number of Epochs: "))
    # learning_rate = float(input("Enter the Learning rate: "))

    net = Perceptron(2, 2, learning_rate=learning_rate)
    print(net)
    net.model(x, y, epochs=epochs)
    print(net)
    net.summary()
    net.plotting()

    ## Prediction
    net.predict(x[1])


from typing import Union
from pyrogram.types import InlineQueryResultArticle as InRes, InputTextMessageContent as InTXT
from pyrogram.types import *

class Msg:
    today_btn = "馃棑 讛转讗专讬讱 讛讬讜诐 馃棑"
    convert_btn = "馃攧 讛诪专转 转讗专讬讱 馃攧"
    error_format = "驻讜专诪讟 砖讙讜讬. 诇讞抓 讻讗谉 馃敇"
    error_unknow = "鈿狅笍 砖讙讬讗讛 诇讗 讬讚讜注讛 鈿狅笍"
    nav_btn = '诇讛住讘专 砖讬诪讜砖 诇讞抓 讻讗谉 馃敇'

    def json_to_msg(json: dict, answer: bool = False) -> Union[str, InRes]:
        """ convert the answer from the api, to msg format """
        full_ge = lambda d: f"{d['gd']}/{d['gm']}/{d['gy']}"

        if not answer:  # simple telegram message
            msg = f'**转讗专讬讱 注讘专讬:** {json["hebrew"]}\n\n'
            msg += f'**转讗专讬讱 诇讜注讝讬:** {full_ge(json)}'
            return msg

        else:  # inline telegram message
            res = InRes(
                json['hebrew'] + " ~ " + full_ge(json),
                InTXT(f"**转讗专讬讱 注讘专讬:** {json['hebrew']}\n\n**转讗专讬讱 诇讜注讝讬:** {full_ge(json)}"),
                thumb_url="https://telegra.ph/file/2388b677bba4403ee2f7e.png"
            )
            return res

    def format_help(message: Message):
        """ return a help and support message """
        txt = "砖讬诪讜 诇讘, 讛讞讬驻讜砖 转讜诪讱 讘诪讙讜讜谉 专讞讘 砖诇 驻讜专诪讟讬诐 诇谞讜讞讬讜转讻诐.\n"
        txt += "诇讚讜讙诪讛, 转讜讻诇讜 诇讛砖转诪砖 讘讗讞讚 诪讛驻讜专诪讟讬诐 讛讘讗讬诐:\n"
        txt += "\n`@DateConverterHGbot <转讗专讬讱 诇讛诪专讛>`\n\n"
        txt += """```讻"讗 住讬讜讜谉 转砖注"讗
讟' 讻住诇讬讜 转砖"驻
讗 住讬讜谉 转砖注讝

```"""
        txt += "讜讘诪拽讘讬诇, 讘诇讜注讝讬:\n"
        txt += """```01/05/2015
1-5-15
1 5 2015```

"""
        txt += "注诐 讝讗转, 讛讘讜讟 转讜诪讱 讻专讙注 讘讟讜讜讞 转讗专讬讻讬诐 讛讙讬讜谞讬 讜谞讜专诪诇讬, 诇诇讗 讛转讞讬讬讘讜转 诇转讗专讬讻讬诐 专讞讜拽讬诐 诪讬讚讬."
        txt += "\n诇讞讬驻讜砖 转讗专讬讱 讘讞讜讚砖 讗讚专 讘', 讛讝讬谞讜: `讗讚专-讘`."

        message.reply(txt, reply_markup=InlineKeyboardMarkup(
            [[InlineKeyboardButton("诇讞讬驻讜砖 转讗专讬讱", switch_inline_query_current_chat="")]]
        ))

    def start_msg(msg: Message):
        """ return a start message """
        txt = f"""讛讬讬 {msg.from_user.mention}! 讘专讜讘讜讟 讝讛 转讜讻诇讜 诇讛诪讬专 转讗专讬讱 注讘专讬 诇诇讜注讝讬 讘讗讜驻谉 讚讜 爪讚讚讬. 讗讜驻谉 讛砖讬诪讜砖
        驻砖讜讟 讜拽诇, 讛拽诇讬讚讜 讘讻诇 爪'讗讟 讗转 讬讜讝专 讛讘讜讟, 讜讗讞专讬讜 讗转 讛转讗专讬讱 砖转专爪讜 诇讛诪讬专. 讛专讜讘讜讟 讬爪讬讙 讘驻谞讬讻诐 讗转 讛转讗专讬讱 讛诪讘讜拽砖
        讘诇讜注讝讬 讜讘注讘专讬.

诪注讜谞讬讬谞讬诐 讘驻讬专讜讟 谞讜住祝? 砖诇讞讜 诇讘讜讟 /help.

讘讜讟 讝讛 谞讜爪专 注诇 讬讚讬 [Yeuda-By](t.me/m100achuzMe) 诪爪讜讜转 [专讜讘讜讟专讬拽](t.me/robot_trick_channel)."""

        return txt


from flask import Flask, render_template, request, redirect, flash
from werkzeug.utils import secure_filename
import os
from appinit import app, db
from dbHandler import Student
import uploadS3

@app.route('/', methods=['POST', 'GET'])
@app.route('/index', methods=['POST', 'GET'])
def index():
    if request.method == 'POST':
        student_name = request.form['name']
        student_course = request.form['course']
        student_phone = request.form['phone']
        student_picture = request.form['picture']

        student = Student(student_name, student_course, student_phone, student_picture)

        try:
            db.session.add(student)
            db.session.commit()
            return redirect('/')
        except Exception as e:
            print("Error while saving to DB." + str(e))
    else:
        students = Student.query.order_by(Student.student_id).all()
        return render_template('index.html', students=students)
    return render_template('index.html')

@app.route('/add', methods=['POST','GET'])
def add():
    if request.method == 'POST':
        student_name = request.form['name']
        student_course = request.form['course']
        student_phone = request.form['phone']
       # student_picture = request.form['picture']

        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)

        file = request.files['file']
        filename = secure_filename(file.filename)

        if file:
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            status = uploadS3.upload(filename)
            if status == True:
                filepath = 'static/files/' + filename
                if os.path.exists(filepath):
                    os.remove(filepath)
                    print(f'{filepath} deleted successfully')
                else:
                    print("The file does not exist")

        student_picture = filename
        student = Student(student_name, student_course, student_phone, student_picture)

        try:
            db.session.add(student)
            db.session.commit()
            return redirect('/')
        except Exception as e:
            print("Error while saving to DB." + str(e))
    else:
        return render_template('add.html')

@app.route('/delete/<int:student_id>')
def delete(student_id):
    record_to_delete = Student.query.get_or_404(student_id)

    try:
        db.session.delete(record_to_delete)
        db.session.commit()
        return redirect('/')
    except Exception as e:
        print("Error while delete from DB." + str(e))
        return 'There was a problem deleting the student.'

@app.route('/update/<int:student_id>', methods=['GET', 'POST'])
def update(student_id):
    student = Student.query.get_or_404(student_id)

    if request.method == 'POST':
        student.student_name = request.form['name']
        student.student_course = request.form['course']
        student.student_phone = request.form['phone']
       # student.student_picture = request.form['picture']

        file = request.files['file']
        filename = secure_filename(file.filename)
        if file:
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            status = uploadS3.upload(filename)
            if status == True:
                filepath = 'static/files/' + filename
                if os.path.exists(filepath):
                    os.remove(filepath)
                    print(f'{filepath} deleted successfully')
                else:
                    print("The file does not exist")
            student.student_picture = filename

        student = Student(student.student_name, student.student_course, student.student_phone, student.student_picture)

        try:
            db.session.commit()
            return redirect('/')
        except Exception as e:
            print("Error while update on DB." + str(e))
            return 'There was a problem updating the student.'

    else:
        return render_template('update.html', student=student)


import sys
from pathlib import Path
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib_venn import venn3
from pyhpo.ontology import Ontology

#Les dictionnaires récupèrent les différents identifiants des symptomes référencés comme associés (Orpha/OMIM) ou non (NA).
#Ils ne sont pas exploité par le programme pour le moment.
diseases_OMIM = {}
diseases_Orpha = {}
diseases_NA = {}

def count_with_twins(hposet, current_disease):
    """
    Interroge l'API HPO et demande, pour chaque signe clinique, s'il est affilié à la maladie de départ.
    Fait le compte total des signes cliniques affilié à la base de données OMIM et/ou Orphanet,
    et celui des signes n'apparaissant dans aucune base de données.
    :param hposet: ensemble des signes cliniques du fichier initial converti en des termes référencés par HPO
    :param current_disease: maladie de départ recherchée sur Entrez Gene de NCBI ou sur ConQuR-Bio.
    :return: le compte des signes cliniques respectivement référencés soit par OMIM, par Orphanet,
    par les deux ou par aucun.
    """

    counter_OMIM = 0
    counter_Orpha = 0
    counter_NA = 0
    duplicates = 0

    for q in range(len(hposet)):
        presence_OMIM = 0
        presence_Orpha = 0

        _hpo_omim = list(hposet[q].omim_diseases)
        _hpo_orpha = list(hposet[q].orpha_diseases)
        for diseases in range(len(_hpo_omim)):
            if current_disease.lower() in _hpo_omim[diseases].name.lower() and presence_OMIM == 0:
                diseases_OMIM.update({hposet[q].id: hposet[0].name})
                counter_OMIM += 1
                presence_OMIM = 1
        for diseases in range(len(_hpo_orpha)):
            if current_disease.lower() in _hpo_orpha[diseases].name.lower() and presence_Orpha == 0 and presence_OMIM == 1:
                duplicates += 1
                if not counter_OMIM == 0:
                    counter_OMIM -= 1
                diseases_Orpha.update({hposet[q].id: hposet[0].name})
                presence_Orpha = 1
            elif current_disease.lower() in _hpo_orpha[diseases].name.lower() and presence_Orpha + presence_OMIM == 0:
                counter_Orpha += 1
                diseases_Orpha.update({hposet[q].id: hposet[0].name})
                presence_Orpha = 1
        if presence_OMIM + presence_Orpha == 0:
            counter_NA += 1
            diseases_NA.update({hposet[q].id: hposet[q].name})

    compte_total = len(hposet)
    return duplicates, counter_OMIM, counter_Orpha, counter_NA, compte_total

def convert_to_graphs(current_disease, output_dir, data_dir, counter_OMIM, duplicates,
                        counter_Orpha, counter_NA, compte_total):
    """
    Exploite les différents comptes obtenus par count_with_twins() sous forme de camemberts
    et de diagramme de Venn.
    :param current_disease: maladie de départ recherchée sur Entrez Gene de NCBI ou sur ConQuR-Bio.
    :param output_dir: dossier où sont déposés les différents graphiques obtenus sur la base de l'ensemble
    des fichiers provenant du dossier initial data_dir.
    :param data_dir: dossier où se trouve les fichiers contenant les signes cliniques récupérés à partir de g:profiler
    sur la base des recherches de maladie de départ entrées sur Entrez Gene de NCBI ou sur ConQuR-Bio.
    :param counter_OMIM: compte des signes cliniques affiliées à la maladie de départ chez OMIM.
    :param duplicates: compte des signes cliniques affiliées à la maladie de départ chez OMIM ET Orphanet.
    :param counter_Orpha: compte des signes cliniques affiliées à la maladie de départ chez Orphanet.
    :param counter_NA: compte des signes cliniques affiliées à la maladie de départ chez aucune base de données.
    :return: créer des fichiers images contenant respectivement les camemberts et les diagrammes de Venn.
    """

    labels = "", "", "", ""
    sizes = [counter_OMIM, duplicates, counter_Orpha, counter_NA]
    colors = ['khaki', 'springgreen', "turquoise", 'gainsboro']
    title = "Pas trouvé !"
    if "NCBI" in data_dir.absolute().as_posix():
        title = plt.title(f"{current_disease} (NCBI)\n N = {compte_total}")
    elif "CQR" in data_dir.absolute().as_posix():
        title = plt.title(f"{current_disease} (ConQur-Bio)\n N = {compte_total}")
    elif "bruit_test" in data_dir.absolute().as_posix():
        title = plt.title(f"{current_disease} (test bruit)\n N = {compte_total}")
    title.set_ha("center")
    plt.pie(sizes, labels=labels, colors=colors, pctdistance=0.5, labeldistance=1.1,
            autopct='%1.1f%%', startangle=90)
    plt.legend([f"OMIM : {counter_OMIM}", f"Orphanet + OMIM : {duplicates}",
                f"Orphanet : {counter_Orpha}", f"Absent : {counter_NA}"], loc='lower right')
    plt.axis('equal')
    if "NCBI" in data_dir.absolute().as_posix():
        plt.savefig(f"{output_dir.joinpath(current_disease).absolute().as_posix()}_NCBI.png")
    elif "CQR" in data_dir.absolute().as_posix():
        plt.savefig(f"{output_dir.joinpath(current_disease).absolute().as_posix()}_CQR-BIO.png")
    elif "bruit_test" in data_dir.absolute().as_posix():
        plt.savefig(f"{output_dir.joinpath(current_disease).absolute().as_posix()}_bruit-test.png")
    plt.figure().clear()

    v = venn3(subsets=(counter_OMIM+1, counter_Orpha+1, duplicates+1, counter_NA+1, 0, 0, 0), set_labels=("", "", ""),
              set_colors=("khaki", "turquoise", "gainsboro"),
              alpha=0.5)
    v.get_label_by_id('100').set_text(f"{counter_OMIM}")
    v.get_label_by_id('010').set_text(f"{counter_Orpha}")
    v.get_label_by_id('001').set_text(f"{counter_NA}")
    v.get_label_by_id('110').set_text(f"{duplicates}")
    v.get_patch_by_id('110').set_color('springgreen')
    plt.title("on a un problème huston")
    plt.legend([f"OMIM : {counter_OMIM}", f"Orphanet : {counter_Orpha}", f"Orphanet + OMIM : {duplicates}",
                f"Absent : {counter_NA}"], loc='lower right')
    if "NCBI" in data_dir.absolute().as_posix():
        plt.title(f"{current_disease} (NCBI)\n N = {compte_total}")
    elif "CQR" in data_dir.absolute().as_posix():
        plt.title(f"{current_disease} (ConQur-Bio)\n N = {compte_total}")
    elif "bruit_test" in data_dir.absolute().as_posix():
        plt.title(f"{current_disease} (test bruit)\n N = {compte_total}")
    if "NCBI" in data_dir.absolute().as_posix():
        plt.savefig(f"{output_dir.joinpath(current_disease).absolute().as_posix()}_Venn_NCBI.png")
    elif "CQR" in data_dir.absolute().as_posix():
        plt.savefig(f"{output_dir.joinpath(current_disease).absolute().as_posix()}_Venn_CQR-BIO.png")
    elif "bruit_test" in data_dir.absolute().as_posix():
        plt.savefig(f"{output_dir.joinpath(current_disease).absolute().as_posix()}_Venn_bruit-test.png")
    plt.figure().clear()

def main(indir, outdir):
    """
    Le Main s'occupe d'automatiser le traitement de chaque fichier du dossier de départ. A partir des listes
    de signes cliniques de chaque maladie de départ, il exploite les deux fonctions du programme pour déposer
    dans le dossier final les résultats des comptes sous forme de camemberts et de diagramme de Venn.
    :param indir: chemin d'accès au dossier initial où se trouve les fichiers exploités
    :param outdir: chemin d'accès au dossier final où sont déposés les résultats du programme.
    :return: les résultats composés de camemberts et de diagramme de Venn mettant en évidence
    les proportions d'affiliation entre signes cliniques et maladie de départ.
    """
    data_dir = Path(indir)  # dossier où se trouvent les listes de gènes provenant des maladies recherchées
    output_dir = Path(outdir)  # dossier où sont placés les résultats de g:profiler pour chaque liste
    if not data_dir.is_dir():
        raise ValueError(f"\nInput directory : {data_dir.as_posix()} is not a directory")

    if not output_dir.is_dir():
        raise ValueError(f"\nOutput directory : {output_dir.as_posix()} is not a directory")

    for fichier in list(data_dir.glob("*.csv")):
        if not fichier.parts[-1].startswith(".") and fichier.is_file():
            data = pd.read_csv(fichier.absolute().as_posix())
            current_disease = fichier.name.replace(".csv", "")
            _ = Ontology()
            hpo_terms = data.name.to_list()
            hposet = [Ontology.match(q) for q in hpo_terms]
            duplicates, counter_OMIM, counter_Orpha, counter_NA, compte_total = count_with_twins(hposet, current_disease)
            convert_to_graphs(current_disease, output_dir, data_dir, counter_OMIM,
                                duplicates, counter_Orpha, counter_NA, compte_total)

if __name__ == '__main__':
    if len(sys.argv) == 3:
        main(sys.argv[1], sys.argv[2])
    else:
        print("usage :")
        print("python convertToCsv.py input_dir output_dir")
        print("input_dir  : directory containing files with lists of genes")
        print("output_dir : directory to store the csv files of related genes")


import torch

from collections import OrderedDict
from functools import partial
from torch.autograd import Variable
from torch.nn import CrossEntropyLoss, Module
from torch.optim import SGD
import numpy

from .utils import add_metrics_to_log, get_loader, log_to_message, ProgressBar

DEFAULT_LOSS = CrossEntropyLoss()
DEFAULT_OPTIMIZER = partial(SGD, lr=0.001, momentum=0.9)

class FitModule(Module):

    def eval_hessian(self, loss_grad):
        cnt = 0
        for g in loss_grad:
            g_vector = g.contiguous().view(-1) if cnt == 0 else torch.cat([g_vector, g.contiguous().view(-1)])
            cnt = 1
        l = g_vector.size(0)
        hessian = torch.zeros(l, l)
        for idx in range(l):
            g_vector[idx].requires_grad = True
            grad2rd = torch.autograd.grad(g_vector[idx], self.parameters(), create_graph=True)
            cnt = 0
            for g in grad2rd:
                g2 = g.contiguous().view(-1) if cnt == 0 else torch.cat([g2, g.contiguous().view(-1)])
                cnt = 1
            hessian[idx] = g2
        return hessian.cpu().data.numpy()

    def fit(self,
            X,
            y,
            batch_size=32,
            epochs=10,
            verbose=0,
            validation_split=0.,
            validation_data=None,
            shuffle=True,
            initial_epoch=0,
            seed=None,
            loss=DEFAULT_LOSS,
            optimizer=DEFAULT_OPTIMIZER,
            metrics=None):
        """Trains the model similar to Keras' .fit(...) method

        # Arguments
            X: training data Tensor.
            y: target data Tensor.i
            batch_size: integer. Number of samples per gradient update.
            epochs: integer, the number of times to iterate
                over the training data arrays.
            verbose: 0, 1. Verbosity mode.
                0 = silent, 1 = verbose.
            validation_split: float between 0 and 1:
                fraction of the training data to be used as validation data.
                The model will set apart this fraction of the training data,
                will not train on it, and will evaluate
                the loss and any model metrics
                on this data at the end of each epoch.
            validation_data: (x_val, y_val) tuple on which to evaluate
                the loss and any model metrics
                at the end of each epoch. The model will not
                be trained on this data.
            shuffle: boolean, whether to shuffle the training data
                before each epoch.
            initial_epoch: epoch at which to start training
                (useful for resuming a previous training run)
            seed: random seed.
            optimizer: training optimizer
            loss: training loss
            metrics: list of functions with signatures `metric(y_true, y_pred)`
                where y_true and y_pred are both Tensors

        # Returns
            list of OrderedDicts with training metrics
        """
        if seed and seed >= 0:
            torch.manual_seed(seed)
        # Prepare validation data
        if validation_data:
            X_val, y_val = validation_data
        elif validation_split and 0. < validation_split < 1.:
            split = int(X.size()[0] * (1. - validation_split))
            X, X_val = X[:split], X[split:]
            y, y_val = y[:split], y[split:]
        else:
            X_val, y_val = None, None
        # Build DataLoaders
        if isinstance(X, numpy.ndarray):
            X = torch.from_numpy(X).float()
        if isinstance(y, numpy.ndarray):
            y = torch.from_numpy(y).float()
        if isinstance(X_val, numpy.ndarray):
            X_val = torch.from_numpy(X_val).float()
        if isinstance(y_val, numpy.ndarray):
            y_val = torch.from_numpy(y_val).float()
        train_data = get_loader(X, y, batch_size, shuffle)
        # Compile optimizer
        opt = optimizer(self.parameters())
        # Run training loop
        logs = []
        self.train()
        for t in range(initial_epoch, epochs):
            if verbose:
                print("Epoch {0} / {1}".format(t+1, epochs))
            # Setup logger
            if verbose:
                pb = ProgressBar(len(train_data))
            log = OrderedDict()
            epoch_loss = 0.0
            # Run batches
            device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
            for batch_i, batch_data in enumerate(train_data):
                # Get batch data
                X_batch = Variable(batch_data[0].to(device), requires_grad=True).float()
                y_batch = Variable(batch_data[1].to(device), requires_grad=True).long()
                # Backprop
                opt.zero_grad()
                y_batch_pred = self(X_batch).float()
                batch_loss = loss(y_batch_pred, y_batch)
                batch_loss.backward()
                opt.step()
                # Update status
                epoch_loss += batch_loss.item()
                for param in self.parameters():
                    param.requires_grad = True
                # print(y_val)
                # hessian = self.eval_hessian(y)
                # print(hessian.shape)
                # print(hessian)
                log['loss'] = float(epoch_loss) / (batch_i + 1)
                if verbose:
                    pb.bar(batch_i, log_to_message(log))
            # Run metrics
            if metrics:
                y_train_pred = self.predict(X, batch_size)
                add_metrics_to_log(log, metrics, y, y_train_pred)
            if X_val is not None and y_val is not None:
                y_val_pred = self.predict(X_val, batch_size)
                val_loss = loss(Variable(y_val_pred, requires_grad=True), Variable(y_val, requires_grad=True))
                log['val_loss'] = val_loss.data[0]
                if metrics:
                    add_metrics_to_log(log, metrics, y_val, y_val_pred, 'val_')
            logs.append(log)
            if verbose:
                pb.close(log_to_message(log))
        return logs

    def predict(self, X, batch_size=32):
        """Generates output predictions for the input samples.

        Computation is done in batches.

        # Arguments
            X: input data Tensor.
            batch_size: integer.

        # Returns
            prediction Tensor.
        """
        # Build DataLoader
        data = get_loader(X, batch_size=batch_size)
        # Batch prediction
        self.eval()
        r, n = 0, X.size()[0]
        device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        for batch_data in data:
            # Predict on batch
            X_batch = Variable(batch_data[0].type('torch.FloatTensor').to(device), requires_grad=True).type('torch.FloatTensor').to(device)
            y_batch_pred = self(X_batch).data.type('torch.FloatTensor')
            # Infer prediction shape
            if r == 0:
                y_pred = (torch.zeros((n,) + y_batch_pred.size()[1:])).data.type('torch.FloatTensor')
            # Add to prediction tensor
            y_pred[r : min(n, r + batch_size)] = y_batch_pred
            r += batch_size
        return y_pred


T=int(input())
monthEnd=[0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
for test_case in range(1, T+1):
    count=1
    startMonth, startDay, endMonth, endDay=map(int, input().split())
    if(startMonth==endMonth):
        count+=endDay-startDay
    else:
        for i in range(startMonth+1, endMonth):
            count+=monthEnd[i]
        count+=monthEnd[startMonth]-startDay
        count+=endDay
    print("#{0} {1}".format(test_case, count))


from collections import deque
import numpy as np

class LaneLine(object):
    """Implementation of a Lane Line object"""

    def __init__(self, x, y, heigth, width):
        self.heigth = heigth
        self.width = width
        self.coef = deque(maxlen=5)
        self.fit_points(x, y)
        self._meter_per_x_axis = 700
        self._meter_per_y_axis = 720
        # lane width assumed is 3.7 meters
        self._land_width = 3.7
        # assuming the lane is 30 m long
        self._image_lane_length = 30

    def fit_points(self, x, y):
        points = len(y) > 0 and (np.max(y) - np.min(y)) > self.heigth * 0.625
        no_coef = len(self.coef) == 0

        if points or no_coef:
            self.coef.append(np.polyfit(y, x, 2))

    def generate_points(self):
        y = np.linspace(0, self.heigth - 1, self.heigth)
        fit = np.array(self.coef).mean(axis=0)

        return np.stack((fit[0] * y ** 2 + fit[1] * y + fit[2], y)).astype(np.int).T

    def radius_of_curvature(self):
        ym_per_pix = self._image_lane_length / self._meter_per_y_axis
        xm_per_pix = self._land_width / self._meter_per_x_axis

        points = self.generate_points()
        x = points[:, 0]
        y = points[:, 1]
        fit = np.polyfit(y * ym_per_pix, x * xm_per_pix, 2)
        first_derivate = 2 * fit[0] * self._meter_per_y_axis * ym_per_pix + fit[1]
        second_derivate = 2 * fit[0]
        radius = int(((1 + (first_derivate ** 2) ** 1.5) / np.absolute(second_derivate)))
        return radius

    def camera_distance(self):
        xm_per_pix = self._land_width / self._meter_per_x_axis

        points = self.generate_points()
        x = points[np.max(points[:, 1])][0]
        distance = np.absolute((self.width // 2 - x) * xm_per_pix)
        return distance


import logging
from os.path import abspath, dirname, join
import sys
import unittest

from merlion.evaluate.forecast import ForecastMetric
from merlion.models.forecast.trees import LGBMForecaster, LGBMForecasterConfig
from merlion.models.utils.rolling_window_dataset import RollingWindowDataset
from merlion.transform.sequence import TransformSequence
from merlion.transform.resample import TemporalResample
from merlion.transform.bound import LowerUpperClip
from merlion.transform.normalize import MeanVarNormalize
from merlion.utils import TimeSeries
from ts_datasets.forecast import SeattleTrail

logger = logging.getLogger(__name__)
rootdir = dirname(dirname(dirname(abspath(__file__))))

class TestLGBMForecaster(unittest.TestCase):
    """
    we test data loading, model instantiation, forecasting consistency, in particular
    (1) load a testing data
    (2) transform data
    (3) instantiate the model and train
    (4) forecast, and the forecasting result agrees with the reference
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def _run_test(self, univariate: bool, use_exog: bool, autoregressive: bool):
        # Get the data and clean it up
        df, md = SeattleTrail(rootdir=join(rootdir, "data", "multivariate", "seattle_trail"))[0]
        t = int(df[md["trainval"]].index[-1].to_pydatetime().timestamp())
        target = "BGT North of NE 70th Total"
        data = TimeSeries.from_pd(df.iloc[:, [1, 0, 2, 3, 4]])
        cleanup = TransformSequence([TemporalResample(missing_value_policy="FFill"), LowerUpperClip(upper=300)])
        cleanup.train(data)
        data = cleanup(data)

        # Pretend the last 2 non-target univariates are exogenous regressors if using exogenous data.
        # Test the benefit of adding them, so don't include them in the endogenous data by default.
        exog_cols = [c for c in data.names if c != target][-3:]
        data_cols = [target] if univariate else [c for c in data.names if c not in exog_cols]
        exog_data = None if not use_exog else TimeSeries([data.univariates[c] for c in exog_cols])
        data = TimeSeries([data.univariates[c] for c in data_cols])
        train_data, test_data = data.bisect(t)

        # Set up the model
        maxlags = 15
        max_forecast_steps = 20
        target_seq_idx = {k: i for i, k in enumerate(train_data.names)}[target]
        model = LGBMForecaster(
            LGBMForecasterConfig(
                max_forecast_steps=None if autoregressive else max_forecast_steps,
                maxlags=maxlags,
                target_seq_index=target_seq_idx,
                prediction_stride=5 if univariate and autoregressive else 1 if autoregressive else max_forecast_steps,
                n_estimators=20,
                max_depth=7,
                n_jobs=1,
                transform=MeanVarNormalize(),
                exog_transform=MeanVarNormalize(),
                invert_transform=True,
            )
        )

        # Train model
        yhat, _ = model.train(train_data, exog_data=exog_data)

        # Check RMSE with forecast transform inversion
        forecast, _ = model.forecast(max_forecast_steps, exog_data=exog_data)
        rmse = ForecastMetric.RMSE.value(test_data, forecast, target_seq_index=target_seq_idx)
        logger.info(f"Immediate forecast RMSE:  {rmse:.2f}")

        # Check look-ahead RMSE using time_series_prev
        dataset = RollingWindowDataset(test_data, target_seq_idx, maxlags, max_forecast_steps, ts_index=True)
        test_prev, test = dataset[0]
        pred, _ = model.forecast(test.time_stamps, time_series_prev=test_prev, exog_data=exog_data)
        lookahead_rmse = ForecastMetric.RMSE.value(test, pred)
        logger.info(f"Look-ahead RMSE with time_series_prev:  {lookahead_rmse:.2f}")

        # save and load
        model.save(dirname=join(rootdir, "tmp", "lgbmforecaster"))
        loaded_model = LGBMForecaster.load(dirname=join(rootdir, "tmp", "lgbmforecaster"))
        loaded_pred, _ = loaded_model.forecast(test.time_stamps, time_series_prev=test_prev, exog_data=exog_data)
        self.assertEqual(len(loaded_pred), len(pred))
        self.assertAlmostEqual((pred.to_pd() - loaded_pred.to_pd()).abs().max().item(), 0, places=5)

    def test_forecast_multi_autoregression(self):
        print("-" * 80)
        logger.info("test_forecast_multi_autoregression" + "\n" + "-" * 80)
        self._run_test(univariate=False, autoregressive=True, use_exog=False)

    def test_forecast_multi_seq2seq(self):
        print("-" * 80)
        logger.info("test_forecast_multi_seq2seq" + "\n" + "-" * 80)
        self._run_test(univariate=False, autoregressive=False, use_exog=False)

    def test_forecast_uni(self):
        print("-" * 80)
        logger.info("test_forecast_uni" + "\n" + "-" * 80)
        self._run_test(univariate=True, autoregressive=True, use_exog=False)

    def test_forecast_multi_autoregression_exog(self):
        print("-" * 80)
        logger.info("test_forecast_multi_autoregression_exog" + "\n" + "-" * 80)
        self._run_test(univariate=False, autoregressive=True, use_exog=True)

    def test_forecast_multi_seq2seq_exog(self):
        print("-" * 80)
        logger.info("test_forecast_multi_seq2seq_exog" + "\n" + "-" * 80)
        self._run_test(univariate=False, autoregressive=False, use_exog=True)

    def test_forecast_uni_exog(self):
        print("-" * 80)
        logger.info("test_forecast_uni_exog" + "\n" + "-" * 80)
        self._run_test(univariate=True, autoregressive=True, use_exog=True)

if __name__ == "__main__":
    logging.basicConfig(
        format="%(asctime)s (%(module)s:%(lineno)d) %(levelname)s: %(message)s", stream=sys.stdout, level=logging.INFO
    )
    unittest.main()


from typing import Tuple, Union

import numpy as np
import torch as to

import pyrado
from pyrado.spaces import BoxSpace

def rosenbrock(x: Union[to.Tensor, np.ndarray]) -> Union[to.Tensor, np.ndarray]:
    """
    The Rosenbrock function
    (consistent with https://docs.scipy.org/doc/scipy/reference/tutorial/optimize.html)

    :param x: multi-dim column vector, or array thereof
    :return: value of the Rosenbrock function at the input point, or array thereof
    """
    if isinstance(x, to.Tensor):
        return to.sum(100.0 * to.pow(x[1:] - to.pow(x[:-1], 2), 2) + to.pow((1.0 - x[:-1]), 2), dim=0)
    elif isinstance(x, np.ndarray):
        return np.sum(100.0 * np.power(x[1:] - np.power(x[:-1], 2), 2) + np.power((1.0 - x[:-1]), 2), axis=0)
    else:
        raise pyrado.TypeErr(given=x, expected_type=[np.ndarray, to.Tensor])

def noisy_nonlin_fcn(
    x: Union[to.Tensor, np.ndarray], f: float = 1.0, noise_std: float = 0.0
) -> Union[to.Tensor, np.ndarray]:
    """
    A 1-dim function (sinus superposed with polynomial), representing the black box function in Bayesian optimization

    :param x: function argument
    :param noise_std: scale of the additive noise sampled from a standard normal distribution
    :param f: frequency of the sinus wave [Hz]
    :return: function value
    """
    if isinstance(x, to.Tensor):
        return -to.sin(2 * np.pi * f * x) - to.pow(x, 2) + 0.7 * x + noise_std * to.randn_like(x)
    elif isinstance(x, np.ndarray):
        return -np.sin(2 * np.pi * f * x) - np.power(x, 2) + 0.7 * x + noise_std * np.random.randn(*x.shape)
    else:
        raise pyrado.TypeErr(given=x, expected_type=[np.ndarray, to.Tensor])

def skyline(
    dt: Union[int, float, np.ndarray],
    t_end: Union[int, float, np.ndarray],
    t_intvl_space: BoxSpace,
    val_space: BoxSpace,
) -> Tuple[np.ndarray, np.ndarray]:
    """
    Step function that randomly samples a value from the given range, and then holds this value for a time interval
    which is also randomly sampled given a range of time intervals. This procedure is repeated until the sequence is
    long enough, i.e. `dt * t_end` samples.

    :param dt: time step size
    :param t_end: final time
    :param t_intvl_space: 1-dim `BoxSpace` determining the range of time intervals that can be sampled
    :param val_space: 1-dim `BoxSpace` determining the range of values that can be sampled
    :return: array of time steps together with the associated array of values
    """
    if dt <= 0:
        raise pyrado.ValueErr(given=dt, g_constraint="0")
    if t_end < dt:
        raise pyrado.ValueErr(given=t_end, ge_constraint=f"{dt}")
    if not isinstance(t_intvl_space, BoxSpace):
        raise pyrado.TypeErr(given=t_intvl_space, expected_type=BoxSpace)
    if not isinstance(val_space, BoxSpace):
        raise pyrado.TypeErr(given=val_space, expected_type=BoxSpace)
    if not t_intvl_space.flat_dim == 1:
        raise pyrado.ShapeErr(given=t_intvl_space, expected_match=(1,))
    if not val_space.flat_dim == 1:
        raise pyrado.ShapeErr(given=val_space, expected_match=(1,))

    dt = np.asarray(dt, dtype=np.float32)
    t_end = np.asarray(t_end, dtype=np.float32)

    # First iter
    t_intvl = t_intvl_space.sample_uniform()
    t_intvl = np.clip(t_intvl, dt, t_end + dt)
    t = np.arange(start=0.0, stop=t_intvl, step=dt)
    vals = val_space.sample_uniform() * np.ones_like(t)

    # Iterate until the time is up
    while t[-1] < t_end:
        t_intvl = t_intvl_space.sample_uniform()
        t_intvl = np.clip(t_intvl, dt, t_end - t[-1] + dt)
        t_new = np.arange(start=t[-1] + dt, stop=t[-1] + t_intvl, step=dt)
        t = np.concatenate([t, t_new])
        val_new = val_space.sample_uniform() * np.ones_like(t_new)
        vals = np.concatenate([vals, val_new])

    return t, vals


import os
from jparse.parser import JSONParser

def main(source="jsons/glossary.json", **kwargs):
    my_parser = JSONParser()

    this_folder = os.path.dirname(__file__)
    source_path = os.path.join(this_folder, source)

    result = my_parser.from_stream(source_path, debug_colors=True, **kwargs)

    print(f"Input:\n {source} \n")
    print(f"Result\n {result} \n")
    print(f"Result Type\n {type(result)} \n")

if __name__ == "__main__":
    main(source="jsons/web_app.json", debug=True)


from sqlalchemy import Column, Integer, String, DateTime
from base import Base
import datetime
import uuid
class AbilityEfficiency(Base):
    """Ability Efficiency"""

    __tablename__ = "ability_efficiency"

    id = Column(Integer, primary_key=True)
    player_id = Column(String(250), nullable=False)
    agent_id = Column(String(50), nullable=False)
    round_start_ability_count = Column(Integer, nullable = False)
    round_end_ability_count = Column(Integer, nullable = False)
    ability_cost = Column(Integer, nullable=False)
    trace_id = Column(String(100), nullable=False)
    date_created = Column(DateTime, nullable=False)

    def __init__(self, player_id, agent_id, round_start_ability_count, round_end_ability_count, ability_cost, trace_id):
        """ Initializes a ability efficiency reading """
        self.player_id = player_id
        self.agent_id = agent_id
        self.round_start_ability_count = round_start_ability_count
        self.round_end_ability_count = round_end_ability_count
        self.ability_cost = ability_cost

        self.trace_id = trace_id
        self.date_created = datetime.datetime.now()

    def to_dict(self):
        """ Dictionary representation of a ability efficiency reading """

        dict = {}
        dict['id'] = self.id
        dict['player_id'] = self.player_id
        dict['agent_id'] = self.agent_id
        dict['round_start_ability_count'] = self.round_start_ability_count
        dict['round_end_ability_count'] = self.round_end_ability_count
        dict['ability_cost'] = self.ability_cost
        dict['trace_id'] = self.trace_id
        dict['date_created'] = self.date_created

        return dict


from typing import List
import re
from collections import Counter

class Solution:
    def mostCommonWord(self, paragraph: str, banned: List[str]) -> str:
        fixed_paragraph = re.sub(r'[^\w]', ' ', paragraph).lower().split()
        word_list = Counter([word for word in fixed_paragraph if word not in banned])
        return word_list.most_common(1)[0][0]

paragraph = "Bob hit a ball, the hit BALL flew far after it was hit."
banned = ["hit"]
solution = Solution().mostCommonWord(paragraph, banned)
print(solution)