import socket
import time
import base_pb2
import setting
import main_ui
from chessman import *
from PyQt5.Qt import QProgressDialog

class PlayGame(QWidget, main_ui.Ui_Form):
    backSignal = pyqtSignal()  # 返回信号

    def __init__(self, sock, username, parent=None):
        super().__init__(parent=parent)
        self.setupUi(self)
        self.sock = sock
        self.isBeginGame = False
        self.isCanXia = False
        self.label.setCursor(QCursor(Qt.ForbiddenCursor))
        self.username = username
        self.setFixedSize(self.width(), self.height())
        self.sock.readyRead.connect(self.readData)
        self.switch = {
            7: self.cmd7,
            9: self.cmd9,
            10: self.cmd10,
            12: self.cmd12,
            13: self.cmd13,
            14: self.cmd14,
            15: self.cmd15,
            16: self.cmd16,
            18: self.cmd18,
        }
        self.sound_piece = QSound("source/luozisheng.wav")
        self.sound_win = QSound("source/win.wav")
        self.sound_defeated = QSound("source/defeated.wav")
        self.sound_piece_t = self.sound_piece
        self.sound_win_t = self.sound_win
        self.sound_defeated_t = self.sound_defeated
        self.comboBox.currentIndexChanged.connect(self.messageSend)
        self.listView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.chessboard = [[None for i in range(19)] for i in range(19)]
        self.history = []
        self.history2 = []
        self.is_over = False
        self.ai = False
        self.lbl = None
        self.isMatch = False
        self.init()
        self.initBeginGame()
        self.setting = setting.Setting(self)
        self.isCloseChat = False
        self.lcdNumber.setMode(QLCDNumber.Dec)
        self.lcdNumber_3.setMode(QLCDNumber.Dec)
        self.lcdNumber_3.setSegmentStyle(QLCDNumber.Flat)
        # self.lcdNumber.setStyleSheet("border: 2px solid black; color: red; ")
        self.time = QTimer(self)
        self.time.setInterval(1000)
        self.time.timeout.connect(self.refresh)

    def init(self):
        self.setWindowTitle("网络五子棋 v1.1")
        # 设置窗口图标
        self.setWindowIcon(QIcon("source/icon.ico"))
        # 设置窗口大小
        # self.setFixedSize(QImage("source/游戏界面.png").size())
        self.listView.clicked.connect(self.listViewClicked)
        self.pushButton.clicked.connect(self.messageSend)
        self.pushButton_2.clicked.connect(self.withdraw)
        self.pushButton_4.clicked.connect(self.beginGame)
        self.pushButton_5.clicked.connect(self.wantToLose)
        self.pushButton_3.clicked.connect(self.draw)
        self.pushButton_6.clicked.connect(self.setUp)
        self.fontText = QFont("Microsoft YaHei", 10, 75)
        self.label_3.setFont(self.fontText)
        self.label_4.setFont(self.fontText)
        self.pushButton_7.clicked.connect(self.aiBegin)
        self.focusPoint = QLabel(self)
        self.focusPoint.setPixmap(QPixmap("source/标识.png"))
        self.pushButton.setShortcut(QKeySequence(Qt.Key_Enter))
        self.pushButton.setShortcut(QKeySequence(Qt.Key_Return))
        self.label_9.setText('在线列表（点击发起对战）：')
        palette = QPalette()
        pix = QPixmap('source/background.png')
        pix = pix.scaled(self.width(), self.height())
        palette.setBrush(QtGui.QPalette.Background, QtGui.QBrush(pix))
        self.setPalette(palette)

    def initBeginGame(self):
        if self.isBeginGame:
            self.pushButton_5.setEnabled(True)
            self.pushButton_4.setEnabled(False)
            self.pushButton_7.setEnabled(False)
            if self.ai == True:
                self.pushButton_3.setEnabled(False)
                self.pushButton_3.setEnabled(False)
            else:
                self.pushButton_3.setEnabled(True)
                self.pushButton_2.setEnabled(True)

            self.time.start()
            self.reStart()
            if self.myColor == 'black':
                self.label_2.setPixmap(QPixmap(r'source\black.png'))
                self.label_5.setPixmap(QPixmap(r'source\white.png'))
                if self.isCanXia:
                    self.turnChessColor = 'black'
                else:
                    self.turnChessColor = 'white'
            elif self.myColor == 'white':
                self.label_2.setPixmap(QPixmap(r'source\white.png'))
                self.label_5.setPixmap(QPixmap(r'source\black.png'))
                if self.isCanXia:
                    self.turnChessColor = 'white'
                else:
                    self.turnChessColor = 'black'
        else:
            self.pushButton_2.setEnabled(False)
            self.pushButton_3.setEnabled(False)
            self.pushButton_4.setEnabled(True)
            self.pushButton_5.setEnabled(False)
            self.pushButton_7.setEnabled(True)
        if self.isCanXia:
            self.label.setCursor(QCursor(Qt.PointingHandCursor))
        else:
            self.label.setCursor(QCursor(Qt.ForbiddenCursor))

        self.endDate = QDateTime.currentMSecsSinceEpoch() + 1000 * 60 * 10
        self.endDateOther = self.endDate

    def refresh(self):
        nowData = QDateTime.currentMSecsSinceEpoch()
        interval = self.endDate - nowData
        interval2 = self.endDateOther - nowData
        if self.isBeginGame == False:
            return
        if interval > 0:
            if self.isCanXia == False:
                self.endDate = self.endDate + 1000
            else:
                self.endDateOther = self.endDateOther + 1000
            self.lcdNumber.display(self.conversionTime(interval))
            self.lcdNumber_2.display(self.conversionTime(interval2))
        else:
            self.lose()

    def conversionTime(self, interval):
        days = interval // (24 * 60 * 60 * 1000)
        hour = (interval - days * 24 * 60 * 60 * 1000) // (60 * 60 * 1000)
        min = (interval - days * 24 * 60 * 60 * 1000 - hour * 60 * 60 * 1000) // (60 * 1000)
        sec = (interval - days * 24 * 60 * 60 * 1000 - hour * 60 * 60 * 1000 - min * 60 * 1000) // 1000
        s = str(min) + ':' + str(sec)
        return s

    def closeEvent(self, a0: QtGui.QCloseEvent):
        self.backSignal.emit()

    def mouseReleaseEvent(self, a0: QtGui.QMouseEvent):
        if self.isCanXia == False:
            self.label.setCursor(QCursor(Qt.ForbiddenCursor))
            return
        else:
            self.label.setCursor(QCursor(Qt.PointingHandCursor))

        if self.isBeginGame == False:
            return None

        pos, chess_index = self.reversePos(a0)
        if pos is None:
            return

        if self.chessboard[chess_index[1]][chess_index[0]] != None:
            return

        self.chess = Chessman(color=self.turnChessColor, parent=self)
        self.chess.setIndex(chess_index[0], chess_index[1])
        self.chess.move()

        s_g_p = base_pb2.server_gobang_position()
        s_g_p.cmd = 6
        s_g_p.x = chess_index[0]
        s_g_p.y = chess_index[1]

        self.writeData(s_g_p.SerializeToString())
        self.isCanXia = False
        self.label.setCursor(QCursor(Qt.ForbiddenCursor))
        if self.ai == True:
            self.autoDown()

    # 坐标转换
    def reversePos(self, a0: QtCore.QPoint):
        if a0.x() <= self.label.x() + 4 or a0.x() >= self.label.x() + 517 + 18 or a0.y() <= self.label.y() + 4 or a0.y() >= self.label.y() + 517 + 18:
            return None, None

        self.x = (a0.x() - self.label.x() - 4) // 36
        self.y = (a0.y() - self.label.y() - 4) // 36

        return QPoint(22 + self.x * 36, 22 + self.y * 36), (self.x, self.y)

    def isWin(self, chessman):
        # print("in iswin,lastChessman:", chessman.color, chessman.x, chessman.y)
        # 水平方向y相同，chessboard[chessman.y][i]
        count = 1
        # 左边
        i = chessman.x - 1
        while i >= 0:
            if self.chessboard[chessman.y][i] == None or self.chessboard[chessman.y][i].color != chessman.color:
                break
            count += 1
            i -= 1
        # 右边
        i = chessman.x + 1
        while i <= 18:
            if self.chessboard[chessman.y][i] == None or self.chessboard[chessman.y][i].color != chessman.color:
                break
            count += 1
            i += 1

        if count >= 5:
            return chessman.color

        count = 1
        j = chessman.y - 1
        while j >= 0:
            if self.chessboard[j][chessman.x] == None or self.chessboard[j][chessman.x].color != chessman.color:
                break
            count += 1
            j -= 1

        j = chessman.y + 1
        while j <= 18:
            if self.chessboard[j][chessman.x] == None or self.chessboard[j][chessman.x].color != chessman.color:
                break
            count += 1
            j += 1

        if count >= 5:
            return chessman.color

        count = 1
        j, i = chessman.y - 1, chessman.x + 1
        while j >= 0 and i <= 18:
            if self.chessboard[j][i] == None or self.chessboard[j][i].color != chessman.color:
                break
            count += 1
            j -= 1
            i += 1

        j, i = chessman.y + 1, chessman.x - 1
        while i >= 0 and j <= 18:
            if self.chessboard[j][i] == None or self.chessboard[j][i].color != chessman.color:
                break
            count += 1
            i -= 1
            j += 1
        if count >= 5:
            return chessman.color

        count = 1
        j, i = chessman.y - 1, chessman.x - 1
        while j >= 0 and i >= 0:
            if self.chessboard[j][i] == None or self.chessboard[j][i].color != chessman.color:
                break
            count += 1
            j -= 1
            i -= 1

        j, i = chessman.y + 1, chessman.x + 1
        while j <= 18 and i <= 18:
            if self.chessboard[j][i] == None or self.chessboard[j][i].color != chessman.color:
                break
            count += 1
            j += 1
            i += 1

        if count >= 5:
            return chessman.color

        return None

    def showResult(self, isWin=None):
        self.lbl = QLabel(self)
        if isWin == 'white':
            self.lbl.setPixmap(QPixmap("source/白棋胜利.png"))
            self.lbl.move(300, 100)
            self.lbl.show()
        elif isWin == 'black':
            self.lbl.setPixmap(QPixmap("source/黑棋胜利.png"))
            self.lbl.move(300, 100)
            self.lbl.show()
        if isWin == self.myColor:
            self.sound_win.play()
        else:
            self.sound_defeated.play()
        self.isBeginGame = False
        self.isCanXia = False
        self.isMatch = False
        self.ai = False
        self.initBeginGame()
        self.time.stop()

    def reStart(self):
        for i in range(19):
            for j in range(19):
                if self.chessboard[i][j] != None:
                    self.chessboard[i][j].close()
                    self.chessboard[i][j] = None
                    self.focusPoint.close()
                else:
                    pass
        if self.lbl != None:
            self.lbl.close()

    def beginGame(self):
        self.reStart()
        if self.isBeginGame == False and self.isMatch == False:
            requestRes = base_pb2.requestResources()
            requestRes.cmd = 17
            requestRes.code = 1
            self.writeData(requestRes.SerializeToString())
            self.progressDialog = QProgressDialog(self)
            self.progressDialog.setRange(0, 0)
            self.progressDialog.setWindowTitle('正在等待对面回应')
            self.progressDialog.canceled.connect(self.progressDialogpdCanceled)
            self.isMatch = True  # 是否匹配中
            self.progressDialog.exec()

    def progressDialogpdCanceled(self):
        requestRes = base_pb2.requestResources()
        requestRes.cmd = 17
        requestRes.code = 2  # 取消匹配
        self.writeData(requestRes.SerializeToString())
        self.isMatch = False

    def wantToLose(self):
        if self.isBeginGame == False:
            return
        reply = QMessageBox.question(self, '提示', '您确定要认输吗?', QMessageBox.Yes | QMessageBox.No)
        if reply == QMessageBox.No:
            return
        else:
            self.lose()

    def lose(self):
        if self.myColor == 'black':
            self.showResult('white')
        elif self.myColor == 'white':
            self.showResult('black')
        w_w = base_pb2.whoWin()
        w_w.cmd = 15
        w_w.code = 2
        w_w.win = ''
        self.writeData(w_w.SerializeToString())

    def draw(self):
        if self.isBeginGame == False:
            return
        reply = QMessageBox.question(self, '提示', '您确定要和棋吗?', QMessageBox.Yes | QMessageBox.No)
        if reply == QMessageBox.No:
            return
        w_w = base_pb2.whoWin()
        w_w.cmd = 15
        w_w.code = 3
        w_w.win = ''
        self.writeData(w_w.SerializeToString())

    def setUp(self):
        self.setting.setWindowModality(Qt.WindowModal)
        self.setting.show()

    def withdraw(self):
        w_d = base_pb2.withDraw()
        w_d.cmd = 16
        if self.isCanXia:  # 自己回合
            w_d.nums = 2
        else:
            w_d.nums = 1
        self.writeData(w_d.SerializeToString())

    def huiBack(self):
        if len(self.history) == 0:
            return
        m = self.history.pop()
        a = self.history2.pop()

        self.chessboard[m.y][m.x] = None
        m.close()
        a.close()
        if self.turnChessColor == "black":
            self.turnChessColor = "white"
        else:
            self.turnChessColor = "black"

    def readData(self):
        headerData = self.sock.read(4)
        dataLen = int.from_bytes(headerData, byteorder='little', signed=False)
        readData = self.sock.read(dataLen)
        if readData == b'':
            return
        cmd = base_pb2.cmd()
        cmd.ParseFromString(readData)
        if cmd.c != 9:
            print("cmd: %s" % cmd.c)
        self.switch[cmd.c](readData)

    def writeData(self, data):
        print("writeData")
        headerData = QByteArray()
        wData = QDataStream(headerData, QIODevice.WriteOnly)
        wData.writeUInt32(socket.htonl(len(data)))
        self.sock.write(headerData + data)
        self.sock.flush()

    def cmd7(self, data):
        # 获取对方的棋子位置，并显示
        s_g_p = base_pb2.server_gobang_position()
        s_g_p.ParseFromString(data)
        self.chessman = Chessman(color=self.turnChessColor, parent=self)
        self.chessman.setIndex(s_g_p.x, s_g_p.y)
        self.chessman.move()
        self.isCanXia = True
        self.label.setCursor(QCursor(Qt.PointingHandCursor))

    def cmd9(self, data):
        # 显示在线用户列表
        s_o_i = base_pb2.server_online_infor()
        s_o_i.ParseFromString(data)
        # print(s_o_i.people)
        self.peopleList = s_o_i.people
        self.peopleList.remove(self.username)
        slm = QStringListModel()
        slm.setStringList(self.peopleList)
        self.listView.setModel(slm)
        self.lcdNumber_3.display(len(self.peopleList) + 1)

    def cmd10(self, data):
        s_g_i = base_pb2.server_game_invite()
        s_g_i.ParseFromString(data)
        reply = QMessageBox.question(self, '提示', '"%s"请求对战' % s_g_i.people, QMessageBox.Yes | QMessageBox.No)
        c_g_i = base_pb2.client_game_invite()
        c_g_i.cmd = 11
        if reply == QMessageBox.Yes:
            c_g_i.code = 1
            self.isBeginGame = True
            self.myColor = "white"
            self.initBeginGame()
        else:
            c_g_i.code = 0
            pass
        self.writeData(c_g_i.SerializeToString())

    def cmd12(self, data):
        s_g_i = base_pb2.server_game_isInvite()
        s_g_i.ParseFromString(data)
        reply = ""
        self.fightBox.close()
        if s_g_i.code == 1:
            reply = '对方接受了请求'
            self.isBeginGame = True
            self.isCanXia = True
            self.myColor = 'black'
            self.initBeginGame()
        elif s_g_i.code == 0:
            reply = "对方拒绝了请求"

        QMessageBox.information(self, '提示', reply, QMessageBox.Ok)

    def cmd13(self, data):
        c_m = base_pb2.chatMessage()
        c_m.ParseFromString(data)
        messagetype = c_m.type
        string = c_m.data
        messageTime = c_m.time
        if self.isCloseChat == True:
            return
        s = '对手 %s
