import sys, os, subprocess, time, traceback, shutil
from functools import wraps

pandoraRoot = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
sys.path.append(os.path.join(os.path.dirname(__file__), "UserInterfacesPandora"))

scriptPath = os.path.abspath(os.path.dirname(__file__))
if scriptPath not in sys.path:
    sys.path.append(scriptPath)

if sys.version[0] == "3":
    pyLibs = "Python37"
    pVersion = 3
else:
    pyLibs = "Python27"
    pVersion = 2

try:
    from PySide2.QtCore import *
    from PySide2.QtGui import *
    from PySide2.QtWidgets import *

    psVersion = 2
except:
    try:
        if "standalone" in sys.argv:
            raise

        from PySide.QtCore import *
        from PySide.QtGui import *

        psVersion = 1
    except:
        sys.path.insert(0, os.path.join(pandoraRoot, "PythonLibs", pyLibs, "PySide"))
        try:
            from PySide2.QtCore import *
            from PySide2.QtGui import *
            from PySide2.QtWidgets import *

            psVersion = 2
        except:
            from PySide.QtCore import *
            from PySide.QtGui import *

            psVersion = 1

for i in ["PandoraSettings_ui"]:
    try:
        del sys.modules[i]
    except:
        pass

sys.path.append(os.path.join(os.path.dirname(__file__), "UserInterfacesPandora"))

if psVersion == 1:
    import PandoraSettings_ui
else:
    import PandoraSettings_ui_ps2 as PandoraSettings_ui

from UserInterfacesPandora import qdarkstyle

