import re

from PySide2.QtGui import QCursor, QIcon, QTextCursor
from PySide2.QtWidgets import QApplication, QFileDialog, QTabBar, QTableWidgetItem, QMenu, QTextEdit, QTableWidget, \
    QAbstractItemView, QMessageBox, QInputDialog, QDialog, QLabel, QLineEdit, QHeaderView, QWidget, QComboBox
from PySide2.QtCore import QPoint, QModelIndex, Qt

import ipaddress  # check input for IP
import netifaces # Get ifaces ip addr
import html # To escape text from snippets

from core.database import Database
from core.config import Config
from ui.custom_command import Custom_Command
from ui.new_scan import New_Scan
from ui.search import Search
from ui.set_variables import Set_Variables
from ui.about import About
from ui.credentials import Credentials_dialog
from utils.QNoteTextEdit import QNoteTextEdit

class View:
    def __init__(self, main_window, controller):
        self.ui = main_window
        self.controller = controller

        self.default_tabs = [{'name': 'Ports', 'object': type(QTableWidget())},
                             {'name': 'Nmap', 'object': type(QTextEdit())},
                             {'name': 'Credentials', 'object': type(QTableWidget())},
                             {'name': 'Notes', 'object': type(QNoteTextEdit())}]
        self.app_tabs = dict()

        self.setup_ui()
        self.connect_slots()

    def setup_ui(self):
        self.ui.ui.host_list.setColumnHidden(0, True)
        self.ui.ui.hosts_for_port_table.setColumnHidden(0, True)
        self.ui.ui.creds_table.setColumnHidden(0, True)
        self.ui.ui.log_table.setColumnHidden(0, True)
        self.ui.ui.job_table.setColumnHidden(0, True)
        self.ui.ui.job_table.setColumnHidden(1, True)
        self.ui.ui.job_table.setColumnHidden(2, True)
        self.ui.ui.job_table.setColumnHidden(6, True)
        self.ui.ui.hosts_for_port_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.ui.ui.creds_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.ui.ui.actionEnable_automatic_tools.setChecked(Config.get()['user_prefs']['enable_autorun'])
        self.ui.ui.actionAutosave_database_every_5_mins.setChecked(Config.get()['user_prefs']['autosave'])
        self.ui.ui.actionEnable_automatic_tools_on_import.setChecked(Config.get()['user_prefs']['enable_autorun_on_xml_import'])
        self.ui.ui.host_list.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.reset_ui_snippets()

    def reset_ui_snippets(self):
        for interface_name in Config.get()['user_prefs']['preferred_interfaces']:
            if interface_name in netifaces.interfaces() and netifaces.AF_INET in netifaces.ifaddresses(interface_name).keys():
                self.ui.ui.lhost.setText(netifaces.ifaddresses(interface_name)[netifaces.AF_INET][0]['addr'])
                break
        self.ui.ui.lport.setText(str(Config.get()['user_prefs']['preferred_lport']))
        self.setup_ui_snippets_body()

    def setup_ui_snippets_body(self):
        # Good luck.
        def create_subsection(level: int, content):
            final_subsection = ""
            for i, chunk in enumerate(content):
                current_string = ""
                if isinstance(chunk, str):
                    for char in set(self.ui.ui.escaped_chars.text()):
                        chunk = chunk.replace(char, '\\' + char)
                    for char in set(self.ui.ui.urlencoded_chars.text()):
                        chunk = chunk.replace(char, '%' + str(ord(char)))
                    chunk = html.escape(chunk)
                    if i != len(content)-1 and isinstance(content[i+1], list):
                        current_string += f'<h{level + 4}>{chunk}</h{level + 4}>'
                    else:
                        current_string += chunk + '
