import os
import sys
import json
import logging

from time import time

from PyQt5.Qt import PYQT_VERSION_STR
from PyQt5.QtCore import (
    QT_VERSION_STR, QStandardPaths, QSysInfo, QLocale, QLibraryInfo,
    QTranslator
)

from novelwriter.error import logException, formatException
from novelwriter.common import splitVersionNumber, formatTimeStamp, NWConfigParser
from novelwriter.constants import nwFiles, nwUnicode

logger = logging.getLogger(__name__)

class Config:

    LANG_NW   = 1
    LANG_PROJ = 2

    def __init__(self):

        # Set Application Variables
        self.appName   = "novelWriter"
        self.appHandle = self.appName.lower()

        # Config Error Handling
        self.hasError  = False  # True if the config class encountered an error
        self.errData   = []     # List of error messages

        # Set Paths
        self.cmdOpen   = None   # Path from command line for project to be opened on launch
        self.confPath  = None   # Folder where the config is saved
        self.confFile  = None   # The config file name
        self.dataPath  = None   # Folder where app data is stored
        self.lastPath  = None   # The last user-selected folder (browse dialogs)
        self.appPath   = None   # The full path to the novelwriter package folder
        self.appRoot   = None   # The full path to the novelwriter root folder
        self.appIcon   = None   # The full path to the novelwriter icon file
        self.assetPath = None   # The full path to the novelwriter/assets folder
        self.pdfDocs   = None   # The location of the PDF manual, if it exists

        # Runtime Settings and Variables
        self.confChanged = False  # True whenever the config has chenged, false after save

        # General
        self.guiTheme    = ""     # GUI theme
        self.guiSyntax   = ""     # Syntax theme
        self.guiIcons    = ""     # Icon theme
        self.guiFont     = ""     # Defaults to system default font
        self.guiFontSize = 11     # Is overridden if system default is loaded
        self.guiScale    = 1.0    # Set automatically by Theme class
        self.lastNotes   = "0x0"  # The latest release notes that have been shown

        self.setDefaultGuiTheme()
        self.setDefaultSyntaxTheme()
        self.setDefaultIconTheme()

        # Localisation
        self.qLocal     = QLocale.system()
        self.guiLang    = self.qLocal.name()
        self.qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
        self.nwLangPath = None
        self.qtTrans    = {}

        # Sizes
        self.winGeometry   = [1200, 650]
        self.prefGeometry  = [700, 615]
        self.treeColWidth  = [200, 50, 30]
        self.novelColWidth = [200, 50]
        self.projColWidth  = [200, 60, 140]
        self.mainPanePos   = [300, 800]
        self.docPanePos    = [400, 400]
        self.viewPanePos   = [500, 150]
        self.outlnPanePos  = [500, 150]
        self.isFullScreen  = False

        # Features
        self.hideVScroll = False  # Hide vertical scroll bars on main widgets
        self.hideHScroll = False  # Hide horizontal scroll bars on main widgets
        self.emphLabels  = True   # Add emphasis to H1 and H2 item labels

        # Project
        self.autoSaveProj = 60  # Interval for auto-saving project in seconds
        self.autoSaveDoc  = 30  # Interval for auto-saving document in seconds

        # Text Editor
        self.textFont        = None   # Editor font
        self.textSize        = 12     # Editor font size
        self.textWidth       = 600    # Editor text width
        self.textMargin      = 40     # Editor/viewer text margin
        self.tabWidth        = 40     # Editor tabulator width

        self.focusWidth      = 800    # Focus Mode text width
        self.hideFocusFooter = False  # Hide document footer in Focus Mode
        self.showFullPath    = True   # Show full document path in editor header
        self.autoSelect      = True   # Auto-select word when applying format with no selection

        self.doJustify       = False  # Justify text
        self.showTabsNSpaces = False  # Show tabs and spaces in edior
        self.showLineEndings = False  # Show line endings in editor
        self.showMultiSpaces = True   # Highlight multiple spaces in the text

        self.doReplace       = True   # Enable auto-replace as you type
        self.doReplaceSQuote = True   # Smart single quotes
        self.doReplaceDQuote = True   # Smart double quotes
        self.doReplaceDash   = True   # Replace multiple hyphens with dashes
        self.doReplaceDots   = True   # Replace three dots with ellipsis

        self.scrollPastEnd   = 25     # Number of lines to scroll past end of document
        self.autoScroll      = False  # Typewriter-like scrolling
        self.autoScrollPos   = 30     # Start point for typewriter-like scrolling

        self.wordCountTimer  = 5.0    # Interval for word count update in seconds
        self.bigDocLimit     = 800    # Size threshold for heavy editor features in kilobytes
        self.incNotesWCount  = True   # The status bar word count includes notes

        self.highlightQuotes = True   # Highlight text in quotes
        self.allowOpenSQuote = False  # Allow open-ended single quotes
        self.allowOpenDQuote = True   # Allow open-ended double quotes
        self.highlightEmph   = True   # Add colour to text emphasis

        self.stopWhenIdle    = True   # Stop the status bar clock when the user is idle
        self.userIdleTime    = 300    # Time of inactivity to consider user idle

        # User-Selected Symbols
        self.fmtApostrophe   = nwUnicode.U_RSQUO
        self.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO]
        self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO]
        self.fmtPadBefore    = ""
        self.fmtPadAfter     = ""
        self.fmtPadThin      = False

        # Spell Checking
        self.spellLanguage = None

        # Search Bar Switches
        self.searchCase     = False
        self.searchWord     = False
        self.searchRegEx    = False
        self.searchLoop     = False
        self.searchNextFile = False
        self.searchMatchCap = False

        # Backup
        self.backupPath      = ""
        self.backupOnClose   = False
        self.askBeforeBackup = True

        # State
        self.showRefPanel = True  # The reference panel for the viewer is visible
        self.viewComments = True  # Comments are shown in the viewer
        self.viewSynopsis = True  # Synopsis is shown in the viewer

        # Check Qt5 Versions
        verQt = splitVersionNumber(QT_VERSION_STR)
        self.verQtString = QT_VERSION_STR
        self.verQtMajor  = verQt[0]
        self.verQtMinor  = verQt[1]
        self.verQtPatch  = verQt[2]
        self.verQtValue  = verQt[3]

        verQt = splitVersionNumber(PYQT_VERSION_STR)
        self.verPyQtString = PYQT_VERSION_STR
        self.verPyQtMajor  = verQt[0]
        self.verPyQtMinor  = verQt[1]
        self.verPyQtPatch  = verQt[2]
        self.verPyQtValue  = verQt[3]

        # Check Python Version
        self.verPyString = sys.version.split()[0]
        self.verPyMajor  = sys.version_info[0]
        self.verPyMinor  = sys.version_info[1]
        self.verPyPatch  = sys.version_info[2]
        self.verPyHexVal = sys.hexversion

        # Check OS Type
        self.osType    = sys.platform
        self.osLinux   = False
        self.osWindows = False
        self.osDarwin  = False
        self.osUnknown = False
        if self.osType.startswith("linux"):
            self.osLinux = True
        elif self.osType.startswith("darwin"):
            self.osDarwin = True
        elif self.osType.startswith("win32"):
            self.osWindows = True
        elif self.osType.startswith("cygwin"):
            self.osWindows = True
        else:
            self.osUnknown = True

        # Other System Info
        self.hostName  = "Unknown"
        self.kernelVer = "Unknown"

        # Packages
        self.hasEnchant = False  # The pyenchant package

        # Recent Cache
        self.recentProj = {}

        return

    ##
    #  Methods
    ##

    def pxInt(self, theSize):
        """Used to scale fixed gui sizes by the screen scale factor.
        This function returns an int, which is always rounded down.
        """
        return int(theSize*self.guiScale)

    def rpxInt(self, theSize):
        """Used to un-scale fixed gui sizes by the screen scale factor.
        This function returns an int, which is always rounded down.
        """
        return int(theSize/self.guiScale)

    ##
    #  Config Actions
    ##

    def initConfig(self, confPath=None, dataPath=None):
        """Initialise the config class. The manual setting of confPath
        and dataPath is mainly intended for the test suite.
        """
        logger.debug("Initialising Config ...")
        if confPath is None:
            confRoot = QStandardPaths.writableLocation(QStandardPaths.ConfigLocation)
            self.confPath = os.path.join(os.path.abspath(confRoot), self.appHandle)
        else:
            logger.info("Setting config from alternative path: %s", confPath)
            self.confPath = confPath

        if dataPath is None:
            if self.verQtValue >= 50400:
                dataRoot = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
            else:
                dataRoot = QStandardPaths.writableLocation(QStandardPaths.DataLocation)
            self.dataPath = os.path.join(os.path.abspath(dataRoot), self.appHandle)
        else:
            logger.info("Setting data path from alternative path: %s", dataPath)
            self.dataPath = dataPath

        logger.verbose("Config path: %s", self.confPath)
        logger.verbose("Data path: %s", self.dataPath)

        # Check Data Path Subdirs
        dataDirs = ["syntax", "themes"]
        for dataDir in dataDirs:
            dirPath = os.path.join(self.dataPath, dataDir)
            if not os.path.isdir(dirPath):
                try:
                    os.mkdir(dirPath)
                    logger.info("Created folder: %s", dirPath)
                except Exception:
                    logger.error("Could not create folder: %s", dirPath)
                    logException()

        self.confFile = self.appHandle+".conf"
        self.lastPath = os.path.expanduser("~")
        self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
        self.appRoot = os.path.abspath(os.path.join(self.appPath, os.path.pardir))

        if os.path.isfile(self.appRoot):
            # novelWriter is packaged as a single file, so the app and
            # root paths are the same, and equal to the folder that
            # contains the single executable.
            self.appRoot = os.path.dirname(self.appRoot)
            self.appPath = self.appRoot

        # Assets
        self.assetPath = os.path.join(self.appPath, "assets")
        self.appIcon   = os.path.join(self.assetPath, "icons", "novelwriter.svg")

        # Internationalisation
        self.nwLangPath = os.path.join(self.assetPath, "i18n")

        logger.debug("Assets: %s", self.assetPath)
        logger.verbose("App path: %s", self.appPath)
        logger.verbose("Last path: %s", self.lastPath)

        # If the config folder does not exist, create it.
        # This assumes that the os config folder itself exists.
        if not os.path.isdir(self.confPath):
            try:
                os.mkdir(self.confPath)
            except Exception as exc:
                logger.error("Could not create folder: %s", self.confPath)
                logException()
                self.hasError = True
                self.errData.append("Could not create folder: %s" % self.confPath)
                self.errData.append(formatException(exc))
                self.confPath = None

        # Check if config file exists
        if self.confPath is not None:
            if os.path.isfile(os.path.join(self.confPath, self.confFile)):
                # If it exists, load it
                self.loadConfig()
            else:
                # If it does not exist, save a copy of the default values
                self.saveConfig()

        # If the data folder does not exist, create it.
        # This assumes that the os data folder itself exists.
        if self.dataPath is not None:
            if not os.path.isdir(self.dataPath):
                try:
                    os.mkdir(self.dataPath)
                except Exception as exc:
                    logger.error("Could not create folder: %s", self.dataPath)
                    logException()
                    self.hasError = True
                    self.errData.append("Could not create folder: %s" % self.dataPath)
                    self.errData.append(formatException(exc))
                    self.dataPath = None

        # Host and Kernel
        if self.verQtValue >= 50600:
            self.hostName  = QSysInfo.machineHostName()
            self.kernelVer = QSysInfo.kernelVersion()

        # Load recent projects cache
        self.loadRecentCache()

        # Check the availability of optional packages
        self._checkOptionalPackages()

        if self.spellLanguage is None:
            self.spellLanguage = "en"

        # Look for a PDF version of the manual
        pdfDocs = os.path.join(self.assetPath, "manual.pdf")
        if os.path.isfile(pdfDocs):
            logger.debug("Found manual: %s", pdfDocs)
            self.pdfDocs = pdfDocs

        logger.debug("Config initialisation complete")

        return True

    def initLocalisation(self, nwApp):
        """Initialise the localisation of the GUI.
        """
        self.qLocal = QLocale(self.guiLang)
        QLocale.setDefault(self.qLocal)
        self.qtTrans = {}

        langList = [
            (self.qtLangPath, "qtbase"),  # Qt 5.x
            (self.nwLangPath, "qtbase"),  # Alternative Qt 5.x
            (self.nwLangPath, "nw"),      # novelWriter
        ]
        for lngPath, lngBase in langList:
            for lngCode in self.qLocal.uiLanguages():
                qTrans = QTranslator()
                lngFile = "%s_%s" % (lngBase, lngCode.replace("-", "_"))
                if lngFile not in self.qtTrans:
                    if qTrans.load(lngFile, lngPath):
                        logger.debug("Loaded: %s", os.path.join(lngPath, lngFile))
                        nwApp.installTranslator(qTrans)
                        self.qtTrans[lngFile] = qTrans

        return

    def listLanguages(self, lngSet):
        """List localisation files in the i18n folder. The default GUI
        language 'en_GB' is British English.
        """
        if lngSet == self.LANG_NW:
            fPre = "nw_"
            fExt = ".qm"
            langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()}
        elif lngSet == self.LANG_PROJ:
            fPre = "project_"
            fExt = ".json"
            langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()}
        else:
            return []

        for qmFile in os.listdir(self.nwLangPath):
            if not os.path.isfile(os.path.join(self.nwLangPath, qmFile)):
                continue
            if not qmFile.startswith(fPre) or not qmFile.endswith(fExt):
                continue
            qmLang = qmFile[len(fPre):-len(fExt)]
            qmName = QLocale(qmLang).nativeLanguageName().title()
            if qmLang and qmName and qmLang != "en_GB":
                langList[qmLang] = qmName

        return sorted(langList.items(), key=lambda x: x[0])

    def loadConfig(self):
        """Load preferences from file and replace default settings.
        """
        logger.debug("Loading config file")
        if self.confPath is None:
            return False

        theConf = NWConfigParser()
        cnfPath = os.path.join(self.confPath, self.confFile)
        try:
            with open(cnfPath, mode="r", encoding="utf-8") as inFile:
                theConf.read_file(inFile)
        except Exception as exc:
            logger.error("Could not load config file")
            logException()
            self.hasError = True
            self.errData.append("Could not load config file")
            self.errData.append(formatException(exc))
            return False

        # Main
        cnfSec = "Main"
        self.guiTheme    = theConf.rdStr(cnfSec, "theme", self.guiTheme)
        self.guiSyntax   = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
        self.guiIcons    = theConf.rdStr(cnfSec, "icons", self.guiIcons)
        self.guiFont     = theConf.rdStr(cnfSec, "guifont", self.guiFont)
        self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize", self.guiFontSize)
        self.lastNotes   = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
        self.guiLang     = theConf.rdStr(cnfSec, "guilang", self.guiLang)
        self.hideVScroll = theConf.rdBool(cnfSec, "hidevscroll", self.hideVScroll)
        self.hideHScroll = theConf.rdBool(cnfSec, "hidehscroll", self.hideHScroll)

        # Sizes
        cnfSec = "Sizes"
        self.winGeometry   = theConf.rdIntList(cnfSec, "geometry", self.winGeometry)
        self.prefGeometry  = theConf.rdIntList(cnfSec, "preferences", self.prefGeometry)
        self.treeColWidth  = theConf.rdIntList(cnfSec, "treecols", self.treeColWidth)
        self.novelColWidth = theConf.rdIntList(cnfSec, "novelcols", self.novelColWidth)
        self.projColWidth  = theConf.rdIntList(cnfSec, "projcols", self.projColWidth)
        self.mainPanePos   = theConf.rdIntList(cnfSec, "mainpane", self.mainPanePos)
        self.docPanePos    = theConf.rdIntList(cnfSec, "docpane", self.docPanePos)
        self.viewPanePos   = theConf.rdIntList(cnfSec, "viewpane", self.viewPanePos)
        self.outlnPanePos  = theConf.rdIntList(cnfSec, "outlinepane", self.outlnPanePos)
        self.isFullScreen  = theConf.rdBool(cnfSec, "fullscreen", self.isFullScreen)

        # Project
        cnfSec = "Project"
        self.autoSaveProj = theConf.rdInt(cnfSec, "autosaveproject", self.autoSaveProj)
        self.autoSaveDoc  = theConf.rdInt(cnfSec, "autosavedoc", self.autoSaveDoc)
        self.emphLabels   = theConf.rdBool(cnfSec, "emphlabels", self.emphLabels)

        # Editor
        cnfSec = "Editor"
        self.textFont        = theConf.rdStr(cnfSec, "textfont", self.textFont)
        self.textSize        = theConf.rdInt(cnfSec, "textsize", self.textSize)
        self.textWidth       = theConf.rdInt(cnfSec, "width", self.textWidth)
        self.textMargin      = theConf.rdInt(cnfSec, "margin", self.textMargin)
        self.tabWidth        = theConf.rdInt(cnfSec, "tabwidth", self.tabWidth)
        self.focusWidth      = theConf.rdInt(cnfSec, "focuswidth", self.focusWidth)
        self.hideFocusFooter = theConf.rdBool(cnfSec, "hidefocusfooter", self.hideFocusFooter)
        self.doJustify       = theConf.rdBool(cnfSec, "justify", self.doJustify)
        self.autoSelect      = theConf.rdBool(cnfSec, "autoselect", self.autoSelect)
        self.doReplace       = theConf.rdBool(cnfSec, "autoreplace", self.doReplace)
        self.doReplaceSQuote = theConf.rdBool(cnfSec, "repsquotes", self.doReplaceSQuote)
        self.doReplaceDQuote = theConf.rdBool(cnfSec, "repdquotes", self.doReplaceDQuote)
        self.doReplaceDash   = theConf.rdBool(cnfSec, "repdash", self.doReplaceDash)
        self.doReplaceDots   = theConf.rdBool(cnfSec, "repdots", self.doReplaceDots)
        self.scrollPastEnd   = theConf.rdInt(cnfSec, "scrollpastend", self.scrollPastEnd)
        self.autoScroll      = theConf.rdBool(cnfSec, "autoscroll", self.autoScroll)
        self.autoScrollPos   = theConf.rdInt(cnfSec, "autoscrollpos", self.autoScrollPos)
        self.fmtSingleQuotes = theConf.rdStrList(cnfSec, "fmtsinglequote", self.fmtSingleQuotes)
        self.fmtDoubleQuotes = theConf.rdStrList(cnfSec, "fmtdoublequote", self.fmtDoubleQuotes)
        self.fmtPadBefore    = theConf.rdStr(cnfSec, "fmtpadbefore", self.fmtPadBefore)
        self.fmtPadAfter     = theConf.rdStr(cnfSec, "fmtpadafter", self.fmtPadAfter)
        self.fmtPadThin      = theConf.rdBool(cnfSec, "fmtpadthin", self.fmtPadThin)
        self.spellLanguage   = theConf.rdStr(cnfSec, "spellcheck", self.spellLanguage)
        self.showTabsNSpaces = theConf.rdBool(cnfSec, "showtabsnspaces", self.showTabsNSpaces)
        self.showLineEndings = theConf.rdBool(cnfSec, "showlineendings", self.showLineEndings)
        self.showMultiSpaces = theConf.rdBool(cnfSec, "showmultispaces", self.showMultiSpaces)
        self.wordCountTimer  = theConf.rdFlt(cnfSec, "wordcounttimer", self.wordCountTimer)
        self.bigDocLimit     = theConf.rdInt(cnfSec, "bigdoclimit", self.bigDocLimit)
        self.incNotesWCount  = theConf.rdBool(cnfSec, "incnoteswcount", self.incNotesWCount)
        self.showFullPath    = theConf.rdBool(cnfSec, "showfullpath", self.showFullPath)
        self.highlightQuotes = theConf.rdBool(cnfSec, "highlightquotes", self.highlightQuotes)
        self.allowOpenSQuote = theConf.rdBool(cnfSec, "allowopensquote", self.allowOpenSQuote)
        self.allowOpenDQuote = theConf.rdBool(cnfSec, "allowopendquote", self.allowOpenDQuote)
        self.highlightEmph   = theConf.rdBool(cnfSec, "highlightemph", self.highlightEmph)
        self.stopWhenIdle    = theConf.rdBool(cnfSec, "stopwhenidle", self.stopWhenIdle)
        self.userIdleTime    = theConf.rdInt(cnfSec, "useridletime", self.userIdleTime)

        # Backup
        cnfSec = "Backup"
        self.backupPath      = theConf.rdStr(cnfSec, "backuppath", self.backupPath)
        self.backupOnClose   = theConf.rdBool(cnfSec, "backuponclose", self.backupOnClose)
        self.askBeforeBackup = theConf.rdBool(cnfSec, "askbeforebackup", self.askBeforeBackup)

        # State
        cnfSec = "State"
        self.showRefPanel   = theConf.rdBool(cnfSec, "showrefpanel", self.showRefPanel)
        self.viewComments   = theConf.rdBool(cnfSec, "viewcomments", self.viewComments)
        self.viewSynopsis   = theConf.rdBool(cnfSec, "viewsynopsis", self.viewSynopsis)
        self.searchCase     = theConf.rdBool(cnfSec, "searchcase", self.searchCase)
        self.searchWord     = theConf.rdBool(cnfSec, "searchword", self.searchWord)
        self.searchRegEx    = theConf.rdBool(cnfSec, "searchregex", self.searchRegEx)
        self.searchLoop     = theConf.rdBool(cnfSec, "searchloop", self.searchLoop)
        self.searchNextFile = theConf.rdBool(cnfSec, "searchnextfile", self.searchNextFile)
        self.searchMatchCap = theConf.rdBool(cnfSec, "searchmatchcap", self.searchMatchCap)

        # Path
        cnfSec = "Path"
        self.lastPath = theConf.rdStr(cnfSec, "lastpath", self.lastPath)

        # Check Certain Values for None
        self.spellLanguage = self._checkNone(self.spellLanguage)

        # If we're using straight quotes, disable auto-replace
        if self.fmtSingleQuotes == ["'", "'"] and self.doReplaceSQuote:
            logger.info("Using straight single quotes, so disabling auto-replace")
            self.doReplaceSQuote = False

        if self.fmtDoubleQuotes == ['"', '"'] and self.doReplaceDQuote:
            logger.info("Using straight double quotes, so disabling auto-replace")
            self.doReplaceDQuote = False

        # Check deprecated settings
        if self.guiIcons in ("typicons_colour_dark", "typicons_grey_dark"):
            self.guiIcons = "typicons_dark"
        elif self.guiIcons in ("typicons_colour_light", "typicons_grey_light"):
            self.guiIcons = "typicons_light"

        return True

    def saveConfig(self):
        """Save the current preferences to file.
        """
        logger.debug("Saving config file")
        if self.confPath is None:
            return False

        theConf = NWConfigParser()

        theConf["Main"] = {
            "timestamp":   formatTimeStamp(time()),
            "theme":       str(self.guiTheme),
            "syntax":      str(self.guiSyntax),
            "icons":       str(self.guiIcons),
            "guifont":     str(self.guiFont),
            "guifontsize": str(self.guiFontSize),
            "lastnotes":   str(self.lastNotes),
            "guilang":     str(self.guiLang),
            "hidevscroll": str(self.hideVScroll),
            "hidehscroll": str(self.hideHScroll),
        }

        theConf["Sizes"] = {
            "geometry":    self._packList(self.winGeometry),
            "preferences": self._packList(self.prefGeometry),
            "treecols":    self._packList(self.treeColWidth),
            "novelcols":   self._packList(self.novelColWidth),
            "projcols":    self._packList(self.projColWidth),
            "mainpane":    self._packList(self.mainPanePos),
            "docpane":     self._packList(self.docPanePos),
            "viewpane":    self._packList(self.viewPanePos),
            "outlinepane": self._packList(self.outlnPanePos),
            "fullscreen":  str(self.isFullScreen),
        }

        theConf["Project"] = {
            "autosaveproject": str(self.autoSaveProj),
            "autosavedoc":     str(self.autoSaveDoc),
            "emphlabels":      str(self.emphLabels),
        }

        theConf["Editor"] = {
            "textfont":        str(self.textFont),
            "textsize":        str(self.textSize),
            "width":           str(self.textWidth),
            "margin":          str(self.textMargin),
            "tabwidth":        str(self.tabWidth),
            "focuswidth":      str(self.focusWidth),
            "hidefocusfooter": str(self.hideFocusFooter),
            "justify":         str(self.doJustify),
            "autoselect":      str(self.autoSelect),
            "autoreplace":     str(self.doReplace),
            "repsquotes":      str(self.doReplaceSQuote),
            "repdquotes":      str(self.doReplaceDQuote),
            "repdash":         str(self.doReplaceDash),
            "repdots":         str(self.doReplaceDots),
            "scrollpastend":   str(self.scrollPastEnd),
            "autoscroll":      str(self.autoScroll),
            "autoscrollpos":   str(self.autoScrollPos),
            "fmtsinglequote":  self._packList(self.fmtSingleQuotes),
            "fmtdoublequote":  self._packList(self.fmtDoubleQuotes),
            "fmtpadbefore":    str(self.fmtPadBefore),
            "fmtpadafter":     str(self.fmtPadAfter),
            "fmtpadthin":      str(self.fmtPadThin),
            "spellcheck":      str(self.spellLanguage),
            "showtabsnspaces": str(self.showTabsNSpaces),
            "showlineendings": str(self.showLineEndings),
            "showmultispaces": str(self.showMultiSpaces),
            "wordcounttimer":  str(self.wordCountTimer),
            "bigdoclimit":     str(self.bigDocLimit),
            "incnoteswcount":  str(self.incNotesWCount),
            "showfullpath":    str(self.showFullPath),
            "highlightquotes": str(self.highlightQuotes),
            "allowopensquote": str(self.allowOpenSQuote),
            "allowopendquote": str(self.allowOpenDQuote),
            "highlightemph":   str(self.highlightEmph),
            "stopwhenidle":    str(self.stopWhenIdle),
            "useridletime":    str(self.userIdleTime),
        }

        theConf["Backup"] = {
            "backuppath":      str(self.backupPath),
            "backuponclose":   str(self.backupOnClose),
            "askbeforebackup": str(self.askBeforeBackup),
        }

        theConf["State"] = {
            "showrefpanel":    str(self.showRefPanel),
            "viewcomments":    str(self.viewComments),
            "viewsynopsis":    str(self.viewSynopsis),
            "searchcase":      str(self.searchCase),
            "searchword":      str(self.searchWord),
            "searchregex":     str(self.searchRegEx),
            "searchloop":      str(self.searchLoop),
            "searchnextfile":  str(self.searchNextFile),
            "searchmatchcap":  str(self.searchMatchCap),
        }

        theConf["Path"] = {
            "lastpath": str(self.lastPath),
        }

        # Write config file
        cnfPath = os.path.join(self.confPath, self.confFile)
        try:
            with open(cnfPath, mode="w", encoding="utf-8") as outFile:
                theConf.write(outFile)
            self.confChanged = False
        except Exception as exc:
            logger.error("Could not save config file")
            logException()
            self.hasError = True
            self.errData.append("Could not save config file")
            self.errData.append(formatException(exc))
            return False

        return True

    def loadRecentCache(self):
        """Load the cache file for recent projects.
        """
        if self.dataPath is None:
            return False

        self.recentProj = {}

        cacheFile = os.path.join(self.dataPath, nwFiles.RECENT_FILE)
        if not os.path.isfile(cacheFile):
            return True

        try:
            with open(cacheFile, mode="r", encoding="utf-8") as inFile:
                theData = json.load(inFile)

            for projPath, theEntry in theData.items():
                self.recentProj[projPath] = {
                    "title": theEntry.get("title", ""),
                    "time": theEntry.get("time", 0),
                    "words": theEntry.get("words", 0),
                }

        except Exception as exc:
            self.hasError = True
            self.errData.append("Could not load recent project cache")
            self.errData.append(formatException(exc))
            return False

        return True

    def saveRecentCache(self):
        """Save the cache dictionary of recent projects.
        """
        if self.dataPath is None:
            return False

        cacheFile = os.path.join(self.dataPath, nwFiles.RECENT_FILE)
        cacheTemp = os.path.join(self.dataPath, nwFiles.RECENT_FILE+"~")

        try:
            with open(cacheTemp, mode="w+", encoding="utf-8") as outFile:
                json.dump(self.recentProj, outFile, indent=2)
        except Exception as exc:
            self.hasError = True
            self.errData.append("Could not save recent project cache")
            self.errData.append(formatException(exc))
            return False

        if os.path.isfile(cacheFile):
            os.unlink(cacheFile)
        os.rename(cacheTemp, cacheFile)

        return True

    def updateRecentCache(self, projPath, projTitle, wordCount, saveTime):
        """Add or update recent cache information on a given project.
        """
        self.recentProj[os.path.abspath(projPath)] = {
            "title": projTitle,
            "time": int(saveTime),
            "words": int(wordCount),
        }
        return True

    def removeFromRecentCache(self, thePath):
        """Trying to remove a path from the recent projects cache.
        """
        if thePath in self.recentProj:
            del self.recentProj[thePath]
            logger.verbose("Removed recent: %s", thePath)
            self.saveRecentCache()
        else:
            logger.error("Unknown recent: %s", thePath)
            return False
        return True

    ##
    #  Setters
    ##

    def setConfPath(self, newPath):
        """Set the path and filename to the config file.
        """
        if newPath is None:
            return True
        if not os.path.isfile(newPath):
            logger.error("File not found, using default config path instead")
            return False
        self.confPath = os.path.dirname(newPath)
        self.confFile = os.path.basename(newPath)
        return True

    def setDataPath(self, newPath):
        """Set the data path.
        """
        if newPath is None:
            return True
        if not os.path.isdir(newPath):
            logger.error("Path not found, using default data path instead")
            return False
        self.dataPath = os.path.abspath(newPath)
        return True

    def setLastPath(self, lastPath):
        """Set the last used path (by the user).
        """
        if lastPath is None or lastPath == "":
            self.lastPath = ""
        else:
            self.lastPath = os.path.dirname(lastPath)
        return True

    def setWinSize(self, newWidth, newHeight):
        """Set the size of the main window, but only if the change is
        larger than 5 pixels. The OS window manager will sometimes
        adjust it a bit, and we don't want the main window to shrink or
        grow each time the app is opened.
        """
        newWidth = int(newWidth/self.guiScale)
        newHeight = int(newHeight/self.guiScale)
        if abs(self.winGeometry[0] - newWidth) > 5:
            self.winGeometry[0] = newWidth
            self.confChanged = True
        if abs(self.winGeometry[1] - newHeight) > 5:
            self.winGeometry[1] = newHeight
            self.confChanged = True
        return True

    def setPreferencesSize(self, newWidth, newHeight):
        """Sat the size of the Preferences dialog window.
        """
        self.prefGeometry[0] = int(newWidth/self.guiScale)
        self.prefGeometry[1] = int(newHeight/self.guiScale)
        self.confChanged = True
        return True

    def setTreeColWidths(self, colWidths):
        """Set the column widths of the main project tree.
        """
        self.treeColWidth = [int(x/self.guiScale) for x in colWidths]
        self.confChanged = True
        return True

    def setNovelColWidths(self, colWidths):
        """Set the column widths of the novel tree.
        """
        self.novelColWidth = [int(x/self.guiScale) for x in colWidths]
        self.confChanged = True
        return True

    def setProjColWidths(self, colWidths):
        """Set the column widths of the Load Project dialog.
        """
        self.projColWidth = [int(x/self.guiScale) for x in colWidths]
        self.confChanged = True
        return True

    def setMainPanePos(self, panePos):
        """Set the position of the main GUI splitter.
        """
        self.mainPanePos = [int(x/self.guiScale) for x in panePos]
        self.confChanged = True
        return True

    def setDocPanePos(self, panePos):
        """Set the position of the main editor/viewer splitter.
        """
        self.docPanePos = [int(x/self.guiScale) for x in panePos]
        self.confChanged = True
        return True

    def setViewPanePos(self, panePos):
        """Set the position of the viewer meta data splitter.
        """
        self.viewPanePos = [int(x/self.guiScale) for x in panePos]
        self.confChanged = True
        return True

    def setOutlinePanePos(self, panePos):
        """Set the position of the outline details splitter.
        """
        self.outlnPanePos = [int(x/self.guiScale) for x in panePos]
        self.confChanged = True
        return True

    def setShowRefPanel(self, checkState):
        """Set the visibility state of the reference panel.
        """
        self.showRefPanel = checkState
        self.confChanged = True
        return self.showRefPanel

    def setViewComments(self, viewState):
        """Set the visibility state of comments in the viewer.
        """
        self.viewComments = viewState
        self.confChanged = True
        return self.viewComments

    def setViewSynopsis(self, viewState):
        """Set the visibility state of synopsis comments in the viewer.
        """
        self.viewSynopsis = viewState
        self.confChanged = True
        return self.viewSynopsis

    ##
    #  Default Setters
    ##

    def setDefaultGuiTheme(self):
        """Reset the GUI theme to default value.
        """
        self.guiTheme = "default"

    def setDefaultSyntaxTheme(self):
        """Reset the syntax theme to default value.
        """
        self.guiSyntax = "default_light"

    def setDefaultIconTheme(self):
        """Reset the icon theme to default value.
        """
        self.guiIcons = "typicons_light"

    ##
    #  Getters
    ##

    def getWinSize(self):
        return [int(x*self.guiScale) for x in self.winGeometry]

    def getPreferencesSize(self):
        return [int(x*self.guiScale) for x in self.prefGeometry]

    def getTreeColWidths(self):
        return [int(x*self.guiScale) for x in self.treeColWidth]

    def getNovelColWidths(self):
        return [int(x*self.guiScale) for x in self.novelColWidth]

    def getProjColWidths(self):
        return [int(x*self.guiScale) for x in self.projColWidth]

    def getMainPanePos(self):
        return [int(x*self.guiScale) for x in self.mainPanePos]

    def getDocPanePos(self):
        return [int(x*self.guiScale) for x in self.docPanePos]

    def getViewPanePos(self):
        return [int(x*self.guiScale) for x in self.viewPanePos]

    def getOutlinePanePos(self):
        return [int(x*self.guiScale) for x in self.outlnPanePos]

    def getTextWidth(self, focusMode=False):
        if focusMode:
            return self.pxInt(max(self.focusWidth, 200))
        else:
            return self.pxInt(max(self.textWidth, 200))

    def getTextMargin(self):
        return self.pxInt(max(self.textMargin, 0))

    def getTabWidth(self):
        return self.pxInt(max(self.tabWidth, 0))

    def getErrData(self):
        """Compile and return error messages from the initialisation of
        the Config class, and clear the error buffer.
        """
        errMessage = "<br>".join(self.errData)
        self.hasError = False
        self.errData = []
        return errMessage

    ##
    #  Internal Functions
    ##

    def _packList(self, inData):
        """Pack a list of items into a comma-separated string for saving
        to the config file.
        """
        return ", ".join([str(inVal) for inVal in inData])

    def _checkNone(self, checkVal):
        """Return a NoneType if the value corresponds to None, otherwise
        return the value unchanged.
        """
        if checkVal is None:
            return None
        if isinstance(checkVal, str):
            if checkVal.lower() == "none":
                return None
        return checkVal

    def _checkOptionalPackages(self):
        """Cheks if we have the optional packages used by some features.
        """
        try:
            import enchant  # noqa: F401
            self.hasEnchant = True
            logger.debug("Checking package 'pyenchant': OK")
        except Exception:
            self.hasEnchant = False
            logger.debug("Checking package 'pyenchant': Missing")

        return