class PandoraSettings(QDialog, PandoraSettings_ui.Ui_dlg_PandoraSettings):
    def __init__(self, core):
        QDialog.__init__(self)
        self.setupUi(self)
        self.core = core
        self.core.parentWindow(self)

        self.groupboxes = [self.gb_slave, self.gb_coordinator]

        self.loadUI()
        self.loadSettings()

        self.startSettings = {
            "checkForUpdates": self.chb_checkForUpdates.isChecked(),
            "localMode": self.chb_localMode.isChecked(),
            "lRootPath": self.e_rootPath.text(),
            "cRootpath": self.e_coordinatorRoot.text(),
            "slaveEnabled": self.gb_slave.isChecked(),
            "coordEnabled": self.gb_coordinator.isChecked(),
        }

        self.refreshSlaves()
        self.refreshWorkstations()

        self.connectEvents()

        screenH = QApplication.desktop().screenGeometry().height()
        space = 100
        if screenH < (self.height() + space):
            self.resize(self.width(), screenH - space)

        self.core.callback(
            name="onPandoraSettingsOpen", types=["curApp", "custom"], args=[self]
        )

    def err_decorator(func):
        @wraps(func)
        def func_wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                exc_type, exc_obj, exc_tb = sys.exc_info()
                erStr = "%s ERROR - PandoraSettings %s:\n%s\n\n%s" % (
                    time.strftime("%d/%m/%y %X"),
                    args[0].core.version,
                    "".join(traceback.format_stack()),
                    traceback.format_exc(),
                )
                args[0].core.writeErrorLog(erStr)

        return func_wrapper

    @err_decorator
    def connectEvents(self):
        self.e_username.textChanged.connect(
            lambda x: self.validate(self.e_username, x, allowChars=[" "])
        )
        self.b_browseRoot.clicked.connect(lambda: self.browse("proot"))
        self.b_browseRoot.customContextMenuRequested.connect(
            lambda: self.core.openFolder(self.e_rootPath.text())
        )
        self.b_browseRepository.clicked.connect(lambda: self.browse("prepository"))
        self.b_browseRepository.customContextMenuRequested.connect(
            lambda: self.core.openFolder(self.e_repositoryPath.text())
        )

        self.b_browseSubmission.clicked.connect(lambda: self.browse("psubmission"))
        self.b_browseSubmission.customContextMenuRequested.connect(
            lambda: self.core.openFolder(self.e_submissionPath.text())
        )
        self.b_browseSlave.clicked.connect(lambda: self.browse("pslave"))
        self.b_browseSlave.customContextMenuRequested.connect(
            lambda: self.core.openFolder(self.e_slavePath.text())
        )
        self.b_browseCoordinatorRoot.clicked.connect(lambda: self.browse("pcoord"))
        self.b_browseCoordinatorRoot.customContextMenuRequested.connect(
            lambda: self.core.openFolder(self.e_coordinatorRoot.text())
        )

        self.chb_localMode.stateChanged.connect(self.lmodeChanged)
        self.b_updatePandora.clicked.connect(self.showUpdate)

        for i in self.exOverridePlugins:
            self.exOverridePlugins[i]["chb"].stateChanged.connect(
                lambda x, y=i: self.orToggled(y, x)
            )
            self.exOverridePlugins[i]["b"].clicked.connect(
                lambda y=None, x=(i + "OR"): self.browse(x, getFile=True)
            )
            self.exOverridePlugins[i]["b"].customContextMenuRequested.connect(
                lambda x, y=i: self.core.openFolder(self.exOverridePlugins[y]["le"].text())
            )
        for i in self.integrationPlugins:
            self.integrationPlugins[i]["badd"].clicked.connect(
                lambda y=None, x=i: self.integrationAdd(x)
            )
            self.integrationPlugins[i]["bremove"].clicked.connect(
                lambda y=None, x=i: self.integrationRemove(x)
            )

        self.b_addSlave.clicked.connect(lambda: self.addSlave(itemType="slave"))
        self.b_deleteSlave.clicked.connect(lambda: self.deleteSlave(itemType="slave"))
        self.b_refreshSlaves.clicked.connect(self.refreshSlaves)
        self.b_addWS.clicked.connect(lambda: self.addSlave(itemType="workstation"))
        self.b_deleteWS.clicked.connect(lambda: self.deleteSlave(itemType="workstation"))
        self.b_refreshWS.clicked.connect(self.refreshWorkstations)
        self.lw_slaves.itemDoubleClicked.connect(lambda: self.openSlave(itemType="slave"))
        self.lw_slaves.customContextMenuRequested.connect(
            lambda: self.rclicked(ltype="slave")
        )
        self.lw_workstations.itemDoubleClicked.connect(
            lambda: self.openSlave(itemType="workstation")
        )
        self.lw_workstations.customContextMenuRequested.connect(
            lambda: self.rclicked(ltype="workstation")
        )
        self.buttonBox.accepted.connect(self.saveSettings)

    @err_decorator
    def browse(self, bType, getFile=False):
        if bType == "proot":
            windowTitle = "Select Pandora root path"
            uiEdit = self.e_rootPath
        elif bType == "prepository":
            windowTitle = "Select local Pandora repository path"
            uiEdit = self.e_repositoryPath
        elif bType == "psubmission":
            windowTitle = "Select Pandora submission path"
            uiEdit = self.e_submissionPath
        elif bType == "pslave":
            windowTitle = "Select PandoraSlave Root"
            uiEdit = self.e_slavePath
        elif bType.endswith("OR"):
            pName = bType[:-2]
            executableName = self.core.getPluginData(pName, "executableName")
            windowTitle = "Select %s" % executableName
            uiEdit = self.exOverridePlugins[pName]["le"]
        elif bType == "pcoord":
            windowTitle = "Select Pandora Coordinator Root"
            uiEdit = self.e_coordinatorRoot
        else:
            return

        if getFile:
            selectedPath = QFileDialog.getOpenFileName(
                self, windowTitle, uiEdit.text(), "Executable (*.exe)"
            )[0]
        else:
            selectedPath = QFileDialog.getExistingDirectory(
                self, windowTitle, uiEdit.text()
            )

        if selectedPath != "":
            uiEdit.setText(selectedPath.replace("/", "\\"))

    @err_decorator
    def integrationAdd(self, prog):
        if prog == self.core.appPlugin.pluginName:
            result = self.core.appPlugin.integrationAdd(self)
        else:
            for i in self.core.unloadedAppPlugins:
                if i.pluginName == prog:
                    result = i.integrationAdd(self)

        if result:
            self.core.integrationAdded(prog, result)
            self.refreshIntegrations()
            self.tw_settings.insertTab(1, self.tab_submissions, "Submissions")

    @err_decorator
    def integrationRemove(self, prog):
        items = self.integrationPlugins[prog]["lw"].selectedItems()
        if len(items) == 0:
            return

        installPath = items[0].text()

        if prog == self.core.appPlugin.pluginName:
            result = self.core.appPlugin.integrationRemove(self, installPath)
        else:
            for i in self.core.unloadedAppPlugins:
                if i.pluginName == prog:
                    result = i.integrationRemove(self, installPath)

        if result:
            self.core.integrationRemoved(prog, installPath)
            self.refreshIntegrations()
            if not self.core.getSubmissionEnabled():
                self.tw_settings.removeTab(self.tw_settings.indexOf(self.tab_submissions))

    @err_decorator
    def lmodeChanged(self, state):
        self.w_rootPath.setVisible(state)
        self.w_sumitterPath.setVisible(not state)
        self.w_slaveRoot.setVisible(not state)
        self.w_coordinatorRoot.setVisible(not state)
        self.w_farmcomputer.setVisible(not state)

    @err_decorator
    def orToggled(self, prog, state):
        self.exOverridePlugins[prog]["le"].setEnabled(state)
        self.exOverridePlugins[prog]["b"].setEnabled(state)

    @err_decorator
    def rclicked(self, ltype):
        if ltype in ["slave", "workstation"]:
            rcmenu = QMenu()

            exAct = QAction("Open in explorer", self)
            exAct.triggered.connect(lambda: self.openSlave(itemType=ltype, mode="open"))
            rcmenu.addAction(exAct)

            exAct = QAction("Copy path", self)
            exAct.triggered.connect(lambda: self.openSlave(itemType=ltype, mode="copy"))
            rcmenu.addAction(exAct)

            getattr(self.core.appPlugin, "setRCStyle", lambda x, y: None)(self, rcmenu)
            rcmenu.exec_(QCursor.pos())

    @err_decorator
    def addSlave(self, itemType):
        if itemType == "slave":
            basePath = os.path.join(self.e_coordinatorRoot.text(), "Slaves")
            prefix = "S_"
            refresh = self.refreshSlaves
        elif itemType == "workstation":
            basePath = os.path.join(self.e_coordinatorRoot.text(), "Workstations")
            prefix = "WS_"
            refresh = self.refreshWorkstations
        else:
            return

        itemDlg = QDialog()

        itemDlg.setWindowTitle("Create %s" % (itemType))
        l_item = QLabel("%s%s name:" % (itemType[0].upper(), itemType[1:]))
        e_item = QLineEdit()

        w_item = QWidget()
        layItem = QHBoxLayout()
        layItem.addWidget(l_item)
        layItem.addWidget(e_item)
        w_item.setLayout(layItem)

        bb_info = QDialogButtonBox()
        bb_info.addButton("Add", QDialogButtonBox.AcceptRole)
        bb_info.addButton("Cancel", QDialogButtonBox.RejectRole)
        bb_info.accepted.connect(itemDlg.accept)
        bb_info.rejected.connect(itemDlg.reject)

        bLayout = QVBoxLayout()
        bLayout.addWidget(w_item)
        bLayout.addWidget(bb_info)
        itemDlg.setLayout(bLayout)
        itemDlg.resize(300, 70)

        action = itemDlg.exec_()

        if action == 1:
            itemName = e_item.text()

            if itemName == "":
                QMessageBox.warning(
                    self.core.messageParent, "Error", "Invalid %s name" % itemType
                )
                return

            itemPath = os.path.join(basePath, prefix + itemName)

            if os.path.exists(itemPath):
                QMessageBox(
                    self.core.messageParent,
                    "Error",
                    "%s%s %s already exists"
                    % (itemType[0].upper(), itemType[1:], itemName),
                )
                return

            try:
                os.makedirs(itemPath)
            except:
                QMessageBox(
                    self.core.messageParent,
                    "Error",
                    "Could not create %s %s" % (itemType, itemName),
                )
            else:
                refresh()

    @err_decorator
    def deleteSlave(self, itemType):
        if itemType == "slave":
            basePath = os.path.join(self.e_coordinatorRoot.text(), "Slaves")
            prefix = "S_"
            curItem = self.lw_slaves.currentItem()
            refresh = self.refreshSlaves
        elif itemType == "workstation":
            basePath = os.path.join(self.e_coordinatorRoot.text(), "Workstations")
            prefix = "WS_"
            curItem = self.lw_workstations.currentItem()
            refresh = self.refreshWorkstations
        else:
            return

        if curItem is None:
            return

        curName = curItem.text()

        msg = QMessageBox(
            QMessageBox.Question,
            "Pandora",
            "Are you sure you want to delete %s %s?" % (itemType, curName),
            QMessageBox.Cancel,
        )
        msg.addButton("Continue", QMessageBox.YesRole)
        action = msg.exec_()

        if action != 0:
            return False

        itemPath = os.path.join(basePath, prefix + curName)

        if os.path.exists(itemPath):
            while True:
                try:
                    shutil.rmtree(itemPath)
                    break
                except WindowsError:
                    msg = QMessageBox(
                        QMessageBox.Warning,
                        "Delete",
                        "Could not delete %s %s.\n\nMake sure no other programs are using files in this folder:\n%s"
                        % (itemType, curName, itemPath),
                        QMessageBox.Cancel,
                    )
                    msg.addButton("Retry", QMessageBox.YesRole)
                    action = msg.exec_()

                    if action != 0:
                        return False

        refresh()

    @err_decorator
    def refreshSlaves(self):
        self.lw_slaves.clear()

        if not self.gb_coordinator.isChecked() or self.e_coordinatorRoot.text() == "":
            return

        slaveDir = os.path.join(self.e_coordinatorRoot.text(), "Slaves")

        if os.path.exists(slaveDir):
            for i in os.listdir(slaveDir):
                if i.startswith("S_"):
                    item = QListWidgetItem(i[2:])
                    self.lw_slaves.addItem(item)

    @err_decorator
    def refreshWorkstations(self):
        self.lw_workstations.clear()

        if not self.gb_coordinator.isChecked() or self.e_coordinatorRoot.text() == "":
            return

        wsDir = os.path.join(self.e_coordinatorRoot.text(), "Workstations")

        if os.path.exists(wsDir):
            for i in os.listdir(wsDir):
                if i.startswith("WS_"):
                    item = QListWidgetItem(i[3:])
                    self.lw_workstations.addItem(item)

    @err_decorator
    def openSlave(self, itemType, mode="open"):
        if itemType == "slave":
            basePath = os.path.join(self.e_coordinatorRoot.text(), "Slaves")
            prefix = "S_"
            curItem = self.lw_slaves.currentItem()
            refresh = self.refreshSlaves
        elif itemType == "workstation":
            basePath = os.path.join(self.e_coordinatorRoot.text(), "Workstations")
            prefix = "WS_"
            curItem = self.lw_workstations.currentItem()
            refresh = self.refreshWorkstations
        else:
            return

        if curItem is None:
            return

        curName = curItem.text()

        itemPath = os.path.join(basePath, prefix + curName)

        if mode == "open":
            self.core.openFolder(itemPath)
        elif mode == "copy":
            self.core.copyToClipboard(itemPath)

    @err_decorator
    def saveSettings(self):
        openRH = False

        if hasattr(self.core, "RenderHandler") and self.core.RenderHandler.isVisible():
            self.core.RenderHandler.close()
            openRH = True

        cData = []

        cData.append(["globals", "checkForUpdates", self.chb_checkForUpdates.isChecked()])
        cData.append(["globals", "localMode", self.chb_localMode.isChecked()])
        cData.append(["globals", "send_error_reports", self.chb_errorReports.isChecked()])

        rPath = self.e_rootPath.text().replace("/", "\\")
        if rPath != "" and not rPath.endswith("\\"):
            rPath += "\\"
        cData.append(["globals", "rootPath", rPath])

        repPath = self.e_repositoryPath.text().replace("/", "\\")
        if repPath != "" and not repPath.endswith("\\"):
            repPath += "\\"
        cData.append(["globals", "repositoryPath", repPath])

        sPath = self.e_submissionPath.text().replace("/", "\\")
        if sPath != "" and not sPath.endswith("\\"):
            sPath += "\\"
        cData.append(["submissions", "submissionPath", sPath])
        cData.append(["submissions", "userName", self.e_username.text()])
        cData.append(["slave", "enabled", self.gb_slave.isChecked()])

        psPath = self.e_slavePath.text().replace("/", "\\")
        if psPath != "" and not psPath.endswith("\\"):
            psPath += "\\"
        cData.append(["slave", "slavePath", psPath])

        cData.append(["coordinator", "enabled", self.gb_coordinator.isChecked()])
        pcPath = self.e_coordinatorRoot.text().replace("/", "\\")
        if pcPath != "" and not pcPath.endswith("\\"):
            pcPath += "\\"
        cData.append(["coordinator", "rootPath", pcPath])

        for i in self.exOverridePlugins:
            res = self.core.getPlugin(i).pandoraSettings_saveSettings(self)
            if type(res) == list:
                cData += res

        for i in self.exOverridePlugins:
            cData.append(
                [
                    "dccoverrides",
                    "%s_override" % i,
                    self.exOverridePlugins[i]["chb"].isChecked(),
                ]
            )
            cData.append(
                ["dccoverrides", "%s_path" % i, self.exOverridePlugins[i]["le"].text()]
            )

        self.core.setConfig(data=cData)

        startupPath = (
            os.getenv("APPDATA") + "\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\"
        )
        trayStartup = startupPath + "Pandora Tray.lnk"
        slaveStartup = startupPath + "Pandora Slave.lnk"
        coordStartup = startupPath + "Pandora Coordinator.lnk"

        if os.path.exists(trayStartup):
            os.remove(trayStartup)

        if self.chb_pandoraTrayStartup.isChecked():
            if not os.path.exists(trayStartup):
                trayPath = os.path.join(self.core.pandoraRoot, "Tools", "Pandora Tray.lnk")
                if os.path.exists(trayPath):
                    shutil.copy2(trayPath, startupPath)
                else:
                    self.core.popup("Couldn't setup Pandora autostart. Run Setup_Startmenu.bat from the Pandora installation folder and try again.")
        else:
            if os.path.exists(trayStartup):
                os.remove(trayStartup)

        if self.gb_slave.isChecked() and self.chb_slaveStartup.isChecked():
            if not os.path.exists(slaveStartup):
                sPath = os.path.join(self.core.pandoraRoot, "Tools", "Pandora Slave.lnk")
                if os.path.exists(sPath):
                    shutil.copy2(sPath, slaveStartup)
                else:
                    self.core.popup("Couldn't setup Pandora Slave autostart. Run Setup_Startmenu.bat from the Pandora installation folder and try again.")
        else:
            if os.path.exists(slaveStartup):
                os.remove(slaveStartup)

        if self.gb_coordinator.isChecked() and self.chb_coordinatorStartup.isChecked():
            if not os.path.exists(coordStartup):
                cPath = os.path.join(
                    self.core.pandoraRoot, "Tools", "Pandora Coordinator.lnk"
                )
                if os.path.exists(cPath):
                    shutil.copy2(cPath, coordStartup)
                else:
                    self.core.popup("Couldn't setup Pandora Coordinator autostart. Run Setup_Startmenu.bat from the Pandora installation folder and try again.")
        else:
            if os.path.exists(coordStartup):
                try:
                    os.remove(coordStartup)
                except Exception as e:
                    if e.errno == 32:
                        self.core.popup("Cannot remove file because it is used by another process:\n\n%s" % coordStartup)
                    else:
                        raise

        if self.startSettings["localMode"] == self.chb_localMode.isChecked():
            if self.chb_localMode.isChecked():
                if self.startSettings["lRootPath"] == self.e_rootPath.text():
                    if self.startSettings["slaveEnabled"] != self.gb_slave.isChecked():
                        if self.gb_slave.isChecked():
                            self.core.startRenderSlave(newProc=True)
                        else:
                            self.core.stopRenderSlave()
                    if (
                        self.startSettings["coordEnabled"]
                        != self.gb_coordinator.isChecked()
                    ):
                        if self.gb_coordinator.isChecked():
                            self.core.startCoordinator()
                        else:
                            self.core.stopCoordinator()
                else:
                    if self.gb_slave.isChecked():
                        self.core.startRenderSlave(newProc=True, restart=True)
                    else:
                        self.core.stopRenderSlave()
                    if self.gb_coordinator.isChecked():
                        self.core.startCoordinator(restart=True)
                    else:
                        self.core.stopCoordinator()
            else:
                if self.startSettings["cRootpath"] == self.e_coordinatorRoot.text():
                    if self.startSettings["slaveEnabled"] != self.gb_slave.isChecked():
                        if self.gb_slave.isChecked():
                            self.core.startRenderSlave(newProc=True)
                        else:
                            self.core.stopRenderSlave()
                    if (
                        self.startSettings["coordEnabled"]
                        != self.gb_coordinator.isChecked()
                    ):
                        if self.gb_coordinator.isChecked():
                            self.core.startCoordinator()
                        else:
                            self.core.stopCoordinator()
                else:
                    if self.gb_slave.isChecked():
                        self.core.startRenderSlave(newProc=True, restart=True)
                    else:
                        self.core.stopRenderSlave()
                    if self.gb_coordinator.isChecked():
                        self.core.startCoordinator(restart=True)
                    else:
                        self.core.stopCoordinator()
        else:
            if self.gb_slave.isChecked():
                self.core.startRenderSlave(newProc=True, restart=True)
            else:
                self.core.stopRenderSlave()
            if self.gb_coordinator.isChecked():
                self.core.startCoordinator(restart=True)
            else:
                self.core.stopCoordinator()

        self.core.callback(name="onPandoraSettingsSave", types=["custom"], args=[self])

        if openRH:
            self.core.openRenderHandler()

    @err_decorator
    def loadSettings(self):
        if not os.path.exists(self.core.configPath):
            return

        ucData = {}

        for i in self.exOverridePlugins:
            ucData["%s_override" % i] = ["dccoverrides", "%s_override" % i, "bool"]
            ucData["%s_path" % i] = ["dccoverrides", "%s_path" % i]

        ucData["checkForUpdates"] = ["globals", "checkForUpdates", "bool"]
        ucData["localMode"] = ["globals", "localMode", "bool"]
        ucData["send_error_reports"] = ["globals", "send_error_reports", "bool"]
        ucData["rootPath"] = ["globals", "rootPath"]
        ucData["repositoryPath"] = ["globals", "repositoryPath"]
        ucData["submissionPath"] = ["submissions", "submissionPath"]
        ucData["userName"] = ["submissions", "userName"]
        ucData["sEnabled"] = ["slave", "enabled", "bool"]
        ucData["slavePath"] = ["slave", "slavePath"]
        ucData["cEnabled"] = ["coordinator", "enabled", "bool"]
        ucData["cRootpath"] = ["coordinator", "rootPath"]

        loadFunctions = {}
        for i in self.exOverridePlugins:
            res = self.core.getPlugin(i).pandoraSettings_loadSettings(self)
            if type(res) == tuple:
                loadData, pLoadFunctions = res
                ucData.update(loadData)
                loadFunctions.update(pLoadFunctions)

        ucData = self.core.getConfig(data=ucData)

        if ucData["checkForUpdates"] is not None:
            self.chb_checkForUpdates.setChecked(ucData["checkForUpdates"])

        if ucData["localMode"] is not None:
            self.chb_localMode.setChecked(ucData["localMode"])

        if ucData["send_error_reports"] is not None:
            self.chb_errorReports.setChecked(ucData["send_error_reports"])

        if ucData["rootPath"] is not None:
            self.e_rootPath.setText(ucData["rootPath"])

        if ucData["repositoryPath"] is not None:
            self.e_repositoryPath.setText(ucData["repositoryPath"])

        if not self.core.getSubmissionEnabled():
            self.tw_settings.removeTab(self.tw_settings.indexOf(self.tab_submissions))

        if ucData["submissionPath"] is not None:
            self.e_submissionPath.setText(ucData["submissionPath"])

        if ucData["userName"] is not None:
            uname = ucData["userName"]

            self.e_username.setText(uname)
            self.validate(uiWidget=self.e_username, allowChars=[" "])

        if ucData["sEnabled"] is not None:
            self.gb_slave.setChecked(ucData["sEnabled"])

        if ucData["slavePath"] is not None:
            self.e_slavePath.setText(ucData["slavePath"])

        if ucData["cEnabled"] is not None:
            self.gb_coordinator.setChecked(ucData["cEnabled"])

        if ucData["cRootpath"] is not None:
            self.e_coordinatorRoot.setText(ucData["cRootpath"])

        for i in self.exOverridePlugins:
            if ucData["%s_override" % i] is not None:
                self.exOverridePlugins[i]["chb"].setChecked(ucData["%s_override" % i])

            if ucData["%s_path" % i] is not None:
                self.exOverridePlugins[i]["le"].setText(ucData["%s_path" % i])

            if (
                not self.exOverridePlugins[i]["chb"].isChecked()
                and self.exOverridePlugins[i]["le"].text() == ""
            ):
                execFunc = self.core.getPluginData(i, "getExecutable")
                if execFunc is not None:
                    examplePath = execFunc()
                    if examplePath is not None:
                        if not os.path.exists(examplePath) and os.path.exists(
                            os.path.dirname(examplePath)
                        ):
                            examplePath = os.path.dirname(examplePath)

                        self.exOverridePlugins[i]["le"].setText(examplePath)

            self.exOverridePlugins[i]["le"].setEnabled(
                self.exOverridePlugins[i]["chb"].isChecked()
            )
            self.exOverridePlugins[i]["b"].setEnabled(
                self.exOverridePlugins[i]["chb"].isChecked()
            )

        startupPath = (
            os.getenv("APPDATA") + "\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\"
        )

        trayStartup = os.path.exists(startupPath + "Pandora Tray.lnk")
        self.chb_pandoraTrayStartup.setChecked(trayStartup)

        slaveStartup = os.path.exists(startupPath + "Pandora Slave.lnk")
        self.chb_slaveStartup.setChecked(slaveStartup)

        coordStartup = os.path.exists(startupPath + "Pandora Coordinator.lnk")
        self.chb_coordinatorStartup.setChecked(coordStartup)

        lmode = self.chb_localMode.isChecked()
        self.w_rootPath.setVisible(lmode)
        self.w_sumitterPath.setVisible(not lmode)
        self.w_slaveRoot.setVisible(not lmode)
        self.w_coordinatorRoot.setVisible(not lmode)
        self.w_farmcomputer.setVisible(not lmode)

    @err_decorator
    def loadUI(self):
        self.exOverridePlugins = {}
        self.integrationPlugins = {}
        self.dccTabs = QTabWidget()

        pluginNames = self.core.getPluginNames()
        for i in pluginNames:
            pAppType = self.core.getPluginData(i, "appType")
            if pAppType != "standalone":
                tab = QWidget()
                w_ovr = QWidget()
                lo_tab = QVBoxLayout()
                lo_ovr = QHBoxLayout()
                tab.setLayout(lo_tab)
                w_ovr.setLayout(lo_ovr)
                lo_tab.setContentsMargins(15, 15, 15, 15)
                lo_ovr.setContentsMargins(0, 9, 0, 9)
                # 	w_ovr.setMinimumSize(0,39)

                if self.core.getPluginData(i, "canOverrideExecuteable") != False:
                    l_ovr = QLabel(
                        "By default Pandora Slave tries to find the correct version of an application to render scenes.\nThe following setting let you override this behaviour by defining an explicit application for rendering."
                    )
                    chb_ovr = QCheckBox("Slave executable override")
                    le_ovr = QLineEdit()
                    b_ovr = QPushButton("...")
                    b_ovr.setMinimumWidth(40)
                    b_ovr.setMaximumWidth(40)
                    b_ovr.setContextMenuPolicy(Qt.CustomContextMenu)

                    lo_ovr.addWidget(chb_ovr)
                    lo_ovr.addWidget(le_ovr)
                    lo_ovr.addWidget(b_ovr)

                    lo_tab.addWidget(l_ovr)
                    lo_tab.addWidget(w_ovr)

                    self.exOverridePlugins[i] = {"chb": chb_ovr, "le": le_ovr, "b": b_ovr}

                gb_integ = QGroupBox("Pandora integrations")
                lo_integ = QVBoxLayout()
                gb_integ.setLayout(lo_integ)
                lw_integ = QListWidget()
                w_integ = QWidget()
                lo_integButtons = QHBoxLayout()
                b_addInteg = QPushButton("Add")
                b_removeInteg = QPushButton("Remove")

                w_integ.setLayout(lo_integButtons)
                lo_integButtons.addStretch()
                lo_integButtons.addWidget(b_addInteg)
                lo_integButtons.addWidget(b_removeInteg)

                lo_integ.addWidget(lw_integ)
                lo_integ.addWidget(w_integ)
                lo_tab.addWidget(gb_integ)

                lw_integ.setSizePolicy(
                    QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
                )

                self.integrationPlugins[i] = {
                    "lw": lw_integ,
                    "badd": b_addInteg,
                    "bremove": b_removeInteg,
                }

                self.core.getPlugin(i).pandoraSettings_loadUI(self, tab)

                lo_tab.addStretch()

                self.dccTabs.addTab(tab, i)

        if self.dccTabs.count() > 0:
            self.tab_dccApps.layout().addWidget(self.dccTabs)

        self.refreshIntegrations()

        self.tab_dccApps.layout().addStretch()

    @err_decorator
    def refreshIntegrations(self):
        installConfig = None
        if os.path.exists(self.core.installLocPath):
            installConfig = self.core.getConfig(
                configPath=self.core.installLocPath, getConf=True
            )

        for i in self.integrationPlugins:
            installPaths = []
            if installConfig is not None and i in installConfig:
                for k in installConfig[i]:
                    installPaths.append(installConfig[i][k])

            self.integrationPlugins[i]["lw"].clear()

            for k in installPaths:
                item = QListWidgetItem(k)
                self.integrationPlugins[i]["lw"].addItem(item)

            if len(installPaths) > 0:
                self.integrationPlugins[i]["lw"].setCurrentRow(0)
                self.integrationPlugins[i]["bremove"].setEnabled(True)
            else:
                self.integrationPlugins[i]["bremove"].setEnabled(False)

    @err_decorator
    def showUpdate(self):
        updateDlg = QDialog()
        updateDlg.setWindowTitle("Update Pandora scripts")
        updateDlg.l_options = QLabel(
            "- Update from GitHub: Downloads the latest version from GitHub. This is definitely the latest version, but may include experimental features.\n\n- Update from .zip: Select a .zip file, which contains the Pandora scripts. You can download .zip files with the Pandora scripts from the GitHub repository."
        )
        updateDlg.b_github = QPushButton("Update from GitHub")
        updateDlg.b_zip = QPushButton("Update from .zip")
        lo_update = QVBoxLayout()
        lo_buttons = QHBoxLayout()
        w_buttons = QWidget()
        w_buttons.setLayout(lo_buttons)
        lo_buttons.addWidget(updateDlg.l_options)
        lo_buttons.addWidget(updateDlg.b_github)
        lo_buttons.addWidget(updateDlg.b_zip)
        lo_update.addWidget(updateDlg.l_options)
        lo_update.addWidget(w_buttons)
        updateDlg.setLayout(lo_update)
        self.core.parentWindow(updateDlg)
        updateDlg.b_zip.clicked.connect(updateDlg.accept)
        updateDlg.b_zip.clicked.connect(self.updateFromZip)
        updateDlg.b_github.clicked.connect(updateDlg.accept)
        updateDlg.b_github.clicked.connect(lambda: self.core.updatePandora(source="github"))
        action = updateDlg.exec_()

    @err_decorator
    def updateFromZip(self):
        pZip = QFileDialog.getOpenFileName(
            self, "Select Pandora Zip", self.core.pandoraRoot, "ZIP (*.zip)"
        )[0]

        if pZip != "":
            self.core.updatePandora(filepath=pZip)

    @err_decorator
    def validate(self, uiWidget, origText=None, allowChars=[]):
        if origText is None:
            origText = uiWidget.text()
        text = self.core.validateStr(origText, allowChars=allowChars)

        if len(text) != len(origText):
            cpos = uiWidget.cursorPosition()
            uiWidget.setText(text)
            uiWidget.setCursorPosition(cpos - 1)

    @err_decorator
    def startTray(self):
        command = '"%s\\%s\\Pandora Tray.exe" "%s\\Scripts\\PandoraTray.py"' % (
            pyLibs,
            self.core.pandoraRoot,
            self.core.pandoraRoot,
        )
        subprocess.Popen(command, shell=True)

    @err_decorator
    def enterEvent(self, event):
        QApplication.restoreOverrideCursor()

