# coding: utf-8

import os
import zipfile
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import *

from chardet import detect
from com import *
from fastpip import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from res.res import *
from settings import *
from ui import *

from .messagebox import MessageBox
from .query_file_path import QueryFilePath

DEFAULT_CUSTOM_TMPDIR = "scf_reqs"
DEFAULT_GENERATED_DIR = "scf_dist"

class CloudFunctionWindow(Ui_cloud_function, QMainWindow, QueryFilePath):
    REQUIRE_FILE = "requirements.txt"
    signal_packing = pyqtSignal(bool, str)
    signal_reqinstalled = pyqtSignal(int, str, bool)
    signal_show_workingtips = pyqtSignal(str)

    def __init__(self, parent):
        super().__init__(parent)
        self.setupUi(self)
        self.__setup_other_widgets()
        self.config = CloudFunctionConfig()
        self.thread_repo = ThreadRepo(500)
        self.environments: Optional[List[EnvDisplayPair]] = None
        self.__signal_connection()
        self.__path_ctrlgroup = (
            self.uiComboBox_preject_path,
            self.uiPushButton_clear_path,
            self.uiPushButton_select_projectdir,
        )
        self.__work_ctrlgroup = (
            self.uiTableWidget_reqirement_lines,
            self.uiPushButton_add_line,
            self.uiPushButton_delete_line,
            self.uiPushButton_start_scfpacking,
        )
        self.__change_ctrlgroups_state(False)

    def display(self):
        self.resize(*self.config.window_size)
        if self.isMaximized():
            self.showMaximized()
        else:
            self.showNormal()
        if self.thread_repo.is_empty():
            self.config_dict_to_widgets()

    def __save_window_size(self):
        if self.isMinimized() or self.isMinimized():
            return
        self.config.window_size = self.width(), self.height()

    def closeEvent(self, event: QCloseEvent):
        if not self.thread_repo.is_empty():
            MessageBox(
                "提示",
                "打包任务正在进行，关闭此窗口不会结束任务，但关闭启动窗口可能会强行结束任务！",
                QMessageBox.Warning,
                parent=self,
            ).exec_()
        self.__save_window_size()
        self.config_widgets_to_dict()
        self.config.save_config()

    def keyPressEvent(self, event: QKeyEvent):
        if event.key() == Qt.Key_Escape:
            self.close()

    def eventFilter(self, sender: QObject, event: QEvent) -> bool:
        if (
            sender == self.uiLabel_whatis_cloudfunction
            and event.type() == QEvent.MouseButtonRelease
        ):
            QDesktopServices.openUrl(
                QUrl("https://cloud.tencent.com/product/scf")
            )
            return True
        elif (
            sender == self.uiLabel_set_excludes
            and event.type() == QEvent.MouseButtonRelease
        ):
            CloudExcludesWindow(self).display()
            return True
        return super().eventFilter(sender, event)

    def __signal_connection(self):
        self.uiPushButton_clear_path.clicked.connect(
            self.__remove_combo_project_path
        )
        self.uiPushButton_select_projectdir.clicked.connect(
            lambda: self.__select_dirpath_settext(
                self.uiComboBox_preject_path.setCurrentText
            )
        )
        self.uiPushButton_start_scfpacking.clicked.connect(
            self.__start_cloud_function_packing
        )
        self.uiCheckBox_confirm_project_path.clicked.connect(
            self.__checkbox_confirm_projectpath
        )
        self.uiPushButton_add_line.clicked.connect(self.__add_requirement_line)
        self.uiPushButton_delete_line.clicked.connect(
            self.__delete_requirement_line
        )
        self.uiPushButton_save_config.clicked.connect(
            self.__save_current_configs
        )
        self.uiPushButton_del_scfconfig.clicked.connect(
            self.__delete_selected_config
        )
        self.uiPushButton_switch_config.clicked.connect(
            self.__checkout_saved_config
        )
        self.uiRadioButton_using_projectdir.clicked.connect(
            self.__workingdir_option_changed
        )
        self.uiRadioButton_using_customtemp.clicked.connect(
            self.__workingdir_option_changed
        )
        self.uiRadioButton_using_autotempdir.clicked.connect(
            self.__workingdir_option_changed
        )
        self.uiPushButton_select_customdir.clicked.connect(
            lambda: self.__select_dirpath_settext(
                self.uiLineEdit_customdir_path.setText
            )
        )
        self.uiPushButton_select_generateddir.clicked.connect(
            lambda: self.__select_dirpath_settext(
                self.uiLineEdit_generateddir.setText
            )
        )
        self.signal_packing.connect(self.__on_scfpacking_completed)
        self.signal_reqinstalled.connect(self.__set_requirement_install_result)
        self.signal_show_workingtips.connect(self.uiLabel_working_tips.setText)
        self.uiListWidget_config_list.clicked.connect(
            self.__scfconfig_list_clicked
        )

    def __setup_other_widgets(self):
        self.uiComboBox_python_envs.setView(QListView())
        self.uiComboBox_preject_path.setView(QListView())
        delegate = ItemDelegate(self.uiTableWidget_reqirement_lines, False)
        self.uiTableWidget_reqirement_lines.setItemDelegateForColumn(
            1, delegate
        )
        self.uiTableWidget_reqirement_lines.setItemDelegateForColumn(
            2, delegate
        )
        horiz_head = self.uiTableWidget_reqirement_lines.horizontalHeader()
        horiz_head.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        horiz_head.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        self.uiComboBox_preject_path.lineEdit().clear()
        self.uiLabel_set_excludes.installEventFilter(self)
        self.uiLabel_whatis_cloudfunction.installEventFilter(self)
        self.__working_movie = QMovie(":/loading.gif")
        self.__working_movie.setScaledSize(QSize(16, 16))
        self.uiLabel_working_movie.setMovie(self.__working_movie)
        self.uiLabel_working_movie.hide()

    def __show_working(self):
        self.uiLabel_working_tips.clear()
        self.__working_movie.start()
        self.uiLabel_working_movie.show()

    def __hide_working(self):
        self.uiLabel_working_tips.clear()
        self.__working_movie.stop()
        self.uiLabel_working_movie.hide()

    def __load_requirements_update_table(self, confirm: bool):
        if confirm:
            project = self.uiComboBox_preject_path.currentText()
            requirements = os.path.join(project, self.REQUIRE_FILE)
            if not os.path.isfile(requirements):
                return
            with open(requirements, "rb") as b:
                encoding = detect(b.read())["encoding"]
            with open(requirements, "rt", encoding=encoding) as f:
                content_lines = f.readlines()
            self.uiTableWidget_reqirement_lines.clearContents()
            self.uiTableWidget_reqirement_lines.setRowCount(len(content_lines))
            for row, line in enumerate(content_lines):
                self.uiTableWidget_reqirement_lines.setItem(
                    row, 0, QTableWidgetItem(line.strip())
                )
        else:
            self.uiTableWidget_reqirement_lines.setRowCount(0)

    def __change_ctrlgroups_state(self, confirm: bool):
        for ctrl in self.__work_ctrlgroup:
            ctrl.setEnabled(confirm)
        for ctrl in self.__path_ctrlgroup:
            ctrl.setEnabled(not confirm)

    def __checkbox_confirm_projectpath(self):
        project = self.uiComboBox_preject_path.currentText()
        confirm = self.uiCheckBox_confirm_project_path.isChecked()
        if os.path.isabs(project) and os.path.isdir(project):
            if confirm:
                self.__check_add_projectpath(project)
                self.uiLineEdit_generatedname.setPlaceholderText(
                    os.path.basename(project)
                )
                self.uiLineEdit_generateddir.setPlaceholderText(
                    os.path.join(project, DEFAULT_GENERATED_DIR)
                )
                self.uiLineEdit_customdir_path.setPlaceholderText(
                    os.path.join(project, DEFAULT_CUSTOM_TMPDIR)
                )
            else:
                self.uiLineEdit_generateddir.setPlaceholderText("")
                self.uiLineEdit_generatedname.setPlaceholderText("")
                self.uiLineEdit_customdir_path.setPlaceholderText("")
            self.__change_ctrlgroups_state(confirm)
            self.__load_requirements_update_table(confirm)
        else:
            MessageBox(
                "提示",
                "当前的项目路径不是绝对路径或者不是一个目录路径！",
                QMessageBox.Warning,
                parent=self,
            ).exec_()
            self.uiCheckBox_confirm_project_path.setChecked(False)

    def __remove_combo_project_path(self):
        cur_index = self.uiComboBox_preject_path.currentIndex()
        if cur_index != -1:
            del self.config.current.project_paths[cur_index]
            self.uiComboBox_preject_path.removeItem(cur_index)

    def __check_add_projectpath(self, project: str):
        if project and project not in self.config.current.project_paths:
            self.uiComboBox_preject_path.addItem(project)
            self.config.current.project_paths.append(project)

    def __environs_combobox_update(self):
        if self.uiComboBox_python_envs.count() != 0:
            return
        if not self.config.cur_pypaths:
            return
        if self.environments:
            return
        self.environments = [
            EnvDisplayPair(PyEnv(p)) for p in self.config.cur_pypaths
        ]
        item_number = len(self.environments)
        if not item_number:
            return
        for i, envp in enumerate(self.environments):
            self.uiComboBox_python_envs.addItem(envp.display)
            envp.signal_connect(self.uiComboBox_python_envs.setItemText, i)
            envp.load_real_display()
        current_index = self.config.current.selected_env
        if current_index < 0 or current_index >= item_number:
            self.config.current.selected_env = current_index = 0
        self.uiComboBox_python_envs.setCurrentIndex(current_index)

    def __project_paths_combobox_update(self):
        if self.uiComboBox_preject_path.count() != 0:
            return
        self.uiComboBox_preject_path.addItems(self.config.current.project_paths)

    def config_widgets_to_dict(self):
        self.config.current.selected_env = (
            self.uiComboBox_python_envs.currentIndex()
        )
        self.config.current.overwrite_reqfile = (
            self.uiCheckBox_overwrite_requirement.isChecked()
        )
        self.config.current.projectpath_index = (
            self.uiComboBox_preject_path.currentIndex()
        )
        if self.uiRadioButton_using_autotempdir.isChecked():
            self.config.current.working_tmpdir = WorkDir.TmpDir
        elif self.uiRadioButton_using_projectdir.isChecked():
            self.config.current.working_tmpdir = WorkDir.Project
        elif self.uiRadioButton_using_customtemp.isChecked():
            self.config.current.working_tmpdir = WorkDir.Custom
        else:
            self.config.current.working_tmpdir = WorkDir.TmpDir
        self.config.current.custom_tempdir = (
            self.uiLineEdit_customdir_path.text()
        )
        self.config.current.generated_dir = self.uiLineEdit_generateddir.text()
        self.config.current.generated_name = (
            self.uiLineEdit_generatedname.text()
        )
        self.config.current.overwrite_samefile = (
            self.uiCheckBox_overwrite_samefile.isChecked()
        )
        self.config.current.upgrade_requires = (
            self.uiCheckBox_upgrade_requires.isChecked()
        )

    def config_dict_to_widgets(self, config_name=None):
        self.__update_configuration_list()
        if config_name:
            self.config.checkout_cfg(config_name)
        else:
            self.__environs_combobox_update()
        self.uiComboBox_python_envs.setCurrentIndex(
            self.config.current.selected_env
        )
        self.__project_paths_combobox_update()
        self.uiCheckBox_overwrite_requirement.setChecked(
            self.config.current.overwrite_reqfile
        )
        self.uiComboBox_preject_path.setCurrentIndex(
            self.config.current.projectpath_index
        )
        working_dir = self.config.current.working_tmpdir
        self.uiRadioButton_using_projectdir.setChecked(
            working_dir == WorkDir.Project
        )
        self.uiRadioButton_using_customtemp.setChecked(
            working_dir == WorkDir.Custom
        )
        self.uiRadioButton_using_autotempdir.setChecked(
            working_dir == WorkDir.TmpDir
        )
        self.__workingdir_option_changed()
        self.uiLineEdit_customdir_path.setText(
            self.config.current.custom_tempdir
        )
        self.uiLineEdit_generateddir.setText(self.config.current.generated_dir)
        self.uiLineEdit_generatedname.setText(
            self.config.current.generated_name
        )
        self.uiCheckBox_overwrite_samefile.setChecked(
            self.config.current.overwrite_samefile
        )
        self.uiCheckBox_upgrade_requires.setChecked(
            self.config.current.upgrade_requires
        )

    def __select_dirpath_settext(self, settextFunc: Callable[[str], None]):
        _path = self.get_dir_path(self.config.current.previous_path)
        if not _path:
            return
        settextFunc(_path)
        self.config.current.previous_path = _path

    def __get_table_requirement_items(self) -> List[str]:
        requirements = list()
        for i in range(self.uiTableWidget_reqirement_lines.rowCount()):
            item = self.uiTableWidget_reqirement_lines.item(i, 0)
            if not item:
                continue
            require = item.text()
            if not require:
                continue
            requirements.append(require)
        return requirements

    def __on_scfpacking_completed(self, succeeded, message):
        if succeeded:
            MessageBox(
                "打包成功",
                "云函数部署包已打包完成！",
                QMessageBox.Information,
                parent=self,
            ).exec_()
        else:
            MessageBox(
                "打包失败",
                message or "在打包过程中出现了错误...",
                QMessageBox.Critical,
                parent=self,
            ).exec_()

    def __cleartable_requirments(self):
        for i in range(self.uiTableWidget_reqirement_lines.rowCount()):
            item = self.uiTableWidget_reqirement_lines.item(i, 1)
            if item:
                item.setText("")

    def __start_cloud_function_packing(self):
        self.__cleartable_requirments()
        env_index = self.uiComboBox_python_envs.currentIndex()
        if env_index == -1 or not self.environments:
            MessageBox(
                "警告",
                "没有选择任何 Python 环境，请检查...",
                QMessageBox.Warning,
                parent=self,
            ).exec_()
            return
        upgrade = self.uiCheckBox_upgrade_requires.isChecked()
        environment = self.environments[env_index].environ
        projectdir = self.uiComboBox_preject_path.currentText()
        if not (os.path.isabs(projectdir) and os.path.isdir(projectdir)):
            MessageBox(
                "错误",
                "项目目录路径不是绝对路径，或者路径不是一个目录路径...",
                QMessageBox.Critical,
                parent=self,
            ).exec_()
            return
        requirements = self.__get_table_requirement_items()
        if self.uiCheckBox_overwrite_requirement.isChecked():
            self.__write_requirements(projectdir, requirements)
        customtmpdir_text = (
            self.uiLineEdit_customdir_path.text()
            or self.uiLineEdit_customdir_path.placeholderText()
        )
        if self.uiRadioButton_using_autotempdir.isChecked():
            workingdir_type = WorkDir.TmpDir
        elif self.uiRadioButton_using_projectdir.isChecked():
            workingdir_type = WorkDir.Project
        elif self.uiRadioButton_using_customtemp.isChecked():
            if not customtmpdir_text:
                MessageBox(
                    "错误",
                    "没有输入自定义工作目录路径...",
                    QMessageBox.Critical,
                    parent=self,
                ).exec_()
                return
            workingdir_type = WorkDir.Custom
        else:
            MessageBox(
                "错误",
                "未知的工作目录选项，请反馈给开发者...",
                QMessageBox.Critical,
                parent=self,
            ).exec_()
            return
        generated_name = (
            self.uiLineEdit_generatedname.text()
            or self.uiLineEdit_generatedname.placeholderText()
        )
        if not generated_name:
            MessageBox(
                "错误",
                "还没有指定生成的文件名，请检查...",
                QMessageBox.Critical,
                parent=self,
            ).exec_()
            return
        generated_dir = (
            self.uiLineEdit_generateddir.text()
            or self.uiLineEdit_generateddir.placeholderText()
        )
        if not generated_dir:
            MessageBox(
                "错误",
                "还没有指定打包文件保存目录，请检查...",
                QMessageBox.Critical,
                parent=self,
            ).exec_()
            return
        generated_file = Path(generated_dir).joinpath(generated_name)
        if not generated_file.suffix.lower() == ".zip":
            generated_file = generated_file.with_suffix(".zip")
        overwrite_samefile = self.uiCheckBox_overwrite_samefile.isChecked()

        def start_scfpackaging():
            self.signal_show_workingtips.emit("开始检查打包条件...")
            if not overwrite_samefile and generated_file.exists():
                self.signal_packing.emit(
                    False, f"已存在<{generated_file}>文件且不允许覆盖..."
                )
                return
            if not os.path.exists(generated_dir):
                try:
                    os.makedirs(generated_dir, exist_ok=True)
                except PermissionError:
                    self.signal_packing.emit(False, "打包文件保存目录创建失败...")
                    return
            elif not os.path.isdir(generated_dir):
                self.signal_packing.emit(False, "打包文件保存路径不是一个目录...")
                return
            workingdir_handle = ""
            if workingdir_type == WorkDir.TmpDir:
                try:
                    workingdir_handle = TemporaryDirectory()
                except Exception as e:
                    self.signal_packing.emit(False, f"临时工作目录创建失败：\n{e}")
                    return
                requires_install_path = workingdir_handle.name
            elif workingdir_type == WorkDir.Custom:
                if os.path.isabs(customtmpdir_text):
                    workingdir_handle = customtmpdir_text
                else:
                    workingdir_handle = os.path.join(
                        projectdir, customtmpdir_text
                    )
                if not os.path.exists(workingdir_handle):
                    try:
                        os.makedirs(workingdir_handle, exist_ok=True)
                    except Exception as e:
                        self.signal_packing.emit(False, f"自定义工作目录创建失败：\n{e}")
                        return
                elif not os.path.isdir(workingdir_handle):
                    self.signal_packing.emit(False, "自定义工作路径不是目录...")
                    return
                requires_install_path = workingdir_handle
            elif workingdir_type == WorkDir.Project:
                if not os.path.isdir(projectdir):
                    self.signal_packing.emit(False, "作为工作目录的项目目录不可用...")
                    return
                requires_install_path = projectdir
            else:
                self.signal_packing.emit(
                    False, f"未知的工作目录类型：{workingdir_type!r}"
                )
                return
            self.signal_show_workingtips.emit("开始安装项目的依赖包...")
            # 本来设计是每个包独立安装的，但是发现一起安装能节省很多时间
            # 所以就砍掉了每个依赖包安装完成就更新安装结果的功能，改为一次性更新
            if requirements:
                _, result = environment.install(
                    *requirements, target=requires_install_path, upgrade=upgrade
                )
                if not result:
                    self.signal_packing.emit(False, f"一个或多个依赖包安装失败，打包中断...")
                    if isinstance(workingdir_handle, TemporaryDirectory):
                        workingdir_handle.cleanup()
                    return
                for index, name in enumerate(requirements):
                    self.signal_reqinstalled.emit(index, name, result)
            self.signal_show_workingtips.emit("开始创建压缩文件...")
            compressed_arcnames: Dict[Path, Path] = dict()
            exclude_paths = [Path(generated_dir).resolve()]
            try:
                with zipfile.ZipFile(
                    generated_file, "w", zipfile.ZIP_DEFLATED
                ) as binary_file:
                    if workingdir_type != WorkDir.Project:
                        filtered_filepaths = self.__filtered_files(
                            Path(projectdir),
                            exclude_paths + [Path(requires_install_path)],
                        )
                    else:
                        filtered_filepaths = self.__filtered_files(
                            Path(projectdir), exclude_paths
                        )
                    for _path in filtered_filepaths:
                        arcfilename = _path.relative_to(projectdir)
                        binary_file.write(_path, arcfilename)
                        compressed_arcnames[arcfilename] = _path
                    if workingdir_type != WorkDir.Project:
                        for _path in self.__filtered_files(
                            Path(requires_install_path), exclude_paths
                        ):
                            arcfilename = _path.relative_to(
                                requires_install_path
                            )
                            if arcfilename in compressed_arcnames:
                                self.signal_packing.emit(
                                    False,
                                    f"继续打包将使以下文件将在压缩包内重复，请处理：\n"
                                    f"{compressed_arcnames[arcfilename]!s}\n{_path!s}",
                                )
                                return
                            binary_file.write(_path, arcfilename)
            except Exception as e:
                self.signal_packing.emit(False, f"创建压缩文件出错：\n{e}")
                return
            finally:
                if isinstance(workingdir_handle, TemporaryDirectory):
                    self.signal_show_workingtips.emit("正在清理临时文件夹...")
                    workingdir_handle.cleanup()
            self.signal_packing.emit(True, None)

        packaging_thread = QThreadModel(start_scfpackaging)
        packaging_thread.before_starting(
            self.__lock_critical_widgets, self.__show_working
        )
        packaging_thread.after_completion(
            self.__release_critical_widgets, self.__hide_working
        )
        packaging_thread.start()
        self.thread_repo.put(packaging_thread, 0)

    def __filtered_files(
        self, _path: Path, prev_excludeds: List[Path]
    ) -> Generator[Path, None, None]:
        previous_cwd = os.getcwd()
        try:
            os.chdir(_path)
            prev_excludeds.extend(
                Path(apath).resolve()
                for apath in self.config.current.excluded_paths
            )
            for afile in _path.rglob("*"):
                if not afile.is_file():
                    continue
                for excluded in prev_excludeds:
                    if excluded.is_file() and afile.samefile(excluded):
                        break
                    elif excluded.is_dir() and excluded in afile.parents:
                        break
                else:
                    yield afile
        finally:
            os.chdir(previous_cwd)

    def __set_requirement_install_result(
        self, index: int, name: str, res: bool
    ):
        if index < 0 or index >= self.uiTableWidget_reqirement_lines.rowCount():
            return
        item = self.uiTableWidget_reqirement_lines.item(index, 0)
        if item is None or item.text() != name:
            return
        result_item = QTableWidgetItem("安装成功" if res else "安装失败")
        result_item.setData(
            Qt.UserRole, RoleData.Success if res else RoleData.Failed
        )
        self.uiTableWidget_reqirement_lines.setItem(index, 1, result_item)

    def __add_requirement_line(self):
        self.uiTableWidget_reqirement_lines.setRowCount(
            self.uiTableWidget_reqirement_lines.rowCount() + 1
        )

    def __delete_requirement_line(self):
        row = self.uiTableWidget_reqirement_lines.currentIndex().row()
        if row == -1:
            return
        self.uiTableWidget_reqirement_lines.removeRow(row)

    def __write_requirements(self, parent, requirements):
        if not requirements:
            return
        try:
            with open(
                os.path.join(parent, self.REQUIRE_FILE),
                "wt",
                encoding="utf-8",
            ) as f:
                f.write(os.linesep.join(requirements))
        except Exception as e:
            MessageBox(
                "错误",
                f"无法重写‘{self.REQUIRE_FILE}’文件：\n{e}",
                QMessageBox.Critical,
                parent=self,
            ).exec_()

    def __lock_critical_widgets(self):
        self.uiPushButton_start_scfpacking.setEnabled(False)
        self.uiCheckBox_confirm_project_path.setEnabled(False)
        self.uiPushButton_add_line.setEnabled(False)
        self.uiPushButton_delete_line.setEnabled(False)
        self.uiRadioButton_using_customtemp.setEnabled(False)
        self.uiRadioButton_using_autotempdir.setEnabled(False)
        self.uiRadioButton_using_projectdir.setEnabled(False)
        self.uiCheckBox_overwrite_samefile.setEnabled(False)
        self.uiLineEdit_generateddir.setEnabled(False)
        self.uiLineEdit_generatedname.setEnabled(False)
        self.uiPushButton_select_generateddir.setEnabled(False)
        self.uiCheckBox_upgrade_requires.setEnabled(False)
        self.uiCheckBox_overwrite_requirement.setEnabled(False)

    def __release_critical_widgets(self):
        self.uiPushButton_start_scfpacking.setEnabled(True)
        self.uiCheckBox_confirm_project_path.setEnabled(True)
        self.uiPushButton_add_line.setEnabled(True)
        self.uiPushButton_delete_line.setEnabled(True)
        self.uiRadioButton_using_customtemp.setEnabled(True)
        self.uiRadioButton_using_autotempdir.setEnabled(True)
        self.uiRadioButton_using_projectdir.setEnabled(True)
        self.uiCheckBox_overwrite_samefile.setEnabled(True)
        self.uiLineEdit_generateddir.setEnabled(True)
        self.uiLineEdit_generatedname.setEnabled(True)
        self.uiPushButton_select_generateddir.setEnabled(True)
        self.uiCheckBox_upgrade_requires.setEnabled(True)
        self.uiCheckBox_overwrite_requirement.setEnabled(True)

    def __update_configuration_list(self, force=False):
        if force:
            self.uiListWidget_config_list.clear()
        elif self.uiListWidget_config_list.count() != 0:
            return
        self.uiListWidget_config_list.addItems(self.config.multicfg)

    def __save_current_configs(self):
        config_name = (
            self.uiLineEdit_config_name.text()
            or self.uiLineEdit_config_name.placeholderText()
        )
        if not config_name:
            MessageBox(
                "警告",
                "还没有输入要保存的配置方案名称。",
                QMessageBox.Warning,
                parent=self,
            ).exec_()
            return
        if config_name in self.config.multicfg:
            if (
                MessageBox(
                    "询问",
                    f"配置方案列表中已经存在名为{config_name}的配置，确定要覆盖吗？",
                    QMessageBox.Question,
                    (("accept", "确定"), ("reject", "取消")),
                    self,
                ).exec_()
                != 0
            ):
                return
        self.uiLineEdit_config_name.clear()
        self.config_widgets_to_dict()
        self.config.store_curcfg(config_name)
        self.__update_configuration_list(force=True)

    def __delete_selected_config(self):
        if not len(self.config.multicfg):
            MessageBox(
                "提示",
                "没有任何已保存的配置方案。",
                QMessageBox.Information,
                parent=self,
            ).exec_()
            return
        selected_item = self.uiListWidget_config_list.currentItem()
        if not selected_item:
            return MessageBox("提示", "没有选择任何配置。").exec_()
        config_name = selected_item.text()
        if config_name not in self.config.multicfg:
            return
        if (
            MessageBox(
                "询问",
                f"即将被删除的配置：{config_name}",
                QMessageBox.Question,
                (("accept", "确定"), ("reject", "取消")),
                self,
            ).exec_()
            != 0
        ):
            return
        del self.config.multicfg[config_name]
        self.__update_configuration_list(force=True)

    def __checkout_saved_config(self):
        if not len(self.config.multicfg):
            MessageBox(
                "提示",
                "没有已保存的配置。",
                QMessageBox.Information,
                parent=self,
            ).exec_()
            return
        config_item = self.uiListWidget_config_list.currentItem()
        if not config_item:
            MessageBox(
                "提示",
                "没有选择任何配置。",
                QMessageBox.Information,
                parent=self,
            ).exec_()
            return
        self.config_dict_to_widgets(config_item.text())
        self.uiListWidget_config_list.setCurrentRow(-1)
        MessageBox(
            "提示", "配置数据切换成功...", QMessageBox.Information, parent=self
        ).exec_()

    def __workingdir_option_changed(self):
        custom = self.uiRadioButton_using_customtemp.isChecked()
        self.uiLineEdit_customdir_path.setEnabled(custom)
        self.uiPushButton_select_customdir.setEnabled(custom)

    def __scfconfig_list_clicked(self):
        index = self.uiListWidget_config_list.currentRow()
        if index == -1:
            return
        self.uiLineEdit_config_name.setText(
            self.uiListWidget_config_list.item(index).text()
        )