# END Class Config


# this is nima nikrouz's midterm project
#=============================================library=====================================================
from tabulate import tabulate
#=============================================library=====================================================
#=============================================roots=====================================================
realBord=(("a1","b1","c1","d1","e1","f1","g1","h1"),
      ("a2","b2","c2","d2","e2","f2","g2","h2"),
      ("a3","b3","c3","d3","e3","f3","g3","h3"),
      ("a4","b4","c4","d4","e4","f4","g4","h4"),
      ("a5","b5","c5","d5","e5","f5","g5","h5"),
      ("a6","b6","c6","d6","e6","f6","g6","h6"),
      ("a7","b7","c7","d7","e7","f7","g7","h7"),
      ("a8","b8","c8","d8","e8","f8","g8","h8"))

primeryBord=[["a1","b1","c1","d1","e1","f1","g1","h1"],
      ["a2","b2","c2","d2","e2","f2","g2","h2"],
      ["a3","b3","c3","d3","e3","f3","g3","h3"],
      ["a4","b4","c4","d4","e4","f4","g4","h4"],
      ["a5","b5","c5","d5","e5","f5","g5","h5"],
      ["a6","b6","c6","d6","e6","f6","g6","h6"],
      ["a7","b7","c7","d7","e7","f7","g7","h7"],
      ["a8","b8","c8","d8","e8","f8","g8","h8"]]