if __name__ == "__main__":
    qApp = QApplication(sys.argv)
    qApp.setStyleSheet(qdarkstyle.load_stylesheet(pyside=True))
    handlerIcon = QIcon(
        os.path.dirname(os.path.abspath(__file__))
        + "\\UserInterfacesPandora\\pandora_tray.ico"
    )
    qApp.setWindowIcon(handlerIcon)
    import PandoraCore

    pc = PandoraCore.PandoraCore()
    pc.openSettings()
    qApp.exec_()


l = [1, 2, 2, 5, 8, 4, 4, 8]

def c1():
    l1 = range (min(l), max(l)+1)
    l11 = []
    for a in l1:
        if a in l:
            l11.append(a)
    print('#1: ', len(l11))
c1()

def c2():
    import collections
    a = collections.Counter(l)
    b = (list(a.items()))
    print('#2: ', len(b))
c2()

def c3():
    print('#3: ',len(set(l)))

c3()

def c4():
    n = 0
    c = []
    for a in l:
        if a not in c:
            n += 1
            c.append(a)
    print(n)
c4()

Advance List Programs/1.count unique values .py


import unittest

from cart import Cart
from cashier import Cashier

class TestEcommerce(unittest.TestCase):

    # Iteration 1
    def test_can_create_an_empty_cart(self):
        cart = self.__empty_cart()

        self.assertTrue(cart.is_empty())

    def test_add_a_book_to_an_empty_cart(self):
        cart = self.__empty_cart()
        book = "book 1"
        cart.add_book(book)

        self.assertFalse(cart.is_empty())
        self.assertEqual(cart.count_book(book), 1)

    def test_be_able_to_add_more_than_one_book(self):
        cart = self.__empty_cart()
        cart.add_book("book 1")
        cart.add_book("book 2")
        cart.add_book("book 3")

        self.assertFalse(cart.is_empty())
        self.assertEqual(cart.count_book("book 1"), 1)
        self.assertEqual(cart.count_book("book 2"), 1)
        self.assertEqual(cart.count_book("book 3"), 1)

    def test_can_add_more_than_one_of_the_same_book(self):
        cart = self.__empty_cart()
        book = "book 1"
        cart.add_book(book, 2)

        self.assertEqual(cart.count_book(book), 2)

    def test_not_be_able_to_add_negative_quantity_of_books(self):
        cart = self.__empty_cart()
        book = "book 1"

        with self.assertRaises(Exception) as context:
            cart.add_book(book, -1)

        self.assertEqual(str(context.exception), Cart.INVALID_QUANTITY_MSG)
        self.assertEqual(cart.count_book(book), 0)

    def test_can_only_add_books_from_tus_libros(self):
        cart = self.__empty_cart()
        book = "external book"

        with self.assertRaises(Exception) as context:
            cart.add_book(book)

        self.assertEqual(str(context.exception), Cart.INVALID_BOOK_MSG)
        self.assertEqual(cart.count_book(book), 0)

    # Iteration 2
    def test_can_not_checkout_empty_cart(self):
        cart = self.__empty_cart()

        cashier = Cashier()

        with self.assertRaises(Exception) as context:
            cashier.checkout(cart)

        self.assertEqual(str(context.exception), Cashier.EMPTY_CART_MSG)

    def test_can_checkout_a_cart_with_one_book_of_10_pesos(self):
        cart = self.__empty_cart()
        book = "book 1"
        cart.add_book(book)

        cashier = Cashier()

        ticket_total = cashier.checkout(cart)

        self.assertEqual(ticket_total, 10)
        self.assertTrue(cart.is_empty())

    def __empty_cart(self):
        catalog = ['book 1', 'book 2', 'book 3']
        return Cart(catalog)