class CloudExcludesWindow(Ui_cloud_excludes, QMainWindow, QueryFilePath):
    def __init__(self, parent: CloudFunctionWindow = None):
        self.__parent = parent
        super().__init__(parent)
        self.setupUi(self)
        self.__setup_widgets()
        self.uiPushButton_add_dirpath.clicked.connect(self.__add_dirpath)
        self.uiPushButton_add_filepath.clicked.connect(self.__add_filepath)
        self.uiPushButton_del_line.clicked.connect(self.__delete_line)
        self.uiPushButton_clear_items.clicked.connect(self.__clear_table_items)
        self.uiPushButton_new_line.clicked.connect(self.__add_new_line)

    def __setup_widgets(self):
        self.uiPushButton_add_dirpath.setIcon(QIcon(":/add_dir.png"))
        self.uiPushButton_add_filepath.setIcon(QIcon(":/add_file.png"))
        self.uiPushButton_new_line.setIcon(QIcon(":/new_line.png"))
        self.uiPushButton_del_line.setIcon(QIcon(":/del_line.png"))
        self.uiPushButton_clear_items.setIcon(QIcon(":/clear.png"))
        self.uiTableWidget_exclude_paths = DropableTableWidget(
            self,
            tooltip=self.uiTableWidget_exclude_paths_old.toolTip(),
        )
        self.uiTableWidget_exclude_paths.setObjectName(
            "uiTableWidget_exclude_paths"
        )
        self.horizontalLayout.replaceWidget(
            self.uiTableWidget_exclude_paths_old,
            self.uiTableWidget_exclude_paths,
        )
        self.uiTableWidget_exclude_paths_old.deleteLater()
        self.uiTableWidget_exclude_paths.setRowCount(
            len(self.__parent.config.current.excluded_paths)
        )
        for row, text in enumerate(self.__parent.config.current.excluded_paths):
            self.uiTableWidget_exclude_paths.setItem(
                row, 0, QTableWidgetItem(text)
            )

    def display(self):
        self.resize(*self.__parent.config.exc_windowsize)
        if self.isMaximized():
            self.showMaximized()
        else:
            self.showNormal()

    def __save_window_size(self):
        if self.isMinimized() or self.isMinimized():
            return
        self.__parent.config.exc_windowsize = self.width(), self.height()

    def closeEvent(self, event: QCloseEvent):
        self.__parent.config.current.excluded_paths = (
            self.uiTableWidget_exclude_paths.getItemsText()
        )
        self.__save_window_size()

    def keyPressEvent(self, event: QKeyEvent):
        if event.key() == Qt.Key_Escape:
            self.close()

    def __add_filepath(self):
        paths, previous = self.get_file_paths(
            self.__parent.config.current.previous_path
        )
        if paths:
            row = self.uiTableWidget_exclude_paths.rowCount()
            self.uiTableWidget_exclude_paths.setRowCount(row + len(paths))
            for increase, _path in enumerate(paths):
                self.uiTableWidget_exclude_paths.setItem(
                    row + increase, 0, QTableWidgetItem(_path)
                )
            self.__parent.config.current.previous_path = previous

    def __add_dirpath(self):
        _path = self.get_dir_path(self.__parent.config.current.previous_path)
        if _path:
            row = self.uiTableWidget_exclude_paths.rowCount()
            self.uiTableWidget_exclude_paths.setRowCount(row + 1)
            self.uiTableWidget_exclude_paths.setItem(
                row, 0, QTableWidgetItem(_path)
            )
            self.__parent.config.current.previous_path = _path

    def __delete_line(self):
        selected_row_indexes = [
            r.row() for r in self.uiTableWidget_exclude_paths.selectedIndexes()
        ]
        if not selected_row_indexes:
            MessageBox(
                "提示", "没有选中任何项目...", QMessageBox.Information, parent=self
            ).exec_()
            return
        selected_row_indexes.sort()
        top_row = selected_row_indexes[0]
        selected_count = len(selected_row_indexes)
        for ii, row in enumerate(selected_row_indexes):
            self.uiTableWidget_exclude_paths.removeRow(row)
            for remain_i in range(ii + 1, selected_count):
                # 因为表格只有一列，行索引不会有重复的，所以这里不做进一步判断
                selected_row_indexes[remain_i] -= 1
        remain_row_count = self.uiTableWidget_exclude_paths.rowCount()
        if remain_row_count:
            if top_row >= remain_row_count:
                top_row = remain_row_count - 1
            self.uiTableWidget_exclude_paths.setRangeSelected(
                QTableWidgetSelectionRange(top_row, 0, top_row, 0), True
            )

    def __add_new_line(self):
        self.uiTableWidget_exclude_paths.setRowCount(
            self.uiTableWidget_exclude_paths.rowCount() + 1
        )

    def __clear_table_items(self):
        self.uiTableWidget_exclude_paths.setRowCount(0)