%s' % (messageTime, string)
        if messagetype == 1:  # 文字信息
            self.insertChatMessage(s)

    def cmd14(self, data):
        s_u_i = base_pb2.server_user_infor()
        s_u_i.ParseFromString(data)
        self.userGameInfor = s_u_i
        s = """昵称:%-5s\n积分：%-5s  级别：%-5s\n总盘数：%-4s 游戏币：%-5s\n胜：%-5s 负：%-5s平：%-5s
        """ % (
            s_u_i.name, s_u_i.integral, s_u_i.level, s_u_i.numsGame, s_u_i.gameCurrency, s_u_i.win, s_u_i.lose,
            s_u_i.draw)
        img_src = r'source\avatar\%s.png' % s_u_i.avatar
        img = QPixmap(img_src)
        if s_u_i.code == 1:
            self.label_11.setPixmap(img)
            self.label_11.setFixedSize(img.size())
            self.label_11.setScaledContents(True)
            self.label_3.setText(s)
        elif s_u_i.code == 2:
            self.label_12.setPixmap(img)
            self.label_12.setFixedSize(img.size())
            self.label_12.setScaledContents(True)
            self.label_4.setText(s)

    def cmd15(self, data):  # 和棋
        w_w = base_pb2.whoWin()
        w_w.ParseFromString(data)
        if w_w.code == 2:
            self.showResult(self.myColor)
        elif w_w.code == 3:
            reply = QMessageBox.question(self, '提示', '对方请求和棋', QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                w_w.code = 4
                self.showResult(self.myColor)
            else:
                w_w.code = 5
            self.writeData(w_w.SerializeToString())
        elif w_w.code == 4:
            self.showResult(self.myColor)

        elif w_w.code == 5:
            reply = QMessageBox.question(self, '提示', '对方不同意和棋', QMessageBox.Yes)
            if reply == QMessageBox.Yes:
                return

    def cmd16(self, data):  # 悔棋
        w_d = base_pb2.withDraw()
        w_d.ParseFromString(data)
        requestRes = base_pb2.responseResources()
        requestRes.cmd = 17
        reply = QMessageBox.question(self, '提示', '对方请求悔棋', QMessageBox.Yes | QMessageBox.No)
        if reply == QMessageBox.Yes:
            requestRes.code = 3
            if w_d.nums == 1:
                self.huiBack()
                self.isCanXia = False
                self.label.setCursor(QCursor(Qt.ForbiddenCursor))
            elif w_d.nums == 2:
                self.huiBack()
                self.huiBack()
        else:
            requestRes.code = 4
        self.writeData(requestRes.SerializeToString())

    def cmd18(self, data):
        resResources = base_pb2.responseResources()
        resResources.ParseFromString(data)
        if resResources.code == 1:
            self.progressDialog.close()
            if resResources.code2 == 1:
                self.myColor = 'white'
                self.isCanXia = False
                self.label.setCursor(QCursor(Qt.PointingHandCursor))
            elif resResources.code2 == 2:
                self.myColor = 'black'
                self.isCanXia = True
                self.label.setCursor(QCursor(Qt.ForbiddenCursor))
            self.isBeginGame = True
            self.isMatch = False
            self.initBeginGame()
        elif resResources.code == 2:
            if resResources.code2 == 1:
                if self.isCanXia:  # 自己回合
                    self.huiBack()
                    self.huiBack()
                else:
                    self.huiBack()
                    self.isCanXia = True
                    self.label.setCursor(QCursor(Qt.PointingHandCursor))
            elif resResources.code2 == 2:
                QMessageBox.information(self, '提示', '对方不同意悔棋', QMessageBox.Ok)

    def insertChatMessage(self, s):
        self.textBrowser.append(s)
        self.textBrowser.moveCursor(QTextCursor.End)
        self.comboBox.lineEdit().clear()

    def listViewClicked(self, qModelIndex):
        if self.isBeginGame == True:
            QMessageBox.information(self, '警告', '您正在游戏中', QMessageBox.Ok)
            return
        self.withusername = self.peopleList[qModelIndex.row()]
        print(self.withusername)
        c_c_g = base_pb2.client_create_game()
        c_c_g.cmd = 8
        c_c_g.withUsername = self.withusername
        self.writeData(c_c_g.SerializeToString())

        # 创建一个问答框，注意是Question
        self.fightBox = QMessageBox(QMessageBox.Question, '对局请求中', '正在等待对方响应中...', parent=self)
        # 添加按钮，可用中文
        cancel = self.fightBox.addButton('取消对局', QMessageBox.YesRole)

        # 设置消息框中内容前面的图标
        self.fightBox.setIcon(1)

        # 显示该问答框
        self.fightBox.show()
        if self.fightBox.clickedButton() == cancel:
            self.fightBox.close()

    def sendGameRes(self, code, win):
        res = base_pb2.whoWin()
        res.cmd = 15
        res.code = code
        res.win = win
        self.writeData(res.SerializeToString())

    def messageSend(self):
        if self.comboBox.lineEdit().text() == "":
            return
        if self.isCloseChat == True:
            s = '您已开启消息屏蔽功能，暂时无法发送消息，如有需要请在设置面板中开启'
            self.insertChatMessage(s)
            return
        c_m = base_pb2.chatMessage()
        c_m.cmd = 13
        c_m.type = 1
        c_m.time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        c_m.data = self.comboBox.lineEdit().text()
        s = '我 %s
%s' % (
            c_m.time, self.comboBox.lineEdit().text())
        self.insertChatMessage(s)
        self.writeData(c_m.SerializeToString())

    def soundSetting(self, state):
        if state == 2:  # 点击，关闭
            self.sound_piece = QSound('')
            self.sound_win = QSound('')
            self.sound_defeated = QSound('')
        else:
            self.sound_piece = self.sound_piece_t
            self.sound_win = self.sound_win_t
            self.sound_defeated = self.sound_defeated_t

    def chatSetting(self, state):
        if state == 2:  # 点击，关闭
            self.isCloseChat = True
            s = '您已开启消息屏蔽功能'
        else:
            self.isCloseChat = False
            s = '您已取消消息屏蔽功能'
        self.insertChatMessage(s)

    def getPoint(self):
        '''
        返回落子位置
        :return:
        '''
        # 简单实现：返回一个空白交点
        # for i in range(19):
        #     for j in range(19):
        #         if self.chessboard[i][j] == None:
        #             return QPoint(j, i)
        #
        #  没有找到合适的点
        white_score = [[0 for i in range(15)] for j in range(15)]
        black_score = [[0 for i in range(15)] for j in range(15)]

        for i in range(15):
            for j in range(15):
                if self.chessboard[i][j] != None:
                    continue
                # 模拟落子
                self.chessboard[i][j] = Chessman(color='white', parent=self)
                white_score[i][j] = self.getPointScore(j, i, 'white')
                self.chessboard[i][j].close()
                self.chessboard[i][j] = None
                self.chessboard[i][j] = Chessman(color='black', parent=self)
                black_score[i][j] = self.getPointScore(j, i, 'black')
                self.chessboard[i][j].close()
                self.chessboard[i][j] = None
        # 将二维坐标转换成以为进行计算
        r_white_score = []
        r_black_score = []
        for i in white_score:
            r_white_score.extend(i)
        for i in black_score:
            r_black_score.extend(i)

        # 找到分数最大值
        score = [max(x, y) for x, y in zip(r_white_score, r_black_score)]

        # 找到分数做大的下标
        chess_index = score.index(max(score))

        # print(score, '\n', max(score))

        y = chess_index // 15
        x = chess_index % 15

        return QPoint(x, y)

    def aiBegin(self):
        self.isBeginGame = True
        self.isCanXia = True
        self.myColor = "black"
        self.ai = True
        self.initBeginGame()
        s_u_i = base_pb2.server_user_infor()
        s_u_i.code = 2
        s_u_i.name, s_u_i.integral, s_u_i.level, s_u_i.numsGame, s_u_i.gameCurrency, s_u_i.win, s_u_i.lose, s_u_i.draw, s_u_i.avatar = (
            '电脑', 9999, 9999, 9999, 9999, 9999, 0, 0, '1')
        self.cmd14(s_u_i.SerializeToString())

    def autoDown(self):
        point = self.getPoint()
        s_g_p = base_pb2.server_gobang_position()
        s_g_p.x = point.x()
        s_g_p.y = point.y()
        self.cmd7(s_g_p.SerializeToString())

    def getPointScore(self, x, y, color):
        '''
        返回每个点的得分
        y:行坐标
        x:列坐标
        color：棋子颜色
        :return:
        '''
        # 分别计算点周围5子以内，空白、和同色的分数
        blank_score = 0
        color_score = 0

        # 记录每个方向的棋子分数
        blank_score_plus = [0, 0, 0, 0]  # 横向 纵向 正斜线 反斜线
        color_score_plus = [0, 0, 0, 0]

        # 横线
        # 右侧
        i = x  # 横坐标
        j = y  # 纵坐标
        while i < 19:
            if self.chessboard[j][i] is None:
                blank_score += 1
                blank_score_plus[0] += 1
                break
            elif self.chessboard[j][i].color == color:
                color_score += 1
                color_score_plus[0] += 1
            else:
                break
            if i >= x + 4:
                break
            i += 1
        # print('123123')
        # 左侧
        i = x  # 横坐标
        j = y  # 纵坐标
        while i >= 0:
            if self.chessboard[j][i] is None:
                blank_score += 1
                blank_score_plus[0] += 1
                break
            elif self.chessboard[j][i].color == color:
                color_score += 1
                color_score_plus[0] += 1
            else:
                break
            if i <= x - 4:
                break
            i -= 1

        # 竖线
        # 上方
        i = x  # 横坐标
        j = y  # 纵坐标
        while j >= 0:
            if self.chessboard[j][i] is None:
                blank_score += 1
                blank_score_plus[1] += 1
                break
            elif self.chessboard[j][i].color == color:
                color_score += 1
                color_score_plus[1] += 1
            else:
                break
            if j <= y - 4:
                break
            j -= 1
        # 竖线
        # 下方
        i = x  # 横坐标
        j = y  # 纵坐标
        while j < 19:
            if self.chessboard[j][i] is None:
                blank_score += 1
                blank_score_plus[1] += 1
                break
            elif self.chessboard[j][i].color == color:
                color_score += 1
                color_score_plus[1] += 1
            else:
                break

            if j >= y + 4:  # 最近五个点
                break
            j += 1
        # 正斜线
        # 右上
        i = x
        j = y
        while i < 19 and j >= 0:
            if self.chessboard[j][i] is None:
                blank_score += 1
                blank_score_plus[2] += 1
                break
            elif self.chessboard[j][i].color == color:
                color_score += 1
                color_score_plus[2] += 1
            else:
                break

            if i >= x + 4:  # 最近五个点
                break
            i += 1
            j -= 1
        # 左下
        i = x
        j = y
        while j < 19 and i >= 0:
            if self.chessboard[j][i] is None:
                blank_score += 1
                blank_score_plus[2] += 1
                break
            elif self.chessboard[j][i].color == color:
                color_score += 1
                color_score_plus[2] += 1
            else:
                break

            if j >= y + 4:  # 最近五个点
                break
            i -= 1
            j += 1
        # 反斜线
        # 左上
        i = x
        j = y
        while i >= 0 and j >= 0:
            if self.chessboard[j][i] is None:
                blank_score += 1
                blank_score_plus[3] += 1
                break
            elif self.chessboard[j][i].color == color:
                color_score += 1
                color_score_plus[3] += 1
            else:
                break
            if i <= x - 4:
                break
            i -= 1
            j -= 1
        # 右上
        i = x
        j = y
        while i < 19 and j < 19:
            if self.chessboard[j][i] is None:
                blank_score += 1
                blank_score_plus[3] += 1
                break
            elif self.chessboard[j][i].color == color:
                color_score += 1
                color_score_plus[3] += 1
            else:
                break
            if i >= x + 4:
                break
            i += 1
            j += 1

        for k in range(4):
            if color_score_plus[k] >= 5:
                return 100

        # color_score *= 5
        return max([x + y for x, y in zip(color_score_plus, blank_score_plus)])