queen="Q"
full="F"
#=============================================roots=====================================================
#=============================================defs=====================================================
def column(n):
#this def is for having a list of the column we want.
    column=[]
    for item in range(8):
        column.append(primeryBord[item][n])
    return column

def orib1(mylist):
#this def is for orib list from top left to bottom right.
    i=int(mylist[0])
    j=int(mylist[1])
    orib1=[]
    while 0<=j<len(realBord) and 0<=i<len(realBord):
        orib1.append(realBord[i][j])
        i+=1
        j+=1
    i=mylist[0]-1
    j=mylist[1]-1
    while 0<=j<len(realBord) and 0<=i<len(realBord):
        orib1.append(realBord[i][j])
        i+=-1
        j+=-1

    return orib1

def orib2(mylist):
# this def is for orib list from top right to bottom left.
    i=mylist[0]
    j=mylist[1]
    orib=[]
    while 0<=j < len(realBord) and i < len(realBord):
        orib.append(realBord[i][j])
        i += 1
        j -= 1
    i = mylist[0] - 1
    j = mylist[1] +1
    while 0 <= j < len(realBord) and 0 <= i < len(realBord):
        orib.append(realBord[i][j])
        i -= 1
        j += 1
    return orib

def index(k):
#this def is for finding the index of an elemnt.
    for item in range(len(realBord)):
        for j in range(len(realBord)):
            if realBord[item][j]==k:
                return [item,j]