from __future__ import print_function

import os
import requests
from django.shortcuts import render
import plotly.express as px
import plotly.graph_objs as go
import pandas as pd
import plotly.offline as pyo
import datetime
import folium
import numpy as np
# Data from John hopkins University
df_country = pd.read_csv('https://raw.githubusercontent.com/CSSEGISandData/COVID-19/web-data/data/cases_country.csv')
df_recovered = pd.read_csv(
    'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_recovered_global.csv')
df_death = pd.read_csv(
    'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_deaths_global.csv')
df_confirmed_case = pd.read_csv(
    'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv')

# clean data
df_country.columns = map(str.lower, df_country.columns)
df_recovered.columns = map(str.lower, df_recovered.columns)
df_death.columns = map(str.lower, df_death.columns)
df_confirmed_case.columns = map(str.lower, df_confirmed_case.columns)

# rename headers
df_recovered = df_recovered.rename(columns={'province/state': 'state', 'country/region': 'country'})
df_death = df_death.rename(columns={'province/state': 'state', 'country/region': 'country'})
df_confirmed_case = df_confirmed_case.rename(columns={'province/state': 'state', 'country/region': 'country'})
df_country = df_country.rename(columns={'country_region': 'country'})

def home(request):
    labels = df_country['country']
    values = df_country['active']
    data = [go.Pie(labels=labels,
                   values=values,
                   textinfo='percent+label')]
    fig = go.Figure(data=data)

    pie_chart = pyo.plot(fig, auto_open=False, output_type='div')
    date = datetime.datetime.today().strftime('%d-%b-%Y')

    # Funnel chart
    new_cases = bubble_chart(30)
    co_map = covid_map()
    monthly_cases = cases_per_month()
    daily_cases = line_graph('world')
   # my_map = test1()
    return render(request, 'home/welcome.html', locals())

def covid_map():
    global_map = folium.Map(location=[1, 0], zoom_start=1, max_zoom=6, min_zoom=0.500)

    for cases in range(0, len(df_confirmed_case)):
        folium.Circle(
            location=[df_confirmed_case.iloc[cases]['lat'], df_confirmed_case.iloc[cases]['long']],
            fill=True,
            radius=(int((np.log(df_confirmed_case.iloc[cases, -1] + 1.00001))) + 0.2) * 50000,
            color='red',
            fill_color='indigo',
            tooltip="
" +
                    "
" + df_confirmed_case.iloc[cases][
                        'country'] + "
"
                                     "
" +
                    "
" +
                    "
Confirmed: " + str(df_confirmed_case.iloc[cases, -1]) + "
" +
                    "
Deaths:   " + str(df_death.iloc[cases, -1]) + "
" +
                    "
Death Rate: " + str(
                np.round(df_death.iloc[cases, -1] / (df_confirmed_case.iloc[cases, -1] + 1.00001) * 100, 2)) + "
" +
                    "

",
        ).add_to(global_map)

    global_map = global_map._repr_html_()
    return global_map

