# Built-in
import os
import sys
import imp
import json
import shutil
import getpass
import datetime
import traceback
import subprocess
from functools import partial

# pymel
import pymel.core as pm
from pymel.core import datatypes

# mgear
import mgear
from .. import attribute, dag, vector, pyqt, skin
from ... import string
from ...vendor.Qt import QtCore, QtWidgets, QtGui

from . import guideUI as guui
from . import customStepUI as csui

# pyside
from maya.app.general.mayaMixin import MayaQDockWidget
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

GUIDE_UI_WINDOW_NAME = "guide_UI_window"
GUIDE_DOCK_NAME = "Guide_Components"

TYPE = "mgear_guide_root"

MGEAR_SHIFTER_CUSTOMSTEP_KEY = "MGEAR_SHIFTER_CUSTOMSTEP_PATH"

class Main(object):
    """The main guide class

    Provide the methods to add parameters, set parameter values,
    create property...

    Attributes:
        paramNames (list): List of parameter name cause it's actually important
        to keep them sorted.
        paramDefs (dict): Dictionary of parameter definition.
        values (dict): Dictionary of options values.
        valid (bool): We will check a few things and make sure the guide we are
            loading is up to date. If parameters or object are missing a
            warning message will be display and the guide should be updated.

    """

    def __init__(self):

        self.paramNames = []
        self.paramDefs = {}
        self.values = {}
        self.valid = True

    def addPropertyParamenters(self, parent):
        """Add attributes from the parameter definition list

        Arguments:
            parent (dagNode): The object to add the attributes.

        Returns:
            dagNode: parent with the attributes.

        """

        for scriptName in self.paramNames:
            paramDef = self.paramDefs[scriptName]
            paramDef.create(parent)

        return parent

    def setParamDefValue(self, scriptName, value):
        """Set the value of parameter with matching scriptname.

        Arguments:
            scriptName (str): Scriptname of the parameter to edit.
            value (variant): New value.

        Returns:
            bool: False if the parameter wasn't found.

        """

        if scriptName not in self.paramDefs.keys():
            mgear.log("Can't find parameter definition for : " + scriptName,
                      mgear.sev_warning)
            return False

        self.paramDefs[scriptName].value = value
        self.values[scriptName] = value

        return True

    def setParamDefValuesFromProperty(self, node):
        """Set the parameter definition values from the attributes of an object

        Arguments:
            node (dagNode): The object with the attributes.
        """

        for scriptName, paramDef in self.paramDefs.items():
            if not pm.attributeQuery(scriptName, node=node, exists=True):
                mgear.log("Can't find parameter '%s' in %s" %
                          (scriptName, node), mgear.sev_warning)
                self.valid = False
            else:
                cnx = pm.listConnections(
                    node + "." + scriptName,
                    destination=False, source=True)
                if cnx:
                    paramDef.value = None
                    self.values[scriptName] = cnx[0]
                else:
                    paramDef.value = pm.getAttr(node + "." + scriptName)
                    self.values[scriptName] = pm.getAttr(
                        node + "." + scriptName)

    def addColorParam(self, scriptName, value=False):
        """Add color paramenter to the paramenter definition Dictionary.

        Arguments:
            scriptName (str): The name of the color parameter.
            value (Variant): The default color value.

        Returns:
            paramDef: The newly create paramenter definition.
        """

        paramDef = attribute.colorParamDef(scriptName, value)
        self.paramDefs[scriptName] = paramDef
        self.paramNames.append(scriptName)

        return paramDef

    def addParam(self, scriptName, valueType, value,
                 minimum=None, maximum=None, keyable=False,
                 readable=True, storable=True, writable=True,
                 niceName=None, shortName=None):
        """Add paramenter to the paramenter definition Dictionary.

        Arguments:
            scriptName (str): Parameter scriptname.
            valueType (str): The Attribute Type. Exp: 'string', 'bool',
                'long', etc..
            value (float or int): Default parameter value.
            niceName (str): Parameter niceName.
            shortName (str): Parameter shortName.
            minimum (float or int): mininum value.
            maximum (float or int): maximum value.
            keyable (boo): If true is keyable
            readable (boo): If true is readable
            storable (boo): If true is storable
            writable (boo): If true is writable

        Returns:
            paramDef: The newly create paramenter definition.

        """
        paramDef = attribute.ParamDef2(scriptName, valueType, value, niceName,
                                       shortName, minimum, maximum, keyable,
                                       readable, storable, writable)
        self.paramDefs[scriptName] = paramDef
        self.values[scriptName] = value
        self.paramNames.append(scriptName)

        return paramDef

    def addFCurveParam(self, scriptName, keys, interpolation=0):
        """Add FCurve paramenter to the paramenter definition Dictionary.

        Arguments:
            scriptName (str): Attribute fullName.
            keys (list): The keyframes to define the function curve.
            interpolation (int): the curve interpolation.

        Returns:
            paramDef: The newly create paramenter definition.

        """
        paramDef = attribute.FCurveParamDef(scriptName, keys, interpolation)
        self.paramDefs[scriptName] = paramDef
        self.values[scriptName] = None
        self.paramNames.append(scriptName)

        return paramDef

    def addEnumParam(self, scriptName, enum, value=False):
        """Add FCurve paramenter to the paramenter definition Dictionary.

        Arguments:
            scriptName (str): Attribute fullName
            enum (list of str): The list of elements in the enumerate control.
            value (int): The default value.

        Returns:
            paramDef: The newly create paramenter definition.

        """
        paramDef = attribute.enumParamDef(scriptName, enum, value)
        self.paramDefs[scriptName] = paramDef
        self.values[scriptName] = value
        self.paramNames.append(scriptName)

        return paramDef

##########################################################
# RIG GUIDE
##########################################################

