from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QStyle, QApplication
from gui import main_ui
from control.task_ctrl import task_ctrl_c
from control.loading import loading_c
from control.filename_control import filename_ctrl_c
from control.folder_select import folder_select_c
from control.read_file import read_file_c
from control.help_file import readme_c
from control.thread import comm_thread, scp_thread, log_thread, terminal_thread
import copy
import json, time
import base64

class main_ui_c(main_ui):
    def  __init__ (self):
        self.setupUi(self)
        self.__load_server_info()
        self.__connect()
        self.__init_tabel()
        self.__init_child_window()
        self.__child_connect()
        self.__init_thread()
        self.__thread_connect()
        self.__set_init_state()
        self.__init_root()
        self.slaver_list = []
        self.curr_file_list = []
        self.curr_folder_list = []
        self.curr_wait_list = []
        self.curr_exec_list = []
        self.curr_hist_list = []
        self.root = []
        self.recv_err_count = 0 # 接收失败计数

    def __init_root(self):
        # 获取服务器的可见目录
        self.send_info({'mode':'get_root','ui': 'main', 'info': ""})

    def __load_server_info(self):
        # 加载服务端信息
        try:
            with open("server_info.json", "r", encoding="utf-8") as f:
                info = json.load(f)
            self.server_info = info
        except Exception as e:
            QtWidgets.QMessageBox.warning(self, "错误", "服务端信息获取失败, 原因是：\n"+str(e))
            exit(1)

    def __set_init_state(self):
        # 设置按钮等元素初始状态
        self.stop_button.setEnabled(False)
        self.change.setEnabled(False)
        self.delete_2.setEnabled(False)
        self.move.setEnabled(False)
        self.copy.setEnabled(False)
        self.new_folder.setEnabled(False)
        self.new_file.setEnabled(False)
        self.remane.setEnabled(False)
        self.delete_file.setEnabled(False)
        self.upload.setEnabled(False)

    def __init_tabel(self):
        # 初始化所有表格和窗口
        self.info_list = QtGui.QStandardItemModel(0, 7)
        self.info_list.setHorizontalHeaderLabels(['节点名', 'CPU使用量', 'CPU被占用', '可用RAM', '网络下行', '网络上行', 'GPU使用量', 'GPU被占用', '可用VRAM'])
        self.info_window.setModel(self.info_list)
        self.info_window.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.info_window.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        self.info_window.verticalHeader().setDefaultSectionSize(100)
        self.info_window.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)

        self.wait_list = QtGui.QStandardItemModel(0, 10)
        self.wait_list.setHorizontalHeaderLabels(['任务ID', '使用环境', '项目路径', '执行命令', '拟使用计算节点', 'GPU需求数', '前驱任务', '是否紧急', '是否复用GPU', '状态'])
        self.wait_table.setModel(self.wait_list)
        self.wait_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.wait_table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.wait_table.verticalHeader().setDefaultSectionSize(100)
        self.wait_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.wait_table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        self.wait_table.horizontalHeader().setSectionResizeMode(5, QtWidgets.QHeaderView.ResizeToContents)
        self.wait_table.horizontalHeader().setSectionResizeMode(6, QtWidgets.QHeaderView.ResizeToContents)
        self.wait_table.horizontalHeader().setSectionResizeMode(7, QtWidgets.QHeaderView.ResizeToContents)
        self.wait_table.horizontalHeader().setSectionResizeMode(8, QtWidgets.QHeaderView.ResizeToContents)
        self.wait_table.horizontalHeader().setSectionResizeMode(9, QtWidgets.QHeaderView.ResizeToContents)

        self.exec_list = QtGui.QStandardItemModel(0, 10)
        self.exec_list.setHorizontalHeaderLabels(['任务ID', '使用环境', '项目路径', '执行命令', '计算节点', '使用GPU数量', '前驱任务', '是否紧急', '是否复用GPU', '状态'])
        self.exec_table.setModel(self.exec_list)
        self.exec_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.exec_table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.exec_table.verticalHeader().setDefaultSectionSize(100)
        self.exec_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.exec_table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        self.exec_table.horizontalHeader().setSectionResizeMode(5, QtWidgets.QHeaderView.ResizeToContents)
        self.exec_table.horizontalHeader().setSectionResizeMode(6, QtWidgets.QHeaderView.ResizeToContents)
        self.exec_table.horizontalHeader().setSectionResizeMode(7, QtWidgets.QHeaderView.ResizeToContents)
        self.exec_table.horizontalHeader().setSectionResizeMode(8, QtWidgets.QHeaderView.ResizeToContents)
        self.exec_table.horizontalHeader().setSectionResizeMode(9, QtWidgets.QHeaderView.ResizeToContents)

        self.history_list = QtGui.QStandardItemModel(0, 10)
        self.history_list.setHorizontalHeaderLabels(['任务ID', '使用环境', '项目路径', '执行命令', '计算节点', '使用GPU数量', '前驱任务', '是否紧急', '是否复用GPU', '状态'])
        self.history_table.setModel(self.history_list)
        self.history_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.history_table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        self.history_table.verticalHeader().setDefaultSectionSize(100)
        self.history_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.history_table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        self.history_table.horizontalHeader().setSectionResizeMode(5, QtWidgets.QHeaderView.ResizeToContents)
        self.history_table.horizontalHeader().setSectionResizeMode(6, QtWidgets.QHeaderView.ResizeToContents)
        self.history_table.horizontalHeader().setSectionResizeMode(7, QtWidgets.QHeaderView.ResizeToContents)
        self.history_table.horizontalHeader().setSectionResizeMode(8, QtWidgets.QHeaderView.ResizeToContents)
        self.history_table.horizontalHeader().setSectionResizeMode(9, QtWidgets.QHeaderView.ResizeToContents)

        self.file_tree_model = QtGui.QStandardItemModel()
        self.file_tree.setModel(self.file_tree_model)
        self.file_tree.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)

        self.curr_folder.setText("/")

        self.env_list.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)

        self.curr_log.setOverwriteMode(True)

        self.log_view.setReadOnly(True)

    def __connect(self):
        # 逻辑操作与按钮连接
        # self.server_setting.triggered.connect(self.set_server)
        self.exit.triggered.connect(self.close)
        self.add.triggered.connect(self.add_task)
        self.change.triggered.connect(self.change_task)
        self.delete_2.triggered.connect(self.del_task)
        self.help.triggered.connect(self.readme)
        self.about.triggered.connect(self.show_about)
        self.add_button.clicked.connect(self.add_task)
        self.change_button.clicked.connect(self.change_task)
        self.delete_button.clicked.connect(self.del_task)
        self.stop_button.clicked.connect(self.stop_task)
        self.resub_button.clicked.connect(self.resub_task)
        self.upload.clicked.connect(self.upload_file)
        self.download.clicked.connect(self.download_file)
        self.watch.clicked.connect(self.watch_file)
        self.move.clicked.connect(lambda: self.file_operate("mv"))
        self.copy.clicked.connect(lambda: self.file_operate("cp"))
        self.new_folder.clicked.connect(lambda: self.file_create("mkdir"))
        self.new_file.clicked.connect(lambda: self.file_create("touch"))
        self.remane.clicked.connect(lambda: self.file_create("mv"))
        self.delete_file.clicked.connect(self.remove_file)
        self.tabWidget.currentChanged.connect(self.change_tab)
        self.tabWidget_2.currentChanged.connect(self.change_task_tab)
        self.test_env.clicked.connect(self.test_env_func)
        self.get_python_version.clicked.connect(self.get_python_version_func)
        self.get_pkgs.clicked.connect(self.get_pkgs_func)
        self.test_cuda_pt.clicked.connect(self.test_cuda_pt_func)
        self.test_cuda_tf.clicked.connect(self.test_cuda_tf_func)
        self.file_tree.clicked.connect(self.select_file)
        self.file_tree.doubleClicked.connect(self.open_file)
        self.pushButton.clicked.connect(self.reflush)
        self.exec_table.doubleClicked.connect(lambda: self.show_train_log("exec"))
        self.history_table.doubleClicked.connect(lambda: self.show_train_log("hist"))
        self.term_input.returnPressed.connect(self.send_term_cmd)
        self.shutdown_ssh.triggered.connect(self.shut_ssh)

    def __init_child_window(self):
        # 初始化所有子窗口
        self.loading_window = loading_c()
        self.loading_window.setWindowTitle("请稍后")
        self.loading_window.setModal(True)
        self.task_ctrl_window = task_ctrl_c()
        # self.task_ctrl_window.setModal(True)
        self.read_file_window = read_file_c()
        self.read_file_window.setModal(True)
        self.read_file_window.setWindowTitle("预览文件")
        self.folder_select_window = folder_select_c()
        self.folder_select_window.setModal(True)
        self.filename_ctrl_window = filename_ctrl_c()
        self.filename_ctrl_window.setModal(True)
        self.loading_window.set_text("正在与服务器通信中，请稍后...")
        self.readme_window = readme_c()

    def __init_thread(self):
        # 初始化并开启所有子线程
        self.monitor_th = comm_thread(parent=self, port=self.server_info["monitor_port"], addr=self.server_info["IP"], buffer=self.server_info["buffer"])   #节点监控通信线程
        self.data_th = comm_thread(parent=self, port=self.server_info["data_port"], addr=self.server_info["IP"], buffer=self.server_info["buffer"])     #操作通信线程
        self.scp_th = scp_thread(ssh_port=self.server_info["ssh_port"], addr=self.server_info["IP"], psw=self.server_info["psw"], username=self.server_info["username"])    #ssh文件操作线程
        self.task_th = comm_thread(parent=self, port=self.server_info["train_state_port"], addr=self.server_info["IP"], buffer=self.server_info["buffer"])     #任务监控线程
        self.train_log_th = log_thread(ssh_port=self.server_info["ssh_port"], addr=self.server_info["IP"], psw=self.server_info["psw"], username=self.server_info["username"])    #训练日志查看线程
        self.term_th = terminal_thread(ssh_port=self.server_info["ssh_port"], addr=self.server_info["IP"], psw=self.server_info["psw"], username=self.server_info["username"])  #命令行线程
        self.server_log_th = log_thread(ssh_port=self.server_info["ssh_port"], addr=self.server_info["IP"], psw=self.server_info["psw"], username=self.server_info["username"])    #服务器日志查看线程

        self.server_log_th.set_mode("tail")

        self.monitor_th.start()
        self.data_th.start()
        self.task_th.start()

    def __thread_connect(self):
        # 对所有子线程的信号进行连接
        self.monitor_th._signal.connect(self.proceed_comm)
        self.data_th._signal.connect(self.proceed_comm)
        self.scp_th._signal.connect(self.proceed_scp)
        self.task_th._signal.connect(self.proceed_comm)
        self.train_log_th._signal.connect(self.curr_log.write)
        self.term_th._signal.connect(self.show_term)
        self.server_log_th._signal.connect(self.log_view.write)

    def __child_connect(self):
        # 将所有子窗口的信号进行连接
        self.filename_ctrl_window._signal.connect(self.file_create_signal)
        self.read_file_window._signal.connect(self.save_file)
        self.folder_select_window._signal.connect(self.folder_select_process)
        self.task_ctrl_window._signal.connect(self.task_ctrl_process)

    def close(self):
        exit(0)

    def send_info(self, info: str):
        # 向服务端发送信息
        try:
            info = json.dumps(info)
        except Exception as e:
            QtWidgets.QMessageBox.warning(self, "错误", "发送失败, 原因是打包信息时出现非法字符:\n"+str(e))
            return
        try:
            self.data_th.send(info)
        except Exception as e:
            QtWidgets.QMessageBox.warning(self, "错误", "发送失败, 原因是"+str(e))

    # def set_server(self):
    #     # 设置服务器参数
    #     pass

    def select_file(self):
        # 选择文件
        curr_name = self.file_tree.currentIndex().data()
        is_file = False
        if curr_name in self.curr_file_list:
            is_file = True
        elif curr_name in self.curr_folder_list:
            is_file = False
        self.curr_file.setText(curr_name)
        if is_file:
            self.watch.setText("预览")
        else:
            self.watch.setText("打开")
        if curr_name == "..":
            # 选中了上级指针
            self.move.setEnabled(False)
            self.copy.setEnabled(False)
            self.remane.setEnabled(False)
            self.delete_file.setEnabled(False)
        else:
            # 选中了其他
            self.move.setEnabled(True)
            self.copy.setEnabled(True)
            self.remane.setEnabled(True)
            self.delete_file.setEnabled(True)
        if self.curr_folder.text() == "/":
            # 位于主目录
            self.move.setEnabled(False)
            self.copy.setEnabled(False)
            self.new_folder.setEnabled(False)
            self.new_file.setEnabled(False)
            self.remane.setEnabled(False)
            self.delete_file.setEnabled(False)

    def open_file(self):
        # 打开文件或文件夹
        self.watch_file()

    def add_task(self):
        # 添加任务
        self.task_ctrl_window.setWindowTitle("添加任务")
        self.send_info({'mode':'env_list','ui': 'task', 'info':''})
        self.task_ctrl_window.init(self.slaver_list, self.curr_wait_list+self.curr_exec_list)
        self.task_ctrl_window.show()

    def change_task(self):
        # 修改当前任务
        if self.task_ctrl_window.envs.count() == 0:
            self.send_info({'mode':'env_list','ui': 'task', 'info':''})
        def str_to_bool_int(s:str):
            if s == "是":
                return 1
            elif s == "否":
                return 0
            else:
                return 0

        idx = self.tabWidget_2.currentIndex()
        if idx == 0:
            selected = self.wait_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_info = {   # 任务信息
            "task_id": self.wait_list.item(index, 0).text(),    # 任务id，新建任务时id为空，修改时为该任务的id
            "envs": self.wait_list.item(index, 1).text(),  # 执行任务时的环境
            "path": self.wait_list.item(index, 2).text(),    # 执行任务时进入的文件夹路径
            "exec": self.wait_list.item(index, 3).text(),     # 任务的执行命令
            "need_gpus": self.wait_list.item(index, 5).text(),    # 执行该任务所需的gpu数量
            "slaver": self.wait_list.item(index, 4).text(),    # 指定任务分配在哪个计算节点
            "prev": self.wait_list.item(index, 6).text(),   # 前驱任务，只有前驱任务执行完毕后才会执行当前任务
            "is_urgent": str_to_bool_int(self.wait_list.item(index, 7).text()),  # 该任务是否紧急
            "is_reuse_gpu": str_to_bool_int(self.wait_list.item(index, 8).text())    # 该任务是否复用GPU(即一个gpu上运行多个任务)
        }
        elif idx == 2:
            selected = self.history_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_info = {   # 任务信息
            "task_id": self.history_list.item(index, 0).text(),    # 任务id，新建任务时id为空，修改时为该任务的id
            "envs": self.history_list.item(index, 1).text(),  # 执行任务时的环境
            "path": self.history_list.item(index, 2).text(),    # 执行任务时进入的文件夹路径
            "exec": self.history_list.item(index, 3).text(),     # 任务的执行命令
            "need_gpus": int(self.history_list.item(index, 5).text()),    # 执行该任务所需的gpu数量
            "slaver": self.history_list.item(index, 4).text(),    # 指定任务分配在哪个计算节点
            "prev": self.history_list.item(index, 6).text(),   # 前驱任务，只有前驱任务执行完毕后才会执行当前任务
            "is_urgent": str_to_bool_int(self.history_list.item(index, 7).text()),  # 该任务是否紧急
            "is_reuse_gpu": str_to_bool_int(self.history_list.item(index, 8).text())
            }
        else:
            return

        self.task_ctrl_window.setWindowTitle("修改任务: "+task_info["task_id"])
        self.task_ctrl_window.set_info(task_info, self.slaver_list, self.curr_wait_list+self.curr_exec_list, task_info["task_id"])
        self.task_ctrl_window.show()

    def del_task(self):
        # 删除当前任务
        cb = QtWidgets.QCheckBox()
        cb.setText("同时删除该任务的所有后继任务")
        mb = QtWidgets.QMessageBox(self)
        mb.setCheckBox(cb)
        mb.setWindowTitle('警告')
        mb.setText("确认删除这个任务吗? 该操作不可逆! ")
        mb.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
        idx = self.tabWidget_2.currentIndex()
        if idx == 0:
            selected = self.wait_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_id = self.wait_list.item(index, 0).text()
        elif idx == 2:
            selected = self.history_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_id = self.history_list.item(index, 0).text()
        else:
            return
        reply = mb.exec_()
        if reply == QtWidgets.QMessageBox.No:
            return
        info = {'mode':'del_task','ui': 'main', 'info': task_id}
        self.send_info(info)
        time.sleep(0.1)
        if cb.isChecked():
            def del_tasks(curr_task_id):
                # 递归删除该任务的所有后继任务(包括后继的后继)
                counts = self.wait_list.rowCount()
                for i in range(counts):
                    prev_task_id = self.wait_list.item(i, 6).text()
                    if curr_task_id == prev_task_id:
                        info = {'mode':'del_task','ui': 'main', 'info': self.wait_list.item(i, 0).text()}
                        self.send_info(info)
                        time.sleep(0.1)
                        del_tasks(self.wait_list.item(i, 0).text())
                counts = self.history_list.rowCount()
                for i in range(counts):
                    prev_task_id = self.history_list.item(i, 6).text()
                    if task_id == prev_task_id:
                        info = {'mode':'del_task','ui': 'main', 'info': self.history_list.item(i, 0).text()}
                        self.send_info(info)
                        time.sleep(0.1)
                        del_tasks(self.wait_list.item(i, 0).text())
            del_tasks(task_id)

    def stop_task(self):
        # 中止当前任务
        idx = self.tabWidget_2.currentIndex()
        if idx == 1:
            selected = self.exec_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_id = self.exec_list.item(index, 0).text()
            info = {'mode':'stop_task','ui': 'main','info': task_id}
            self.send_info(info)
        else:
            return

    def resub_task(self):
        # 重新提交当前任务
        idx = self.tabWidget_2.currentIndex()
        if idx == 0:
            selected = self.wait_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_id = self.wait_list.item(index, 0).text()
        elif idx == 1:
            selected = self.wait_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_id = self.exec_list.item(index, 0).text()
        elif idx == 2:
            selected = self.history_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_id = self.history_list.item(index, 0).text()
        else:
            return
        info = {'mode':'resub_task','ui': 'main', 'info': task_id}
        self.send_info(info)

    def shut_ssh(self):
        # 关闭命令行的ssh连接
        self.term_th.set_run(False)

    def send_term_cmd(self):
        # 命令行发送消息
        cmd = self.term_input.text()
        self.term_th.put_cmd(cmd)
        self.term_output.appendPlainText("$ "+cmd)
        self.term_input.clear()

    def show_term(self, text: str):
        # 将命令行运行结果返回至界面
        self.term_output.appendPlainText(text)

    def readme(self):
        # 帮助文档
        self.readme_window.show()

    def show_train_log(self, mode):
        # 显示训练日志
        self.curr_log.clear()
        self.train_log_th.set_run(False)
        if self.train_log_th.isRunning():
            self.train_log_th.terminate()
        if mode == "exec":
            self.train_log_th.set_mode("tail")
            selected = self.exec_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_id = self.exec_list.item(index, 0).text()
            state = self.exec_list.item(index, 9).text()
            if state == "准备中":
                # 准备状态的任务还没产生日志
                self.curr_log.write("任务处于准备状态, 暂无日志, 请稍后...")
                return
        elif mode == "hist":
            self.train_log_th.set_mode("cat")
            selected = self.history_table.selectionModel().selectedRows()
            if len(selected) == 0:
                QtWidgets.QMessageBox.warning(self, "警告", "请选择任务")
                return
            index = QtCore.QPersistentModelIndex(selected[0])
            index = index.row()
            task_id = self.history_list.item(index, 0).text()
        else:
            return
        info = {'mode':'get_train_log_path','ui': 'main', 'info':task_id}
        self.send_info(info)
        pass

    def show_about(self):
        # 关于文档
        QtWidgets.QMessageBox.about(self, "关于", "分布式深度学习任务管理系统 版本1.0\n作者: 许鹏程\n此软件为开源软件, 欢迎使用和测试。\n若在使用中发现问题, 欢迎提出意见和建议。")

    def upload_file(self):
        # 上传文件
        if self.scp_th.isRunning():
            QtWidgets.QMessageBox.warning(self, "警告", "正在进行文件传输, 请稍后...")
        else:
            file_name, _ = QtWidgets.QFileDialog.getOpenFileNames(self, "上传文件", "", "")
            if file_name != "":
                self.scp_th.set_info("C", file_name, self.curr_folder.text(), False)
                self.scp_th.start()

    def download_file(self):
        # 下载文件
        if self.scp_th.isRunning():
            QtWidgets.QMessageBox.warning(self, "警告", "正在进行文件传输, 请稍后...")
        else:
            if self.curr_file.text() == "":
                QtWidgets.QMessageBox.warning(self, "警告", "请选择要下载的文件")
            elif self.curr_file.text() == "..":
                QtWidgets.QMessageBox.warning(self, "警告", "你不可以下载上级目录指针")
            else:
                folder_name = QtWidgets.QFileDialog.getExistingDirectory(self, "选择下载位置", "")
                if folder_name != "":
                    is_folder = False
                    if self.curr_file.text() in self.curr_file_list:
                        is_folder = False
                    elif self.curr_file.text() in self.curr_folder_list:
                        is_folder = True
                    self.scp_th.set_info("S", folder_name, self.curr_folder.text()+self.curr_file.text(), is_folder)
                    self.scp_th.start()

    def watch_file(self):
        # 预览文件
        curr_name = self.file_tree.currentIndex().data()
        if not self.curr_file.text() == "":
            is_file = True
            if curr_name in self.curr_file_list:
                is_file = True
            elif curr_name in self.curr_folder_list:
                is_file = False
            if is_file:
                # 预览文件
                info = {'mode':'get_file','ui': 'main', 'info':self.curr_folder.text()+self.curr_file.text()}
                self.send_info(info)
            else:
                # 打开文件夹
                info = {'mode':'get_folder','ui': 'main', 'info':self.curr_folder.text()+curr_name}
                self.send_info(info)

    def save_file(self, text: str):
        # 保存文件
        try:
            text = str(base64.b64encode(bytes(text, encoding="utf-8")).decode("utf-8"))
            info = {'mode':'save_file','ui': 'main','info':{"path": self.curr_folder.text()+self.curr_file.text(), "content": text}}
            self.send_info(info)
        except:
            QtWidgets.QMessageBox.warning(self, "保存文件", "保存失败, 原因是: 编码错误, 文件中可能存在非法字符")

    def reflush(self):
        # 刷新
        if self.curr_folder.text() == "/":
            # 位于主目录
            pass
        else:
            self.file_tree_model.clear()
            info = {'mode':'get_folder','ui': 'main','info':self.curr_folder.text()}
            self.send_info(info)

    def file_operate(self, mode: str):
        # 文件操作(移动, 复制)
        if self.scp_th.isRunning():
            # 正在进行文件操作
            QtWidgets.QMessageBox.warning(self, "警告", "正在进行文件操作, 请稍后...")
        else:
            if not self.curr_file.text() == "":
                self.folder_select_window.setWindowTitle("复制到" if mode=="cp" else "移动到")
                self.folder_select_window.get_curr_folder_info(curr_folder=self.curr_folder.text(), folder_list=self.curr_folder_list, wake_ui="main", opera=mode)
                self.folder_select_window.show()
            else:
                QtWidgets.QMessageBox.warning(self, "警告", "请先选择需要"+ ("复制" if mode=="cp" else "移动") +"的文件或文件夹！")

    def file_create(self, mode: str):
        # 创建, 重命名文件(文件夹)
        if not self.curr_folder.text() == "":
            self.filename_ctrl_window.set_info(mode, self.curr_folder.text())
            self.filename_ctrl_window.set_filename("")
            if mode == "mkdir":
                # 新建文件夹
                self.filename_ctrl_window.setWindowTitle("新建文件夹")
                self.filename_ctrl_window.set_text("请输入文件夹名称：")
                self.filename_ctrl_window.show()
            elif mode == "touch":
                # 新建文件
                self.filename_ctrl_window.setWindowTitle("新建文件")
                self.filename_ctrl_window.set_text("请输入文件名称：")
                self.filename_ctrl_window.show()
            elif mode == "mv":
                # 重命名
                if not self.curr_file.text() == "":
                    self.filename_ctrl_window.setWindowTitle("重命名")
                    self.filename_ctrl_window.set_text("请输入新名称：")
                    self.filename_ctrl_window.set_filename(self.curr_file.text())
                    self.filename_ctrl_window.show()
                else:
                    QtWidgets.QMessageBox.warning(self, "警告", "请先选择需要重命名的文件！")
        else:
            if mode == "mv":
                QtWidgets.QMessageBox.warning(self, "警告", "请先选择需要重命名的文件！")
            else:
                QtWidgets.QMessageBox.warning(self, "警告", "请先确定文件要创建在哪儿！")

    def remove_file(self):
        # 删除文件
        reply = QtWidgets.QMessageBox.warning(self, "删除文件", "确认删除这个文件吗? 该操作不可逆! ", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
        if reply == QtWidgets.QMessageBox.Yes:
            info = {'mode':'rm_file','ui': 'main','info':self.curr_folder.text()+self.curr_file.text()}
            self.send_info(info)

    def test_env_func(self):
        # 测试环境
        curr_env = self.env_list.currentIndex().data()
        if not curr_env == None:
            # 选择了某个环境
            info = {'mode':'test_env','ui': 'main','info':curr_env}
            self.send_info(info)

    def get_python_version_func(self):
        # 查看python版本
        curr_env = self.env_list.currentIndex().data()
        if not curr_env == None:
            # 选择了某个环境
            info = {'mode':'py_v','ui': 'main','info':curr_env}
            self.send_info(info)
            self.loading_window.show()

    def test_cuda_pt_func(self):
        # 查看pytorch的cuda支持情况
        curr_env = self.env_list.currentIndex().data()
        if not curr_env == None:
            # 选择了某个环境
            info = {'mode':'cuda_pt','ui': 'main','info':curr_env}
            self.send_info(info)
            self.loading_window.show()

    def test_cuda_tf_func(self):
        # 查看tensorflow的cuda支持情况
        curr_env = self.env_list.currentIndex().data()
        if not curr_env == None:
            # 选择了某个环境
            info = {'mode':'cuda_tf','ui': 'main','info':curr_env}
            self.send_info(info)
            self.loading_window.show()

    def get_pkgs_func(self):
        # 查看当前环境下的包
        curr_env = self.env_list.currentIndex().data()
        if not curr_env == None:
            # 选择了某个环境
            info = {'mode':'pkgs','ui': 'main','info':curr_env}
            self.send_info(info)
            self.loading_window.show()

    def file_create_signal(self, ret: dict):
        # 对文件名子窗口发送的信号进行操作
        info = {'mode':'file_operate','ui': 'main','info': ret}
        self.send_info(info)
        pass

    def change_tab(self, idx):
        # 点击了主界面的tab
        if idx == 0:
            # 节点监控
            self.change.setEnabled(False)
            self.delete_2.setEnabled(False)
            pass
        elif idx == 1:
            # 训练管理
            self.change.setEnabled(True)
            self.delete_2.setEnabled(True)
            pass
        elif idx == 2:
            # 文件管理
            self.change.setEnabled(False)
            self.delete_2.setEnabled(False)
            self.curr_file.setText("")
            pass
        elif idx == 3:
            # 环境管理
            self.change.setEnabled(False)
            self.delete_2.setEnabled(False)
            self.send_info({'mode':'env_list','ui': 'main', 'info':''})
            pass
        elif idx == 4:
            # 命令行
            self.change.setEnabled(False)
            self.delete_2.setEnabled(False)
            self.term_th.set_run(True)
            if not self.term_th.isRunning():
                self.term_th.start()
            pass
        elif idx == 5:
            # 日志信息
            self.change.setEnabled(False)
            self.delete_2.setEnabled(False)
            if not self.server_log_th.isRunning():
                self.send_info({'mode':'get_server_log','ui': 'main', 'info':''})

    def change_task_tab(self, idx):
        # 点击了任务管理界面的tab
        if idx == 0:
            # 等待区
            self.stop_button.setEnabled(False)
            self.delete_button.setEnabled(True)
            self.delete_2.setEnabled(True)
            self.change_button.setEnabled(True)
            self.change.setEnabled(True)
            self.resub_button.setEnabled(True)
        elif idx == 1:
            # 执行区
            self.stop_button.setEnabled(True)
            self.delete_button.setEnabled(False)
            self.delete_2.setEnabled(False)
            self.change_button.setEnabled(False)
            self.change.setEnabled(False)
            self.resub_button.setEnabled(False)
        elif idx == 2:
            # 历史任务
            self.stop_button.setEnabled(False)
            self.delete_button.setEnabled(True)
            self.delete_2.setEnabled(True)
            self.change_button.setEnabled(True)
            self.change.setEnabled(True)
            self.resub_button.setEnabled(True)

    def closeEvent(self, event):
        # 退出软件
        # 关闭所有窗口
        self.loading_window.close()
        self.task_ctrl_window.close()
        self.read_file_window.close()
        self.folder_select_window.close()
        self.filename_ctrl_window.close()
        self.readme_window.close()
        # 向服务端发送断开连接消息
        self.data_th.send("CLIEND_CLOSE")
        self.monitor_th.send("CLIEND_CLOSE")
        self.task_th.send("CLIEND_CLOSE")
        # 终止所有子线程
        if self.data_th.isRunning():
            self.data_th.terminate()
        if self.monitor_th.isRunning():
            self.monitor_th.terminate()
        if self.scp_th.isRunning():
            self.scp_th.terminate()
        if self.task_th.isRunning():
            self.task_th.terminate()
        if self.train_log_th.isRunning():
            self.train_log_th.terminate()
        if self.server_log_th.isRunning():
            self.server_log_th.terminate()

    def proceed_scp(self, text:str):
        # 处理文件操作(上传, 下载, 移动到, 复制到)的结果
        self.curr_opera.setText(text)
        if "完成" in text or "错误" in text:
            self.reflush()

    def folder_select_process(self, info):
        # 对选择文件夹界面的信号进行处理
        func = info["func"] # 操作
        wake = info["wake"] # 该子窗口由哪个窗口调用
        msg = info["info"]  # 操作内容
        if func == "open_folder":
            self.send_info(msg)
        elif func == "ok":
            if wake == "main":
                # 由主窗口调用
                self.scp_th.set_info(msg["opera"], self.curr_folder.text()+self.curr_file.text(), msg["target"], False)
                self.scp_th.start()
            elif wake == "task":
                # 由任务添加, 修改窗口调用
                self.task_ctrl_window.folder_name.setText(msg["target"])
        else:
            pass

    def task_ctrl_process(self, info):
        # 对任务创建, 修改界面信号进行处理
        func = info["func"] # 操作
        msg = info["info"]  # 操作内容
        if func == "open_folder":
            self.folder_select_window.setWindowTitle("选择项目路径")
            if self.task_ctrl_window.folder_name.text() == "/":
                self.folder_select_window.set_wake_ui("task")
                self.folder_select_window.get_curr_folder_info("/", folder_list=self.root, wake_ui="task")
                self.folder_select_window.show()
            else:
                self.folder_select_window.set_wake_ui("task")
                self.send_info({'mode':'get_folder','ui': 'folder_select', 'info':self.task_ctrl_window.folder_name.text()})
                self.folder_select_window.show()
        elif func == "ok":
            if msg["task_id"] == "":
                self.send_info({'mode':'add_task','ui': 'task_ctrl', 'info': msg})
            else:
                self.send_info({'mode':'change_task','ui': 'task_ctrl', 'info': msg})
        pass

    def proceed_comm(self, info):
        # 对服务端发送的数据进行处理并显示
        def bool_int_to_str(i: int):
            if i == 0:
                return "否"
            elif i == 1:
                return "是"
            else:
                return "否"
        try:
            self.recv_err_count = 0 # 重置接收失败计数
            mode = info["mode"]
            msg = info["info"]
            ui = info["ui"]
            if mode == "mon":
                # 将节点信息展示在主窗口
                name = msg["name"]
                online = msg["online"]
                if name not in self.slaver_list:
                    self.slaver_list.append(name)
                idx = self.slaver_list.index(name)
                self.info_list.setItem(idx, 0, QtGui.QStandardItem(name))
                self.info_list.setItem(idx, 1, QtGui.QStandardItem(msg["cpu_usage"]))
                self.info_list.setItem(idx, 2, QtGui.QStandardItem(bool_int_to_str(msg["cpu_used"])))
                self.info_list.setItem(idx, 3, QtGui.QStandardItem(msg["avail_ram"]))
                self.info_list.setItem(idx, 4, QtGui.QStandardItem(msg["web_down"]))
                self.info_list.setItem(idx, 5, QtGui.QStandardItem(msg["web_up"]))
                gpu_used = ""
                avail_vram = ""
                for g in msg["gpus"]:
                    gpu_used += g["gpu_used"]
                    gpu_used += "  "
                    avail_vram += g["avail_vram"]
                    avail_vram += "  "
                self.info_list.setItem(idx, 6, QtGui.QStandardItem(gpu_used))
                if len(msg["gpu_used"]) == 0:
                    self.info_list.setItem(idx, 7, QtGui.QStandardItem("No data"))
                else:
                    gpu_state = ""
                    for u in msg["gpu_used"]:
                        gpu_state += bool_int_to_str(u)
                        gpu_state += "  "
                    self.info_list.setItem(idx, 7, QtGui.QStandardItem(gpu_state))

                self.info_list.setItem(idx, 8, QtGui.QStandardItem(avail_vram))
                need_delete = []
                for i in range(self.info_list.rowCount()):
                    n = self.info_list.item(i, 0).text()
                    if n not in online:
                        self.slaver_list.remove(n)
                        need_delete.append(i)
                for idx in need_delete:
                    self.info_list.removeRow(idx)
            elif mode == "ret_env_list":
                # 返回环境信息
                if ui == "main":
                    # 将环境信息显示在主窗口
                    env_info = QtCore.QStringListModel()
                    env_info.setStringList(msg)
                    self.env_list.setModel(env_info)
                elif ui == "task":
                    # 将环境信息显示在任务创建, 修改窗口
                    self.task_ctrl_window.envs.clear()
                    self.task_ctrl_window.envs.addItems(msg)
                else:
                    pass
            elif mode == "ret_test_env":
                # 返回环境测试结果
                if msg == "True":
                    QtWidgets.QMessageBox.information(self, "环境测试成功", "环境测试成功")
                else:
                    QtWidgets.QMessageBox.information(self, "环境测试失败", msg)
                self.loading_window.close()
            elif mode == "ret_py_v":
                # 返回该环境的python版本
                if msg["res"] == "seccess":
                    QtWidgets.QMessageBox.information(self, "Python版本", msg["msg"])
                elif msg["res"] == "falied":
                    QtWidgets.QMessageBox.information(self, "Python版本获取失败", msg["msg"])
                self.loading_window.close()
            elif mode == "ret_cuda_pt":
                # 返回该环境使用pytorch时的cuda可用情况
                if msg["res"] == "seccess":
                    QtWidgets.QMessageBox.information(self, "Cuda结果(PyTorch)", msg["msg"])
                elif msg["res"] == "falied":
                    QtWidgets.QMessageBox.information(self, "错误", msg["msg"])
                self.loading_window.close()
            elif mode == "ret_cuda_tf":
                # 返回该环境使用tensorflow时的cuda可用情况
                if msg["res"] == "seccess":
                    QtWidgets.QMessageBox.information(self, "Cuda结果(PyTorch)", msg["msg"])
                elif msg["res"] == "falied":
                    QtWidgets.QMessageBox.information(self, "错误", msg["msg"])
                self.loading_window.close()
            elif mode == "ret_pkgs":
                # 返回该环境的包
                if msg["res"] == "seccess":
                    self.pkg_list.setText(msg["msg"])
                elif msg["res"] == "falied":
                    QtWidgets.QMessageBox.information(self, "错误", msg["msg"])
                self.loading_window.close()
            elif mode == "ret_folder":
                # 返回当前目录下的所有文件和文件夹
                if ui == "main":
                    # 返回到主界面
                    self.curr_folder.setText(msg["curr_path"]+"/")
                    self.file_tree_model.clear()
                    self.curr_file_list = msg["file"]
                    self.curr_folder_list = msg["folder"]
                    self.curr_file.setText("")
                    if msg["curr_path"] == "":
                        self.curr_file_list = []
                        self.curr_folder_list = self.root
                        for f in self.root:
                            self.file_tree_model.appendRow(QtGui.QStandardItem(QApplication.style().standardIcon(21), f))
                        self.move.setEnabled(False)
                        self.copy.setEnabled(False)
                        self.new_folder.setEnabled(False)
                        self.new_file.setEnabled(False)
                        self.remane.setEnabled(False)
                        self.delete_file.setEnabled(False)
                        self.upload.setEnabled(False)
                    else:
                        self.curr_folder_list.insert(0, "..")
                        for folder in msg["folder"]:
                            self.file_tree_model.appendRow(QtGui.QStandardItem(QApplication.style().standardIcon(21), folder))
                        for file in msg["file"]:
                            self.file_tree_model.appendRow(QtGui.QStandardItem(QApplication.style().standardIcon(25), file))
                        self.move.setEnabled(True)
                        self.copy.setEnabled(True)
                        self.new_folder.setEnabled(True)
                        self.new_file.setEnabled(True)
                        self.remane.setEnabled(True)
                        self.delete_file.setEnabled(True)
                        self.upload.setEnabled(True)
                elif ui == "folder_select":
                    # 返回到选择文件夹界面
                    if msg["curr_path"] == "":
                        self.folder_select_window.get_curr_folder_info(msg["curr_path"]+"/", self.root, None)
                        self.folder_select_window.ok.setEnabled(False)
                    else:
                        folder_list = msg["folder"]
                        folder_list.insert(0, "..")
                        self.folder_select_window.get_curr_folder_info(msg["curr_path"]+"/", folder_list, None)
                        self.folder_select_window.ok.setEnabled(True)
                else:
                    pass
            elif mode == "ret_file":
                # 返回文件内容
                state = msg["state"]
                if state == "success":
                    try:
                        content = str(base64.b64decode(bytes(str(msg["content"]).encode("utf-8"))).decode("utf-8"))
                        self.read_file_window.textEdit.setText(content)
                        self.read_file_window.setWindowTitle("正在预览 "+msg["file_name"][1:])
                        self.read_file_window.show()
                    except:
                        QtWidgets.QMessageBox.information(self, "文件打开失败", "解码失败")
                else:
                    QtWidgets.QMessageBox.information(self, "文件打开失败", "该文件无法打开, 原因可能是其不是文本文件或包含非法字符")
            elif mode == "ret_save_file":
                # 返回文件保存结果
                if msg == "success":
                    QtWidgets.QMessageBox.information(self, "保存文件", "保存成功! ")
                else:
                    QtWidgets.QMessageBox.information(self, "保存文件", "保存失败, 原因是: \n"+msg)
                self.reflush()
            elif mode == "ret_file_operate":
                # 返回文件操作(新建, 重命名)结果
                if msg == "success":
                    self.reflush()
                else:
                    QtWidgets.QMessageBox.information(self, "文件操作失败", msg)
            elif mode == "ret_rm_file":
                # 返回文件删除结果
                if msg == "success":
                    pass
                else:
                    QtWidgets.QMessageBox.information(self, "删除文件", "删除失败, 原因是: \n"+msg)
                self.reflush()
            elif mode == "ret_add_task":
                # 返回添加任务结果
                if msg:
                    QtWidgets.QMessageBox.information(self, "添加任务", "添加失败, 原因是: \n"+msg)
                else:
                    QtWidgets.QMessageBox.information(self, "添加任务", "添加成功")
            elif mode == "task_mon":
                # 任务监控
                state_info = {"wait": "等待中", "exec": "运行中", "pexec": "准备中", "accp": "完成", "err": "错误", "offline_error": "错误, 节点掉线", "term": "中止"}
                wait_task = msg["wait"]
                wait_task_ids = []
                for item in wait_task:
                    wait_task_ids.append(item["task_id"])
                exec_task = msg["exec"]
                exec_task_ids = []
                for item in exec_task:
                    exec_task_ids.append(item["task_id"])
                hist_task = msg["hist"]
                hist_task_ids = []
                for item in hist_task:
                    hist_task_ids.append(item["task_id"])
                for item in wait_task:
                    task_id = item["task_id"]
                    if task_id not in self.curr_wait_list:
                        self.curr_wait_list.append(task_id)
                    idx = self.curr_wait_list.index(task_id)
                    self.wait_list.setItem(idx, 0, QtGui.QStandardItem(task_id))    # 任务ID
                    self.wait_list.setItem(idx, 1, QtGui.QStandardItem(item["envs"]))   # 使用环境
                    self.wait_list.setItem(idx, 2, QtGui.QStandardItem(item["path"]))   # 项目路径
                    self.wait_list.setItem(idx, 3, QtGui.QStandardItem(item["exec"]))   # 执行命令
                    self.wait_list.setItem(idx, 4, QtGui.QStandardItem(item["slaver"])) # 拟使用计算节点
                    self.wait_list.setItem(idx, 5, QtGui.QStandardItem(item["need_gpus"])) # GPU需求数
                    self.wait_list.setItem(idx, 6, QtGui.QStandardItem(item["prev"]))   # 前驱任务
                    self.wait_list.setItem(idx, 7, QtGui.QStandardItem(bool_int_to_str(item["is_urgent"]))) # 是否紧急
                    self.wait_list.setItem(idx, 8, QtGui.QStandardItem(bool_int_to_str(item["is_reuse_gpu"]))) # 是否复用GPU'
                    self.wait_list.setItem(idx, 9, QtGui.QStandardItem(state_info[item["state"]]))
                need_delete = []
                for i in range(self.wait_list.rowCount()):
                    n = self.wait_list.item(i, 0).text()
                    if n not in wait_task_ids:
                        self.curr_wait_list.remove(n)
                        need_delete.append(i)
                need_delete.sort(reverse=True)
                for idx in need_delete:
                    self.wait_list.removeRow(idx)

                for item in exec_task:
                    task_id = item["task_id"]
                    if task_id not in self.curr_exec_list:
                        self.curr_exec_list.append(task_id)
                    idx = self.curr_exec_list.index(task_id)
                    self.exec_list.setItem(idx, 0, QtGui.QStandardItem(task_id))    # 任务ID
                    self.exec_list.setItem(idx, 1, QtGui.QStandardItem(item["envs"]))   # 使用环境
                    self.exec_list.setItem(idx, 2, QtGui.QStandardItem(item["path"]))   # 项目路径
                    self.exec_list.setItem(idx, 3, QtGui.QStandardItem(item["exec"]))   # 执行命令
                    self.exec_list.setItem(idx, 4, QtGui.QStandardItem(item["slaver"])) # 拟使用计算节点
                    self.exec_list.setItem(idx, 5, QtGui.QStandardItem(item["need_gpus"])) # GPU需求数
                    self.exec_list.setItem(idx, 6, QtGui.QStandardItem(item["prev"]))   # 前驱任务
                    self.exec_list.setItem(idx, 7, QtGui.QStandardItem(bool_int_to_str(item["is_urgent"]))) # 是否紧急
                    self.exec_list.setItem(idx, 8, QtGui.QStandardItem(bool_int_to_str(item["is_reuse_gpu"]))) # 是否复用GPU'
                    self.exec_list.setItem(idx, 9, QtGui.QStandardItem(state_info[item["state"]]))
                need_delete = []
                for i in range(self.exec_list.rowCount()):
                    n = self.exec_list.item(i, 0).text()
                    if n not in exec_task_ids:
                        self.curr_exec_list.remove(n)
                        need_delete.append(i)
                need_delete.sort(reverse=True)
                for idx in need_delete:
                    self.exec_list.removeRow(idx)

                for item in hist_task:
                    task_id = item["task_id"]
                    if task_id not in self.curr_hist_list:
                        self.curr_hist_list.append(task_id)
                    idx = self.curr_hist_list.index(task_id)
                    self.history_list.setItem(idx, 0, QtGui.QStandardItem(task_id))    # 任务ID
                    self.history_list.setItem(idx, 1, QtGui.QStandardItem(item["envs"]))   # 使用环境
                    self.history_list.setItem(idx, 2, QtGui.QStandardItem(item["path"]))   # 项目路径
                    self.history_list.setItem(idx, 3, QtGui.QStandardItem(item["exec"]))   # 执行命令
                    self.history_list.setItem(idx, 4, QtGui.QStandardItem(item["slaver"])) # 拟使用计算节点
                    self.history_list.setItem(idx, 5, QtGui.QStandardItem(item["need_gpus"])) # GPU需求数
                    self.history_list.setItem(idx, 6, QtGui.QStandardItem(item["prev"]))   # 前驱任务
                    self.history_list.setItem(idx, 7, QtGui.QStandardItem(bool_int_to_str(item["is_urgent"]))) # 是否紧急
                    self.history_list.setItem(idx, 8, QtGui.QStandardItem(bool_int_to_str(item["is_reuse_gpu"]))) # 是否复用GPU'
                    self.history_list.setItem(idx, 9, QtGui.QStandardItem(state_info[item["state"]]))
                need_delete = []
                for i in range(self.history_list.rowCount()):
                    n = self.history_list.item(i, 0).text()
                    if n not in hist_task_ids:
                        self.curr_hist_list.remove(n)
                        need_delete.append(i)
                need_delete.sort(reverse=True)
                for idx in need_delete:
                    self.history_list.removeRow(idx)
            elif mode == "ret_change_task":
                if msg == "":
                    QtWidgets.QMessageBox.information(self, "修改任务", "修改成功")
                else:
                    QtWidgets.QMessageBox.information(self, "修改任务", msg)
            elif mode == "ret_del_task":
                task_id = msg["task_id"]
                ret = msg["info"]
                if ret == "":
                    QtWidgets.QMessageBox.information(self, "删除任务 "+task_id, "任务"+ task_id+"删除成功")
                else:
                    QtWidgets.QMessageBox.information(self, "删除任务"+task_id, msg)
            elif mode == "ret_resub_task":
                task_id = msg["task_id"]
                ret = msg["info"]
                if ret == "":
                    QtWidgets.QMessageBox.information(self, "重新提交任务 ", "重新提交任务成功, 新任务ID为"+ task_id)
                else:
                    QtWidgets.QMessageBox.information(self, "重新提交任务", msg)
            elif mode == "ret_root":
                # 返回服务器可见目录
                self.root = copy.deepcopy(msg)
                self.curr_folder_list = copy.deepcopy(msg)
                for f in self.root:
                    self.file_tree_model.appendRow(QtGui.QStandardItem(QApplication.style().standardIcon(21), f))
            elif mode == "ret_stop_task":
                # 返回停止结果
                task_id = msg["task_id"]
                if msg["info"] == "success":
                    QtWidgets.QMessageBox.information(self, "停止任务 ", "任务"+ task_id+ "停止成功")
                elif msg["info"] == "failed":
                    QtWidgets.QMessageBox.information(self, "停止任务 ", "任务"+ task_id+ "停止失败, 原因是该任务已不在执行区中")
            elif mode == "ret_train_log_path":
                # 返回日志路径
                self.train_log_th.set_log_path(msg)
                self.train_log_th.set_run(True)
                self.train_log_th.start()
            elif mode == "ret_server_log":
                # 返回服务端日志路径
                self.server_log_th.set_log_path(msg)
                self.server_log_th.set_run(True)
                if not self.server_log_th.isRunning():
                    self.server_log_th.start()

            elif mode == "recv_err":
                # 服务端消息接收失败
                self.loading_window.close()
                QtWidgets.QMessageBox.information(self, "消息接收失败", "消息接收失败, 请再试一次")

            elif mode == "conn_err":
                # 服务端连接失败
                self.loading_window.close()
                QtWidgets.QMessageBox.information(self, "服务器连接失败", msg)
                self.data_th.terminate()
                self.monitor_th.terminate()
        except Exception as e:
            self.recv_err_count += 1
            if self.recv_err_count == 3:
                QtWidgets.QMessageBox.information(self, "消息接收失败", "消息接收失败")
                self.recv_err_count = 0


from player.player import Player
from typing import Dict, List, Optional
import numpy as np
import tensorflow as tf
import itertools
from collections import deque
import random
import pandas as pd
import os
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from player.deep_neural_network_model import DnnModel
from tensorflow.keras.optimizers import Adam

def saveCSV(result: list, path):
    path += 'result.csv'
    sc = pd.DataFrame(result, columns=['reward', 'success', 'failure', 'sensing'])
    sc.to_csv(path, index=False)

class DqnPlayer(Player):
    def __init__(self, identifier: str, max_num_unit_packet: int, observation_history_length: int,
                 sensing_unit_packet_length_ratio: int, unit_packet_success_reward: float,
                 unit_packet_failure_reward: float, dnn_layers_list: List[Dict], random_sensing_prob: float,
                 sensing_discount_factor: float, dnn_learning_rate: float, scenario: int, modelNumber: int):
        super(DqnPlayer, self).__init__(identifier)
        self._freq_channel_list: List[int] = []
        self._num_freq_channel = 0
        self._max_num_unit_packet = max_num_unit_packet
        self._freq_channel_combination = []
        self._num_freq_channel_combination = 0
        self._num_action = 0
        self._observation_history_length = observation_history_length
        self._sensing_unit_packet_length_ratio = sensing_unit_packet_length_ratio
        self._observation_history = np.empty(0)
        self._cca_thresh = -70
        self._replay_memory = deque()
        self._latest_observation_dict = None
        self._unit_packet_success_reward = unit_packet_success_reward
        self._unit_packet_failure_reward = unit_packet_failure_reward
        self._main_dnn: Optional[tf.keras.Model] = None
        self._target_dnn: Optional[tf.keras.Model] = None
        self._dnn_layers_list = dnn_layers_list
        self._random_sensing_prob = random_sensing_prob
        self._sensing_discount_factor = sensing_discount_factor
        self._dnn_learning_rate = dnn_learning_rate
        self._scenario = scenario
        self._modelNumber = modelNumber
        self._result = []

    def connect_to_server(self, server_address: str, server_port: int):
        super(DqnPlayer, self).connect_to_server(server_address, server_port)
        self._freq_channel_list = self.operator_info['freq channel list']
        self._num_freq_channel = len(self._freq_channel_list)
        self._freq_channel_combination = [np.where(np.flip(np.array(x)))[0].tolist()
                                          for x in itertools.product((0, 1), repeat=self._num_freq_channel)][1:]
        self._num_freq_channel_combination = 2 ** self._num_freq_channel - 1
        self._num_action = self._num_freq_channel_combination * self._max_num_unit_packet + 1
        self._observation_history = np.zeros((self._observation_history_length, self._num_freq_channel, 2))
        initial_action = {'type': 'sensing'}
        self._latest_observation_dict = self.step(initial_action)
        self.update_observation_history(initial_action, self._latest_observation_dict)
        self._main_dnn = DnnModel(conv_layers_list=self._dnn_layers_list, num_action=self._num_action)
        self._target_dnn = DnnModel(conv_layers_list=self._dnn_layers_list, num_action=self._num_action)
        self._main_dnn.compile(optimizer=Adam(lr=self._dnn_learning_rate), loss="mse")

    def train_dnn(self, num_episodes: int, replay_memory_size: int, mini_batch_size: int, initial_epsilon: float,
                  epsilon_decay: float, min_epsilon: float, dnn_epochs: int, progress_report: bool,
                  test_run_length: int):
        epsilon = initial_epsilon
        for episode in range(num_episodes):
            if progress_report:
                print(f"Episode: {episode} (epsilon: {epsilon})")
            self.accumulate_replay_memory(replay_memory_size, epsilon, progress_report)
            observation, target_reward = self.get_mini_batch(mini_batch_size)
            checkpoint_path = "scenario_1/cp.ckpt"
            self._main_dnn.fit(observation, target_reward, epochs=dnn_epochs)
            epsilon *= epsilon_decay
            epsilon = max(epsilon, min_epsilon)
            self._target_dnn.set_weights(self._main_dnn.get_weights())
            if test_run_length > 0:
                self.test_run(test_run_length)
            # if episode % 20 == 0:
            #     self.model_save(scenario=self._scenario, modelNumber=self._modelNumber, episode=episode)
        self.model_save(scenario=self._scenario, modelNumber=self._modelNumber, episode=num_episodes, CSV=True)

    def get_main_dnn_action_and_value(self, observation: np.ndarray):
        return self.get_dnn_action_and_value(self._main_dnn, observation)

    def get_target_dnn_action_and_value(self, observation: np.ndarray):
        return self.get_dnn_action_and_value(self._target_dnn, observation)

    @staticmethod
    def get_dnn_action_and_value(dnn: tf.keras.Model, observation: np.ndarray):
        single = False
        if observation.ndim == 3:
            observation = observation[np.newaxis, ...]
            single = True
        action_value = dnn.predict(observation)
        best_action = np.argmax(action_value, axis=1)
        best_value = np.amax(action_value, axis=1)
        if single:
            best_action = best_action[0]
            best_value = best_value[0]
            action_value = action_value[0]
        return best_action, best_value, action_value

    def get_random_action(self, sensing_prob: float):
        tx_data_prob = (1 - sensing_prob) / (self._num_action - 1)
        distribution = tx_data_prob * np.ones(self._num_action)
        distribution[0] = sensing_prob
        return int(np.random.choice(np.arange(self._num_action), p=distribution))

    def get_next_action(self, observation: np.ndarray, random_prob: float):
        if np.random.rand() < random_prob:
            action = self.get_random_action(self._random_sensing_prob)
        else:
            action, _, _ = self.get_main_dnn_action_and_value(observation)
        return int(action)

    def accumulate_replay_memory(self, replay_memory_size: int, random_prob: float, progress_report: bool):
        self._replay_memory.clear()
        for ind in range(replay_memory_size):
            if progress_report:
                print(f"Replay memory sample: {ind}/{replay_memory_size}\r", end='')
            prev_observation_history = self._observation_history
            action_index = self.get_next_action(prev_observation_history, random_prob)
            action_dict = self.convert_action_index_to_dict(action_index)
            observation_dict = self.step(action_dict)
            self._latest_observation_dict = observation_dict
            reward = self.get_reward(action_dict, observation_dict)
            self.update_observation_history(action_dict, observation_dict)
            current_observation_history = self._observation_history
            experience = (prev_observation_history, action_index, reward, current_observation_history)
            self._replay_memory.append(experience)
        if progress_report:
            print()

    def test_run(self, length: int):
        tx_success = 0
        tx_failure = 0
        sensing = 0
        reward = 0
        time = 0
        for ind in range(length):
            print(f"Test run: {ind}/{length}\r", end='')
            action_index, _, _ = self.get_main_dnn_action_and_value(self._observation_history)
            action_dict = self.convert_action_index_to_dict(int(action_index))
            observation_dict = self.step(action_dict)
            self.update_observation_history(action_dict, observation_dict)
            observation_type = observation_dict['type']
            if observation_type == 'sensing':
                sensing += 1
                time += 1
            elif observation_type == 'tx_data_packet':
                tx_freq_channel_list = action_dict['freq_channel_list']
                success_freq_channel_list = observation_dict['success_freq_channel_list']
                failure_freq_channel_list = list(set(tx_freq_channel_list) - set(success_freq_channel_list))
                num_unit_packet = action_dict['num_unit_packet']
                tx_time = self._sensing_unit_packet_length_ratio * num_unit_packet
                time += tx_time
                tx_success += len(success_freq_channel_list) * tx_time
                tx_failure += len(failure_freq_channel_list) * tx_time
            reward += self.get_reward(action_dict, observation_dict)
        reward /= time
        tx_success /= (time * self._num_freq_channel)
        tx_failure /= (time * self._num_freq_channel)
        sensing /= time
        self._result.append([reward, tx_success, tx_failure, sensing])
        print(f"\nReward: {reward}, Sensing: {sensing}, Tx Success: {tx_success}, Tx Failure: {tx_failure}")

    def get_mini_batch(self, batch_size: int):
        samples = random.sample(self._replay_memory, batch_size)
        observation = np.stack([x[0] for x in samples], axis=0)
        next_observation = np.stack([x[3] for x in samples], axis=0)
        _, _, target_action_reward = self.get_main_dnn_action_and_value(observation)
        _, future_reward, _ = self.get_target_dnn_action_and_value(next_observation)
        for ind, sample in enumerate(samples):
            action = sample[1]
            action_dict = self.convert_action_index_to_dict(action)
            discount_factor = self._sensing_discount_factor
            if action_dict['type'] == 'tx_data_packet':
                num_unit_packet = action_dict['num_unit_packet']
                discount_factor = discount_factor ** (num_unit_packet * self._sensing_unit_packet_length_ratio)
            immediate_reward = sample[2]
            target_action_reward[ind, action] = immediate_reward + discount_factor * future_reward[ind]
        return observation, target_action_reward

    def convert_action_index_to_dict(self, action_index: int) -> Dict:
        """ Convert action index to dictionary form
        Args:
            action_index: index of action (0: sensing, 1 to (2^num_freq_channel-1)*max_num_unit_packet: tx_data_packet)
        Returns:
            action in dictionary form
                'type': 'sensing' or 'tx_data_packet',
                'freq_channel_list': list of frequency channels for data transmission
                'num_unit_packet': number of unit packets
        """
        if action_index == 0:
            action_dict = {'type': 'sensing'}
        else:
            num_unit_packet = (action_index - 1) // self._num_freq_channel_combination + 1
            freq_channel_combination_index = (action_index - 1) % self._num_freq_channel_combination
            freq_channel_list = self._freq_channel_combination[freq_channel_combination_index]
            action_dict = {'type': 'tx_data_packet', 'freq_channel_list': freq_channel_list,
                           'num_unit_packet': num_unit_packet}
        return action_dict

    def update_observation_history(self, action: Dict, observation: Dict):
        observation_type = observation['type']
        new_observation = np.zeros((self._num_freq_channel, 2))
        new_observation_length = 1
        if observation_type == 'sensing':
            sensed_power = observation['sensed_power']
            occupied_channel_list = [int(freq_channel) for freq_channel in sensed_power
                                     if sensed_power[freq_channel] > self._cca_thresh]
            new_observation[occupied_channel_list, 0] = 1
            new_observation_length = 1
        elif observation_type == 'tx_data_packet':
            tx_freq_channel_list = action['freq_channel_list']
            success_freq_channel_list = observation['success_freq_channel_list']
            failure_freq_channel_list = list(set(tx_freq_channel_list) - set(success_freq_channel_list))
            num_unit_packet = action['num_unit_packet']
            new_observation[failure_freq_channel_list, 1] = 1

            new_observation_length = num_unit_packet * self._sensing_unit_packet_length_ratio
        new_observation = np.broadcast_to(new_observation, (new_observation_length, self._num_freq_channel, 2))
        self._observation_history = np.concatenate((new_observation, self._observation_history),
                                                   axis=0)[:self._observation_history_length, ...]

    def get_reward(self, action: Dict, observation: Dict):
        observation_type = observation['type']
        reward = 0
        if observation_type == 'sensing':
            reward = 0
        elif observation_type == 'tx_data_packet':
            num_unit_packet = action['num_unit_packet']
            num_tx_packet = len(action['freq_channel_list'])
            num_success_packet = len(observation['success_freq_channel_list'])
            num_failure_packet = num_tx_packet - num_success_packet
            reward = (num_success_packet * self._unit_packet_success_reward +
                      num_failure_packet * self._unit_packet_failure_reward) * num_unit_packet
        return reward

    def model_save(self, scenario: int, modelNumber: int, episode: int, CSV: bool = False):
        num = 1
        path = 'savedModel/scenario_%d/model_%d/' % (scenario, modelNumber)
        self._main_dnn.save(path+"episode_%d" % episode)
        if CSV:
            saveCSV(self._result, path)


import os

from werkzeug.exceptions import NotFound, Conflict, BadRequest

from src.email.sendgrid_email import send_registration_email, decode_user_info
from src.utils.logging import Logger
from swagger_server.models import OrgUser
from swagger_server.models.inline_response2003 import InlineResponse2003  # noqa: E501
from src.auth.jwt import decode_token
import src.db.mysql_interface as db
from src.constants_enums.obj_keys import *
from src.constants_enums.privileges import PrivilegeLevels

def accept_org_invite(encrypted_data):  # noqa: E501
    """Accept invitation user to org

    Use this to invite user to org # noqa: E501

    :param encrypted_data:
    :type encrypted_data: str

    :rtype: None
    """
    print(encrypted_data)
    new_user = decode_user_info(encrypted_data)
    uid_tuple = db.get_user_id(new_user[UserInfoKeys.EMAIL])
    if not uid_tuple or len(uid_tuple) == 0:
        raise NotFound("No user associated with that email")
    org_user_info_tuple = db.get_organization(new_user[OrgInfoKeys.ORG_ID], uid_tuple[0])
    if org_user_info_tuple is None or len(org_user_info_tuple) == 0:
        raise NotFound("Cannot find the organization to accept invite")
    if PrivilegeLevels.INVITEE != org_user_info_tuple[2]:
        raise Conflict("You can only accept invitations if you are invited")
    db.update_privilege(uid_tuple[0], new_user[OrgInfoKeys.ORG_ID], PrivilegeLevels.MEMBER)

    url = os.getenv('POLLINATION_URL')
    return "invitation accepted", 301, {'Location': f'https://{url}'}

def change_user_privilege(body, token_info):  # noqa
    """Change user privileges

    user privileges are:
 - 0 := invited
 - 1 := member
 - 2 := admin
 - 3 := owner  # noqa: E501

    :param body: Information about the user to have the privileges changed
    :type body: dict | bytes

    :rtype: None
    """
    uid = body[UserInfoKeys.UID]
    privilege = body[OrgInfoKeys.PRIVILEGE]
    org_id = body[OrgInfoKeys.ORG_ID]
    db.update_privilege(uid, org_id, privilege)
    return None

def get_org_users(org_id, privilege_level=1):  # noqa: E501
    """Fetch org users

    Get all users # noqa: E501

    :rtype: InlineResponse2003
    """
    if org_id is None:
        raise BadRequest("must provide org_id in url params")
    org_users = db.get_users_from_org(org_id, privilege_level)
    if org_users is None:
        raise NotFound("No such organization")
    org_user_models = []
    for org_user in org_users:
        uid = org_user[0]
        first_name = org_user[1]
        last_name = org_user[2]
        email = org_user[3]
        dob = org_user[4]
        privilege = org_user[5]
        user_org_id = org_user[6]
        org_user_models.append(OrgUser(uid, first_name, last_name, email, dob, privilege=privilege,
                                       user_org_id=user_org_id))
    return InlineResponse2003(org_user_models)

def remove_org_user(body, token_info):  # noqa
    """Kick user from org

     # noqa: E501

    :param body:
    :type body: dict | bytes

    :rtype: None
    """
    change_user_privilege({
        OrgInfoKeys.PRIVILEGE: PrivilegeLevels.REMOVED,
        UserInfoKeys.UID: body[UserInfoKeys.UID]
    }, token_info)
    return None

def log_invite(email, logger):
    log_string = f"\nREJECTED INVITATION: \n" \
                 f"Reason: user not found\n" \
                 f"email: {email}"
    logger.add_entry(log_string)

def org_invite_user(body, token_info):  # noqa
    """Add user to org

    Use this to invite user to org # noqa: E501

    :param body: A list of info for users to be invited
    :type body: dict | bytes

    :rtype: None
    """
    users_to_invite = body[OrgInfoKeys.INVITES]
    org_id = body[OrgInfoKeys.ORG_ID]
    logger = Logger(f'logs/invitations/org_{org_id}log')
    org_name_tuple = db.get_organization(org_id, token_info[JwtTokenKeys.UID])
    org_name = org_name_tuple[1]
    for user in users_to_invite:
        uid_tuple = db.get_user_id(user[UserInfoKeys.EMAIL])
        email = user[UserInfoKeys.EMAIL]
        if not uid_tuple or len(uid_tuple) == 0:
            log_invite(email, logger)
            continue
        uid = uid_tuple[0]
        voting_token_tuple = db.get_user_token(uid)
        voting_token = voting_token_tuple[0]
        user_org_id = user[OrgInfoKeys.USER_ORG_ID]
        db.invite_user(email, user_org_id, org_id)
        send_registration_email(org_name, org_id, email, voting_token)
    return None


import re

import pytest
from scootbot3.client import make_client
from scootbot3.listener import listen
from slack_sdk.socket_mode.request import SocketModeRequest
from slack_sdk.socket_mode.response import SocketModeResponse

@pytest.fixture
def client():
    return make_client()

@pytest.fixture
def make_request():
    """
    pytest fixture factory to create a SocketModeRequest.

    This fixture factory returns a function with signature:

        make_request(text: str) -> SocketModeRequest

    The returned SocketModeRequest will have the following properties:
    - type: "events_api"
    - envelope_id: "test_envelope_id"
    - payload: {"event": {"channel": "test_channel", "text": text}}
    - accepts_response_payload: True
    """

    def _make_request(text):
        return SocketModeRequest(
            type="events_api",
            envelope_id="test_envelope_id",
            payload={"event": {"channel": "test_channel", "text": text}},
            accepts_response_payload=True,
        )

    return _make_request

@pytest.fixture
def test_listener(mocker, client, make_request):
    """
    pytest fixture factory to test if the scootbot listener responds as expected
    to a given input.

    This fixture factory returns a function with signature:

        test_listener(input: str, output: re.Pattern) -> None

    The listener will be presented with input message text and we will assert
    that its response matches the given output pattern.
    """

    def _test_listener(input: str, expected_output: re.Pattern) -> None:
        # mock
        post_mock = mocker.patch.object(client.web_client, "chat_postMessage")
        make_response_mock = mocker.patch.object(
            SocketModeResponse, "__init__", return_value=None
        )
        mocker.patch.object(client, "send_socket_mode_response")

        # send a request to the listener
        listen(client, make_request(input))

        # check mocks
        make_response_mock.assert_called_once_with(envelope_id="test_envelope_id")
        assert post_mock.call_args.kwargs["channel"] == "test_channel"
        assert expected_output.match(post_mock.call_args.kwargs["text"])

    return _test_listener


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def sumNumbers(self, root: Optional[TreeNode]) -> int:
        print(root)
        self.ans = 0
        def dfs(root,curr):
            curr = curr * 10 + root.val
            if not root.right and not root.left:
                self.ans += curr
                return

            if root.left:
                dfs(root.left,curr)

            if root.right:
                dfs(root.right,curr)

            curr //= 10

        dfs(root,0)
        return self.ans


"""
17 Letter Combinations of a Phone Number
Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.
Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
Note:
Although the above answer is in lexicographical order, your answer could be in any order you want.
"""
# Recursive solution on 7/15/2020
def letterCombinations(digits):
    if len(digits) < 1: return []
    digitstr = {
                "2":list("abc"),
                "3":list("def"),
                "4":list("ghi"),
                "5":list("jkl"),
                "6":list("mno"),
                "7":list("pqrs"),
                "8":list("tuv"),
                "9":list("wxyz")
                }
    result = []
    if len(digits) == 1:
        return digitstr[digits]

    for prefix in digitstr[digits[0]]:
        for suffix in letterCombinations(digits[1:]):
            result.append(prefix + suffix)

    return result

# 1st try iterative solution
def letterCombinations2(digits):
    """
    :type digits: str
    :rtype: List[str]
    """
    result = []

    if not digits.isdigit():
        return result

    digitstr = {"0":" ",
                "1":"*",
                "2":"abc",
                "3":"def",
                "4":"ghi",
                "5":"jkl",
                "6":"mno",
                "7":"pqrs",
                "8":"tuv",
                "9":"wxyz"
                }

    result = []
    for digit in digits:
        s = digitstr[digit]
        if len(result) == 0:
            for c in s:
                result.append(c)
        else:
            n = len(result)
            for i in range(n): # bug fixed. Should not use "for e in result" because result will increase size in each for loop and this will never end
                e = result[i]
                for c in s:
                    result.append(e + c)
            result = result[n:]

    return result

test_case = ["23"]
for case in test_case:
    print(letterCombinations(case))


from asgiref.sync import sync_to_async
from django.db.models import QuerySet
from logrich.logger_ import errlog, log  # noqa

from src.auth.schemas.ads import AdAttr, AdCreate, AdScheme
from src.auth.users.exceptions import AdNotExists
from src.django_space.ads.models import Ads

class AdManager:
    def __init__(self) -> None:
        self.objects = Ads.objects

    async def create(self, ad_create: AdCreate) -> AdScheme:
        """Вернуть или создать объявление"""
        ad, _ = await self.objects.aget_or_create(
            name=ad_create.name,
            price=ad_create.price,
            desc=ad_create.desc,
        )
        return await sync_to_async(AdScheme.from_orm)(ad)

    async def update(self, ad: Ads, payload: dict) -> Ads:
        """update ad"""

        await self.objects.filter(pk=ad.pk).aupdate(**payload)
        ad: Ads = await self.get_one_by_uniq_attr(AdAttr(attr=ad.pk))

        return ad

    async def delete(self, ad: Ads) -> None:
        """remove ad"""
        await self.objects.filter(pk=ad.pk).adelete()

    async def get_one_by_uniq_attr(self, ad_attr: AdAttr) -> Ads | None:
        """get one ad by uniq attr"""
        attr = ad_attr.attr
        if isinstance(attr, int) or attr.isdigit():
            ad_in_db = await self.objects.filter(pk=attr).afirst()
        else:
            ad_in_db = await self.objects.filter(name=attr).afirst()
        if not ad_in_db:
            raise AdNotExists(ad=attr)
        return ad_in_db

    async def get_list_ads(self) -> QuerySet:
        """Вернёт список объявлений"""
        ads = await sync_to_async(self.objects.all)()
        return ads


#!/usr/bin/env python
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import sklearn.datasets
import pandas as pd
from tensorflow import keras
tf.random.set_seed(0)

def main():
    test_data=np.load('test_data.npy')
    test_label=np.load('test_label.npy')
    training_data=np.load('training_data.npy')
    training_label=np.load('training_label.npy')

    model=keras.Sequential()
    model.add(keras.layers.LSTM(30, input_shape=(training_data.shape[1], training_data.shape[2])))
    model.add(keras.layers.Dense(1,))

    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate=0.001),
        loss='mean_squared_error',
        metrics=['accuracy']
        )

    model.fit(training_data, training_label, epochs=25, batch_size=32, verbose=1)

    plt.figure(figsize=(10,8))

    plt.subplot(3,1,1)
    plt.plot(range(training_label.shape[0]),training_label, label='Training data')
    plt.plot(range(training_label.shape[0], training_label.shape[0]+test_label.shape[0]) ,test_label, label='Test data')
    plt.title('Training data')
    plt.xlabel('Time')
    plt.ylabel('Data')
    plt.legend()

    plt.subplot(3,2,3)
    plt.plot(range(test_label.shape[0]) ,test_label, label='true')
    plt.plot(range(test_label.shape[0]) ,model.predict(test_data), label='pred')
    plt.title('Test prediction')
    plt.xlabel('Time')
    plt.ylabel('Data')
    plt.legend()

    plt.subplot(3,2,4)
    plt.plot(range(100) ,test_label[0:100], label='true')
    plt.plot(range(100) ,model.predict(test_data)[0:100], label='pred')
    plt.title('First 100 days')
    plt.xlabel('Time')
    plt.ylabel('Data')
    plt.legend()

    plt.subplot(3,2,5)
    plt.plot(range(test_label.shape[0]) ,test_label-model.predict(test_data), label='true')
    plt.title('True - prediction')
    plt.xlabel('Time')
    plt.ylabel('Difference')
    plt.legend()

    plt.subplot(3,2,6)
    plt.scatter(model.predict(test_data), test_label, s=2, color='black')
    plt.title('Scatter plot')
    plt.xlabel('Prediction')
    plt.ylabel('True')
    plt.legend()

    plt.subplots_adjust(hspace = 0.5, wspace=0.3)
    plt.show()

    return 0