def bubble_chart(n):
    sorted_country_df = df_country.sort_values('confirmed', ascending=False)
    fig = px.scatter(sorted_country_df.head(n), x="country", y="confirmed", size="confirmed", color="country",
                     hover_name="country", size_max=60)
    fig.update_layout(
        xaxis_title="Countries",
        yaxis_title="Confirmed Cases",
        width=900,

    )
    new_cases = pyo.plot(fig, auto_open=False, output_type='div')
    return new_cases

# def funnel():
#     fig1 = px.funnel_area(names=df['Country'],
#                           values=df['TotalDeaths'],
#                           )
#     new_cases = pyo.plot(fig1, auto_open=False, output_type='div')
#     return new_cases
#

def cases_per_month():
    df = pd.read_csv('/home/ubuntu/project/covid19/covid19/covid19/static/report_2020-04-08.csv')
    month = df['date']
    confirm_cases = df['new_confirmed_cases']
    recover = df['new_recoveries']
    deaths = df['new_deaths']
    recover = df['new_recoveries']

    fig = go.Figure()
    # Create and style traces
    fig.add_trace(go.Scatter(x=month, y=confirm_cases, name='Confirm cases',
                             line=dict(color='firebrick', width=4, dash='dot')))
    fig.add_trace(go.Scatter(x=month, y=recover, name='Recovered cases',
                             line=dict(color='green', width=4, dash="dot")))
    fig.add_trace(go.Scatter(x=month, y=deaths, name='Deaths ',
                             line=dict(color='red', width=4, dash='dot')))

    # Edit the layout
    fig.update_layout(title='Number of Covid-19 Cases per month',
                      xaxis_title='Month',
                      yaxis_title='Covid-19 Cases',
                      width=900,

                      ),

    month_cases = pyo.plot(fig, auto_open=False, output_type='div')
    return month_cases

def line_graph(country):
    labels = ['confirmed', 'deaths', 'recovered']
    colors = ['blue', 'red', 'green']
    line_size = [1, 1, 1]

    df_list = [df_confirmed_case, df_death, df_recovered]

    fig = go.Figure()

    for i, df in enumerate(df_list):
        country = 'world'
        x_data = np.array(list(df.iloc[:, 20:].columns))
        y_data = np.sum(np.asarray(df.iloc[:, 6:]), axis=0)
        fig.add_trace(go.Scatter(x=x_data, y=y_data, mode='lines+markers',
                                 name=labels[i],
                                 line=dict(color=colors[i], width=line_size[i]),
                                 connectgaps=True,
                                 text="Total " + str(labels[i]) + ": " + str(y_data[-1])
                                 ))
        fig.update_layout(
            title=country + " " + "COVID-19 Cases",
            xaxis_title='Date',
            yaxis_title='No. of Confirmed Cases',
            margin=dict(l=20, r=20, t=40, b=20),
            paper_bgcolor="lightgrey",
            width=800,

        )
    fig.update_yaxes(type="linear")
    test_plot = pyo.plot(fig, auto_open=False, output_type='div')
    return test_plot

def show_table(request):
    layout = go.Layout(title='Covid-19 Cases per Day',
                       )
    fig = go.Figure(layout=layout, data=[go.Table(
        header=dict(values=list(df_country.columns),
                    fill_color='paleturquoise',
                    align='left'),
        cells=dict(values=[df.Country, df.TotalCases, df.NewCases, df.TotalDeaths, df.NewDeaths,
                           df.TotalRecovered, df.ActiveCases, df.Critical, df.TotalTests,
                           df.iso_alpha, df.date],
                   fill_color='lavender',
                   align='left'))
    ])

    shows_table = pyo.plot(fig, auto_open=False, output_type='div')
    return render(request, 'home/table.html', locals())

# def test1():
#     SF_COORDINATES = (-16.378575, 167.862999)
#     crimedata = pd.read_csv('stat.csv')
#
#     m = folium.Map(location=SF_COORDINATES,
#                    zoom_start=10
#                    )
#     test = crimedata[['X', 'Y']]
#     list = test.values.tolist()
#     size = len(list)
#
#     for each in range(0, size):
#         folium.Marker(list[each],
#
#                       popup="Village:" + str(crimedata['Village_Name'][each]) +
#                             "\nMale:" + str(crimedata['Male'][each]) +
#                             "\nFemale:" + str(crimedata['Female'][each]) +
#                             "\nTotal Pop:" + str(crimedata['Totpop'][each]) +
#                             "\nHH:" + str(crimedata['HH'][each]),
#                       icon=folium.Icon(color='orange')).add_to(m)
#
#     folium.Marker(
#         location=[-16.308389, 168.267770],
#         radius=50,
#         popup='S/E Number of Villages: 21 Male:791 Female:758 Population:1,549 HH:457',
#         color='#3186cc',
#         fill=True,
#         fill_color='#3186cc',
#         icon=folium.Icon(icon='cloud')
#     ).add_to(m)
#
#     folium.Marker(
#         location=[-16.239604, 167.916719],
#         popup='West Number of Villages: 34 Male:1428 Female:1389 Population:2,817 HH:642',
#         icon=folium.Icon(icon='cloud')
#     ).add_to(m)
#
#     folium.Marker(
#         location=[-16.128442, 168.160146],
#         popup='North Number of Villages: 50 Male:1459 Female:1560 Population:3,019 HH:664 ',
#         icon=folium.Icon(icon='cloud')
#     ).add_to(m)
#
#     folium.Marker(
#         location=[-16.458311, 168.235905],
#         popup='Number of Villages: 19 Male:867 Female:853 Population:1,720 HH:390',
#         icon=folium.Icon(icon='cloud')
#     ).add_to(m)
#
#     folium.Circle(
#         radius=100,
#         location=[-16.350303, 168.272950],
#         popup='Utas Polling Station',
#         fill=True,
#         opacity=8.8,
#         stroke=True,
#         weitht=1.0,
#         color='red',
#         fill_color='red'
#     ).add_to(m)
#
#     folium.Circle(
#         radius=100,
#         location=[-16.333166, 168.300781],
#         popup='Ulei Polling Station',
#         fill=True,
#         opacity=8.8,
#         stroke=True,
#         weitht=1.0,
#         color='red',
#         fill_color='red'
#
#     ).add_to(m)
#
#     folium.Circle(
#         radius=100,
#         location=[-16.357205, 168.252766],
#         popup='Moru harbour',
#         fill=True,
#         color='yellow'
#
#     ).add_to(m)
#
#     folium.Circle(
#         radius=100,
#         location=[-16.357805, 168.233938],
#         popup='Taveak harbour',
#         fill=True,
#         color='yellow'
#
#     ).add_to(m)
#     m = m._repr_html_()
#     return m
#
#
# def test(request):
#     my_map = test1()
#     return render(request, 'home/map.html', locals())


from migen.fhdl.std import *
from migen.genlib.record import Record
from migen.genlib.resetsync import AsyncResetSynchronizer

from .dac import Dac
from .comm import Comm
from .ft245r import Ft245r_rx, SimFt245r_rx, SimReader

class Pdq2Base(Module):
    """PDQ2 Base configuration.

    Used both in functional simulation and final gateware.

    Holds the three :mod:`gateware.dac.Dac` and the communication handler
    :mod:`gateware.comm.Comm`.

    Args:
        ctrl_pads (Record): Control pads for :mod:`gateware.comm.Comm`.
        mem_depth (list[int]): Memory depths for the DAC channels.

    Attributes:
        dacs (list): List of :mod:`gateware.dac.Dac`.
        comm (Module): :mod:`gateware.comm.Comm`.
    """
    def __init__(self, ctrl_pads, mem_depths=(1 << 13, 1 << 13, 1 << 12)):
        # (1 << 13, (1 << 12) + (1 << 11), (1 << 12) + (1 << 11))
        self.dacs = []
        for i, depth in enumerate(mem_depths):
            dac = Dac(mem_depth=depth)
            setattr(self.submodules, "dac{}".format(i), dac)
            self.dacs.append(dac)
        self.submodules.comm = Comm(ctrl_pads, self.dacs)

class Pdq2Sim(Module):
    ctrl_layout = [
        ("adr", 4),
        ("aux", 1),
        ("frame", 3),
        ("trigger", 1),
        ("reset", 1),
        ("go2_in", 1),
        ("go2_out", 1),
    ]

    def __init__(self, mem, skip_ft245r=True):
        self.ctrl_pads = Record(self.ctrl_layout)
        self.ctrl_pads.adr.reset = 0b1111
        self.ctrl_pads.trigger.reset = 1
        self.ctrl_pads.frame.reset = 0b000
        self.submodules.dut = ResetInserter(["sys"])(Pdq2Base(self.ctrl_pads))
        self.comb += self.dut.reset_sys.eq(self.dut.comm.ctrl.reset)
        if skip_ft245r:
            reader = SimReader(mem)
        else:
            reader = SimFt245r_rx(mem)
        self.submodules.reader = ResetInserter(["sys"])(reader)
        self.comb += self.reader.reset_sys.eq(self.dut.comm.ctrl.reset)
        self.comb += self.dut.comm.sink.connect(self.reader.source)
        # override high-ack during reset draining the reader
        self.comb += self.reader.source.ack.eq(self.dut.comm.sink.ack &
                                               ~self.dut.comm.ctrl.reset)
        self.outputs = []

    def do_simulation(self, selfp):
        self.outputs.append([dac.out.data for dac in selfp.dut.dacs])
    do_simulation.passive = True

class CRG(Module):
    """PDQ2 Clock and Reset generator.

    Args:
        platform (Platform): PDQ2 Platform.

    Attributes:
        rst (Signal): Reset input.
        clk_p (Signal): Positive clock output.
        clk_n (Signal): Negative clock output.
        dcm_sel (Signal): Select doubled clock. Input.
        dcm_locked (Signal): DCM locked. Output.
        cd_sys (ClockDomain): System clock domain driven.
    """
    def __init__(self, platform):
        self.clock_domains.cd_sys = ClockDomain()
        self.clk_p = self.cd_sys.clk
        self.clk_n = Signal()
        self.rst = Signal()
        self.dcm_locked = Signal()
        self.dcm_sel = Signal()

        clkin = platform.request("clk50")
        clkin_period = 20.

        clkin_sdr = Signal()
        self.specials += Instance("IBUFG", i_I=clkin, o_O=clkin_sdr)

        dcm_clk2x = Signal()
        dcm_clk2x180 = Signal()
        self.specials += Instance("DCM_SP",
                p_CLKDV_DIVIDE=2,
                p_CLKFX_DIVIDE=1,
                p_CLKFX_MULTIPLY=2,
                p_CLKIN_DIVIDE_BY_2="FALSE",
                p_CLKIN_PERIOD=clkin_period,
                #p_CLK_FEEDBACK="2X",
                p_CLK_FEEDBACK="NONE",
                p_DLL_FREQUENCY_MODE="LOW",
                p_DFS_FREQUENCY_MODE="LOW",
                p_STARTUP_WAIT="TRUE",
                p_CLKOUT_PHASE_SHIFT="NONE",
                p_PHASE_SHIFT=0,
                p_DUTY_CYCLE_CORRECTION="TRUE",
                i_RST=0,
                i_PSEN=0,
                i_PSINCDEC=0,
                i_PSCLK=0,
                i_CLKIN=clkin_sdr,
                o_LOCKED=self.dcm_locked,
                #o_CLK2X=dcm_clk2x,
                #o_CLK2X180=dcm_clk2x180,
                o_CLKFX=dcm_clk2x,
                o_CLKFX180=dcm_clk2x180,
                #i_CLKFB=clk_p,
                i_CLKFB=0,
                )
        self.specials += Instance("BUFGMUX",
                i_I0=clkin_sdr, i_I1=dcm_clk2x, i_S=self.dcm_sel,
                o_O=self.clk_p)
        self.specials += Instance("BUFGMUX",
                i_I0=~clkin_sdr, i_I1=dcm_clk2x180, i_S=self.dcm_sel,
                o_O=self.clk_n)
        self.specials += AsyncResetSynchronizer(
            self.cd_sys, ~self.dcm_locked | self.rst)