class Rig(Main):
    """Rig guide class.

    This is the class for complete rig guide definition.

        * It contains the component guide in correct hierarchy order and the
            options to generate the rig.
        * Provide the methods to add more component, import/export guide.

    Attributes:
        paramNames (list): List of parameter name cause it's actually important
            to keep them sorted.
        paramDefs (dict): Dictionary of parameter definition.
        values (dict): Dictionary of options values.
        valid (bool): We will check a few things and make sure the guide we are
            loading is up to date. If parameters or object are missing a
            warning message will be display and the guide should be updated.
        controllers (dict): Dictionary of controllers.
        components (dict): Dictionary of component. Keys are the component
            fullname (ie. 'arm_L0')
        componentsIndex (list): List of component name sorted by order
            creation (hierarchy order)
        parents (list): List of the parent of each component, in same order
            as self.components
    """

    def __init__(self):

        # Parameters names, definition and values.
        self.paramNames = []
        self.paramDefs = {}
        self.values = {}
        self.valid = True

        self.controllers = {}
        self.components = {}  # Keys are the component fullname (ie. 'arm_L0')
        self.componentsIndex = []
        self.parents = []

        self.addParameters()

    def addParameters(self):
        """Parameters for rig options.

        Add more parameter to the parameter definition list.

        """
        # --------------------------------------------------
        # Main Tab
        self.pRigName = self.addParam("rig_name", "string", "rig")
        self.pMode = self.addEnumParam("mode", ["Final", "WIP"], 0)
        self.pStep = self.addEnumParam(
            "step",
            ["All Steps", "Objects", "Properties",
                "Operators", "Connect", "Joints", "Finalize"],
            6)
        self.pIsModel = self.addParam("ismodel", "bool", True)
        self.pClassicChannelNames = self.addParam(
            "classicChannelNames",
            "bool",
            True)
        self.pProxyChannels = self.addParam("proxyChannels", "bool", False)
        self.pWorldCtl = self.addParam("worldCtl", "bool", False)

        # --------------------------------------------------
        # skin
        self.pSkin = self.addParam("importSkin", "bool", False)
        self.pSkinPackPath = self.addParam("skin", "string", "")

        # --------------------------------------------------
        # Colors

        # Index color
        self.pLColorIndexfk = self.addParam("L_color_fk", "long", 6, 0, 31)
        self.pLColorIndexik = self.addParam("L_color_ik", "long", 18, 0, 31)
        self.pRColorIndexfk = self.addParam("R_color_fk", "long", 23, 0, 31)
        self.pRColorIndexik = self.addParam("R_color_ik", "long", 14, 0, 31)
        self.pCColorIndexfk = self.addParam("C_color_fk", "long", 13, 0, 31)
        self.pCColorIndexik = self.addParam("C_color_ik", "long", 17, 0, 31)

        # RGB colors for Maya 2015 and up
        # self.pLColorfk = self.addColorParam("L_RGB_fk", [0, 1, 0])
        # self.pLColorik = self.addColorParam("L_RGB_ik", [0, .5, 0])
        # self.pRColorfk = self.addColorParam("R_RGB_fk", [0, 0, 1])
        # self.pRColorik = self.addColorParam("R_RGB_ik", [0, 0, .6])
        # self.pCColorfk = self.addColorParam("C_RGB_fk", [1, 0, 0])
        # self.pCColorik = self.addColorParam("C_RGB_ik", [.6, 0, 0])

        # --------------------------------------------------
        # Settings
        self.pJointRig = self.addParam("joint_rig", "bool", True)
        self.pSynoptic = self.addParam("synoptic", "string", "")

        self.pDoPreCustomStep = self.addParam("doPreCustomStep", "bool", False)
        self.pDoPostCustomStep = self.addParam("doPostCustomStep",
                                               "bool", False)
        self.pPreCustomStep = self.addParam("preCustomStep", "string", "")
        self.pPostCustomStep = self.addParam("postCustomStep", "string", "")

        # --------------------------------------------------
        # Comments
        self.pComments = self.addParam("comments", "string", "")
        self.pUser = self.addParam("user", "string", getpass.getuser())
        self.pDate = self.addParam(
            "date", "string", str(datetime.datetime.now()))
        self.pMayaVersion = self.addParam(
            "maya_version", "string",
            str(pm.mel.eval("getApplicationVersionAsFloat")))
        self.pGearVersion = self.addParam(
            "gear_version", "string", mgear.getVersion())

    def setFromSelection(self):
        """Set the guide hierarchy from selection."""
        selection = pm.ls(selection=True)
        if not selection:
            mgear.log(
                "Select one or more guide root or a guide model",
                mgear.sev_error)
            self.valid = False
            return False

        for node in selection:
            self.setFromHierarchy(node, node.hasAttr("ismodel"))

        return True

    def setFromHierarchy(self, root, branch=True):
        """Set the guide from given hierarchy.

        Arguments:
            root (dagNode): The root of the hierarchy to parse.
            branch (bool): True to parse children components.

        """
        startTime = datetime.datetime.now()
        # Start
        mgear.log("Checking guide")

        # Get the model and the root
        self.model = root.getParent(generations=-1)
        while True:
            if root.hasAttr("comp_type") or self.model == root:
                break
            root = root.getParent()
            mgear.log(root)

        # ---------------------------------------------------
        # First check and set the options
        mgear.log("Get options")
        self.setParamDefValuesFromProperty(self.model)

        # ---------------------------------------------------
        # Get the controllers
        mgear.log("Get controllers")
        self.controllers_org = dag.findChild(self.model, "controllers_org")
        if self.controllers_org:
            for child in self.controllers_org.getChildren():
                self.controllers[child.name().split("|")[-1]] = child

        # ---------------------------------------------------
        # Components
        mgear.log("Get components")
        self.findComponentRecursive(root, branch)
        endTime = datetime.datetime.now()
        finalTime = endTime - startTime
        mgear.log("Find recursive in  [ " + str(finalTime) + " ]")
        # Parenting
        if self.valid:
            for name in self.componentsIndex:
                mgear.log("Get parenting for: " + name)
                # TODO: In the future should use connections to retrive this
                # data
                # We try the fastes aproach, will fail if is not the top node
                try:
                    # search for his parent
                    compParent = self.components[name].root.getParent()
                    if compParent and compParent.hasAttr("isGearGuide"):
                        pName = "_".join(compParent.name().split("_")[:2])
                        pLocal = "_".join(compParent.name().split("_")[2:])

                        pComp = self.components[pName]
                        self.components[name].parentComponent = pComp
                        self.components[name].parentLocalName = pLocal
                # This will scan the hierachy in reverse. It is much slower
                except KeyError:
                    # search children and set him as parent
                    compParent = self.components[name]
                    # for localName, element in compParent.getObjects(
                    #         self.model, False).items():
                    # NOTE: getObjects3 is an experimental function
                    for localName, element in compParent.getObjects3(
                            self.model).items():
                        for name in self.componentsIndex:
                            compChild = self.components[name]
                            compChild_parent = compChild.root.getParent()
                            if (element is not None
                                    and element == compChild_parent):
                                compChild.parentComponent = compParent
                                compChild.parentLocalName = localName

            # More option values
            self.addOptionsValues()

        # End
        if not self.valid:
            mgear.log("The guide doesn't seem to be up to date."
                      "Check logged messages and update the guide.",
                      mgear.sev_warning)

        endTime = datetime.datetime.now()
        finalTime = endTime - startTime
        mgear.log("Guide loaded from hierarchy in  [ " + str(finalTime) + " ]")

    def addOptionsValues(self):
        """Gather or change some options values according to some others.

        Note:
            For the moment only gets the rig size to adapt size of object to
            the scale of the character

        """
        # Get rig size to adapt size of object to the scale of the character
        maximum = 1
        v = datatypes.Vector()
        for comp in self.components.values():
            for pos in comp.apos:
                d = vector.getDistance(v, pos)
                maximum = max(d, maximum)

        self.values["size"] = max(maximum * .05, .1)

    def findComponentRecursive(self, node, branch=True):
        """Finds components by recursive search.

        Arguments:
            node (dagNode): Object frome where start the search.
            branch (bool): If True search recursive all the children.
        """

        if node.hasAttr("comp_type"):
            comp_type = node.getAttr("comp_type")
            comp_guide = self.getComponentGuide(comp_type)

            if comp_guide:
                comp_guide.setFromHierarchy(node)
                mgear.log(comp_guide.fullName + " (" + comp_type + ")")
                if not comp_guide.valid:
                    self.valid = False

                self.componentsIndex.append(comp_guide.fullName)
                self.components[comp_guide.fullName] = comp_guide

        if branch:
            for child in node.getChildren(type="transform"):
                self.findComponentRecursive(child)

    def getComponentGuide(self, comp_type):
        """Get the componet guide python object

        ie. Finds the guide.py of the component.

        Arguments:
            comp_type (str): The component type.

        Returns:
            The component guide instance class.
        """

        # Check component type
        '''
        path = os.path.join(basepath, comp_type, "guide.py")
        if not os.path.exists(path):
            mgear.log("Can't find guide definition for : " + comp_type + ".\n"+
                path, mgear.sev_error)
            return False
        '''

        # Import module and get class
        import mgear.maya.shifter as shifter
        module = shifter.importComponentGuide(comp_type)

        ComponentGuide = getattr(module, "Guide")

        return ComponentGuide()

    # =====================================================
    # DRAW

    def initialHierarchy(self):
        """Create the initial rig guide hierarchy (model, options...)"""
        self.model = pm.group(n="guide", em=True, w=True)

        # Options
        self.options = self.addPropertyParamenters(self.model)

        # the basic org nulls (Maya groups)
        self.controllers_org = pm.group(
            n="controllers_org",
            em=True,
            p=self.model)
        self.controllers_org.attr('visibility').set(0)

    def drawNewComponent(self, parent, comp_type):
        """Add a new component to the guide.

        Arguments:
            parent (dagNode): Parent of this new component guide.
            compType (str): Type of component to add.

        """
        comp_guide = self.getComponentGuide(comp_type)

        if not comp_guide:
            mgear.log("Not component guide of type: " + comp_type +
                      " have been found.", mgear.sev_error)
            return
        if parent is None:
            self.initialHierarchy()
            parent = self.model
        else:
            parent_root = parent
            while True:
                if parent_root.hasAttr("ismodel"):
                    break

                if parent_root.hasAttr("comp_type"):
                    parent_type = parent_root.attr("comp_type").get()
                    parent_side = parent_root.attr("comp_side").get()
                    parent_uihost = parent_root.attr("ui_host").get()
                    parent_ctlGrp = parent_root.attr("ctlGrp").get()

                    if parent_type in comp_guide.connectors:
                        comp_guide.setParamDefValue("connector", parent_type)

                    comp_guide.setParamDefValue("comp_side", parent_side)
                    comp_guide.setParamDefValue("ui_host", parent_uihost)
                    comp_guide.setParamDefValue("ctlGrp", parent_ctlGrp)

                    break

                parent_root = parent_root.getParent()

        comp_guide.drawFromUI(parent)

    def drawUpdate(self, oldRoot, parent=None):

        # Initial hierarchy
        if parent is None:
            self.initialHierarchy()
            parent = self.model
            newParentName = parent.name()

        # controls shape
        try:
            pm.delete(pm.PyNode(newParentName + "|controllers_org"))
            oldRootName = oldRoot.name().split("|")[0] + "|controllers_org"
            pm.parent(oldRootName, newParentName)
        except TypeError:
            pm.displayError("The guide don't have controllers_org")

        # Components
        for name in self.componentsIndex:
            comp_guide = self.components[name]
            oldParentName = comp_guide.root.getParent().name()

            try:
                parent = pm.PyNode(oldParentName.replace(
                    oldParentName.split("|")[0], newParentName))
            except TypeError:
                pm.displayWarning("No parent for the guide")
                parent = self.model

            comp_guide.draw(parent)

    def update(self, sel, force=False):
        """Update the guide if a parameter is missing"""

        if pm.attributeQuery("ismodel", node=sel, ex=True):
            self.model = sel
        else:
            pm.displayWarning("select the top guide node")
            return

        name = self.model.name()
        self.setFromHierarchy(self.model, True)
        if self.valid and not force:
            pm.displayInfo("The Guide is updated")
            return

        pm.rename(self.model, name + "_old")
        deleteLater = self.model
        self.drawUpdate(deleteLater)
        pm.rename(self.model, name)
        pm.displayInfo("The guide %s have been updated" % name)
        pm.delete(deleteLater)

    def duplicate(self, root, symmetrize=False):
        """Duplicate the guide hierarchy

        Note:
            Indeed this method is not duplicating.
            What it is doing is parse the compoment guide,
            and creating an new one base on the current selection.

        Warning:
            Don't use the default Maya's duplicate tool to duplicate a
            Shifter's guide.

        Arguments:
            root (dagNode): The guide root to duplicate.
            symmetrize (bool): If True, duplicate symmetrical in X axis.
            The guide have to be "Left" or "Right".

        """
        if not pm.attributeQuery("comp_type", node=root, ex=True):
            mgear.log("Select a component root to duplicate", mgear.sev_error)
            return

        self.setFromHierarchy(root)
        for name in self.componentsIndex:
            comp_guide = self.components[name]
            if symmetrize:
                if not comp_guide.symmetrize():
                    return

        # Draw
        if pm.attributeQuery("ismodel", node=root, ex=True):
            self.draw()

        else:

            for name in self.componentsIndex:
                comp_guide = self.components[name]

                if comp_guide.parentComponent is None:
                    parent = comp_guide.root.getParent()
                    if symmetrize:
                        parent = dag.findChild(
                            self.model,
                            string.convertRLName(
                                comp_guide.root.getParent().name()))
                        if not parent:
                            parent = comp_guide.root.getParent()

                    else:
                        parent = comp_guide.root.getParent()

                else:
                    parent = dag.findChild(
                        self.model,
                        comp_guide.parentComponent.getName(
                            comp_guide.parentLocalName))
                    if not parent:
                        mgear.log(
                            "Unable to find parent (%s.%s) for guide %s" %
                            (comp_guide.parentComponent.getFullName,
                                comp_guide.parentLocalName,
                                comp_guide.getFullName))
                        parent = self.model

                # Reset the root so we force the draw to duplicate
                comp_guide.root = None

                comp_guide.setIndex(self.model)

                comp_guide.draw(parent)

        pm.select(self.components[self.componentsIndex[0]].root)

    def updateProperties(self, root, newName, newSide, newIndex):
        """Update the Properties of the component.

        Arguments:
            root (dagNode): Root of the component.
            newName (str): New name of the component
            newSide (str): New side of the component
            newIndex (str): New index of the component
        """

        if not pm.attributeQuery("comp_type", node=root, ex=True):
            mgear.log("Select a root to edit properties", mgear.sev_error)
            return
        self.setFromHierarchy(root, False)
        name = "_".join(root.name().split("|")[-1].split("_")[0:2])
        comp_guide = self.components[name]
        comp_guide.rename(root, newName, newSide, newIndex)