import os
import sys
import time
import wandb
import numpy as np
import torch
from torch.utils.data import DataLoader
from dataloader_from_numpy import *
from my_models import *
from torch.utils.tensorboard import SummaryWriter
from UniqueDraw import UniqueDraw
from utils import *
import torchvision
from mobile_net_v3 import *
from SincNet import SincConv_fast
import torchaudio

def hook_fn(module, input, output):
    """ Store the output of the hook """
    global hooked_output
    hooked_output = output

def compute_EER(sim_matrix):
    """
    Compute EER, FAR, FRR and the threshold at which EER occurs.

    Args:
    - sim_matrix (torch.Tensor): A similarity matrix of shape
      (num of speakers, num of utterances, num of speakers).

    Returns:
    - EER (float): Equal error rate.
    - threshold (float): The threshold at which EER occurs.
    - FAR (float): False acceptance rate at EER.
    - FRR (float): False rejection rate at EER.
    """
    num_of_speakers, num_of_utters, _ = sim_matrix.shape

    # Initialize values
    diff = float('inf')
    EER = 0.0
    threshold = 0.0
    EER_FAR = 0.0
    EER_FRR = 0.0

    # Iterate over potential thresholds
    for thres in torch.linspace(0.5, 1.0, 501):
        sim_matrix_thresh = sim_matrix > thres

        # Compute FAR and FRR
        FAR = sum([(sim_matrix_thresh[i].sum() - sim_matrix_thresh[i, :, i].sum()).float()
                    for i in range(num_of_speakers)]) / (num_of_speakers - 1.0) / (num_of_utters) / num_of_speakers

        FRR = sum([(num_of_utters - sim_matrix_thresh[i, :, i].sum()).float()
                   for i in range(num_of_speakers)]) / (num_of_utters) / num_of_speakers

        # Update if this is the closest FAR and FRR we've seen so far
        if diff > abs(FAR - FRR):
            diff = abs(FAR - FRR)
            EER = ((FAR + FRR) / 2).item()
            threshold = thres.item()
            EER_FAR = FAR.item()
            EER_FRR = FRR.item()

    return EER, threshold, EER_FAR, EER_FRR