class Pdq2(Pdq2Base):
    """PDQ2 Top module.

    Wires up USB FIFO reader :mod:`gateware.ft245r.Ft345r_rx`, clock and reset
    generator :mod:`CRG`, and the DAC output signals.
    Delegates the wiring of the remaining modules to :mod:`Pdq2Base`.

    ``pads.go2_out`` is assigned the DCM locked signal.

    Args:
        platform (Platform): PDQ2 platform.
    """
    def __init__(self, platform):
        ctrl_pads = platform.request("ctrl")
        Pdq2Base.__init__(self, ctrl_pads)
        self.submodules.crg = CRG(platform)
        comm_pads = platform.request("comm")
        self.submodules.reader = Ft245r_rx(comm_pads)
        self.comb += [
                self.comm.sink.connect(self.reader.source),
                self.crg.rst.eq(self.comm.ctrl.reset),
                ctrl_pads.go2_out.eq(self.crg.dcm_locked),
                self.crg.dcm_sel.eq(self.comm.ctrl.dcm_sel)
        ]

        for i, dac in enumerate(self.dacs):
            pads = platform.request("dac", i)
            # inverted clocks ensure setup and hold times of data
            ce = Signal()
            d = Signal.like(dac.out.data)
            self.comb += [
                    ce.eq(~dac.out.silence),
                    d.eq(~dac.out.data),  # pcb inversion
            ]

            self.specials += Instance("ODDR2",
                    i_C0=self.crg.clk_p, i_C1=self.crg.clk_n, i_CE=ce,
                    i_D0=0, i_D1=1, i_R=0, i_S=0, o_Q=pads.clk_p)
            self.specials += Instance("ODDR2",
                    i_C0=self.crg.clk_p, i_C1=self.crg.clk_n, i_CE=ce,
                    i_D0=1, i_D1=0, i_R=0, i_S=0, o_Q=pads.clk_n)
            dclk = Signal()
            self.specials += Instance("ODDR2",
                    i_C0=self.crg.clk_p, i_C1=self.crg.clk_n, i_CE=ce,
                    i_D0=0, i_D1=1, i_R=0, i_S=0, o_Q=dclk)
            self.specials += Instance("OBUFDS",
                    i_I=dclk, o_O=pads.data_clk_p, o_OB=pads.data_clk_n)
            for i in range(16):
                self.specials += Instance("OBUFDS",
                        i_I=d[i], o_O=pads.data_p[i], o_OB=pads.data_n[i])


import cv2 as cv

img = cv.imread('C:/Users/emilb/NTNU/Master/bilder/emil_kontakt_oss.jpg', -1)
img2 = cv.imread('C:/Users/emilb/NTNU/Master/bilder/iv_dagene_bilde.jpg', -1)
#C:\\Documents\\example.tx

# Endrer storleiken på bildet
img = cv.resize(img, (400,500))
img2 = cv.resize(img2, (0,0), fx= 0.3, fy=0.3)

height = img2.shape[0]
width = img2.shape[1]
print("Width and height: " + str(width) + " " +str(height))

#Linje gjennom bildet
#img = cv.line(img, (0,0), (width, height), (0, 255, 0), 1)
img = cv.line(img, (100,0), (100, width), (0, 255, 0), 5)

# Farge greier
hsv = cv. cvtColor(img2, cv.COLOR_BGR2HSV)

cv.imshow("Display window", img)
#cv.imshow("Display window", hsv)
k = cv.waitKey(0) # Wait for a keystroke in the windows
cv.destroyAllWindows()


from typing import List

class Solution:
    """
        如果能到达某个位置，那一定能到达它前面的所有位置。
    """
    def canJump(self, nums: List[int]) -> bool:
        # 初始化当前能到达的最远的位置
        max_i = 0
        # i 为当前位置， jump是当前位置的跳数
        for i, jump in enumerate(nums):
            # 如果当前位置能到达最远的位置，并且当前位置+跳数>最远位置
            if max_i >= i and i + jump > max_i:
                max_i = i + jump

        return max_i >= i

    def canJump_solution2(self, nums: List[int]) -> bool:
        # 初始化当前能到达的最远的位置
        farthest = 0
        # i 为当前位置， jump是当前位置的跳数
        for i, jump in enumerate(nums):
            # 如果当前位置小于最远的位置，并且当前位置+跳数>最远位置
            if i  <= farthest:
                farthest = max(farthest, i + jump)

        return farthest >= i


import os
import requests
import openai
import streamlit as st
import nltk
from nltk.sentiment import SentimentIntensityAnalyzer

nltk.download('vader_lexicon')

def get_news_articles():
    url = 'https://newsapi.org/v2/top-headlines'
    params = {
        'country': 'us',
        'apiKey': os.getenv('NEWS_API_KEY'),
        'pageSize':5,
    }
    response = requests.get(url, params=params)
    articles = response.json()['articles']
    return articles

def generate_summary(text):
    openai.api_key =  os.getenv('CHATGPT_API_KEY')
    response = openai.Completion.create(
        engine ='text-davinci-003',
        prompt =f"Summarize this text:{text}",
        max_tokens = 100,
        n = 1,
        stop = None,
        temperature = 1,
    )
    summary = response.choices[0].text.strip()
    #summary = "MOCK: Uncomment this line if you want to test this out without hitting GPT API."
    return summary

def analyze_sentiment(text):
    sia = SentimentIntensityAnalyzer()

    # polarity_scores returns a sentiment dictionary.
    # It contains pos, neg, neu, and compound scores
    sentiment_dict = sia.polarity_scores(text)
    print("The news was rated as ", sentiment_dict['neg'] * 100, "% Negative")
    print("The news was rated as ", sentiment_dict['neu'] * 100, "% Neutral")
    print("The news was rated as ", sentiment_dict['pos'] * 100, "% Positive")

    # decide sentiment as positive, negative and neutral
    if sentiment_dict['compound'] >= 0.05:
        sentiment_scores = "Positive"
    elif sentiment_dict['compound'] <= -0.05:
        sentiment_scores = "Negative"
    else:
        sentiment_scores = "Neutral"

    print("Overall, the news was rated as ", sentiment_scores)
    return sentiment_scores

def classify_article(text):
    categories = []

    # Define keywords for each category
    categories = {
        'Business': ['business', 'economy', 'finance', 'stock', 'share', 'cost', 'loan', 'inflation', 'credit', 'bank'],
        'Technology': ['technology', 'internet', 'software', 'hardware', 'crypto', 'cloud', 'artificial'],
        'Sports': ['sport', 'football', 'basketball', 'soccer', 'game', 'golf', 'cricket', 'baseball', 'NBA'],
        'Entertainment': ['entertainment', 'celebrity', 'movie', 'music', 'television', 'film', 'netflix'],
        'Politics': ['summit', 'election', 'politic', 'vote', 'ukraine', 'conflict', 'government'],
        'Society': ['pollution', 'hospital', 'weather', 'judge', 'social', 'shop', 'school', 'children', 'family']
    }

    text = text.lower()

    # Check if any category keywords are present in the text
    for category, keywords in categories.items():
        if any(keyword in text for keyword in keywords):
            return category

    return 'Other'


import serial
import time
import UARTParser
import numpy as np
import matplotlib.pyplot as plt

LOG_TIME = 20
SERIAL_DEVICE = 'COM3'

log_code = np.zeros(int(1.5*LOG_TIME*10000), dtype=np.uint8)
log_timit = np.zeros(int(1.5*LOG_TIME*10000), dtype=np.uint16)
log_timms = np.zeros(int(1.5*LOG_TIME*10000), dtype=np.uint32)
log_freq = np.zeros(int(1.5*LOG_TIME*10000), dtype=np.float32)
log_ampl = np.zeros(int(1.5*LOG_TIME*10000), dtype=np.float32)

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    port = serial.Serial(port=SERIAL_DEVICE, baudrate=921600,
                         bytesize=serial.EIGHTBITS, stopbits=serial.STOPBITS_ONE, xonxoff=False,
                         rtscts=False, dsrdtr=False)
    parser_machine = UARTParser.UARTParserState()
    t0 = time.time()
    port.flushInput()
    log_cnt = 0
    while (time.time() - t0) < LOG_TIME:
        data_len = port.in_waiting
        if data_len > 0:
            buffer = port.read(size=data_len)
            # port.flushInput()
            for next_byte in buffer:
                parser_machine.parse_byte(next_byte)
                if parser_machine.data_ready:
                    serial_data = bytes(parser_machine.buffer[0:parser_machine.len])
                    packet = UARTParser.ReportPacketStructure.from_buffer_copy(serial_data)
                    log_timit[log_cnt] = float(packet.timit)
                    log_timms[log_cnt] = float(packet.timms)
                    log_freq[log_cnt] = packet.freq
                    log_ampl[log_cnt] = packet.ampl
                    log_code[log_cnt] = packet.code
                    log_cnt += 1

    plt.figure()
    plt.plot(log_timit[0:log_cnt]/1000.0)
    plt.grid()
    plt.show()

    plt.figure()
    plt.plot(log_timms[0:log_cnt])
    plt.grid()
    plt.show()

    plt.figure()
    plt.plot(log_ampl[0:log_cnt])
    plt.grid()
    plt.show()

    plt.figure()
    plt.plot(log_code[0:log_cnt])
    plt.grid()
    plt.show()

    print('got {} packets'.format(log_cnt))


import logging
import os
import sys
import shutil
import requests
from os.path import join

import torchvision.models as zoomodels
from tqdm import tqdm

from . import constants as c
from . import datasets as dataset_module
from .models import pytorch_model_zoo, tensorflow_model_zoo, list_models

logger = logging.getLogger(__name__)
dataset_base_url = "https://github.com/bethgelab/model-vs-human/releases/download/v0.1/{NAME}.tar.gz"

def try_download_dataset_from_github(dataset_name):
    download_url = dataset_base_url.format(NAME=dataset_name)
    response = requests.get(download_url, stream=True)
    if response.status_code == 200:
        total_length = response.headers.get('content-length')
        if not os.path.exists(c.DATASET_DIR):
            os.makedirs(c.DATASET_DIR)
        dataset_file = join(c.DATASET_DIR, f'{dataset_name}.tar.gz')
        print(f"Downloading dataset {dataset_name} to {dataset_file}")
        with open(dataset_file, 'wb') as fd:
            if total_length is None:  # no content length header
                fd.write(response.content)
            else:
                for chunk in tqdm(response.iter_content(chunk_size=4096)):
                    fd.write(chunk)
        shutil.unpack_archive(dataset_file, c.DATASET_DIR)
        os.remove(dataset_file)
        return True
    else:
        return False

def load_dataset(name, *args, **kwargs):
    default_kwargs = {"batch_size": 16, "num_workers": 4}
    kwargs = {**default_kwargs, **kwargs}
    logger.info(f"Loading dataset {name}")
    supported_datasets = dataset_module.list_datasets()
    module_name = supported_datasets.get(name, None)
    if module_name is None:
        raise NameError(f"Dataset {name} is not supported, "
                        f"please select from {list(supported_datasets.keys())}")
    elif os.path.exists(join(c.DATASET_DIR, name)):
        return eval(f"dataset_module.{module_name}")(*args, **kwargs)
    elif try_download_dataset_from_github(name):
        return eval(f"dataset_module.{module_name}")(*args, **kwargs)
    else:
        raise NotImplementedError(f"Dataset {name} not available for download, please obtain the dataset "
                                  f"yourself and save it to {c.DATASET_DIR}")

def no_op():
    assert tensorflow_model_zoo
    assert pytorch_model_zoo