def check(n):
#this def is for checking if there is any queen in those areas.
    i=index(n)[0]
    j=index(n)[1]
    sets=list(set(realBord[i]).union(set(column(j))).union(set(orib1(index(n)))).union(set(orib2(index(n)))))
    for item in range(len(sets)):
        if sets[item]=="Q":
            return False
def emptyplace(mylist):
#this def finds empty places for placing queens.
    emptyPlace1 = []
    for j in range(len(mylist)):
        if mylist[j] == "Q" or mylist[j] == "F":
            pass
        else:
            if check(mylist[j]) != False:
                emptyPlace1.append(mylist[j])
    return emptyPlace1
def queenfinder():
#this def is a primary memory for remembering where the queen was.
    queenlist=[]
    for item in range(8):
        for j in range(8):
            if primeryBord[item][j]=="Q":
                queenlist.append([item,j])
    return queenlist

def queenPlacer(x):
#this def is for placing queen and which areas are not empty for other queens.
    i=index(x)[0]
    j=index(x)[1]
    y=orib1(index(x))
    z=orib2(index(x))
    oribs=list(set(y)^set(z))
    for item in range(8):
            primeryBord[i][item]=full
    for item in range(8):
            primeryBord[item][j]=full
    for item in range(len(oribs)):
            if oribs[item]=="F" or oribs[item]=="Q":
                pass
            else:
                inorb=index(oribs[item])
                primeryBord[int(inorb[0])][int(inorb[1])]=full
    primeryBord[i][j]=queen
    return primeryBord