class HelperSlots(object):

    def updateHostUI(self, lEdit, targetAttr):
        oType = pm.nodetypes.Transform

        oSel = pm.selected()
        if oSel:
            if isinstance(oSel[0], oType) and oSel[0].hasAttr("isGearGuide"):
                lEdit.setText(oSel[0].name())
                self.root.attr(targetAttr).set(lEdit.text())
            else:
                pm.displayWarning("The selected element is not a "
                                  "valid object or not from a guide")
        else:
            pm.displayWarning("Please select first the object.")

    def updateLineEdit(self, lEdit, targetAttr):
        name = string.removeInvalidCharacter(lEdit.text())
        self.root.attr(targetAttr).set(name)

    def addItem2listWidget(self, listWidget, targetAttr=None):

        items = pm.selected()
        itemsList = [i.text() for i in listWidget.findItems(
            "", QtCore.Qt.MatchContains)]
        # Quick clean the first empty item
        if itemsList and not itemsList[0]:
            listWidget.takeItem(0)

        for item in items:
            if item.name() not in itemsList:
                if item.hasAttr("isGearGuide"):
                    listWidget.addItem(item.name())

                else:
                    pm.displayWarning(
                        "The object: %s, is not a valid"
                        " reference, Please select only guide componet"
                        " roots and guide locators." % item.name())
            else:
                pm.displayWarning("The object: %s, is already in the list." %
                                  item.name())

        if targetAttr:
            self.updateListAttr(listWidget, targetAttr)

    def removeSelectedFromListWidget(self, listWidget, targetAttr=None):
        for item in listWidget.selectedItems():
            listWidget.takeItem(listWidget.row(item))
        if targetAttr:
            self.updateListAttr(listWidget, targetAttr)

    def moveFromListWidget2ListWidget(self, sourceListWidget, targetListWidget,
                                      targetAttrListWidget, targetAttr=None):
        # Quick clean the first empty item
        itemsList = [i.text() for i in targetAttrListWidget.findItems(
            "", QtCore.Qt.MatchContains)]
        if itemsList and not itemsList[0]:
            targetAttrListWidget.takeItem(0)

        for item in sourceListWidget.selectedItems():
            targetListWidget.addItem(item.text())
            sourceListWidget.takeItem(sourceListWidget.row(item))

        if targetAttr:
            self.updateListAttr(targetAttrListWidget, targetAttr)

    def copyFromListWidget(self, sourceListWidget, targetListWidget,
                           targetAttr=None):
        targetListWidget.clear()
        itemsList = [i.text() for i in sourceListWidget.findItems(
            "", QtCore.Qt.MatchContains)]
        for item in itemsList:
            targetListWidget.addItem(item)
        if targetAttr:
            self.updateListAttr(sourceListWidget, targetAttr)

    def updateListAttr(self, sourceListWidget, targetAttr):
        """Update the string attribute with values separated by commas"""
        newValue = ",".join([i.text() for i in sourceListWidget.findItems(
            "", QtCore.Qt.MatchContains)])
        self.root.attr(targetAttr).set(newValue)

    def updateComponentName(self):

        newName = self.mainSettingsTab.name_lineEdit.text()
        # remove invalid characters in the name and update
        newName = string.removeInvalidCharacter(newName)
        self.mainSettingsTab.name_lineEdit.setText(newName)
        sideSet = ["C", "L", "R"]
        sideIndex = self.mainSettingsTab.side_comboBox.currentIndex()
        newSide = sideSet[sideIndex]
        newIndex = self.mainSettingsTab.componentIndex_spinBox.value()
        guide = Rig()
        guide.updateProperties(self.root, newName, newSide, newIndex)
        pm.select(self.root, r=True)
        # sync index
        self.mainSettingsTab.componentIndex_spinBox.setValue(
            self.root.attr("comp_index").get())

    def updateConnector(self, sourceWidget, itemsList, *args):
        self.root.attr("connector").set(itemsList[sourceWidget.currentIndex()])

    def populateCheck(self, targetWidget, sourceAttr, *args):
        if self.root.attr(sourceAttr).get():
            targetWidget.setCheckState(QtCore.Qt.Checked)
        else:
            targetWidget.setCheckState(QtCore.Qt.Unchecked)

    def updateCheck(self, sourceWidget, targetAttr, *args):
        self.root.attr(targetAttr).set(sourceWidget.isChecked())

    def updateSpinBox(self, sourceWidget, targetAttr, *args):
        self.root.attr(targetAttr).set(sourceWidget.value())
        return True

    def updateSlider(self, sourceWidget, targetAttr, *args):
        self.root.attr(targetAttr).set(float(sourceWidget.value()) / 100)

    def updateComboBox(self, sourceWidget, targetAttr, *args):
        self.root.attr(targetAttr).set(sourceWidget.currentIndex())

    def updateControlShape(self, sourceWidget, ctlList, targetAttr, *args):
        curIndx = sourceWidget.currentIndex()
        self.root.attr(targetAttr).set(ctlList[curIndx])

    def setProfile(self):
        pm.select(self.root, r=True)
        pm.runtime.GraphEditor()

    def close_settings(self):
        self.close()
        pyqt.deleteInstances(self, MayaQDockWidget)

    def editFile(self, widgetList):
        try:
            filepath = widgetList.selectedItems()[0].text().split("|")[-1][1:]
            if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
                editPath = os.path.join(
                    os.environ.get(
                        MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""), filepath)
            else:
                editPath = filepath
            if filepath:
                if sys.platform.startswith('darwin'):
                    subprocess.call(('open', editPath))
                elif os.name == 'nt':
                    os.startfile(editPath)
                elif os.name == 'posix':
                    subprocess.call(('xdg-open', editPath))
            else:
                pm.displayWarning("Please select one item from the list")
        except Exception:
            pm.displayError("The step can't be find or does't exists")

    @classmethod
    def runStep(self, stepPath, customStepDic):
        try:
            with pm.UndoChunk():
                pm.displayInfo(
                    "EXEC: Executing custom step: %s" % stepPath)
                fileName = os.path.split(stepPath)[1].split(".")[0]
                if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
                    runPath = os.path.join(
                        os.environ.get(
                            MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""), stepPath)
                else:
                    runPath = stepPath
                customStep = imp.load_source(fileName, runPath)
                if hasattr(customStep, "CustomShifterStep"):
                    cs = customStep.CustomShifterStep()
                    cs.run(customStepDic)
                    customStepDic[cs.name] = cs
                    pm.displayInfo(
                        "SUCCEED: Custom Shifter Step Class: %s. "
                        "Succeed!!" % stepPath)
                else:
                    pm.displayInfo(
                        "SUCCEED: Custom Step simple script: %s. "
                        "Succeed!!" % stepPath)

        except Exception as ex:
            template = "An exception of type {0} occured. "
            "Arguments:\n{1!r}"
            message = template.format(type(ex).__name__, ex.args)
            pm.displayError(message)
            pm.displayError(traceback.format_exc())
            cont = pm.confirmBox(
                "FAIL: Custom Step Fail",
                "The step:%s has failed. Continue with next step?" %
                stepPath + "\n\n" + message + "\n\n" +
                traceback.format_exc(),
                "Continue", "Stop Build", "Try Again!")
            if cont == "Stop Build":
                # stop Build
                return True
            elif cont == "Try Again!":
                try:  # just in case there is nothing to undo
                    pm.undo()
                except Exception:
                    pass
                pm.displayInfo("Trying again! : {}".format(stepPath))
                inception = self.runStep(stepPath, customStepDic)
                if inception:  # stops build from the recursion loop.
                    return True
            else:
                return False

    def runManualStep(self, widgetList):
        selItems = widgetList.selectedItems()
        for item in selItems:
            self.runStep(item.text().split("|")[-1][1:], customStepDic={})

class GuideSettingsTab(QtWidgets.QDialog, guui.Ui_Form):

    def __init__(self, parent=None):
        super(guideSettingsTab, self).__init__(parent)
        self.setupUi(self)

class CustomStepTab(QtWidgets.QDialog, csui.Ui_Form):

    def __init__(self, parent=None):
        super(customStepTab, self).__init__(parent)
        self.setupUi(self)