def train_and_test_model(device, models, loss_func, data_set, optimizer, train_batch_size, test_batch_size,
                         n_fft=512, hop_length=256, win_length=512, window_fn = torch.hann_window, power=None,
                         num_epochs=2000, train_ratio=0.8, contain_sicnet=False, contain_2_extractor=False):
    # writer = SummaryWriter()
    # number of user for train and test in each epoch. Train 4 users, test 2 users.
    data_size = len(data_set)
    train_size = int(data_size * train_ratio)
    test_size = data_size - train_size
    train_tmp_set, test_tmp_set = torch.utils.data.random_split(data_set, [train_size, test_size])
    train_loader = DataLoader(train_tmp_set, batch_size=train_batch_size, shuffle=True, drop_last=True)
    test_loader = DataLoader(test_tmp_set, batch_size=test_batch_size, shuffle=True, drop_last=True)

    if contain_2_extractor:
        sinc_a, sinc_p, extractor_a, extractor_p = models
    else:
        sinc_a, sinc_p, extractor = models

    # functions for STFT matrix calculation
    spectrogram = torchaudio.transforms.Spectrogram(
                                                    n_fft=n_fft,
                                                    win_length=win_length,
                                                    hop_length=hop_length,
                                                    window_fn=window_fn,
                                                    power=power,  # For power spectrogram, use 2. For complex spectrogram, use None.
                                                    # batch_first=True,
                                                    # sample_rate=16000
                                                ).to(device)
    amplitude_to_db = torchaudio.transforms.AmplitudeToDB(stype='magnitude', top_db=80).to(device)

    for epoch in range(num_epochs):
        print(f'Epoch {epoch + 1}/{num_epochs}')
        print('-' * 10)

        # train and test model
        for phase in ['train', 'test']:
            if phase == 'train':
                # set model to training
                sinc_a.train()
                sinc_p.train()
                if contain_2_extractor:
                    extractor_a.train()
                    extractor_p.train()
                else:
                    extractor.train()
                dataloader = train_loader
            else:
                # set model to test
                sinc_a.eval()
                sinc_p.eval()
                if contain_2_extractor:
                    extractor_a.eval()
                    extractor_p.eval()
                else:
                    extractor.eval()
                dataloader = test_loader

            # train each batch
            loss_avg_batch_all = 0.0
            loss_avg_batch_all_piezo = 0.0
            loss_avg_batch_all_audio = 0.0
            acc_audio = 0.0
            acc_piezo = 0.0

            for batch_id, (piezo_clips, audio_clips, ids) in enumerate(dataloader):
                # get shape of input
                batch_size, _ = piezo_clips.shape

                with torch.set_grad_enabled(phase == 'train') and torch.autograd.set_detect_anomaly(True):

                    if phase == 'train':
                        # training
                        # (batch_size, samples) (8000 raw audio signal, 16000 sample rate)
                        # process data using sincnet
                        # (batch_size, samples) -> (batch_size, sinc_out_channel, samples)
                        piezo_clips = piezo_clips.unsqueeze(1).to(device)
                        audio_clips = audio_clips.unsqueeze(1).to(device)
                        if contain_sicnet:
                            piezo_clips = sinc_p(piezo_clips)
                            # audio_clips = sinc_a(audio_clips)
                        _, channel_clip, _ = piezo_clips.shape
                        piezo_clips.contiguous()
                        piezo_clips = piezo_clips.view(batch_size * channel_clip, -1)
                        audio_clips.contiguous()
                        audio_clips = audio_clips.view(batch_size * 1, -1) # changed to 1

                        piezos = amplitude_to_db(spectrogram(piezo_clips).abs())
                        _, f_len, t_len = piezos.shape
                        audios = amplitude_to_db(spectrogram(audio_clips).abs())
                        _, f_len_a, t_len_a = audios.shape

                        # audio-based normalization
                        # piezos, audios = normalize_tensors_based_audio(tensor_p=piezos, tensor_a=audios)
                        #reshape
                        piezos.contiguous()
                        piezos = piezos.view(batch_size, channel_clip, f_len, t_len)
                        audios.contiguous()
                        audios = audios.view(batch_size, 1, f_len_a, t_len_a) ## changed to 1

                        if contain_2_extractor:
                            pred_ids_piezo = extractor_p(piezos)
                            pred_ids_audio = extractor_a(audios)
                        else:
                            pred_ids_piezo = extractor(piezos)
                            pred_ids_audio = extractor(audios)

                        ids_gpu = ids.to(device)
                        loss_class_piezo = loss_func(pred_ids_piezo, ids_gpu)
                        loss_class_audio = loss_func(pred_ids_audio, ids_gpu)
                        acc_audio += np.sum(np.argmax(pred_ids_audio.cpu().data.numpy(), axis=1) == ids_gpu.cpu().data.numpy())
                        acc_piezo += np.sum(np.argmax(pred_ids_piezo.cpu().data.numpy(), axis=1) == ids_gpu.cpu().data.numpy())
                        loss_extractor = loss_class_piezo + loss_class_audio
                        loss_avg_batch_all += loss_extractor.item()
                        loss_avg_batch_all_audio += loss_class_audio.item()
                        loss_avg_batch_all_piezo += loss_class_piezo.item()

                        optimizer.zero_grad()
                        loss_extractor.backward()
                        torch.nn.utils.clip_grad_norm_(sinc_a.parameters(), 10.0)
                        torch.nn.utils.clip_grad_norm_(sinc_p.parameters(), 10.0)
                        if contain_2_extractor:
                            torch.nn.utils.clip_grad_norm_(extractor_p.parameters(), 10.0)
                            torch.nn.utils.clip_grad_norm_(extractor_a.parameters(), 10.0)
                        else:
                            torch.nn.utils.clip_grad_norm_(extractor.parameters(), 10.0)
                        optimizer.step()

                    if phase == 'test':
                        # tesing
                        # (batch_size, samples) (8000 raw audio signal, 16000 sample rate)
                        # process data using sincnet
                        # (batch_size, samples) -> (batch_size, sinc_out_channel, samples)
                        piezo_clips = piezo_clips.unsqueeze(1).to(device)
                        audio_clips = audio_clips.unsqueeze(1).to(device)
                        if contain_sicnet:
                            piezo_clips = sinc_p(piezo_clips)
                            # audio_clips = sinc_a(audio_clips)
                        _, channel_clip, _ = piezo_clips.shape
                        piezo_clips.contiguous()
                        piezo_clips = piezo_clips.view(batch_size * channel_clip, -1)
                        audio_clips.contiguous()
                        audio_clips = audio_clips.view(batch_size * 1, -1)

                        piezos = amplitude_to_db(spectrogram(piezo_clips).abs())
                        _, f_len, t_len = piezos.shape
                        audios = amplitude_to_db(spectrogram(audio_clips).abs())
                        _, f_len_a, t_len_a = audios.shape

                        # audio-based normalization
                        # piezos, audios = normalize_tensors_based_audio(tensor_p=piezos, tensor_a=audios)
                        #reshape
                        piezos.contiguous()
                        piezos = piezos.view(batch_size, channel_clip, f_len, t_len)
                        audios.contiguous()
                        audios = audios.view(batch_size, 1, f_len_a, t_len_a)

                        if contain_2_extractor:
                            pred_ids_piezo = extractor_p(piezos)
                            pred_ids_audio = extractor_a(audios)
                        else:
                            pred_ids_piezo = extractor(piezos)
                            pred_ids_audio = extractor(audios)

                        ids_gpu = ids.to(device)
                        loss_class_piezo = loss_func(pred_ids_piezo, ids_gpu)
                        loss_class_audio = loss_func(pred_ids_audio, ids_gpu)
                        acc_audio += np.sum(np.argmax(pred_ids_audio.cpu().data.numpy(), axis=1) == ids_gpu.cpu().data.numpy())
                        acc_piezo += np.sum(np.argmax(pred_ids_piezo.cpu().data.numpy(), axis=1) == ids_gpu.cpu().data.numpy())
                        loss_extractor = loss_class_piezo + loss_class_audio
                        loss_avg_batch_all += loss_extractor.item()
                        loss_avg_batch_all_audio += loss_class_audio.item()
                        loss_avg_batch_all_piezo += loss_class_piezo.item()

            epoch_loss_all = loss_avg_batch_all / len(dataloader)
            epoch_loss_audio = loss_avg_batch_all_audio / len(dataloader)
            epoch_loss_piezo = loss_avg_batch_all_piezo / len(dataloader)

            if phase == 'train':
                epoch_acc_audio = acc_audio / (len(dataloader) * train_batch_size)
                epoch_acc_piezo = acc_piezo / (len(dataloader) * train_batch_size)
                print(f'{phase} Loss Extractor: {epoch_loss_all:.4f}')
                print(f'{phase} Acc Audio: {epoch_acc_audio:.4f}')
                print(f'{phase} Acc Piezo: {epoch_acc_piezo:.4f}')
                wandb.log({'epoch': epoch, f'Loss/{phase}_all': epoch_loss_all})
                wandb.log({'epoch': epoch, f'Loss/{phase}_audio': epoch_loss_audio})
                wandb.log({'epoch': epoch, f'Loss/{phase}_piezo': epoch_loss_piezo})

                wandb.log({'epoch': epoch, f'Acc/{phase}_audio': epoch_acc_audio})
                wandb.log({'epoch': epoch, f'Acc/{phase}_piezo': epoch_acc_piezo})
            if phase == 'test':
                epoch_acc_audio = acc_audio / (len(dataloader) * test_batch_size)
                epoch_acc_piezo = acc_piezo / (len(dataloader) * test_batch_size)
                print(f'{phase} Loss Extractor: {epoch_loss_all:.4f}')
                print(f'{phase} Acc Audio: {epoch_acc_audio:.4f}')
                print(f'{phase} Acc Piezo: {epoch_acc_piezo:.4f}')
                wandb.log({'epoch': epoch, f'Loss/{phase}_extractor': epoch_loss_all})
                wandb.log({'epoch': epoch, f'Loss/{phase}_audio': epoch_loss_audio})
                wandb.log({'epoch': epoch, f'Loss/{phase}_piezo': epoch_loss_piezo})

                wandb.log({'epoch': epoch, f'Acc/{phase}_audio': epoch_acc_audio})
                wandb.log({'epoch': epoch, f'Acc/{phase}_piezo': epoch_acc_piezo})

    return (sinc_a, sinc_p, extractor)