def load_model(model_name, *args):
    if model_name in zoomodels.__dict__:
        model = eval("pytorch_model_zoo.model_pytorch")(model_name, *args)
        framework = 'pytorch'
    elif model_name in list_models("pytorch"):
        model = eval(f"pytorch_model_zoo.{model_name}")(model_name, *args)
        framework = 'pytorch'
    elif model_name in list_models('tensorflow'):
        model = eval(f"tensorflow_model_zoo.{model_name}")(model_name, *args)
        framework = 'tensorflow'
    else:
        raise NameError(f"Model {model_name} is not supported.")
    return model, framework

class AverageMeter(object):
    """Computes and stores the average and current value"""

    def __init__(self, name, fmt=':f'):
        self.name = name
        self.fmt = fmt
        self.reset()

    def reset(self):
        self.val = 0
        self.avg = 0
        self.sum = 0
        self.count = 0

    def update(self, val, n=1):
        self.val = val
        self.sum += val * n
        self.count += n
        self.avg = self.sum / self.count

    def __str__(self):
        fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})'
        return fmtstr.format(**self.__dict__)


from django import forms
from django.utils.safestring import mark_safe

class SelectCategoryField(forms.ModelMultipleChoiceField):

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

        self.name = kwargs.pop('name', '')
        print("SELF select", self)

        placeholder = kwargs['placeholder']
        del kwargs['placeholder']

        is_loading = kwargs['is_loading']
        del kwargs['is_loading']

        title = kwargs['title']
        del kwargs['title']

        copy_kwargs = kwargs.copy()
        del copy_kwargs['queryset']
        kwargs['widget'] = SelectCategory(self.name, placeholder, is_loading, title, *args, copy_kwargs)

        if 'label' not in kwargs.keys():
            kwargs['label'] = False

        super(SelectCategoryField, self).__init__(*args, **kwargs)

class SelectCategory(forms.SelectMultiple):

    def __init__(self, elem_name, placeholder, is_loading, title, *args, **kwargs):
        super(SelectCategory, self).__init__(*args, **kwargs)
        self.elem_name = str(elem_name)
        self.placeholder = placeholder
        self.is_loading = is_loading
        self.title = title
        self.template_name = 'select_category.html'
        self.option_template_name = 'select_category_option.html'
        self.option_template_hidden_name = 'select_category_option.html'

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        context['widget']['optgroups'] = self.optgroups(name, context['widget']['value'], attrs)
        context['widget']['placeholder'] = self.placeholder
        context['widget']['is_loading'] = self.is_loading
        context['widget']['title'] = self.title
        return context

    def render(self, name, value, attrs=None, renderer=None):
        context = self.get_context(name, value, attrs)
        s = self._render(self.template_name, context, renderer)
        return mark_safe(s)


from pyspark.sql import SparkSession
from pyspark.ml.feature import Tokenizer

bucket = 'deb-gcp-bucket-pius'
#staging_bucket = 'useranalytics-pipeline-bucket-staging'
key = 'Bronze/movie_review.csv'
local_key = '../data/movie_review.csv'

# Create a Spark session
spark = SparkSession.builder.appName("ProcessMovieReview").getOrCreate()

#datax = spark.read.csv(f'gs://{bucket}/{key}', inferSchema=True, header=True, sep=',')
datax = spark.read.csv(local_key, inferSchema=True, header=True, sep=',')

# Create a DataFrame
df = spark.createDataFrame(datax, ["cid", "review_str"])

# Instantiate the Tokenizer
tokenizer = Tokenizer(inputCol="review_str", outputCol="review_token")

# Tokenize the 'review_str' column and add the result to a new column 'review_token'
tokenized_df = tokenizer.transform(df)

# Show the result
tokenized_df.select("cid", "review_token").show(truncate=False)

# Stop the Spark session
spark.stop()


import numpy as np
import statsmodels.api as statm
import statsmodels.formula.api as statfa
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

data = [[2, 0, 81], [4, 4, 93], [6, 2, 91], [8, 3, 97]]
X = [i[0:2] for i in data]
Y = [i[2] for i in data]

X_1=statm.add_constant(X)
results=statm.OLS(Y,X_1).fit()

hour_class=pd.DataFrame(X,columns=['study_hours','private_class'])
hour_class['Score']=pd.Series(Y)

model = statfa.ols(formula='Score ~ study_hours + private_class', data=hour_class)

results_formula = model.fit()

a, b = np.meshgrid(np.linspace(hour_class.study_hours.min(),hour_class.study_hours.max(),100),
                   np.linspace(hour_class.private_class.min(),hour_class.private_class.max(),100))

X_ax = pd.DataFrame({'study_hours': a.ravel(), 'private_class': b.ravel()})
fittedY=results_formula.predict(exog=X_ax)
fig = plt.figure()

graph = fig.add_subplot(111, projection='3d')

graph.scatter(hour_class['study_hours'],hour_class['private_class'],hour_class['Score'],
              c='blue',marker='o', alpha=1)
graph.plot_surface(a,b,fittedY.values.reshape(a.shape),
                   rstride=1, cstride=1, color='none', alpha=0.4)
graph.set_xlabel('study_hours')
graph.set_ylabel('private_class')
graph.set_zlabel('Score')

plt.show()


import pyautogui
pyautogui.PAUSE = 1
# pyautogui.keyDown('ctrl')
# pyautogui.keyDown('g')
# pyautogui.keyUp('ctrl')
# pyautogui.keyUp('g')

cont = 1
pyautogui.hotkey('alt', 'f2')
while cont < 15:
    pyautogui.typewrite ('EU ESTOU PUTO COM VC')
    pyautogui.press('enter')
    cont += 1

# pyautogui.typewrite('')
# pyautogui.press('enter')

#
# sgf


from flask import Flask
from flask import render_template
import Panel
import State
from pygooglechart import Chart
from pygooglechart import SimpleLineChart
from pygooglechart import Axis
import os

app = Flask(__name__)
panel1 = Panel.panel_production('Jim', 2, 2, 4, 1)
panel2 = Panel.panel_production('Jim2', 2, 2, 4, 2)
panels = [panel1, panel2]
myPanel = "jim"
CA = State.stateReg('California',15,5)

#initialize sample vars
panel1.setOCV(1)
panel1.setSCI(1)

def makeChart():
    # Set the vertical range from 0 to 100
    max_y = 100

    # Chart size of 200x125 pixels and specifying the range for the Y axis
    chart = SimpleLineChart(400, 250, y_range=[0, max_y])

    # Add the chart data
    # Aggregate data into array before adding to chart
    data = [
        32, 34, 34, 32, 34, 34, 32, 32, 32, 34, 34, 32, 29, 29, 34, 34, 34, 37,
        37, 39, 42, 47, 50, 54, 57, 60, 60, 60, 60, 60, 60, 60, 62, 62, 60, 55,
        55, 52, 47, 44, 44, 40, 40, 37, 34, 34, 32, 32, 32, 31, 32
    ]
    #for i in data:
    #    chart.add_data(i)
    chart.add_data(data)

    # Set the line colour to blue
    chart.set_colours(['0000FF'])

    # Set the vertical stripes
    chart.fill_linear_stripes(Chart.CHART, 0, 'CCCCCC', 0.2, 'FFFFFF', 0.2)

    # Set the horizontal dotted lines
    chart.set_grid(0, 25, 5, 5)

    # The Y axis labels contains 0 to 100 skipping every 25, but remove the
    # first number because it's obvious and gets in the way of the first X
    # label.
    left_axis = range(0, max_y + 1, 25)
    left_axis[0] = ''
    chart.set_axis_labels(Axis.LEFT, left_axis)

    # X axis labels
    chart.set_axis_labels(Axis.BOTTOM, \
        ['9 am', '6 pm', '12 am'])

    return chart

@app.route('/')
#@app.route('/hello/<name>')
def hello_world(name='test'):
    panel_OCV = panel1.getOCV()
    panel_SCI = panel1.getSCI()
    panelWatt = panel_OCV*panel_SCI
    elecSell = CA.geteCost()
    tonsCoal = 15
    chart = makeChart()
    display = chart.get_url()
    return render_template('template.html', name=name, power=panelWatt,
                           sellAt=elecSell, tonsCoal = tonsCoal, chart = display,
                           panelsOwned = panels)

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 5000))
    app.run(host='0.0.0.0', port=port)


import os
import numpy as np
import imgui
import threading
from array import array
from .gui_utils import imgui_utils

import slideflow as sf

#----------------------------------------------------------------------------

def _apply_cmap(img, cmap):
    import matplotlib.pyplot as plt

    cmap = plt.get_cmap(cmap)
    return (cmap(img) * 255).astype(np.uint8)

def process_grid(_heatmap, _grid):
    if _heatmap.uq:
        logits = _grid[:, :, :-(_heatmap.num_uncertainty)]
        uncertainty = _grid[:, :, -(_heatmap.num_uncertainty):]
        return logits, uncertainty
    else:
        return _grid, None

#----------------------------------------------------------------------------