class GuideSettings(MayaQWidgetDockableMixin, QtWidgets.QDialog, HelperSlots):
    # valueChanged = QtCore.Signal(int)
    greenBrush = QtGui.QBrush()
    greenBrush.setColor('#179e83')
    redBrush = QtGui.QBrush()
    redBrush.setColor('#9b2d22')
    whiteBrush = QtGui.QBrush()
    whiteBrush.setColor('#ffffff')
    whiteDownBrush = QtGui.QBrush()
    whiteDownBrush.setColor('#E2E2E2')
    orangeBrush = QtGui.QBrush()
    orangeBrush.setColor('#e67e22')

    def __init__(self, parent=None):
        self.toolName = TYPE
        # Delete old instances of the componet settings window.
        pyqt.deleteInstances(self, MayaQDockWidget)
        # super(self.__class__, self).__init__(parent=parent)
        super(guideSettings, self).__init__()
        # the inspectSettings function set the current selection to the
        # component root before open the settings dialog
        self.root = pm.selected()[0]

        self.guideSettingsTab = guideSettingsTab()
        self.customStepTab = customStepTab()

        self.setup_SettingWindow()
        self.create_controls()
        self.populate_controls()
        self.create_layout()
        self.create_connections()

        self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)

    def setup_SettingWindow(self):
        self.mayaMainWindow = pyqt.maya_main_window()

        self.setObjectName(self.toolName)
        self.setWindowFlags(QtCore.Qt.Window)
        self.setWindowTitle(TYPE)
        self.resize(500, 615)

    def create_controls(self):
        """Create the controls for the component base"""
        self.tabs = QtWidgets.QTabWidget()
        self.tabs.setObjectName("settings_tab")

        # Close Button
        self.close_button = QtWidgets.QPushButton("Close")

    def populate_controls(self):
        """Populate the controls values
            from the custom attributes of the component.

        """
        # populate tab
        self.tabs.insertTab(0, self.guideSettingsTab, "Guide Settings")
        self.tabs.insertTab(1, self.customStepTab, "Custom Steps")

        # populate main settings
        self.guideSettingsTab.rigName_lineEdit.setText(
            self.root.attr("rig_name").get())
        self.guideSettingsTab.mode_comboBox.setCurrentIndex(
            self.root.attr("mode").get())
        self.guideSettingsTab.step_comboBox.setCurrentIndex(
            self.root.attr("step").get())
        self.populateCheck(
            self.guideSettingsTab.proxyChannels_checkBox, "proxyChannels")

        self.populateCheck(self.guideSettingsTab.worldCtl_checkBox, "worldCtl")

        self.populateCheck(
            self.guideSettingsTab.classicChannelNames_checkBox,
            "classicChannelNames")
        self.populateCheck(
            self.guideSettingsTab.importSkin_checkBox, "importSkin")
        self.guideSettingsTab.skin_lineEdit.setText(
            self.root.attr("skin").get())
        self.populateCheck(
            self.guideSettingsTab.jointRig_checkBox, "joint_rig")
        self.populateAvailableSynopticTabs()

        for item in self.root.attr("synoptic").get().split(","):
            self.guideSettingsTab.rigTabs_listWidget.addItem(item)

        self.guideSettingsTab.L_color_fk_spinBox.setValue(
            self.root.attr("L_color_fk").get())
        self.guideSettingsTab.L_color_ik_spinBox.setValue(
            self.root.attr("L_color_ik").get())
        self.guideSettingsTab.C_color_fk_spinBox.setValue(
            self.root.attr("C_color_fk").get())
        self.guideSettingsTab.C_color_ik_spinBox.setValue(
            self.root.attr("C_color_ik").get())
        self.guideSettingsTab.R_color_fk_spinBox.setValue(
            self.root.attr("R_color_fk").get())
        self.guideSettingsTab.R_color_ik_spinBox.setValue(
            self.root.attr("R_color_ik").get())

        # pupulate custom steps sttings
        self.populateCheck(
            self.customStepTab.preCustomStep_checkBox, "doPreCustomStep")
        for item in self.root.attr("preCustomStep").get().split(","):
            self.customStepTab.preCustomStep_listWidget.addItem(item)
        self.refreshStatusColor(self.customStepTab.preCustomStep_listWidget)

        self.populateCheck(
            self.customStepTab.postCustomStep_checkBox, "doPostCustomStep")
        for item in self.root.attr("postCustomStep").get().split(","):
            self.customStepTab.postCustomStep_listWidget.addItem(item)
        self.refreshStatusColor(self.customStepTab.postCustomStep_listWidget)

    def create_layout(self):
        """
        Create the layout for the component base settings

        """
        self.settings_layout = QtWidgets.QVBoxLayout()
        self.settings_layout.addWidget(self.tabs)
        self.settings_layout.addWidget(self.close_button)

        self.setLayout(self.settings_layout)

    def create_connections(self):
        """Create the slots connections to the controls functions"""
        self.close_button.clicked.connect(self.close_settings)

        # Setting Tab
        tap = self.guideSettingsTab
        tap.rigName_lineEdit.editingFinished.connect(
            partial(self.updateLineEdit,
                    tap.rigName_lineEdit,
                    "rig_name"))
        tap.mode_comboBox.currentIndexChanged.connect(
            partial(self.updateComboBox,
                    tap.mode_comboBox,
                    "mode"))
        tap.step_comboBox.currentIndexChanged.connect(
            partial(self.updateComboBox,
                    tap.step_comboBox,
                    "step"))
        tap.proxyChannels_checkBox.stateChanged.connect(
            partial(self.updateCheck,
                    tap.proxyChannels_checkBox,
                    "proxyChannels"))
        tap.worldCtl_checkBox.stateChanged.connect(
            partial(self.updateCheck,
                    tap.worldCtl_checkBox,
                    "worldCtl"))
        tap.classicChannelNames_checkBox.stateChanged.connect(
            partial(self.updateCheck,
                    tap.classicChannelNames_checkBox,
                    "classicChannelNames"))
        tap.importSkin_checkBox.stateChanged.connect(
            partial(self.updateCheck,
                    tap.importSkin_checkBox,
                    "importSkin"))
        tap.jointRig_checkBox.stateChanged.connect(
            partial(self.updateCheck,
                    tap.jointRig_checkBox,
                    "joint_rig"))
        tap.addTab_pushButton.clicked.connect(
            partial(self.moveFromListWidget2ListWidget,
                    tap.available_listWidget,
                    tap.rigTabs_listWidget,
                    tap.rigTabs_listWidget,
                    "synoptic"))
        tap.removeTab_pushButton.clicked.connect(
            partial(self.moveFromListWidget2ListWidget,
                    tap.rigTabs_listWidget,
                    tap.available_listWidget,
                    tap.rigTabs_listWidget,
                    "synoptic"))
        tap.loadSkinPath_pushButton.clicked.connect(
            self.skinLoad)
        tap.rigTabs_listWidget.installEventFilter(self)

        tap.L_color_fk_spinBox.valueChanged.connect(
            partial(self.updateSpinBox,
                    tap.L_color_fk_spinBox,
                    "L_color_fk"))
        tap.L_color_ik_spinBox.valueChanged.connect(
            partial(self.updateSpinBox,
                    tap.L_color_ik_spinBox,
                    "L_color_ik"))
        tap.C_color_fk_spinBox.valueChanged.connect(
            partial(self.updateSpinBox,
                    tap.C_color_fk_spinBox,
                    "C_color_fk"))
        tap.C_color_ik_spinBox.valueChanged.connect(
            partial(self.updateSpinBox,
                    tap.C_color_ik_spinBox,
                    "C_color_ik"))
        tap.R_color_fk_spinBox.valueChanged.connect(
            partial(self.updateSpinBox,
                    tap.R_color_fk_spinBox,
                    "R_color_fk"))
        tap.R_color_ik_spinBox.valueChanged.connect(
            partial(self.updateSpinBox,
                    tap.R_color_ik_spinBox,
                    "R_color_ik"))

        # custom Step Tab
        csTap = self.customStepTab
        csTap.preCustomStep_checkBox.stateChanged.connect(
            partial(self.updateCheck,
                    csTap.preCustomStep_checkBox,
                    "doPreCustomStep"))
        csTap.preCustomStepAdd_pushButton.clicked.connect(
            self.addCustomStep)
        csTap.preCustomStepNew_pushButton.clicked.connect(
            self.newCustomStep)
        csTap.preCustomStepDuplicate_pushButton.clicked.connect(
            self.duplicateCustomStep)
        csTap.preCustomStepExport_pushButton.clicked.connect(
            self.exportCustomStep)
        csTap.preCustomStepImport_pushButton.clicked.connect(
            self.importCustomStep)
        csTap.preCustomStepRemove_pushButton.clicked.connect(
            partial(self.removeSelectedFromListWidget,
                    csTap.preCustomStep_listWidget,
                    "preCustomStep"))
        csTap.preCustomStep_listWidget.installEventFilter(self)
        csTap.preCustomStepRun_pushButton.clicked.connect(
            partial(self.runManualStep,
                    csTap.preCustomStep_listWidget))
        csTap.preCustomStepEdit_pushButton.clicked.connect(
            partial(self.editFile,
                    csTap.preCustomStep_listWidget))

        csTap.postCustomStep_checkBox.stateChanged.connect(
            partial(self.updateCheck,
                    csTap.postCustomStep_checkBox,
                    "doPostCustomStep"))
        csTap.postCustomStepAdd_pushButton.clicked.connect(
            partial(self.addCustomStep, False))
        csTap.postCustomStepNew_pushButton.clicked.connect(
            partial(self.newCustomStep, False))
        csTap.postCustomStepDuplicate_pushButton.clicked.connect(
            partial(self.duplicateCustomStep, False))
        csTap.postCustomStepExport_pushButton.clicked.connect(
            partial(self.exportCustomStep, False))
        csTap.postCustomStepImport_pushButton.clicked.connect(
            partial(self.importCustomStep, False))
        csTap.postCustomStepRemove_pushButton.clicked.connect(
            partial(self.removeSelectedFromListWidget,
                    csTap.postCustomStep_listWidget,
                    "postCustomStep"))
        csTap.postCustomStep_listWidget.installEventFilter(self)
        csTap.postCustomStepRun_pushButton.clicked.connect(
            partial(self.runManualStep,
                    csTap.postCustomStep_listWidget))
        csTap.postCustomStepEdit_pushButton.clicked.connect(
            partial(self.editFile,
                    csTap.postCustomStep_listWidget))

        # right click menus
        csTap.preCustomStep_listWidget.setContextMenuPolicy(
            QtCore.Qt.CustomContextMenu)
        csTap.preCustomStep_listWidget.customContextMenuRequested.connect(
            self.preCustomStepMenu)
        csTap.postCustomStep_listWidget.setContextMenuPolicy(
            QtCore.Qt.CustomContextMenu)
        csTap.postCustomStep_listWidget.customContextMenuRequested.connect(
            self.postCustomStepMenu)

        # search hightlight
        csTap.preSearch_lineEdit.textChanged.connect(
            self.preHighlightSearch)
        csTap.postSearch_lineEdit.textChanged.connect(
            self.postHighlightSearch)

    def eventFilter(self, sender, event):
        if event.type() == QtCore.QEvent.ChildRemoved:
            if sender == self.guideSettingsTab.rigTabs_listWidget:
                self.updateListAttr(sender, "synoptic")
            elif sender == self.customStepTab.preCustomStep_listWidget:
                self.updateListAttr(sender, "preCustomStep")
            elif sender == self.customStepTab.postCustomStep_listWidget:
                self.updateListAttr(sender, "postCustomStep")
            return True
        else:
            return QtWidgets.QDialog.eventFilter(self, sender, event)

    # Slots ########################################################

    def populateAvailableSynopticTabs(self):

        import mgear.maya.shifter as shifter
        defPath = os.environ.get("MGEAR_SYNOPTIC_PATH", None)
        if not defPath or not os.path.isdir(defPath):
            defPath = shifter.SYNOPTIC_PATH

        tabsDirectories = [name for name in os.listdir(defPath) if
                           os.path.isdir(os.path.join(defPath, name))]
        # Quick clean the first empty item
        if tabsDirectories and not tabsDirectories[0]:
            self.guideSettingsTab.available_listWidget.takeItem(0)

        itemsList = self.root.attr("synoptic").get().split(",")
        for tab in sorted(tabsDirectories):
            if tab not in itemsList:
                self.guideSettingsTab.available_listWidget.addItem(tab)

    def skinLoad(self, *args):
        startDir = self.root.attr("skin").get()
        filePath = pm.fileDialog2(
            dialogStyle=2,
            fileMode=1,
            startingDirectory=startDir,
            okc="Apply",
            fileFilter='mGear skin (*%s)' % skin.FILE_EXT)
        if not filePath:
            return
        if not isinstance(filePath, basestring):
            filePath = filePath[0]

        self.root.attr("skin").set(filePath)
        self.guideSettingsTab.skin_lineEdit.setText(filePath)

    def addCustomStep(self, pre=True, *args):
        """Add a new custom step

        Arguments:
            pre (bool, optional): If true adds the steps to the pre step list
            *args: Maya's Dummy

        Returns:
            None: None
        """

        if pre:
            stepAttr = "preCustomStep"
            stepWidget = self.customStepTab.preCustomStep_listWidget
        else:
            stepAttr = "postCustomStep"
            stepWidget = self.customStepTab.postCustomStep_listWidget

        # Check if we have a custom env for the custom steps initial folder
        if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
            startDir = os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, "")
        else:
            startDir = self.root.attr(stepAttr).get()

        filePath = pm.fileDialog2(
            dialogStyle=2,
            fileMode=1,
            startingDirectory=startDir,
            okc="Add",
            fileFilter='Custom Step .py (*.py)')
        if not filePath:
            return
        if not isinstance(filePath, basestring):
            filePath = filePath[0]

        # Quick clean the first empty item
        itemsList = [i.text() for i in stepWidget.findItems(
            "", QtCore.Qt.MatchContains)]
        if itemsList and not itemsList[0]:
            stepWidget.takeItem(0)

        if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
            filePath = os.path.abspath(filePath)
            baseReplace = os.path.abspath(os.environ.get(
                MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""))
            filePath = filePath.replace(baseReplace, "")[1:]

        fileName = os.path.split(filePath)[1].split(".")[0]
        stepWidget.addItem(fileName + " | " + filePath)
        self.updateListAttr(stepWidget, stepAttr)

    def newCustomStep(self, pre=True, *args):
        """Creates a new custom step

        Arguments:
            pre (bool, optional): If true adds the steps to the pre step list
            *args: Maya's Dummy

        Returns:
            None: None
        """

        if pre:
            stepAttr = "preCustomStep"
            stepWidget = self.customStepTab.preCustomStep_listWidget
        else:
            stepAttr = "postCustomStep"
            stepWidget = self.customStepTab.postCustomStep_listWidget

        # Check if we have a custom env for the custom steps initial folder
        if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
            startDir = os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, "")
        else:
            startDir = self.root.attr(stepAttr).get()

        filePath = pm.fileDialog2(
            dialogStyle=2,
            fileMode=0,
            startingDirectory=startDir,
            okc="New",
            fileFilter='Custom Step .py (*.py)')
        if not filePath:
            return
        if not isinstance(filePath, basestring):
            filePath = filePath[0]

        n, e = os.path.splitext(filePath)
        stepName = os.path.split(n)[-1]
        # raw custome step string
        rawString = r'''
import mgear.maya.shifter.customStep as cstp

class CustomShifterStep(cstp.customShifterMainStep):
    def __init__(self):
        self.name = "%s"

    def run(self, stepDict):
        """Run method.

            i.e:  stepDict["mgearRun"].global_ctl  gets the global_ctl from
                    shifter rig on post step
            i.e:  stepDict["otherCustomStepName"].ctlMesh  gets the ctlMesh
                    from a previous custom step called "otherCustomStepName"
        Arguments:
            stepDict (dict): Dictionary containing the objects from
                the previous steps

        Returns:
            None: None
        """
        return''' % stepName
        f = open(filePath, 'w')
        f.write(rawString + "\n")
        f.close()

        # Quick clean the first empty item
        itemsList = [i.text() for i in stepWidget.findItems(
            "", QtCore.Qt.MatchContains)]
        if itemsList and not itemsList[0]:
            stepWidget.takeItem(0)

        if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
            filePath = os.path.abspath(filePath)
            baseReplace = os.path.abspath(os.environ.get(
                MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""))
            filePath = filePath.replace(baseReplace, "")[1:]

        fileName = os.path.split(filePath)[1].split(".")[0]
        stepWidget.addItem(fileName + " | " + filePath)
        self.updateListAttr(stepWidget, stepAttr)

    def duplicateCustomStep(self, pre=True, *args):
        """Duplicate the selected step

        Arguments:
            pre (bool, optional): If true adds the steps to the pre step list
            *args: Maya's Dummy

        Returns:
            None: None
        """

        if pre:
            stepAttr = "preCustomStep"
            stepWidget = self.customStepTab.preCustomStep_listWidget
        else:
            stepAttr = "postCustomStep"
            stepWidget = self.customStepTab.postCustomStep_listWidget

        # Check if we have a custom env for the custom steps initial folder
        if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
            startDir = os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, "")
        else:
            startDir = self.root.attr(stepAttr).get()

        if stepWidget.selectedItems():
            sourcePath = stepWidget.selectedItems()[0].text().split(
                "|")[-1][1:]

        filePath = pm.fileDialog2(
            dialogStyle=2,
            fileMode=0,
            startingDirectory=startDir,
            okc="New",
            fileFilter='Custom Step .py (*.py)')
        if not filePath:
            return
        if not isinstance(filePath, basestring):
            filePath = filePath[0]

        if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
            sourcePath = os.path.join(startDir, sourcePath)
        shutil.copy(sourcePath, filePath)

        # Quick clean the first empty item
        itemsList = [i.text() for i in stepWidget.findItems(
            "", QtCore.Qt.MatchContains)]
        if itemsList and not itemsList[0]:
            stepWidget.takeItem(0)

        if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
            filePath = os.path.abspath(filePath)
            baseReplace = os.path.abspath(os.environ.get(
                MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""))
            filePath = filePath.replace(baseReplace, "")[1:]

        fileName = os.path.split(filePath)[1].split(".")[0]
        stepWidget.addItem(fileName + " | " + filePath)
        self.updateListAttr(stepWidget, stepAttr)

    def exportCustomStep(self, pre=True, *args):
        """Export custom steps to a json file

        Arguments:
            pre (bool, optional): If true takes the steps from the
                pre step list
            *args: Maya's Dummy

        Returns:
            None: None

        """

        if pre:
            stepWidget = self.customStepTab.preCustomStep_listWidget
        else:
            stepWidget = self.customStepTab.postCustomStep_listWidget

        # Quick clean the first empty item
        itemsList = [i.text() for i in stepWidget.findItems(
            "", QtCore.Qt.MatchContains)]
        if itemsList and not itemsList[0]:
            stepWidget.takeItem(0)

        # Check if we have a custom env for the custom steps initial folder
        if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
            startDir = os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, "")
            itemsList = [os.path.join(startDir, i.text().split("|")[-1][1:])
                         for i in stepWidget.findItems(
                         "", QtCore.Qt.MatchContains)]
        else:
            itemsList = [i.text().split("|")[-1][1:]
                         for i in stepWidget.findItems(
                         "", QtCore.Qt.MatchContains)]
            if itemsList:
                startDir = os.path.split(itemsList[-1])[0]
            else:
                pm.displayWarning("No custom steps to export.")
                return

        stepsDict = {}
        stepsDict["itemsList"] = itemsList
        for item in itemsList:
            step = open(item, "r")
            data = step.read()
            stepsDict[item] = data
            step.close()

        data_string = json.dumps(stepsDict, indent=4, sort_keys=True)
        filePath = pm.fileDialog2(
            dialogStyle=2,
            fileMode=0,
            startingDirectory=startDir,
            fileFilter='Shifter Custom Steps .scs (*%s)' % ".scs")
        if not filePath:
            return
        if not isinstance(filePath, basestring):
            filePath = filePath[0]
        f = open(filePath, 'w')
        f.write(data_string)
        f.close()

    def importCustomStep(self, pre=True, *args):
        """Import custom steps from a json file

        Arguments:
            pre (bool, optional): If true import to pre steps list
            *args: Maya's Dummy

        Returns:
            None: None

        """

        if pre:
            stepAttr = "preCustomStep"
            stepWidget = self.customStepTab.preCustomStep_listWidget
        else:
            stepAttr = "postCustomStep"
            stepWidget = self.customStepTab.postCustomStep_listWidget

        # option import only paths or unpack steps
        option = pm.confirmDialog(
            title='Shifter Custom Step Import Style',
            message='Do you want to import only the path or'
                    ' unpack and import?',
            button=['Only Path', 'Unpack', 'Cancel'],
            defaultButton='Only Path',
            cancelButton='Cancel',
            dismissString='Cancel')

        if option in ['Only Path', 'Unpack']:
            if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
                startDir = os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, "")
            else:
                startDir = pm.workspace(q=True, rootDirectory=True)

            filePath = pm.fileDialog2(
                dialogStyle=2,
                fileMode=1,
                startingDirectory=startDir,
                fileFilter='Shifter Custom Steps .scs (*%s)' % ".scs")
            if not filePath:
                return
            if not isinstance(filePath, basestring):
                filePath = filePath[0]
            stepDict = json.load(open(filePath))
            stepsList = []

        if option == 'Only Path':
            for item in stepDict["itemsList"]:
                stepsList.append(item)

        elif option == 'Unpack':
            unPackDir = pm.fileDialog2(
                dialogStyle=2,
                fileMode=2,
                startingDirectory=startDir)
            if not filePath:
                return
            if not isinstance(unPackDir, basestring):
                unPackDir = unPackDir[0]

            for item in stepDict["itemsList"]:
                fileName = os.path.split(item)[1]
                fileNewPath = os.path.join(unPackDir, fileName)
                stepsList.append(fileNewPath)
                f = open(fileNewPath, 'w')
                f.write(stepDict[item])
                f.close()

        if option in ['Only Path', 'Unpack']:

            for item in stepsList:
                # Quick clean the first empty item
                itemsList = [i.text() for i in stepWidget.findItems(
                    "", QtCore.Qt.MatchContains)]
                if itemsList and not itemsList[0]:
                    stepWidget.takeItem(0)

                if os.environ.get(MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""):
                    item = os.path.abspath(item)
                    baseReplace = os.path.abspath(os.environ.get(
                        MGEAR_SHIFTER_CUSTOMSTEP_KEY, ""))
                    item = item.replace(baseReplace, "")[1:]

                fileName = os.path.split(item)[1].split(".")[0]
                stepWidget.addItem(fileName + " | " + item)
                self.updateListAttr(stepWidget, stepAttr)

    def _customStepMenu(self, cs_listWidget, stepAttr, QPos):
        "right click context menu for custom step"
        currentSelection = cs_listWidget.currentItem()
        if currentSelection is None:
            return
        self.csMenu = QtWidgets.QMenu()
        parentPosition = cs_listWidget.mapToGlobal(QtCore.QPoint(0, 0))
        menu_item_01 = self.csMenu.addAction("Toggle Custom Step")
        self.csMenu.addSeparator()
        menu_item_02 = self.csMenu.addAction("Turn OFF Selected")
        menu_item_03 = self.csMenu.addAction("Turn ON Selected")
        self.csMenu.addSeparator()
        menu_item_04 = self.csMenu.addAction("Turn OFF All")
        menu_item_05 = self.csMenu.addAction("Turn ON All")

        menu_item_01.triggered.connect(partial(self.toggleStatusCustomStep,
                                               cs_listWidget,
                                               stepAttr))
        menu_item_02.triggered.connect(partial(self.setStatusCustomStep,
                                               cs_listWidget,
                                               stepAttr,
                                               False))
        menu_item_03.triggered.connect(partial(self.setStatusCustomStep,
                                               cs_listWidget,
                                               stepAttr,
                                               True))
        menu_item_04.triggered.connect(partial(self.setStatusCustomStep,
                                               cs_listWidget,
                                               stepAttr,
                                               False,
                                               False))
        menu_item_05.triggered.connect(partial(self.setStatusCustomStep,
                                               cs_listWidget,
                                               stepAttr,
                                               True,
                                               False))

        self.csMenu.move(parentPosition + QPos)
        self.csMenu.show()

    def preCustomStepMenu(self, QPos):
        self._customStepMenu(self.customStepTab.preCustomStep_listWidget,
                             "preCustomStep",
                             QPos)

    def postCustomStepMenu(self, QPos):
        self._customStepMenu(self.customStepTab.postCustomStep_listWidget,
                             "postCustomStep",
                             QPos)

    def toggleStatusCustomStep(self, cs_listWidget, stepAttr):
        items = cs_listWidget.selectedItems()
        for item in items:
            if item.text().startswith("*"):
                item.setText(item.text()[1:])
                item.setForeground(self.whiteDownBrush)
            else:
                item.setText("*" + item.text())
                item.setForeground(self.redBrush)

        self.updateListAttr(cs_listWidget, stepAttr)

    def setStatusCustomStep(
            self, cs_listWidget, stepAttr, status=True, selected=True):
        if selected:
            items = cs_listWidget.selectedItems()
        else:
            items = self.getAllItems(cs_listWidget)
        for item in items:
            off = item.text().startswith("*")
            if status and off:
                item.setText(item.text()[1:])
            elif not status and not off:
                item.setText("*" + item.text())
            self.setStatusColor(item)
        self.updateListAttr(cs_listWidget, stepAttr)

    def getAllItems(self, cs_listWidget):
        return [cs_listWidget.item(i) for i in range(cs_listWidget.count())]

    def setStatusColor(self, item):
        if item.text().startswith("*"):
            item.setForeground(self.redBrush)
        else:
            item.setForeground(self.whiteDownBrush)

    def refreshStatusColor(self, cs_listWidget):
        items = self.getAllItems(cs_listWidget)
        for i in items:
            self.setStatusColor(i)

    # Highligter filter
    def _highlightSearch(self, cs_listWidget, searchText):
        items = self.getAllItems(cs_listWidget)
        for i in items:
            if searchText and searchText.lower() in i.text().lower():
                i.setBackground(QtGui.QColor(128, 128, 128, 255))
            else:
                i.setBackground(QtGui.QColor(255, 255, 255, 0))

    def preHighlightSearch(self):
        searchText = self.customStepTab.preSearch_lineEdit.text()
        self._highlightSearch(self.customStepTab.preCustomStep_listWidget,
                              searchText)

    def postHighlightSearch(self):
        searchText = self.customStepTab.postSearch_lineEdit.text()
        self._highlightSearch(self.customStepTab.postCustomStep_listWidget,
                              searchText)