if __name__ == "__main__":

    device = "cuda:1" if torch.cuda.is_available() else "cpu"

    data_file_dir = './processed_data/wav_clips/piezobuds/' # folder where stores the data for training and test
    pth_store_dir = './pth_model/'
    os.makedirs(pth_store_dir, exist_ok=True)

    # set the params of each train
    # ----------------------------------------------------------------------------------------------------------------
    # Be sure to go through all the params before each run in case the models are saved in wrong folders!
    # ----------------------------------------------------------------------------------------------------------------

    lr = 0.001
    n_user = 51
    train_ratio = 0.8
    num_of_epoches = 300
    train_seperate = False
    sinc_out_channel = 4
    sinc_kernel_size = 512
    contain_sincnet = True
    contain_2_extractor = True
    train_batch_size = 512
    test_batch_size = 128

    n_fft = 512  # Size of FFT, affects the frequency granularity
    hop_length = 256  # Typically n_fft // 4 (is None, then hop_length = n_fft // 2 by default)
    win_length = n_fft  # Typically the same as n_fft
    window_fn = torch.hann_window # Window function

    if contain_sincnet:
        comment = 'sincnet_out_{}_ksize_{}_mobilenetv3small_nfft_{}_hop_{}_classification'.format(sinc_out_channel, sinc_kernel_size, n_fft, hop_length)
    else:
        comment = 'nfft_{}_hop_{}_classification'.format(sinc_out_channel, sinc_kernel_size, n_fft, hop_length)
    if contain_2_extractor:
        comment += '_2_extractor'
    # comment = 'mobilenetv3large1d_960_hop_256_t_16_class_pwr_spec_49u' # simple descriptions of specifications of this model, for example, 't_f' means we use the model which contains time and frequency nn layers

    if contain_sincnet == False:
        sinc_out_channel = 1
    # SincNet initialization
    sinc_p = SincConv_fast(out_channels=sinc_out_channel, kernel_size=sinc_kernel_size).to(device)
    sinc_a = SincConv_fast(out_channels=sinc_out_channel, kernel_size=sinc_kernel_size).to(device)

    # extractor initialization
    if contain_2_extractor == False:
        extractor = torchvision.models.mobilenet_v3_large(pretrained=True)
        extractor.features[0][0] = nn.Conv2d(sinc_out_channel, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
        extractor.classifier[3] = nn.Linear(extractor.classifier[3].in_features, n_user)
        # hook for feature layer output
        hook = extractor.avgpool.register_forward_hook(hook_fn)
        extractor.to(device)
    else:
        extractor_a = torchvision.models.mobilenet_v3_large(pretrained=True)
        extractor_a.features[0][0] = nn.Conv2d(1, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) ## changed to 1s
        extractor_a.classifier[3] = nn.Linear(extractor_a.classifier[3].in_features, n_user)
        # hook for feature layer output
        hook_a = extractor_a.avgpool.register_forward_hook(hook_fn)
        extractor_a.to(device)

        extractor_p = torchvision.models.mobilenet_v3_large(pretrained=True)
        extractor_p.features[0][0] = nn.Conv2d(sinc_out_channel, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
        extractor_p.classifier[3] = nn.Linear(extractor_p.classifier[3].in_features, n_user)
        # hook for feature layer output
        hook = extractor_p.avgpool.register_forward_hook(hook_fn)
        extractor_p.to(device)

    if contain_sincnet:
        if contain_2_extractor:
            optimizer = torch.optim.Adam([
            {'params': extractor_a.parameters()},
            {'params': extractor_p.parameters()},
            {'params': sinc_a.parameters()},
            {'params': sinc_p.parameters()}
        ], lr=lr)
        else:
            optimizer = torch.optim.Adam([
            {'params': extractor.parameters()},
            {'params': sinc_a.parameters()},
            {'params': sinc_p.parameters()}
        ], lr=lr)
    else:
        if contain_2_extractor:
            optimizer = torch.optim.Adam([
            {'params': extractor_a.parameters()},
            {'params': extractor_p.parameters()},
        ], lr=lr)
        else:
            optimizer = torch.optim.Adam([
            {'params': extractor.parameters()},
        ], lr=lr)

    # create the folder to store the model
    model_struct = 'model_' + comment
    # initialize the wandb configuration
    time_stamp = time.strftime("%Y_%m_%d_%H_%M", time.localtime())
    wandb.init(
        # team name
        entity="piezobuds",
        # set the project name
        project="PiezoBuds",
        # params of the task
        name=model_struct+'_'+time_stamp
    )
    model_store_pth = pth_store_dir + model_struct + '/'
    os.makedirs(model_store_pth, exist_ok=True)
    model_final_path = model_store_pth + time_stamp + '/'
    os.makedirs(model_final_path, exist_ok=True)

    # load the data
    data_set = WavDataset('./processed_data/wav_clips/piezobuds/', n_user=n_user, is_multi_moda=True)
    print(len(data_set))

    loss_func = nn.CrossEntropyLoss()

    if contain_2_extractor:
        models = (sinc_a, sinc_p, extractor_a, extractor_p)
        (sinc_a, sinc_p, extractor_a, extractor_p) = train_and_test_model(device=device, models=models, loss_func=loss_func, data_set=data_set, optimizer=optimizer,
                                                        train_batch_size=train_batch_size, test_batch_size=test_batch_size, n_fft=n_fft,
                                                        hop_length=hop_length, win_length=win_length, window_fn=window_fn, power=None,
                                                        num_epochs=num_of_epoches, train_ratio=train_ratio, contain_sicnet=True, contain_2_extractor=contain_2_extractor)

        torch.save(extractor_a.state_dict(), model_final_path+'extractor_a.pth')
        torch.save(extractor_a.state_dict(), model_final_path+'extractor_p.pth')
        torch.save(sinc_a.state_dict(), model_final_path+'sinc_a.pth')
        torch.save(sinc_p.state_dict(), model_final_path+'sinc_p.pth')
    else:
        models = (sinc_a, sinc_p, extractor)
        (sinc_a, sinc_p, extractor) = train_and_test_model(device=device, models=models, loss_func=loss_func, data_set=data_set, optimizer=optimizer,
                                                        train_batch_size=train_batch_size, test_batch_size=test_batch_size, n_fft=n_fft,
                                                        hop_length=hop_length, win_length=win_length, window_fn=window_fn, power=None,
                                                        num_epochs=num_of_epoches, train_ratio=train_ratio, contain_sicnet=True, contain_2_extractor=contain_2_extractor)

        torch.save(extractor.state_dict(), model_final_path+'extractor.pth')
        torch.save(sinc_a.state_dict(), model_final_path+'sinc_a.pth')
        torch.save(sinc_p.state_dict(), model_final_path+'sinc_p.pth')


from work import config

class BasicConverter:
    """Basic class to configure default parameters"""

    _d = config.default_font.split('.')
    defaults = {
        'font': _d[0],
        'size': _d[1],
        'bold': str(int(_d[2] != 'normal')),
        'italic': str(int(_d[3] != 'roman')),
        'underline': _d[4],
        'overstrike': _d[5],
        'f_color': config.default_foreground_color,
        'b_color': config.default_background_color
    }

class Parser(BasicConverter):
    """Class to convert text from file into character list"""

    d_font = config.default_font
    d_f_color = config.default_foreground_color
    d_b_color = config.default_background_color

    parser_tags = {
        'f': ['font', config.families],
        's': ['size', config.sizes],
        'b': ['bold', config.weights],
        'i': ['italic', config.slants],
        'u': ['underline', config.underlines],
        'o': ['overstrike', config.overstrikes],
        'cb': ['b_color', config.colors],
        'cf': ['f_color', config.colors]
    }

    def params_to_tags(self, params: dict):
        """Convert parameters dict into set of string tags"""
        _params = dict(params)
        tags = set()
        _params['bold'] = 'bold' if _params['bold'] == '1' else 'normal'
        _params['italic'] = 'italic' if _params['italic'] == '1' else 'roman'
        font = '.'.join(map(str, [_params[k] for k in list(_params)[:-2]]))
        if font != self.d_font:
            tags.add(font)
        if _params['f_color'] != self.d_f_color:
            tags.add(_params['f_color'] + '_fore')
        if _params['b_color'] != self.d_b_color:
            tags.add(_params['b_color'] + '_back')
        return tags

    def update_params(self, tag: str, params: dict):
        """Change character parameters according to given tag"""
        try:
            key, value = tag.split(':')
            if key == 'b':
                if value == '1':
                    v = 'bold'
                elif value == '0':
                    v = 'normal'
                else:
                    v = 'wrong'
            elif key == 'i':
                if value == '1':
                    v = 'italic'
                elif value == '0':
                    v = 'roman'
                else:
                    v = 'wrong'
            else:
                v = value
            param, variants = self.parser_tags[key]
            if v not in variants:
                return 0
            params[param] = value
        except (KeyError, ValueError) as e:
            return 0
        return 1

    def parse(self, text: str):
        """Convert text into character list"""
        char_list = []
        params = dict(self.defaults)
        i, j = 1, 0
        k = 0
        while k < len(text):
            if text[k] == '<':
                if text[k + 1] == '<' or text[k + 1] == '>':
                    char_el = {
                        'char': text[k + 1],
                        'index': str(i) + '.' + str(j),
                        'tags': self.params_to_tags(params)
                    }
                    char_list.append(char_el)
                    k += 1
                    j += 1
                else:
                    m = k + 1
                    while text[k] != '>':
                        k += 1
                    if not self.update_params(text[m: k], params):
                        return None
            elif text[k] == '>':
                if k == 0 or text[k - 1] != '<':
                    return None
            else:
                char = text[k]
                char_el = {
                    'char': char,
                    'index': str(i) + '.' + str(j),
                    'tags': self.params_to_tags(params)
                }
                char_list.append(char_el)
                j += 1
                if char == '\n':
                    i += 1
                    j = 0
            k += 1
        return char_list

    def parse_from_txt(self, text: str):
        """Convert text from txt files into character list"""
        char_list = []
        tags = self.params_to_tags(self.defaults)
        i, j = 1, 0
        for char in text:
            char_el = {
                'char': char,
                'index': str(i) + '.' + str(j),
                'tags': tags
            }
            char_list.append(char_el)
            j += 1
            if char == '\n':
                i += 1
                j = 0
        return char_list

class Serializer(BasicConverter):
    """Class to convert character list into text for file"""

    serializer_tags = {
        'font': '<f:%s>',
        'size': '<s:%s>',
        'bold': '<b:%s>',
        'italic': '<i:%s>',
        'underline': '<u:%s>',
        'overstrike': '<o:%s>',
        'b_color': '<cb:%s>',
        'f_color': '<cf:%s>'
    }

    def tags_to_params(self, tags: set):
        """Convert set of string tags into parameters dict"""
        params = dict(self.defaults)
        for tag in tags:
            if tag == 'sel':
                continue
            elif tag.startswith('#'):
                color, pos = tag.split('_')
                if pos == 'fore':
                    params['f_color'] = color
                else:
                    params['b_color'] = color
            else:
                p = tag.split('.')
                params['font'] = p[0]
                params['size'] = p[1]
                params['bold'] = str(int(p[2] != 'normal'))
                params['italic'] = str(int(p[3] != 'roman'))
                params['underline'] = p[4]
                params['overstrike'] = p[5]
        return params

    def serialize(self, char_list: list):
        """Convert character list into text"""
        cur_params = self.tags_to_params(set())
        text = []
        for char_el in char_list:
            char_params = self.tags_to_params(char_el['tags'])
            for param in char_params:
                if char_params[param] != cur_params[param]:
                    cur_params[param] = char_params[param]
                    text.append(self.serializer_tags[param] % char_params[param])
            char = char_el['char']
            if char == '<' or char == '>':
                text.append('<')
            text.append(char)
        text = ''.join(text[:-1])
        return text

    def serialize_to_txt(self, char_list: list):
        """Convert character list into text for txt files"""
        text = ''.join([char['char'] for char in char_list])
        return text


npm = []
nama = []
uts = []
uas = []
absen = []
tugas = []
akhir = []

jumlahData = int(input('Jumlah Data: '))

for i in range(0,jumlahData):
    print('\nInput Data ke -',i+1)
    npmSiswa = str(input('NPM: '))
    namaSiswa = str(input('Nama: '))
    nilaiUts = float(input('Nilai UTS: '))
    nilaiUas = float(input('Nilai UAS: '))
    nilaiAbsen = float(input('Nilai Absen: '))
    nilaiTugas = float(input('Nilai Tugas: '))
    npm.append(npmSiswa)
    nama.append(namaSiswa)
    uts.append(nilaiUts)
    uas.append(nilaiUas)
    absen.append(nilaiAbsen)
    tugas.append(nilaiTugas)
    nilaiAkhir = (0.3*nilaiUts) + (0.4*nilaiUts) + (0.3*nilaiUas) + (0.1*nilaiAbsen) + (0.2*nilaiTugas)

print('\nNPM','\t','Nama','\t','UTS','\t','UAS','\t','ABSEN','\t','TUGAS','\t','AKHIR')

for i in range(0, jumlahData):
    print(i)
    print(jumlahData)
    print(npm[i],'\t',nama[i],'\t',uts[i],'\t',uas[i],'\t',absen[i],'\t',tugas[i],'\t',akhir[i])


from fileinput import filename
import socket
from tqdm import tqdm
import os

def main():
    s = socket.socket()
    s.bind((socket.gethostname(), 3339))
    s.listen(10)

    conn, address = s.accept()
    data = conn.recv(1024).decode("utf-8")
    print(data)

    item = data.split("_")
    filename = item[0]
    filesize = int(item[1])

    print("filename and filesize recieved")
    conn.send("filename and filesize recieved".encode("utf-8"))

    bar = tqdm(range(filesize), f"receiving {filename}", unit="B", unit_scale=True, unit_divisor=1024)

    with open(f"recv_{filename}", 'w') as f:
        while True:
            data = conn.recv(1024).decode("utf-8")
            if not data:
                break
            f.write(data)
            bar.update(len(data))
    conn.close()
    s.close()

if __name__ == "__main__":
    main()


# import math module to use for rounding up.
import math

# Create a function to do the matchs of working out the number of paint cans needed for a wall.

def paint_calc(height, width, cover):
    total_cans_needed = math.ceil((height*width) / cover)
    print(f"The total cans of paint needed are: {total_cans_needed}")

# Request the size of the wall:

test_h = int(input("Height of wall: "))
test_w = int(input("Width of wall: "))

# The volume of paint per can:
coverage = 5

# Call the function:
paint_calc(height=test_h, width=test_w, cover=coverage)


from pyspark import SparkConf
from pyspark.context import SparkContext
from pyspark.mllib.recommendation import ALS, Rating
import random
import itertools
import csv
import time
import math
import sys

start = time.time()
sc = SparkContext.getOrCreate(SparkConf().setMaster("local[*]"))

train_file_path = str(sys.argv[1])
test_file_path = str(sys.argv[2])
case_id = int(sys.argv[3])
output_file = str(sys.argv[4])

trainfile = sc.textFile(train_file_path)
trainfile = trainfile.map(lambda row: row.split(",")).filter(lambda a: a[0]!= "user_id")

testfile = sc.textFile(test_file_path)
testfile = testfile.map(lambda row: row.split(",")).filter(lambda a: a[0]!= "user_id")

def normalize(x):
    prediction = x[1][1]
    if(prediction < 1):
        prediction = 1.0
    elif(prediction > 5):
        prediction = 5
    y = [x[0], (x[1][0], prediction)]
    return y

def find_weights(tuples):
    active_user = tuples[0]
    active_business = tuples[1]
    other_users = tuples[2]

    user_weights = [] #Weights between active user and all other users
    active_user_businesses = businesses_per_user[active_user]

    for eachUser in other_users:
        businesses_for_user_i = businesses_per_user[eachUser]
        common = active_user_businesses.intersection(businesses_for_user_i)
        if len(common) == 0:
            user_weights.append([eachUser, 0, user_business_rating[eachUser][active_business]])
            continue

        ratings_active_user = []
        for eachBusiness in common:
            ratings_active_user.append(user_business_rating[active_user][eachBusiness])

        ratings_other_user = []
        for eachBusiness in common:
            ratings_other_user.append(user_business_rating[eachUser][eachBusiness])

        average_rating_active_user = sum(ratings_active_user)/ len(ratings_active_user)
        average_rating_other_user = sum(ratings_other_user)/ len(ratings_other_user)

        normalised_rating_active_user =[]
        for eachRating in ratings_active_user:
            normalised_rating_active_user.append(eachRating-average_rating_active_user)

        normalised_rating_other_user =[]
        for eachRating in ratings_other_user:
            normalised_rating_other_user.append(eachRating-average_rating_other_user)

        den1=[]
        for eachRating in normalised_rating_active_user:
            den1.append(eachRating*eachRating)

        den= pow(sum(den1),0.5)

        den2=[]
        for eachRating in normalised_rating_other_user:
            den2.append(eachRating*eachRating)

        den = den * pow(sum(den2),0.5)

        num1=[]
        for i in range(len(normalised_rating_other_user)):
            num1.append(normalised_rating_active_user[i] - normalised_rating_other_user[i])
        num = sum(num1)

        weight = 0
        if(den!=0):
            weight = round((num/den),2)

        user_weights.append([eachUser, weight, user_business_rating[eachUser][active_business]])

    return [(active_user, active_business), user_weights]

def find_P(tuples):
    active_user = tuples[0][0]
    active_business = tuples[0][1]
    weights = tuples[1]

    average_active_user = (user_avg_dict[active_user][0]) / user_avg_dict[active_user][1]

    num = 0
    den = 0
    for eachWeight in weights:
        average_for_this_user = user_avg_dict[eachWeight[0]][0]
        rating_for_this_user = user_business_rating[eachWeight[0]][active_business]

        temp = (average_for_this_user - rating_for_this_user) / user_avg_dict[eachWeight[0]][1] - 1
        num += eachWeight[1] * (eachWeight[2] - temp)
        den += abs(eachWeight[1])

    if(den != 0):
        prediction = average_active_user + num / den
    else:
        prediction = average_active_user

    return [(active_user), {active_business: prediction}]

def find_weights_lsh(tuples):

    active_user = tuples[0]
    active_business = tuples[1]
    other_businesses = tuples[2]

    if(active_business in candidates):
        other_businesses = candidates[active_business]

    business_weights = []
    users_for_active_business = users_per_business[active_business]

    sum_rating_active_business =[]
    for eachUser in users_for_active_business:
        sum_rating_active_business.append(user_business_rating[active_business][eachUser])

    average_rating_active_business = sum(sum_rating_active_business) / len(sum_rating_active_business)

    for eachBusiness in other_businesses:
        users_for_this_business = users_per_business[eachBusiness]
        common_users = users_for_this_business.intersection(users_for_active_business)

        if len(common_users) == 0:
            if(active_user in user_business_rating[eachBusiness]):
                business_weights.append([eachBusiness, 0, user_business_rating[eachBusiness][active_user]])
            else:
                business_weights.append([eachBusiness, 0, business_avg_dict[eachBusiness][0]/business_avg_dict[eachBusiness][1]])
            continue

        active_business_ratings =[]
        for eachUser in common_users:
            active_business_ratings.append(user_business_rating[active_business][eachUser])

        other_business_ratings =[]
        for eachUser in common_users:
            other_business_ratings.append(user_business_rating[eachBusiness][eachUser])

        other_business_average_rating_list =[]
        for eachUser in users_for_this_business:
            other_business_average_rating_list.append(user_business_rating[eachBusiness][eachUser])

        other_business_average_rating = sum(other_business_average_rating_list) / len(other_business_average_rating_list)

        normalised_rating_active_business =[]
        for eachRating in active_business_ratings:
            normalised_rating_active_business.append(eachRating - average_rating_active_business)

        normalised_rating_other_business =[]
        for eachRating in other_business_ratings:
            normalised_rating_other_business.append(eachRating - other_business_average_rating)

        den1=[]
        for eachRating in normalised_rating_active_business:
            den1.append(eachRating*eachRating)

        den= pow(sum(den1),0.5)

        den2=[]
        for eachRating in normalised_rating_other_business:
            den2.append(eachRating*eachRating)

        den = den * pow(sum(den2),0.5)

        num1=[]
        for i in range(len(normalised_rating_other_business)):
            num1.append(normalised_rating_active_business[i] - normalised_rating_other_business[i])
        num = sum(num1)

        weight = 0

        if(den!=0):
            weight = num/den

        if (active_user in user_business_rating[eachBusiness]):
            business_weights.append([eachBusiness, weight, user_business_rating[eachBusiness][active_user]])
        else:
            business_weights.append([eachBusiness, weight, business_avg_dict[eachBusiness][0]/float(business_avg_dict[eachBusiness][1])])

    return [(active_user, active_business), business_weights]

def find_P_lsh(tuples):
    active_user = tuples[0][0]
    active_business = tuples[0][1]
    weights = tuples[1]

    num = 0
    den = 0
    for eachWeight in weights:
        if(active_user not in user_business_rating[eachWeight[0]]):
            return [(active_user), {active_business: business_avg_dict[active_business][0]/float(business_avg_dict[active_business][1])}]
        num += eachWeight[1] * eachWeight[2]
        den += abs(eachWeight[1])

    prediction = business_avg_dict[active_business][0]/float(business_avg_dict[active_business][1])

    if (den != 0):
        prediction = num / den

    return [(active_user), {active_business: prediction}]

def Signature_Matrix(businessId_userList):
    businessId_hashedUserList = []
    for i in hashes:
        a = i[0]
        b = i[1]
        p = i[2]
        temp =[]
        for j in businessId_userList[1]:
            temp.append(((a * int(j) + b) % p) % mod)
        businessId_hashedUserList.append(min(temp))
    return businessId_hashedUserList

def DivideInBands(Users_each_business):
    bandedElements =[]
    for i in range(bands):
        bandedElements.append(((i, tuple(Users_each_business[1][i * rows:(i + 1) * rows])), [Users_each_business[0]]))
    return bandedElements

def FindCandidates(bucket):
    allPossiblePairsInBucket = []

    sortedbucket = sorted(list(bucket[1]))

    for i in range(len(bucket[1])):
        for j in range(i + 1, len(bucket[1])):
            allPossiblePairsInBucket.append(((sortedbucket[i], sortedbucket[j]), 1))
    return allPossiblePairsInBucket

if(case_id==1):
    train_users = trainfile.map(lambda x: (x[0], 1)).reduceByKey(lambda x, y: x).map(lambda x: x[0])
    train_businesses = trainfile.map(lambda x: (x[1], 1)).reduceByKey(lambda x, y: x).map(lambda x: x[0])

    test_users = testfile.map(lambda x: (x[0], 1)).reduceByKey(lambda x, y: x).map(lambda x: x[0])
    test_businesses = testfile.map(lambda x: (x[1], 1)).reduceByKey(lambda x, y: x).map(lambda x: x[0])

    all_users = train_users.union(test_users).distinct()
    all_businesses = train_businesses.union(test_businesses).distinct()

    users = all_users.collect()
    businesses = all_businesses.collect()
    business_dict1 = {}
    business_dict2 = {}
    for i, k in enumerate(businesses):
        business_dict1[k] = i
        business_dict2[i] = k

    user_dict1 = {}
    user_dict2 = {}
    for i, k in enumerate(users):
        user_dict1[k] = i
        user_dict2[i] = k

    train_ratings = trainfile.map(lambda x: (user_dict1[x[0]], business_dict1[x[1]], float(x[2])))

    rank = 3
    numIterations = 10
    model = ALS.train(train_ratings, rank, numIterations)

    test_ratings = testfile.map(lambda x: (user_dict1[x[0]], business_dict1[x[1]], x[2]))
    test_data = test_ratings.map(lambda x: (x[0], x[1]))

    prediction = model.predictAll(test_data).map(lambda r: ((r[0], r[1]), r[2]))
    test_ratings_predictions = test_ratings.map(lambda r: ((r[0], r[1]), r[2])).join(prediction)
    test_ratings_predictions = test_ratings_predictions.map(normalize)

    ratings = test_ratings_predictions.collect()

    f = open(output_file, "w")
    f.write("user_id, business_id, prediction\n")

    for i in ratings:
        f.write(user_dict2[i[0][0]] + "," + business_dict2[i[0][1]] + "," + str(i[1][1]) + "\n")

    mean_squared = test_ratings_predictions.map(lambda x: pow(float(x[1][0]) - float(x[1][1]), 2)).mean()
    print("RMSE : " + str(pow(mean_squared, 0.5)))
    end = time.time()
    duration = end - start
    print("Duration : " + str(duration))

elif(case_id==2):
    businesses_rdd = trainfile.map(lambda x: (x[1], 1)).reduceByKey(lambda x, y: x).map(lambda x: x[0])
    businesses = set(businesses_rdd.collect())

    user_business_rating_rdd = trainfile.map(lambda x: (x[0], {x[1]: float(x[2])}))
    user_business_rating_rdd = user_business_rating_rdd.reduceByKey(lambda x, y: {**x, **y})
    user_business_rating = dict(user_business_rating_rdd.collect())

    businesses_per_user_rdd = trainfile.map(lambda row: (row[0], {row[1]})).reduceByKey(lambda x, y: x | y)
    businesses_per_user = dict(businesses_per_user_rdd.collect())

    user_avg = trainfile.map(lambda row: (row[0], (float(row[2]), 1))).reduceByKey(lambda x, y: (x[0] + y[0], x[1] + y[1]))
    user_avg_dict = dict(user_avg.collect())

    users_per_business_rdd = trainfile.map(lambda row: (row[1], {row[0]})).reduceByKey(lambda a, b: a | b)
    users_per_business = dict(users_per_business_rdd.collect())

    test_business = testfile.map(lambda row: (row[0], row[1]))
    test_business_filter = test_business.filter(lambda x: x[1] in businesses)

    test_users = test_business_filter.map(lambda row: (row[0], row[1], users_per_business[row[1]]))

    users_weights = test_users.map(find_weights)

    predicted_ratings_rdd = users_weights.map(find_P).reduceByKey(lambda x, y: {**x, **y})
    predicted_ratings = dict(predicted_ratings_rdd.collect())

    test_user_business_rating_rdd = testfile.map(lambda x: (x[0], {x[1]: float(x[2])})).reduceByKey(lambda x, y: {**x, **y})
    test_user_business_rating = dict(test_user_business_rating_rdd.collect())

    error = 0
    count = 0

    f = open(output_file, "w")
    f.write("user_id, business_id, prediction\n")
    for user in test_user_business_rating:
        for business in test_user_business_rating[user]:
            count += 1
            if user in predicted_ratings and business in predicted_ratings[user]:
                f.write(user + "," + business + "," + str(predicted_ratings[user][business]) + "\n")
                diff = predicted_ratings[user][business] - test_user_business_rating[user][business]
                error += diff * diff
    print("RMSE : ", pow((error / count), 0.5))
    f.close()
    end = time.time()
    duration = end - start
    print("Duration : " + str(duration))

elif(case_id==3):
    Users_Businesses = trainfile.map(lambda row: (row[0],row[1]))
    Users = Users_Businesses.map(lambda a : a[0]).distinct().collect()
    Users.sort()
    Businesses = Users_Businesses.map(lambda a : a[1]).distinct().collect()

    User_dictionary = {}
    for i,k in enumerate(Users):
        User_dictionary[k] = i
    mod = len(User_dictionary)

    Business_dictionary1 = dict()
    for i,k in enumerate(Businesses):
        Business_dictionary1[k] = i

    Users_per_business = Users_Businesses.map(lambda row: (row[1], {User_dictionary[row[0]]})).reduceByKey(lambda x, y: x | y)
    Users_per_business_list = Users_per_business.collect()
    Users_per_business_dict = dict(Users_per_business_list)

    hashes = [[31, 67, 6151], [911, 781, 24593], [14, 23, 769], [387, 552, 98317], [3, 87, 193], [17, 91, 1543],
                  [189, 37, 3079], [14, 53, 6299], [3, 79, 53],
                  [91, 29, 12658], [53, 803, 49157], [8, 109, 389],
                  [443, 487, 899613], [14, 67, 499], [21, 91, 7691],
                  [17, 27, 937], [31, 79, 693],[7, 21, 419], [29, 51, 7691], [19, 19, 11923]]

    signature_matrix_rdd = Users_per_business.map(lambda x: (x[0], Signature_Matrix(x)))

    n = len(hashes)
    bands = 10
    rows = int(n / bands)
    # Dividing into b bands
    candidate_pairs = signature_matrix_rdd.flatMap(DivideInBands).reduceByKey(lambda x, y: x+y).filter(lambda x: len(x[1]) > 1)
    #for x in signature_matrix_rdd.take(2):
    #    print(x)
    #for x in candidate_pairs.take(2):
    #    print(x)
    # Finding all possible pairs
    candidate_pairs = candidate_pairs.flatMap(FindCandidates).reduceByKey(lambda x, y: x).map(lambda x: x[0])
    #candidate_pairs = candidate_pairs.collect()

    User_Business_dict = candidate_pairs.map(lambda x: (x[0], {x[1]}))
    Business_User_dict = candidate_pairs.map(lambda x: (x[1], {x[0]}))

    candidates = User_Business_dict.union(Business_User_dict)
    candidates = dict(candidates.reduceByKey(lambda x, y: x | y).collect())

    business_avg = trainfile.map(lambda x: (x[1], (float(x[2]), 1))).reduceByKey(lambda x, y: (x[0] + y[0], x[1] + y[1]))
    business_avg_dict = dict(business_avg.collect())

    businesses_per_user = Users_Businesses.map(lambda x: (x[0], {x[1]})).reduceByKey(lambda x, y: x | y)
    businesses_per_user = dict(businesses_per_user.collect())

    users_per_business = Users_Businesses.map(lambda x: (x[1], {x[0]})).reduceByKey(lambda x, y: x | y)
    users_per_business = dict(users_per_business.collect())

    user_business_rating_rdd = trainfile.map(lambda x: (x[1], {x[0]: float(x[2])})).reduceByKey(lambda x, y: {**x, **y})
    user_business_rating = dict(user_business_rating_rdd.collect())

    test_business_filter = testfile.map(lambda row: (row[0],row[1])).filter(lambda x: x[1] in Businesses)
    test_businesses = test_business_filter.map(lambda row: (row[0], row[1], businesses_per_user[row[0]]))

    business_weights = test_businesses.map(find_weights_lsh)

    predicted_ratings_rdd = business_weights.map(find_P_lsh).reduceByKey(lambda x, y: {**x, **y})
    predicted_ratings = dict(predicted_ratings_rdd.collect())

    test_user_business_rating_rdd = testfile.map(lambda x: (x[0], {x[1]: float(x[2])})).reduceByKey(lambda x, y: {**x, **y})
    test_user_business_rating = dict(test_user_business_rating_rdd.collect())

    error = 0
    count = 0

    f = open(output_file, "w")
    f.write("user_id, business_id, prediction\n")
    for eachUser in test_user_business_rating:
        for eachBusiness in test_user_business_rating[eachUser]:
            count += 1
            if eachUser in predicted_ratings and eachBusiness in predicted_ratings[eachUser]:
                f.write(eachUser + "," + eachBusiness + "," + str(predicted_ratings[eachUser][eachBusiness]) + "\n")
                diff = predicted_ratings[eachUser][eachBusiness] - test_user_business_rating[eachUser][eachBusiness]
                error += diff * diff

    print("RMSE : " + str(pow((error / count), 0.5)))
    f.close()
    end = time.time()
    duration = end - start
    print("Duration : " + str(duration))


from django.urls import path, re_path
from .views import *

urlpatterns = [
    path('sign_up', sign_up),
    path('sign_in', sign_in),
    re_path('sign_out', sign_out),
    re_path('profile', profile),
    re_path('prof_wish', prof_wish),
    path('ajax_reg', ajax_reg),
    path('sign_up_in', sign_up_in)
]


from tkinter import *
import numpy as np
import os
import matplotlib as mpl
import matplotlib.pyplot as plt
import tkinter.messagebox
from tkinter import filedialog as fd

directory_files = "main/saves/" #saves directory
lst = [] #list user drawing
root = Tk()
root.title('DROWA GRAPHICS 1.0') #title

def savePosn(event):
    global lastx, lasty
    lastx, lasty = event.x, event.y

def addLine(event):
    canvas.create_line((lastx, lasty, event.x, event.y))
    savePosn(event)
    lst.append((event.x,event.y))

def create_table():
    tl1 = Toplevel(root)
    tl1.title("TABLE")
    lstbox = Listbox(tl1,width=30,height=200)
    for i in range(len(lst)):
        lstbox.insert(END,f"x={lst[i][0]} y={lst[i][1]}")
    lstbox.pack(side='left',fill='both')

def create_table_load():
    tl2 = Toplevel(root)
    tl2.title("TABLE LOAD DATA")
    lstboxLD = Listbox(tl2,width=30,height=200)
    for i in range(len(lstload)):
        lstboxLD.insert(END,f"x={lstload[i][0]} y={lstload[i][1]}")
    lstboxLD.pack(side='left',fill='both')

def clear_canvas():
    canvas.delete("all")
    lst.clear()

def saving_data():
    files = os.listdir(directory_files)
    np.save(f"main/saves/data{len(files)}",lst[1:])
    tkinter.messagebox.showinfo(message=f"data saved in main/saves/data{len(files)}")

def create_from_load():
    clear_canvas()
    for k in range(len(lstload)):
        if (k < len(lstload)):
            canvas.create_line((lstload[k][0],lstload[k][1], lstload[k+1][0],lstload[k+1][1]))

def graphic_show():
    X = []
    for i in range(len(lstload)):
        X.append(lstload[i][0])
    Y = []
    for i in range(len(lstload)):
        Y.append(lstload[i][1])
    X = np.array(X)
    Y = np.array(Y)
    fig, ax = plt.subplots()
    ax.plot(X,Y,color='C1')
    ax.grid()
    plt.show()

def choose_file():
    name = fd.askopenfilename(title='numpy files',filetypes=[('Numpy files', '.npy')])
    global lstload
    lstload = []
    lstload.clear()
    lstload = np.load(f"{name}")
    btn6.configure(state=NORMAL)
    btn7.configure(state=NORMAL)
    tkinter.messagebox.showinfo(message=f"data load from {name}")

#INTERFACE
canvas = Canvas(root,bg='white',width=1100,height=500)
canvas.grid(column=0, row=0,sticky=(E))
canvas.bind("<Button-1>", savePosn)
canvas.bind("<B1-Motion>", addLine)

btn1 = Button(root,width=15, height=5,text='create table', command=lambda:create_table())
btn1.grid(column=0,row=1)

btn2 = Button(root,width=15, height=5,text='clear', command=lambda:clear_canvas())
btn2.grid(column=0,row=2)

btn3 = Button(root,width=15, height=5,text='save', command=lambda:saving_data())
btn3.grid(column=0,row=3)

btn6 = Button(root,width=15, height=5,text='print', state=DISABLED, command=lambda:create_from_load())
btn6.grid(column=1,row=0)

btn7 = Button(root,width=15, height=5,text='show', state=DISABLED, command=lambda:graphic_show())
btn7.grid(column=1,row=1)

btn8 = Button(root,width=15, height=5,text='choose file', command=lambda:choose_file())
btn8.grid(column=2,row=0)

btn9 = Button(root,width=15, height=5,text='create load data table', command=lambda:create_table_load())
btn9.grid(column=2,row=1)

root.mainloop()


"""
Task creators for doit
"""

from cpe_help import util

class TaskHelper(object):
    """
    I help with the creation of tasks
    """
    @staticmethod
    def download(url, out, overwrite=False, **kwargs):
        """
        Generate a task to download a file

        Note that, by default, downloads are never cleaned. You can
        change this behavior by sending clean=True along with the
        kwargs.

        Parameters
        ----------
        url : str
            The url to download from.
        out : str or Path
            The output filename.
        overwrite : bool, default False
            If True, overwrite existing files. Otherwise, do not perform
            the download when out exists.
        kwargs
            Keyword arguments are added as items of the resulting dict.

        Returns
        -------
        dict
            The task to be performed.
        """
        task = {
            'targets': [out],
            'actions': [(util.network.download, (url, out))],
            'uptodate': [not overwrite],
        }
        task.update(kwargs)
        return task

    @staticmethod
    def unzip(file, dir, **kwargs):
        """
        Generate a task to extract files from a ZIP archive

        Parameters
        ----------
        file : str or Path
            The input ZIP file.
        dir : str or Path
            The directory to extract the ZIP contents to.
        kwargs
            Keyword arguments are added as items of the resulting dict.

        Returns
        -------
        dict
            The task to be performed.

        Notes
        -----
        As we can't know the ZIP archive contents beforehand, this task
        will only be rerun when the whole output directory goes missing
        (not its contents).
        """
        task = {
            'file_dep': [file],
            'targets': [dir],
            'actions': [(util.compression.extract_zipfile, (file, dir))],
            'clean': [(util.file.maybe_rmtree, (dir,))],
        }
        task.update(kwargs)
        return task


from src.common.config import users
from src.libs.CrabadaWeb2Client.IdleGameWeb2Client import IdleGameWeb2Client
from pprint import pprint

# VARS
client = IdleGameWeb2Client()
userAddress = users[0]["address"]

# TEST FUNCTIONS
def testGetAvailableTeams() -> None:
    params = {"is_team_available": 1, "limit": 5, "page": 1}
    pprint(client.listTeams(userAddress, params=params))

def testGetAllTeams() -> None:
    pprint(client.listTeams(userAddress))

# EXECUTE
print(">>> AVAILABLE TEAMS")
testGetAvailableTeams()
print(">>> ALL TEAMS")
testGetAllTeams()


import csv
from Aircraft import Aircraft

class AirLines:
    def __init__(self) -> None:
        self.aircrafts = None
        self.load_aircrafts("./data/aircraft.csv")

    def load_aircrafts(self, csv_file):
        with open(csv_file, "r") as file:
            lines = csv.reader(file)
            _ = next(lines)
            for line in lines:
                self.aircrafts[line[0]] =  Aircraft(line[3], line[0], line[1], line[4])
        file.close()

    def get_aircraft(self, aircraft_code):
        return self.aircrafts[aircraft_code]

AirLines()

OOP & Python Programming and Problem Solving Part - IV/Module 20 Basic Class of a Airport Flight Scheduler/practice/Air_Lines.py