예제 #1
0
class PropPropertyBox(QDialog, Ui_propPropertyBox):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None, mode=None, objectDict=None, designModel=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(PropPropertyBox, self).__init__(parent)
        self.helper = Helper()
        self.setupUi(self)
        self.designModel = designModel
        self.modelData = self.designModel.modelData
        self.objectDict = objectDict
        self.mode = mode
        # load combo box
        self.loadDataTypeDropDown()
        #populate ui data from object
        self.populateUIfromObject()
            
        if self.mode == "NEW":
            # set focus to name
            pass
        else: 
            # disable name entry and set focus to description
            self.editName.setEnabled(False)
            self.editDescription.setFocus()
            
    def populateUIfromObject(self, ):
        # default combobox to first entry which is Unknown
        self.cmbDataType.setCurrentIndex(0) 
        if self.objectDict is not None:
            self.editName.insert(str(self.objectDict["name"]))
            self.editDescription.appendPlainText(self.objectDict["desc"])
            index = self.cmbDataType.findText(self.objectDict.get("dataType", "Unknown"))
            if index >= 0:
                self.cmbDataType.setCurrentIndex(index)       

            
    def loadDataTypeDropDown(self):
        '''
        load datatype dropdown
        '''
        dropdownList = []
        dataTypeList = dropdownList + [dataType.value for dataType in DataType]
        self.cmbDataType.addItems(dataTypeList)
            
    @pyqtSlot()
    def on_okButton_clicked(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)
    
    @pyqtSlot()
    def on_cancelButton_clicked(self):
        """
        Slot documentation goes here.
        """
        QDialog.reject(self)

    def validate(self):
        if self.objectDict is None:
            self.objectDict = {}
        name = self.editName.text()
        if self.helper.NoTextValueError(name, "Must enter a Name"):
            self.editName.setFocus()
            return False
        if self.mode == 'NEW':
            if self.helper.DupObjectError(designModel = self.designModel, objName=name, topLevel = "Property", txtMsg = "A Property named {} already exists".format(name)):
                self.editName.setFocus()
                return False
        # must have a data type
        if self.helper.NoTextValueError(self.cmbDataType.currentText(), "Must select a data type"):
            self.cmbDataType.setFocus()
            return False

        return True
        
    def apply(self, ):
        self.objectDict["name"] = self.editName.text()
        desc = self.editDescription.toPlainText()
        if desc is not None:
            self.objectDict["desc"] = desc
        if self.cmbDataType.currentIndex() > 0:
            self.objectDict["dataType"] = self.cmbDataType.currentText()
        else:
            self.objectDict["dataType"] = 'Unknown'
#        print(self.objectDict)
    
    @pyqtSlot(int)
    def on_cmbDataType_currentIndexChanged(self, index):
        """
        Slot documentation goes here.
        
        @param index DESCRIPTION
        @type int
        """
        return