class HeatmapWidget:
    def __init__(self, viz):
        import matplotlib.pyplot as plt

        self.viz                    = viz
        self.alpha                  = 0.5
        self.gain                   = 1.0
        self.show                   = True
        self.heatmap_logits         = 0
        self.heatmap_uncertainty    = 0
        self.use_logits             = True
        self.use_uncertainty        = False
        self.cmap_idx               = 0
        self.logits                 = None
        self.uncertainty            = None
        self.content_height         = 0
        self._generating            = False
        self._button_pressed        = False
        self._old_logits            = 0
        self._old_uncertainty       = 0
        self._logits_gain           = 1.0
        self._uq_gain               = 1.0
        self._heatmap_sum           = 0
        self._heatmap_grid          = None
        self._heatmap_thread        = None
        self._colormaps             = plt.colormaps()
        self._rendering_message     = "Calculating heatmap..."

    def _create_heatmap(self):
        viz = self.viz
        self.reset()
        if viz.low_memory or sf.slide_backend() == 'cucim':
            mp_kw = dict(num_threads=os.cpu_count())
        else:
            mp_kw = dict(num_processes=os.cpu_count())
        viz.heatmap = sf.heatmap.ModelHeatmap(
            viz.wsi,
            viz.model,
            img_format=viz._model_config['img_format'],
            generate=False,
            normalizer=viz._normalizer,
            uq=viz.has_uq(),
            **mp_kw
        )

    def generate_heatmap(self):
        """Create and generate a heatmap asynchronously."""

        sw = self.viz.slide_widget
        self._create_heatmap()
        self._button_pressed = True
        self._generating = True
        self._heatmap_grid, self._heatmap_thread = self.viz.heatmap.generate(
            asynchronous=True,
            grayspace_fraction=sw.gs_fraction,
            grayspace_threshold=sw.gs_threshold,
            whitespace_fraction=sw.ws_fraction,
            whitespace_threshold=sw.ws_threshold,
        )

    def load(self, path):
        """Load a heatmap from a saved *.npz file."""

        if self.viz.heatmap is None:
            self._create_heatmap()
        self.viz.heatmap.load(path)
        self.logits = self.viz.heatmap.logits
        self.uncertainty = self.viz.heatmap.uncertainty
        self.render_heatmap()

    def refresh_generating_heatmap(self):
        """Refresh render of the asynchronously generating heatmap."""

        if self.viz.heatmap is not None and self._heatmap_grid is not None:
            logits, uncertainty = process_grid(self.viz.heatmap, self._heatmap_grid)
            _sum = np.sum(logits)
            if _sum != self._heatmap_sum:
                self.logits = logits
                self.uncertainty = uncertainty
                self.render_heatmap()
                self._heatmap_sum = _sum

        if self._heatmap_thread is not None and not self._heatmap_thread.is_alive():
            self._generating = False
            self._button_pressed = False
            self._heatmap_thread = None
            self.viz.clear_message(self._rendering_message)

    def render_heatmap(self):
        """Render the current heatmap."""

        self._old_logits = self.heatmap_logits
        self._old_uncertainty = self.heatmap_uncertainty
        if self.viz.heatmap is None:
            return
        if self.use_logits:
            heatmap_arr = self.logits[:, :, self.heatmap_logits]
            gain = self._logits_gain
        else:
            heatmap_arr = self.uncertainty[:, :, self.heatmap_uncertainty]
            gain = self._uq_gain
        self.viz.rendered_heatmap = _apply_cmap(heatmap_arr * gain, self._colormaps[self.cmap_idx])[:, :, 0:3]
        self.update_transparency()

    def reset(self):
        """Reset the heatmap display."""

        self.viz.rendered_heatmap   = None
        self.viz.overlay            = None
        self.viz.heatmap            = None
        self._heatmap_sum           = 0
        self._heatmap_grid          = None
        self._heatmap_thread        = None
        self._old_logits            = 0
        self._old_uncertainty       = 0
        self.logits                 = None
        self.uncertainty            = None
        self._generating            = False
        self.heatmap_logits         = 0
        self.heatmap_uncertainty    = 0

    def update_transparency(self):
        """Update transparency of the heatmap overlay."""

        if self.viz.rendered_heatmap is not None:
            alpha_channel = np.full(self.viz.rendered_heatmap.shape[0:2],
                                    int(self.alpha * 255),
                                    dtype=np.uint8)
            self.viz.overlay = np.dstack((self.viz.rendered_heatmap[:, :, 0:3],
                                                  alpha_channel))
            full_extract = int(self.viz.wsi.tile_um / self.viz.wsi.mpp)
            wsi_stride = int(full_extract / self.viz.wsi.stride_div)
            self.viz._overlay_wsi_dim = (wsi_stride * (self.viz.overlay.shape[1]),
                                         wsi_stride * (self.viz.overlay.shape[0]))
            self.viz._overlay_offset_wsi_dim = (full_extract/2 - wsi_stride/2, full_extract/2 - wsi_stride/2)

    @imgui_utils.scoped_by_object_id
    def __call__(self, show=True):
        viz = self.viz

        if self._generating:
            self.refresh_generating_heatmap()

        if show:
            self.content_height = imgui.get_text_line_height_with_spacing() * 13 + viz.spacing * 2
            _cmap_changed = False
            _uq_logits_switched = False
            bg_color = [0.16, 0.29, 0.48, 0.2]
            dim_color = list(imgui.get_style().colors[imgui.COLOR_TEXT])
            dim_color[-1] *= 0.5

            # Begin heatmap view.
            width = viz.font_size * 20
            height = imgui.get_text_line_height_with_spacing() * 13 + viz.spacing
            imgui.push_style_var(imgui.STYLE_FRAME_PADDING, [0, 0])
            imgui.push_style_color(imgui.COLOR_CHILD_BACKGROUND, *bg_color)
            imgui.push_style_color(imgui.COLOR_HEADER, 0, 0, 0, 0)
            imgui.push_style_color(imgui.COLOR_HEADER_HOVERED, 0.16, 0.29, 0.48, 0.5)
            imgui.push_style_color(imgui.COLOR_HEADER_ACTIVE, 0.16, 0.29, 0.48, 0.9)
            imgui.begin_child('##heatmap_display', width=width, height=height, border=True)

            if viz.rendered_heatmap is not None:
                hw_ratio = (viz.rendered_heatmap.shape[0] / viz.rendered_heatmap.shape[1])
                max_width = min(width - viz.spacing*2, (height - viz.spacing*2) / hw_ratio)
                max_height = max_width * hw_ratio

                if viz._heatmap_tex_obj is not None:
                    imgui.same_line(int((width - max_width)/2))
                    imgui.image(viz._heatmap_tex_obj.gl_id, max_width, max_height)

            # End heatmap view.
            if viz.rendered_heatmap is None:
                imgui.text_colored('Heatmap not generated', *dim_color)
            imgui.end_child()
            imgui.pop_style_color(4)
            imgui.pop_style_var(1)

            imgui.same_line()
            imgui.begin_child('##heatmap_options', width=-1, height=height, border=False)

            # Heatmap options.
            with imgui_utils.grayed_out(viz._model_path is None or viz.wsi is None):

                # Colormap.
                with imgui_utils.item_width(viz.font_size * 6):
                    _clicked, self.cmap_idx = imgui.combo("##cmap", self.cmap_idx, self._colormaps)
                if _clicked:
                    _cmap_changed = True

                imgui.same_line(viz.font_size * 6 + viz.spacing)
                _clicked, self.show = imgui.checkbox('Show', self.show)
                if _clicked:
                    self.render_heatmap()
                    if self.show:
                        self.viz.slide_widget.show_slide_filter  = False
                        self.viz.slide_widget.show_tile_filter   = False

                imgui.same_line(imgui.get_content_region_max()[0] - 1 - viz.button_w)
                _button_text = ('Generate' if not self._button_pressed else "Working...")
                if imgui_utils.button(_button_text, width=viz.button_w, enabled=(not self._button_pressed)):
                    self.viz.set_message(self._rendering_message)
                    _thread = threading.Thread(target=self.generate_heatmap)
                    _thread.start()
                    self.show = True

                with imgui_utils.grayed_out(viz.heatmap is None):

                    # Alpha.
                    with imgui_utils.item_width(-1 - viz.button_w - viz.spacing):
                        _alpha_changed, self.alpha = imgui.slider_float('##alpha',
                                                                        self.alpha,
                                                                        min_value=0,
                                                                        max_value=1,
                                                                        format='Alpha %.2f')

                    imgui.same_line(imgui.get_content_region_max()[0] - 1 - viz.button_w)
                    if imgui_utils.button('Reset##alpha', width=-1, enabled=True):
                        self.alpha = 0.5
                        _alpha_changed = True

                    # Gain.
                    with imgui_utils.item_width(-1 - viz.button_w - viz.spacing):
                        _gain_changed, self.gain = imgui.slider_float('##gain',
                                                                      self.gain,
                                                                      min_value=0,
                                                                      max_value=10,
                                                                      format='Gain %.2f')

                    imgui.same_line(imgui.get_content_region_max()[0] - 1 - viz.button_w)
                    if imgui_utils.button('Reset##gain', width=-1, enabled=True):
                        if self.use_logits:
                            self.gain = 1.0
                        else:
                            self.gain = 1.0
                        _gain_changed = True

                # Logits.
                heatmap_logits_max = 0 if self.logits is None else self.logits.shape[2]-1
                self.heatmap_logits = min(max(self.heatmap_logits, 0), heatmap_logits_max)
                narrow_w = imgui.get_text_line_height_with_spacing()
                with imgui_utils.grayed_out(heatmap_logits_max == 0):
                    if imgui.radio_button('##logits_radio', self.use_logits):
                        if viz.has_uq():
                            _uq_logits_switched = True
                            self.use_logits = True

                    imgui.same_line()
                    with imgui_utils.item_width(-1 - viz.button_w - narrow_w * 2 - viz.spacing * 3):
                        _changed, self.heatmap_logits = imgui.drag_int('##heatmap_logits',
                                                                       self.heatmap_logits,
                                                                       change_speed=0.05,
                                                                       min_value=0,
                                                                       max_value=heatmap_logits_max,
                                                                       format=f'Logits %d/{heatmap_logits_max}')
                    imgui.same_line()
                    if imgui_utils.button('-##heatmap_logits', width=narrow_w):
                        self.heatmap_logits -= 1
                    imgui.same_line()
                    if imgui_utils.button('+##heatmap_logits', width=narrow_w):
                        self.heatmap_logits += 1
                    self.heatmap_logits = min(max(self.heatmap_logits, 0), heatmap_logits_max)
                    if heatmap_logits_max > 0:
                        imgui.same_line()
                        imgui.text(viz._model_config['outcome_labels'][str(self.heatmap_logits)])

                # Uncertainty.
                if viz.heatmap is None or self.uncertainty is None:
                    heatmap_uncertainty_max = 0
                else:
                    heatmap_uncertainty_max = self.uncertainty.shape[2] - 1

                self.heatmap_uncertainty = min(max(self.heatmap_uncertainty, 0), heatmap_uncertainty_max)
                narrow_w = imgui.get_text_line_height_with_spacing()
                with imgui_utils.grayed_out(viz.heatmap is None or not viz.has_uq()):
                    if imgui.radio_button('##uncertainty_radio', not self.use_logits):
                        if viz.has_uq():
                            _uq_logits_switched = True
                            self.use_logits = False

                    imgui.same_line()
                    with imgui_utils.item_width(-1 - viz.button_w - narrow_w * 2 - viz.spacing * 3):
                        _changed, self.heatmap_uncertainty = imgui.drag_int('##heatmap_uncertainty',
                                                                            self.heatmap_uncertainty,
                                                                            change_speed=0.05,
                                                                            min_value=0,
                                                                            max_value=heatmap_uncertainty_max,
                                                                            format=f'UQ %d/{heatmap_uncertainty_max}')
                    imgui.same_line()
                    if imgui_utils.button('-##heatmap_uncertainty', width=narrow_w):
                        self.heatmap_uncertainty -= 1
                    imgui.same_line()
                    if imgui_utils.button('+##heatmap_uncertainty', width=narrow_w):
                        self.heatmap_uncertainty += 1
                    self.heatmap_uncertainty = min(max(self.heatmap_uncertainty, 0), heatmap_uncertainty_max)

                _histogram_size = imgui.get_content_region_max()[0] - 1, viz.font_size * 4
                if viz.heatmap and self.logits is not None:
                    flattened = self.logits[:, :, self.heatmap_logits].flatten()
                    flattened = flattened[flattened >= 0]
                    _hist, _bin_edges = np.histogram(flattened, range=(0, 1))
                    if flattened.shape[0] > 0:
                        overlay_text = f"Predictions (avg: {np.mean(flattened):.2f})"
                        _hist_arr = array('f', _hist/np.sum(_hist))
                        scale_max = np.max(_hist/np.sum(_hist))
                    else:
                        overlay_text = "Predictions (avg: - )"
                        _hist_arr = array('f', [0])
                        scale_max = 1
                    imgui.separator()
                    imgui.core.plot_histogram('##heatmap_pred',
                                              _hist_arr,
                                              scale_min=0,
                                              overlay_text=overlay_text,
                                              scale_max=scale_max,
                                              graph_size=_histogram_size)

                if viz.heatmap and self.uncertainty is not None:
                    flattened = self.uncertainty[:, :, self.heatmap_uncertainty].flatten()
                    flattened = flattened[flattened >= 0]
                    _hist, _bin_edges = np.histogram(flattened)
                    if flattened.shape[0] > 0:
                        overlay_text = f"Uncertainty (avg: {np.mean(flattened):.2f})"
                        _hist_arr = array('f', _hist/np.sum(_hist))
                        scale_max = np.max(_hist/np.sum(_hist))
                    else:
                        overlay_text = "Uncertainty (avg: - )"
                        _hist_arr = array('f', [0])
                        scale_max = 1
                    imgui.separator()
                    imgui.core.plot_histogram('##heatmap_pred',
                                              _hist_arr,
                                              scale_min=0,
                                              overlay_text=overlay_text,
                                              scale_max=scale_max,
                                              graph_size=_histogram_size)
                    imgui.separator()
                elif not viz.has_uq():
                    imgui.text("Model not trained with uncertainty.")

            imgui.end_child()

            # Render heatmap.
            if _alpha_changed:
                self.update_transparency()
            if _gain_changed:
                if self.use_logits:
                    self._logits_gain = self.gain
                else:
                    self._uq_gain = self.gain
                self.render_heatmap()
            if _uq_logits_switched:
                self.gain = self._logits_gain if self.use_logits else self._uq_gain
                self.render_heatmap()
            if (_cmap_changed
               or (self.heatmap_logits != self._old_logits and self.use_logits)
               or (self.heatmap_uncertainty != self._old_uncertainty and self.use_uncertainty)
               or _uq_logits_switched):
                self.render_heatmap()
        else:
            self.content_height = 0