def maindef():
#this def is the main def for finding different situations.
    num=1
#these loops are for placing queens.
    for item1 in range(8):
        queenPlacer(column(0)[item1])
        for item2 in range(len(emptyplace(column(1)))):
            queenPlacer(emptyplace(column(1))[item2])
            for item3 in range(len(emptyplace(column(2)))):
                queenPlacer(emptyplace(column(2))[item3])
                for item4 in range(len(emptyplace(column(3)))):
                    queenPlacer(emptyplace(column(3))[item4])
                    for item5 in range(len(emptyplace(column(4)))):
                        queenPlacer(emptyplace(column(4))[item5])
                        for item6 in range(len(emptyplace(column(5)))):
                            queenPlacer(emptyplace(column(5))[item6])
                            for item7 in range(len(emptyplace(column(6)))):
                                queenPlacer(emptyplace(column(6))[item7])
                                for item8 in range(len(emptyplace(column(7)))):
                                    queenPlacer(emptyplace(column(7))[item8])
                                    print("NO.",num,":")
                                    print(tabulate(primeryBord,headers=["a","b","c","d","e","f","g","h"],
                                                   showindex=["1 ","2 ","3 ","4 ","5 ","6 ","7 ","8 "]))
                                    print("-----------------------------------------------","\n")
                                    num+=1
                                    #these loops are for change primary bord to previous level.
                                    for item9 in range(len(column(7))):
                                            primeryBord[item9][7]=realBord[item9][7]
                                    for j1 in range(len(queenfinder())):
                                        ix=int(queenfinder()[j1][0])
                                        jx=int(queenfinder()[j1][1])
                                        x=realBord[ix][jx]
                                        queenPlacer(x)
                                for item10 in range(len(column(6))):
                                        primeryBord[item10][6] = realBord[item10][6]
                                        primeryBord[item10][7]=realBord[item10][7]
                                for j1 in range(len(queenfinder())):
                                    ix = int(queenfinder()[j1][0])
                                    jx = int(queenfinder()[j1][1])
                                    x = realBord[ix][jx]
                                    queenPlacer(x)
                            for item11 in range(len(column(5))):
                                    primeryBord[item11][5]=realBord[item11][5]
                                    primeryBord[item11][6]=realBord[item11][6]
                                    primeryBord[item11][7]=realBord[item11][7]
                            for j1 in range(len(queenfinder())):
                                ix = int(queenfinder()[j1][0])
                                jx = int(queenfinder()[j1][1])
                                x = realBord[ix][jx]
                                queenPlacer(x)
                        for item12 in range(len(column(4))):
                                primeryBord[item12][4]=realBord[item12][4]
                                primeryBord[item12][5]=realBord[item12][5]
                                primeryBord[item12][6]=realBord[item12][6]
                                primeryBord[item12][7]=realBord[item12][7]
                        for j1 in range(len(queenfinder())):
                            ix = int(queenfinder()[j1][0])
                            jx = int(queenfinder()[j1][1])
                            x = realBord[ix][jx]
                            queenPlacer(x)
                    for item13 in range(len(column(3))):
                            primeryBord[item13][3]=realBord[item13][3]
                            primeryBord[item13][4]=realBord[item13][4]
                            primeryBord[item13][5]=realBord[item13][5]
                            primeryBord[item13][6]=realBord[item13][6]
                            primeryBord[item13][7]=realBord[item13][7]
                    for j1 in range(len(queenfinder())):
                        ix = int(queenfinder()[j1][0])
                        jx = int(queenfinder()[j1][1])
                        x = realBord[ix][jx]
                        queenPlacer(x)
                for item14 in range(len(column(2))):
                        primeryBord[item14][2]=realBord[item14][2]
                        primeryBord[item14][3]=realBord[item14][3]
                        primeryBord[item14][4]=realBord[item14][4]
                        primeryBord[item14][5]=realBord[item14][5]
                        primeryBord[item14][6]=realBord[item14][6]
                        primeryBord[item14][7]=realBord[item14][7]
                for j1 in range(len(queenfinder())):
                    ix = int(queenfinder()[j1][0])
                    jx = int(queenfinder()[j1][1])
                    x = realBord[ix][jx]
                    queenPlacer(x)
            for item15 in range(len(column(1))):
                    primeryBord[item15][1]=realBord[item15][1]
                    primeryBord[item15][2]=realBord[item15][2]
                    primeryBord[item15][3]=realBord[item15][3]
                    primeryBord[item15][4]=realBord[item15][4]
                    primeryBord[item15][5]=realBord[item15][5]
                    primeryBord[item15][6]=realBord[item15][6]
                    primeryBord[item15][7]=realBord[item15][7]
            for j1 in range(len(queenfinder())):
                ix = int(queenfinder()[j1][0])
                jx = int(queenfinder()[j1][1])
                x = realBord[ix][jx]
                queenPlacer(x)
        for item16 in range(len(column(0))):
                primeryBord[item16][0]=realBord[item16][0]
                primeryBord[item16][1]=realBord[item16][1]
                primeryBord[item16][2]=realBord[item16][2]
                primeryBord[item16][3]=realBord[item16][3]
                primeryBord[item16][4]=realBord[item16][4]
                primeryBord[item16][5]=realBord[item16][5]
                primeryBord[item16][6]=realBord[item16][6]
                primeryBord[item16][7]=realBord[item16][7]
#=============================================defs=====================================================
#=============================================action=====================================================
maindef()
#=============================================action=====================================================
# this is nima nikrouz's midterm project


# -----------------
# Extension Details
# -----------------

name = "Space Station"
version = "0.1"
developer = "Type Supply"
developerURL = "http://typesupply.com"
roboFontVersion = "3.2"
pycOnly = False
menuItems = [
    dict(
        path="menu_glyphEditorSpaceStation.py",
        preferredName="Glyph Editor",
        shortKey=("command", "/")
    ),
    dict(
        path="menu_fontEditorSpaceStation.py",
        preferredName="Font Editor",
        shortKey=""
    )
]

installAfterBuild = True

# ----------------------
# Don't edit below here.
# ----------------------

from AppKit import *
import os
import shutil
from mojo.extensions import ExtensionBundle

# Convert short key modifiers.

modifierMap = {
	"command": NSCommandKeyMask,
    "control": NSAlternateKeyMask,
    "option": NSAlternateKeyMask,
    "shift": NSShiftKeyMask,
    "capslock": NSAlphaShiftKeyMask,
}