예제 #2
0
class LabelPropertyBox(QDialog, Ui_Dialog):
    """
    Class documentation goes here.
    """
    def __init__(self,
                 parent=None,
                 mode=None,
                 objectDict=None,
                 designModel=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(LabelPropertyBox, self).__init__(parent)
        self.helper = Helper()
        self.setupUi(self)
        self.designModel = designModel
        self.modelData = self.designModel.modelData
        self.objectDict = objectDict
        self.mode = mode
        #populate ui data from object
        self.populateUIfromObject()

        if self.mode == "NEW":
            # set focus to name
            pass
        else:
            # disable name entry and set focus to description
            self.editName.setEnabled(False)
            self.editDescription.setFocus()

    def populateUIfromObject(self, ):
        if self.objectDict is not None:
            self.editName.insert(str(self.objectDict["name"]))
            self.editDescription.appendPlainText(self.objectDict["desc"])

    @pyqtSlot()
    def on_okButton_clicked(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)

    @pyqtSlot()
    def on_cancelButton_clicked(self):
        """
        Slot documentation goes here.
        """
        QDialog.reject(self)

    def validate(self):
        if self.objectDict is None:
            self.objectDict = {}
        name = self.editName.text()
        if self.helper.NoTextValueError(name, "Must enter a Name"):
            self.editName.setFocus()
            return False
        if self.mode == 'NEW':
            if self.helper.DupObjectError(
                    designModel=self.designModel,
                    objName=name,
                    topLevel="Label",
                    txtMsg="A Label named {} already exists".format(name)):
                self.editName.setFocus()
                return False
        return True

    def apply(self, ):
        self.objectDict["name"] = self.editName.text()
        desc = self.editDescription.toPlainText()
        if desc is not None:
            self.objectDict["desc"] = desc
예제 #3
0
class TRPropertyBox(QDialog, Ui_TRPropertyBox):
    """
    This displays and manages the Relationship Template Dialog box
    """
    def __init__(self, parent=None, mode=None, objectDict=None, designModel = None):
        """
        objectDict - Relationship Template object dictionary. For creating a new Relationship Template this will be None
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(TRPropertyBox, self).__init__(parent)
        self.parent = parent
        self.schemaModel = self.parent.schemaObject 
        self.settings = QSettings()
        self.formatChanged = False
        self.helper = Helper()
        self.setupUi(self)
        self.designModel = designModel
        self.modelData = self.designModel.modelData
        if objectDict is None:
            self.objectDict = self.designModel.newRelTemplateDict()
        else:
            self.objectDict = objectDict
        self.mode = mode
        
        # get the class that controls the data grid for relationship templates
        self.RelTemplateCypher = RelTemplateCypher(templateDict=self.objectDict)
        
        # get neocon object for this project page
        self.neoCon = NeoDriver(name=self.parent.pageItem.neoConName, promptPW=self.parent.pageItem.promptPW)
        
        # Properties Grid
        self.gridProps.setModel(self.createPropModel())
        comboPropList = [""] 
        comboPropList.extend(sorted(set(self.designModel.instanceList("Property") + self.schemaModel.instanceList("Property"))))
        
        dataTypeList = [dataType.value for dataType in DataType]
        self.gridProps.setItemDelegateForColumn(DATATYPE, CBDelegate(self, dataTypeList, setEditable=False ))

        self.gridProps.setItemDelegateForColumn(PROPERTY, CBDelegate(self, comboPropList, setEditable=True ))
        self.gridProps.setItemDelegateForColumn(PROPDEF, NeoEditDelegate(self))
        self.gridProps.setColumnWidth(PROPERTY, 350)
        self.gridProps.setColumnWidth(DATATYPE, 125)       
        self.gridProps.setColumnWidth(PROPREQ, 100)
        self.gridProps.setColumnWidth(PROPDEF, 150)
        self.gridProps.setColumnWidth(EXISTS, 100)
        self.gridProps.setSelectionBehavior(QAbstractItemView.SelectItems) 
        self.gridProps.setSelectionMode(QAbstractItemView.SingleSelection)

        header = self.gridProps.horizontalHeader()
        header.setSectionResizeMode(PROPERTY, QHeaderView.Interactive)  
        header.setSectionResizeMode(DATATYPE, QHeaderView.Fixed)  
        header.setSectionResizeMode(PROPREQ, QHeaderView.Fixed) 
        header.setSectionResizeMode(PROPDEF, QHeaderView.Interactive)  
        header.setSectionResizeMode(EXISTS, QHeaderView.Fixed)

        # constraints grid
        self.gridConstraints.setModel(self.createConstraintsModel())
        conTypeList = ["Property Exists"]
        self.gridConstraints.setItemDelegateForColumn(CONTYPE, CBDelegate(self, conTypeList, setEditable=False ))      
        self.gridConstraints.setColumnWidth(CONTYPE, 120)
        self.gridConstraints.setColumnWidth(CONPROP, 350)
        header = self.gridConstraints.horizontalHeader()
         
        header.setSectionResizeMode(CONTYPE, QHeaderView.Fixed) 
        header.setSectionResizeMode(CONPROP, QHeaderView.Interactive)
        
        # populate the rel template dropdowns
        self.loadTemplateDropdowns()
        
        # add the data grid widget.  
        self.relGrid = DataGridWidget(self, neoCon=self.neoCon, genCypher=self.RelTemplateCypher)
        self.relGridLayout = QVBoxLayout(self.dataTabFrame)
        self.relGridLayout.setObjectName("relGridLayout")
        self.relGridLayout.addWidget(self.relGrid)    
        
        #populate ui data from object
        self.populateUIfromObject()
        # populate combo boxes used on constraint and index grids
        self.updateComboBoxes()
        # sync definition checkboxes with constraints
        self.syncDefCheckBoxes()     
        
        if self.mode == "NEW":
            self.txtRelTemplateName.setFocus()               
        else: 
            # disable definition fields
            self.txtRelTemplateName.setEnabled(False)
            self.cboRelName.setEnabled(False)
            # disable from and to nodes if they have been defined
            if self.cmbFromTemplate.currentIndex() > 0:
                self.cmbFromTemplate.setEnabled(False)
            if self.cmbToTemplate.currentIndex() > 0:
                self.cmbToTemplate.setEnabled(False)


    def updateComboBoxes(self):
        propList = [""] + [ self.gridProps.model().item(row,PROPERTY).data(Qt.EditRole) for row in range(0,self.gridProps.model().rowCount())]
        self.gridConstraints.setItemDelegateForColumn(CONPROP, CBDelegate(self, propList, setEditable=True ))        
        
    def loadTemplateDropdowns(self, ):
        # load source node template dropdown
        dropdownList = []
        dropdownList.append("No Template Selected")
        nodeList = dropdownList + self.designModel.instanceList("Node Template")
        self.cmbFromTemplate.addItems(nodeList)
        self.cmbToTemplate.addItems(nodeList)
        
    def createPropModel(self):
        model = QStandardItemModel(0, 5)
        model.setHeaderData(PROPERTY, Qt.Horizontal, "Property")
        model.setHeaderData(DATATYPE, Qt.Horizontal, "Data Type")
        model.setHeaderData(PROPREQ, Qt.Horizontal, "Required")
        model.setHeaderData(PROPDEF, Qt.Horizontal, "Default Value")
        model.setHeaderData(EXISTS, Qt.Horizontal, "Exists")
        # connect model slots 
        model.itemChanged.connect(self.propModelItemChanged)        
        return model   
            
    def createConstraintsModel(self):
        # CONSTRAINT GRID - CONTYPE, CONPROP,
        model = QStandardItemModel(0,2)
        model.setHeaderData(CONTYPE, Qt.Horizontal, "Constraint Type")
        model.setHeaderData(CONPROP, Qt.Horizontal, "Property")
        # connect model slots 
        model.itemChanged.connect(self.constraintModelItemChanged)
        return model
        
    def populateUIfromObject(self, ):
        # default the combo boxes to nothing selected
        self.cmbFromTemplate.setCurrentIndex(0) 
        self.cmbToTemplate.setCurrentIndex(0)              
        
        # get the custom template format if any
        self.TRFormatDict = self.objectDict.get("TRformat", None)
        if self.TRFormatDict == None:
            self.rbTemplateDefaultFormat.setChecked(True)
            self.btnDefineTemplateFormat.setEnabled(False)
        else:
            self.rbTemplateCustomFormat.setChecked(True)
            self.btnDefineTemplateFormat.setEnabled(True)   
            
        # get the custom instance format if any
        self.IRFormatDict = self.objectDict.get("IRformat", None)
        if self.IRFormatDict == None:
            self.rbInstanceDefaultFormat.setChecked(True)
            self.btnDefineInstanceFormat.setEnabled(False)
        else:
            self.rbInstanceCustomFormat.setChecked(True)
            self.btnDefineInstanceFormat.setEnabled(True)               
        
        # name, relname, desc
        self.txtRelTemplateName.insert(str(self.objectDict["name"]))
        self.loadRelationshipNameDropDown()
        self.editDescription.appendPlainText(self.objectDict["desc"])
        
        # from and to node templates
        index = self.cmbFromTemplate.findText(self.objectDict["fromTemplate"])
        if index >= 0:
            self.cmbFromTemplate.setCurrentIndex(index)       
        index = self.cmbToTemplate.findText(self.objectDict["toTemplate"])
        if index >= 0:
            self.cmbToTemplate.setCurrentIndex(index) 
            
        # from and to cardinalities
        index = self.cmbFromCardinality.findText(self.objectDict["fromCardinality"])
        if index >= 0:
            self.cmbFromCardinality.setCurrentIndex(index)       
        index = self.cmbToCardinality.findText(self.objectDict["toCardinality"])
        if index >= 0:
            self.cmbToCardinality.setCurrentIndex(index)     
            
        # cardinality description
        self.editToFromType.setText("Node Type {}".format(self.cmbFromTemplate.currentText()))
        self.editFromToType.setText("Node Type {}".format(self.cmbToTemplate.currentText()))
        
        #properties
        for relProp in self.objectDict["properties"]:
            dataType = self.designModel.getPropertyDataType(relProp[PROPERTY])
            self.addProp(self.gridProps.model(), relProp[PROPERTY], dataType, relProp[PROPREQ], relProp[PROPDEF], relProp[EXISTS])
        # load constraints
        try:
            #CONTYPE, CONLBL, CONPROP, CONPROPLIST
            for nodeCon in self.objectDict["constraints"]:
                self.addConstraint(self.gridConstraints.model(), nodeCon[CONTYPE], nodeCon[CONPROP])
        except:
            pass

    def loadRelationshipNameDropDown(self, ):
        # load relationship name  dropdown
        dropdownList = []
        dropdownList.append("")  # blank option for user to enter a new rel type
        # get relationship types from the project model
        templateRelList = [relDict["relname"] for relDict in self.modelData["Relationship Template"] ]    
        # get relationship types from the schemamodel - this needs to be fixed, need common access to schemamodel from project page and instance diagram
        schemaRelList =  [rel["name"] for rel in self.schemaModel.schemaData["Relationship"] ]
#        schemaRelList = []
        # merge, dedup, and sort the two lists and put them in the dropdown
        dropdownList.extend(sorted(set(templateRelList + schemaRelList)))
        self.cboRelName.addItems(dropdownList)      
        # if no relationship name has been set then display enabled combo box  
        if str(self.objectDict["relname"]) is None: 
            self.cboRelName.setCurrentIndex(0)
        elif (str(self.objectDict["relname"]) == "NoRelationshipName" or len(self.objectDict["relname"]) < 1):
            self.cboRelName.setCurrentIndex(0)
        else:
            # if the relationship name has been set then display the rel name
            index = self.cboRelName.findText(str(self.objectDict["relname"]))
            if index >= 0:
                self.cboRelName.setCurrentIndex(index)
            else:
                # its not an existing name so just set the text in the combo box
                self.cboRelName.setEditText(str(self.objectDict["relname"]))
                
    #
    # PROPERTY GRID BUTTONS
    #
    @pyqtSlot()
    def on_btnPropUp_clicked(self):
        """
        User clicks on property up button
        """
        self.helper.moveTableViewRowUp(self.gridProps)
    
    @pyqtSlot()
    def on_btnPropDown_clicked(self):
        """
        User clicks on property down button
        """
        self.helper.moveTableViewRowDown(self.gridProps)
    
    @pyqtSlot()
    def on_btnPropAdd_clicked(self):
        """
        Slot documentation goes here.
        """
        self.gridProps.setSortingEnabled(False)    
        self.addProp(self.gridProps.model(), "","Unknown","N","Null","N")
        # generic function to adjust grid after adding a row
        self.helper.adjustGrid(grid=self.gridProps)
#        # scroll to bottom to insure the new row is visible
#        self.gridProps.scrollToBottom()      
        
    @pyqtSlot()
    def on_btnPropRemove_clicked(self):
        """
        Slot documentation goes here.
        """
#        indexes = self.gridProps.selectionModel().selectedIndexes()
#        for index in sorted(indexes):
#            print('Row %d is selected' % index.row())
#            self.gridProps.model().removeRows(index.row(),1)
            
        indexes = self.gridProps.selectionModel().selectedIndexes()
        if len(indexes) > 0:
            for index in sorted(indexes):
                self.gridProps.model().removeRows(index.row(),1)
        else:
            self.helper.displayErrMsg("Remove Property", "You must select a row to remove")         
            
    def addProp(self,model,prop,dataType, propreq, propdef, exists):
        item1 = QStandardItem(prop)
        item1.setEditable(True)
        item11 = QStandardItem(dataType)
        item11.setEditable(False)
        item12 = QStandardItem(propreq)
        item12.setEditable(True)
        item12.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        item12.setText("Required")        
        item13 = QStandardItem(propdef)
        # store the datatype in role + 1                
        item13.setData(dataType, Qt.UserRole+1)
        item13.setEditable(True)        
        item2 = QStandardItem()
        item2.setFlags(Qt.ItemIsEnabled)
        item2.setText("Exists")
        
        if exists in [0, 1, 2]:
            item2.setCheckState(exists)  
        else:
            item2.setCheckState(Qt.Unchecked)  
            
        if propreq in [0, 1, 2]:
            item12.setCheckState(propreq)  
        else:
            item12.setCheckState(Qt.Unchecked)    
            
        model.appendRow([item1, item11, item12,item13, item2])

    def handleItemClicked(self, item):
        return


    def addConstraint(self,model, conType, prop):
        # CONTYPE, CONPROP
        item1 = QStandardItem(conType)
        item1.setEditable(True)

        item3 = QStandardItem(prop)
        item3.setEditable(True)
            
        model.appendRow([item1, item3])
        
    def syncDefCheckBoxes(self):    
        # clear all checkboxes on the definition tab
        self.clearCheckBoxes()
        # loop through the constraints grid
        model=self.gridConstraints.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            relCon = [model.item(row,CONTYPE).data(Qt.EditRole), model.item(row,CONPROP).data(Qt.EditRole)]
            # process Property Exists
            if relCon[CONTYPE] == "Property Exists":
                self.setPropCheckBox(prop=relCon[CONPROP], chkBox=EXISTS )
                self.setPropCheckBox(prop=relCon[CONPROP], chkBox=PROPREQ )

    def setPropCheckBox(self, prop=None, chkBox=None):
        try:
            model = self.gridProps.model()
            numrows = model.rowCount()
            if not prop is None:
                for row in range(0,numrows):
                    if prop == model.item(row,PROPERTY).data(Qt.EditRole):
                        model.item(row,chkBox).setCheckState(Qt.Checked)        
        except:
            pass       
            
    def clearCheckBoxes(self):

        model = self.gridProps.model()
        numrows = model.rowCount()
        for row in range(0,numrows):
            model.item(row,EXISTS).setCheckState(0) 

            
#################################################################################        
# DIALOG BUTTONS
#################################################################################    
    @pyqtSlot()
    def on_okButton_clicked(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)
    
    @pyqtSlot()
    def on_cancelButton_clicked(self):
        """
        User Selects the Cancel button
        """
        QDialog.reject(self)

    def validate(self):
        if self.objectDict is None:
            self.objectDict = {}
        templateName = self.txtRelTemplateName.text()
        # template name
        if self.helper.NoTextValueError(templateName, "Must enter a Relationship Template Name"):
            self.txtRelTemplateName.setFocus()
            return False
        # relationship name            
#        name = self.txtRelName.text() 
        name = self.cboRelName.currentText()    
        if self.helper.NoTextValueError(name, "Must enter a Relationship Name"):
#            self.txtRelName.setFocus()
            self.cboRelName.setFocus()
            return False
        # dup check
        if self.mode == 'NEW':
            if self.helper.DupObjectError(designModel = self.designModel, objName=templateName, topLevel = "Relationship Template", txtMsg = "A Relationship Template named {} already exists".format(name)):
                self.txtRelTemplateName.setFocus()
                return False
        # must have a From Node Template
        if self.helper.NoComboBoxSelectionError(self.cmbFromTemplate, "Must enter a From Node Template"):
            self.cmbFromTemplate.setFocus()
            return False                
        # must have a To Node Template
        if self.helper.NoComboBoxSelectionError(self.cmbToTemplate, "Must enter a To Node Template"):
            self.cmbToTemplate.setFocus()
            return False
        # property name must exist
        if self.helper.gridNoNameError(grid=self.gridProps, col=PROPERTY, txtMsg="You must supply a name for each Property"):
            self.gridProps.setFocus()
            return False
        # dup property
        if self.helper.gridDupEntryError(self.gridProps, col=PROPERTY, txtMsg="has been entered more than once. You can only use a Property once"):
            self.gridProps.setFocus()
            return False
#        # required property  must have a default value
#        model = self.gridProps.model()
#        numrows = model.rowCount()
#        for row in range(0,numrows):
#            nodeProp = [model.item(row,PROPERTY).data(Qt.EditRole),
#                             model.item(row,DATATYPE).data(Qt.EditRole), 
#                             model.item(row,PROPREQ).checkState(), 
#                             model.item(row,PROPDEF).data(Qt.EditRole), 
#                             model.item(row,EXISTS).checkState()]
#            if nodeProp[PROPREQ] == Qt.Checked and nodeProp[PROPDEF] == "":
#                self.helper.displayErrMsg("Validate", "Required property {} must have a default value.".format(nodeProp[PROPERTY]))
#                self.gridProps.setFocus()
#                return False
        # constraint type exists
        if self.helper.gridNoNameError(grid=self.gridConstraints, col=CONTYPE, txtMsg="You must select a constraint type"):
            self.tabRelTemplate.setCurrentIndex(CONSTRAINT)
            self.gridConstraints.setFocus()
            return False        
        model =self.gridConstraints.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            nodeCon = [model.item(row,CONTYPE).data(Qt.EditRole), model.item(row,CONPROP).data(Qt.EditRole)]
            if nodeCon[CONTYPE]  in ["Property Exists", "Property Unique"]:
                if self.helper.NoTextValueError(nodeCon[CONPROP], "Row {} - You must select a property".format(row+1)):
                    self.tabRelTemplate.setCurrentIndex(CONSTRAINT)
                    self.gridConstraints.setFocus()
                    return False
        # passed all edits so return True
        return True
        
    def apply(self, ):
        
        self.objectDict["TRformat"] = self.TRFormatDict
        self.objectDict["IRformat"] = self.IRFormatDict
        self.objectDict["name"] = self.txtRelTemplateName.text()
#        self.objectDict["relname"] = self.txtRelName.text()
        self.objectDict["relname"] = self.cboRelName.currentText()
        # if its a new rel type then add it to the model
        self.designModel.newRelationship(self.objectDict["relname"])
        if self.cmbFromTemplate.currentIndex() > 0:
            self.objectDict["fromTemplate"] = self.cmbFromTemplate.currentText()
        else:
            self.objectDict["fromTemplate"] = ''
        if self.cmbToTemplate.currentIndex() > 0:    
            self.objectDict["toTemplate"]  = self.cmbToTemplate.currentText()
        else:
            self.objectDict["toTemplate"] = ''
        self.objectDict["fromCardinality"] = self.cmbFromCardinality.currentText()
        self.objectDict["toCardinality"] = self.cmbToCardinality.currentText()
        desc = self.editDescription.toPlainText()
        if desc is not None:
            self.objectDict["desc"] = desc
        #save the properties
        self.objectDict["properties"] = []
        model = self.gridProps.model()
        numrows = model.rowCount()
        for row in range(0,numrows):
            relProp = [model.item(row,PROPERTY).data(Qt.EditRole),
                            model.item(row,DATATYPE).data(Qt.EditRole), 
                             model.item(row,PROPREQ).checkState(), 
                             model.item(row,PROPDEF).data(Qt.EditRole),                             
                            model.item(row,EXISTS).checkState()]
            self.designModel.newProperty(relProp[PROPERTY], relProp[DATATYPE])   # check to see if this is a new Property and create a Property object in the dictionary
            self.objectDict["properties"].append(relProp)
        # save the constraints
        # CONTYPE, CONPROP
        self.objectDict["constraints"] = []
        model =self.gridConstraints.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            relCon = [model.item(row,CONTYPE).data(Qt.EditRole), model.item(row,CONPROP).data(Qt.EditRole)]
            self.objectDict["constraints"].append(relCon)
    
    @pyqtSlot()
    def on_rbDefaultFormat_clicked(self):
        """
        Slot documentation goes here.
        """
        self.btnDefineFormat.setEnabled(False)
    
    @pyqtSlot()
    def on_rbCustomFormat_clicked(self):
        """
        Slot documentation goes here.
        """
        self.btnDefineFormat.setEnabled(True)
    
    @pyqtSlot()
    def on_btnDefineFormat_clicked(self):
        """
        Slot documentation goes here.
        """
        d = IRelFormatDlg(self, modelData = None, relFormat = IRelFormat(formatDict=self.TRFormatDict))
        if d.exec_():
            self.TRFormatDict = IRelFormat(formatDict=d.relFormat.formatDict).formatDict
            # tell diagrams to redraw
            self.formatChanged = True
            
    def constraintModelItemChanged(self, item):
        self.syncDefCheckBoxes()    
        
    @pyqtSlot()
    def on_btnAddConstraint_clicked(self):
        """
        Slot documentation goes here.
        """
        self.gridConstraints.setSortingEnabled(False)    
        self.addConstraint(self.gridConstraints.model(), "", "")
        # generic function to adjust grid after adding a row
        self.helper.adjustGrid(grid=self.gridConstraints)
#        # scroll to bottom to insure the new row is visible
#        self.gridConstraints.scrollToBottom()        
        
    @pyqtSlot()
    def on_btnRemoveConstraint_clicked(self):
        """
        User clicked the remove row button
        """
        indexes = self.gridConstraints.selectionModel().selectedIndexes()
        if len(indexes) > 0:
            for index in sorted(indexes):
                self.gridConstraints.model().removeRows(index.row(),1)
            self.syncDefCheckBoxes()    
        else:
            self.helper.displayErrMsg("Remove Constraint", "You must select a row to remove")         
            
#        indexes = self.gridConstraints.selectionModel().selectedRows()
#        for index in sorted(indexes):
#            self.gridConstraints.model().removeRows(index.row(),1)
            
            
    def propModelItemChanged(self, item):
        
#        print("item data changed {} at {} {}".format(str(item.checkState()), item.index().row(), item.index().column()))

        # this fires when checkbox is selected or deselected. 
        columnIndex = item.index().column()
        propName = self.gridProps.model().item(item.index().row(),PROPERTY).data(Qt.EditRole)
        dataType = self.designModel.getPropertyDataType(propName)
        if columnIndex == PROPERTY:
            # if property has changed then change the datatype
            propName = self.gridProps.model().item(item.index().row(),PROPERTY).data(Qt.EditRole)
            # get the defined datatype for the property
            dataType = self.designModel.getPropertyDataType(propName)
            self.gridProps.model().item(item.index().row(), DATATYPE).setText(dataType)
            # store the datatype in role + 1 for the default value
            self.gridProps.model().item(item.index().row(), PROPDEF).setData(dataType, Qt.UserRole+1)
            # set default value to a null string
            self.gridProps.model().item(item.index().row(), PROPDEF).setText("")
            # if the property doesn't exist yet then allow the datatype to be changed
            if self.designModel.objectExists(topLevel="Property",objectName=propName ) == False:
                self.gridProps.model().item(item.index().row(), DATATYPE).setEditable(True)
            else:
                self.gridProps.model().item(item.index().row(), DATATYPE).setEditable(False)

        if columnIndex == DATATYPE:
            # datatype changed so reset default value and store new datatype   
            dataType = self.gridProps.model().item(item.index().row(),DATATYPE).data(Qt.EditRole)
            # store the datatype in role + 1    
            self.gridProps.model().item(item.index().row(), PROPDEF).setData(dataType, Qt.UserRole+1)
            self.gridProps.model().item(item.index().row(), PROPDEF).setText("")
            
    @pyqtSlot(int)
    def on_tabRelTemplate_currentChanged(self, index):
        """
        If the user has switched to the data tab then update the object dictionary
        
        @param index DESCRIPTION
        @type int
        """
        # user switched to the description tab.  must regenerate description
        if index == DESCRIPTION:
            self.apply()
            self.brwsrGeneratedDesc.setText(self.designModel.getRelationshipDescription(self.objectDict))

        # user switched to the definition tab.  must resync checkboxes with the current values on the constraints tab
        if index == DEFINITION:
            self.syncDefCheckBoxes()
        if index == DATAGRID:
            if self.validate():
                self.apply()
                self.relGrid.refreshGrid()
            else:
                self.tabRelTemplate.setCurrentIndex(0)
        # user switched to the constraint tab so update the combo boxes to get latest values from def
        if index == CONSTRAINT:
            self.updateComboBoxes()     
    
    @pyqtSlot()
    def on_btnDefineTemplateFormat_clicked(self):
        """
        Display the Node Template Format Editor
        """
        myTemplateRelFormatDict = self.TRFormatDict
        # if the template doesn't have a specific instance node format then get the project default
        if myTemplateRelFormatDict is None:
            myTemplateRelFormatDict = deepcopy(self.modelData["TRformat"])
        d = TRelFormatDlg(self, modelData = None, relFormat = TRelFormat(formatDict=myTemplateRelFormatDict))
        if d.exec_():
#            self.TRFormatDict = TRelFormat(formatDict=d.relFormat.formatDict).formatDict
            self.TRFormatDict = d.relFormat.formatDict
            self.formatChanged = True
    
    @pyqtSlot()
    def on_btnDefineInstanceFormat_clicked(self):
        """
        Display the Instance Node Format Editor
        """
        myInstanceRelFormatDict = self.IRFormatDict
        # if the template doesn't have a specific instance rel format then get the project default
        if myInstanceRelFormatDict is None:
            myInstanceRelFormatDict = deepcopy(self.modelData["IRformat"])
        d = IRelFormatDlg(self, modelData = None, relFormat = IRelFormat(formatDict=myInstanceRelFormatDict))
        if d.exec_():
#            self.IRFormatDict = IRelFormat(formatDict=d.relFormat.formatDict).formatDict
            self.IRFormatDict = d.relFormat.formatDict
            self.formatChanged = True
    
    @pyqtSlot()
    def on_rbTemplateDefaultFormat_clicked(self):
        """
        If default radio button selected, then disable the define format button
        """
        self.btnDefineTemplateFormat.setEnabled(False)
    
    @pyqtSlot()
    def on_rbTemplateCustomFormat_clicked(self):
        """
        If custom radio button selected, then enable the define format button
        """
        self.btnDefineTemplateFormat.setEnabled(True)

    
    @pyqtSlot()
    def on_rbInstanceDefaultFormat_clicked(self):
        """
        If default radio button selected, then disable the define format button
        """
        self.btnDefineInstanceFormat.setEnabled(False)
   
    @pyqtSlot()
    def on_rbInstanceCustomFormat_clicked(self):
        """
        If custom radio button selected, then enable the define format button
        """
        self.btnDefineInstanceFormat.setEnabled(True)
    
    @pyqtSlot()
    def on_btnSetDefaultNull_clicked(self):
        """
        User requests to set a property default to Null
        """
        # grid only allows single selection
        indexes = self.gridProps.selectionModel().selectedIndexes()
        for index in indexes:
            valueIndex = self.gridProps.model().index(index.row(), PROPDEF)
            self.gridProps.model().setData(valueIndex, "Null", Qt.DisplayRole)

    @pyqtSlot(int)
    def on_cmbToTemplate_currentIndexChanged(self, index):
        """
        The to template changed
        
        @param index DESCRIPTION
        @type int
        """
        self.editFromToType.setText("Node Type {}".format(self.cmbToTemplate.currentText()))
#        print(self.editFromToType.text())
        
    @pyqtSlot(int)
    def on_cmbFromTemplate_currentIndexChanged(self, index):
        """
        The from template changed
        
        @param index DESCRIPTION
        @type int
        """
        self.editToFromType.setText("Node Type {}".format(self.cmbFromTemplate.currentText()))
#        print(self.editToFromType.text())
        
    @pyqtSlot(int)
    def on_cmbFromCardinality_currentIndexChanged(self, index):
        """
        Slot documentation goes here.
        
        @param index DESCRIPTION
        @type int
        """
        self.editFromToType.setText("Node Type {}".format(self.cmbToTemplate.currentText()))
예제 #4
0
class ConstraintNodePropUniqueDlg(QDialog, Ui_ConstraintNodePropUniqueDlg):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(ConstraintNodePropUniqueDlg, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.schemaModel = self.parent.schemaModel
        self.helper = Helper()
        self.initUi()

    def initUi(self, ):
        aList = sorted(self.schemaModel.instanceList("Label"))
        self.cbLabel.addItem("")
        for indexName in aList:
            self.cbLabel.addItem(indexName)

        aList = sorted(self.schemaModel.instanceList("Property"))
        self.cbProperty.addItem("")
        for indexName in aList:
            self.cbProperty.addItem(indexName)

    def validate(self, ):

        # must enter or select a label
        if self.helper.NoTextValueError(self.cbLabel.currentText(),
                                        "You must enter or select a Label"):
            return False
        # must add at least one property to the list
        if self.helper.NoTextValueError(self.cbProperty.currentText(),
                                        "You must enter or select a Property"):
            return False

        return True

    def apply(self, ):
        """
        Generate and run the create constraint statement
        """
        prop = self.cbProperty.currentText()
        label = self.cbLabel.currentText()
        self.cypherCommand = None
        '''
        CREATE CONSTRAINT ON (p:Person) ASSERT p.name IS UNIQUE
        '''
        self.cypherCommand = "CREATE CONSTRAINT ON (p:{}) ASSERT p.{} IS UNIQUE".format(
            label, prop)

        if self.cypherCommand:
            QApplication.setOverrideCursor(Qt.WaitCursor)
            rc, msg = self.schemaModel.createConstraint(self.cypherCommand)
            QApplication.restoreOverrideCursor()
            self.helper.displayErrMsg("Create Constraint", msg)
        else:
            self.helper.displayErrMsg(
                "Create Constraint", "Error Generating Constraint Statement.")

    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)

    @pyqtSlot()
    def on_buttonBox_rejected(self):
        """
        Slot documentation goes here.
        """
        QDialog.reject(self)
예제 #5
0
class ChangeUserPW(QDialog, Ui_ChangeUserPW):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(ChangeUserPW, self).__init__(parent)
        self.setupUi(self)
        self.helper = Helper()
        self.settings = QSettings()
        self.parent = parent
        self.editPW.setEchoMode(QLineEdit.Password)
        self.editNewPW.setEchoMode(QLineEdit.Password)
        self.editRepeatPW.setEchoMode(QLineEdit.Password)
        
    def logMessage(self, msg):
        if logging:
            logging.info(msg)                    
            
    @pyqtSlot()
    def on_btnCurrentShow_clicked(self):
        """
        User clicks the show button for the current password
        """
        if self.btnCurrentShow.text() == "Show":
            self.btnCurrentShow.setText("Hide")
            self.editPW.setEchoMode(QLineEdit.Normal)
        else:
            self.btnCurrentShow.setText("Show")
            self.editPW.setEchoMode(QLineEdit.Password)            
    
    @pyqtSlot()
    def on_btnNewShow_clicked(self):
        """
        User clicks the show button for the new passwords
        """
        if self.btnNewShow.text() == "Show":
            self.btnNewShow.setText("Hide")
            self.editNewPW.setEchoMode(QLineEdit.Normal)
            self.editRepeatPW.setEchoMode(QLineEdit.Normal)
        else:
            self.btnNewShow.setText("Show")
            self.editNewPW.setEchoMode(QLineEdit.Password)      
            self.editRepeatPW.setEchoMode(QLineEdit.Password)      
    
    @pyqtSlot()
    def on_btnReset_clicked(self):
        """
        User clicks the reset password button so do it
        """
        if self.validate():
            self.resetPassword()

    def validate(self, ):
        if self.helper.NoTextValueError(self.editUserID.text(), "You must enter a user ID to login with."):
            self.editUserID.setFocus()
            return False        
        if self.helper.NoTextValueError(self.editPW.text(), "You must enter a password to login with."):
            self.editPW.setFocus()
            return False        
        if self.helper.NoTextValueError(self.editNewPW.text(), "You must enter a new password."):
            self.editNewPW.setFocus()
            return False        
        if self.helper.NoTextValueError(self.editRepeatPW.text(), "You must repeat the new password."):
            self.editRepeatPW.setFocus()
            return False      
        if self.editNewPW.text() != self.editRepeatPW.text():
            self.helper.displayErrMsg("Reset Password", "The new password does not match the repeat password.")
            self.editNewPW.setFocus()
            return False    
        
        return True

    def resetPassword(self):
        '''login to Neo4j and reset the password
        '''
        # get the currently selected schema tab's neocon name
        newConName = self.parent.pageItem.neoConName
        # get the neocon dictionary from settings
        newConDict = self.settings.value("NeoCon/connection/{}".format(newConName))
        newConDict["userid"]=self.editUserID.text()
        savePW = self.helper.putText(self.editPW.text())
        newConDict["password"]=savePW
        # create a new neoCon using the userid/password the person entered on this form
        self.newNeoCon = NeoDriver(name=newConName,  neoDict = newConDict)
        
        QApplication.setOverrideCursor(Qt.WaitCursor)
        rc, msg = self.changePassword(userName=self.editUserID.text(), pw=self.editNewPW.text(), forceChange=False)
        if rc:
            self.helper.displayErrMsg("Change Password", msg)
        else:
            self.helper.displayErrMsg("Change Password Error", msg)
        QApplication.restoreOverrideCursor()         

    def changePassword(self, userName=None, pw=None, forceChange=None):
        try:
#            cypherCmd = "CALL dbms.security.changeUserPassword('{}','{}',{})".format(userName, pw, str(forceChange))
            cypherCmd = "CALL dbms.changePassword('{}')".format(pw)
            self.logMessage("Attempting: {}".format(cypherCmd))
            #run the query
            rc1, msg1 = self.newNeoCon.runCypherAuto(cypherCmd)
            if rc1:
                msg = "Password Changed."       
            else:
                msg = "Change Password Error {}".format(msg1)
        except BaseException as e:
            msg = "{} - Change Password Error.".format(repr(e))
        finally: 
            self.logMessage(msg) 
            return rc1, msg                          
        
    @pyqtSlot()
    def on_btnClose_clicked(self):
        """
        User clicks the Close button so exit the dialog
        """
        QDialog.accept(self)
예제 #6
0
class ConstraintNodeKeyDlg(QDialog, Ui_ConstraintNodeKeyDlg):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(ConstraintNodeKeyDlg, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.schemaModel = self.parent.schemaModel
        self.helper = Helper()
        self.initUi()

    def initUi(self, ):
        aList = sorted(self.schemaModel.instanceList("Label"))
        self.cbLabel.addItem("")
        for indexName in aList:
            self.cbLabel.addItem(indexName)

        aList = sorted(self.schemaModel.instanceList("Property"))
        self.cbProperty.addItem("")
        for indexName in aList:
            self.cbProperty.addItem(indexName)

    def validate(self, ):
        # must enter or select a label
        if self.helper.NoTextValueError(self.cbLabel.currentText(),
                                        "You must enter or select a Label"):
            return False
        # must add at least one property to the list
        if self.helper.NoTextValueError(
                self.lstProperty,
                "You must have at least one property in the list."):
            return False

        return True

    def apply(self, ):
        """
        Generate and run the create constraint statement
        """
        propList = [
            "p." + str(self.lstProperty.item(i).text())
            for i in range(self.lstProperty.count())
        ]
        propComma = ",".join(x for x in propList)
        label = self.cbLabel.currentText()
        self.cypherCommand = None
        '''
        CREATE CONSTRAINT ON (p:Person) ASSERT (p.firstname, p.surname) IS NODE KEY            
        '''
        self.cypherCommand = "CREATE CONSTRAINT ON (p:{}) ASSERT ({}) IS NODE KEY".format(
            label, str(propComma))

        if self.cypherCommand:
            QApplication.setOverrideCursor(Qt.WaitCursor)
            rc, msg = self.schemaModel.createConstraint(self.cypherCommand)
            QApplication.restoreOverrideCursor()
            self.helper.displayErrMsg("Create Node KeyConstraint", msg)
        else:
            self.helper.displayErrMsg(
                "Create Node Key Constraint",
                "Error Generating Node Key Constraint Statement.")

    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)

    @pyqtSlot()
    def on_buttonBox_rejected(self):
        """
        Slot documentation goes here.
        """
        QDialog.reject(self)

    @pyqtSlot()
    def on_pbAddToList_clicked(self):
        """
        Get the property name in the combo box and add it to the list
        """
        if len(
                self.lstProperty.findItems(self.cbProperty.currentText(),
                                           Qt.MatchFixedString)) == 0:
            self.lstProperty.addItem(self.cbProperty.currentText())
        else:
            self.helper.displayErrMsg(
                "Create Node Key Constraint",
                "You can't use the same property twice.")

    @pyqtSlot()
    def on_pbRemoveList_clicked(self):
        """
        Remove the selected property from the list
        """
        self.lstProperty.takeItem(self.lstProperty.currentRow())

    @pyqtSlot()
    def on_btnMoveUp_clicked(self):
        """
        User clicks the Move Up button, move the selected property up on in the list
        """
        self.helper.moveListItemUp(self.lstProperty)

    @pyqtSlot()
    def on_btnMoveDown_clicked(self):
        """
        User clicks the Move Down button, move the selected property down on in the list
        """
        self.helper.moveListItemDown(self.lstProperty)
예제 #7
0
class NeoConPropertyBox(QDialog, Ui_NeoConPropertyBox):
    """
    Display the property editor for a neo4j connection definition
    """
    def __init__(self, parent=None, mode=None, objectDict=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(NeoConPropertyBox, self).__init__(parent)
        self.parent = parent
        self.mode = mode
        self.objectDict = objectDict
        self.helper = Helper()
        self.setupUi(self)
        self.chkSecureCon.setTristate(False)
        self.populateUIfromObject()

    def populateUIfromObject(self, ):

        # window title
        self.setWindowTitle("Neo4j Connection: {}".format(
            self.objectDict["slot"]))

        if self.objectDict["usesecure"] == "True":
            self.chkSecureCon.setCheckState(Qt.Checked)
        else:
            self.chkSecureCon.setCheckState(Qt.Unchecked)

        if self.objectDict["prompt"] == "True":
            self.chkPromptForPW.setCheckState(Qt.Checked)
            self.editPassWord.setReadOnly(True)
            self.btnShow.setEnabled(False)
        else:
            self.chkPromptForPW.setCheckState(Qt.Unchecked)

        self.conType = self.objectDict.get("conType", "bolt")
        index = self.cboScheme.findText(self.conType)
        if index >= 0:
            self.cboScheme.setCurrentIndex(index)

        self.editHostName.insert(self.objectDict["host"])
        self.editPort.insert(self.objectDict.get("port", ""))
        self.editUserid.insert(self.objectDict["userid"])
        pw = self.objectDict["password"]
        if len(pw) > 0:
            displayPw = self.helper.getText(pw)
        else:
            displayPw = ""
        # only display the password if the prompt checkbox is unchecked.
        if self.objectDict["prompt"] == "False":
            self.editPassWord.insert(displayPw)

    @pyqtSlot(str)
    def on_cboScheme_currentIndexChanged(self, p0):
        """
        User selects the connection type from the dropdown.
        Change the secure connection checkbox as appropriate
        
        @param p0 DESCRIPTION
        @type str
        """
        pass

    @pyqtSlot()
    def on_okButton_clicked(self):
        """
        User selects OK button, edit data, then save/close the dialog box if ok.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)
        else:
            return

    @pyqtSlot()
    def on_cancelButton_clicked(self):
        """
        User selects Cancel button, close dialog box without saving
        """
        QDialog.reject(self)

    def validate(self):
        '''
        Validate the user has entered correct data.
        '''
        # edit host name field
        if self.helper.NoTextValueError(self.editHostName.text(),
                                        "Must supply a host name."):
            return False
        # edit port field
        if self.helper.NoTextValueError(self.editPort.text(),
                                        "Must supply a port number."):
            return False
        # userid field
        if self.helper.NoTextValueError(self.editUserid.text(),
                                        "Must supply a user id."):
            return False

        return True

    def apply(self, ):
        self.objectDict["conType"] = self.cboScheme.currentText()
        self.objectDict["port"] = self.editPort.text()
        self.objectDict["host"] = self.editHostName.text()
        self.objectDict["userid"] = self.editUserid.text()
        pw = self.editPassWord.text()
        if len(pw) > 0:
            savePw = self.helper.putText(pw)
        else:
            savePw = ""
        self.objectDict["password"] = savePw
        if self.chkSecureCon.isChecked():
            self.objectDict["usesecure"] = "True"
        else:
            self.objectDict["usesecure"] = "False"
        if self.chkPromptForPW.isChecked():
            self.objectDict["prompt"] = "True"
        else:
            self.objectDict["prompt"] = "False"

        return

    @pyqtSlot()
    def on_btnShow_clicked(self):
        """
        User clicked the Show button so show the password in clear text
        """
        if self.btnShow.text() == "Show":
            self.btnShow.setText("Hide")
            self.editPassWord.setEchoMode(QLineEdit.Normal)
        else:
            self.btnShow.setText("Show")
            self.editPassWord.setEchoMode(QLineEdit.Password)
        return

    @pyqtSlot(bool)
    def on_chkPromptForPW_clicked(self, checked):
        """
        User selects to prompt for password instead of saving the password.
        
        @param checked DESCRIPTION
        @type bool
        """
        # user sets prompt for password to true
        if checked:
            # warn user the currently set password will be cleared
            if len(self.editPassWord.text()) > 0:
                msgBox = QMessageBox()
                msgBox.setIcon(QMessageBox.Warning)
                msgBox.setText(
                    "Warning: This will clear the password from the screen. Do you want to continue?"
                )
                msgBox.setWindowTitle("CONFIRM CLEAR PASSWORD")
                msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
                result = msgBox.exec_()
                if result == QMessageBox.No:
                    # clear the checkbox
                    self.chkPromptForPW.setChecked(Qt.Unchecked)
                    return
            # return password text edit to normal state if it is currently showing
            if self.btnShow.text() == "Hide":
                self.btnShow.setText("Show")
            self.editPassWord.setEchoMode(QLineEdit.Password)
            # clear the text edit
            self.editPassWord.setText("")
            # disable password entry
            self.editPassWord.setReadOnly(True)
            self.btnShow.setEnabled(False)
            # clear the password from settings

        else:
            # enable password entry
            self.editPassWord.setReadOnly(False)
            self.btnShow.setEnabled(True)
            self.editPassWord.setEchoMode(QLineEdit.Password)
            if self.btnShow.text() == "Hide":
                self.btnShow.setText("Show")