if __name__ == '__main__':
    unittest.main()


import json
import os

# class Employee:
#     def __init__(self, name, surname):
#         self.first_name = name
#         self.last_name = surname
#         self.salary = 1000

#     def increase_salary(self):
#         self.salary += 100

#     def decrease_salary(self):
#         self.salary -= 100

# bob = Employee('Bob', 'Jones')
# sam = Employee('Sam', 'Heckley')

# class Animal:
#     def __init__(self, name, age) -> None:
#         self.name = name
#         self.age = age
#         self.legs_count = 4

#     def make_sound(self):
#         print('unknown sound')

# class Cat(Animal):
#     pass

# class Dog(Animal):
#     def make_sound(self):
#         print('Woof')

# tom = Cat('tom', 4)
# goofy = Dog('goofy', 6)

# print(tom.age)
# tom.make_sound()
# goofy.make_sound()

# class Mammal:
#     def __init__(self, color, has_tail=True, legs_count=4, sound=None) -> None:
#         self.color = color
#         self.has_tail = has_tail
#         self.legs_count = legs_count

#     def make_sound(self):
#         if self.sound is not None:
#             print(self.sound)
#         else:
#             print('')

# class PetMammal(Mammal):
#     def __init__(self, name, color, has_tail=True, legs_count=4, sound=None) -> None:
#         self.name = name
#         super().__init__(color, has_tail, legs_count, sound)

# class Cat(Mammal):
#     def make_sound(self) -> None:
#         print(f'{self.color} cat says ', end='')
#         super().make_sound()

# class PetCat(PetMammal):
#     def make_sound(self) -> None:
#         print(f'{self.name} says ', end='')
#         super().make_sound()

# class Dog(Mammal):
#     def make_sound(self) -> None:
#         print(f'{self.color} dog says ', end='')
#         super().make_sound()

# class PetDog(PetMammal):
#     def make_sound(self) -> None:
#         print(f'{self.name} says ', end='')
#         super().make_sound()

# class Tiger:
#     interests = ['sleeping', 'eating']

#     def __init__(self, name):
#         self.name = name

# bob = Tiger('Bob')
# print(bob.interests)
# bob.interests = ['doing nothing']
# print(bob.interests)

# print('---')

# maddy = Tiger('Madelyn')
# maddy.interests = ['sleeping', 'eating', 'playing']
# print(Tiger.interests)
# print(maddy.interests)

# print('---')
# Tiger.interests = []

# simon = Tiger('Simon')
# print(simon.interests)\

# class Numbers:
#     def __init__(self) -> None:
#         self._data = []

#     def add(self, value):
#         self._data.append(value)

#     def get(self, index):
#         return self._data[index]

#     def get_length(self):
#         return len(self._data)

#     def __len__(self):
#         return len(self._data)

# class NumbersIter:
#     def __init__(self, instance) -> None:
#         self.instance = instance
#         self.position = - 1

#     def __next__(self):
#         self.position += 1
#         if self.position < len(self.instance):
#             return self.instance[self.position]
#         else:
#             raise StopIteration

# class Numbers:
#     def __init__(self) -> None:
#         self._data = [1, 2, 3, 4]

#     def __len__(self):
#         return len(self._data)

#     def __getitem__(self, index):
#         return self._data[index]

#     def __setitem__(self, index, value):
#         self._data[index] = value

#     def __str__(self):
#         return str(self._data)

#     def __repr__(self):
#         return self._data

#     def __iter__(self):
#         return NumbersIter(self)

# class NumbersIterator:
#     def __init__(self, numbers_instance: 'Numbers') -> None:
#         self._numbers_to_iterate = numbers_instance
#         self._position = -1

#     def __next__(self) -> int:
#         self._position += 1
#         if self._position < len(self._numbers_to_iterate):
#             return self._numbers_to_iterate.get(self._position)
#         else:
#             raise StopIteration

# class Numbers:
#     def __init__(self) -> None:
#         self._data = [1, 2, 3, 4]

#     def __getitem__(self, index: int) -> int:
#         return self._data[index]

#     def __setitem__(self, index: int, value: int) -> None:
#         self._data[index] = value

#     def __iter__(self):
#         # Если метода __contains__ нет, то код:
#         # numbers = Numbers()
#         # value in numbers
#         # вызовет __iterator__
#         # и при помощи полученного итератора переберет все значения
#         print('inside __iter__')
#         return NumbersIterator(self)

#     def __contains__(self, value: int) -> bool:
#         # Если метод __contains__ есть, то код:
#         # numbers = Numbers()
#         # value in numbers
#         # вызовет его
#         print('inside __contains__')
#         return value in self._data

#     def __len__(self) -> int:
#         return len(self._data)

#     def __str__(self) -> str:
#         # Показать строковое представление объекта для человека
#         return str(self._data)

#     def __repr__(self) -> str:
#         # От representation - показать "техническое" строковое представление для программиста
#         return f'<Объект Numbers c числами {self._data}>'

# разделение и ответственность!

class Student:
    def __init__(self, name: str, last_name: str, age: int) -> None:
        self.name = name
        self.last_name = last_name
        self.age = age

    def __repr__(self) -> str:
        return f'{self.name}, {self.last_name}, {self.age}'

# class Program:
#     def __init__(self, students=None) -> None:
#         # self.students = []
#         # if students is not None:
#         #     self.students = students
#         if students == None:
#             self.students = []
#         else:
#             self.students = students

#     def list(self) -> list[Student]:
#         return self.students

#     def add_student(self, first_name: str, last_name: str, age: int):
#         new_student = Student(first_name=first_name, last_name=last_name, age=age)
#         self.students.append(new_student)

class InMemoryStudentsRepository:
    def __init__(self) -> None:
        self.students = []

    def get_all_students(self):
        return self.students

    def add_student(self, student: Student):
        self.students.append(student)

class JsonFileStudentsRepository:
    """
    Репозиторий для хранения данных о студентах в JSON-файлах следующего формата:
    [
        {"first_name" <first_name>, "last_name": <last_name>, "age": <age>},
        ...
    ]
    """
    def __init__(self, filename) -> None:
        self.filename = filename

    def get_all_students(self):
        student_records = self._read_records_from_file()
        students = self._parse_student_records_from_file(student_records)
        return students

    def add_student(self, student: Student):
        student_records = self._read_records_from_file()
        new_student_record = self._conver_student_to_dict(student)
        student_records.append(new_student_record)
        self._write_student_records_to_file(student_records)

    def _write_student_records_to_file(self, student_records: list[dict]) -> None:
        """Записать на диск набор записей (словарей нужного формата)."""
        with open(self.filename, 'w') as data_file:
            data_file.write(json.dumps(student_records))

    def _read_records_from_file(self) -> list[dict]:
        """Прочесть с диска набор записей (словарей нужного формата)."""
        # Если файла еще нет, надо его создать и сохранить в него пустой список студентов"""
        if not os.path.exists(self.filename):
            self._write_student_records_to_file([])

        # try:
        #     with open(self.filename, 'r') as data_file:
        #         data = data_file.read()
        # except FileNotFoundError:
        #     self._write_student_records_to_file([])

        # А теперь, когда он точно существует, можно просто его смело читать
        with open(self.filename, 'r') as data_file:
            data = data_file.read()
        return json.loads(data)

    def _parse_student_records_from_file(self, student_records: list[dict]) -> list[Student]:
        """Сконвертировать набор записей (словарей нужного формата) в набор объектов Student."""
        result = []
        for record in student_records:
            student = Student(first_name=record['first_name'], last_name=record['last_name'], age=record['age'])
            result.append(student)
        return result

    def _conver_student_to_dict(self, student: Student) -> dict:
        """Сконвертировать объект Student в словарь подходящего для записи в файл формата."""
        return {
            'name': student.name,
            'last_name': student.last_name,
            'age': student.age
        }

class ConsoleUI:

    @staticmethod
    def get_action():
        return ConsoleUI._get_user_input('enter a command: ')

    @staticmethod
    def get_student_data() -> tuple(str, str, int):
        first_name = ConsoleUI._get_user_input('Введите имя студнта: ')
        last_name = ConsoleUI._get_user_input('Введите фамилию студента: ')
        age = ConsoleUI._get_user_input('Введите возраст студента: ', converter_func=int)
        return (first_name, last_name, age)

    @staticmethod
    def list_students(students: list[Student]) -> None:
        print(students)

    @staticmethod
    def _get_user_input(prompt_text: str, converter_func = str):
        user_input = input(prompt_text)
        return converter_func(user_input)

    @staticmethod
    def list_available_actions():
        print('add', 'list', 'delete', 'quit')

if __name__ == '__main__':
     # Репозиторий нужен для взаимодействия с хранилищем данных
    student_repository = JsonFileStudentsRepository(filename='students.json')
    ui_functions = ConsoleUI

    ui_functions.list_available_actions()
    while True:
        ui_functions.list_available_actions()
        selected_action = ui_functions.get_action()

        if selected_action == 'help':
            ui_functions.list_available_actions()
        elif selected_action == 'list':
            students = student_repository.get_all_students()
            ui_functions.list_students()
        elif selected_action == 'add':
            first_name, last_name, age = ui_functions.get_student_data()
            new_student = Student(first_name=first_name, last_name=last_name, age=age)
            student_repository.add_student(new_student)
        elif selected_action == 'qConsoleUIt':
            break


import requests
from time import localtime, strftime
import copy
from operator import itemgetter

#########  PARAMETERS #########