if __name__ == '__main__':
    main()


import os
import zipfile

import pandas as pd

class SML2010Data:
    PATH = os.sep.join(['.', 'data', 'SML2010'])
    FILE = 'NEW-DATA.zip'
    ARCH = os.sep.join([PATH, FILE])
    COLNAMES = ['Date', 'Time',
                'Temperature_dining_room', 'Temperature_room', 'Weather_temperature',
                'CO2_dining_room', 'CO2_room',
                'Humidity_dining_room', 'Humidity_room',
                'Lighting_dining_room', 'Lighting_room',
                'Rain', 'Sun_dusk', 'Wind',
                'Sun_light_west', 'Sun_light_east', 'Sun_light_south',
                'Sun_irradiance',
                'Enthalpic_motor_1', 'Enthalpic_motor_2', 'Enthalpic_motor_turbo',
                'Outdoor_temperature', 'Outdoor_relative_humidity', 'Day_of_week'
                ]

    @staticmethod
    def new_data_1():
        with zipfile.ZipFile(SML2010Data.ARCH, mode='r') as myzip:
            with myzip.open('NEW-DATA-1.T15.txt') as fp:
                data = pd.read_table(fp, header=None, skiprows=1, sep=' ',
                                     names=SML2010Data.COLNAMES)

        return data

    @staticmethod
    def new_data_2():
        with zipfile.ZipFile(SML2010Data.ARCH, mode='r') as myzip:
            with myzip.open('NEW-DATA-2.T15.txt') as fp:
                data = pd.read_table(fp, header=None, skiprows=1, sep=' ',
                                     names=SML2010Data.COLNAMES)

        return data

    @staticmethod
    def get_data():
        df1 = SML2010Data.new_data_1()
        df2 = SML2010Data.new_data_2()

        data = pd.concat([df1, df2])

        return data