예제 #8
0
class DlgExportCSV(QDialog, Ui_DlgExportCSV):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(DlgExportCSV, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.settings = parent.parent.settings
        self.helper = Helper()

        # add the CSV File widget.
        self.CSVWriterWidget = CSVWriterWidget(parent=self)

        self.CSVLayout = QVBoxLayout(self.frmCSVWidget)
        self.CSVLayout.setObjectName("CSVLayout")
        self.CSVLayout.addWidget(self.CSVWriterWidget)

    def validate(self, ):
        # edit the csv parameters entered by the user for correctness
        if self.CSVWriterWidget.chkWriteHeader.checkState() == Qt.Checked:
            self.writeHeader = True
        else:
            self.writeHeader = False
        self.writeHeader = self.CSVWriterWidget.chkWriteHeader.checkState()
        self.fileName = self.CSVWriterWidget.txtCSVFileName.text()
        if self.helper.NoTextValueError(self.fileName,
                                        "Must enter a file name"):
            self.CSVWriterWidget.txtCSVFileName.setFocus()
            return False
        self.delimiterChar = self.CSVWriterWidget.cmbDelimiter.currentText()
        if self.helper.textBadLengthError(
                self.delimiterChar, 1, 1,
                "Delimiter can only be one character"):
            self.CSVWriterWidget.cmbDelimiter.setFocus()
            return False
        self.quoteChar = self.CSVWriterWidget.cmbQuoteChar.currentText()
        if self.quoteChar != "None":
            if self.helper.textBadLengthError(
                    self.quoteChar, 1, 1,
                    "Quote Character can only be one character"):
                self.CSVWriterWidget.cmbQuoteChar.setFocus()
                return False
        else:
            self.quoteChar = ''

        if self.CSVWriterWidget.cmbUseQuotes.currentText() == "All":
            self.useQuote = csv.QUOTE_ALL
        if self.CSVWriterWidget.cmbUseQuotes.currentText() == "Minimal":
            self.useQuote = csv.QUOTE_MINIMAL
        if self.CSVWriterWidget.cmbUseQuotes.currentText() == "Non-Numeric":
            self.useQuote = csv.QUOTE_NONNUMERIC
        if self.CSVWriterWidget.cmbUseQuotes.currentText() == "None":
            self.useQuote = csv.QUOTE_NONE

        self.doubleQuote = self.CSVWriterWidget.cmbDoubleQuote.currentText()
        self.escapeChar = self.CSVWriterWidget.cmbEscapeCharacter.currentText()
        if self.escapeChar != "None":
            if self.helper.textBadLengthError(
                    self.escapeChar, 1, 1,
                    "Escape Character can only be one character"):
                self.CSVWriterWidget.cmbEscapeCharacter.setFocus()
                return False
        else:
            self.escapeChar = ''
        self.lineTerminator = self.CSVWriterWidget.cmbLineTerminator.currentText(
        )
        # this seems crazy but it won't work if you just use the commented line above.
        if self.CSVWriterWidget.cmbLineTerminator.currentText() == r'\r\n':
            self.lineTerminator = '\r\n'
        if self.CSVWriterWidget.cmbLineTerminator.currentText() == r'\r':
            self.lineTerminator = '\r'
        if self.CSVWriterWidget.cmbLineTerminator.currentText() == r'\n':
            self.lineTerminator = '\n'

        if self.helper.NoTextValueError(self.fileName,
                                        "Must enter a file name"):
            self.CSVWriterWidget.cmbLineTerminator.setFocus()
            return False

        # all edits passed
        return True

    @pyqtSlot()
    def on_btnExport_clicked(self):
        """
        User clicks the Export button.  Validate and if no errors then accept and exit dialog
        The data grid widget does the actual csv write operation
        """
        if self.validate():
            QDialog.accept(self)

    @pyqtSlot()
    def on_btnCancel_clicked(self):
        """
        User clicks cancel so reject dialog and exit
        """
        QDialog.reject(self)
예제 #9
0
class CreateIndexDlg(QDialog, Ui_CreateIndexDlg):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(CreateIndexDlg, self).__init__(parent)
        self.helper = Helper()
        self.setupUi(self)
        self.parent = parent
        self.schemaModel = self.parent.schemaModel
        self.initUi()
        
    def initUi(self, ):
        aList = self.schemaModel.instanceList("Label")
        self.cbLabel.addItem("")
        aSortedList = sorted(aList)
        for indexName in aSortedList:
            self.cbLabel.addItem(indexName)
        
        aList = sorted(self.schemaModel.instanceList("Property"))
        self.cbProperty.addItem("")
        for indexName in aList:
            self.cbProperty.addItem(indexName)
        return

    def validate(self, ):

        # must enter or select a label
        if self.helper.NoTextValueError(self.cbLabel.currentText(), "You must enter or select a Label"):
            return False
        # must add at least one property to the list
        if self.helper.NoTextValueError(self.lstProperty, "You must have at least one property in the list."):
            return False
        
        return True
        
    def apply(self, ):
        """
        Generate the create index statement
        """
        propList = [str(self.lstProperty.item(i).text()) for i in range(self.lstProperty.count())]
        propComma = ",".join(x for x in propList) 
        self.cypherCommand = "CREATE INDEX ON :{}({})".format(self.cbLabel.currentText(), str(propComma))
        if self.cypherCommand:
            QApplication.setOverrideCursor(Qt.WaitCursor)
            rc, msg = self.schemaModel.createIndex(self.cypherCommand)
            QApplication.restoreOverrideCursor()            
            self.helper.displayErrMsg("Create Index", msg)
        else:
            self.helper.displayErrMsg("Create Index", "Error Generating Index Statement.")

    
    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)
    
    @pyqtSlot()
    def on_buttonBox_rejected(self):
        """
        Slot documentation goes here.
        """
        QDialog.reject(self)
    
    @pyqtSlot()
    def on_pbAddToList_clicked(self):
        """
        Get the property name in the combo box and add it to the list
        """
        self.lstProperty.addItem(self.cbProperty.currentText())
    
    @pyqtSlot()
    def on_pbRemoveList_clicked(self):
        """
        Remove the selected property from the list
        """
        self.lstProperty.takeItem(self.lstProperty.currentRow())
    
    @pyqtSlot()
    def on_btnMoveUp_clicked(self):
        """
        User clicks the Move Up button, move the selected property up on in the list
        """
        self.helper.moveListItemUp(self.lstProperty)
    
    @pyqtSlot()
    def on_btnMoveDown_clicked(self):
        """
        User clicks the Move Down button, move the selected property up on in the list
        """
        self.helper.moveListItemDown(self.lstProperty)        