# Default values
IATA_departure = 'SXF'  # Flight departure location, IATA code
IATA_arrival = ''       # Flight arrival location, IATA code (optional)
Language = 'pl'         # Language in which airport names are displayed
Limit = '100'           # Maximum number of displayed flights
Offset = '0'            # ? No idea, no official API documentation
Flex_in = '0'           # The number of extra days to include with DateIn
Flex_out = '0'          # The number of extra days to include with DateOut
Price_value = '200'     # Maximum prize for a one-way or two-way ticket
dep_out_date_from = '2018-04-28' # Earliest departure date of an outbound flight
dep_out_date_to = '2018-05-03'   # Latest departure date of an outbound flight
dep_in_date_from ='2018-05-11'   # Earliest departure date of an inbound flight
dep_in_date_to = '2018-05-30'    # Latest departure date of an inbound flight

def change_parameters():
    ''' User defines other parameters than default ones.
    Variables must be defined as global, until the code will not be
    rewritten with classes.
    '''

    global dep_out_date_from
    global dep_out_date_to
    global dep_in_date_from
    global dep_in_date_to
    global IATA_departure
    global IATA_arrival
    global Price_value

    choice = None
    while choice != "0":
        print \
        ("""
        --------------- Changing flight search details ---------------

        0 - Back to the main menu
        1 - Change: Earliest departure date of an outbound flight
        2 - Change: Latest departure date of an outbound flight
        3 - Change: Earliest departure date of an inbound flight
        4 - Change: Latest departure date of an inbound flight
        5 - Change: Flight departure location
        6 - Change: Flight arrival location
        7 - Change: Maximum price for one-way or two-way ticket

        """)

        choice = input("Choose: ")
        print()

        if choice == "0":
            print("Back to the main menu")
        elif choice == "1":
            dep_out_date_from = input("Earliest departure date of an outbound flight (YYYY-MM-DD): ")
        elif choice == "2":
            dep_out_date_to = input("Latest departure date of an outbound flight (YYYY-MM-DD): ")
        elif choice == "3":
            dep_in_date_from = input("Earliest departure date of an inbound flight (YYYY-MM-DD): ")
        elif choice == "4":
            dep_in_date_to = input("Latest departure date of an inbound flight (YYYY-MM-DD): ")
        elif choice == "5":
            IATA_departure = input("Flight departure location, three-letter IATA airport code: ")
        elif choice == "6":
        	iata = input('Do you want to change destination (press 1) or erase it completely, so that many locations are searched? (press 2): ')
        	if iata == "1":
        		IATA_arrival = input("Flight arrival location, three-letter IATA airport code: ")
        	if iata == "2":
        		IATA_arrival=''
        		print('/nArrival location erased!')
        elif choice == "7":
        	Price_value = input("Maximum price for one-way or two-way ticket (in departure location currency): ")
        else:
            print(" !!!!!!!!! Wrong option !!!!!!!!! ")

def show_parameters():
    ''' Displaying current parameters values'''

    print("Earliest departure date of an outbound flight: " + str(dep_out_date_from) + '\n')
    print("Latest departure date of an outbound flight: " + str(dep_out_date_to) + '\n')
    print("Earliest departure date of an inbound flight: " + str(dep_in_date_from) + '\n')
    print("Latest departure date of an inbound flight: " + str(dep_in_date_to) + '\n')
    print("Maximum price for a one-way or two-way ticket (in departure location currency): " + str(Price_value) + '\n')
    print("Departure from: " + str(IATA_departure) + '\n')
    if IATA_arrival:
        print("Arrival in: " + str(IATA_arrival ) + '\n')
    else:
    	print("Arrival in: not specified")

def get_url(ways):
    '''Creating an URL to query Ryanair website'''

    if ways ==1:
        if IATA_arrival:
            return 'https://api.ryanair.com/farefinder/3/oneWayFares?'  + \
            '&arrivalAirportIataCode=' + IATA_arrival + \
            '&departureAirportIataCode=' + IATA_departure + \
            '&language=' + Language +   '&limit=' + Limit + '&FlexDaysIn=' + \
            Flex_in + '&FlexDaysOut=' + Flex_out +'&market=' + Language + '-' + \
            Language + '&offset=' + Offset + '&outboundDepartureDateFrom=' + \
            dep_out_date_from + '&outboundDepartureDateTo=' + \
            dep_out_date_to + '&priceValueTo=' + Price_value
        else:
            return 'https://api.ryanair.com/farefinder/3/oneWayFares?'  + \
            '&departureAirportIataCode=' + IATA_departure + \
            '&language=' + Language +   '&limit=' + Limit + '&FlexDaysIn=' + \
            Flex_in + '&FlexDaysOut=' + Flex_out +'&market=' + Language + '-' + \
            Language + '&offset=' + Offset + '&outboundDepartureDateFrom=' + \
            dep_out_date_from + '&outboundDepartureDateTo=' + \
            dep_out_date_to + '&priceValueTo=' + Price_value

    if ways == 2:
        if IATA_arrival:
            return 'https://api.ryanair.com/farefinder/3/roundTripFares?' + \
            '&arrivalAirportIataCode=' + IATA_arrival + \
            '&departureAirportIataCode=' + IATA_departure + \
            '&inboundDepartureDateFrom=' + dep_in_date_from + \
            '&inboundDepartureDateTo=' + dep_in_date_to + \
            '&language=' + Language +   '&limit=' + Limit + '&FlexDaysIn=' + \
            Flex_in + '&FlexDaysOut=' + Flex_out +'&market=' + Language + '-' + \
            Language + '&offset=' + Offset + '&outboundDepartureDateFrom=' + \
            dep_out_date_from + '&outboundDepartureDateTo=' + \
            dep_out_date_to + '&priceValueTo=' + Price_value
        else:
            return 'https://api.ryanair.com/farefinder/3/roundTripFares?' + \
            '&departureAirportIataCode=' + IATA_departure + \
            '&inboundDepartureDateFrom=' + dep_in_date_from + \
            '&inboundDepartureDateTo=' + dep_in_date_to + \
            '&language=' + Language +   '&limit=' + Limit + '&FlexDaysIn=' + \
            Flex_in + '&FlexDaysOut=' + Flex_out +'&market=' + Language + '-' + \
            Language + '&offset=' + Offset + '&outboundDepartureDateFrom=' + \
            dep_out_date_from + '&outboundDepartureDateTo=' + \
            dep_out_date_to + '&priceValueTo=' + Price_value

def find_fares(ways):
    '''Getting JSON data containing lowest fares from Ryanair website'''

    # Connecting to Ryanair website
    url = get_url(ways)
    r = requests.get(url)
    if r.ok:
        print("Status code:", r.status_code)
    else:
        print("HTTP %i - %s, Message %s" % (r.status_code, r.reason, r.text))

    # Saving response
    result  = r.json()
    return result['fares']

def get_vals(flight, key, one, two = 0):
    '''Fares is a list of dictionaries that consist of 3 keys: outbound, inbound, summary
    We need to dig deeper inside their values, sub-values and sub-subvalues'''

    temp = []
    if two:
        temp = [v[one][two] for k,v in flight.items() if k == key]
    else:
        temp = [v[one] for k,v in flight.items() if k == key]
    return temp[0]

def clean_results(ways):
    ''' Results from JSON file are in a really ugly form. Let's make it more readable'''

    fares = find_fares(ways)
    flights_clean = []
    lowest_fares = {}

    for flight in fares:
        # Przygotowujemy slownik dla każdego kolejnego lotniska z listy
        lowest_fares['airport'] = get_vals(flight, 'outbound', 'arrivalAirport', 'name')
        lowest_fares['currency'] = get_vals(flight, 'outbound','price', 'currencyCode')
        lowest_fares['date'] =  get_vals(flight, 'outbound','departureDate')[:10]
        if ways == 2: lowest_fares['date_back'] = get_vals(flight,'inbound','departureDate')[:10]
        lowest_fares['price_main'] = int(get_vals(flight, 'summary', 'price', 'valueMainUnit'))
        lowest_fares['price_frac'] = int(get_vals(flight, 'summary', 'price', 'valueFractionalUnit'))
        lowest_fares['price_total'] = lowest_fares['price_main'] + lowest_fares['price_frac']/100
        flights_clean.append(copy.copy(lowest_fares)) # nasza zbiorcza lista wzbogaca sie o kolejny slownik-lotnisko, ALE UWAGA! Musi byc kopia, bo inaczej dane sie nadpisuja

    # itemgetter: sort the list of dictionaries by the price
    return sorted(flights_clean, key=itemgetter('price_main'), reverse=False)

def print_results(ways, flights_clean):
    ''' Printing out to the screen the lowest fares sorted by price '''

    print("\n\n" + IATA_departure, end='')

    if ways == 1:
        print(" - One-way trip: " + str(len(flights_clean)) + " offer(s) \n\n")
    if ways == 2:
        print(" - Two-way trip: " + str(len(flights_clean)) + " offer(s) \n\n")

    for flight in flights_clean:
        print (flight['airport'], end=": " )
        print (str(flight['price_total']) + ' ' + flight['currency'], end='')
        if ways == 1:
            print(" on " + flight['date'], end="\n\n")
        if ways == 2:
            print(" on " + str(flight['date']) + ' - ' + str(flight['date_back']), end="\n\n")

def txt_results(ways, flights_clean):
    ''' Saving the lowest fares to Ryanair.txt file in the current directory '''

    with open("Ryanair.txt", 'a') as f:
        f.write("================================================ \n\n")
        f.write("Script was initialized: ")
        f.write(strftime('%Y-%m-%d %H:%M:%S', localtime()))
        f.write('\n\n')
        f.write("----------------------------------------------- \n\n")
        f.write(IATA_departure)

        if ways == 1:
            f.write('- One-way trip: \n\n')
        if ways == 2:
            f.write('- Two-way trip: \n\n')

        for flight in flights_clean:
            f.write(str(flight['airport']) + ": ")
            f.write(str(flight['price_total']) + ' ')
            f.write(str(flight['currency']) + ' on ')
            if ways == 1:
                f.write(str(flight['date']) + "\n\n")
            if ways == 2:
                f.write(str(flight['date']) + " - ")
                f.write(str(flight['date_back']) + "\n\n")

def n_way(ways):
    ''' Getting clean results for one-way or two-way flights and printing them (to the screen + into txt file) '''

    flights_clean = clean_results(ways)
    print_results(ways, flights_clean)
    # txt_results(ways, flights_clean)
    # send_mail(toaddr="XYZ@gmail.com", subject="Wyszukiwarka lotow Ryanair", body="Patrz: załącznik", att_path="C:\\1. Patryk\\Data science\\Python\\Kody\\Raporty", att_filename="Ryanair.txt")


import csv
from collections import defaultdict
import datetime

#-----Extras------

def ingresarGenerico(strg):
    return int(input(strg))

def verificarDNI(strg):
    ver = int(input(strg))
    while(ver>99999999 or ver<10000000):
        print("Valor invalido!")
        ver = int(input(strg))
    return ver

def verificarValor(strg):
    return float(input(strg))

def verificarFechas(strg):
    print(strg)

    año = int(input("Ingrese el año: "))
    while (año<=0):
        print("Año invalido!")
        año = int(input("Ingrese el año: "))

    mes = int(input("Ingrese el mes: "))
    while (mes<=0 or 13<=mes):
        print("Mes invalido!")
        mes = int(input("Ingrese el mes: "))

    dia = int(input("Ingrese el día: "))
    if(mes==1 or mes==3 or mes==5 or mes==7 or mes==9 or mes==11):
        ultimo=31
    elif(mes==4 or mes==6 or mes==8 or mes==10 or mes==12):
        ultimo=30
    else:
        ultimo=29

    while(dia<=0 or ultimo<dia):
        print("Dia invalido!")
        dia = int(input("Ingrese el día: "))

    fecha = datetime.datetime(año,mes,dia)
    return fecha.strftime("%Y-%m-%d")

