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
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
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()))
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)
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)
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)
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")
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)
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)
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
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))
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)
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)
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()