# Backwards compatibility aliases
MainGuide = Main
RigGuide = Rig
helperSlots = HelperSlots
guideSettingsTab = GuideSettingsTab
customStepTab = CustomStepTab
guideSettings = GuideSettings


'''
Dynamitelaw

Defines the tradingAccount class, which keeps track of balance,
trading history, owned stocks, and handles buy and sell orders.
'''

import pandas as pd
import utils
import PandaDatabase as database
import datetime
import time
import os
import RetrieveStockData as rsd
import numpy as np
import json

#=============================================================================
#       tradingAccount class
#=============================================================================

class tradingAccount():
    def __init__(self, name="TESTACCOUNT"):
        '''
        Trading Account constructor. Must be passed a database object.
        '''
        self.stocksOwned = {}
        self.balance = 0  #balance is an integer (in cents), NOT dollars
        self.stockAssets = 0  #assets is an integer (in cents), NOT dollars
        self.commision = 0 #balance is an integer (in cents), NOT dollars

        self.timeSaved = "NONE"

        self.name = utils.sanitizeString(name)
        cwd = os.getcwd()
        self.savepath = cwd + "\\Data\AccountData\\" + self.name

        if not os.path.exists(self.savepath):
            os.makedirs(self.savepath)

        self.dailyLogColumns = ["Date", "Ticker", "Quantity", "Price", "Action","TimeOfExecution", "TotalAssets", "LiquidFunds", "StockAssets"]
        self.dailyLogs = pd.DataFrame(columns=self.dailyLogColumns)

        self.tradeHistoryColumns = ["Ticker", "Date Bought", "Buy Price", "Date Sold", "Sell Price", "Quantity", "Commission", "Trade Profit"]
        self.tradeHistory = pd.DataFrame(columns=self.tradeHistoryColumns)

    def depositFunds(self, dollarValue):
        '''
        Increase (or decrease) account balance
        '''
        if (dollarValue >= 0):
            centValue = int(dollarValue*100)
            self.balance += centValue
        else:
            raise ValueError("Negative deposit value passed")

    def withdrawFunds(self, dollarValue):
        '''
        Decreases account balance
        '''
        centValue = int(dollarValue*100)
        if (centValue <= self.balance):
            self.balance -= centValue
        else:
            raise ValueError("Insufficient funds to execute withdrawl")

    def getBalance(self, denomination="Dollars"):
        '''
        Returns the current account balance in specified denomination
        Valid arguments: denomination=["Dollars", "Cents"]
        '''
        if (denomination=="Dollars"):
            return (float(self.balance) / 100)
        elif (denomination=="Cents"):
            return self.balance
        else:
            raise ValueError("Invalid denomination passed")

    def getOwnedStocks(self):
        '''
        Returns a dictionary of stocks owned by this account
        '''
        return self.stocksOwned

    def setCommision(self, commision):
        '''
        Sets the commision on each trade (in $)
        '''
        self.commision = int(commision*100)

    def updateAssets(self, date=False):
        '''
        Updates the net value of all currently owned stock assets.
        If date is not included, the current real-time price is used.
        '''
        self.stockAssets = 0

        for ticker in self.stocksOwned:
            quantityOwned = self.stocksOwned.get(ticker)[0]
            if (date):
                tickerData = database.getDataframe(ticker)
                try:
                    isMissing = pd.isnull(tickerData).loc[date,"Open"].bool()
                    if (not isMissing):
                        value = tickerData.loc[date, "Open"]
                    else:
                        value = self.stocksOwned.get(ticker)[3]

                except:
                    #Using old value if current value cannot be found
                    value = self.stocksOwned.get(ticker)[3]

                #Check to see if any splits occured
                try:
                    splitData = pd.DataFrame.from_csv("Data/SplitData/"+ticker+".csv").values.tolist()
                    for line in splitData[0]:
                        splitDict = json.loads(line.replace("'",'"'))
                        if splitDict['paymentDate']==date:
                            #print(splitDict)
                            self.stocksOwned.get(ticker)[0] *= splitDict['toFactor']
                            quantityOwned = self.stocksOwned.get(ticker)[0]
                            #print("SPLIT:",ticker)
                except Exception as e:
                    pass

            else:
                value = rsd.getCurrentPrice(ticker)

            self.stockAssets += int(value*quantityOwned*100)
            #print (date + ": "+ticker + "  $" + str(float(value)))

    def placeBuyOrders(self, listOfOrders, date, simulation=True):
        '''
        places Sell Order for every order in listOfOrders.
        Each element in listOfOrders is a list in the following format:
            [<str ticker>, <int quantity>]
        Date is the current date, in string format "YYYY-MM-DD".
        Setting simulation to False will actually execute the buy orders (not yet implimented)
        '''
        if (not simulation):
            pass  #actually execute buy orders

        action = "Buy"
        executedOrders = []

        for order in listOfOrders:
            ticker = order[0]
            quantity = order[1]

            if (quantity > 0):
                tickerData = database.getDataframe(ticker)
                openPrice = tickerData.loc[date, "Open"]

                self.balance -= self.commision
                tradeCost = int(openPrice*quantity*100)

                if ((self.balance - tradeCost) >= 0):
                    #There is enough money for the trade
                    self.balance -= tradeCost  #*100 since balance is in cents

                    if (ticker in self.stocksOwned):
                        #If we own the stock, add the new quantity to the old quantity
                        self.stocksOwned[ticker] = self.stocksOwned[ticker][0] + quantity, openPrice, date, openPrice  #each element in stocksOwned is ticker: (quantity, buyPrice, dateOfPurchase, mostRecentPrice)
                    else:
                        self.stocksOwned[ticker] = quantity, openPrice, date, openPrice  #each element in stocksOwned is ticker: (quantity, buyPrice, dateOfPurchase, mostRecentPrice)

                    if (simulation):
                        self.updateAssets(date)
                    else:
                        self.updateAssets()

                    totalAssets = float(self.balance + self.stockAssets)/100

                    timeStamp = time.time()
                    timeOfExecution = datetime.datetime.fromtimestamp(timeStamp).strftime('%Y-%m-%d %H:%M:%S')

                    executedOrders.append([date, ticker, quantity, float(openPrice), action, timeOfExecution, totalAssets, float(self.balance)/100, float(self.stockAssets)/100])

                else:
                    raise ValueError("Insufficient funds to execute trade {}".format(order))
            else:
                raise ValueError("Buy quantity of 0 recieved")

        self.updateAssets(date)
        totalAssets = float(self.balance + self.stockAssets)/100
        executedOrders.append([date, "", "", "", "CHECKPOINT", datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S'),  totalAssets, float(self.balance)/100, float(self.stockAssets)/100])

        logAdditions = pd.DataFrame(executedOrders, columns=self.dailyLogColumns)
        self.dailyLogs = self.dailyLogs.append(logAdditions, ignore_index=True)

        self.updateAssets(date)

    def placeSellOrders(self, listOfTickers, date, simulation=True):
        '''
        places a sell order for every ticker in list of tickers.
        Will sell ALL stocks owned of that ticker
        Date is the current date, in string format "YYYY-MM-DD".
        Setting simulation to False will actually execute the sell orders (not yet implimented)
        '''
        if (not simulation):
            pass  #actually execute sell orders

        action = "Sell"
        executedOrders = []
        completedBuySellCyles = []

        for ticker in listOfTickers:
            tickerData = database.getDataframe(ticker)
            openPrice = tickerData.loc[date, "Close"].values[0]
            if np.isnan(openPrice):
                date_index = tickerData.index.get_loc(date)
                openPrice = tickerData.iloc[date_index+1,"Close"]
            quantity = self.stocksOwned[ticker][0]
            buyPrice = self.stocksOwned[ticker][1]
            buyDate = self.stocksOwned[ticker][2]

            tradeIncome = int(openPrice*quantity*100) - self.commision

            if (ticker in self.stocksOwned):
                #We own the stock
                self.balance += tradeIncome  #*100 since balance is in cents
                del self.stocksOwned[ticker]  #Remove stock from stocksOwned

                if (simulation):
                    self.updateAssets(date)
                else:
                    self.updateAssets()

                totalAssets = float(self.balance + self.stockAssets)/100

                timeStamp = time.time()
                timeOfExecution = datetime.datetime.fromtimestamp(timeStamp).strftime('%Y-%m-%d %H:%M:%S')

                executedOrders.append([date, ticker, quantity, float(openPrice), action, timeOfExecution, totalAssets, float(self.balance)/100, float(self.stockAssets)/100])

                tradeProfit = (tradeIncome - int(quantity*buyPrice*100) - self.commision)/100     #account for commision when you originally bought the stock
                completedBuySellCyles.append([ticker, buyDate, float(buyPrice), date, float(openPrice), quantity, self.commision/100, tradeProfit])

            else:
                raise ValueError("Ticker {} is not a currently owned stock".format(ticker))

        logAdditions = pd.DataFrame(executedOrders, columns=self.dailyLogColumns)
        self.dailyLogs = self.dailyLogs.append(logAdditions, ignore_index=True)

        tradeHistoryAdditions = pd.DataFrame(completedBuySellCyles, columns=self.tradeHistoryColumns)
        self.tradeHistory = self.tradeHistory.append(tradeHistoryAdditions, ignore_index=True)

    def saveHistory(self, SelectorName = "NAN"):
        '''
        Saves daily logs and trading history to seperate CSVs.
        Defualts to <Account Name>_<Logs||Trades>_<timeStamp>.csv
        '''
        self.timeSaved = str(time.time())

        filepath = self.savepath+"\\"+SelectorName+"_" + self.name + "_Log_" + self.timeSaved + ".csv"
        self.dailyLogs.to_csv(filepath)

        filepath = self.savepath+"\\"+SelectorName+"_" + self.name + "_TradeHistory_" + self.timeSaved + ".csv"
        self.tradeHistory.to_csv(filepath)

    def getHistory(self):
        '''
        Returns the trading history for this account as a dataframe
        '''
        return self.tradeHistory

    def getLogs(self):
        '''
        Returns the daily account logs as a dataframe
        '''
        return self.dailyLogs

#================END tradingAccount class=====================================


import os

from setuptools import setup, find_packages, Command

class RunTestsCommand(Command):
    description = "Test command to run testr in virtualenv"
    user_options = []
    def initialize_options(self):
        self.cwd = None
    def finalize_options(self):
        self.cwd = os.getcwd()
    def run(self):
        os.system('./run_tests.sh -V')

setup(
    name='vnc_openstack',
    version='0.1dev',
    packages=find_packages(),
    include_package_data=True,
    zip_safe=False,
    long_description="VNC openstack interface package",
    entry_points={
        'vnc_cfg_api.resync': [
            'xxx = vnc_openstack:OpenstackDriver',
        ],
        'vnc_cfg_api.resourceApi': [
            'xxx = vnc_openstack:ResourceApiDriver',
        ],
        'vnc_cfg_api.neutronApi': [
            'xxx = vnc_openstack:NeutronApiDriver',
        ],
    },
    install_requires=[
    ],
    cmdclass={
       'run_tests': RunTestsCommand,
    },
)


from gpiozero.pins.pigpio import PiGPIOFactory
import gpiozero
import time
import numpy as np

class ServoClass:
  def __init__(self, servo_pin=12):
    self.factory = PiGPIOFactory()
    self.servo_pin = servo_pin
    self.servo = gpiozero.Servo(servo_pin, pin_factory=self.factory)
    self.last_position = 0
    self.moving = False
    self.rotate(0)

  def rotate(self, pid_output):
    self.servo.value = 0
    # while self.servo.value < 0.95:
    #   self.servo.value += 0.05
    #   print(self.servo.value)
    #   time.sleep(0.5)

servo = ServoClass()


import django_filters
from django_filters import FilterSet
from .models import Post
from django import forms

# Создаем свой набор фильтров для модели Post.
class PostFilter(FilterSet):
    title = django_filters.CharFilter(field_name='title',
                                      label='В названии:',
                                      lookup_expr='icontains', )
    author = django_filters.CharFilter(field_name='author__user__username',
                                       label='По автору:',
                                       lookup_expr='icontains', )
    date_time_in = django_filters.DateFilter(field_name='date_time_in',
                                             label='Начиная с даты:',
                                             widget=forms.DateInput(attrs={"type": "date"}),
                                             lookup_expr='date__gte', )
    # class Meta:
    # В Meta классе мы должны указать Django модель,
    # в которой будем фильтровать записи.
    # model = Post
    # В fields мы описываем по каким полям модели
    # будет производиться фильтрация.
    # fields = {
    # поиск по названию
    # 'title': ['icontains'],
    # количество товаров должно быть больше или равно
    # 'author__user__username': ['icontains'],
    # }


import flask
from subprocess import call
import socket
app = flask.Flask(__name__)

def get_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

HOST = get_ip()

@app.route("/")
def hello():
	return flask.redirect("https://www.google.com", code=302)

@app.route('/command')
def command():
	if flask.request.method == "GET":
		if flask.request.args.get('cmd'):
			if flask.request.args.get('cmd') == 'hibernate':
				call(["shutdown", "-h"])
				return "Received hibernate signal from command center"

			elif flask.request.args.get('cmd') == 'shutdown':
			 	call(["shutdown", "-s", "-t", "0"])
			 	return "Received shutdown signal from command center"
	return flask.redirect("https://www.google.com", code=302)

if __name__ == "__main__":
    app.run(debug =True, host=HOST, port = 5000)


import sys
import numpy as np
import pandas as pd
from sqlalchemy import create_engine

def load_data(messages_filepath, categories_filepath):
    """
    Load messages and categories data, merge to one dataframe.

    Parameters:
        messages_filepath (str): path to the CSV file containing messages
        categories_filepath (str): path to the CSV file containing categories
    Returns:
        df (dataframe): combined data containing messages and categories
    """
    # load messages dataset
    messages = pd.read_csv(messages_filepath)
    # load categories dataset
    categories = pd.read_csv(categories_filepath)
    # merge datasets
    df = messages.merge(categories, on='id')

    return df

def clean_data(df):
    """
    Clean categories data.

    Parameters:
        df (dataframe): combined data containing messages and categories
    Returns:
        df (dataframe): Combined data containing messages and cleaned categories
    """
    # create a dataframe of the 36 individual category columns
    categories = df['categories'].str.split(pat=';', expand=True)

    # select the first row of the categories dataframe
    row = categories.loc[1]
    # extract a list of new column names for categories.
    category_colnames = row.apply(lambda x: x[:-2]).tolist()
    # rename the columns of `categories`
    categories.columns = category_colnames

    for column in categories:
        # set each value to be the last character of the string
        categories[column] = categories[column].str[-1:]

        # convert column from string to numeric
        categories[column] = categories[column].astype(int)

    # drop the original categories column from `df`
    df.drop(['categories'], axis=1, inplace=True)
    # concatenate the original dataframe with the new `categories` dataframe
    df = pd.concat([df,categories], axis=1)
    # keep category columns binary, assume detected 2 as 0
    df['related'] = df['related'].map(lambda x: 0 if x == 2 else x)
    # drop duplicates
    df.drop_duplicates(inplace=True)

    return df

def save_data(df, database_filename):
    """
    Save data to SQLite database file.

    Parameters:
        df (dataframe): combined data containing messages and cleaned categories
        database_filename (str): path to SQLite database
    """
    engine = create_engine('sqlite:///' + database_filename)
    df.to_sql('disaster_response_table', engine, index=False, if_exists='replace')

def main():
    """
    Main function to run the data processing functions.
        1) Load messages data with categories
        2) Clean categories data
        3) Save cleaned data to SQLite database
    """
    if len(sys.argv) == 4:

        messages_filepath, categories_filepath, database_filepath = sys.argv[1:]

        print('Loading data...\n    MESSAGES: {}\n    CATEGORIES: {}'
              .format(messages_filepath, categories_filepath))
        df = load_data(messages_filepath, categories_filepath)

        print('Cleaning data...')
        df = clean_data(df)

        print('Saving data...\n    DATABASE: {}'.format(database_filepath))
        save_data(df, database_filepath)

        print('Cleaned data saved to database!')

    else:
        print('Please provide the filepaths of the messages and categories '\
              'datasets as the first and second argument respectively, as '\
              'well as the filepath of the database to save the cleaned data '\
              'to as the third argument. \n\nExample: python process_data.py '\
              'messages.csv categories.csv '\
              'DisasterResponse.db')

if __name__ == '__main__':
    main()


from pagarme_integration.schemas.balance import BalanceSchema
from pagarme_integration.utils.handle_errors import handle_error_pagarme
from pagarme_integration.classes.config import Config

from jsonschema import validate

from abc import abstractmethod

import requests
import json

class Balance(BalanceSchema):
    __url_additional = "/recipients/ /balance"

    def __init__(
        self,
        currency,
        available_amount,
        waiting_funds_amount,
        transferred_amount,
        recipient,
    ) -> None:
        self.currency = currency
        self.available_amount = available_amount
        self.waiting_funds_amount = waiting_funds_amount
        self.transferred_amount = transferred_amount
        self.recipient = recipient

    @abstractmethod
    def mount_obj(content: dict):
        return Balance(
            currency=content.get("currency"),
            available_amount=content.get("available_amount"),
            waiting_funds_amount=content.get("waiting_funds_amount"),
            transferred_amount=content.get("transferred_amount"),
            recipient=content.get("recipient"),
        ).__dict__

    @classmethod
    def url_with_pk(cls, recipient_id) -> None:
        return cls.__url_additional.replace(" ", recipient_id)

    @classmethod
    def get_balance(cls, recipient_id):
        url = Config.get_url() + cls.url_with_pk(recipient_id=recipient_id)
        content = json.loads(
            requests.get(
                url,
                headers=Config.get_header(),
            ).text
        )
        content_validated = handle_error_pagarme(content)
        validate(instance=content_validated, schema=cls.validate_get())
        return cls.mount_obj(content_validated)


import random
import arcade

class Spaceship(arcade.Sprite):
    def __init__(self, game):
        super().__init__(":resources:images/space_shooter/playerShip1_blue.png")
        self.center_x = game.width // 2
        self.center_y = 50
        self.width = 50
        self.height = 50
        self.speed = 10

class Enemy(arcade.Sprite):
    def __init__(self, game):
        super().__init__(":resources:images/space_shooter/playerShip3_orange.png")
        self.center_x = random.randint(0, game.width)
        self.center_y = game.height + 30
        self.width = 50
        self.height = 50
        self.angle = 180
        self.speed = 5

class Game(arcade.Window):
    def __init__(self):
        super().__init__(width=680, height=720, title="star's game")
        arcade.set_background_color(arcade.color.SMOKY_BLACK)
        self.background = arcade.load_texture(":resources:images/backgrounds/stars.png")
        self.my_ship = Spaceship(self)
        self.enemy = Enemy(self)

    def on_draw(self):
        arcade.start_render()
        arcade.draw_lrwh_rectangle_textured(0, 0, self.width, self.height, self.background)
        self.enemy.draw()
        self.my_ship.draw()

    def on_update(self, delta_time):
        self.enemy.center_y -= self.enemy.speed

    def on_key_press(self, symbol, modifiers: int):
        if symbol == 97 or symbol == 65361:  # left
            self.my_ship.center_x -= self.my_ship.speed

        elif symbol == 100 or symbol == 65363:  # right
            self.my_ship.center_x += self.my_ship.speed

        elif symbol == 115 or symbol == 65364:
            self.my_ship.center_x = self.width // 2

window = Game()

arcade.run()


from pydub import AudioSegment
import os

def convert_audio_files(input_folder, output_folder, input_format):
    # Create output directory if it doesn't exist
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

# Convert each file with the specified input format to .wav and save to output directory
    for file in os.listdir(input_folder):
        if file.endswith(input_format):
            filepath = os.path.join(input_folder, file)
            output_path = os.path.join(output_folder, os.path.splitext(file)[0] + ".wav")
            sound = AudioSegment.from_file(filepath, format=input_format)
            sound.export(output_path, format="wav")


from copy import copy

import torch
import logging
import models.modules.SRResNet_arch as SRResNet_arch
import models.modules.discriminator_vgg_arch as SRGAN_arch
import models.modules.RRDBNet_arch as RRDBNet_arch
import models.modules.USRGAN_arch as USRGAN_arch
import models.modules.USRGANLarge_arch as USRGANLarge_arch
import models.modules.USRGAN_Connections_arch as USRGAN_Connections_arch
import models.modules.BOWGAN_arch as BOWGAN_arch
logger = logging.getLogger('base')

####################
# define network
####################
#### Generator
def define_G(opt):
    opt_net = copy(opt['network_G'])
    which_model = opt_net.pop('which_model_G')
    if 'scale' in opt_net:
        scale = opt_net.pop('scale')

    if which_model == 'MSRResNet':
        netG = SRResNet_arch.MSRResNet(**opt_net, upscale=scale)
    elif which_model == 'RRDBNet':
        netG = RRDBNet_arch.RRDBNet(**opt_net)
    elif which_model == 'USRGAN':
        netG = USRGAN_arch.USRGAN(**opt_net)
    elif which_model == 'USRGANLarge':
        netG = USRGANLarge_arch.USRGANLarge(**opt_net)
    elif which_model == 'USRGAN_conns':
        netG = USRGAN_Connections_arch.USRGANLarge(**opt_net)
    elif which_model == 'BOWGAN':
        netG = BOWGAN_arch.BOWGAN(**opt_net)
    else:
        raise NotImplementedError('Generator model [{:s}] not recognized'.format(which_model))
    return netG

#### Discriminator
def define_D(opt):
    opt_net = opt['network_D']
    which_model = opt_net['which_model_D']

    if which_model == 'discriminator_vgg_128':
        netD = SRGAN_arch.Discriminator_VGG_128(in_nc=opt_net['in_nc'], nf=opt_net['nf'])
    elif which_model == 'discriminator_vgg_128_patch':
        netD = SRGAN_arch.Discriminator_VGG_128_Patch(in_nc=opt_net['in_nc'], nf=opt_net['nf'])
    else:
        raise NotImplementedError('Discriminator model [{:s}] not recognized'.format(which_model))
    return netD

#### Define Network used for Perceptual Loss
def define_F(opt, use_bn=False):
    gpu_ids = opt['gpu_ids']
    device = torch.device('cuda' if gpu_ids else 'cpu')
    # PyTorch pretrained VGG19-54, before ReLU.
    if use_bn:
        feature_layer = 49
    else:
        feature_layer = 34
    netF = SRGAN_arch.VGGFeatureExtractor(feature_layer=feature_layer, use_bn=use_bn,
                                          use_input_norm=True, device=device)
    netF.eval()  # No need to train
    return netF


import chromedriver_binary
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(chrome_options=options)

url = 'https://www.idfpr.com/applications/professionprofile/default.aspx'
driver.get(url)

ln = 'MainContent_MainContentContainer_LastName'
lnbox = driver.find_element_by_id(ln)
lnbox.send_keys('fred')

submit = driver.find_element_by_id('MainContent_MainContentContainer_Search')
submit.click()

ayys = driver.find_elements_by_css_selector('a[href]')
ayys[16].click()

ayys = driver.find_elements_by_css_selector('a[href]')
for i, a in enumerate(ayys):
    if a.text == "Education":
        educ_no = i

ayys[21].click()

e = 'MainContent_MainContentContainer_repSection2_ctl00_0_SchoolLocation_0'
education = driver.find_element_by_id(e).text