def verificarTipo(strg):
    ver = input(strg)
    while(ver.lower()!="depositado" and ver.lower()!="emitido"):
        print("Valor invalido, los tipos de cheques son 'depositado' o 'emitido'")
        ver = input(strg)
    return ver

def verificarEstado(strg):
    ver = input(strg)
    while(ver.lower()!="aprobado" and ver.lower()!="pendiente" and ver.lower()!="rechazado"):
        print("Valor invalido, los estados de cheques son 'aprobado','pendiente' o 'rechazado'")
        ver = input(strg)
    return ver

def mostrarTitulo():
    print(f"|NroCheque|CodigoBanco|CodigoSucursal|NumeroCuentaOrigen|NumeroCuentaDestino|Valor|FechaOrigen|FechaPago|DNI|Estado|Tipo")

def mostrarDatos(dict):
    print(f"|{dict['NroCheque']}|{dict['CodigoBanco']}|{dict['CodigoSucursal']}|{dict['NumeroCuentaOrigen']}|{dict['NumeroCuentaDestino']}|{dict['Valor']}|{dict['FechaOrigen']}|{dict['FechaPago']}|{dict['DNI']}|{dict['Estado']}|{dict['Tipo']}|")

def crearArchivo(filtro,opcion):
    with open (f"{filtro}_{datetime.datetime.now().strftime('%Y-%m-%d')}_.csv","w",newline='') as wfile:
        with open("cheques.csv","r") as rfile:
            writer = csv.writer(wfile)
            reader = csv.DictReader(rfile)
            writer.writerow(reader.fieldnames)
            for row in reader:
                dni = row["DNI"]
                dni = int(dni)
                if(dni==filtro and opcion==1):
                    writer.writerow(row.values())
                elif(row['Tipo']==filtro and opcion==2):
                    writer.writerow(row.values())
                elif(row['Estado']==filtro and opcion==3):
                    writer.writerow(row.values())

def crearArchivoFechas(strg1,strg2):
    with open (f"FECHAS_{datetime.datetime.now().strftime('%Y-%m-%d')}_.csv","w",newline='') as wfile:
        with open("cheques.csv","r") as rfile:
            writer = csv.writer(wfile)
            reader = csv.DictReader(rfile)
            writer.writerow(reader.fieldnames)
            for row in reader:
                if(strg1<row["FechaOrigen"]<strg2):
                    writer.writerow(row.values())

#----Agregar---------

def agregar():
    with open ("cheques.csv","r") as lectura:
        reader = csv.reader(lectura)
        for row in reader:
            ultimo = row[0]
        ultimo = int(ultimo)
    with open ("cheques.csv","+a",newline='') as salida:
        writer = csv.writer(salida)
        NroCheque = ultimo + 1
        CodigoBanco = ingresarGenerico("Ingrese el codigo de Banco: ")
        CodigoSucursal = ingresarGenerico("Ingrese el codigo de la sucursal: ")
        NumeroCuentaOrigen = verificarDNI("Ingrese el numero de cuenta de origen: ")
        NumeroCuentaDestino = verificarDNI("Ingrese el numero de la cuenta de destino: ")
        Valor = verificarValor("Ingrese el valor del cheque: ")
        FechaOrigen = verificarFechas("Ingrese la fecha de origen del cheque: ")
        FechaPago = verificarFechas("Ingrese la fecha de pago del cheque: ")
        dni = verificarDNI("Ingrese su numero de DNI: ")
        Estado = verificarEstado("Ingrese el estado del cheque: ")
        Tipo = verificarTipo("Ingrese el tipo de cheque: ")
        writer.writerow([NroCheque,CodigoBanco,CodigoSucursal,NumeroCuentaOrigen,NumeroCuentaDestino,Valor,FechaOrigen,FechaPago,dni,Estado,Tipo])
        print("Agregados de manera exitosa")

#----Fin Agregar----------

#----Inicio Filtrado--------

def filtrado(filtro,opcion,salida):
    if(salida == 1):
        with open("cheques.csv","r") as file:
            reader = csv.DictReader(file)
            if(salida == 1):
                mostrarTitulo()
                for row in reader:
                    dni = row["DNI"]
                    dni = int(dni)
                    if(dni==filtro and opcion==1):
                        mostrarDatos(row)
                    elif(row["Tipo"]==filtro and opcion==2):
                        mostrarDatos(row)
                    elif(row["Estado"]==filtro and opcion==3):
                        mostrarDatos(row)
    elif(salida == 2):
        crearArchivo(filtro,opcion)

def filtradoFechas(strg1,strg2,salida):
    if(salida==1):
        with open("cheques.csv","r") as file:
            reader = csv.DictReader(file)
            for row in reader:
                if(strg1<row["FechaOrigen"]<strg2):
                    mostrarDatos(row)
    elif(salida==2):
        crearArchivoFechas(strg1,strg2)

def menuFiltros():
    opcion = 0
    strg = ""
    strg1 = ""
    strg2 = ""
    salida = int(input("Ingrese '1' si desea una salida por pantalla o '2' si la desea creando un archivo csv: "))
    while(opcion>0 or opcion<=4):
        print("Seleccione uno de los siguientes filtros: ")
        print("1-Por un DNI particular")
        print("2-Por el tipo de cheque")
        print("3-Estado del cheque")
        print("4-Rango de fechas")
        print("5-Salir")
        opcion = int(input())
        if(opcion==1):
            strg = verificarDNI("Ingrese el DNI a filtrar: ")
            filtrado(strg,opcion,salida)
        elif(opcion==2):
            strg = verificarTipo("Ingrese el tipo de cheque: ")
            filtrado(strg,opcion,salida)
        elif(opcion==3):
            strg =verificarEstado("Ingrese el estado del cheque: ")
            filtrado(strg,opcion,salida)
        elif(opcion==4):
            strg1 = verificarFechas("Ingrese la primera fecha: ")
            strg2 = verificarFechas("Ingrese la segunda fecha: ")
            filtradoFechas(strg1,strg2,salida)
        elif(opcion==5):
            break
        else:
            print("Ingrese una opción valida!")

def filtrar():
    menuFiltros()
    print("Filtrado exitoso")

#----Fin Filtrado----

#---Inicio Salir

def salir():
    print("Fin")

#----Fin salir----

#---Inicio Menu----

def menu():
    opcion = 0
    while(opcion>0 or opcion<=3):
        print("Bienvenido al menu")
        print("Seleccione una de las opciones para continar")
        print("1-Agregar")
        print("2-Filtrar")
        print("3-Salir")
        opcion = int(input())
        if(opcion==1):
            agregar()
        elif(opcion==2):
            filtrar()
        elif(opcion==3):
            salir()
            break
        else:
            print("La opción ingresada no es valida, ingrese una opción válida")

#-----Fin Menu-----

#----MAIN------
menu()
#----FIN MAIN-----


# 主业务逻辑中的视图和路由的定义
from flask import Flask, render_template, request, session, redirect
# 导入蓝图程序，用于构建路由
from . import cart
# 导入db，用于操作数据库
from .. import db
# 导入实体类，用于操作数据库
from ..models import *
from ..seller.redis_store import r
import json
# 返回购物车数据

path = "http://127.0.0.1:5000/static/image/"
@cart.route('/get_cart')
def get_cart():
    token = request.args.get("token")
    if not r.get(token):
        return "登录失效",403
    cid = r.get(token)
    cart = Cart.query.filter_by(customer_id=cid).first()
    cp_products = cart.cp_products.all()
    products = []
    for i in cp_products:
        products.append(i.products)
    store = []
    product = []
    for i in products:
        pd = {}
        seller = i.seller
        if seller not in store:
            store.append(seller)
        num = Cprelate.query.filter_by(
            product_id=i.id, cart_id=cart.id).first().num
        pd['id'] = i.id
        pd['store_id'] = seller.id
        pd['url'] = path + i.pictures.filter_by(type_id=1).first().picture_name
        pd['text'] = i.description
        pd['price'] = i.price
        pd['remaining'] = i.remain
        pd['product_checked'] = True
        if num == 1:
            pd['delStatus'] = 'disabled'
            pd['addStatus'] = 'normal'
        elif num == i.remain:
            pd['delStatus'] = 'normal'
            pd['addStatus'] = 'disabled'
        else:
            pd['delStatus'] = 'normal'
            pd['addStatus'] = 'normal'
        pd['quantity'] = num
        product.append(pd)

    def make_store(i):
        s = {}
        s['name'] = i.seller_name
        s['kind_checked'] = False
        s['store_id'] = i.id
        return s
    store = list(map(make_store, store))

    res = {}
    res['store'] = store
    res['product'] = product
    res["cart_id"] = cart.id
    return json.dumps(res)

@cart.route('/delete_cart', methods=['GET','POST'])
def delete_cart():
    if request.method == "POST":
        token = request.form.get("token")
        if not r.get(token):
            return "登录失效",403
        cid = r.get(token)
        pid = json.loads(request.form.get('pid'))
        cart = Cart.query.filter_by(customer_id=cid).first()
        for cprel in cart.cp_products:
            if cprel.product_id in pid:
                db.session.delete(cprel)
        return '成功删除'

@cart.route('/add_cart', methods=["GET", "POST"])
def add_cart():
    if request.method=="GET":
        sell_num = request.args.get("quantity")
        product_id = request.args.get("product_id")
        token = request.args.get("token")
        if not r.get(token):
            return "登录失效",403
        customer_id = r.get(token)
        cart = Cart.query.filter_by(customer_id=customer_id).first()
        cprelate = Cprelate.query.filter_by(cart_id=cart.id,product_id=product_id).first()
        if not cprelate:
            cprelate = Cprelate(product_id=product_id, cart_id=cart.id, num=sell_num)
        else:
            cprelate.num = sell_num
        db.session.add(cprelate)
        return "购物车加入成功"

@cart.route("/update_cart", methods=["GET", "POST"])
def update_cart():
    if request.method=="POST":
        cart_id = request.form.get("cart_id")
        products = json.loads(request.form.get("product"))
        for product in products:
            cprelate = Cprelate.query.filter_by(product_id=product['id'],cart_id=cart_id).first()
            if product["quantity"] == cprelate.num:
                return "不用更新"
            else:
                cprelate.num = product["quantity"]
                db.session.add(cprelate)
                return "更新成功"
        return "购物车为空了"


from rest_framework import serializers

from targets.models import Target

class TargetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Target
        fields = [
            'id',
            'user_id',
            'months',
            'title',
            'completed',
            'description',
            'price',
            'pros',
            'minuses',
            'images',
            'updated_at',
            'created_at'
        ]


import copy
import random

from eth_utils import ValidationError
from eth_utils.toolz import assoc
import pytest

from eth2._utils.bls import bls
from eth2.beacon.attestation_helpers import (
    is_slashable_attestation_data,
    validate_indexed_attestation,
    validate_indexed_attestation_aggregate_signature,
)
from eth2.beacon.helpers import compute_signing_root, get_domain
from eth2.beacon.signature_domain import SignatureDomain
from eth2.beacon.types.attestation_data import AttestationData
from eth2.beacon.types.attestations import Attestation, IndexedAttestation
from eth2.beacon.types.forks import Fork