#----------------------------------------------------------------------------


import torch
import torchvision.datasets
from sklearn.neighbors import KNeighborsClassifier
from torchvision import models
from utils.Dataset_config import *
from utils.Dataset import *

knn_model = None
pt_resnet50_model_cut = None

'''
rose
1) Dataset usage example and forward into resnet50 model in "get_random_samples_resnet50_forward(nBatches : int = 1)"
    a) the example above also includes combining the results of different forward iterations
2) for knn module you might need to read documentation, can be found in sklearn documentation

'''

def init_Knn_model(
        train_dataset,  # iterable, dim0 = X, dim1 = y
        n_neighbors: int
):
    global knn_model
    knn_model = KNeighborsClassifier(n_neighbors=n_neighbors)
    knn_model.fit(train_dataset[0], train_dataset[1])

def knn_predict(X):
    global knn_model
    assert knn_model is not None  # must init first
    x = X.detach()
    print(x.shape)
    return knn_model.predict(x )

def knn_validate(X_test,y_test, use_print=False):
    global knn_model
    assert knn_model is not None  # must init first
    y_pred = knn_model.predict(X_test)
    miss_classifications = y_test - y_pred
    accu = 1 - torch.count_nonzero(miss_classifications) / len(y_test)
    if use_print:
        print(f"knn miss classified {torch.count_nonzero(miss_classifications)} out of {len(y_test)}")
        print(f"Accuracy {accu} ")
    return accu

def knn_sanity_check():
    forw,y = get_random_samples_resnet50_forward(2)
    to_knn_train_fit = [forw[32:],y[32:]] # must stack , or change innnir implementation of init_Knn_model
    X_test = forw[:32]
    y_test = y[:32]

    init_Knn_model(dataset=to_knn_train_fit,n_neighbors=1)
    print("validate on same trainset, with k=0 should have 100% accuracy")
    knn_validate(to_knn_train_fit[0],to_knn_train_fit[1],use_print=True)
    print("now test set ... ")
    knn_validate(X_test,y_test,use_print=True)

def init_pre_trained_resnet50_model():
    global pt_resnet50_model_cut
    resnet50 = models.resnet50(pretrained=True)
    resnet50.eval()
    pt_resnet50_model_cut = torch.nn.Sequential(*(list(resnet50.children())[:-1]))  # strips off last linear layer

def get_random_samples_resnet50_forward(nBatches : int = 1):
    global pt_resnet50_model_cut
    if pt_resnet50_model_cut is None:
        init_pre_trained_resnet50_model()

    result_tensor = torch.zeros(size=(nBatches*BATCH_SIZE, 2048))
    tags_tensor = torch.zeros(nBatches*BATCH_SIZE)
    with torch.no_grad():
        for i, Xy in zip(range(nBatches),camelyon17_dl):
            X, y = Xy[0], Xy[1]  # X,y shape[0] == BATCH_SIZE
            result_tensor[i*BATCH_SIZE: (i+1) * BATCH_SIZE] = pt_resnet50_model_cut.forward(X).squeeze()
            tags_tensor[i*BATCH_SIZE: (i+1) * BATCH_SIZE] = y
    return result_tensor, tags_tensor


import pytest
import selenium.common
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

"""
|-------------------------------------------------------------------------------------------------|
Test functionality of deleting items from list

Prepare browser:
    1. Open chrome
    2. Insert URL
    3. Delete all cookies
    5. Maximize window
    6. Set page timeout to 5 seconds
    7. Find and save input to insert new items
    8. Create objects to handle keyboard and mouse operation
    9. Create 3 new items add it to list
    10. Localize this items and their destroy button
After test execution:
    1. Close browser
|-------------------------------------------------------------------------------------------------|
"""

@pytest.fixture
def openAndPrepareBrowser():
    global driver, todo, keyboard_action, mouse_action, entry1, entry2, entry3, delete_button1, delete_button2, delete_button3
    driver = webdriver.Chrome()
    driver.get("https://todomvc.com/examples/vue/")
    driver.delete_all_cookies()
    driver.maximize_window()
    driver.set_page_load_timeout(5)
    todo = driver.find_element(By.XPATH, "//input[@class='new-todo']")

    mouse_action = ActionChains(driver)
    keyboard_action = ActionChains(driver)

    todo.send_keys("test1")
    keyboard_action.send_keys(Keys.ENTER).perform()
    todo.clear()
    todo.send_keys("test2")
    keyboard_action.send_keys(Keys.ENTER).perform()
    todo.clear()
    todo.send_keys("test3")
    keyboard_action.send_keys(Keys.ENTER).perform()
    todo.clear()

    entry1 = driver.find_element(By.XPATH, "//label[text()='test1']")
    entry2 = driver.find_element(By.XPATH, "//label[text()='test2']")
    entry3 = driver.find_element(By.XPATH, "//label[text()='test3']")

    delete_button1 = driver.find_element(By.XPATH, "//label[text()='test1']/following-sibling::button")
    delete_button2 = driver.find_element(By.XPATH, "//label[text()='test2']/following-sibling::button")
    delete_button3 = driver.find_element(By.XPATH, "//label[text()='test3']/following-sibling::button")

    mouse_action = ActionChains(driver)

    yield
    driver.close()

"""
|-------------------------------------------------------------------------------------------------|
Test deleting all added items to list
Steps:
    1. Delete all items
    2. Try to check it they exist
    3. If not - test passed
|-------------------------------------------------------------------------------------------------|
"""

@pytest.mark.DeleteAll
def test_Delete_All_ToDos(openAndPrepareBrowser):
    mouse_action.move_to_element(entry1).perform()
    delete_button1.click()

    mouse_action.move_to_element(entry2).perform()
    delete_button2.click()

    mouse_action.move_to_element(entry3).perform()
    delete_button3.click()

    driver.get_screenshot_as_file("screenshots\\test_Delete_All_ToDos.png")

    try:
        entry1
    except selenium.common.exceptions.NoSuchElementException:
        assert True

    try:
        entry2
    except selenium.common.exceptions.NoSuchElementException:
        assert True

    try:
        entry3
    except selenium.common.exceptions.NoSuchElementException:
        assert True

"""
|-------------------------------------------------------------------------------------------------|
Test deleting an item located between two other items
Steps:
    1. Delete item
    2. Try to check it it exist
    3. Check if another two items still exists
|-------------------------------------------------------------------------------------------------|
"""

@pytest.mark.DeleteChoosen
def test_Delete_From_Between(openAndPrepareBrowser):
    mouse_action.move_to_element(entry2).perform()
    delete_button2.click()

    driver.get_screenshot_as_file("screenshots\\test_Delete_From_Between.png")

    try:
        entry2
    except selenium.common.exceptions.NoSuchElementException:
        assert True

    assert entry1
    assert entry3

"""
|-------------------------------------------------------------------------------------------------|
Test deleting first item
Steps:
    1. Delete first item
    2. Try to check it it exist
    3. Check if another two items exist
|-------------------------------------------------------------------------------------------------|
"""

@pytest.mark.DeleteChoosen
def test_Delete_First_One(openAndPrepareBrowser):
    mouse_action.move_to_element(entry1).perform()
    delete_button1.click()

    driver.get_screenshot_as_file("screenshots\\test_Delete_First_One.png")

    try:
        entry1
    except selenium.common.exceptions.NoSuchElementException:
        assert True

    assert entry2
    assert entry3

"""
|-------------------------------------------------------------------------------------------------|
Test deleting last item
Steps:
    1. Delete last item
    2. Try to check it it exist
    3. Check if another two items exist
|-------------------------------------------------------------------------------------------------|
"""

@pytest.mark.DeleteChoosen
def test_Delete_Last_One(openAndPrepareBrowser):
    mouse_action.move_to_element(entry3).perform()
    delete_button3.click()

    driver.get_screenshot_as_file("screenshots\\test_Delete_Last_One.png")

    try:
        entry3
    except selenium.common.exceptions.NoSuchElementException:
        assert True

    assert entry1
    assert entry2


"""

"""
from order_app.models import BuyOrder, SellOrder, OrderBookState
from datetime import datetime

def populate_order_table(self) -> None:
    """"""
    state, b_created = OrderBookState.objects.get_or_create(
        lookup_time=self.lookup_time)

    for order_type, order_set in (
            ("sell", self.sell_orders), ("buy", self.buy_orders)
    ):
        for row in order_set:
            date_as_int = float(
                "{}.{}".format(row["date"][:10], row["date"][10:])
            )
            date = datetime.fromtimestamp(date_as_int)
            if order_type == "sell":
                use_model = SellOrder
            else:
                use_model = BuyOrder
            obj, created = use_model.objects.get_or_create(
                amount=row["amount"],
                date=date,
                label=row["label"],
                order_id=row["orderId"],
                price=row["price"],
                total=row["total"])
            if not created:
                print("Cannot create Order coz already created")
            if order_type == "sell":
                state.sell_orders.add(obj)
            else:
                state.buy_orders.add(obj)

if __name__ == '__main__':
    for row in rows:
        row.poulate_order_table()


from firecore_torch.metrics import Accuracy, Average
import firecore
import typed_args as ta
from pathlib import Path

from typing import Dict, List
from torch import nn

import torch
from icecream import ic
from firecore_torch.helpers.dist_utils import init_process_group
from torch import Tensor
from firecore_torch.runners import Trainer
from firecore_torch import helpers
import logging
from torch.utils.tensorboard import SummaryWriter
from firecore_torch.helpers.arguments import Args

logger = logging.getLogger(__name__)

def get_backend(device_type: str):
    return {
        'cuda': 'nccl',
        'cpu': 'gloo',
    }[device_type]

@firecore.main_fn
def main():
    # import tracemalloc

    # tracemalloc.start()

    # firecore.logging.init(level='INFO')

    args = Args.parse_args()

    args.work_dir.mkdir(parents=True)

    firecore.logging.init(
        filename=str(args.work_dir / 'run.log'),
        level=logging.INFO,
    )

    device = args.device
    backend = get_backend(device.type)

    init_process_group(backend)

    ic(args)

    cfg = firecore.config.from_file(str(args.config), jpathdir='.')

    ic(cfg)

    base_model: nn.Module = firecore.resolve(cfg['model'])
    model = helpers.make_dist_model(base_model, device)
    ic(base_model)
    criterion: nn.Module = firecore.resolve(cfg['criterion'])
    criterion.to(device)
    ic(criterion)
    params = firecore.resolve(cfg['params'], model=base_model)
    ic(params)
    optimizer = firecore.resolve(cfg['optimizer'], params=params)
    ic(optimizer)
    lr_scheduler = firecore.resolve(cfg['lr_scheduler'], optimizer=optimizer)
    ic(lr_scheduler)

    max_epochs: int = cfg['base']['max_epochs']

    summary_writer = SummaryWriter(
        log_dir=str(args.work_dir/'tf_logs')
    )

    plans: List[Dict] = cfg['plans']

    shared = dict(
        base_model=base_model,
        model=model,
        criterion=criterion,
        optimizer=optimizer,
        lr_scheduler=lr_scheduler,
        device=device,
        max_epochs=max_epochs,
        summary_writer=summary_writer,
        work_dir=args.work_dir,
    )

    workflows: Dict[str, Trainer] = {}
    for plan in plans:
        key = plan['key']
        workflows[key] = firecore.resolve(
            cfg[key]
        )(**shared, stage=key)

    for epoch in range(1, 2 + 1):
        for plan in plans:
            if epoch % plan['interval'] == 0:
                workflow = workflows[plan['key']]
                workflow.step(epoch)