# Problem 4: Find the largest palindrome made from the product of two 3-digit numbers

def palindromeCheck(n):
    digits = [int(x) for x in str(n)] # Decompose digits into a list

    for i in range(0, int(len(digits) / 2), 1):
        # If at any point palindrome pattern is broken, return false
        if digits[i] != digits[len(digits) - i - 1]:
            return False

    return True

largest = 0

for i in range(999, 100, -1):
    j = i
    # Sub-index j sets to current i, because multiplication is commutative
    while j >= 100:
        product = i * j
        # Checks if product is palindrome AND bigger than current largest held value
        # This could probably be more efficient with better python knowledge
        if palindromeCheck(product) == True and product > largest:
            largest = product
            # Breaks out of while loop if a palindrome is found due to diminishing returns
            break
        j -= 1

print(largest) # Solution: 906609


# -*- coding: utf-8 -*-
"""
Created on Sat Feb 26 22:16:33 2022

@author: ADMIN
"""
import random

doors=[0]*3
goatdoor=[0]*2
swap=0                 #no. swap wins
dont_swap=0             #no. of dont swap wins
j=0
while(j<10):
    x=random.randint(0,2)    #xth door comprise of BMW
    doors[x]="BMW"
    for i in range(0,3):
        if (i==x):
            continue
        else:
            doors[i]="Goat"
            goatdoor.append(i)
    choice=int(input("Enter your choice: "))
    door_open=random.choice(goatdoor)                  #open door that comprises of goat

    while(door_open==choice):                        #door open shouldn't be equal to choice made by participants
        door_open=random.choice(goatdoor)
    ch=input("Do you want to swap? y/n: ")
    if(ch=='y'):
        if(doors[choice]=='Goat'):
            print("Hurrray,You Won!")
            swap=swap+1
        else:
            print("Ohh oh! You Lost")
    else:
        if(doors[choice]=="Goat"):
            print("Ohh oh! You lost")
        else:
            print("Hurray,You won!")
            dont_swap=dont_swap+1
    j=j+1
print(swap)
print(dont_swap)

#higher probablity of winning if you swap.