for menuItem in menuItems:
	shortKey = menuItem.get("shortKey")
	if isinstance(shortKey, tuple):
		shortKey = list(shortKey)
		character = shortKey.pop(-1)
		modifiers = [modifierMap.get(modifier, modifier) for modifier in shortKey]
		if len(modifiers) == 1:
			modifiers = modifiers[0]
		else:
			m = None
			for modifier in modifiers:
				if m is None:
					m = modifier
				else:
					m |= modifier
			modifiers = m
		converted = (modifiers, character)
		menuItem["shortKey"] = tuple(converted)

# Make the various paths.

basePath = os.path.dirname(__file__)
sourcePath = os.path.join(basePath, "source")
libPath = os.path.join(sourcePath, "code")
licensePath = os.path.join(basePath, "license.txt")
requirementsPath = os.path.join(basePath, "requirements.txt")
extensionFile = "%s.roboFontExt" % name
buildPath = os.path.join(basePath, "build")
extensionPath = os.path.join(buildPath, extensionFile)

# Build the extension.

B = ExtensionBundle()
B.name = name
B.developer = developer
B.developerURL = developerURL
B.version = version
B.launchAtStartUp = True
B.mainScript = "main.py"
B.html = os.path.exists(os.path.join(sourcePath, "documentation", "index.html"))
B.requiresVersionMajor = roboFontVersion.split(".")[0]
B.requiresVersionMinor = roboFontVersion.split(".")[1]
B.addToMenu = menuItems
with open(licensePath) as license:
    B.license = license.read()
with open(requirementsPath) as requirements:
    B.requirements = requirements.read()
print("Building extension...", end=" ")
v = B.save(extensionPath, libPath=libPath, pycOnly=pycOnly)
print("done!")
errors = B.validationErrors()
if errors:
	print("Uh oh! There were errors:")
	print(errors)

# Install the extension.

if installAfterBuild:
	print("Installing extension...", end=" ")
	installDirectory = os.path.expanduser("~/Library/Application Support/RoboFont/plugins")
	installPath = os.path.join(installDirectory, extensionFile)
	if os.path.exists(installPath):
		shutil.rmtree(installPath)
	shutil.copytree(extensionPath, installPath)
	print("done!")
	print("RoboFont must now be restarted.")


import wizard
import pooler

def _check_sections(self, cr, uid, data, context):
    pool = pooler.get_pool(cr.dbname)
    data_obj = pool.get('ir.model.data')
    sec_obj = pool.get('crm.case.section')
    bug_id = sec_obj.search(cr, uid, [('code','=','BugSup')])
    if not bug_id:
        raise wizard.except_wizard(_('Error !'),
            _('You did not installed the Bug Tracking when you configured the crm module.' \
              '\nyou must create a section with the code \'BugSup\'.'
              ))
    else:
        id1 = data_obj._get_id(cr, uid, 'crm', 'crm_case_form_view')
        if id1:
            id1 = data_obj.browse(cr, uid, id1, context=context).res_id
        return {
            'domain':"[('section_id.name','=','Bug Tracking')]",
            'name': _('New Bug'),
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'crm.case',
            'view_id': False,
            'views': [(id1,'form')],
            'type': 'ir.actions.act_window',
            }

class check_section(wizard.interface):
    states = {
        'init': {
            'actions': [],
            'result': {'type': 'action', 'action':_check_sections, 'state' : 'end'}
        },
    }
check_section('portal.crm.check.section')

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:


'''
This program is used to get information from a user and
make an email from that information
'''

#asking for the user's first name and then storing it in the variable firstName
firstName = input("Enter your first name: ")

#asking for the user's last name and then storing it in the variable lastName
lastName = input("Enter your last name: ")

#asking for the user's domain name and then storing it in the variable domainName
domainName = input("Enter your domain name: ")

#creating the full email address
emailAddress = (lastName + "." + firstName + "@" + domainName)

#printing all the information to the user about their newly assigned email address
print("Hello " + firstName + ",\nYour new email address is:  " + emailAddress)


from env import data
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from time import sleep

#Program Isi Data
def program(data_set):
    #masuk website
    browser = webdriver.Chrome()
    actions = ActionChains(browser)
    browser.get(data['linkActive'])
    print('==== Welcome To Bangef, Automated Post-Test ====');

    try:
        for d in data_set:
            # mengecek apakah elemen input sudah ada dan mengisikannya
            # Nama
            WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.ID, data['selectorById'][0]))
            ).send_keys(d['namaLengkap'])

            # Email
            WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.ID, data['selectorById'][1]))
            ).send_keys(d['email'])

            # Nomer Telepon
            WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.ID, data['selectorById'][2]))
            ).send_keys('0'+d['noTelpon'])

            # Jenis Kelamin
            elementJK = browser.find_element(By.ID, data['selectorById'][3])
            Select(elementJK).select_by_value(d['jk'])

            # Usia
            WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.ID, data['selectorById'][4]))
            ).send_keys(d['usia'])

            # Pekerjaan
            elementPekerjaan = browser.find_element(By.ID, data['selectorById'][5])
            Select(elementPekerjaan).select_by_value(d['pekerjaan'])

            # Komunitas
            WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.ID, data['selectorById'][6]))
            ).send_keys(d['organisasi'])

            # Pendidikan
            elementPendidikan = browser.find_element(By.ID, data['selectorById'][7])
            Select(elementPendidikan).select_by_value(d['pendidikan'])

            # Provinsi
            WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.ID, data['selectorById'][8]))
            ).send_keys(d['provinsi'], Keys.RETURN)

            # Kota Asal
            elementCities = browser.find_element(By.ID, data['selectorById'][9])
            elementCities.send_keys(d['kotaAsal'], Keys.RETURN)

            # Captcha
            browser.execute_script("arguments[0].scrollIntoView();", elementCities)
            captcha = input('Masukan validasi captcha (sample : 9*9): \n');
            arr = list(captcha)
            if arr[1] == '+' :
                result = int(arr[0]) + int(arr[2])
            else :
                result = int(arr[0]) * int(arr[2])

            browser.find_element(By.ID, data['selectorById'][10]).send_keys(result)

            # Select Radio Button
            q1 = browser.find_element(By.ID, "1070-"+d['qSatu'])
            browser.execute_script("arguments[0].scrollIntoView();", q1)
            sleep(.5)
            q1.click()

            q2 = browser.find_element(By.ID, "1071-"+d['qDua'])
            browser.execute_script("arguments[0].scrollIntoView();", q2)
            q2.click()

            q3 = browser.find_element(By.ID, "1072-"+d['qTiga'])
            browser.execute_script("arguments[0].scrollIntoView();", q3)
            q3.click()

            q4 = browser.find_element(By.ID, "1073-"+d['qEmpat'])
            actions.move_to_element(q4).click().perform()
            browser.execute_script("arguments[0].scrollIntoView();", q4)
            sleep(.5)
            q4.click()

            q5 = browser.find_element(By.ID, "1076-"+d['qLima'])
            browser.execute_script("arguments[0].scrollIntoView();", q5)
            q5.click()

            # submit
            footer = browser.find_element(By.CSS_SELECTOR, '#__next > div > div.footer.mt-3')
            browser.execute_script("arguments[0].scrollIntoView();", footer)
            sleep(1)
            browser.find_element(By.XPATH, data['selectorByXpath']).click()
            sleep(3)

            # kembali ke page sebelumnya
            browser.get(data['linkActive'])
            WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.ID, data['selectorById'][0]))
            )
            print('Data dengan atas nama '+d['namaLengkap']+' berhasil ✔')

        print('Total Data : '+ str(d['id']) +' Selesai Post Test')

    except Exception as err:
        print('Data Selesai Terakhir : "id": "'+str(d['id'])+'".')
        print(err)
        browser.quit()


import random as r
class node:
    def __init__(self,val) -> None:
        self.data=val
        self.left=None
        self.right=None
class BST:
    def __init__(self) -> None:
        self.root=None
    def insertR(self,data,root):
        if root==None:
            return node(data)
        else:
            if data<self.root.data:
                root.left=self.insertR(data,root.left)
            else:
                root.right=self.insertR(data,root.right)

        return root

    def inorder(self,root):
        current=root
        if current==None:
            return
        self.inorder(current.left)
        print(current.data)
        self.inorder(current.right)

if __name__=='__main__':
    tree=BST()
    lister=[]
    for i in range(12):
        lister.append(r.randint(10,123))

    for val in lister:
        tree.root=tree.insertR(val,tree.root)

    tree.inorder(tree.root)


import argparse
from distutils.util import strtobool
import pathlib

import siml