@pytest.mark.parametrize(("validator_count",), [(40,)])
def test_verify_indexed_attestation_signature(
    slots_per_epoch,
    validator_count,
    genesis_state,
    config,
    privkeys,
    sample_attestation_params,
    sample_indexed_attestation_params,
    sample_fork_params,
):
    state = genesis_state.set("fork", Fork.create(**sample_fork_params))
    attestation = Attestation.create(**sample_attestation_params)

    valid_params = _correct_indexed_attestation_params(
        validator_count,
        attestation,
        sample_indexed_attestation_params,
        privkeys,
        state,
        config,
    )
    valid_votes = IndexedAttestation.create(**valid_params)

    validate_indexed_attestation_aggregate_signature(
        state, valid_votes, slots_per_epoch
    )

    invalid_params = _corrupt_signature(slots_per_epoch, valid_params, state.fork)
    invalid_votes = IndexedAttestation.create(**invalid_params)
    with pytest.raises(ValidationError):
        validate_indexed_attestation_aggregate_signature(
            state, invalid_votes, slots_per_epoch
        )

def _get_indices_and_signatures(validator_count, state, config, attestation, privkeys):
    num_indices = 5
    assert validator_count >= num_indices
    indices = random.sample(range(validator_count), num_indices)
    indices.sort()

    privkeys = [privkeys[i] for i in indices]
    domain = get_domain(
        state=state,
        signature_domain=SignatureDomain.DOMAIN_BEACON_ATTESTER,
        slots_per_epoch=config.SLOTS_PER_EPOCH,
    )
    signing_root = compute_signing_root(attestation.data, domain)

    signatures = tuple(map(lambda key: bls.sign(key, signing_root), privkeys))
    return (indices, signatures)

def _run_verify_indexed_vote(
    slots_per_epoch, params, state, max_validators_per_committee, should_succeed
):
    votes = IndexedAttestation.create(**params)
    if should_succeed:
        validate_indexed_attestation(
            state, votes, max_validators_per_committee, slots_per_epoch
        )
    else:
        with pytest.raises(ValidationError):
            validate_indexed_attestation(
                state, votes, max_validators_per_committee, slots_per_epoch
            )

def _correct_indexed_attestation_params(
    validator_count, attestation, params, privkeys, state, config
):
    valid_params = copy.deepcopy(params)

    (attesting_indices, signatures) = _get_indices_and_signatures(
        validator_count, state, config, attestation, privkeys
    )

    valid_params["attesting_indices"] = attesting_indices

    signature = bls.aggregate(*signatures)

    valid_params["signature"] = signature

    return valid_params

def _corrupt_attesting_indices(params):
    corrupt_attesting_indices = (
        params["attesting_indices"][1],
        params["attesting_indices"][0],
    ) + tuple(params["attesting_indices"][2:])

    return assoc(params, "attesting_indices", corrupt_attesting_indices)

def _corrupt_attesting_indices_max(max_validators_per_committee, params):
    corrupt_attesting_indices = [i for i in range(max_validators_per_committee + 1)]
    return assoc(params, "attesting_indices", corrupt_attesting_indices)

def _corrupt_signature(slots_per_epoch, params, fork):
    return assoc(params, "signature", b"\x12" * 96)

@pytest.mark.parametrize(("validator_count",), [(40,)])
@pytest.mark.parametrize(
    ("param_mapper", "should_succeed", "needs_fork", "is_testing_max_length"),
    [
        (lambda params: params, True, False, False),
        (_corrupt_attesting_indices, False, False, False),
        (_corrupt_signature, False, True, False),
    ],
)
def test_validate_indexed_attestation(
    slots_per_epoch,
    validator_count,
    genesis_state,
    param_mapper,
    should_succeed,
    needs_fork,
    is_testing_max_length,
    privkeys,
    sample_indexed_attestation_params,
    sample_fork_params,
    max_validators_per_committee,
    config,
):
    state = genesis_state.set("fork", Fork.create(**sample_fork_params))
    attestation = IndexedAttestation.create(**sample_indexed_attestation_params)

    params = _correct_indexed_attestation_params(
        validator_count,
        attestation,
        sample_indexed_attestation_params,
        privkeys,
        state,
        config,
    )
    if needs_fork:
        params = param_mapper(slots_per_epoch, params, state.fork)
    elif is_testing_max_length:
        params = param_mapper(max_validators_per_committee, params)

    else:
        params = param_mapper(params)
    _run_verify_indexed_vote(
        slots_per_epoch, params, state, max_validators_per_committee, should_succeed
    )

@pytest.mark.parametrize(("validator_count",), [(40,)])
def test_verify_indexed_attestation_after_fork(
    genesis_state,
    slots_per_epoch,
    validator_count,
    privkeys,
    sample_attestation_params,
    sample_indexed_attestation_params,
    config,
    max_validators_per_committee,
):
    # Test that indexed data is still valid after fork
    # Indexed data slot = 10, fork slot = 15, current slot = 20
    past_fork_params = {
        "previous_version": (0).to_bytes(4, "little"),
        "current_version": (1).to_bytes(4, "little"),
        "epoch": 15,
    }

    state = genesis_state.mset("slot", 20, "fork", Fork.create(**past_fork_params))
    attestation = Attestation.create(**sample_attestation_params)

    valid_params = _correct_indexed_attestation_params(
        validator_count,
        attestation,
        sample_indexed_attestation_params,
        privkeys,
        state,
        config,
    )
    _run_verify_indexed_vote(
        slots_per_epoch, valid_params, state, max_validators_per_committee, True
    )

@pytest.mark.parametrize(
    ("is_double_vote," "is_surround_vote,"),
    [(False, False), (False, True), (True, False), (True, True)],
)
def test_is_slashable_attestation_data(
    sample_attestation_data_params, is_double_vote, is_surround_vote
):
    data_1 = AttestationData.create(**sample_attestation_data_params)
    data_2 = AttestationData.create(**sample_attestation_data_params)

    if is_double_vote:
        data_2 = data_2.set(
            "beacon_block_root",
            (int.from_bytes(data_1.beacon_block_root, "little") + 1).to_bytes(
                32, "little"
            ),
        )

    if is_surround_vote:
        data_1 = data_1.transform(
            ("source", "epoch"), data_2.source.epoch - 1
        ).transform(("target", "epoch"), data_2.target.epoch + 1)

    assert is_slashable_attestation_data(data_1, data_2) == (
        is_double_vote or is_surround_vote
    )


import argparse
import os

def get_parser():
    parser = argparse.ArgumentParser(
        description="change extension of files in a working directory"
    )
    parser.add_argument(
        "work_dir",
        metavar="WORK_DIR",
        type=str,
        nargs=1,
        help="the directory where to change extension",
    )
    parser.add_argument(
        "old_ext", metavar="OLD_EXT", type=str, nargs=1, help="old extension"
    )
    parser.add_argument(
        "new_ext", metavar="NEW_EXT", type=str, nargs=1, help="new extension"
    )
    parser.add_argument(
        "ant_arg", metavar="ANT_ARG", type=str, nargs=1, help="another extension"
    )
    return parser

def main():
    """
    This will be called if the script is directly invoked.
    """
    # adding command line argument
    parser = get_parser()
    args = vars(parser.parse_args())
    print(type(args))
    print(args)

if __name__ == "__main__":
    main()


from openerp import models, fields, api, modules, _, SUPERUSER_ID
from datetime import datetime

FIELDS_BLACKLIST = [
    'id', 'create_uid', 'create_date', 'write_uid', 'write_date',
    'display_name', '__last_update',
]

EMPTY_DICT = {}