예제 #10
0
class TPPropertyBox(QDialog, Ui_TPPropertyBox):
    """
    Class documentation goes here.
    """
    def __init__(self,
                 parent=None,
                 mode=None,
                 objectDict=None,
                 designModel=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(TPPropertyBox, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.schemaModel = self.parent.schemaObject
        self.settings = QSettings()
        self.helper = Helper()
        self.designModel = designModel
        self.modelData = self.designModel.modelData
        if objectDict is None:
            self.objectDict = self.designModel.newPathTemplateDict()
        else:
            self.objectDict = objectDict
        self.mode = mode

        # get the class that controls the data grid for relationship templates
        self.CypherGenPath = CypherGenPath(parent=self,
                                           templateDict=self.objectDict)

        # get neocon object for this project page
        self.neoCon = NeoDriver(name=self.parent.pageItem.neoConName,
                                promptPW=self.parent.pageItem.promptPW)

        # path treeview setup
        self.tvPath.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tvPath.customContextMenuRequested.connect(self.openMenu)
        self.tvPath.setDragDropMode(QAbstractItemView.DragOnly)

        # add the data grid widget.
        self.nodeGrid = DataGridWidget(self,
                                       neoCon=self.neoCon,
                                       genCypher=self.CypherGenPath)
        self.nodeGridLayout = QVBoxLayout(self.dataTabFrame)
        self.nodeGridLayout.setObjectName("nodeGridLayout")
        self.nodeGridLayout.addWidget(self.nodeGrid)

        self.populatingMetaBox = False

        #populate ui data from object
        self.populateUIfromObject()

        if self.mode == "NEW":
            self.txtPathTemplateName.setFocus()
        else:
            # disable definition fields
            self.txtPathTemplateName.setEnabled(False)

    def populateUIfromObject(self):
        self.txtPathTemplateName.setText(self.objectDict["name"])
        self.editDescription.appendPlainText(self.objectDict["description"])
        self.populateTree()

    def validate(self):
        if self.objectDict is None:
            self.objectDict = {}
        templateName = self.txtPathTemplateName.text()
        # template name
        if self.helper.NoTextValueError(templateName,
                                        "Must enter a Path Template Name"):
            self.txtPathTemplateName.setFocus()
            return False
        # find duplicate cypher variables
        varList = []
        # iterate through the treeview
        tvPathIterator = QTreeWidgetItemIterator(
            self.tvPath, flags=QTreeWidgetItemIterator.All)
        returnCount = 0
        while tvPathIterator:
            if not tvPathIterator.value() is None:
                queryPathNodeItem = tvPathIterator.value()
                thisQueryPathNode = queryPathNodeItem.data(0, Qt.UserRole)
                if thisQueryPathNode.type in ["Node", "Relationship"]:
                    returnCount = returnCount + thisQueryPathNode.numReturnProps(
                    )
                    if returnCount < 1:
                        self.helper.displayErrMsg(
                            thisQueryPathNode.templateName,
                            "Must return at least one property.")
                        return False
                    if self.helper.NoTextValueError(
                            thisQueryPathNode.templateName,
                            "Must supply a template name."):
                        return False
                    if self.helper.NoTextValueError(
                            thisQueryPathNode.cypherVar,
                            "Must supply a cypher variable for {}.".format(
                                thisQueryPathNode.templateName)):
                        return False
                    if thisQueryPathNode.cypherVar in varList:
                        self.helper.displayErrMsg(
                            "Validate Path",
                            "Error - duplicate cypher variable {} defined in template: {} "
                            .format(thisQueryPathNode.cypherVar,
                                    thisQueryPathNode.templateName))
                        return False
                    if thisQueryPathNode.templateName == "No Template Selected" and thisQueryPathNode.blankTemplate == False:
                        self.helper.displayErrMsg(
                            "Validate Path",
                            "Error - must select a template or set blank template to True "
                        )
                        return False

                    varList.append(thisQueryPathNode.cypherVar)
                tvPathIterator.__iadd__(1)
            else:
                break
        # passed all edits so return True
        return True

    def apply(self, ):
        '''save the object dictionary'''
        self.objectDict["name"] = self.txtPathTemplateName.text()
        self.objectDict["description"] = self.editDescription.toPlainText()
        nodeList = []
        # iterate through the treeview
        tvPathIterator = QTreeWidgetItemIterator(
            self.tvPath, flags=QTreeWidgetItemIterator.All)
        while tvPathIterator:
            if not tvPathIterator.value() is None:
                #                print("save node {}".format(tvPathIterator.value().text(0)))
                queryPathNodeItem = tvPathIterator.value()
                queryPathNodeDict = queryPathNodeItem.data(0,
                                                           Qt.UserRole).dict()
                nodeList.append(queryPathNodeDict)
                tvPathIterator.__iadd__(1)
            else:
                break

        self.objectDict["queryPath"] = nodeList
        self.designModel.setModelDirty()

#-----------------------------------------------------------------------------------------------------------------------
#  metabox grid methods
#-----------------------------------------------------------------------------------------------------------------------

    def createMetaBoxModel(self):
        #        ATTRNAME, ATTRVALUE  gridNodeMeta
        model = QStandardItemModel(0, 2)
        model.setHeaderData(ATTRNAME, Qt.Horizontal, "Attribute")
        model.setHeaderData(ATTRVALUE, Qt.Horizontal, "Value")
        return model

    def clearMetaBox(self):
        self.lblMetaHeader.setText("Definition")
        # metabox grid setup
        #        ATTRNAME, ATTRVALUE  gridMetaBox
        self.gridMetaBox.setModel(None)
        self.metaBoxModel = self.createMetaBoxModel()
        self.gridMetaBox.setModel(self.metaBoxModel)
        self.gridMetaBox.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.gridMetaBox.setSelectionMode(QAbstractItemView.SingleSelection)
        self.gridMetaBox.setColumnWidth(ATTRNAME, 150)
        self.gridMetaBox.setColumnWidth(ATTRVALUE, 300)
        # header
        header = self.gridMetaBox.horizontalHeader()
        header.setSectionResizeMode(ATTRNAME, QHeaderView.Fixed)
        header.setSectionResizeMode(ATTRVALUE, QHeaderView.Stretch)
        # set editor delegate
        self.gridMetaBox.setItemDelegateForColumn(ATTRVALUE,
                                                  MetaBoxDelegate(self))
        #        self.gridMetaBox.itemDelegateForColumn(ATTRVALUE).closeEditor.connect(self.metaBoxEditorClosed)
        # connect model slots
        self.metaBoxModel.itemChanged.connect(self.metaBoxModelItemChanged)
        # connect grid slots
        self.gridMetaBox.selectionModel().selectionChanged.connect(
            self.metaBoxGridSelectionChanged)

    def addMetaRow(self, attribute=None, value=None, editable=None):
        '''
        add a row to the  metabox grid
        '''
        #        print("add metarow {}-{}-{}".format(attribute, value, editable))
        self.gridMetaBox.setSortingEnabled(False)
        # attribute
        if attribute is None:
            attribute = ""
        item1 = QStandardItem(attribute)
        item1.setEditable(False)
        # value
        if value is None:
            value = ""
        item2 = QStandardItem(str(value))
        # save the attribute name in the value item so the custom editor MetaBoxDelegate can find it
        item2.setData(attribute, Qt.UserRole)
        item2.setEditable(editable)

        self.gridMetaBox.model().appendRow([item1, item2])

    def populateMetaBox(self, queryPathNode=None):
        self.populatingMetaBox = True
        if not queryPathNode is None:
            self.clearMetaBox()
            self.lblMetaHeader.setText("{} Definition".format(
                queryPathNode.type))
            for attr in queryPathNode.attributes():
                self.addMetaRow(attribute=attr[0],
                                value=attr[1],
                                editable=attr[2])
        self.populatingMetaBox = False

    def metaBoxModelItemChanged(self, item):
        if self.populatingMetaBox == False:
            print("item data changed {} at row:{} col:{}".format(
                str(item.checkState()),
                item.index().row(),
                item.index().column()))
            # a cell changed so see if other changes are needed
            # update the queryPathNode object
            selected = self.tvPath.currentItem()
            if not (selected is None):
                queryPathNode = selected.data(0, Qt.UserRole)
                name = self.gridMetaBox.model().item(
                    item.index().row(), ATTRNAME).data(Qt.EditRole)
                value = self.gridMetaBox.model().item(
                    item.index().row(), ATTRVALUE).data(Qt.EditRole)
                queryPathNode.updateAttr(name=name, value=value)
                #            if name == "Blank Template" and value == "True":
                #                # set template name to first entry in list
                #                queryPathNode.updateAttr(name="Node Template", value="No Template Selected")
                # reload the metabox since changing one property can update others
                self.populateMetaBox(queryPathNode=queryPathNode)
                # repopulate return property grid as template may have
                self.populatePropBox(queryPathNode=queryPathNode)
                # update the tree view description
                queryPathNode.updateTreeView()
                # refresh cypher view
                self.refreshCypher()

    #        # update displayName
    #        self.rePopulateMetaBox(queryPathNode=queryPathNode)
    # force selection of this cell
            self.gridMetaBox.setCurrentIndex(item.index())

    def metaBoxGridSelectionChanged(self):
        # not used
        #        print("metabox grid selection changed")
        return

    def getCurrentCypherVar(self, ):
        # get the queryPathNode object
        selected = self.tvPath.currentItem()
        if not (selected is None):
            queryPathNode = selected.data(0, Qt.UserRole)
            return queryPathNode.cypherVar
        return None

#-----------------------------------------------------------------------------------------------------------------------
#  property box grid methods
#-----------------------------------------------------------------------------------------------------------------------

    def createPropBoxModel(self):
        #       PROPRETURN, PROPPARM, PROPNAME, COLNAME
        model = QStandardItemModel(0, 4)
        model.setHeaderData(PROPRETURN, Qt.Horizontal, "Return")
        model.setHeaderData(PROPPARM, Qt.Horizontal, "Parameter")
        model.setHeaderData(PROPNAME, Qt.Horizontal, "Property/Function")
        model.setHeaderData(COLNAME, Qt.Horizontal, "Column Name")
        return model

    def clearPropBox(self):
        # property box grid setup
        #       PROPRETURN, PROPPARM, PROPNAME, COLNAME
        self.gridPropBox.setModel(None)
        self.propBoxModel = self.createPropBoxModel()
        self.gridPropBox.setModel(self.propBoxModel)
        self.gridPropBox.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.gridPropBox.setSelectionMode(QAbstractItemView.SingleSelection)
        self.gridPropBox.setColumnWidth(PROPRETURN, 100)
        self.gridPropBox.setColumnWidth(PROPPARM, 100)
        self.gridPropBox.setColumnWidth(PROPNAME, 400)
        self.gridPropBox.setColumnWidth(COLNAME, 400)
        # header
        header = self.gridPropBox.horizontalHeader()
        header.setSectionResizeMode(PROPRETURN, QHeaderView.Fixed)
        header.setSectionResizeMode(PROPPARM, QHeaderView.Fixed)
        header.setSectionResizeMode(PROPNAME, QHeaderView.Stretch)
        header.setSectionResizeMode(COLNAME, QHeaderView.Stretch)
        # set editor delegate
        #        self.gridPropBox.setItemDelegateForColumn(PROPNAME, MetaBoxDelegate(self))
        # connect model slots
        self.propBoxModel.itemChanged.connect(self.propBoxModelItemChanged)
        # connect grid slots
        self.gridPropBox.selectionModel().selectionChanged.connect(
            self.propBoxGridSelectionChanged)

#    def addPropRow(self, propName=None, colName=None, propParm=None, propReturn=None ):

    def addPropRow(self, returnProp=None):
        '''
        add a row to the property box grid
        '''
        #  PROPRETURN, PROPPARM, PROPNAME, COLNAME
        self.gridPropBox.setSortingEnabled(False)
        # checkbox to add property to the return clause
        propReturn = returnProp[PROPRETURN]
        if propReturn is None or propReturn == "":
            propReturn = Qt.Unchecked
        item1 = QStandardItem()
        item1.setEditable(True)
        item1.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        #        # save the attribute name in the value item so the custom editor MetaBoxDelegate can find it
        #        item1.setData(propReturn, Qt.UserRole)
        if propReturn in [0, 1, 2]:
            item1.setCheckState(propReturn)
        else:
            item1.setCheckState(Qt.Unchecked)
        item1.setText("")

        # checkbox to indicate property is a parameter
        propParm = returnProp[PROPPARM]
        if propParm is None or propParm == "":
            propParm = Qt.Unchecked
        item2 = QStandardItem()
        item2.setEditable(True)
        item2.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        #        # save the attribute name in the value item so the custom editor MetaBoxDelegate can find it
        #        item2.setData(propParm, Qt.UserRole)
        if propParm in [0, 1, 2]:
            item2.setCheckState(propParm)
        else:
            item2.setCheckState(Qt.Unchecked)
        item2.setText("")

        # property name
        if returnProp[PROPNAME] is None:
            returnProp[PROPNAME] = ""
        item3 = QStandardItem(returnProp[PROPNAME])
        item3.setEditable(False)

        # column name
        if returnProp[COLNAME] is None or returnProp[COLNAME] == "":
            colName = "{}_{}".format(str(self.getCurrentCypherVar()),
                                     returnProp[PROPNAME])
        else:
            colName = returnProp[COLNAME]
        item4 = QStandardItem(colName)
        item4.setEditable(True)

        # add it to the grid
        self.gridPropBox.model().appendRow([item1, item2, item3, item4])

    def populatePropBox(self, queryPathNode=None):
        #        PROPRETURN, PARAMETER, PROPNAME, COLNAME
        if not queryPathNode is None:
            self.clearPropBox()
            for returnProp in queryPathNode.returnProps:
                self.addPropRow(returnProp)

    def propBoxModelItemChanged(self, item):
        # a cell changed so see if other changes are needed
        # update the queryPathNode object
        # print("item data changed {} {} at row:{} col:{}".format(str(item.checkState()), propName, item.index().row(), item.index().column()))
        selected = self.tvPath.currentItem()
        if not (selected is None):
            queryPathNode = selected.data(0, Qt.UserRole)
            propName = self.gridPropBox.model().item(
                item.index().row(), PROPNAME).data(Qt.EditRole)
            colName = self.gridPropBox.model().item(item.index().row(),
                                                    COLNAME).data(Qt.EditRole)
            propReturn = self.gridPropBox.model().item(
                item.index().row(), PROPRETURN).checkState()
            propParm = self.gridPropBox.model().item(item.index().row(),
                                                     PROPPARM).checkState()
            queryPathNode.updateProp([propReturn, propParm, propName, colName])
            self.refreshCypher()

        # force selection of this cell
        self.gridPropBox.setCurrentIndex(item.index())

    def propBoxGridSelectionChanged(self):
        # not used
        #        print("propbox grid selection changed")
        return

#-----------------------------------------------------------------------------------------------------------------------
#  tree view methods
#-----------------------------------------------------------------------------------------------------------------------

    def clearTree(self):
        self.tvPath.clear()
        self.tvPath.setColumnCount(1)
        self.tvPath.setHeaderLabels(["Query Path"])
        self.tvPath.setItemsExpandable(True)

    def getMaxTreeOrder(self, ):
        '''scan the tree and find the max order attribute.  This is used to increment and create the next highest order number'''
        max = 0
        # iterate through the treeview
        tvPathIterator = QTreeWidgetItemIterator(
            self.tvPath, flags=QTreeWidgetItemIterator.All)
        while tvPathIterator:
            if not tvPathIterator.value() is None:
                queryPathNodeItem = tvPathIterator.value()
                queryPathNodeDict = queryPathNodeItem.data(0,
                                                           Qt.UserRole).dict()
                order = queryPathNodeDict.get("order", 0)
                # save the value for order if it's greater
                if order > max:
                    max = order
                tvPathIterator.__iadd__(1)
            else:
                break
        return max

    def findParentWidget(self, findOrder=None):
        '''scan the tree and find the treeviewwidget with the matching parentOrder'''
        # if the find id is None then this is the root so return the tree view itself
        if findOrder is None:
            return self.tvPath
        # find the parent tree view widget
        parentWidget = None
        # iterate through the treeview
        tvPathIterator = QTreeWidgetItemIterator(
            self.tvPath, flags=QTreeWidgetItemIterator.All)
        while tvPathIterator:
            if not tvPathIterator.value() is None:
                queryPathNodeItem = tvPathIterator.value()
                queryPathNodeDict = queryPathNodeItem.data(0,
                                                           Qt.UserRole).dict()
                order = queryPathNodeDict.get("order", 0)
                # save the value for order if it's greater
                if order == findOrder:
                    parentWidget = queryPathNodeItem
                    break
                tvPathIterator.__iadd__(1)
            else:
                break

        return parentWidget

    def populateTree(self, ):
        self.clearTree()
        #        print("path dict {}".format(self.objectDict))
        # add tree items
        if len(self.objectDict["queryPath"]) > 0:
            for tvPathDict in self.objectDict["queryPath"]:
                # create the tree view path item object
                pathItem = QueryPathNode(designModel=self.designModel,
                                         nodeDict=tvPathDict)
                #                print("add path item {}".format(pathItem.displayName))
                # figure out who the parent is
                parent = self.findParentWidget(findOrder=pathItem.parentOrder)
                # add the treewidgetitem to the tree
                pathItem.treeItem = self.addTreeNode(parent=parent,
                                                     pathItem=pathItem)
        else:
            # add a new root node
            pathItem = QueryPathNode(designModel=self.designModel,
                                     root=True,
                                     parentOrder=None,
                                     order=0,
                                     type="Path Template")
            pathItem.treeItem = self.addTreeNode(parent=self.tvPath,
                                                 pathItem=pathItem)

        self.tvPath.resizeColumnToContents(0)
        self.tvPath.setCurrentItem(self.tvPath.topLevelItem(0))
        # update cypher
        self.refreshCypher()

    def addTreeNode(self, parent=None, pathItem=None):
        #        print("add tree node {}".format(pathItem.displayName))
        item = QTreeWidgetItem(parent, [pathItem.displayName])
        item.setData(0, Qt.UserRole, pathItem)
        item.setChildIndicatorPolicy(QTreeWidgetItem.ShowIndicator)
        item.setExpanded(True)
        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable
                      | Qt.ItemIsEnabled)
        return item

###########################################################################################
# query path tree view methods
###########################################################################################

    def openMenu(self, position):
        selected = self.tvPath.currentItem()
        if not (selected is None):
            tvPathNode = selected.data(0, Qt.UserRole)
            if (tvPathNode.type == "Path Template"):
                menu = QMenu()
                addNodeAction = menu.addAction("Add Node")
                addNodeAction.triggered.connect(self.addNodeTreeNode)
                addNodeAction = menu.addAction("Add Relationship")
                addNodeAction.triggered.connect(self.addRelTreeNode)
                menu.exec_(self.tvPath.mapToGlobal(position))
                return
            if (tvPathNode.type == "Node"):
                menu = QMenu()
                #                addNodeAction = menu.addAction("Add Relationship")
                #                addNodeAction.triggered.connect(self.addRelTreeNode)
                subOutMenu = QMenu("Out Bound Relationships", parent=menu)
                # generate outboundrel menu items
                outBoundList = self.parent.model.getOutboundRelTemplates(
                    nodeTemplateName=tvPathNode.templateName)
                for relTemplate in outBoundList:
                    aSubAction = subOutMenu.addAction(relTemplate["name"])
                    aSubAction.setData(relTemplate)
                    aSubAction.triggered.connect(self.addOutboundRel)
#                    print("outboundlist {}".format(relTemplate))
                if len(outBoundList) == 0:
                    aSubAction = subOutMenu.addAction(
                        "No Outbound Relationship Templates")
                menu.addMenu(subOutMenu)

                subInMenu = QMenu("In Bound Relationships", parent=menu)
                # generate outboundrel menu items
                inBoundList = self.parent.model.getInboundRelTemplates(
                    nodeTemplateName=tvPathNode.templateName)
                for relTemplate in inBoundList:
                    aSubAction = subInMenu.addAction(relTemplate["name"])
                    aSubAction.setData(relTemplate)
                    aSubAction.triggered.connect(self.addInboundRel)
#                    print("inboundlist {}".format(relTemplate))
                if len(inBoundList) == 0:
                    aSubAction = subInMenu.addAction(
                        "No Inbound Relationship Templates")
                menu.addMenu(subInMenu)

                removeNodeAction = menu.addAction("Remove this Node")
                removeNodeAction.triggered.connect(self.removeTreeNode)
                menu.exec_(self.tvPath.mapToGlobal(position))
                return
            if (tvPathNode.type == "Relationship"):
                menu = QMenu()
                addNodeAction = menu.addAction("Add Node")
                addNodeAction.triggered.connect(self.addNodeTreeNode)
                addNodeAction = menu.addAction("Remove This Relationship")
                addNodeAction.triggered.connect(self.removeTreeNode)
                menu.exec_(self.tvPath.mapToGlobal(position))
                return

    def addOutboundRel(self):
        # get the action that called this method
        aSubAction = QObject.sender(self)
        relTemplateDict = aSubAction.data()
        if not relTemplateDict is None:
            # add the rel template to the path
            self.addRelTreeNode(templateName=relTemplateDict["name"])
            # add the node template to the path
            self.addNodeTreeNode(templateName=relTemplateDict["toTemplate"])

    def addInboundRel(self):
        # get the action that called this method
        aSubAction = QObject.sender(self)
        relTemplateDict = aSubAction.data()
        if not relTemplateDict is None:
            # add the rel template to the path
            self.addRelTreeNode(templateName=relTemplateDict["name"])
            # add the node template to the path
            self.addNodeTreeNode(templateName=relTemplateDict["fromTemplate"])

    def addRelTreeNode(self, templateName=None):
        '''add a relationship tree-node into the query path tree'''
        parentItem = self.tvPath.currentItem()
        parentDict = parentItem.data(0, Qt.UserRole).dict()
        parentOrder = parentDict.get("order", 0)
        order = self.getMaxTreeOrder() + 1
        #        print("add rel node {}-{}".format(order, parentOrder))
        queryPathNode = QueryPathNode(designModel=self.designModel,
                                      parentOrder=parentOrder,
                                      order=order,
                                      type="Relationship",
                                      templateName=templateName)
        # set a ypher variable name for the relationship
        queryPathNode.cypherVar = "r{}".format(
            str(queryPathNode.order).lower())
        #        queryPathNode.reltype = self.designModel.getRelType(templateName)
        queryPathNode.treeItem = self.addTreeNode(parent=parentItem,
                                                  pathItem=queryPathNode)
        self.tvPath.setCurrentItem(queryPathNode.treeItem)
        # update cypher
        self.refreshCypher()

    def addNodeTreeNode(self, templateName=None):
        '''add a node tree-node into the query path tree'''
        # don't know why this happens...
        if type(templateName) is bool:
            templateName = None
        parentItem = self.tvPath.currentItem()
        parentDict = parentItem.data(0, Qt.UserRole).dict()
        parentOrder = parentDict.get("order", 0)
        order = self.getMaxTreeOrder() + 1
        #        print("add node node {}-{}".format(order, parentOrder))
        queryPathNode = QueryPathNode(designModel=self.designModel,
                                      parentOrder=parentOrder,
                                      order=order,
                                      type="Node",
                                      templateName=templateName)
        # set a cypher variable name for the Node
        queryPathNode.cypherVar = "{}".format(
            queryPathNode.templateName[0].lower())
        queryPathNode.treeItem = self.addTreeNode(parent=parentItem,
                                                  pathItem=queryPathNode)
        self.tvPath.setCurrentItem(queryPathNode.treeItem)
        # update cypher
        self.refreshCypher()

    def removeTreeNode(self):
        '''remove a node from the tree and all descedants'''
        #        print("remove item")
        currentItem = self.tvPath.currentItem()
        parentItem = currentItem.parent()
        parentItem.removeChild(currentItem)
        self.tvPath.takeTopLevelItem(
            self.tvPath.indexOfTopLevelItem(currentItem))
        # update cypher
        self.refreshCypher()

    @pyqtSlot()
    def on_okButton_clicked(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)

    @pyqtSlot()
    def on_cancelButton_clicked(self):
        """
        User Selects the Cancel button
        """
        QDialog.reject(self)

    @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
    def on_tvPath_currentItemChanged(self, current, previous):
        """
        Not Used
        
        @param current DESCRIPTION
        @type QTreeWidgetItem
        @param previous DESCRIPTION
        @type QTreeWidgetItem
        """
        print("on tvpath current item changed")
        return

    @pyqtSlot(QModelIndex)
    def on_tvPath_clicked(self, index):
        """
        Slot documentation goes here.
        
        @param index DESCRIPTION
        @type QModelIndex
        """
        print("on_tvPath_clicked")

    @pyqtSlot(QModelIndex)
    def on_tvPath_activated(self, index):
        """
        Slot documentation goes here.
        
        @param index DESCRIPTION
        @type QModelIndex
        """
        print("on_tvPath_activated")

    @pyqtSlot(QTreeWidgetItem, int)
    def on_tvPath_itemClicked(self, item, column):
        """
        Slot documentation goes here.
        
        @param item DESCRIPTION
        @type QTreeWidgetItem
        @param column DESCRIPTION
        @type int
        """
        print("on_tvPath_itemClicked")

    @pyqtSlot(QTreeWidgetItem, int)
    def on_tvPath_itemActivated(self, item, column):
        """
        Slot documentation goes here.
        
        @param item DESCRIPTION
        @type QTreeWidgetItem
        @param column DESCRIPTION
        @type int
        """
        print("on_tvPath_itemActivated")

    @pyqtSlot(QTreeWidgetItem, int)
    def on_tvPath_itemEntered(self, item, column):
        """
        Slot documentation goes here.
        
        @param item DESCRIPTION
        @type QTreeWidgetItem
        @param column DESCRIPTION
        @type int
        """
        print("on_tvPath_itemEntered")

    @pyqtSlot()
    def on_tvPath_itemSelectionChanged(self):
        """
        User clicks on an item in the tree view
        """
        print("on_tvPath_itemSelectionChanged")
        selected = self.tvPath.currentItem()
        if not (selected is None):
            #            parent = self.tvPath.currentItem().parent()
            queryPathNode = selected.data(0, Qt.UserRole)
            self.populateMetaBox(queryPathNode=queryPathNode)
            self.populatePropBox(queryPathNode=queryPathNode)

    @pyqtSlot(int)
    def on_tabPathTemplate_currentChanged(self, index):
        """
        The user has switched to a different tab
        DEFINITION, DESCRIPTION, DATAGRID
        @param index DESCRIPTION
        @type int
        """
        # user switched to the description tab.  regenerate the description
        #        if index == DESCRIPTION:
        #            if self.validate():
        #                self.apply()
        #                # get generated description
        #                self.brwsrGenDescription.setText(self.designModel.getNodeDescription(self.objectDict["name"]))

        # user switched to the definition tab.
        #        if index == DEFINITION:
        #            self.syncDefCheckBoxes()
        # user switched to the data grid then update object dictionary so query will generate with latest values
        if index == DATAGRID:
            if self.validate():
                self.apply()
                self.nodeGrid.refreshGrid()
            else:
                self.tabPathTemplate.setCurrentIndex(0)

    def refreshCypher(self):
        if self.validate():
            self.apply()
            self.cypher, self.editParmDict = self.nodeGrid.genCypher.genMatch()
            #                print("cypher:{}".format(self.cypher))
            self.txtCypher.clear()
            self.txtCypher.appendPlainText(self.cypher)

    @pyqtSlot()
    def on_btnAddNode_clicked(self):
        """
        user clicks on add node button
        """
        '''add a node tree-node into the query path tree'''
        parentItem = self.tvPath.currentItem()
        parentDict = parentItem.data(0, Qt.UserRole).dict()
        parentOrder = parentDict.get("order", 0)
        order = self.getMaxTreeOrder() + 1
        queryPathNode = QueryPathNode(designModel=self.designModel,
                                      parentOrder=parentOrder,
                                      order=order,
                                      type="Node",
                                      templateName="Anonymous Node")
        # set a cypher variable name for the Node
        queryPathNode.cypherVar = "n{}".format(
            str(queryPathNode.order).lower())
        queryPathNode.treeItem = self.addTreeNode(parent=parentItem,
                                                  pathItem=queryPathNode)
        self.tvPath.setCurrentItem(queryPathNode.treeItem)
        # update cypher
        self.refreshCypher()

    @pyqtSlot()
    def on_btnAddRel_clicked(self):
        """
        user clicks on add relationship button
        """
        parentItem = self.tvPath.currentItem()
        parentDict = parentItem.data(0, Qt.UserRole).dict()
        parentOrder = parentDict.get("order", 0)
        order = self.getMaxTreeOrder() + 1
        queryPathNode = QueryPathNode(designModel=self.designModel,
                                      parentOrder=parentOrder,
                                      order=order,
                                      type="Relationship",
                                      templateName="Anonymous Relationship")
        # set a ypher variable name for the relationship
        queryPathNode.cypherVar = "r{}".format(
            str(queryPathNode.order).lower())
        queryPathNode.treeItem = self.addTreeNode(parent=parentItem,
                                                  pathItem=queryPathNode)
        self.tvPath.setCurrentItem(queryPathNode.treeItem)
        # update cypher
        self.refreshCypher()

    @pyqtSlot()
    def on_btnRemove_clicked(self):
        """
        User clicks on remove button
        """
        return

    @pyqtSlot()
    def on_btnAdd_clicked(self):
        """
        Slot documentation goes here.
        """
        # TODO: not implemented yet
        raise NotImplementedError

    @pyqtSlot()
    def on_btnRemove_2_clicked(self):
        """
        Slot documentation goes here.
        """
        # TODO: not implemented yet
        raise NotImplementedError
예제 #11
0
class IRPropertyBox(QDialog, Ui_IRPropertyBox):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None, diagramInstance=None, model=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(IRPropertyBox, self).__init__(parent)
        self.appStartup = True
        self.mergeTemplate = False
        self.parent = parent
        self.schemaModel = self.parent.schemaObject
        self.helper = Helper()
        # this is the RelationInstance object - called generically diagramInstance.
        self.diagramInstance = diagramInstance
        self.diagramInstance.reloadDictValues()
        self.model = model
        self.modelData = self.model.modelData
        self.rel = None
        self.setupUi(self)
        #        self.appSetToTemplate = False
        #        self.appSetFromTemplate = False

        self.initUI()

        self.populateUIfromObject()

        self.appStartup = False
        self.msg = ""

    def initUI(self, ):

        # property grid
        self.gridProps.setSortingEnabled(False)
        self.gridProps.setModel(self.createPropModel())
        self.gridProps.setSortingEnabled(False)
        comboPropList = [""]
        comboPropList.extend(
            sorted(
                set(
                    self.model.instanceList("Property") +
                    self.schemaModel.instanceList("Property"))))
        dataTypeList = [dataType.value for dataType in DataType]
        self.gridProps.setItemDelegate(NeoEditDelegate(self))
        self.gridProps.setItemDelegateForColumn(
            DATATYPE, CBDelegate(self, dataTypeList, setEditable=False))
        self.gridProps.setItemDelegateForColumn(
            PROPERTY, CBDelegate(self, comboPropList, setEditable=True))
        self.gridProps.setColumnWidth(PROPERTY, 200)
        self.gridProps.setColumnWidth(DATATYPE, 125)
        self.gridProps.setColumnWidth(VALUE, 300)
        self.gridProps.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.gridProps.setSelectionMode(QAbstractItemView.SingleSelection)
        header = self.gridProps.horizontalHeader()
        header.setSectionResizeMode(PROPERTY, QHeaderView.Interactive)
        header.setSectionResizeMode(DATATYPE, QHeaderView.Fixed)
        header.setSectionResizeMode(VALUE, QHeaderView.Stretch)

    def populateUIfromObject(self, ):

        # load rel name list, select rel name
        self.loadRelationshipNameDropDown()
        # load from nodes, select the from node and load the from node template if any
        self.loadFromNodeDropdown()
        # load to nodes, select the to node and load the to node template if any
        self.loadToNodeDropdown()
        # load rel template names, select rel template name if any
        self.loadRelTemplateDropDown()

        #NZID
        try:
            self.EditNZID.insert(str(self.diagramInstance.NZID))
        except:
            print("no NZID")  # this shouldn't happen
        # neoID
        try:
            self.editNeoID.insert(str(self.diagramInstance.neoID))
        except:
            self.editNeoID.insert("None")

        # disable rel name if already defined
        if self.editNeoID.text() == "None":
            self.cboRelName.setEnabled(True)
        else:
            self.cboRelName.setEnabled(False)

        # disable dropdowns if start and end nodes defined
        if self.cboFromNode.currentIndex() > 0:
            self.cboFromNode.setEnabled(False)
        if self.cboToNode.currentIndex() > 0:
            self.cboToNode.setEnabled(False)

        # load the properties
        for nodeProp in self.diagramInstance.propList:
            self.addProp(self.gridProps.model(), nodeProp[PROPERTY],
                         nodeProp[DATATYPE], nodeProp[VALUE])

    def loadRelationshipNameDropDown(self, ):
        # load relationship name (type)  dropdown
        dropdownList = []
        dropdownList.append("Enter or Select Relationship Type")
        # get relationship types from the project model
        templateRelList = [
            relDict["name"] for relDict in self.modelData["Relationship"]
        ]
        #        templateRelList = [relDict["relname"] for relDict in self.modelData["Relationship Template"] ]
        # get relationship types from the schemamodel - this needs to be fixed, need common access to schemamodel from project page and instance diagram
        schemaRelList = [
            rel["name"] for rel in self.schemaModel.schemaData["Relationship"]
        ]
        #        schemaRelList = []
        # merge, dedup, and sort the two lists and put them in the dropdown
        dropdownList.extend(sorted(set(templateRelList + schemaRelList)))
        self.cboRelName.addItems(dropdownList)
        # if no relationship name has been set then display enabled combo box
        if (self.diagramInstance.relName is None
                or self.diagramInstance.relName == "NoRelationshipName"):
            self.cboRelName.setCurrentIndex(0)
        else:
            # if the relationship name has been set then display the rel name
            index = self.cboRelName.findText(self.diagramInstance.relName)
            if index >= 0:
                self.cboRelName.setCurrentIndex(index)
            else:
                # its not a template rel name so just set the text value of the combo box
                self.cboRelName.setEditText(self.diagramInstance.relName)

    def loadFromNodeDropdown(self, ):
        # load from node dropdown
        dropdownList = []
        dropdownList.append("No From Node Selected")
        nodeList = self.model.instanceDisplayNameList("Instance Node")
        for node in nodeList:
            dropdownList.append(node)
        self.cboFromNode.addItems(dropdownList)
        if not self.diagramInstance.startNZID is None:
            item, objectDict = self.model.getDictByName(
                "Instance Node", self.diagramInstance.startNZID)
            if not objectDict is None:
                index = self.cboFromNode.findText(
                    objectDict.get("displayName", ""))
                if index >= 0:
                    self.cboFromNode.setCurrentIndex(index)
                    self.cboFromNode.setEnabled(False)

    def loadToNodeDropdown(self, ):

        # load from node dropdown
        dropdownList = []
        dropdownList.append("No To Node Selected")
        nodeList = self.model.instanceDisplayNameList("Instance Node")
        for node in nodeList:
            dropdownList.append(node)
        self.cboToNode.addItems(dropdownList)
        if not self.diagramInstance.endNZID is None:
            item, objectDict = self.model.getDictByName(
                "Instance Node", self.diagramInstance.endNZID)
            if not objectDict is None:
                index = self.cboToNode.findText(
                    objectDict.get("displayName", ""))
                if index >= 0:
                    self.cboToNode.setCurrentIndex(index)
                    self.cboToNode.setEnabled(False)

    def loadRelTemplateDropDown(self, ):
        # load rel template dropdown
        self.cboTemplate.clear()
        startNodeTemplate = self.txtFromTemplate.text()
        endNodeTemplate = self.txtToTemplate.text()
        #        relName = self.diagramInstance.relName
        relName = self.cboRelName.currentText()
        dropdownList = ["No Template Selected"]
        dropdownList.extend(
            sorted(
                self.model.matchAllRelTemplates(
                    startNodeTemplate=startNodeTemplate,
                    endNodeTemplate=endNodeTemplate,
                    relName=relName)))
        self.cboTemplate.addItems(dropdownList)
        if not self.diagramInstance.relTemplate is None:
            index = self.cboTemplate.findText(self.diagramInstance.relTemplate)
            if index >= 0:
                self.cboTemplate.setCurrentIndex(index)

    def createPropModel(self):

        model = QStandardItemModel(0, 3)
        model.setHeaderData(PROPERTY, Qt.Horizontal, "Property")
        model.setHeaderData(DATATYPE, Qt.Horizontal, "Data Type")
        model.setHeaderData(VALUE, Qt.Horizontal, "Value")
        # connect model slots
        model.itemChanged.connect(self.propModelItemChanged)

        return model

    def getNodeInstance(self, NZID):
        '''
        Using an NZID, create a NodeInstanceObject and return it
        if it doesn't exist, return None
        '''
        index, nodeDict = self.model.getDictByName(topLevel="Instance Node",
                                                   objectName=NZID)
        nodeInstance = NodeInstance(model=self.model,
                                    nodeInstanceDict=nodeDict)
        return nodeInstance

    def logMsg(self, msg):
        # add message to the log
        if logging:
            logging.info(msg)

    def validate(self, ):
        if self.helper.gridNoNameError(
                grid=self.gridProps,
                col=PROPERTY,
                txtMsg="You must supply a name for each Property"):
            self.gridProps.setFocus()
            return False
        if self.helper.gridDupEntryError(
                self.gridProps,
                col=PROPERTY,
                txtMsg=
                "has been entered more than once. You can only use a Property once"
        ):
            self.gridProps.setFocus()
            return False
        if self.helper.NoTextValueError(self.cboRelName.currentText(),
                                        "You must enter a relationship name."):
            self.cboRelName.setFocus()
            return False
        if self.cboRelName.currentText(
        ) == "Enter or Select Relationship Type":
            self.helper.displayErrMsg("Instance Relationship",
                                      "You must enter a relationship name.")
            self.cboRelName.setFocus()
            return False
        if self.cboFromNode.currentIndex() == 0:
            self.helper.displayErrMsg("Instance Relationship",
                                      "You must select a from node.")
            self.cboFromNode.setFocus()
            return False
        if self.cboToNode.currentIndex() == 0:
            self.helper.displayErrMsg("Instance Relationship",
                                      "You must select a to node.")
            self.cboToNode.setFocus()
            return False
        # property datatype matches property definition
        model = self.gridProps.model()
        for row in range(0, model.rowCount()):
            nodeProp = [
                model.item(row, PROPERTY).data(Qt.EditRole),
                model.item(row, DATATYPE).data(Qt.EditRole),
                model.item(row, VALUE).data(Qt.EditRole)
            ]
            if self.model.propertyDataTypeValid(
                    name=nodeProp[PROPERTY],
                    dataType=nodeProp[DATATYPE]) == False:
                self.helper.displayErrMsg(
                    "Validate",
                    "You entered datatype {} for  property {} which does not match the property definition. Please enter the correct datatype."
                    .format(nodeProp[DATATYPE], nodeProp[PROPERTY]))
                self.gridProps.setFocus()
                return False

        # template defined required property has a value
        templateName = self.cboTemplate.currentText()
        saveIndex, relTemplateDict = self.model.getDictByName(
            topLevel="Relationship Template", objectName=templateName)
        if not relTemplateDict is None:
            model = self.gridProps.model()
            numrows = model.rowCount()
            for row in range(0, numrows):
                nodeProp = [
                    model.item(row, PROPERTY).data(Qt.EditRole),
                    model.item(row, DATATYPE).data(Qt.EditRole),
                    model.item(row, VALUE).data(Qt.EditRole)
                ]
                if nodeProp[VALUE] == "Null":
                    #  the value is null so see if it is required
                    for templateProp in relTemplateDict["properties"]:
                        if templateProp[PROPERTY] == nodeProp[PROPERTY]:
                            if templateProp[PROPREQ] == Qt.Checked:
                                # this property must have a value
                                self.helper.displayErrMsg(
                                    "Validate",
                                    "The property {} is required. Please enter a value."
                                    .format(nodeProp[PROPERTY]))
                                return False
        return True

    def apply(self, ):
        #update the diagramInstance object with values from the UI.
        self.diagramInstance.relName = self.cboRelName.currentText()
        # if its a new rel type then add it to the model
        self.model.newRelationship(self.diagramInstance.relName)
        # update property list
        self.diagramInstance.propList = []
        model = self.gridProps.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            nodeProp = [
                model.item(row, PROPERTY).data(Qt.EditRole),
                model.item(row, DATATYPE).data(Qt.EditRole),
                model.item(row, VALUE).data(Qt.EditRole)
            ]
            #            print(nodeProp)
            # if its a new property add it to the model
            self.model.newProperty(nodeProp[PROPERTY], nodeProp[DATATYPE])
            self.diagramInstance.propList.append(nodeProp)
        #save the template
        selectedTemplate = self.cboTemplate.currentText()
        self.diagramInstance.relTemplate = selectedTemplate
        # save the relationship itself in Neo4j
        rc, msg = self.updateNeo()
        self.msg = msg
        return rc, msg

    def updateNeo(self, ):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        if self.modelData["SyncMode"] == "On":
            rc, msg = self.diagramInstance.syncToDB(self.logMsg)
            if rc is True:
                returnmsg = "Update Relationship Succesful"
            else:
                returnmsg = "Update Relationship Failed: {}".format(msg)
                self.helper.displayErrMsg("Update Relationship", returnmsg)
        else:
            rc = True
            returnmsg = "Database Sync is off - Relationship not saved to graph"

        QApplication.restoreOverrideCursor()
        return rc, returnmsg

    @pyqtSlot()
    def on_btnPropAdd_clicked(self):
        """
        Slot documentation goes here.
        """
        self.addProp(self.gridProps.model(), "", "String", "Null")
        # common function to adjust grid after appending a row
        self.helper.adjustGrid(grid=self.gridProps)
#        # scroll to bottom to insure the new row is visible
#        self.gridProps.scrollToBottom()

    @pyqtSlot()
    def on_btnPropRemove_clicked(self):
        """
        Slot documentation goes here.
        """
        indexes = self.gridProps.selectionModel().selectedIndexes()
        if len(indexes) > 0:
            for index in sorted(indexes):
                self.gridProps.model().removeRows(index.row(), 1)
        else:
            self.helper.displayErrMsg("Remove Property",
                                      "You must select a row to remove")

    @pyqtSlot()
    def on_btnPropUp_clicked(self):
        """
        User clicks on property up button
        """
        self.helper.moveTableViewRowUp(self.gridProps)

    @pyqtSlot()
    def on_btnPropDown_clicked(self):
        """
        User clicks on property down button
        """
        self.helper.moveTableViewRowDown(self.gridProps)

    @pyqtSlot()
    def on_okButton_clicked(self):
        """
        User clicked on OK button, validate data and sync to graph
        """
        if self.validate():
            rc, msg = self.apply()
            if rc == True:
                QDialog.accept(self)

    @pyqtSlot()
    def on_cancelButton_clicked(self):
        """
        Slot documentation goes here.
        """
        QDialog.reject(self)

    def addProp(self, model, prop, dataType, value):

        self.gridProps.setSortingEnabled(False)
        item1 = QStandardItem(prop)
        item1.setEditable(True)
        item11 = QStandardItem(dataType)
        item11.setEditable(True)
        item2 = QStandardItem(value)
        item2.setData(dataType, Qt.UserRole + 1)
        item2.setEditable(True)
        model.appendRow([item1, item11, item2])

    @pyqtSlot()
    def on_btnCreateTemplate_clicked(self):
        """
        Create a new Relationship Template based on this instance relationship.
        """
        text, ok = QInputDialog.getText(
            self, 'New Relationship Template',
            'Enter the Relationship Template Name:')
        if ok:
            #  make sure they entered something
            if len(text) < 1:
                self.helper.displayErrMsg(
                    "Create New Relationship Template Error",
                    "You must enter a name.".format(text))
            #make sure entered name doesn't exist
            index, relDict = self.model.getDictByName("Relationship Template",
                                                      text)
            if not relDict is None:
                self.helper.displayErrMsg(
                    "Create New Relationship Template Error",
                    "The Relationship template {} already exists".format(text))
                return
            # validate the data first, then add the new relationship template
            if self.validate():
                # if its a new rel type then add it to the model
                self.model.newRelationship(self.cboRelName.currentText())
                # save the properties
                propList = []
                model = self.gridProps.model()
                numrows = model.rowCount()
                for row in range(0, numrows):
                    nodeProp = [
                        model.item(row, PROPERTY).data(Qt.EditRole),
                        model.item(row, DATATYPE).data(Qt.EditRole),
                        Qt.Unchecked, "", Qt.Unchecked
                    ]
                    self.model.newProperty(nodeProp[PROPERTY],
                                           nodeProp[DATATYPE])
                    propList.append(nodeProp)
                # generate the constraints
                conList = []
                # look at each constraint in the schemaModel and see if it belongs to the rel template
                self.schemaModel.matchConstraintRelTemplate(
                    conList, [prop[0] for prop in propList],
                    relName=self.cboRelName.currentText())
                # get the source/target node template if any
                # can only save a source target if both instance nodes are based on a node template
                try:
                    startNodeInstance = self.getNodeInstance(
                        self.diagramInstance.startNZID)
                    fromTemplate = startNodeInstance.nodeTemplate
                    endNodeInstance = self.getNodeInstance(
                        self.diagramInstance.endNZID)
                    toTemplate = endNodeInstance.nodeTemplate
                    if (not fromTemplate == "No Template Selected"
                            and not toTemplate == "No Template Selected"):
                        #save it to the model
                        relDict = self.model.newRelTemplateDict(
                            name=text,
                            relname=self.cboRelName.currentText(),
                            propList=propList,
                            desc=
                            "Template generated from Instance Relationship.",
                            conList=conList,
                            fromTemplate=fromTemplate,
                            toTemplate=toTemplate)
                        self.model.modelData["Relationship Template"].append(
                            relDict)
                        # refresh the treeview
                        self.model.updateTV()
                        # update dropdown  and select the newly created template
                        self.loadRelTemplateDropDown()
                        index = self.cboTemplate.findText(text)
                        if index >= 0:
                            self.cboTemplate.setCurrentIndex(index)
                    else:
                        self.helper.displayErrMsg(
                            "Create Template",
                            "Cannot create a relationship template without a from node template and a to node template."
                        )
                except Exception as e:
                    self.helper.displayErrMsg(
                        "Create Template",
                        "Error creating relationship template: {}".format(
                            str(e)))

    def templateChange(self, index):
        if index > 0:
            self.mergeTemplateWithInstanceRel(self.cboTemplate.currentText())

    def mergeTemplateWithInstanceRel(self, templateName):
        '''this merges the properties from the relationship template with the  properties that already exist on the instance relationship
        '''
        # if app is just starting up don't do this
        if self.appStartup:
            return
        # tell the other functions we're merging the template
        self.mergeTemplate = True

        saveIndex, relDict = self.model.getDictByName(
            topLevel="Relationship Template", objectName=templateName)
        if relDict is None:
            self.helper.displayErrMsg(
                "Merge Relationship Template",
                "Error - Relationship Template {} doesn't exist".format(
                    templateName))
            return

        # properties
        # what's on the form now
        existingPropList = [
            self.gridProps.model().item(row, PROPERTY).data(Qt.EditRole)
            for row in range(0,
                             self.gridProps.model().rowCount())
        ]
        existingValueList = [
            self.gridProps.model().item(row, VALUE).data(Qt.EditRole)
            for row in range(0,
                             self.gridProps.model().rowCount())
        ]
        # property list from the template
        newPropList = [
            nodeProp[PROPERTY] for nodeProp in relDict["properties"]
        ]
        newValueList = [
            nodeProp[PROPDEF] for nodeProp in relDict["properties"]
        ]  # this should get default values some day
        # merge them together
        mergePropList = (list(set(existingPropList + newPropList)))
        mergePropList.sort()
        self.gridProps.model().removeRows(0, self.gridProps.model().rowCount())
        for prop in mergePropList:
            val = ""
            # get default value from template first
            if prop in newPropList:
                val = newValueList[newPropList.index(prop)]
            # override with existing value for same property if it exists
            if prop in existingPropList:
                val = existingValueList[existingPropList.index(prop)]
            # set Null so editor delegates will work
            if val == "" or val is None:
                val = "Null"
            dataType = self.model.getPropertyDataType(prop)
            self.addProp(self.gridProps.model(), prop, dataType, val)

        self.diagramInstance.relName = relDict["relname"]
        index = self.cboRelName.findText(self.diagramInstance.relName)
        if index >= 0:
            self.cboRelName.setCurrentIndex(index)

        # we're done merging
        self.mergeTemplate = False

    @pyqtSlot(int)
    def on_cboTemplate_currentIndexChanged(self, index):
        """
        The User has selected a relationship template to use.
        
        @param index DESCRIPTION
        @type int
        """
        # if they selected a rel template then merge it with whatever is on the gui
        if index > 0:
            self.mergeTemplateWithInstanceRel(self.cboTemplate.currentText())

    @pyqtSlot(int)
    def on_cboFromNode_currentIndexChanged(self, index):
        """
        The user has selected a new from instance node for the relationship
        
        @param index DESCRIPTION
        @type int
        """
        if index > 0:
            # create NodeInstance object
            displayName = self.cboFromNode.currentText()
            NZID = self.model.lookupNZIDfromDisplayName(
                displayName=displayName, topLevel="Instance Node")
            if not NZID is None:
                self.diagramInstance.startNode = self.getNodeInstance(NZID)
                self.diagramInstance.startNZID = NZID
                # if the instance node is based on a node template then set that
                fromTemplate = self.diagramInstance.startNode.nodeTemplate
                self.txtFromTemplate.setText(fromTemplate)

        else:
            self.txtFromTemplate.setText("")

        # force rel template dropdown to refill valid choices
        if self.mergeTemplate == False:
            self.loadRelTemplateDropDown()

    @pyqtSlot(int)
    def on_cboToNode_currentIndexChanged(self, index):
        """
        The user has selected a new to instance node for the relationship
        
        @param index DESCRIPTION
        @type int
        """
        if index > 0:
            # create NodeInstance object
            displayName = self.cboToNode.currentText()
            NZID = self.model.lookupNZIDfromDisplayName(
                displayName=displayName, topLevel="Instance Node")
            if not NZID is None:
                self.diagramInstance.endNode = self.getNodeInstance(NZID)
                self.diagramInstance.endNZID = NZID
                # if the instance node is based on a node template then set that
                toTemplate = self.diagramInstance.endNode.nodeTemplate
                self.txtToTemplate.setText(toTemplate)
        else:
            self.txtToTemplate.setText("")

        # force node template dropdown to refill valid choices
        if self.mergeTemplate == False:
            self.loadRelTemplateDropDown()

    @pyqtSlot(int)
    def on_cboRelName_currentIndexChanged(self, index):
        """
        The relationship type was changed
        
        @param index DESCRIPTION
        @type int
        """
        # user changed the relationship type name so reset the template dropdown to new list of valid templates and pick the "none selected" option which forces the user to reselect a template
        if self.mergeTemplate == False:
            self.loadRelTemplateDropDown()
            self.cboTemplate.setCurrentIndex(0)

#        if index > 0:
##            relName = self.cboRelName.currentText()
#            # reload rel template dropdown to reflect newly selected rel type
##            relTemplate = self.cboTemplate.currentText()
#            self.loadRelTemplateDropDown()
#            self.cboTemplate.setCurrentIndex(0)
##            relList = ["select a relationship template"] + [relDict["name"] for relDict in self.modelData["Relationship Template"] if relDict["relname"] == relName]
##            self.cboTemplate.clear()
##            self.cboTemplate.addItems(relList)
#            # set to no rel template selected.
##            self.cboTemplate.setCurrentIndex(0)
#        else:
#            # user switched back to no rel name selected so relaod all rel templates
#            self.loadRelTemplateDropDown()
#            self.cboTemplate.setCurrentIndex(0)

    def propModelItemChanged(self, item):

        #        print("item data changed {} at {} {}".format(str(item.checkState()), item.index().row(), item.index().column()))

        # this checks to see if property name changed and updates the data type accordingly
        columnIndex = item.index().column()
        if columnIndex == PROPERTY:
            # if property has changed then change the datatype
            propName = self.gridProps.model().item(item.index().row(),
                                                   PROPERTY).data(Qt.EditRole)
            dataType = self.model.getPropertyDataType(propName)
            self.gridProps.model().item(item.index().row(),
                                        DATATYPE).setText(dataType)
        if columnIndex == DATATYPE:
            # if datatype has changed then change value to "Null"
            self.gridProps.model().item(item.index().row(),
                                        VALUE).setText("Null")
            dataType = self.gridProps.model().item(item.index().row(),
                                                   DATATYPE).data(Qt.EditRole)
            self.gridProps.model().item(item.index().row(),
                                        VALUE).setData(dataType,
                                                       Qt.UserRole + 1)

    @pyqtSlot()
    def on_btnSetNull_clicked(self):
        """
        User clicks button to set a property value to Null
        """
        indexes = self.gridProps.selectionModel().selectedIndexes()
        for index in indexes:
            valueIndex = self.gridProps.model().index(index.row(), VALUE)
            self.gridProps.model().setData(valueIndex, "Null", Qt.DisplayRole)

    @pyqtSlot(int)
    def on_tabRelInspector_currentChanged(self, index):
        """
        User has switched to another tab
        
        @param index DESCRIPTION
        @type int
        """
        # user switched to the description tab.  must regenerate description if there is a node template selected
        if index == DESCRIPTION:
            if self.cboTemplate.currentIndex() > 0:
                saveIndex, objectDict = self.model.getDictByName(
                    topLevel="Relationship Template",
                    objectName=self.cboTemplate.currentText())
                if not objectDict is None:
                    self.brwsrGeneratedDesc.setText(
                        self.model.getRelationshipDescription(objectDict))
                else:
                    self.helper.displayErrMsg(
                        "Get Description",
                        "Error - could not find node template: {}".format(
                            self.cboTemplate.currentText))
예제 #12
0
class ObjectRenameDlg(QDialog, Ui_Dialog):
    """
    Provide a modal dialog that allows the user to rename a toplevel object.
    """
    def __init__(self,
                 parent=None,
                 mode=None,
                 objectType=None,
                 objectName=None,
                 designModel=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(ObjectRenameDlg, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.objectType = objectType
        self.objectName = objectName
        self.designModel = designModel
        self.settings = QSettings()
        self.mode = mode
        self.helper = Helper()
        self.instanceObjectChanged = False
        # initialize UI
        self.txtCurrentObject.insert(self.objectType)
        self.txtCurrentName.insert(self.objectName)

        if self.mode == "VIEW":
            self.editNewName.setVisible(False)
            self.label_2.setVisible(False)
            self.lblInstructions.setVisible(False)
            self.lblInstructions2.setVisible(False)
            self.setWindowTitle("Where Used")

        if self.mode == "DELETE":
            self.editNewName.setVisible(False)
            self.label_2.setVisible(False)
            self.lblInstructions.setVisible(True)
            self.lblInstructions.setText(
                "Press OK to delete the object from the project and remove it from everywhere it is used."
            )
            self.lblInstructions2.setVisible(True)
            self.lblInstructions2.setText(
                "Press Cancel to exit this dialog without deleting.")
            self.setWindowTitle("Delete Object From Project")

        # setup usage grid
        self.gridUsage.setModel(self.createUsageModel())
        self.gridUsage.setColumnWidth(OBJECTTYPE, 200)
        self.gridUsage.setColumnWidth(OBJECTNAME, 200)
        self.gridUsage.setColumnWidth(OBJECTUSAGE, 200)
        self.gridUsage.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.gridUsage.setSelectionMode(QAbstractItemView.SingleSelection)
        header = self.gridUsage.horizontalHeader()
        header.setSectionResizeMode(OBJECTTYPE, QHeaderView.Interactive)
        header.setSectionResizeMode(OBJECTNAME, QHeaderView.Interactive)
        header.setSectionResizeMode(OBJECTUSAGE, QHeaderView.Interactive)

        # get where used descriptions
        self.displayList = []
        self.hitList = self.designModel.scanForObjectUse(
            self.objectType, self.objectName)

        # display in grid
        for hit in self.hitList:
            self.addHit(self.gridUsage.model(), hit[OBJECTTYPE],
                        hit[OBJECTNAME], hit[OBJECTUSAGE])

    def createUsageModel(self):
        # OBJECTTYPE, OBJECTNAME, OBJECTUSAGE
        model = QStandardItemModel(0, 3)
        model.setHeaderData(OBJECTTYPE, Qt.Horizontal, "Used In Object Type")
        model.setHeaderData(OBJECTNAME, Qt.Horizontal, "Used In Object Name")
        model.setHeaderData(OBJECTUSAGE, Qt.Horizontal, "Usage")
        return model

    def addHit(self, model, objectType, objectName, objectUsage):
        # OBJECTTYPE, OBJECTNAME, OBJECTUSAGE
        item1 = QStandardItem(objectType)
        item1.setEditable(False)
        item2 = QStandardItem(objectName)
        item2.setEditable(False)
        item3 = QStandardItem(objectUsage)
        item3.setEditable(False)

        model.appendRow([item1, item2, item3])

    def validate(self, ):
        if self.mode == "RENAME":
            if self.helper.NoTextValueError(self.editNewName.text().strip(),
                                            "You must enter a new Name"):
                self.editNewName.setFocus()
                return False

            if self.helper.DupObjectError(
                    designModel=self.designModel,
                    objName=self.editNewName.text(),
                    topLevel=self.objectType,
                    txtMsg="That name is already used.  Enter a new name."):
                self.editNewName.setFocus()
                return False

        return True

    def apply(self, ):
        '''The dialog passes all edits so do the rename.'''
        if self.mode == "RENAME":
            hitList = self.designModel.renameTopLevelObject(
                topLevel=self.objectType,
                objectName=self.objectName,
                newName=self.editNewName.text())
            # check to see if something changed that might require a redraw of open diagrams
            if not hitList is None:
                if len(hitList) > 0:
                    self.instanceObjectChanged = True

        if self.mode == "DELETE":
            hitList = self.designModel.deleteTopLevelObject(
                topLevel=self.objectType, objectName=self.objectName)
            # check to see if something changed that might require a redraw of open diagrams
            if not hitList is None:
                if len(hitList) > 0:
                    self.instanceObjectChanged = True

    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)

    @pyqtSlot()
    def on_buttonBox_rejected(self):
        """
        Slot documentation goes here.
        """
        QDialog.reject(self)
예제 #13
0
class dlgReverseEngineer(QDialog, Ui_dlgReverseEngineer):
    """
    This dialog box provides a facility to scan a neo4j database and generate node and relationship templates
    """
    def __init__(self,
                 parent=None,
                 schemaModel=None,
                 model=None,
                 settings=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(dlgReverseEngineer, self).__init__(parent)
        self.parent = parent
        self.schemaModel = schemaModel
        self.echo = True
        self.settings = settings
        self.model = model
        self.helper = Helper()
        self.neoTypeFunc = NeoTypeFunc()
        self.myNeoCon = self.model.modelNeoCon
        self.setupUi(self)
        self.initPage()

    def initPage(self, ):
        # header area
        self.editNeoURL.setText("{} - {}".format(self.myNeoCon.name,
                                                 self.myNeoCon.neoDict["URL"]))
        self.cbScanNodes.setCheckState(Qt.Checked)
        self.cbScanRels.setCheckState(Qt.Checked)
        # count of nodes and rels
        self.countStuff()
        # update percents
        self.testScan()
        # results area
        self.clearResults()
        # buttons
        self.stopScan = False
        self.btnStop.setEnabled(False)
        self.btnStart.setEnabled(True)

    def countStuff(self, ):
        # get max ID's in use
        try:
            cypher = '''call dbms.queryJmx("org.neo4j:instance=kernel#0,name=Primitive count") yield attributes
                        with  keys(attributes) as k , attributes
                        unwind k as row
                        return "ID Allocations" as type,row,attributes[row]["value"]
                     '''
            #run the query
            rc1, msg1 = self.myNeoCon.runCypherAuto(cypher)
            if rc1:
                self.maxRelId = 0
                self.maxNodeId = 0
                for row in self.myNeoCon.resultSet:
                    if row["row"] == 'NumberOfNodeIdsInUse':
                        self.maxNodeId = row['attributes[row]["value"]']
                    if row["row"] == 'NumberOfRelationshipIdsInUse':
                        self.maxRelId = row['attributes[row]["value"]']
                msg = "Max Node ID: {} Max Relationship ID: {}".format(
                    self.maxNodeId, self.maxRelId)
#                    self.editNumNodes.setText(str(self.myNeoCon.resultSet[0]["count(*)"]))
            else:
                msg = "Get Max ID Error {}".format(msg1)
        except BaseException as e:
            msg = "{} - Get Max ID failed.".format(repr(e))
        finally:
            QApplication.restoreOverrideCursor()
            self.displayScanMsg(msg)

        # get number of nodes
        try:
            cypher = "MATCH (n) RETURN count(*)"
            #run the query
            rc1, msg1 = self.myNeoCon.runCypherAuto(cypher)
            if rc1:
                msg = "Counted {} Nodes.".format(str(self.myNeoCon.resultSet))
                self.editNumNodes.setText(
                    str(self.myNeoCon.resultSet[0]["count(*)"]))
            else:
                msg = "Count Nodes Error {}".format(msg1)
        except BaseException as e:
            msg = "{} - Node Count failed.".format(repr(e))
        finally:
            QApplication.restoreOverrideCursor()
            self.displayScanMsg(msg)

        # get number of rels
        try:
            cypher = "MATCH ()-[r]->() RETURN count(*)"
            #run the query
            rc1, msg1 = self.myNeoCon.runCypherAuto(cypher)
            if rc1:
                msg = "Counted {} Relationships.".format(
                    str(self.myNeoCon.resultSet))
                self.editNumRels.setText(
                    str(self.myNeoCon.resultSet[0]["count(*)"]))
            else:
                msg = "Count Relationships Error {}".format(msg1)
        except BaseException as e:
            msg = "{} - Relationships Count failed.".format(repr(e))
        finally:
            QApplication.restoreOverrideCursor()
            self.displayScanMsg(msg)

    def clearResults(self, ):
        # message text area
        self.editProgress.clear()
        # NODE results grid
        #        GENERATE,, TEMPLATENAME, LABELPATTERN, PROPERTYPATTERN
        self.gridTemplates.setModel(self.createResultsModel())
        self.gridTemplates.setColumnWidth(GENERATE, 50)
        self.gridTemplates.setColumnWidth(TEMPLATENAME, 150)
        self.gridTemplates.setColumnWidth(LABELPATTERN, 300)
        self.gridTemplates.setColumnWidth(PROPERTYPATTERN, 300)
        self.gridTemplates.setColumnWidth(NODECOUNT, 100)

        header = self.gridTemplates.horizontalHeader()
        header.setSectionResizeMode(GENERATE, QHeaderView.Fixed)
        header.setSectionResizeMode(TEMPLATENAME, QHeaderView.Interactive)
        header.setSectionResizeMode(LABELPATTERN, QHeaderView.Interactive)
        header.setSectionResizeMode(PROPERTYPATTERN, QHeaderView.Interactive)
        header.setSectionResizeMode(NODECOUNT, QHeaderView.Fixed)

        # RELATIONSHIP results grid
        #       GENERATE, RELTEMPLATENAME, RELATIONSHIPNAME, FROMTEMPLATE, TOTEMPLATE, RELPROPERTYPATTERN = range(6)
        self.gridTemplates_Rel.setModel(self.createRelResultsModel())
        self.gridTemplates_Rel.setColumnWidth(GENERATE, 50)
        self.gridTemplates_Rel.setColumnWidth(RELTEMPLATENAME, 150)
        self.gridTemplates_Rel.setColumnWidth(RELATIONSHIPNAME, 125)
        self.gridTemplates_Rel.setColumnWidth(FROMTEMPLATE, 150)
        self.gridTemplates_Rel.setColumnWidth(TOTEMPLATE, 150)
        self.gridTemplates_Rel.setColumnWidth(RELPROPERTYPATTERN, 300)
        self.gridTemplates_Rel.setColumnWidth(RELCOUNT, 100)
        self.gridTemplates_Rel.setColumnWidth(RELKEY, 100)

        header = self.gridTemplates_Rel.horizontalHeader()
        header.setSectionResizeMode(GENERATE, QHeaderView.Fixed)
        header.setSectionResizeMode(RELTEMPLATENAME, QHeaderView.Interactive)
        header.setSectionResizeMode(RELATIONSHIPNAME, QHeaderView.Interactive)
        header.setSectionResizeMode(FROMTEMPLATE, QHeaderView.Interactive)
        header.setSectionResizeMode(TOTEMPLATE, QHeaderView.Interactive)
        header.setSectionResizeMode(RELPROPERTYPATTERN,
                                    QHeaderView.Interactive)
        header.setSectionResizeMode(RELCOUNT, QHeaderView.Fixed)
        header.setSectionResizeMode(RELKEY, QHeaderView.Fixed)

    def createRelResultsModel(self):
        #       GENERATE, TEMPLATENAME, RELATIONSHIPNAME, FROMTEMPLATE, TOTEMPLATE, PROPERTYPATTERN = range(6)
        model = QStandardItemModel(0, 8)
        model.setHeaderData(GENERATE, Qt.Horizontal, "")
        model.setHeaderData(RELTEMPLATENAME, Qt.Horizontal, "Template Name")
        model.setHeaderData(RELATIONSHIPNAME, Qt.Horizontal,
                            "Relationship Name")
        model.setHeaderData(FROMTEMPLATE, Qt.Horizontal, "From Node Template")
        model.setHeaderData(TOTEMPLATE, Qt.Horizontal, "To Node Template")
        model.setHeaderData(RELPROPERTYPATTERN, Qt.Horizontal,
                            "Property Pattern")
        model.setHeaderData(RELCOUNT, Qt.Horizontal, "# Scanned")
        model.setHeaderData(RELKEY, Qt.Horizontal, "Unique Key")
        model.dataChanged.connect(self.templateRelGridChanged)
        return model

    def createResultsModel(self):
        #        GENERATE, TEMPLATENAME, LABELPATTERN, PROPERTYPATTERN
        model = QStandardItemModel(0, 5)
        model.setHeaderData(GENERATE, Qt.Horizontal, "")
        model.setHeaderData(TEMPLATENAME, Qt.Horizontal, "Template Name")
        model.setHeaderData(LABELPATTERN, Qt.Horizontal, "Label Pattern")
        model.setHeaderData(PROPERTYPATTERN, Qt.Horizontal, "Property Pattern")
        model.setHeaderData(NODECOUNT, Qt.Horizontal, "# Scanned")
        model.dataChanged.connect(self.templateGridChanged)
        return model

    def addResultRow(self, model, c1, c2, c3, c4, c5):
        #        GENERATE, TEMPLATENAME, LABELPATTERN, PROPERTYPATTERN, COUNT
        item1 = QStandardItem(c1)
        item1.setEditable(True)
        item1.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        item2 = QStandardItem(c2)
        item3 = QStandardItem(c3)
        item3.setEditable(False)
        item4 = QStandardItem(c4)
        item4.setEditable(False)
        item5 = QStandardItem(c5)
        item5.setEditable(False)
        if c1 in [0, 1, 2]:
            item1.setCheckState(c1)
        else:
            item1.setCheckState(Qt.Unchecked)

        model.appendRow([item1, item2, item3, item4, item5])

    def addRelResultRow(self, model, c1, c2, c3, c4, c5, c6, c7, c8):
        #       GENERATE, RELTEMPLATENAME, RELATIONSHIPNAME, FROMTEMPLATE, TOTEMPLATE, PROPERTYPATTERN, COUNT
        item1 = QStandardItem(c1)
        item1.setEditable(True)
        item1.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        item2 = QStandardItem(c2)
        # save the original rel templatename in case the user renames it on the grid.
        item2.setData(c2, Qt.UserRole)
        item3 = QStandardItem(c3)
        item3.setEditable(False)
        item4 = QStandardItem(c4)
        item4.setEditable(False)
        item5 = QStandardItem(c5)
        item5.setEditable(False)
        item6 = QStandardItem(c6)
        item6.setEditable(False)
        item7 = QStandardItem(c7)
        item7.setEditable(False)
        item8 = QStandardItem(c8)
        item8.setEditable(False)
        if c1 in [0, 1, 2]:
            item1.setCheckState(c1)
        else:
            item1.setCheckState(Qt.Unchecked)

#        print(c1, c2, c3, c4, c5, c6, c7, c8)
        model.appendRow(
            [item1, item2, item3, item4, item5, item6, item7, item8])

    def newScan(self, ):
        self.clearResults()
        self.patternList = []  # keeps track of the unique label combinations
        #        self.patternListCount = []   # keeps track of the number of nodes matching the unique label combinations in patternList
        self.relPatternListCount = []
        self.nodeDict = {}  # dictionary to hold discovered node patterns
        self.relDict = {
        }  # dictionary to hold discovered relationship patterns

    def displayScanMsg(self, text):
        if self.echo:
            # display on UI
            self.editProgress.appendPlainText("{}: {}".format(
                str(datetime.now()), text))
            QApplication.processEvents()
        # add real logging here
#        logging.info(text)

    def logMessage(self, msg):
        if self.echo:
            # display on UI
            self.browserOutput.append(msg)
            QApplication.processEvents()
#        logging.info(msg)

    def templateGridChanged(self, index1, index2):
        '''
        The user can over write the automatically generted Node template name.
        This needs to be recorded in the nodeDict template dictionary so it
        can be properly generated.
        '''
        if index1.column() == TEMPLATENAME:
            #            print("row - {}, col - {}, data - {}".format(index1.row(), index1.column(), index1.data(role = Qt.DisplayRole)))
            #            print("before:{}".format(self.nodeDict["{0:0>5}".format(index1.row())]))
            beforeTemplateName = self.nodeDict["{0:0>5}".format(
                index1.row())]["templateLblName"]
            afterTemplateName = index1.data(role=Qt.DisplayRole)
            #update the node pattern dictionary with the new name
            try:
                self.nodeDict["{0:0>5}".format(
                    index1.row())]["templateLblName"] = afterTemplateName
                # update the rel dict
                for key, value in self.relDict.items():
                    if value["toTemplate"] == beforeTemplateName:
                        value["toTemplate"] = afterTemplateName
                    if value["fromTemplate"] == beforeTemplateName:
                        value["fromTemplate"] = afterTemplateName
                # update the rel template grid on the UI
                model = self.gridTemplates_Rel.model()
                numrows = model.rowCount()
                for row in range(0, numrows):
                    curRelTemplateName = model.item(row, RELTEMPLATENAME).data(
                        Qt.EditRole)
                    if model.item(row, FROMTEMPLATE).data(
                            Qt.EditRole) == beforeTemplateName:
                        model.setData(model.index(row, FROMTEMPLATE),
                                      afterTemplateName)
                        model.setData(
                            model.index(row, RELTEMPLATENAME),
                            curRelTemplateName.replace(beforeTemplateName,
                                                       afterTemplateName))
                    if model.item(row, TOTEMPLATE).data(
                            Qt.EditRole) == beforeTemplateName:
                        model.setData(model.index(row, TOTEMPLATE),
                                      afterTemplateName)
                        model.setData(
                            model.index(row, RELTEMPLATENAME),
                            curRelTemplateName.replace(beforeTemplateName,
                                                       afterTemplateName))

            except:
                pass
#                print("error")

            finally:
                pass
#                print("AFTER:{}".format(self.nodeDict["{0:0>5}".format(index1.row())]))

    def templateRelGridChanged(self, index1, index2):
        '''
        The user can over write the automatically generated Relationship template name.
        '''
        return

    @pyqtSlot()
    def on_btnStart_clicked(self):
        """
        Reverse Engineer The current Graph.
        Create Node and Relationship templates
        """
        # switch to the progress tab
        self.tabWidget.setCurrentIndex(0)

        # for now, must scan nodes
        if self.cbScanNodes.isChecked() == False:
            self.cbScanNodes.setCheckState(Qt.Checked)

        #setup for a new scan
        self.newScan()
        self.stopScan = False
        self.btnStop.setEnabled(True)
        self.btnStart.setEnabled(False)

        # do the scan
        self.scanAll()

        # reset the buttons
        self.btnStop.setEnabled(False)
        self.btnStart.setEnabled(True)

        # switch to the results tab
        self.tabWidget.setCurrentIndex(1)

    def getDataTypes(self,
                     nodeID=None,
                     relID=None,
                     propDataTypeDict=None,
                     propList=None):
        if not nodeID is None:
            returnPropList = self.genReturnPropList("n", propList)
            if len(returnPropList) > 0:
                cypher = "match (n) where id(n) = {} return {}".format(
                    str(nodeID), returnPropList)
            else:
                return
        if not relID is None:
            returnPropList = self.genReturnPropList("r", propList)
            if len(returnPropList) > 0:
                cypher = "match ()-[r]->() where id(r) = {} return {}".format(
                    str(relID), returnPropList)
            else:
                return

        #run the query
        rc1, msg1 = self.myNeoCon.runCypherAuto(cypher)
        if rc1:
            record = self.myNeoCon.resultSet[0]
            # get the datatype for each property
            # value has the correct python object for each cell in the result set
            for index, value in record.items():
                if not value is None:
                    dataType = self.neoTypeFunc.getNeo4jDataType(value)
                    propDataTypeDict[index] = dataType

    def genReturnPropList(self, nodeName, propList):
        'return all properties in the template'
        genPropList = ""
        genPropList = ",".join(nodeName + "." + x + " as " + x
                               for x in propList)
        return genPropList

    def scanAll(self, ):
        msg = "Scan finished"
        if self.cbScanNodes.isChecked() == True:
            # scan the nodes
            self.displayScanMsg("Start Scanning Nodes.")
            limitAmt = self.spinProcessSize.value()
            skipIncrement = self.spinSkipAmt.value()
            skipAmt = 0
            totAmt = 0
            self.moreData = True
            try:
                while (self.moreData and self.stopScan == False):
                    rc = False
                    cypher = "match (n) return id(n) as nodeID, labels(n), keys(n) skip {} limit {}".format(
                        str(skipAmt), str(limitAmt))
                    #run the query
                    rc1, msg1 = self.myNeoCon.runCypherAuto(cypher)
                    if rc1 == True:
                        x = self.processModelChunk()
                        if x > 0:
                            totAmt = totAmt + x
                            self.displayScanMsg(
                                "Skip to: {} Processed: {} Total Processed {} Nodes."
                                .format(str(skipAmt), str(limitAmt), totAmt))
                            skipAmt = skipAmt + limitAmt + skipIncrement
                        else:
                            self.moreData = False
                            rc = True
                            msg = "Scan Nodes complete"
                    else:
                        msg = "Scan Nodes Error {}".format(msg1)
                        self.moreData = False

            except BaseException as e:
                msg = "{} - Node Scan failed.".format(repr(e))
            finally:
                self.displayScanMsg(msg)
            # add scanned node templates to the grid
            if self.stopScan == False:
                self.genNodeResult()

        if self.cbScanRels.isChecked() == True:
            # scan the relationships
            self.displayScanMsg("Start Scanning Relationships.")
            limitAmt = self.spinProcessSize.value()
            skipIncrement = self.spinSkipAmt.value()
            skipAmt = 0
            totAmt = 0
            self.moreData = True
            try:
                while (self.moreData and self.stopScan == False):
                    rc = False
                    cypher = "match (f)-[r]->(t) return id(r), keys(r), type(r), labels(f), labels(t) skip {} limit {}".format(
                        str(skipAmt), str(limitAmt))
                    #run the query
                    rc1, msg1 = self.myNeoCon.runCypherAuto(cypher)
                    if rc1:
                        x = self.processRelModelChunk()
                        if x > 0:
                            totAmt = totAmt + x
                            self.displayScanMsg(
                                "Skip to: {} Processed: {} Total Processed {} Relationships."
                                .format(str(skipAmt), str(x), totAmt))
                            skipAmt = skipAmt + limitAmt + skipIncrement
                        else:
                            self.moreData = False
                            rc = True
                            msg = "Scan Relationships complete"
                    else:
                        msg = "Scan Relationships Error {}".format(msg1)
                        self.moreData = False

            except BaseException as e:
                msg = "{} - Relationships Scan failed.".format(repr(e))
            finally:
                self.displayScanMsg(msg)

            if self.stopScan == False:
                self.genRelResult()

            return rc, msg

    def processModelChunk(self, ):
        ctr = 0
        for record in self.myNeoCon.resultSet:
            ctr = ctr + 1
            labels = record["labels(n)"]
            props = record["keys(n)"]
            nodeID = record["nodeID"]
            try:
                x = self.patternList.index(labels)
                patternName = "{0:0>5}".format(x)
            except ValueError:
                nextx = len(self.patternList)
                self.patternList.insert(nextx, labels)
                patternName = "{0:0>5}".format(nextx)
                self.nodeDict[patternName] = {}
                self.nodeDict[patternName]["propList"] = []
                self.nodeDict[patternName]["labelList"] = labels
                self.nodeDict[patternName]["templateName"] = "Node{}".format(
                    patternName)
                self.nodeDict[patternName]["templateLblName"] = "{}".format(
                    "_".join(labels))
                self.nodeDict[patternName]["propDataType"] = {}
                self.nodeDict[patternName]["count"] = 0
            finally:
                count = self.nodeDict[patternName]["count"] + 1
                self.nodeDict[patternName]["count"] = count
                # get datatypes for newly discovered properties
                newProps = list(
                    set(props) -
                    set(self.nodeDict[patternName].get("propList", [])))
                if len(newProps) > 0:
                    self.getDataTypes(
                        nodeID=nodeID,
                        propDataTypeDict=self.nodeDict[patternName]
                        ["propDataType"],
                        propList=newProps)
                # merge in any newly discovered properties
                self.nodeDict[patternName]["propList"] = list(
                    set(self.nodeDict[patternName].get("propList", []) +
                        props))
        return ctr

    def processRelModelChunk(self, ):
        ctr = 0
        for record in self.myNeoCon.resultSet:
            ctr = ctr + 1
            relProps = record["keys(r)"]
            relType = record["type(r)"]
            fromLbls = record["labels(f)"]
            toLbls = record["labels(t)"]
            relID = record["id(r)"]
            # get from and to node templates
            fromTemplate = "Unknown"
            toTemplate = "Unknown"
            for nodepattern, nodedata in self.nodeDict.items():
                if nodedata["labelList"] == fromLbls:
                    fromTemplate = nodedata["templateLblName"]
                if nodedata["labelList"] == toLbls:
                    toTemplate = nodedata["templateLblName"]
            try:
                relKey = "{}:{}:{}".format(relType, fromTemplate, toTemplate)
                # check to see if the relKey entry in the dictionary already exists, if it doesn't you get a KeyError
                check = self.relDict[relKey]
            except KeyError:
                # count how many times the relType has been used in a rel template
                countReltypeUsed = self.countRelType(relType)
                # the relkey doesn't exist so add it to the dictionary
                self.relDict[relKey] = {}
                self.relDict[relKey]["propList"] = []
                self.relDict[relKey]["propDataType"] = {}
                if countReltypeUsed > 0:
                    self.relDict[relKey]["templateName"] = "{0}{1:0>3}".format(
                        relType, countReltypeUsed)
                else:
                    self.relDict[relKey]["templateName"] = relType
                self.relDict[relKey]["relName"] = relType
                self.relDict[relKey]["fromTemplate"] = fromTemplate
                self.relDict[relKey]["toTemplate"] = toTemplate
                self.relDict[relKey]["count"] = 0
            finally:
                count = self.relDict[relKey]["count"] + 1
                self.relDict[relKey]["count"] = count
                # get datatypes for newly discovered properties
                newProps = list(
                    set(relProps) -
                    set(self.relDict[relKey].get("propList", [])))
                if len(newProps) > 0:
                    self.getDataTypes(
                        relID=relID,
                        propDataTypeDict=self.relDict[relKey]["propDataType"],
                        propList=newProps)
                # merge in the properties
                self.relDict[relKey]["propList"] = list(
                    set(self.relDict[relKey].get("propList", []) + relProps))

        return ctr

    def countRelType(self, relType):
        cnt = 0
        for key in self.relDict.keys():
            if self.relDict[key]["relName"] == relType:
                cnt = cnt + 1
        return cnt

    def testScan(self):
        '''simulate the scan to get total nodes and percent nodes
        '''
        # simulate Node Count
        self.stopScan = False
        totNodes = int(self.editNumNodes.text())
        nodesRemaining = totNodes
        totRels = int(self.editNumRels.text())
        relsRemaining = totRels
        limitAmt = self.spinProcessSize.value()
        skipIncrement = self.spinSkipAmt.value()
        skipAmt = 0
        totAmt = 0
        self.moreData = True
        try:
            while (self.moreData and self.stopScan == False):
                if nodesRemaining > limitAmt:
                    x = limitAmt
                    nodesRemaining = nodesRemaining - limitAmt - skipIncrement
                else:
                    x = nodesRemaining
                    nodesRemaining = 0
                if x > 0:
                    totAmt = totAmt + x
                    #                    print("Skip to: {} Processed: {} Total Processed {} Nodes.".format(str(skipAmt), str(limitAmt),totAmt))
                    skipAmt = skipAmt + limitAmt + skipIncrement
                else:
                    self.moreData = False
            else:
                self.moreData = False
        except BaseException as e:
            self.helper.displayErrMsg(
                "Calcuate Percents",
                "{} - Error Calculating Percents.".format(repr(e)))
        finally:
            # update UI
            self.txtNodeScanAmt.setText(str(totAmt))
            displayPercent = "{0:.0%}".format(totAmt / totNodes)
            self.txtNodePercent.setText(displayPercent)

        # simulate Rel Count
        self.stopScan = False
        totRels = int(self.editNumRels.text())
        relsRemaining = totRels
        limitAmt = self.spinProcessSize.value()
        skipIncrement = self.spinSkipAmt.value()
        skipAmt = 0
        totAmt = 0
        self.moreData = True
        try:
            while (self.moreData and self.stopScan == False):
                if relsRemaining > limitAmt:
                    x = limitAmt
                    relsRemaining = relsRemaining - limitAmt - skipIncrement
                else:
                    x = relsRemaining
                    relsRemaining = 0
                if x > 0:
                    totAmt = totAmt + x
                    #                    print("Skip to: {} Processed: {} Total Processed {} Nodes.".format(str(skipAmt), str(limitAmt),totAmt))
                    skipAmt = skipAmt + limitAmt + skipIncrement
                else:
                    self.moreData = False
            else:
                self.moreData = False
        except BaseException as e:
            self.helper.displayErrMsg(
                "Calcuate Percents",
                "{} - Error Calculating Percents.".format(repr(e)))
        finally:
            # update UI
            self.txtRelScanAmt.setText(str(totAmt))
            if totRels == 0 or totRels is None:
                displayPercent = "{0:.0%}".format(0)
            else:
                displayPercent = "{0:.0%}".format(totAmt / totRels)
            self.txtRelPercent.setText(displayPercent)

    def genNodeResult(self, ):
        '''
        Scan the reverse engineered node dictionary and create Node Template rows in the grid
        GENERATE, TEMPLATENAME, LABELPATTERN, PROPERTYPATTERN        
        '''
        for key in sorted(self.nodeDict.keys()):
            value = self.nodeDict[key]
            self.addResultRow(self.gridTemplates.model(), Qt.Checked,
                              str(value["templateLblName"]),
                              str(value["labelList"]), str(value["propList"]),
                              str(value["count"]))
#            print("{}-{} {}".format(key, value["labelList"], value["propList"]))

    def genNodeTemplates(self, ):
        '''
        Scan the reverse engineered node dictionary and create Node Templates
        GENERATE, TEMPLATENAME, LABELPATTERN, PROPERTYPATTERN
        '''
        # generate node templates
        model = self.gridTemplates.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            genFlag = model.item(row, GENERATE).checkState()
            # is the pattern checked?
            if genFlag == Qt.Unchecked:
                continue
            if self.genNodeTemplate(model, row) == False:
                break
            # uncheck the template
            model.item(row, GENERATE).setCheckState(Qt.Unchecked)
            # refresh the treeview
            self.model.updateTV()

    def genRelTemplates(self, ):
        # generate rel templates
        model = self.gridTemplates_Rel.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            genFlag = model.item(row, GENERATE).checkState()
            # is the pattern checked?
            if genFlag == Qt.Unchecked:
                continue
            if self.genRelTemplate(model, row) == False:
                break
            # uncheck the template
            model.item(row, GENERATE).setCheckState(Qt.Unchecked)
            # refresh the treeview
            self.model.updateTV()

    def genNodeTemplate(self, model, row):

        templateName = model.item(row, TEMPLATENAME).data(Qt.EditRole)
        labelPattern = model.item(row, LABELPATTERN).data(Qt.EditRole)
        # find entry in nodeDict that matches the label pattern in the grid
        # you can't use the template name because the user may have overwritten it
        labelPatternList = None
        for key, value in self.nodeDict.items():
            if str(value["labelList"]) == labelPattern:
                labelPatternList = value["labelList"]
                propertyPatternList = value["propList"]
                propertyDataTypeDict = value.get("propDataType", {})
        # this should never happen
        if labelPatternList == None:
            self.helper.displayErrMsg(
                "Create New Node Template Error",
                "The label pattern {} not found".format(labelPattern))
            self.gridTemplates.setFocus()
            return False
        # make sure there is a template name
        if self.helper.NoTextValueError(
                templateName, "You must supply a name for the Node Template"):
            self.gridTemplates.setFocus()
            return False
        #make sure template name doesn't exist
        index, nodeDict = self.model.getDictByName("Node Template",
                                                   templateName)
        if not nodeDict is None:
            self.helper.displayErrMsg(
                "Create New Node Template Error",
                "The node template {} already exists".format(templateName))
            self.gridTemplates.setFocus()
            return False
#        print("{} {} {}".format(templateName, str(labelPatternList),str(propertyPatternList) ))
        labelList = []
        for label in labelPatternList:
            nodeLbl = [label, Qt.Checked, Qt.Unchecked]
            self.model.newLabel(label)
            labelList.append(nodeLbl)
        propList = []
        for property in propertyPatternList:
            dataType = propertyDataTypeDict.get(property, "Unknown")
            nodeProp = [
                property, dataType, Qt.Unchecked, "", Qt.Unchecked,
                Qt.Unchecked, Qt.Unchecked
            ]
            self.model.newProperty(property, dataType=dataType)
            propList.append(nodeProp)

        # generate the constraints and indexes
        conList = []
        # CONTYPE, CONLBL, CONPROP, CONPROPLIST
        # look at each constraint in the schemaModel and see if it belongs to the node template
        self.schemaModel.matchConstraintNodeTemplate(
            conList, [lbl[0] for lbl in labelList],
            [prop[0] for prop in propList])
        # look at each index in the schemaModel and see if it belongs to the node template
        idxList = []
        self.schemaModel.matchIndexNodeTemplate(idxList,
                                                [lbl[0] for lbl in labelList],
                                                [prop[0] for prop in propList])

        #save it to the model
        nodeTemplateDict = self.model.newNodeTemplate(
            name=templateName,
            labelList=labelList,
            propList=propList,
            conList=conList,
            idxList=idxList,
            desc="Template generated from Reverse Engineering.")

        self.model.modelData["Node Template"].append(nodeTemplateDict)

        return True

    def genRelTemplate(self, model, row):
        '''
        Create a relationship template based on the row in the results grid
        GENERATE, RELTEMPLATENAME, RELATIONSHIPNAME, FROMTEMPLATE, TOTEMPLATE, RELPROPERTYPATTERN = range(6)
        '''
        # templateName and relationship name
        templateName = model.item(row, RELTEMPLATENAME).data(Qt.EditRole)
        relName = model.item(row, RELATIONSHIPNAME).data(Qt.EditRole)
        fromTemplate = model.item(row, FROMTEMPLATE).data(Qt.EditRole)
        toTemplate = model.item(row, TOTEMPLATE).data(Qt.EditRole)
        uniqueKey = model.item(row, RELKEY).data(Qt.EditRole)

        #        # get property pattern from relDict since it is an actual list, not a string like what is stored in the grid
        propertyPatternList = self.relDict[uniqueKey]["propList"]
        propertyDataTypeDict = self.relDict[uniqueKey].get("propDataType", {})
        # make sure there is a relationship name
        if self.helper.NoTextValueError(
                relName, "You must supply a name for the Relationship"):
            self.gridTemplates_Rel.setFocus()
            return False
        # make sure there is a template name
        if self.helper.NoTextValueError(
                templateName,
                "You must supply a name for the Relationship Template"):
            self.gridTemplates_Rel.setFocus()
            return False
        #make sure template name doesn't exist
        index, nodeDict = self.model.getDictByName("Relationship Template",
                                                   templateName)
        if not nodeDict is None:
            self.helper.displayErrMsg(
                "Create New Relationship Template Error",
                "The Relationship template {} already exists".format(
                    templateName))
            self.gridTemplates.setFocus()
            return False

#        print("{} {}".format(templateName,str(propertyPatternList) ))
# if its a new rel type then add it to the model
        self.model.newRelationship(relName)
        # properties
        propList = []
        for property in propertyPatternList:
            dataType = propertyDataTypeDict.get(property, "Unknown")
            relProp = [property, dataType, Qt.Unchecked, "", Qt.Unchecked]
            self.model.newProperty(property, dataType=dataType)
            propList.append(relProp)
        # generate the constraints
        conList = []
        # look at each constraint in the schemaModel and see if it belongs to the rel template
        self.schemaModel.matchConstraintRelTemplate(
            conList, [prop[0] for prop in propList], relName=relName)

        relDict = self.model.newRelTemplateDict(
            name=templateName,
            relname=relName,
            propList=propList,
            fromTemplate=fromTemplate,
            toTemplate=toTemplate,
            conList=conList,
            desc="Template generated from Reverse Engineering.")
        self.model.modelData["Relationship Template"].append(relDict)
        return True

    def genRelResult(self, ):
        '''
        Scan the reverse engineered relationship dictionary and create Relationship Template rows in the grid
        '''
        for key in sorted(self.relDict.keys()):
            value = self.relDict[key]
            #            self.addRelResultRow(self.gridTemplates_Rel.model(), Qt.Checked, "{}".format(key), str(value["relName"]), str(value["fromTemplate"]), str(value["toTemplate"]), str(value["propList"]), str(value["count"]) )
            self.addRelResultRow(self.gridTemplates_Rel.model(), Qt.Checked,
                                 str(value["templateName"]),
                                 str(value["relName"]),
                                 str(value["fromTemplate"]),
                                 str(value["toTemplate"]),
                                 str(value["propList"]), str(value["count"]),
                                 key)

    @pyqtSlot(QAbstractButton)
    def on_buttonBox_clicked(self, button):
        """
        Slot documentation goes here.
        
        @param button DESCRIPTION
        @type QAbstractButton
        """
        QDialog.accept(self)

#    @pyqtSlot()
#    def on_rbAllNodes_clicked(self):
#        """
#        User selects to scan all nodes so set relationships to scan all
#        """
#        if self.rbAllNodes.isChecked():
#            self.rbAllRels.setChecked(True)
#        else:
#            self.rbAllRels.setChecked(False)

#    @pyqtSlot()
#    def on_rbPercentNodes_clicked(self):
#        """
#        User selects to scan a percentage of nodes so set relationships to scan percentage
#        """
#        if self.rbPercentNodes.isChecked():
#            self.rbPercentRels.setChecked(True)
#        else:
#            self.rbPercentRels.setChecked(False)
#
#    @pyqtSlot()
#    def on_rbAllRels_clicked(self):
#        """
#        User selects to scan all Rels, so set nodes to scan all
#        """
#        if self.rbAllRels.isChecked():
#            self.rbAllNodes.setChecked(True)
#        else:
#            self.rbAllNodes.setChecked(False)
#
#    @pyqtSlot()
#    def on_rbPercentRels_clicked(self):
#        """
#        User selects to scan a percentage of relationships
#        Set nodes to scan percentage
#        """
#        if self.rbPercentRels.isChecked():
#            self.rbPercentNodes.setChecked(True)
#        else:
#            self.rbPercentNodes.setChecked(False)

    @pyqtSlot()
    def on_btnSaveTemplates_clicked(self):
        """
        User requests to Generate the node templates
        """
        self.genNodeTemplates()
        return

    @pyqtSlot(bool)
    def on_cbScanRels_clicked(self, checked):
        """
        The scan relationships checkbox has been clicked
        Check to make sure the scan Nodes relationship has also been clicked.
        You can't scan relationships without having scanned node patterns.
        
        @param checked DESCRIPTION
        @type bool
        """
        if checked:
            self.cbScanNodes.setCheckState(Qt.Checked)

    @pyqtSlot()
    def on_pbUncheck_clicked(self):
        """
        Uncheck all the node templates
        """
        model = self.gridTemplates.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            model.item(row, GENERATE).setCheckState(Qt.Unchecked)

    @pyqtSlot()
    def on_pbCheck_clicked(self):
        """
        check all the node template checkboxes
        """
        model = self.gridTemplates.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            model.item(row, GENERATE).setCheckState(Qt.Checked)

    @pyqtSlot()
    def on_pbUncheck_Rel_clicked(self):
        """
        Uncheck all the relationship templates
        """
        model = self.gridTemplates_Rel.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            model.item(row, GENERATE).setCheckState(Qt.Unchecked)

    @pyqtSlot()
    def on_pbCheck_Rel_clicked(self):
        """
        check all the node template checkboxes
        """
        model = self.gridTemplates_Rel.model()
        numrows = model.rowCount()
        for row in range(0, numrows):
            model.item(row, GENERATE).setCheckState(Qt.Checked)

    @pyqtSlot()
    def on_btnSaveTemplates_Rel_clicked(self):
        """
        User requests to Generate the relationship templates
        """
        self.genRelTemplates()
        return

    @pyqtSlot(bool)
    def on_cbScanNodes_clicked(self, checked):
        """
        The scan nodes checkbox has been clicked
        If they unchecked it display a message and turn it back on.  You always have to scan Nodes
        """
        if not checked:
            self.helper.displayErrMsg("Reverse Engineer",
                                      "You always have to scan the Nodes")
            self.cbScanNodes.setCheckState(Qt.Checked)

    @pyqtSlot()
    def on_btnStop_clicked(self):
        """
        User  clicks the stop button to stop reverse engineering
        """
        self.stopScan = True
        self.displayScanMsg("User Canceled Scan")

    @pyqtSlot(int)
    def on_spinProcessSize_valueChanged(self, p0):
        """
        User changed the process size spin box
        
        @param p0 DESCRIPTION
        @type int
        """
        # update the percents
        self.testScan()

    @pyqtSlot(int)
    def on_spinSkipAmt_valueChanged(self, p0):
        """
        User changed the skip amount spin box
        
        @param p0 DESCRIPTION
        @type int
        """
        #update the percents
        self.testScan()
class ConstraintRelPropExistsDlg(QDialog, Ui_ConstraintRelPropExistsDlg):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(ConstraintRelPropExistsDlg, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.schemaModel = self.parent.schemaModel
        self.helper = Helper()
        self.initUi()

    def initUi(self, ):
        aList = sorted(self.schemaModel.instanceList("Property"))
        self.cbProperty.addItem("")
        for indexName in aList:
            self.cbProperty.addItem(indexName)

        aList = sorted(self.schemaModel.instanceList("Relationship"))
        self.cbRelationships.addItem("")
        for indexName in aList:
            self.cbRelationships.addItem(indexName)
        return

    def validate(self, ):

        # must enter or select a relationship
        if self.helper.NoTextValueError(
                self.cbRelationships.currentText(),
                "You must enter or select a Relationship"):
            return False
        # must add at least one property to the list
        if self.helper.NoTextValueError(self.cbProperty.currentText(),
                                        "You must enter or select a Property"):
            return False

        return True

    def apply(self, ):
        """
        Generate and run the create constraint statement
        """
        prop = self.cbProperty.currentText()
        relationship = self.cbRelationships.currentText()
        self.cypherCommand = None
        '''
        CREATE CONSTRAINT ON ()-[r:RelID]-() ASSERT exists(r.propname)       
        '''
        self.cypherCommand = "CREATE CONSTRAINT ON ()-[p:{}]-() ASSERT exists(p.{})".format(
            relationship, prop)

        if self.cypherCommand:
            QApplication.setOverrideCursor(Qt.WaitCursor)
            rc, msg = self.schemaModel.createConstraint(self.cypherCommand)
            QApplication.restoreOverrideCursor()
            self.helper.displayErrMsg("Create Constraint", msg)
        else:
            self.helper.displayErrMsg(
                "Create Constraint", "Error Generating Constraint Statement.")

    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Slot documentation goes here.
        """
        if self.validate():
            self.apply()
            QDialog.accept(self)

    @pyqtSlot()
    def on_buttonBox_rejected(self):
        """
        Slot documentation goes here.
        """
        QDialog.reject(self)

    @pyqtSlot()
    def on_pbAddToList_clicked(self):
        """
        Get the property name in the combo box and add it to the list
        """
        self.lstProperty.addItem(self.cbProperty.currentText())

    @pyqtSlot()
    def on_pbRemoveList_clicked(self):
        """
        Remove the selected property from the list
        """
        self.lstProperty.takeItem(self.lstProperty.currentRow())

    @pyqtSlot()
    def on_rbUnique_clicked(self):
        """
        Slot documentation goes here.
        """
        self.cbLabel.setEnabled(True)
        self.cbProperty.setEnabled(True)
        self.cbRelationships.setEnabled(False)
        self.pbAddToList.setEnabled(True)
        self.pbRemoveList.setEnabled(True)

    @pyqtSlot()
    def on_rbNodePropExists_clicked(self):
        """
        Slot documentation goes here.
        """
        self.cbLabel.setEnabled(True)
        self.cbProperty.setEnabled(True)
        self.cbRelationships.setEnabled(False)
        self.pbAddToList.setEnabled(True)
        self.pbRemoveList.setEnabled(True)

    @pyqtSlot()
    def on_rbNodeKey_clicked(self):
        """
        Slot documentation goes here.
        """
        self.cbLabel.setEnabled(True)
        self.cbProperty.setEnabled(True)
        self.cbRelationships.setEnabled(False)
        self.pbAddToList.setEnabled(True)
        self.pbRemoveList.setEnabled(True)

    @pyqtSlot()
    def on_rbRelPropExists_clicked(self):
        """
        Slot documentation goes here.
        """
        self.cbLabel.setEnabled(False)
        self.cbProperty.setEnabled(True)
        self.cbRelationships.setEnabled(True)
        self.pbAddToList.setEnabled(True)
        self.pbRemoveList.setEnabled(True)
예제 #15
0
class EditUserDlg(QDialog, Ui_EditUserDlg):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None, mode=None, userName=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(EditUserDlg, self).__init__(parent)
        self.parent = parent
        self.setupUi(self)
        self.userName = userName
        self.mode = mode
        self.helper = Helper()

        self.initUI()

    def initUI(self, ):
        self.txtUserName.setText(self.userName)
        self.txtUserName.setReadOnly(True)

        # get the user status and change password flag
        self.resetFlags()

        # populate the role combo box
        self.cbRoleName.addItems(self.parent.schemaModel.instanceList("Role"))
        # populate users roles in the list box
        userRoles = self.parent.schemaModel.getUserRoles(
            userName=self.userName)
        for userRole in userRoles:
            self.lstUserRoles.addItem(userRole)

    def resetFlags(self, ):
        # get the user status and change password flag
        self.userFlags = self.parent.schemaModel.getUserFlags(
            userName=self.userName)
        if "is_suspended" in self.userFlags:
            self.pbActivate.setChecked(False)
            self.pbSuspend.setChecked(True)
        else:
            self.pbSuspend.setChecked(False)
            self.pbActivate.setChecked(True)
        if "password_change_required" in self.userFlags:
            self.pbChangeOnLogin.setChecked(True)
        else:
            self.pbChangeOnLogin.setChecked(False)
        # the password change required flag can't be changed, the radio button just indicates how it is currently set
        self.pbChangeOnLogin.setEnabled(False)

    @pyqtSlot()
    def on_btnAddRole_clicked(self):
        """
        Give the user a role from the combobox of roles
        """
        addRole = self.cbRoleName.currentText()
        currentRoles = [
            str(self.lstUserRoles.item(i).text())
            for i in range(self.lstUserRoles.count())
        ]
        if addRole in currentRoles:
            # error, the user already has the selected role
            self.helper.displayErrMsg("Edit User",
                                      "The user already has this role.")
        else:
            # give the user the role
            QApplication.setOverrideCursor(Qt.WaitCursor)
            rc, msg = self.parent.schemaModel.addUserRole(
                userName=self.userName, role=addRole)
            if rc:
                self.lstUserRoles.addItem(addRole)
            else:
                self.helper.displayErrMsg("Add Role to User Error", msg)
            QApplication.restoreOverrideCursor()

    @pyqtSlot()
    def on_btnRemoveRoles_clicked(self):
        """
        Remove the selected role from the user
        """
        removeRole = self.lstUserRoles.currentItem().text()
        QApplication.setOverrideCursor(Qt.WaitCursor)
        rc, msg = self.parent.schemaModel.removeUserRole(
            userName=self.userName, role=removeRole)
        if rc:
            self.lstUserRoles.takeItem(self.lstUserRoles.currentRow())
        else:
            self.helper.displayErrMsg("Remove Role from User Error", msg)
        QApplication.restoreOverrideCursor()

    @pyqtSlot()
    def on_dlgBtnBox_accepted(self):
        """
        User clicks Close
        """
        QDialog.accept(self)

    @pyqtSlot()
    def on_dlgBtnBox_rejected(self):
        """
        User clicks something that generates the reject
        """
        QDialog.reject(self)

    @pyqtSlot()
    def on_pbActivate_clicked(self):
        """
        Activate a suspended user
        """
        if not "is_suspended" in self.userFlags:
            # nothing to do
            return

        QApplication.setOverrideCursor(Qt.WaitCursor)
        rc, msg = self.parent.schemaModel.activateUser(userName=self.userName)
        if rc:
            self.resetFlags()
        else:
            self.helper.displayErrMsg("Activate User Error", msg)
            self.resetFlags()

        QApplication.restoreOverrideCursor()

    @pyqtSlot()
    def on_pbSuspend_clicked(self):
        """
        Suspend an active user
        """
        if "is_suspended" in self.userFlags:
            # nothing to do
            return

        QApplication.setOverrideCursor(Qt.WaitCursor)
        rc, msg = self.parent.schemaModel.suspendUser(userName=self.userName)
        if rc:
            self.resetFlags()
        else:
            self.helper.displayErrMsg("Suspend User Error", msg)
            self.resetFlags()

        QApplication.restoreOverrideCursor()

    @pyqtSlot()
    def on_btnChangePassword_clicked(self):
        """
        User requests to change the password.
        """
        if not self.helper.NoTextValueError(self.txtNewPassword.text(),
                                            "You must supply a new password"):
            QApplication.setOverrideCursor(Qt.WaitCursor)
            rc, msg = self.parent.schemaModel.changePassword(
                userName=self.userName,
                pw=self.txtNewPassword.text(),
                forceChange=self.pbChangeOnLogin.isChecked())
            if rc:
                self.helper.displayErrMsg("Change Password",
                                          "The password has been changed")
            else:
                self.helper.displayErrMsg("Change Password Error", msg)
            self.resetFlags()
            QApplication.restoreOverrideCursor()