import convert_raw_data

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'settings_yaml',
        type=pathlib.Path,
        help='YAML file name of settings.')
    parser.add_argument(
        'raw_data_directory',
        type=pathlib.Path,
        help='Raw data directory')
    parser.add_argument(
        '-p', '--preprocessors-pkl',
        type=pathlib.Path,
        default=None,
        help='Preprocessors.pkl file')
    parser.add_argument(
        '-o', '--out-dir',
        type=pathlib.Path,
        default=None,
        help='Output directory name')
    parser.add_argument(
        '-f', '--force-renew',
        type=strtobool,
        default=0,
        help='If True, overwrite existing data [False]')
    parser.add_argument(
        '-l', '--light',
        type=strtobool,
        default=0,
        help='If True, compute minimum required data only [False]')
    parser.add_argument(
        '-n', '--read-npy',
        type=strtobool,
        default=1,
        help='If True, read .npy files instead of original files '
        'if exists [True]')
    parser.add_argument(
        '-r', '--recursive',
        type=strtobool,
        default=1,
        help='If True, process directory recursively [True]')
    parser.add_argument(
        '-e', '--elemental',
        type=strtobool,
        default=0,
        help='If True, create also elemental features [False]')
    parser.add_argument(
        '-a', '--convert-answer',
        type=strtobool,
        default=1,
        help='If True, convert answer [True]')
    parser.add_argument(
        '-s', '--skip-interim',
        type=strtobool,
        default=0,
        help='If True, skip conversion of interim data [False]')
    args = parser.parse_args()

    main_setting = siml.setting.MainSetting.read_settings_yaml(
        args.settings_yaml)
    if not args.convert_answer:
        main_setting.conversion.required_file_names = ['*.msh', '*.cnt']
    main_setting.data.raw = args.raw_data_directory
    if args.out_dir is None:
        args.out_dir = args.raw_data_directory
        main_setting.data.interim = [siml.prepost.determine_output_directory(
            main_setting.data.raw,
            main_setting.data.raw.parent / 'interim', 'raw')]
        main_setting.data.preprocessed = [
            siml.prepost.determine_output_directory(
                main_setting.data.raw,
                main_setting.data.raw.parent / 'preprocessed', 'raw')]
    else:
        main_setting.data.interim = [args.out_dir / 'interim']
        main_setting.data.preprocessed = [args.out_dir / 'preprocessed']

    if not args.skip_interim:
        conversion_function = convert_raw_data.HeatConversionFuncionCreator(
            create_elemental=args.elemental,
            convert_answer=args.convert_answer,
            light=args.light)
        raw_converter = siml.prepost.RawConverter(
            main_setting,
            conversion_function=conversion_function,
            filter_function=convert_raw_data.filter_function_heat,
            force_renew=args.force_renew,
            recursive=args.recursive,
            to_first_order=True,
            write_ucd=False,
            read_npy=args.read_npy, read_res=args.convert_answer)
        raw_converter.convert()

    preprocessor = siml.prepost.Preprocessor(
        main_setting, force_renew=args.force_renew,
        allow_missing=True)
    preprocessor.convert_interim_data(preprocessor_pkl=args.preprocessors_pkl)

    return

if __name__ == '__main__':
    main()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jun 22 16:48:54 2019