class AuditTrackedFieldsRule(models.Model):
    _name = 'audit.tracked_fields.rule'
    _description = "Audit Tracked Fields - Settings"
    _rec_name = "model_id"

    @api.one
    @api.depends('field_ids')
    def _compute_field_names(self):
        field_names = [ f.name for f in self.field_ids ]
        self.field_names = str(field_names or [])

    model_id = fields.Many2one('ir.model', "Model", required=True, ondelete='cascade', help="Select model for which you want to track a field.")
    model = fields.Char("Model Technical Name", related='model_id.model', store=True, readonly=True)
    field_ids = fields.Many2many('ir.model.fields', 'audit_tracked_fields_rule_ir_fields_rel', 'field_id', 'rule_id',
                                  string="Tracking Fields", required=True, domain="[('model_id','=',model_id)]")
    field_names = fields.Char('Fields Names List', compute='_compute_field_names', store=True)
    log_create = fields.Boolean("Log Creates", default=True)
    log_write = fields.Boolean("Log Writes", default=True)
    log_unlink = fields.Boolean("Log Deletes", default=True)
    state = fields.Selection([('draft', "Draft"), ('subscribed', "Subscribed")],
        string="State", required=True, default='subscribed')

    _sql_constraints = [
        ('model_uniq', 'unique(model_id)',
         ("There is already a configuration defined on this model\n"
          "You cannot define another: please edit the existing one."))
    ]

    def _register_hook(self, cr, ids=None):
        super(AuditTrackedFieldsRule, self)._register_hook(cr)
        if not hasattr(self.pool, '_audit_tracked_field_cache'):
            self.pool._audit_tracked_field_cache = {}
        if not hasattr(self.pool, '_audit_tracked_field_model_cache'):
            self.pool._audit_tracked_field_model_cache = {}
        if ids is None:
            ids = self.search(cr, SUPERUSER_ID, [('state', '=', 'subscribed')])
        return self._patch_methods(cr, SUPERUSER_ID, ids)

    @api.multi
    def _patch_methods(self):
        """ Patch ORM methods of models defined in rules to log their calls. """
        updated = False
        model_cache = self.pool._audit_tracked_field_model_cache
        for rule in self:
            if rule.state != 'subscribed':
                continue
            if not self.pool.get(rule.model_id.model):
                # ignore rules for models not loadable currently
                continue
            model_cache[rule.model_id.model] = rule.model_id.id
            model_model = self.env[rule.model_id.model]
            #   -> create
            check_attr = 'audit_tracked_fields_ruled_create'
            if getattr(rule, 'log_create') \
                    and not hasattr(model_model, check_attr):
                model_model._patch_method('create', rule._make_create())
                setattr(model_model, check_attr, True)
                updated = True
            #   -> write
            check_attr = 'audit_tracked_fields_ruled_write'
            if getattr(rule, 'log_write') \
                    and not hasattr(model_model, check_attr):
                model_model._patch_method('write', rule._make_write())
                setattr(model_model, check_attr, True)
                updated = True
            #   -> unlink
            check_attr = 'audit_tracked_fields_ruled_unlink'
            if getattr(rule, 'log_unlink') \
                    and not hasattr(model_model, check_attr):
                model_model._patch_method('unlink', rule._make_unlink())
                setattr(model_model, check_attr, True)
                updated = True
        return updated

    @api.multi
    def _revert_methods(self):
        """Restore original ORM methods of models defined in rules."""
        updated = False
        for rule in self:
            model_model = self.env[rule.model_id.model]
            for method in ['create','write','unlink']:
                if getattr(rule, 'log_%s' % method) and hasattr(
                        getattr(model_model, method), 'origin'):
                    model_model._revert_method(method)
                    updated = True
        if updated:
            modules.registry.RegistryManager.signal_registry_change(
                self.env.cr.dbname)

    @api.model
    def create(self, vals):
        """ Update the registry when a new rule is created. """
        new_record = super(AuditTrackedFieldsRule, self).create(vals)
        if self._model._register_hook(self.env.cr, new_record.ids):
            modules.registry.RegistryManager.signal_registry_change(
                self.env.cr.dbname)
        return new_record

    @api.multi
    def write(self, vals):
        """ Update the registry when existing rules are updated. """
        super(AuditTrackedFieldsRule, self).write(vals)
        if self._model._register_hook(self.env.cr, self.ids):
            modules.registry.RegistryManager.signal_registry_change(
                self.env.cr.dbname)
        return True

    @api.multi
    def unlink(self):
        """ Unsubscribe rules before removing them. """
        self.unsubscribe()
        return super(AuditTrackedFieldsRule, self).unlink()

    @api.multi
    def _make_create(self):
        self.ensure_one()

        @api.model
        @api.returns('self', lambda value: value.id)
        def do_create(self, vals, **kwargs):
            rule_model = self.env['audit.tracked_fields.rule']
            do_logging = rule_model.search([('model','=',self._name)], limit=1)
            if do_logging:
                self = self.with_context(audit_tracked_fields_disabled=True)
            new_record = do_create.origin(self, vals, **kwargs)
            if do_logging:
                new_values = dict(
                    (d['id'], d) for d in new_record.sudo()
                    .with_context(prefetch_fields=False).read(eval(do_logging.field_names)))
                rule_model.sudo().create_logs(
                    self.env.uid, self._name, new_record.ids,
                    'create', None, new_values)
            return new_record

        return do_create

    @api.multi
    def _make_write(self):
        self.ensure_one()

        @api.multi
        def do_write(self, vals, **kwargs):
            rule_model = self.env['audit.tracked_fields.rule']
            record = rule_model.search([('model','=',self._name)], limit=1)
            read_fields = record and [ f for f in vals.keys() if f in eval(record.field_names)] or []
            do_logging = bool(read_fields)
            if do_logging:
                self = self.with_context(audit_tracked_fields_disabled=True)
                old_values = dict(
                    (d['id'], d) for d in self.sudo()
                    .with_context(prefetch_fields=False).read(read_fields))
            result = do_write.origin(self, vals, **kwargs)
            if do_logging:
                new_values = dict(
                    (d['id'], d) for d in self.sudo()
                    .with_context(prefetch_fields=False).read(read_fields))
                rule_model.sudo().create_logs(
                    self.env.uid, self._name, self.ids,
                    'write', old_values, new_values)
            return result

        return do_write

    @api.multi
    def _make_unlink(self):
        self.ensure_one()

        @api.multi
        def do_unlink(self, **kwargs):
            do_logging = self.env['audit.tracked_fields.log'].sudo().search_count([('model','=',self._name),('res_id','in',self.ids)])
            if do_logging:
                rule_model = self.env['audit.tracked_fields.rule']
                self = self.with_context(audit_tracked_fields_disabled=True)
                rule_model.sudo().create_logs(
                    self.env.uid, self._name, self.ids, 'unlink', {}, {})
            return do_unlink.origin(self, **kwargs)

        return do_unlink

    def create_logs(self, uid, res_model, res_ids, method,
                    old_values=None, new_values=None):
        """Create logs. `old_values` and `new_values` are dictionaries, e.g:
            {RES_ID: {'FIELD': VALUE, ...}}
        """
        if 'lang' not in (self._context or {}):
            self = self.with_context(lang=self.env.user.lang)

        if old_values is None:
            old_values = EMPTY_DICT
        if new_values is None:
            new_values = EMPTY_DICT

        def _get_field_value_text(value, field_def):
            field_type = field_def.get('ttype', False)
            if field_type == 'many2one':
                return value[1] if (isinstance(value, tuple) and len(value)==2) else value
            if field_type == 'selection':
                try:
                    return _(dict(self.env[field_def.get('model')]._fields[field_def['name']].selection).get(value, '')) or value
                except:
                    return value
            return value

        log_model = self.env['audit.tracked_fields.log'].sudo()
        IrModel = self.env['ir.model']
        model_model = self.env[res_model]
        for res_id in res_ids:
            name = model_model.browse(res_id).name_get()
            res_name = name and name[0] and name[0][1]
            vals = {
                'name': res_name,
                'model_id': self.pool._audit_tracked_field_model_cache[res_model],
                'res_id': res_id,
                'user_id': uid,
            }
            if method == 'create':
                fields_list = new_values.get(res_id, EMPTY_DICT).keys()
                for field_name in fields_list:
                    if field_name in FIELDS_BLACKLIST:
                        continue
                    field = self._get_field(IrModel.browse(vals['model_id']), field_name)
                    if field:
                        if field['relation'] and '2many' in field['ttype']:
                            continue
                        log_vals = vals.copy()
                        log_vals.update({
                            'field_id': field['id'],
                            'new_value': new_values[res_id][field['name']],
                            'new_value_text': _get_field_value_text(new_values[res_id][field['name']], field),
                            'description': _('Record Created')
                        })
                        log_model.create(log_vals)
            if method == 'write':
                fields_list = old_values.get(res_id, EMPTY_DICT).keys()
                for field_name in fields_list:
                    if field_name in FIELDS_BLACKLIST:
                        continue
                    field = self._get_field(IrModel.browse(vals['model_id']), field_name)
                    if field:
                        if field['relation'] and '2many' in field['ttype']:
                            continue
                        log_vals = vals.copy()
                        log_vals.update({
                            'field_id': field['id'],
                            'old_value': old_values[res_id][field['name']],
                            'new_value': new_values[res_id][field['name']],
                            'old_value_text': _get_field_value_text(old_values[res_id][field['name']], field),
                            'new_value_text': _get_field_value_text(new_values[res_id][field['name']], field),
                            'description': _('Record Updated')
                        })
                        previous_log = log_model.search([('model_id','=',vals['model_id']),
                                                         ('res_id','=',res_id),
                                                         ('field_id','=',field['id'])], limit=1)
                        new_log = log_model.create(log_vals)
                        if previous_log:
                            elapsed_time = (datetime.strptime(new_log.create_date, '%Y-%m-%d %H:%M:%S') - datetime.strptime(previous_log.create_date, '%Y-%m-%d %H:%M:%S')).total_seconds() / 3600.0
                            previous_log.write({'elapsed_time': elapsed_time, 'elapsed_time_set': True})
            if method == 'unlink':
                log_vals = vals.copy()
                log_vals.update({
                    'old_value': False,
                    'old_value_text': False,
                    'new_value': False,
                    'new_value_text': False,
                    'field_id': False,
                    'description': _('Record Deleted')
                })
                previous_logs = log_model.search([('model_id','=',vals['model_id']),('res_id','=',res_id),('elapsed_time_set','=',False)])
                if previous_logs:
                    new_log = previous_logs[0].copy(log_vals)
                    updated_field_ids = []
                    for log in previous_logs:
                        if log.field_id.id not in updated_field_ids:
                            updated_field_ids.append(log.field_id.id)
                            elapsed_time = (datetime.strptime(new_log.create_date, '%Y-%m-%d %H:%M:%S') - datetime.strptime(log.create_date, '%Y-%m-%d %H:%M:%S')).total_seconds() / 3600.0
                            log.write({'elapsed_time': elapsed_time, 'elapsed_time_set': True})

    def _get_field(self, model, field_name):
        cache = self.pool._audit_tracked_field_cache
        if field_name not in cache.get(model.model, {}):
            cache.setdefault(model.model, {})
            rule_model = self.env['audit.tracked_fields.rule'].sudo()
            record = rule_model.search([('model_id','=',model.id)], limit=1)
            do_logging = record and field_name in eval(record.field_names)
            if not do_logging:
                cache[model.model][field_name] = False
            else:
                # - search the field in the current model and those it inherits
                field_model = self.env['ir.model.fields'].sudo()
                all_model_ids = [model.id]
                all_model_ids.extend(model.inherited_model_ids.ids)
                field = field_model.search(
                    [('model_id', 'in', all_model_ids), ('name', '=', field_name)])
                # The field can be a dummy one
                if not field:
                    cache[model.model][field_name] = False
                else:
                    field_data = field.read(load='_classic_write')[0]
                    cache[model.model][field_name] = field_data
        return cache[model.model][field_name]

    @api.multi
    def unsubscribe(self):
        """Unsubscribe Auditing Rule on model."""
        # Revert patched methods
        self._revert_methods()
        self.write({'state': 'draft'})
        return True

class AuditTrackedFieldsLog(models.Model):
    _name = 'audit.tracked_fields.log'
    _description = "Audit Tracked Fields - Log"
    _order = "create_date desc"

    @api.one
    @api.depends('elapsed_time_set')
    def _compute_elapsed_time(self):
        elapsed_time_computed = 0.0
        if self.field_id:
            if not self.elapsed_time_set:
                elapsed_time_computed = (datetime.now() - datetime.strptime(self.create_date, '%Y-%m-%d %H:%M:%S')).total_seconds() / 3600.0
            else:
                elapsed_time_computed = self.elapsed_time
        self.elapsed_time_computed = elapsed_time_computed

    name = fields.Char("Resource Name")
    model_id = fields.Many2one('ir.model', string="Model", required=True, index=True, ondelete='cascade')
    model = fields.Char("Model Technical Name", related='model_id.model', store=True, readonly=True)
    field_id = fields.Many2one('ir.model.fields', string="Field", required=False, index=True, ondelete='cascade')
    field_name = fields.Char("Field Technical Name", related='field_id.name', readonly=True)
    old_value = fields.Text("Old Value")
    new_value = fields.Text("New Value")
    old_value_text = fields.Text("Old Value")
    new_value_text = fields.Text("New Value")
    elapsed_time = fields.Float('Elapsed Time', default=0.0, help="Elapsed time in hours with the field value of record.")
    elapsed_time_computed = fields.Float('Elapsed Time', compute="_compute_elapsed_time", help="Elapsed time in hours with the field value of record.")
    elapsed_time_set = fields.Boolean("Elapsed Time Set", readonly=True, default=False)
    res_id = fields.Integer("Related Resource ID")
    user_id = fields.Many2one('res.users', string="User", help="User that makes field modification")
    description = fields.Text("Additional Information")

    def fields_get(self, cr, uid, allfields=None, context=None, write_access=True, attributes=None):
        res = super(AuditTrackedFieldsLog, self).fields_get(cr, uid, allfields=allfields, context=context, write_access=write_access, attributes=attributes)
        if 'res_id' in res:
            res['res_id'].update({'type': 'char', 'searchable': False})
        return res

    @api.model
    def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
        result = super(AuditTrackedFieldsLog, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
        if 'elapsed_time_computed' in fields or 'elapsed_time' in fields:
            cr = self.env.cr
            for line in result:
                line_domain = line.get('__domain', domain)
                line_domain_ets = line_domain + [('elapsed_time_set','=',True)]
                line_domain_no_ets = line_domain + [('elapsed_time_set','=',False)]
                tables = 'audit_tracked_fields_log'
                where_clause = ""
                where_clause_params = []
                if line_domain:
                    query = self._where_calc(line_domain_ets)
                    tables, where_clause, where_clause_params = query.get_sql()

                query = """ SELECT sum(elapsed_time) as et
                            FROM %s """ % (tables,)
                if where_clause:
                    query += " WHERE %s " % (where_clause,)
                    cr.execute(query, tuple(where_clause_params))
                else:
                    cr.execute(query)
                __res = cr.fetchone()
                elapsed_time = __res and __res[0] or 0
                for row in self.search(line_domain_no_ets):
                    elapsed_time += row.elapsed_time_computed

                if 'elapsed_time' in fields:
                    line['elapsed_time'] = elapsed_time
                if 'elapsed_time_computed' in fields:
                    line['elapsed_time_computed'] = elapsed_time

        return result

    @api.multi
    def open_related_record(self):
        self.ensure_one()
        return self.pool[self.model].get_formview_action(self._cr, self._uid, self.res_id, context=self._context)