'
                elif isinstance(chunk, list):
                    if final_subsection and '<h' not in final_subsection:
                        final_subsection = f'
{final_subsection}
'
                    current_string += create_subsection(level+1, chunk)

                if '<h' in current_string:
                    if (i == len(content)-1 or isinstance(content[i+1], list)) and final_subsection and '<h' not in final_subsection and '<p' not in final_subsection:
                        final_subsection = f'
{final_subsection}
'
                    final_subsection += current_string
                else:
                    final_subsection += current_string
                    if (i == len(content)-1 or isinstance(content[i+1], list)) and final_subsection and '<h' not in final_subsection and '<p' not in final_subsection:
                        final_subsection = f'
{final_subsection}
'

            if final_subsection and '<h' not in final_subsection:
                final_subsection = f'
{final_subsection}
'

            # Clean-up
            final_subsection = re.sub('(
]+>)(
]+>)+', '\\1', final_subsection)
            final_subsection = re.sub('(
)(
)+', '\\1', final_subsection)
            final_subsection = re.sub('

', '
', final_subsection)

            return final_subsection

        self.ui.ui.snippets_tabs.clear()
        tabs = {}
        for i, tab in enumerate(Config.get()['snippets']):
            tabs[i] = QTextEdit()
            if not isinstance(Config.get()['snippets'][tab], list):
                continue
            section = ""
            for element in Config.get()['snippets'][tab]:
                if isinstance(element, str):
                    section += f'
{element}
'
                if isinstance(element, list):
                    section += create_subsection(0, element)

            section = section.replace("%%%LHOST%%%", self.ui.ui.lhost.text())
            section = section.replace("%%%LPORT%%%", self.ui.ui.lport.text())

            tabs[i].setHtml(section)
            tabs[i].moveCursor(QTextCursor.Start)
            self.ui.ui.snippets_tabs.addTab(tabs[i], tab)

    def connect_slots(self):
        self.ui.ui.actionNew.triggered.connect(self.controller.clear_db)
        self.ui.ui.actionOpen.triggered.connect(self.open_db)
        self.ui.ui.actionSave.triggered.connect(self.save_db)
        self.ui.ui.actionSaveAs.triggered.connect(self.save_as_db)
        self.ui.ui.actionExit.triggered.connect(self.ui.close)
        self.ui.ui.actionSet_variables.triggered.connect(self.set_variables)
        self.ui.ui.actionSearch_string.triggered.connect(self.dialog_search)
        self.ui.ui.actionReload_configuration_from_file.triggered.connect(self.controller.reload_conf)
        self.ui.ui.actionAbout.triggered.connect(self.about)
        self.ui.ui.actionAbout_Qt.triggered.connect(QApplication.aboutQt)
        self.ui.ui.ImportNmap.clicked.connect(self.dialog_import_xml)
        self.ui.ui.ScanHost.clicked.connect(self.dialog_scan_host)
        self.ui.ui.host_list.clicked.connect(self.update_right_panel)
        self.ui.ui.button_play.clicked.connect(self.clicked_button_play)
        self.ui.ui.button_pause.clicked.connect(self.clicked_button_pause)
        self.ui.ui.button_stop.clicked.connect(self.clicked_button_stop)
        self.ui.ui.job_table.clicked.connect(self.enable_buttons)
        self.ui.ui.host_list.doubleClicked.connect(self.copy_ip_in_clipboard_from_hostlist)
        self.ui.ui.host_list.customContextMenuRequested.connect(self.right_click_in_host_table)
        self.ui.ui.job_table.customContextMenuRequested.connect(self.right_click_in_job_table)
        self.ui.ui.log_table.customContextMenuRequested.connect(self.right_click_in_log_table)
        self.ui.ui.application_TabWidget.tabCloseRequested.connect(self.remove_tab)
        self.ui.ui.host_list_filter.textEdited.connect(self.apply_filter_on_host_list)
        self.ui.ui.creds_table_filter.textEdited.connect(self.apply_filter_on_creds_table)
        self.ui.ui.creds_table.customContextMenuRequested.connect(self.right_click_in_creds_table)
        self.ui.ui.creds_table_copy_selection_to_clipboard.clicked.connect(lambda: self.copy_selectedIndexes_to_clipboard(self.ui.ui.creds_table.selectedIndexes()))
        self.ui.ui.creds_table_copy_all_to_clipboard.clicked.connect(self.creds_table_copy_all_to_clipboard)
        self.ui.ui.actionEnable_automatic_tools.triggered.connect(lambda checked: Config.set(["user_prefs", "enable_autorun"], checked))
        self.ui.ui.actionEnable_automatic_tools_on_import.triggered.connect(lambda checked: Config.set(["user_prefs", "enable_autorun_on_xml_import"], checked))
        self.ui.ui.reset_lhost_lport.clicked.connect(self.reset_ui_snippets)
        self.ui.ui.lhost.textEdited.connect(self.setup_ui_snippets_body)
        self.ui.ui.lport.textEdited.connect(self.setup_ui_snippets_body)
        self.ui.ui.escaped_chars.textEdited.connect(self.setup_ui_snippets_body)
        self.ui.ui.urlencoded_chars.textEdited.connect(self.setup_ui_snippets_body)
        self.ui.ui.port_table.itemSelectionChanged.connect(self.change_filter_hosts_for_port)
        self.ui.ui.machine_list_copy_selection_to_clipboard.clicked.connect(lambda: self.copy_selectedIndexes_to_clipboard(self.ui.ui.hosts_for_port_table.selectedIndexes()))
        self.ui.ui.machine_list_copy_all_to_clipboard.clicked.connect(self.machine_list_copy_all_to_clipboard)
        self.ui.ui.hosts_for_port_table.customContextMenuRequested.connect(self.right_click_in_hosts_for_port_table)

    def about(self):
        about_dialog = About()
        about_dialog.setWindowTitle("About this program")
        about_dialog.ui.text.setText(f"
QtRecon {self.controller.APPLICATION_VERSION}

2023, licenced under Creative Commons CC-BY

Thanks to my friends, for all the advices they gave me and the beta-testing while developping this thing

https://github.com/bouligo/cuterecon/")
        about_dialog.ui.image.setPixmap("icons/icon.ico")
        about_dialog.ui.button.clicked.connect(about_dialog.close)
        about_dialog.exec()

    def dialog_search(self):
        search_dialog = Search()
        search_dialog.ui.search_button.clicked.connect(lambda: self.search(search_dialog))
        search_dialog.ui.host_list.currentRowChanged.connect(lambda: self.search_in_host(search_dialog))
        search_dialog.exec()

    def search(self, search_dialog: QDialog):
        search_dialog.ui.host_list.clear()
        search_dialog.ui.results.clear()
        search_dialog.ui.host_list.addItems(self.controller.search_string_in_db_hosts(search_dialog.ui.search_input.text()))

    def search_in_host(self, search_dialog: QDialog):
        if search_dialog.ui.host_list.currentItem() is None:
            return
        search_dialog.ui.results.setText('\n'.join(
            self.controller.search_string_in_db_hosts(search_dialog.ui.search_input.text(),
            search_dialog.ui.host_list.currentItem().text())))

    def set_variables(self):
        set_variables_dialog = Set_Variables()
        config_variables = Config.get()['user_variables']
        dialog_variables = {} #Storing Widgets to get inputs easily
        for i, variable in enumerate(config_variables):
            input_field = QLineEdit(config_variables[variable])
            dialog_variables[variable] = input_field
            set_variables_dialog.ui.formLayout.insertRow(i, QLabel(variable + ':'), input_field)

        if not set_variables_dialog.exec():
            return

        for variable in dialog_variables:
            Config.set(["user_variables", variable], dialog_variables[variable].text())

    def select_credentials_dialog(self, credentials: list) -> (str, str, str, str):
        select_credentials_dialog = Credentials_dialog()
        for i, credential in enumerate(credentials):
            select_credentials_dialog.ui.creds_table.insertRow(select_credentials_dialog.ui.creds_table.rowCount())
            select_credentials_dialog.ui.creds_table.setItem(i, 0, QTableWidgetItem(credential['type']))
            select_credentials_dialog.ui.creds_table.setItem(i, 1, QTableWidgetItem(credential['domain']))
            select_credentials_dialog.ui.creds_table.setItem(i, 2, QTableWidgetItem(credential['username']))
            select_credentials_dialog.ui.creds_table.setItem(i, 3, QTableWidgetItem(credential['password']))
        select_credentials_dialog.ui.creds_table.selectRow(0)
        select_credentials_dialog.exec()

        if select_credentials_dialog.result():
            # Returns type, domain, username and password
            return [cell.data() for cell in select_credentials_dialog.ui.creds_table.selectedIndexes()]
        else:
            return None, None, None, None

    def enable_buttons(self, index: QModelIndex):
        job_details = self.controller.get_job_details(index)
        if job_details['state'] == 'Running':
            self.ui.ui.button_play.setEnabled(False)
            self.ui.ui.button_pause.setEnabled(True)
            self.ui.ui.button_stop.setEnabled(True)
        elif job_details['state'] == 'NotRunning':
            self.ui.ui.button_play.setEnabled(True)
            self.ui.ui.button_pause.setEnabled(False)
            self.ui.ui.button_stop.setEnabled(True)
        else:
            self.ui.ui.button_play.setEnabled(False)
            self.ui.ui.button_pause.setEnabled(False)
            self.ui.ui.button_stop.setEnabled(False)

    def refresh_buttons(self):
        if self.ui.ui.job_table.selectedIndexes():
            self.enable_buttons(self.ui.ui.job_table.selectedIndexes()[0])

    def clicked_button_play(self):
        selected_row = self.ui.ui.job_table.selectedIndexes()
        job_details = self.controller.get_job_details(selected_row[0])  # Choosing first cell to get the job details
        self.controller.resume_job(job_details['id'])
        self.ui.ui.button_play.setEnabled(False)
        self.ui.ui.button_pause.setEnabled(True)

    def clicked_button_pause(self):
        selected_row = self.ui.ui.job_table.selectedIndexes()
        job_details = self.controller.get_job_details(selected_row[0])  # Choosing first cell to get the job details
        self.controller.pause_job(job_details['id'])
        self.ui.ui.button_play.setEnabled(True)
        self.ui.ui.button_pause.setEnabled(False)

    def clicked_button_stop(self):
        selected_row = self.ui.ui.job_table.selectedIndexes()
        job_details = self.controller.get_job_details(selected_row[0]) # Choosing first cell to get the job details
        self.controller.kill_job(job_details['id'])
        self.ui.ui.button_play.setEnabled(False)
        self.ui.ui.button_pause.setEnabled(False)
        self.ui.ui.button_stop.setEnabled(False)

    def apply_filter_on_host_list(self, search_input: str):
        self.controller.filter_hostlist(search_input)

    def apply_filter_on_creds_table(self, search_input: str):
        self.controller.filter_credstable(search_input)

    def copy_ip_in_clipboard_from_hostlist(self, index: QModelIndex):
        host = self.controller.get_selected_host(index)
        clipboard = QApplication.clipboard()
        clipboard.setText(host['ip'])
        self.ui.statusBar().showMessage('IP copied to clipboard !', 5000)

    def right_click_in_host_table(self, qpoint: QPoint):
        item = self.ui.ui.host_list.indexAt(qpoint)
        if item.row() == -1:
            return

        self.update_right_panel(item)
        host = self.controller.get_selected_host(item)

        top_menu = QMenu(None)
        set_ip_action = top_menu.addAction("Change IP")
        set_hostname_action = top_menu.addAction("Set / Change hostname")
        set_os_action = top_menu.addAction("Set / Change OS")
        set_pwned = top_menu.addAction("(Un-)Mark as pwned")
        set_color = top_menu.addMenu("Highlight with color ...")
        color_menu_dict = {}

        color_menu_dict['None'] = set_color.addAction('None')
        set_color.addSeparator()
        # list of colors from doc: https://doc.qt.io/qtforpython-5/PySide2/QtCore/Qt.html#PySide2.QtCore.PySide2.QtCore.Qt.GlobalColor
        for color in ["white", "red", "green", "cyan", "magenta", "yellow", "darkRed", "darkGreen", "blue", "darkBlue", "darkCyan", "darkMagenta", "darkYellow", "gray", "darkGray", "lightGray", "black"]:
            color_menu_dict[color] = set_color.addAction(color)

        top_menu.addSeparator()
        autorun_action = top_menu.addAction("Run all autorun configured programs")
        top_menu.addSeparator()
        remove_action = top_menu.addAction("Delete host and data")

        action = top_menu.exec_(QCursor.pos())
        if not action:
            return

        if action == remove_action:
            self.controller.delete_host(item)
            self.ui.ui.host_list.clearSelection()
            self.close_right_panel_tabs()
        elif action == set_ip_action:
            new_ip, confirmed = QInputDialog.getText(None, "Change IP", "Set the IP address to :", text = host['ip'])
            if confirmed:
                try:
                    ipaddress.IPv4Address(new_ip) # fails if not a valid IP address
                    if host['ip'] != new_ip:
                        self.controller.change_host_ip(item, new_ip)
                        self.close_right_panel_tabs()
                except ipaddress.AddressValueError:
                    QMessageBox.critical(None, "Cannot change IP", f"Cannot change ip address of \"{host['ip']}\" to \"{new_ip}\"")
        elif action == set_hostname_action:
            new_hostname, confirmed = QInputDialog.getText(None, "Set hostname", "Set the hostname to :", text = host['hostname'])
            if confirmed:
                self.controller.change_host_hostname(item, new_hostname)
                self.close_right_panel_tabs()
        elif action == set_os_action:
            new_os, confirmed = QInputDialog.getItem(None, "Set Operating System", "Set the OS to :", ["Windows", "Linux", "IOS", "Unknown"], editable=False)
            if confirmed:
                self.controller.change_host_os(item, new_os)
                self.close_right_panel_tabs()
        elif action == set_pwned:
            self.controller.set_host_pwned(item)
        elif action in color_menu_dict.values():
            if action.text() == 'None': # 'd better not trust this ? Use the key from color_menu_dict instead ?
                self.controller.set_host_highlight_color(item, "")
            else:
                 self.controller.set_host_highlight_color(item, action.text())
        elif action == autorun_action:
            self.controller.autorun([host['id']])

    def right_click_in_log_table(self, qpoint: QPoint):
        item = self.ui.ui.log_table.indexAt(qpoint)
        log = self.ui.ui.log_table.model().itemData(item)

        if not log:
            return

        top_menu = QMenu(None)
        clipboard_action = top_menu.addAction("Copy log into clipboard")
        clipboard_action.setIcon(QIcon.fromTheme("edit-copy"))

        action = top_menu.exec_(QCursor.pos())
        if not action:
            return

        if action == clipboard_action:
            clipboard = QApplication.clipboard()
            clipboard.setText(log['log'])

    def right_click_in_job_table(self, qpoint: QPoint):
        item = self.ui.ui.job_table.indexAt(qpoint)
        job = self.controller.get_job_details(item)

        if not job:
            return

        command = job['command']

        top_menu = QMenu(None)
        clipboard_action = top_menu.addAction("Copy command into clipboard")
        clipboard_action.setIcon(QIcon.fromTheme("edit-copy"))

        action = top_menu.exec_(QCursor.pos())
        if not action:
            return

        if action == clipboard_action:
            clipboard = QApplication.clipboard()
            clipboard.setText(command)

    def right_click_in_port_table(self, qpoint: QPoint):
        item = self.app_tabs['Ports'].itemAt(qpoint)
        if item is None:
            return

        host = self.ui.ui.host_list.model().itemData(self.ui.ui.host_list.currentIndex())
        port = self.app_tabs['Ports'].item(item.row(), 2).text()
        proto = self.app_tabs['Ports'].item(item.row(), 0).text()
        description = self.app_tabs['Ports'].item(item.row(), 3).text()

        top_menu, visible_actions = self.create_actions_from_port(proto, port)

        custom_command = top_menu.addAction("Custom command ...")
        copy_description = top_menu.addAction("Copy service description into clipboard")
        copy_description.setIcon(QIcon.fromTheme("edit-copy"))
        copy_uri = top_menu.addAction(f"Copy \"{host['ip']}:{port}\" into clipboard")
        copy_uri.setIcon(QIcon.fromTheme("edit-copy"))
        action = top_menu.exec_(QCursor.pos())

        if not action:
            return

        if action == copy_description:
            clipboard = QApplication.clipboard()
            clipboard.setText(description)

        if action == copy_uri:
            clipboard = QApplication.clipboard()
            clipboard.setText(f"{host['ip']}:{port}")

        if action == custom_command:
            self.launch_custom_command_dialog()

        for program in visible_actions:
            if program['text'] == action.text():
                self.controller.new_job(program)
                return

    def right_click_in_hosts_for_port_table(self, qpoint: QPoint):
        item = self.ui.ui.hosts_for_port_table.indexAt(qpoint) # But we're not using this here, as multiple selection can occur
        if item is None:
            return

        proto = self.ui.ui.port_table.item(self.ui.ui.port_table.currentRow(),0).text()
        port = self.ui.ui.port_table.item(self.ui.ui.port_table.currentRow(),1).text()
        hosts = dict()
        for index in self.ui.ui.hosts_for_port_table.selectedIndexes():
            itemData = self.ui.ui.hosts_for_port_table.model().itemData(index)
            if itemData['id'] not in hosts.keys():
                hosts[itemData['id']] = itemData

        top_menu, visible_actions = self.create_actions_from_port(proto, port)
        custom_command = top_menu.addAction("Custom command ...")
        action = top_menu.exec_(QCursor.pos())

        if not action:
            return

        if action == custom_command:
            self.launch_custom_command_dialog()

        for program in visible_actions:
            if program['text'] == action.text():
                for host in hosts:
                    self.controller.new_job(program, hosts[host]['id'], port)
                return

    def right_click_in_creds_table(self, qpoint: QPoint):
        if self.ui.ui.work.currentIndex() == 0: # Calling from a host
            widget_calling = self.app_tabs['Credentials']
        elif self.ui.ui.work.currentIndex() == 2:  # Calling from the credentials tab
            widget_calling = self.ui.ui.creds_table

        item = widget_calling.indexAt(qpoint)

        if item is None:
            return

        top_menu = QMenu()

        if item.row() >= 0 or item.column() >= 0:
            clipboard_action = top_menu.addAction("Copy selected cells into clipboard")
            clipboard_action.setIcon(QIcon.fromTheme("edit-copy"))
            delete_action = top_menu.addAction("Delete credentials")
            delete_action.setIcon(QIcon.fromTheme("edit-delete"))
            if self.ui.ui.work.currentIndex() == 0:
                top_menu.addSeparator()
        if self.ui.ui.work.currentIndex() == 0:
            insert_action = top_menu.addAction("Insert new credentials")
            insert_action.setIcon(QIcon.fromTheme("document-new"))

        action = top_menu.exec_(QCursor.pos())
        if not action:
            return

        if self.ui.ui.work.currentIndex() == 0 and action == insert_action:
            widget_calling.insertRow(widget_calling.rowCount())
            new_id = self.controller.create_creds()
            widget_calling.setItem(widget_calling.rowCount()-1, 0, QTableWidgetItem(str(new_id)))

            combo = QComboBox()
            combo.addItems(["password", "hash", "ssh_key", "other"])
            combo.setProperty('cred_id', str(new_id))
            widget_calling.setCellWidget(widget_calling.rowCount()-1, 1, combo)
            combo.currentTextChanged.connect(lambda new_type, qcombobox=combo: self.credentials_changed(qcombobox)) # signal in loop: https://stackoverflow.com/questions/46300229/connecting-multiples-signal-slot-in-a-for-loop-in-pyqt

        elif action == clipboard_action:
            self.copy_selectedIndexes_to_clipboard(widget_calling.selectedIndexes())

        elif action == delete_action:
            items = widget_calling.selectedIndexes()

            creds_ids = []
            rows_to_delete = []
            for item in items:
                if self.ui.ui.work.currentIndex() == 0:
                    creds_ids.append(widget_calling.item(item.row(), 0).text())
                    rows_to_delete.append(item.row())
                if self.ui.ui.work.currentIndex() == 2:
                    creds_ids.append(item.model().itemData(item)['id'])

            self.controller.remove_creds(list(set(creds_ids)))

            if self.ui.ui.work.currentIndex() == 0:
                for row in reversed(sorted(rows_to_delete)):
                    self.app_tabs['Credentials'].removeRow(int(row))
            if self.ui.ui.work.currentIndex() == 2:
                self.reset_right_panel()

    def launch_custom_command_dialog(self):
        new_scan_dialog = Custom_Command()
        new_scan_dialog.ui.detached.stateChanged.connect(lambda checked: new_scan_dialog.in_terminal.setEnabled(checked))

        if new_scan_dialog.exec():
            command = new_scan_dialog.ui.command.text()

            if command:
                program = {
                    'name': command.split()[0],
                    'binary': command.split()[0],
                    'args': command.split()[1:],
                    'detached': new_scan_dialog.ui.detached.checkState() == Qt.CheckState.Checked,
                    'in_terminal': new_scan_dialog.ui.in_terminal.checkState() == Qt.CheckState.Checked
                }
                self.controller.new_job(program)

    def create_actions_from_port(self, proto: str, port: str):
        port_associations = Config.get()['ports_associations']
        user_binaries = Config.get()['user_binaries']
        visible_actions = []  # Storing proposed actions here, rather than parsing AGAIN the conf ...

        top_menu = QMenu(None)

        for current_port in ["any", port]:
            if current_port != "any" and port not in port_associations[proto].keys():
                top_menu.addAction(f"No specific program declared for port {current_port}/{proto}").setEnabled(False)
            else:
                if current_port == "any" and "any" not in Config.get()['ports_associations'][proto]:
                    continue
                for program in port_associations[proto][current_port]:
                    _ = top_menu.addAction(user_binaries[program]['text'])
                    if 'icon' in user_binaries[program].keys():
                        _.setIcon(QIcon(user_binaries[program]['icon']))
                    elif 'in_terminal' in user_binaries[program].keys() and user_binaries[program]['in_terminal']:
                        _.setIcon(QIcon.fromTheme("utilities-terminal"))

                    visible_actions.append(user_binaries[program])

            top_menu.addSeparator()

        return top_menu, visible_actions

    def open_db(self):
        filename, _ = QFileDialog.getOpenFileName(caption='Restore saved session', dir='.', filter='*.sqlite')
        if filename:
            self.controller.open_db(filename)

    def save_db(self):
        if not Database.current_savefile:
            self.save_as_db()
        else:
            self.controller.save_db()

    def save_as_db(self):
        filename, _ = QFileDialog.getSaveFileName(caption='Save current session', dir='.', filter='*.sqlite')
        if filename:
            if not filename.endswith('.sqlite'):
                filename += '.sqlite'
            self.controller.save_db(filename)

    def dialog_import_xml(self):
        filename, _ = QFileDialog.getOpenFileName(caption='Import nmap XML output', dir='.', filter='*.xml')
        if filename:
            self.controller.log('INFO', f"Importing Nmap results from {filename}")
            new_hosts = self.controller.parse_nmap_data('xml', filename)

            if Config.get()['user_prefs']['enable_autorun_on_xml_import']:
                self.controller.autorun(new_hosts)

    def dialog_scan_host(self):
        new_scan_dialog = New_Scan()
        if new_scan_dialog.exec():
            target = new_scan_dialog.ui.target.text()
            nmap_speed = new_scan_dialog.ui.nmap_speed.text()
            ports = new_scan_dialog.ui.ports.text()
            if target:
                self.controller.new_scan(target, nmap_speed, ports,
                                         new_scan_dialog.ui.skip_host_discovery.checkState(),
                                         new_scan_dialog.ui.check_versions.checkState(),
                                         new_scan_dialog.ui.launch_scripts.checkState(),
                                         new_scan_dialog.ui.os_detection.checkState(),
                                         new_scan_dialog.ui.tcp_and_udp.checkState())

    def update_notes(self):
        current_notes = self.app_tabs['Notes'].toHtml()
        self.controller.update_notes_for_current_host(current_notes)

    def credentials_changed(self, item: QComboBox | QTableWidgetItem):
        columns_description = {0: "id", 1: "type", 2: "domain", 3: "username", 4: "password"}
        if type(item) == QComboBox: # Type of cred has changed
            self.controller.update_credentials(item.property('cred_id'), columns_description[1], item.currentText())
        elif type(item) == QTableWidgetItem: # Any cell other than the credential type has changed
            cred_id = self.app_tabs['Credentials'].item(item.row(), 0).text()
            self.controller.update_credentials(cred_id, columns_description[item.column()], item.text())

    def new_tab(self, title: str, job_id: int, content: str = "") -> QTextEdit:
        tab = QTextEdit(content)
        tab.setReadOnly(True)
        tab_index = self.ui.ui.application_TabWidget.addTab(tab, title)
        self.app_tabs[tab_index] = {'widget': tab, 'job_id': job_id}
        self.link_job_and_widget(job_id, tab)
        return tab

    def link_job_and_widget(self, job_id: int, widget: QTextEdit):
        if self.controller.get_job(job_id):
            job = self.controller.get_job(job_id)
            job.readyReadStandardOutput.connect(lambda: widget.setText(job.get_output_text().replace(chr(0), "")))
            job.readyReadStandardError.connect(lambda: widget.setText(job.get_output_text().replace(chr(0), "")))

    def close_right_panel_tabs(self):
        for i in range(self.ui.ui.application_TabWidget.count()):
            self.ui.ui.application_TabWidget.removeTab(0)
        self.app_tabs = {}

    def reset_right_panel(self):
        self.close_right_panel_tabs()

        for widget in self.default_tabs:
            if issubclass(widget['object'], QWidget):
                self.app_tabs[widget['name']] = widget['object']()
            else:
                self.app_tabs[widget['name']] = QWidget()
            self.ui.ui.application_TabWidget.addTab(self.app_tabs[widget['name']], widget['name'])

        # Setup ports view
        self.app_tabs['Ports'].setColumnCount(4)
        self.app_tabs['Ports'].setHorizontalHeaderLabels(['Protocol', 'Status','Port','Description'])
        self.app_tabs['Ports'].verticalHeader().setVisible(False)
        self.app_tabs['Ports'].horizontalHeader().setStretchLastSection(True)
        self.app_tabs['Ports'].setGridStyle(Qt.DotLine)
        self.app_tabs['Ports'].setSelectionBehavior(QAbstractItemView.SelectRows)
        self.app_tabs['Ports'].setSelectionMode(QAbstractItemView.SingleSelection)
        self.app_tabs['Ports'].setContextMenuPolicy(Qt.CustomContextMenu)
        self.app_tabs['Ports'].setEditTriggers(QAbstractItemView.NoEditTriggers)

        # Setup nmap tab
        self.app_tabs['Nmap'].setReadOnly(True)

        # Setup creds tab
        self.app_tabs['Credentials'].setColumnCount(5)
        self.app_tabs['Credentials'].setColumnHidden(0, True)
        self.app_tabs['Credentials'].setHorizontalHeaderItem(0, QTableWidgetItem('id'))
        self.app_tabs['Credentials'].setHorizontalHeaderItem(1, QTableWidgetItem('type'))
        self.app_tabs['Credentials'].setHorizontalHeaderItem(2, QTableWidgetItem('domain'))
        self.app_tabs['Credentials'].setHorizontalHeaderItem(3, QTableWidgetItem('username'))
        self.app_tabs['Credentials'].setHorizontalHeaderItem(4, QTableWidgetItem('password'))
        self.app_tabs['Credentials'].horizontalHeader().setStretchLastSection(True)
        self.app_tabs['Credentials'].itemChanged.connect(self.credentials_changed)
        self.app_tabs['Credentials'].setContextMenuPolicy(Qt.CustomContextMenu)
        self.app_tabs['Credentials'].customContextMenuRequested.connect(self.right_click_in_creds_table)

        # Setup notes signal / slot
        self.app_tabs['Notes'].textChanged.connect(self.update_notes)

        # Delete close button on default tabs
        self.ui.ui.application_TabWidget.tabBar().tabButton(0, QTabBar.RightSide).resize(0, 0)
        self.ui.ui.application_TabWidget.tabBar().tabButton(1, QTabBar.RightSide).resize(0, 0)
        self.ui.ui.application_TabWidget.tabBar().tabButton(2, QTabBar.RightSide).resize(0, 0)
        self.ui.ui.application_TabWidget.tabBar().tabButton(3, QTabBar.RightSide).resize(0, 0)

    def update_right_panel(self, host: QModelIndex):
        host_details = self.controller.get_selected_host(host)
        if not host_details:
            return

        current_index = self.ui.ui.application_TabWidget.currentIndex()

        self.reset_right_panel()

        ## Filling default tabs
        # Port listing
        self.app_tabs['Ports'].setRowCount(len(host_details['ports'])) # Number of row to add (counting number of ports in both tcp and udp)

        row_count = 0
        for port in host_details['ports']:
            self.app_tabs['Ports'].setItem(row_count, 0, QTableWidgetItem(port['proto']))
            self.app_tabs['Ports'].setItem(row_count, 1, QTableWidgetItem(port['status']))
            self.app_tabs['Ports'].setItem(row_count, 2, QTableWidgetItem(port['port']))
            self.app_tabs['Ports'].setItem(row_count, 3, QTableWidgetItem(port['description']))

            row_count += 1

        # Connect the right click on a row
        self.app_tabs['Ports'].customContextMenuRequested.connect(self.right_click_in_port_table)

        # Nmap output
        if 'nmap' in host_details.keys():
            self.app_tabs['Nmap'].setText(host_details['nmap'])

        # Credentials
        self.app_tabs['Credentials'].blockSignals(True)
        self.app_tabs['Credentials'].setRowCount(len(host_details['credentials']))
        for row in range(len(host_details['credentials'])):
            combo = QComboBox()
            combo.addItems(["password", "hash", "ssh_key", "other"])
            self.app_tabs['Credentials'].setCellWidget(row, 1, combo)

        for i, cred in enumerate(host_details['credentials']):
            self.app_tabs['Credentials'].setItem(i, 0, QTableWidgetItem(str(cred['id'])))
            self.app_tabs['Credentials'].cellWidget(i, 1).setCurrentText(str(cred['type']))
            self.app_tabs['Credentials'].cellWidget(i, 1).setProperty('cred_id', str(cred['id']))
            self.app_tabs['Credentials'].cellWidget(i, 1).currentTextChanged.connect(lambda new_type, qcombobox=self.app_tabs['Credentials'].cellWidget(i, 1): self.credentials_changed(qcombobox)) # signal in loop: https://stackoverflow.com/questions/46300229/connecting-multiples-signal-slot-in-a-for-loop-in-pyqt
            self.app_tabs['Credentials'].setItem(i, 2, QTableWidgetItem(str(cred['domain'])))
            self.app_tabs['Credentials'].setItem(i, 3, QTableWidgetItem(str(cred['username'])))
            self.app_tabs['Credentials'].setItem(i, 4, QTableWidgetItem(str(cred['password'])))

        self.app_tabs['Credentials'].blockSignals(False)

        # Notes
        if 'notes' in host_details.keys():
            self.app_tabs['Notes'].blockSignals(True) # Consumes less resources, and do not trigger the dirty marker on database
            try:
                self.app_tabs['Notes'].setHtml(host_details['notes'])
            except ValueError:
                self.app_tabs['Notes'].setHtml(host_details['notes'].replace(chr(0), ""))
            self.app_tabs['Notes'].blockSignals(False)

        ## Application tabs
        for app in host_details['external_tabs']:
            if app['text']:
                self.new_tab(app['title'], app['job_id'], app['text'].replace(chr(0), ""))
            else:
                self.new_tab(app['title'], app['job_id'], "")

        # Restoring tab focus if currentIndex < 4
        if current_index < 4:
            self.ui.ui.application_TabWidget.setCurrentIndex(current_index)

    def update_hosts_for_port_panel(self, ports: dict):
        self.ui.ui.port_table.setRowCount(len(ports['udp']) + len(ports['tcp']))

        row_count = 0
        for proto in ['udp', 'tcp']:
            for port in ports[proto]:
                self.ui.ui.port_table.setItem(row_count, 0, QTableWidgetItem(proto))
                self.ui.ui.port_table.setItem(row_count, 1, QTableWidgetItem(str(port)))
                row_count += 1

    def change_filter_hosts_for_port(self):
        proto = port = ""
        if self.ui.ui.port_table.item(self.ui.ui.port_table.currentRow(),0):
            proto = self.ui.ui.port_table.item(self.ui.ui.port_table.currentRow(),0).text()
        if self.ui.ui.port_table.item(self.ui.ui.port_table.currentRow(),1):
            port = self.ui.ui.port_table.item(self.ui.ui.port_table.currentRow(),1).text()
        if proto and port:
            self.controller.filter_hosts_for_port_table(proto, port)

    def remove_tab(self, tab_index: int):
        # There is a job, It is running, User wants to confirm
        if Config.get()['user_prefs']['confirm_before_tab_removal']:
            question = "Tab content will be discarded. Are you sure ?"
            if self.controller.get_job(self.app_tabs[tab_index]['job_id']):
                job = self.controller.get_job(self.app_tabs[tab_index]['job_id'])
                if job.state() == 2:
                    question = "The currently running program will be killed, and tab content will be discarded. Are you sure ?"

            if QMessageBox.question(None, 'Confirmation', question) != QMessageBox.Yes:
                return

        self.controller.remove_tab(self.app_tabs[tab_index])

        # Removing and re-indexing internal view app_tabs
        del self.app_tabs[tab_index]
        if range(tab_index, len(self.app_tabs)):
            for i in range(tab_index, len(self.app_tabs)):
                self.app_tabs[i] = self.app_tabs[i+1]
            del self.app_tabs[i+1]

        self.ui.ui.application_TabWidget.removeTab(tab_index)

    def copy_selectedIndexes_to_clipboard(self, selectedIndexes: list):
        data = {}
        for item in selectedIndexes:
            if item.data() is None:
                continue
            if item.row() in data.keys():
                data[item.row()].append(item.data())
            else:
                data[item.row()] = [item.data()]

        clipboard_data = ""
        for key in data:
            clipboard_data += ';'.join(data[key]) + '\n'

        clipboard = QApplication.clipboard()
        clipboard.setText(clipboard_data.strip('\n'))
        self.ui.statusBar().showMessage('Data copied to clipboard !', 5000)

    def machine_list_copy_all_to_clipboard(self):
        self.ui.ui.hosts_for_port_table.selectAll()
        self.copy_selectedIndexes_to_clipboard(self.ui.ui.hosts_for_port_table.selectedIndexes())

    def creds_table_copy_all_to_clipboard(self):
        self.ui.ui.creds_table.selectAll()
        self.copy_selectedIndexes_to_clipboard(self.ui.ui.creds_table.selectedIndexes())


import os
import subprocess

from aws_cdk import (
    Duration,
    Stack,
    aws_lambda as _lambda,
    aws_dynamodb as dynamodb,
    aws_apigateway as api_gw
)
from constructs import Construct

class PyconIt2023Stack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        layer = self.create_dependencies_layer('pycon-it-2023')

        table = dynamodb.Table(
            self, 'PullRequests',
            partition_key=dynamodb.Attribute(
                name='timestamp',
                type=dynamodb.AttributeType.STRING
            )
        )

        integration_responses = [
            {
                'statusCode': '200'
            },
            {
                'statusCode': '500',
                'selectionPattern': '^Unexpected error$',
            }
        ]

        method_responses = [
            {
                'statusCode': '200',
                'responseModels': {
                    'application/json': api_gw.Model.EMPTY_MODEL,
                },
            },
            {
                'statusCode': '500',
                'responseModels': {
                    'application/json': api_gw.Model.EMPTY_MODEL,
                },
            }
        ]

        gateway = self.create_api_gateway('pycon-it-2023')

        resource = gateway.root.add_resource('pull-requests')

        functions = (
            'get',
            'post'
        )

        for function_name in functions:
            lambda_function = self.create_lambda_function(
                function_name,
                layer,
                table
            )

            resource.add_method(
                function_name.capitalize(),
                api_key_required=True,
                integration=api_gw.LambdaIntegration(
                    handler=lambda_function,
                    proxy=False,
                    integration_responses=integration_responses,
                ),
                method_responses=method_responses
            )

            if function_name == 'get':
                table.grant_read_data(lambda_function)
            elif function_name == 'post':
                table.grant_write_data(lambda_function)

    def create_api_gateway(
        self,
        gw_name: str,
    ) -> api_gw.RestApi:
        gateway = api_gw.RestApi(
            self, f'{gw_name}Api',
            rest_api_name=gw_name,
        )

        plan = gateway.add_usage_plan(
            f'{gw_name}UsagePlan',
            name=gw_name,
            throttle=api_gw.ThrottleSettings(
                rate_limit=100,
                burst_limit=200
            )
        )

        key = gateway.add_api_key(f'{gw_name}Key')
        plan.add_api_key(key)
        plan.add_api_stage(
            stage=gateway.deployment_stage
        )

        return gateway

    def create_lambda_function(
        self,
        function_name: str,
        lambda_layer: _lambda.LayerVersion,
        dynamo_table: dynamodb.Table
    ) -> _lambda.Function:
        lambda_function = _lambda.Function(
            self,
            f'{function_name}-pycon-it-lambda',
            runtime=_lambda.Runtime.PYTHON_3_9,
            code=_lambda.Code.from_asset('code'),
            handler=f'{function_name}.lambda_handler',
            timeout=Duration.seconds(30),
            memory_size=128,
            layers=[
                lambda_layer
            ],
            environment={
                'DYNAMO_TABLE': dynamo_table.table_name
            }
        )

        return lambda_function

    def create_dependencies_layer(self, function_name: str) -> _lambda.LayerVersion:
        requirements_file = 'code/requirements.txt'
        output_dir = '.lambda_dependencies/' + function_name
        # Install requirements for layer in the output_dir
        if not os.environ.get('SKIP_PIP'):
        # Note: Pip will create the output dir if it does not exist
            subprocess.check_call(
            f'pip install -r {requirements_file} -t {output_dir}/python'.split()
            )
        return _lambda.LayerVersion(
            self,
            function_name + '-dependencies',
            code=_lambda.Code.from_asset(output_dir)
        )


# -*- coding: utf-8 -*-
"""
Created on Mon Dec 30 14:37:43 2019
dimension reduction pca
@author: Beytu
"""

#1.import libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

#2. data preprocessing

#2. import data
veriler = pd.read_csv('Wine.csv')

X=veriler.iloc[:,0:13].values
Y=veriler.iloc[:,13].values

#split the data train and test
from sklearn.model_selection import train_test_split
x_train, x_test,y_train,y_test = train_test_split(X,Y,test_size=0.2, random_state=0)

#data normalization
from sklearn.preprocessing import StandardScaler

sc = StandardScaler()
X_train = sc.fit_transform(x_train)
X_test = sc.transform(x_test)

#PCA
from sklearn.decomposition import PCA
pca=PCA(n_components=2)
X_train2=pca.fit_transform(X_train)
X_test2=pca.transform(X_test)

#before pca
from sklearn.linear_model import LogisticRegression
classifier=LogisticRegression(random_state=0)
classifier.fit(X_train,y_train)

#after pca
classifier2=LogisticRegression(random_state=0)
classifier2.fit(X_train2,y_train)

#prediction
y_pred=classifier.predict(X_test)
y_pred2=classifier2.predict(X_test2)

from sklearn.metrics import confusion_matrix
cm=confusion_matrix(y_test,y_pred)
print('real/without pca')
print(cm)

cm1=confusion_matrix(y_test,y_pred2)
print('real/with pca')
print(cm1)

cm2=confusion_matrix(y_pred,y_pred2)
print('without pca and with pca')
print(cm2)

#lda linear discriminat analysis
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
lda=LDA(n_components=2)
X_train_lda=lda.fit_transform(X_train,y_train)
X_test_lda=lda.transform(X_test)

#after lda
classifier_lda=LogisticRegression(random_state=0)
classifier_lda.fit(X_train_lda,y_train)

#predict
y_pred_lda=classifier_lda.predict(X_test_lda)

cm3=confusion_matrix(y_test,y_pred_lda)
print('lda/real')
print(cm3)


"""
165 比较版本号
比较两个版本号 version1 和 version2。
如果 version1 > version2 返回 1，如果 version1 < version2 返回 -1， 除此之外返回 0。

你可以假设版本字符串非空，并且只包含数字和 . 字符。

 . 字符不代表小数点，而是用于分隔数字序列。

例如，2.5 不是“两个半”，也不是“差一半到三”，而是第二版中的第五个小版本。

你可以假设版本号的每一级的默认修订版号为 0。例如，版本号 3.4 的第一级（大版本）和第二级（小版本）修订号分别为 3 和 4。其第三级和第四级修订号均为 0。

示例 1:

输入: version1 = "0.1", version2 = "1.1"
输出: -1

来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/compare-version-numbers
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
"""

class Solution:
    def compareVersion(self, version1: str, version2: str) -> int:
        """
        split，转数字，0补齐，
        """
        version1 = version1.split('.')
        version2 = version2.split('.')
        version1 = [int(x) for x in version1]
        version2 = [int(x) for x in version2]
        lens1, lens2 = len(version1), len(version2)
        if lens1 > lens2:
            version2.extend([0 for _ in range(lens1 - lens2)])
        else:
            version1 = version1 + [0 for _ in range(lens2 - lens1)]
        for i in range(max(lens1, lens2)):
            if version1[i] > version2[i]:
                return 1
            if version1[i] < version2[i]:
                return -1
        return 0


"""https://sagebionetworks.jira.com/browse/WORKFLOWS-390
This workflow was created to run the `nf-core/rnaseq` pipeline for GRCh38 RNASeq data.
"""
import asyncio
import boto3

from synapseclient import Synapse
from dataclasses import dataclass
from pathlib import Path

from orca.services.nextflowtower import NextflowTowerOps
from orca.services.nextflowtower.models import LaunchInfo

session = boto3.Session(profile_name="nextflow-prod")
s3 = session.client("s3")

async def main():
    ops = NextflowTowerOps()
    datasets = generate_datasets()
    runs = [run_workflows(ops, dataset) for dataset in datasets]
    statuses = await asyncio.gather(*runs)
    print(statuses)

@dataclass
class Dataset:
    id: str
    """The synapse id for the samplesheet."""

    samplesheet: str
    """The name of the samplesheet to run."""

    synapse_id_for_output: str
    """The synapse id for the output folder, this is where the output will be uploaded to."""

    run_number: int
    """The number for the run, this is used to generate the run_name for the workflow."""

    output_number: int
    """The number for the output, in some cases this is the same as the run_number,
    but in other cases it isn't because the pipeline has been re-ran."""

    bucket_name: str
    """The name of the bucket to stage the samplesheet in."""

    staging_key: str
    """The key in the S3 bucket where this workflow is going to run."""

    @property
    def samplesheet_location(self) -> str:
        """The location where the unstaged samplesheet is located."""
        return f"{self.samplesheet_location_prefix}{self.samplesheet}"

    @property
    def samplesheet_to_stage_key(self) -> str:
        """The key in the S3 bucket where the samplesheet is going to be staged."""
        return f"{self.staging_key}to_stage/{self.samplesheet}"

    @property
    def staged_samplesheet_location(self) -> str:
        """The S3 uri where the samplesheet is staged."""
        return f"{self.staging_location}synstage_{self.id}/{self.samplesheet}"

    @property
    def staging_location(self) -> str:
        """The S3 uri where the workflow is going to be run."""
        return f"s3://{self.bucket_name}/{self.staging_key}"

    @property
    def samplesheet_location_prefix(self) -> str:
        """The S3 uri where the unstaged samplesheet is located."""
        return f"s3://{self.bucket_name}/{self.staging_key}to_stage/"

    @property
    def output_directory(self) -> str:
        """The S3 uri where the output is going to be uploaded to. The is used as the
        input for the synindex workflow."""
        return f"s3://{self.bucket_name}/outputs/rnaseq_GRCh38_{self.id}_{self.output_number}/"

    @property
    def synstage_run_name(self) -> str:
        """The name of the synstage run."""
        return f"synstage_{self.id}"

    @property
    def rnaseq_run_name(self) -> str:
        """The name of the rnaseq run."""
        return f"rnaseq_GRCh38_{self.id}_{self.run_number}"

    @property
    def synindex_run_name(self) -> str:
        """The name of the synindex run."""
        return f"synindex_{self.id}_{self.run_number}"

def generate_datasets() -> list[Dataset]:
    """Generate list of datasets.

    Source: https://sagebionetworks.jira.com/browse/WORKFLOWS-538

    This is expecting that the SampleSheet to be run exists in the `prefix` location.
    """
    return [
        Dataset(
            id="syn52120378",
            samplesheet="syn51199007_RNA_Seq_SampleSheet.csv",
            staging_key="samplesheets/Reprocess/WORKFLOWS-390/",
            bucket_name="ntap-add5-project-tower-bucket",
            synapse_id_for_output="syn53072020",
            run_number=2,
            output_number=1,
        )
    ]

def stage_samplesheet(syn: Synapse, dataset: Dataset):
    """Download the samplesheet from synapse and upload it to S3 in the location where synstage
    is going to grab the file.

    :param syn: The logged in synapse instance.
    :param dataset: The dataset to stage the samplesheet for.
    """
    samplesheet_file = syn.get(dataset.id)
    samplesheet_file_path = samplesheet_file.path

    s3.upload_file(
        samplesheet_file_path, dataset.bucket_name, dataset.samplesheet_to_stage_key
    )

def prepare_synstage_info(dataset: Dataset) -> LaunchInfo:
    """Generate LaunchInfo for nf-synstage."""
    return LaunchInfo(
        run_name=dataset.synstage_run_name,
        pipeline="Sage-Bionetworks-Workflows/nf-synstage",
        revision="disable_wave",
        profiles=["sage"],
        params={
            "input": dataset.samplesheet_location,
            "outdir": dataset.staging_location,
        },
        pre_run_script="NXF_VER=22.10.4",
        workspace_secrets=["SYNAPSE_AUTH_TOKEN"],
        nextflow_config="wave.enabled=false",
    )

def prepare_rnaseq_launch_info(dataset: Dataset) -> LaunchInfo:
    """Generate LaunchInfo for nf-core/rnaseq workflow run."""
    return LaunchInfo(
        run_name=dataset.rnaseq_run_name,
        pipeline="nf-core/rnaseq",
        revision="3.11.2",
        profiles=["sage"],
        params={
            "input": dataset.staged_samplesheet_location,
            "outdir": dataset.output_directory,
            "gencode": True,
            "fasta": "s3://sage-igenomes/igenomes/Homo_sapiens/NCBI/GRCh38/Sequence/WholeGenomeFasta/genome.fa",
            "gtf": "s3://sage-igenomes/igenomes/Homo_sapiens/NCBI/GRCh38/Annotation/Genes/genes.gtf",
            "gene_bed": "s3://sage-igenomes/igenomes/Homo_sapiens/NCBI/GRCh38/Annotation/Genes/genes.bed",
            "star_index": "s3://sage-igenomes/igenomes/Homo_sapiens/NCBI/GRCh38/Sequence/STARIndex/",
            # TODO: These could not be found in `s3://sage-igenomes/igenomes/Homo_sapiens/`
            # "transcript_fasta": "",
            # "salmon_index": "",
            # "rsem_index": "",
        },
        pre_run_script="NXF_VER=23.10.0",
        nextflow_config=Path("local/ntap/nextflow-rnaseq.config").read_text(),
    )

def prepare_synindex_launch_info(dataset: Dataset) -> LaunchInfo:
    """Generate LaunchInfo for nf-synindex workflow run."""
    return LaunchInfo(
        run_name=dataset.synindex_run_name,
        pipeline="Sage-Bionetworks-Workflows/nf-synindex",
        revision="disable_wave",
        profiles=["sage"],
        params={
            "s3_prefix": dataset.output_directory,
            "parent_id": dataset.synapse_id_for_output,
        },
        pre_run_script="NXF_VER=22.10.4",
        workspace_secrets=["SYNAPSE_AUTH_TOKEN"],
        nextflow_config="wave.enabled=false",
    )

async def run_workflows(ops: NextflowTowerOps, dataset: Dataset):
    syn = Synapse()
    syn.login()

    stage_samplesheet(syn, dataset)

    synstage_info = prepare_synstage_info(dataset)
    synstage_run_id = ops.launch_workflow(synstage_info, "spot")
    status = await ops.monitor_workflow(run_id=synstage_run_id, wait_time=60 * 2)
    print(status)

    scrnaseq_info = prepare_rnaseq_launch_info(dataset)
    scrnaseq_run_id = ops.launch_workflow(scrnaseq_info, "spot")
    status = await ops.monitor_workflow(run_id=scrnaseq_run_id, wait_time=60 * 2)
    print(status)

    synindex_info = prepare_synindex_launch_info(dataset)
    synindex_run_id = ops.launch_workflow(synindex_info, "spot")
    status = await ops.monitor_workflow(run_id=synindex_run_id, wait_time=60 * 2)
    print(status)

if __name__ == "__main__":
    asyncio.run(main())


def unet(sz = (256, 256, 3)):
  x = Input(sz)
  inputs = x

  #down sampling
  f = 8
  layers = []

  for i in range(0, 6):
    x = Conv2D(f, 3, activation='relu', padding='same') (x)
    x = Conv2D(f, 3, activation='relu', padding='same') (x)
    layers.append(x)
    x = MaxPooling2D() (x)
    f = f*2
  ff2 = 64

  #bottleneck
  j = len(layers) - 1
  x = Conv2D(f, 3, activation='relu', padding='same') (x)
  x = Conv2D(f, 3, activation='relu', padding='same') (x)
  x = Conv2DTranspose(ff2, 2, strides=(2, 2), padding='same') (x)
  x = Concatenate(axis=3)([x, layers[j]])
  j = j -1

  #upsampling
  for i in range(0, 5):
    ff2 = ff2//2
    f = f // 2
    x = Conv2D(f, 3, activation='relu', padding='same') (x)
    x = Conv2D(f, 3, activation='relu', padding='same') (x)
    x = Conv2DTranspose(ff2, 2, strides=(2, 2), padding='same') (x)
    x = Concatenate(axis=3)([x, layers[j]])
    j = j -1

  #classification
  x = Conv2D(f, 3, activation='relu', padding='same') (x)
  x = Conv2D(f, 3, activation='relu', padding='same') (x)
  outputs = Conv2D(1, 1, activation='sigmoid') (x)

  #model creation
  model = Model(inputs=[inputs], outputs=[outputs])
  model.compile(optimizer = 'rmsprop', loss = 'binary_crossentropy', metrics = [mean_iou])

  return model

def build_callbacks():
  checkpointer = ModelCheckpoint(filepath='unet.h5', verbose=0, save_best_only=True, save_weights_only=True)
  callbacks = [checkpointer, PlotLearning()]
  return callbacks

# inheritance for training process plot
class PlotLearning(keras.callbacks.Callback):

  def on_train_begin(self, logs={}):
    self.i = 0
    self.x = []
    self.losses = []
    self.val_losses = []
    self.acc = []
    self.val_acc = []
    #self.fig = plt.figure()
    self.logs = []
  def on_epoch_end(self, epoch, logs={}):
    self.logs.append(logs)
    self.x.append(self.i)
    self.losses.append(logs.get('loss'))
    self.val_losses.append(logs.get('val_loss'))
    self.acc.append(logs.get('mean_iou'))
    self.val_acc.append(logs.get('val_mean_iou'))
    self.i += 1
    print('i=',self.i,'loss=',logs.get('loss'),'val_loss=',logs.get('val_loss'),'mean_iou=',logs.get('mean_iou'),'val_mean_iou=',logs.get('val_mean_iou'))

    #choose a random test image and preprocess
    path = np.random.choice(test_files)
    raw = Image.open(f'images/{path}')
    raw = np.array(raw.resize((256, 256)))/255.
    raw = raw[:,:,0:3]

    #predict the mask
    pred = model.predict(np.expand_dims(raw, 0))

    #mask post-processing
    msk  = pred.squeeze()
    msk = np.stack((msk,)*3, axis=-1)
    msk[msk >= 0.5] = 1
    msk[msk < 0.5] = 0

    #show the mask and the segmented image
    combined = np.concatenate([raw, msk, raw* msk], axis = 1)
    plt.axis('off')
    plt.imshow(combined)
    plt.show()


# -*- coding: utf-8 -*-
# @Time : 2020/5/3 11:24
# @Author : lzf
# @File : re_test.py
# @Software: PyCharm
# @Description:

#
# !/usr/bin/python3
import re
def get_num(content):
    phone = content #我是测试你好啊2004-959-559abc # 这是一个电话号码
    # 删除注释
    # num = re.sub(r'#.*$', "", phone)
    # print("电话号码 : ", num)

    # 移除非数字的内容
    num = re.sub(r'\D', "", phone)
    print("数字 : ", num)
#
# get_num('我是李佳琪999')
# get_num('我是测试你好啊2004-959-559abc # 这是一个电话号码')
#
# phone = '我是李佳琪999'  # 我是测试你好啊2004-959-559abc # 这是一个电话号码
# num = re.sub(r'\D', "", phone)
# print("数字 : ", num)
#
# phone = '我是测试你好啊2004-959-559abc # 这是一个电话号码'  # 我是测试你好啊2004-959-559abc # 这是一个电话号码
# num = re.sub(r'\D', "", phone)
# print("数字 : ", num)
#

# for i in range(1,6):
#     print(i)

html = 'class=fd '
pattern = re.compile(
    r'.*?class=.*?',
    re.S)
image_url = re.findall(r'.*?class=.*?', html)
print(image_url)


import snowflake.snowpark.functions as F
from snowflake.snowpark.functions import lit
import datetime

def model(dbt, session):

    df = dbt.ref("demand_forecast_training_base")
    orders_df = dbt.ref("fct_orders")

    # Get earliest date
    min_date = df.select(F.dateadd("year", F.lit(1), F.min("date"))).collect()[0][0]

    # Filter out the first year in the data
    df = df.filter(F.col("date") >= min_date)

    # Join in third party data
    df_locations = orders_df.select("location_id", "latitude", "longitude").distinct()
    df = df.join(df_locations, "location_id")

    df_holidays = orders_df.select("date", "primary_city", "holiday_flag").distinct()
    df = df.join(df_holidays, ["date", "primary_city"])

    # Encode
    df = df.with_column("shift", F.iff(F.col("shift") == "AM", 1, 0))

    # Split into training and testing
    max_date = df.select(F.max("date")).collect()[0][0]
    df = df.with_column("split_type",
                        F.iff(F.col("date") <= (max_date - datetime.timedelta(days=30)),
                                F.lit("train"),
                                F.lit("test")))

    # Drop ID columns
    df = df.drop("date",
            "primary_city",
            "location_id",
            "menu_type",
            "menu_item_id",)

    return df


import math
import numpy as np

def Seidel(A, b, eps):
    steps = 0
    n = len(A)
    x = [0.0 for i in range(n)]

    conv = False
    while not conv:
        newX = np.copy(x)
        for i in range(n):
            s1 = sum(A[i][j] * newX[j] for j in range(i))
            s2 = sum(A[i][j] * x[j] for j in range(i+1, n))
            newX[i] = (b[i]-s1-s2) / A[i][i]

        conv = math.sqrt(sum((newX[i] - x[i]) ** 2 for i in range(n))) <= eps
        x = newX

        steps += 1

    return x, steps

def Jacobi(A, b, eps):
    steps = 0
    n = len(A)
    tempX = [0.0 for i in range(n)]
    x = [0.0 for i in range(n)]
    norm = eps + 1

    while norm > eps:
        for i in range(n):
            tempX[i] = b[i]
            for j in range(n):
                if i != j:
                    tempX[i] -= A[i][j] * x[j]
            tempX[i] /= A[i][i]

        norm = math.fabs(x[0] - tempX[0])

        for j in range(n):
            if math.fabs(x[j]) - tempX[j] > norm:
                norm = math.fabs(x[j] - tempX[j])
            x[j] = tempX[j]

        steps += 1

    return x, steps