@author: xiaohaoren
"""

import json
import pickle
import numpy as np

negative_word = ['悶熱','吵雜','髒','髒亂','加強','改進','缺點']

def Load_All_Info(json_path,pickle_path):

    with open(json_path,'r') as fp:
        json_data = json.load(fp)
    with open(pickle_path, 'rb') as fp:
        pickle_data = pickle.load(fp)

    keys = list(json_data.keys())

    return json_data,pickle_data,keys

def FilteringAndRanking(querys,places,corpus,review_list=None):
    """
    query = ['冷氣','衛生',...]
    place = ['春山茶水舖','小川拉麵',...]
    corpus = {'春山茶水舖':{'不錯':(正向次數,評論編號),'五花肉':(正向分數,評論編號),...}}
    """
    scoreboard = {}

    for i,place in enumerate(places):

        #N = corpus[place]['__termNum__']
        N = corpus[place]['__reviewNum__']

        scoreboard[place]=0

        if place not in corpus:
            continue

        for term in querys:
            term_score = 0
            term_sign = -1 if term in negative_word else 1
            if term not in corpus[place]:
                continue
            else:
                keyword_data = corpus[place][term]
                for rid,p in keyword_data.items():
                    term_score += (term_sign * p)

                    if review_list is not None:
                        rid = int(rid)
                        review_content = review_list[rid]
                        print('"%s"由於「%s」中的"%s"而加%d分' % (place,review_content,term,term_sign*p))

            scoreboard[place] += term_score

        scoreboard[place] = scoreboard[place]/(N*len(querys)) * 100

    return scoreboard

if __name__ == "__main__":

    corpus_path = '../data/place_dict.json'
    reviewContent_path = '../data/review_list.json'
    querys = ['乾淨','衛生']

    corpus,review_list,places = Load_All_Info(json_path=corpus_path,pickle_path=reviewContent_path)

    scoreboard = FilteringAndRanking(querys=querys,places=places,corpus=corpus,review_list=review_list)


import numpy as np
import pandas as pd

class Model:
    def __init__(self, bias: float, age_y: float, bmi: float, bmisq,
                 gender: float, dysrhythmia: float, heart_failure: float, dys_hf_interaction: float,
                 discharge_home_self: float, discharge_facility: float, ed_visits: float, psych_dx: float,
                 pta_med_count: float, drug_abuse_dx: float, narcotic_meds: float, tja_within_past_yr: float):
        """Input regression coefficients as positional or named parameters.
        Keep them organized and expose list or data frame. Note: 16 coefficients.
        """
        self.as_list = [bias, age_y, bmi, bmisq,
                        gender, dysrhythmia, heart_failure, dys_hf_interaction,
                        discharge_home_self, discharge_facility, ed_visits, psych_dx,
                        pta_med_count, drug_abuse_dx, narcotic_meds, tja_within_past_yr]
        row_names = ['bias', 'age_y', 'bmi', 'bmi ** 2',
                     '(gender == male)', 'dysrhythmia', 'heart_failure', 'dysrhythmia * heart_failure',
                     'disch_home_or_self', 'disch_facility', '(ed_visits > 9)', 'psych_dx',
                     'pta_med_count', 'drug_abuse_dx', 'narcotic_meds', 'TJA within past yr']
        self.as_dataframe = pd.DataFrame(self.as_list, index=row_names)

class Patient:
    def __init__(self, age_y: float, bmi: float, gender: str, dysrhythmia: bool,
                 heart_failure: bool, discharge: str, ed_visits: int, psych_dx: bool,
                 pta_med_count: float, drug_abuse_dx: bool, narcotic_meds: bool, tja_within_past_12_mo: bool):
        """Input 12 patient characteristics as positional or named parameters.
        Keep them organized.
        Expose list of 16 numbers, for multiplying with model coefficients.
        """
        # self.age_y = age_y
        # self.bmi = bmi
        # self.gender = gender
        # self.dysrhythmia = dysrhythmia
        # self.heart_failure = heart_failure
        # self.discharge = discharge
        # self.ed_visits = ed_visits
        # self.psych_dx = psych_dx
        # self.pta_med_count = pta_med_count
        # self.drug_abuse_dx = drug_abuse_dx
        # self.narcotic_meds = narcotic_meds
        # self.tja_within_past_12_mo = tja_within_past_12_mo
        self.as_list = [1, age_y,
                        bmi, bmi ** 2,
                        gender == 'male', dysrhythmia,
                        heart_failure, dysrhythmia and heart_failure,
                        discharge == 'home' or discharge == 'self-care', discharge == 'facility',
                        ed_visits > 9, psych_dx,
                        pta_med_count, drug_abuse_dx,
                        narcotic_meds, tja_within_past_12_mo]

model_90_days = Model(
    -0.5527,
    0,  # age
    -0.0903,  # bmi
    0.00145,  # bmi ** 2
    0.2241,  # (gender == 'male')
    -0.1169,  # dysrhythmia
    -0.1284,  # heart_failure
    0.7544,  # dysrhythmia * heart_failure
    -0.2464,  # (discharge == 'home' or discharge == 'self-care')
    0.3233,  # (discharge == 'facility')
    0.3325,  # (ed_visits > 9)
    0,  # psych dx
    0.0193,  # pta_med_count
    0.2475,  # drug_abuse_dx
    0.1296,  # narcotic_meds
    -0.3820  # tja_within_past_12_mo
)

model_30_days = Model(-2.6576, 0.0291, -0.1345, 0.00218,
                      0.2070, -0.0505, -0.3669, 0.7994,
                      -0.3124, 0.3645, 0.5942, 0.1934,
                      0.0332, 0, 0, 0)

p = Patient(65, 30, 'male', True,
            True, 'home', 3, False,
            5, False, True, False)

if __name__ == '__main__':
    print("Patient, length =", len(p.as_list))
    print(p.as_list)
    print()

    print("Model, length =", len(model_30_days.as_list))
    print(model_30_days.as_list)
    print()

    print("Score =", np.dot(p.as_list, model_30_days.as_list))


import re
import time
import textwrap
from copy import copy
import torch.nn.functional as F
from training_utils import *

BASH_FORMATTING = {
                   'PURPLE': '\033[95m',
                   'CYAN': '\033[96m',
                   'DARKCYAN': '\033[36m',
                   'BLUE': '\033[94m',
                   'GREEN': '\033[92m',
                   'YELLOW': '\033[93m',
                   'RED':'\033[91m',
                   'BOLD': '\033[1m',
                   'UNDERLINE': '\033[4m',
                   'END': '\033[0m'
}

def bash_format_text(text, *args):
    formatting = ''
    for arg in args:
        formatting += BASH_FORMATTING[arg]
    return formatting + text + BASH_FORMATTING['END']

# maybe add do_sample ?
# randomly select one of two values that evaluate to true or false lols hehehe
def transfer_learning_bot(model, tokenizer, max_length, top_k, top_p):
    '''
    for chatbot trained using transfer learning
    '''
    input_sentence = input('User >> ')
    input_sentence = input_sentence.lower()
    context = copy(input_sentence)
    input_sentence = tokenizer.encode(input_sentence, truncation = True, max_length = 128, return_tensors = 'pt')
    continue_convo = True
    while continue_convo:
        print(bash_format_text('Typing...', 'YELLOW', 'BOLD'), end='\r' )
        uni_temp = round(torch.rand(1).clamp(0.1).item(), 2)
        repeat_penalty = round((torch.rand(1) * 5).clamp(1).item(), 2)
        ngram =  int(np.random.choice([2,3,4], 1)[0])
        bot_reply = model.generate(input_sentence, max_length = max_length, top_k = top_k, top_p = top_p, temperature = uni_temp,
                                   repetition_penalty = repeat_penalty, skip_special_tokens = True,
                                   no_repeat_ngram_size=ngram, pad_token_id = tokenizer.eos_token_id)
                                   # length_penalty=length_penalty)
        bot_reply = tokenizer.decode(bot_reply.squeeze()).replace('<|endoftext|>', '')
        bot_reply = textwrap.fill(bot_reply, width=75)
        print(bash_format_text('Aubrey: {}'.format(bot_reply), 'YELLOW', 'BOLD'))
        response = input('User >> ')
        if (response == 'q' or response == 'quit' or response == 'exit'):
            continue_convo = False
        input_sentence = tokenizer.encode(response.lower(), truncation= True, max_length = 128, return_tensors = 'pt')


from unittest import TestCase
import unittest
import requests

# import sys
#
# sys.path.insert(0, '../../src')

class TestLoadTimeSeries(TestCase):
    def test_load_data_success(self):
        f = open("tests/routes/time_series_covid19_recovered_global.csv", "rb")
        file = f.read()
        url = 'https://covid-monitor-61.herokuapp.com/time_series/data?type=recovered'
        r = requests.post(url, data=file, headers={"Content-Type": "text/csv"})
        f.close()
        self.assertEqual(r.status_code, 200)

    def test_query_data(self):
        url = 'https://covid-monitor-61.herokuapp.com/time_series/cases'
        body = {"return_type": "json",
                "start_date": "01/26/20",
                "end_date": "01/28/20",
                "types": ["Recovered"],
                "locations":
                    [
                        {"Country/Region": "Albania"},
                        {"Country/Region": "Canada", "Province/State": "Ontario"},
                        {"Country/Region": "Australia"}
                    ]
                }
        r = requests.post(url, json=body, headers={"Content-Type": "application/json"})
        self.assertEqual(r.status_code, 200)

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


import numpy as np
import sys

from vispy import app, visuals, scene

# build visuals
Plot3D = scene.visuals.create_visual_node(visuals.line.line.LineVisual)

# build canvas
canvas = scene.SceneCanvas(keys='interactive', title='plot3d', show=True)

# Add a ViewBox to let the user zoom/rotate
view = canvas.central_widget.add_view()
view.camera = 'turntable'
view.camera.fov = 45
view.camera.distance = 6

# prepare data
x, y, z, segments = [], [], [], []
for start, i in enumerate(np.linspace(-5, 5, 1000)):
    N = 6000
    x.append(np.sin(np.linspace(-5-i, 5+1, N)*np.pi))
    y.append(np.cos(np.linspace(-5+i, 5-i, N)*np.pi))
    z.append(np.linspace(-5-i, 5-i, N))
    start_idx = 1000 * start
    idxs = np.arange(start_idx, start_idx+N-1)
    idxs = np.stack([idxs, idxs+1], axis=-1)
    segments.append(idxs)
x, y, z = np.concatenate(x), np.concatenate(y), np.concatenate(z)
segments = np.concatenate(segments, axis=0)

# plot
pos = np.c_[x, y, z]
Plot3D(pos, width=10.0,
    color=(1.0, 0.0, 0.0, 1.0), method='gl',
    connect=segments,
    parent=view.scene)

if __name__ == '__main__':
    if sys.flags.interactive != 1:
        app.run()


#Considere uma tupla que guarde temperaturas em Celsius (C) ou Fahrenheit (F)
# como um valor em duas partes: temperatura e escala. Por exemplo:
# 32,5 graus Celsius é representado como (32.5, ‘C’) e 45,2 graus Fahrenheit
# é representado como (45.2, ‘F’). Desenvolva uma função que soma duas
# temperaturas que podem estar em Celsius ou em Fahrenheit. Se as duas
# temperaturas estiverem na mesma escala, a resposta deve estar na mesma escala.
# Se as temperaturas estiverem em escalas diferentes, a resposta deve ser dada na
# escala da segunda temperatura. Considere até 4 (quatro) casas decimais).

soma = ()

t = (float(input()),
     str(input()).upper().strip())

t2 = (float(input()),
      str(input()).upper().strip())

if t[1] == t2 [1]:
    soma = t[0] + t2[0]
    print(f"({soma}, '{t[1]}')")

elif t[1] != t2[1] and t[1] == 'C':
    F =  9 * t[0] / 5 + 32
    soma = F + t2[0]
    print(f"({soma}, '{t2[1]}')")
elif t[1] != t2 and t[1] == 'F':
    C = (t[0] - 32) * (5/9)
    soma = C + t2[0]
    print(f"({soma:.4f}, '{t2[1]}')")


import argparse
import glob
import os

import h5py
import hdbscan
import numpy as np
from scipy.ndimage import binary_erosion
from skimage.filters import gaussian
from skimage.segmentation import watershed
from sklearn.cluster import MeanShift

def expand_labels_watershed(seg, raw, erosion_iters=4):
    bg_mask = seg == 0
    # don't need to  do anything if we only have background
    if bg_mask.size == int(bg_mask.sum()):
        return seg

    hmap = gaussian(raw, sigma=1.)

    bg_mask = binary_erosion(bg_mask, iterations=erosion_iters)
    seg_new = seg.copy()
    bg_id = int(seg.max()) + 1
    seg_new[bg_mask] = bg_id

    seg_new = watershed(hmap, seg_new)

    seg_new[seg_new == bg_id] = 0
    return seg_new

def cluster(emb, clustering_alg, semantic_mask=None):
    output_shape = emb.shape[1:]
    # reshape (E, D, H, W) -> (E, D * H * W) and transpose -> (D * H * W, E)
    flattened_embeddings = emb.reshape(emb.shape[0], -1).transpose()

    result = np.zeros(flattened_embeddings.shape[0])

    if semantic_mask is not None:
        flattened_mask = semantic_mask.reshape(-1)
        assert flattened_mask.shape[0] == flattened_embeddings.shape[0]
    else:
        flattened_mask = np.ones(flattened_embeddings.shape[0])

    if flattened_mask.sum() == 0:
        # return zeros for empty masks
        return result.reshape(output_shape)

    # cluster only within the foreground mask
    clusters = clustering_alg.fit_predict(flattened_embeddings[flattened_mask == 1])
    # always increase the labels by 1 cause clustering results start from 0 and we may loose one object
    result[flattened_mask == 1] = clusters + 1

    return result.reshape(output_shape)

def cluster_hdbscan(emb, min_size, eps, min_samples=None, semantic_mask=None):
    clustering = hdbscan.HDBSCAN(min_cluster_size=min_size, cluster_selection_epsilon=eps, min_samples=min_samples)
    return cluster(emb, clustering, semantic_mask)

def cluster_ms(emb, bandwidth, semantic_mask=None):
    clustering = MeanShift(bandwidth=bandwidth, bin_seeding=True)
    return cluster(emb, clustering, semantic_mask)

def run_clustering(emb, clustering, delta_var, min_size, expand_labels, remove_largest):
    assert clustering in ['ms', 'hdbscan']
    if clustering == 'hdbscan':
        clusters = cluster_hdbscan(emb, min_size, delta_var)
    else:
        clusters = cluster_ms(emb, delta_var)

    # watershed the empty (i.e. noise) region
    if expand_labels:
        clusters = expand_labels_watershed(clusters, raw)

    if remove_largest:
        ids, counts = np.unique(clusters, return_counts=True)
        clusters[ids[np.argmax(counts)] == clusters] = 0

    return clusters

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Segment embryos')
    parser.add_argument('--emb_dir', type=str, help='Path to embedding predictions directory', required=True)
    parser.add_argument('--clustering', type=str, help='Clustering algorithm: ms or hdbscan', required=True)
    parser.add_argument('--seg_ds', type=str, help='Output seg dataset name', required=True)
    parser.add_argument('--delta_var', type=float, help='delta_var param', default=0.5)
    parser.add_argument('--min_size', type=int, help='HDBSCAN min_size param', default=50)
    parser.add_argument('--remove_largest', help='Remove largest instance (BG)', action='store_true')
    parser.add_argument('--expand_labels', help='Expand labels with watershed', action='store_true')
    parser.add_argument('--min_instance_size', type=int, help='Min instance size filtering', required=False,
                        default=None)

    args = parser.parse_args()

    assert os.path.isdir(args.emb_dir)

    for file_path in glob.glob(os.path.join(args.emb_dir, '*predictions.h5')):
        _, filename = os.path.split(file_path)

        print(f'Processing {filename}')

        with h5py.File(file_path, 'r+') as f:
            raw_sequence = f['raw_sequence'][:]
            embedding_sequence = f['embedding_sequence1'][:]
            seg_sequence = []

            i = 0
            for raw, emb in zip(raw_sequence, embedding_sequence):
                i += 1
                print(f'Processing patch {i}')
                seg = run_clustering(emb, args.clustering, args.delta_var, args.min_size, args.expand_labels,
                                     args.remove_largest)
                seg_sequence.append(seg)

            if args.seg_ds in f:
                del f[args.seg_ds]

            segments = np.stack(seg_sequence, axis=0)
            f.create_dataset(args.seg_ds, data=segments, compression='gzip')

    print('Done')