class PageItem(): def __init__(self, neoConName=None, actionButton=None, pageWidget=None, pageWidgetIndex=None): self.settings = QSettings() self.helper = Helper() # the name of the neocon for this page self.neoConName = neoConName # see if we need to prompt for the password self.promptPW = None self.checkPW() # the qaction on the menubar self.actionButton = actionButton self.pageWidget = pageWidget self.pageWidgetIndex = pageWidgetIndex return def checkPW(self, ): '''If the connection is prompt for password, then prompt until the user enters something. ''' # get the neocon dictionary neoDict = self.settings.value("NeoCon/connection/{}".format( self.neoConName)) if not neoDict is None: if neoDict["prompt"] == "True": # prompt for a password if needed and save what the user enters pw = '' while len(pw) < 1: pw = self.helper.passwordPrompt(conName=self.neoConName, conURL=neoDict["URL"]) if not pw is None: if len(pw) > 0: # save the encrypted password in the page item so it's ready to be used by any function self.promptPW = self.helper.putText(pw) else: self.helper.displayErrMsg( "Prompt Password", "You must enter a password.")
class NeoDriver(): def __init__(self, name=None, promptPW=None): self.name = name self.settings = QSettings() self.neoDict = None self.helper = Helper() # setup the dictionary that defines this neo4j connection self.getSavedConnection() # if we prompted the user for a password, save it in the neocon dictionary if not promptPW is None: self.neoDict["password"] = promptPW # driver used by this NeoDriver object self.myDriver = None # session used for unmanaged transaction self.session = None # transaction used for unmanaged transaction self.tx = None # result object used while consuming query results and saving them to self.resultSet self.result = None # persistent result set and summary self.resultSet = None self.resultSummary = None # manage chunking your way thru a result set self.cursorChunk = None self.chunkStart = 0 # this is the record number of the first record in the chunk self.chunkEnd = 0 # this is the record number of the last record in the chunk self.chunkSize = 10 # some day we'll make this a parameter setting self.curRecord = 0 # this is the last record retrieved self.endReached = False # track query statistics self.stats = None # default to true self.autoCommit = True # dictionary that gathers up facts about an individual cypher execution. self.cypherLogDict = {} def logMsg(self, msg): if logging: logging.info(msg) def logScript(self, script): # if logging: # logging.info(script) # print(script) return def logDriverStatus(self, ): return # if not self.myDriver is None: # driverStat = "Open" # else: # driverStat = "None" # if not self.session is None: # sessionStat = "Open" # else: # sessionStat = "None" # if not self.tx is None: # if self.tx.closed(): # txStat = "Closed" # else: # txStat = "Open" # else: # txStat = "None" # # if not self.result is None: # if self.result.peek() == None: # resultStat = "No Data Left" # else: # resultStat = "Data Remains" # else: # resultStat = "None" # # driverStatus = "Driver: {} Session: {} Txn: {} Result: {}".format(driverStat, sessionStat, txStat, resultStat) # print(driverStatus) def setAutoCommit(self, setting): self.autoCommit = setting def getSavedConnection(self, ): ''' get the dictionary from a saved connection in settings and use it to initialize this neoDriver if you don't find it, then create a new blank dictionary' ''' if not (self.name is None): self.neoDict = self.settings.value("NeoCon/connection/{}".format(self.name)) if self.neoDict is None: self.neoDict = self.newNeoDriverDict() else: self.name = self.neoDict["slot"] else: self.neoDict = self.newNeoDriverDict() def newNeoDriverDict(self, ): # returns a default empty dictionary of neodriver properties neoDict = {} if self.name is None: neoDict["slot"] = "Unnamed" else: neoDict["slot"] = self.name neoDict["URL"] = "never connected" neoDict["port"] = "" neoDict["conType"] = "" neoDict["host"] = "" neoDict["userid"] = "" neoDict["password"] = "" neoDict["usesecure"] = "False" neoDict["prompt"] = "False" return neoDict def localNeoConDict(self, ): # returns a neoCon dictionary ready to access a localhost system using bolt neoDict = {} neoDict["slot"] = "LOCAL" neoDict["URL"] = "never connected" neoDict["port"] = "7687" neoDict["conType"] = "bolt" neoDict["host"] = "localhost" neoDict["userid"] = "neo4j" pw = self.helper.putText("neo4j") neoDict["password"] = pw neoDict["usesecure"] = "False" neoDict["prompt"] = "False" return neoDict def displayNode(self, node): '''return the string representation of the node or a blank string ''' strNode = "" if not node is None: strNode = str(node) return strNode def displayRelationship(self, relationship): '''return the string representation of the relationship or a blank string ''' strRel = "" if not relationship is None: strRel = str(relationship) return strRel def runCypherAuto(self, cypherText, cypherParms=None): ''' run a cypher in an automatic transaction. save the entire result. ''' # first create the driver object if we haven't done that yet if self.myDriver is not None: pass else: rc, msg = self.setDriver() if rc == False: return False, msg # now run the query and save the result set errSuffix = "Run Cypher Auto Error" try: rc = False self.cypherLogDict = {} self.cypherLogDict["error"] = "" self.cypherLogDict["startTime"] = datetime.datetime.now() self.cypherLogDict["offset"] = -1 self.cypherLogDict["cypher"] = cypherText # create a session self.logScript('aSession = aDriver.session()') self.session = self.myDriver.session() self.logDriverStatus() # run an autocommit transaction self.logScript('aResult = aSession.run({})'.format(cypherText)) self.result = self.session.run(cypherText) self.logDriverStatus() # save the result as a list of records self.logScript('self.resultSet = []') self.logScript('for rec in self.result:') self.logScript(' self.resultSet.append(rec)') self.resultSet = [] for rec in self.result: self.resultSet.append(rec) # self.logScript('aResultSet = aResult.data()') # self.resultSet = self.result.data() self.logDriverStatus() # this gets the query stats self.logScript('aResultSummary = aResult.consume()') self.resultSummary = self.result.consume() self.logDriverStatus() rc = True msg = "Query Completed" except Neo4jError as e: msg = "Neo4j Error :{} - {}".format(repr(e), errSuffix) except DriverError as e: msg = "Driver Error :{} - {}".format(repr(e), errSuffix) except BaseException as e: msg = "Base Exception :{} - {}".format(repr(e), errSuffix) finally: self.cypherLogDict["endTime"] = datetime.datetime.now() # save query results if present if not self.resultSummary is None: if not self.resultSummary.plan is None: self.cypherLogDict["plan"] = self.resultSummary.plan else: self.cypherLogDict["plan"] = {} if not self.resultSummary.profile is None: self.cypherLogDict["profile"] = self.resultSummary.profile else: self.cypherLogDict["profile"] = {} if not self.resultSummary.counters is None: self.stats = self.resultSummary.counters else: self.stats = None if rc == False: # save error message for log grid self.cypherLogDict["error"] = msg # see if there is a syntax error if msg.find("CypherSyntaxError") > -1: if msg.find("(offset: ") > -1: self.cypherLogDict["offset"] = int(msg[msg.find("(offset: ")+9 : msg.find(")", msg.find("(offset: ")) ]) if not self.session is None: self.logScript('aSession.close()') self.session.close() self.logDriverStatus() return rc, msg def runCypherExplicit(self, cypherText, parmData=None): ''' run a cypher query in an explicit txn consume the entire result set leave the transaction open for possible further queries ''' # first create the driver object if we haven't done that yet if self.myDriver is not None: pass else: rc, msg = self.setDriver() if rc == False: return False, msg try: rc = False errSuffix = "Run Cypher Explicit Error" # create a session if we don't have one if self.session is None: self.logScript('aSession = aDriver.session()') self.session = self.myDriver.session() self.logDriverStatus() # get a new transaction if needed self.getNewTransaction() # now run the query self.chunkStart = 0 self.chunkEnd = 0 self.endReached = False self.cypherLogDict = {} self.cypherLogDict["error"] = "" self.cypherLogDict["offset"] = 0 self.cypherLogDict["cypher"] = cypherText self.cypherLogDict["startTime"] = datetime.datetime.now() if parmData is None: self.logScript('aResult = aTx.run("{}")'.format(cypherText)) self.result = self.tx.run(cypherText) self.logDriverStatus() else: self.logScript('aResult = aTx.run("{}",parameters="{}"'.format(cypherText, parmData)) self.result = self.tx.run(cypherText, parameters=parmData) self.logDriverStatus() # this consumes the entire result and saves it # self.logScript('aResultSet = aResult.data()') self.logScript('self.resultSet = []') self.logScript('for rec in self.result:') self.logScript(' self.resultSet.append(rec)') self.resultSet = [] for rec in self.result: self.resultSet.append(rec) self.logDriverStatus() # this gets the query stats self.logScript('aResultSummary = aResult.consume()') self.resultSummary = self.result.consume() self.logDriverStatus() # if autocommit is true then force a commit of this txn if self.autoCommit == True: self.commitTxn() rc = True msg = "Cursor Created" except Neo4jError as e: msg = "Neo4j Error :{} - {}".format(repr(e), errSuffix) except DriverError as e: msg = "Driver Error :{} - {}".format(repr(e), errSuffix) except BaseException as e: msg = "Base Exception :{} - {}".format(repr(e), errSuffix) finally: self.cypherLogDict["endTime"] = datetime.datetime.now() # save query summary if present if not self.resultSummary is None: if not self.resultSummary.plan is None: self.cypherLogDict["plan"] = self.resultSummary.plan else: self.cypherLogDict["plan"] = {} if not self.resultSummary.profile is None: self.cypherLogDict["profile"] = self.resultSummary.profile else: self.cypherLogDict["profile"] = {} if not self.resultSummary.counters is None: self.stats = self.resultSummary.counters else: self.stats = None if rc == False: # save error message for log grid self.cypherLogDict["error"] = msg # see if there is a syntax error if msg.find("SyntaxError") > -1: if msg.find("(offset: ") > -1: self.cypherLogDict["offset"] = int(msg[msg.find("(offset: ")+9 : msg.find(")", msg.find("(offset: ")) ]) # if we had an error then close the session if not self.session is None: # if there's a transaction, then close it if not self.tx is None: self.closeTxn() self.logDriverStatus() self.logScript('aSession.close()') self.session.close() self.logDriverStatus() return rc, msg def forwardCursor(self, ): ctr = 0 self.cursorChunk = [] self.endReached = False try: errSuffix = "Error fetching rows" # make sure we have a result if not self.resultSet is None: # make sure there's at least one record if len(self.resultSet) > 0: if len(self.resultSet) > self.chunkStart+self.chunkSize: self.chunkEnd = self.chunkStart+self.chunkSize else: self.chunkEnd = len(self.resultSet) self.endReached = True # get the next chunk of records for record in self.resultSet[self.chunkStart:self.chunkEnd]: # append the next record to the chunk self.cursorChunk.append(record) ctr = ctr + 1 self.chunkStart = self.chunkEnd else: self.endReached = True msg = "Fetch complete" except BaseException as e: msg = "Base Exception :{} - {}".format(repr(e), errSuffix) finally: # print('resultLen:{} chunkStart:{} chunkEnd:{}'.format(str(len(self.resultSet)), self.chunkStart, self.chunkEnd)) return ctr, msg def getNewTransaction(self, ): # create a new transaction if needed if self.tx is None: self.logScript('aTx = aSession.begin_transaction()') self.tx = self.session.begin_transaction() self.logDriverStatus() self.logMsg("Start first txn - AutoCommit = {}".format(self.autoCommit)) else: if self.tx.closed() is True: self.logScript('aTx = aSession.begin_transaction()') self.tx = self.session.begin_transaction() self.logDriverStatus() self.logMsg("Start another txn - AutoCommit = {}".format(self.autoCommit)) else: self.logMsg("Using the same transaction - AutoCommit = {}".format(self.autoCommit)) def commitTxn(self, ): ''' if we are in a txn then commit it. ''' try: rc = False errSuffix = "Commit Error" if not self.tx is None: if not self.tx.closed(): self.logScript('aTx.commit()') self.tx.commit() self.logDriverStatus() rc = True msg = "Transaction Committed" else: rc = True msg = "no active txn to commit" else: rc = True msg = "no txn exists to commit" except Neo4jError as e: msg = "Neo4j Error :{} - {}".format(repr(e), errSuffix) except DriverError as e: msg = "Driver Error :{} - {}".format(repr(e), errSuffix) except BaseException as e: msg = "Base Exception :{} - {}".format(repr(e), errSuffix) finally: self.logMsg(msg) return rc, msg def closeTxn(self, ): ''' if we are in a txn then close it. uncommitted txn's will be rolled back ''' try: rc = False errSuffix = "Txn Close Error" if not self.tx is None: if not self.tx.closed(): self.logScript('aTx.close() # implied rollback') self.tx.close() self.logDriverStatus() rc = True msg = "Transaction Closed" else: rc = True msg = "no active txn to close" else: rc = True msg = "no txn exists to close" except Neo4jError as e: msg = "Neo4j Error :{} - {}".format(repr(e), errSuffix) except DriverError as e: msg = "Driver Error :{} - {}".format(repr(e), errSuffix) except BaseException as e: msg = "Base Exception :{} - {}".format(repr(e), errSuffix) finally: self.logMsg(msg) return rc, msg def rollbackTxn(self, ): ''' if we are in a txn then do a rollback. ''' try: rc = False errSuffix = "RollBack Error" if not self.tx is None: if not self.tx.closed(): self.logScript('aTx.rollback()') self.tx.rollback() self.logDriverStatus() rc = True msg = "Transaction Rolled Back" else: rc = True msg = "no active txn to roll back" else: rc = True msg = "no txn exists to roll back" except Neo4jError as e: msg = "Neo4j Error :{} - {}".format(repr(e), errSuffix) except DriverError as e: msg = "Driver Error :{} - {}".format(repr(e), errSuffix) except BaseException as e: msg = "Base Exception :{} - {}".format(repr(e), errSuffix) finally: self.logMsg(msg) return rc, msg def setDriver(self): # create a driver object to run transactions try: if self.myDriver is None: rc = False errSuffix = "Driver Object Error" # get the keyword parameters from the necon dictionary uri = "bolt://{}:{}".format(self.neoDict["host"], self.neoDict["port"]) # create a base driver graphdatabase object, this verifies connectivity and authentication and will produce usable error messages if anything is wrong. self.myDriver = None pw = self.helper.getText(self.neoDict['password']) self.logScript('aDriver = GraphDatabase.driver({},auth=({},{})'.format(uri, self.neoDict['userid'], pw)) self.myDriver = GraphDatabase.driver(uri,auth=(self.neoDict['userid'], pw)) self.logDriverStatus() rc = True msg = "Connection Created" # save the URI self.neoDict["URL"] = uri else: rc = True msg = "Connection Exists" except Neo4jError as e: msg = "Neo4j Error :{} - {}".format(repr(e), errSuffix) except DriverError as e: msg = "Driver Error :{} - {}".format(repr(e), errSuffix) except BaseException as e: msg = "Base Exception :{} - {}".format(repr(e), errSuffix) finally: if rc == False: self.myDriver = None return rc, msg def test(self): # test the connection to see if it is working # this will create a new driver object if none has been created or it will use the existing driver try: rc = False errSuffix = "Test Neo4j Connection: {}".format(self.name) rc, msg = self.runCypherAuto("match (n) return n limit 1") if rc == True: msg = "Connection Successful: {}".format(self.name) except Neo4jError as e: msg = "Neo4j Error :{} - {}".format(repr(e), errSuffix) except DriverError as e: msg = "Driver Error :{} - {}".format(repr(e), errSuffix) except BaseException as e: msg = "Base Exception :{} - {}".format(repr(e), errSuffix) finally: return rc, msg
class NodeeraMain(QMainWindow, Ui_NodeeraMain): displayWelcomeMsg = pyqtSignal(str) closeWelcomeMsg = pyqtSignal() """ Create Ui_NodeeraMain and display it. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(NodeeraMain, self).__init__(parent) self.setupUi(self) self.parent = parent # object data self.pageDict = {} self.curPage = None self.helper = Helper() # get startup settings self.initSettings() # launch the welcome wagon self.welcomeDlg = HelloUserDlg(self) self.welcomeDlg.show() QApplication.processEvents() self.displayWelcomeMsg.emit("Starting NodeEra...") # setup stuff not covered by generated code icon = QIcon() icon.addPixmap(QPixmap("icons/RUN and DATA TAB/Disconnected.png"), QIcon.Normal, QIcon.Off) self.actionOpen_Connection.setIcon(icon) # main window self.setTitle("No Connection") self.resize(self.winSize) self.move(self.position) # display welcome message self.displayWelcomeMsg.emit("Welcome To NodeEra") # display announcement message from website try: url = 'https://{}'.format(appPage) response = requests.get(url) if response.status_code == 200: start = response.text.find('class="display"') + 16 end = response.text.find('</p>', start) self.displayWelcomeMsg.emit(response.text[start:end]) self.displayWelcomeMsg.emit( "For more information see www.singerlinks.com/nodeera") else: self.logMsg( "Unable to access announcement page:{} http response:{}". format(appPage, response.status_code)) self.displayWelcomeMsg.emit( "Unable to access announcement page:{} http response:{}". format(appPage, response.status_code)) except Exception as e: self.logMsg("Error accessing announcement page - {}".format( repr(e))) self.displayWelcomeMsg.emit( "Error accessing announcement page - {}".format(repr(e))) # auto open last used connection try: lastNeoConName = self.settings.value("NeoCon/LastUsed") if not lastNeoConName is None: neoDict = self.settings.value( "NeoCon/connection/{}".format(lastNeoConName)) self.openConnection(neoConName=lastNeoConName, neoConDict=neoDict) self.displayWelcomeMsg.emit(" ") self.displayWelcomeMsg.emit( "Opened most recent connection: {}".format(lastNeoConName)) self.setTitle(lastNeoConName) except: self.logMsg( "Unable to open last connection: {}".format(lastNeoConName)) self.displayWelcomeMsg.emit( "Unable to open last connection: {}".format(lastNeoConName)) # auto close the welcome dialog box # time.sleep(5) # self.closeWelcomeMsg.emit() def logMsg(self, msg): if logging: logging.info(msg) def initSettings(self, ): ''' get the system settings needed to start NodeEra. If a system setting doesn't exist then create it with default value - this happens on initial startup ''' self.settings = QSettings() try: self.expirationDate = self.helper.getText( self.settings.value("License/expirationDate")) if self.expirationDate is None: self.expirationDate = 'No expiration date set' self.settings.setValue( "License/expirationDate", self.helper.putText(self.expirationDate)) except: self.expirationDate = 'No expiration date set' self.settings.setValue("License/expirationDate", self.helper.putText(self.expirationDate)) try: self.currentVersion = self.settings.value("License/currentVersion") if self.currentVersion is None: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) elif self.currentVersion != currentVersion: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) except: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) try: self.winSize = self.settings.value("MainWindow/Size") if self.winSize is None: self.winSize = QSize(800, 500) except: self.winSize = QSize(800, 500) try: self.position = self.settings.value("MainWindow/Position") if self.position is None: self.position = QPoint(0, 0) except: self.position = QPoint(0, 0) try: self.recentList = self.settings.value("Default/RecentList") if self.recentList is None: self.recentList = [] self.settings.setValue("Default/RecentList", self.recentList) except: self.recentList = [] self.settings.setValue("Default/RecentList", self.recentList) try: self.defaultLoggingPath = self.settings.value( "Default/LoggingPath") if self.defaultLoggingPath is None: self.logDir = os.getcwd() self.logDir = os.path.realpath(os.path.abspath(self.logDir)) self.settings.setValue("Default/LoggingPath", self.logDir) except: self.logDir = os.getcwd() self.logDir = os.path.realpath(os.path.abspath(self.logDir)) self.settings.setValue("Default/LoggingPath", self.logDir) try: self.defaultProjPath = self.settings.value("Default/ProjPath") if self.defaultProjPath is None: self.defaultProjPath = os.getcwd() self.defaultProjPath = os.path.realpath( os.path.abspath(self.defaultProjPath)) self.settings.setValue("Default/ProjectPath", self.defaultProjPath) except: self.defaultProjPath = os.getcwd() self.defaultProjPath = os.path.realpath( os.path.abspath(self.defaultProjPath)) self.settings.setValue("Default/ProjectPath", self.defaultProjPath) # default custom formats for diagram objects # Display Format - Instance Node try: test = self.settings.value("Default/Format/InstanceNode") if test is None: self.settings.setValue("Default/Format/InstanceNode", INodeFormat().formatDict) except: self.settings.setValue("Default/Format/InstanceNode", INodeFormat().formatDict) # Display Format - Instance Relationship try: test = self.settings.value("Default/Format/InstanceRelation") if test is None: self.settings.setValue("Default/Format/InstanceRelation", IRelFormat().formatDict) except: self.settings.setValue("Default/Format/InstanceRelation", IRelFormat().formatDict) # Display Format - Template Node try: test = self.settings.value("Default/Format/TemplateNode") if test is None: self.settings.setValue("Default/Format/TemplateNode", TNodeFormat().formatDict) except: self.settings.setValue("Default/Format/TemplateNode", TNodeFormat().formatDict) # Display Format - Template Relationship try: test = self.settings.value("Default/Format/TemplateRelation") if test is None: self.settings.setValue("Default/Format/TemplateRelation", TRelFormat().formatDict) except: self.settings.setValue("Default/Format/TemplateRelation", TRelFormat().formatDict) # page setup try: test = self.settings.value("Default/PageSetup") if test is None: self.settings.setValue("Default/PageSetup", PageSetup().objectDict) except: self.settings.setValue("Default/PageSetup", PageSetup().objectDict) # default project neocon try: defaultNeoConName = self.settings.value("NeoCon/Default") if defaultNeoConName is None: self.settings.setValue("NeoCon/Default", "LOCAL") except: self.settings.setValue("NeoCon/Default", "LOCAL") # LOCAL neocon definition try: self.localNeoCon = self.settings.value("NeoCon/connection/LOCAL") if self.localNeoCon is None: self.settings.setValue("NeoCon/connection/LOCAL", NeoDriver().localNeoConDict()) except: self.settings.setValue("NeoCon/connection/LOCAL", NeoDriver().localNeoConDict()) # default lexer font size try: defaultLexerFontSize = self.settings.value("Lexer/FontSize") if defaultLexerFontSize is None: self.settings.setValue("Lexer/FontSize", "10") except: self.settings.setValue("Lexer/FontSize", "10") # validate all neocons have the prompt dictionary key which was added in 1.04 self.settings.beginGroup("NeoCon/connection") neoKeys = self.settings.childKeys() for key in neoKeys: neoDict = self.settings.value(key) promptVal = neoDict.get("prompt", None) if promptVal is None: neoDict["prompt"] = "False" self.settings.setValue(key, neoDict) self.settings.endGroup() def setTitle(self, filename): self.setWindowTitle("{} - {}".format(productName, filename)) def setMenuAccess(self, ): ''' determine what connection tab is currently selected and enable/disable menu items as needed ''' # turn on all Settings menu items for action in self.menuSettings.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(True) try: # get tab widget (schema or project) that is currently active - if there isn't one this will cause an exception currentTab = self.stackedPageItems.currentWidget( ).tabPage.currentWidget() # enable all the schema actions for action in self.menuNeo.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(True) for action in self.menuProject.actions(): # enable project actions if type(action) == QAction and not action.isSeparator(): if currentTab.pageType == "PROJECT": action.setEnabled(True) else: if action.text() in [ "New", "Open...", "Recent Projects" ]: action.setEnabled(True) else: action.setEnabled(False) except: # no connection tabs are open # disable all project menu actions for action in self.menuProject.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(False) for action in self.menuNeo.actions(): if type(action) == QAction and not action.isSeparator(): if action.text() in [ "Close Connection", "Generate Schema..." ]: action.setEnabled(False) else: action.setEnabled(True) ######################################################################## # NEO CONNECTION Dropdown Menu Actions ######################################################################## @pyqtSlot() def on_actionOpen_Connection_triggered(self): """ This slot provides functionality for the open connection button """ d = dlgNeoCons(parent=self) if d.exec_(): if d.selectedNeoConName: # make sure it isn't already opened if d.selectedNeoConName not in self.pageDict: self.openConnection(neoConName=d.selectedNeoConName, neoConDict=d.selectedNeoConDict) else: self.helper.displayErrMsg( "NodeEra - Open Connection", "Connection {} is already open.".format( d.selectedNeoConName)) # switch to the page they tried to open self.pageDict[d.selectedNeoConName].actionButton.trigger() def openConnection(self, neoConName=None, neoConDict=None): ''' User selects a connection to open so create the schema page and display it ''' # set the last used neocon in system settings self.settings.setValue("NeoCon/LastUsed", neoConName) # create a new toolbar button and add it to the toolbar newConAction = QAction(self) newConAction.setObjectName("newConnection") newConAction.setText("Neo4j - {}".format(neoConName)) newConAction.setData(neoConName) newConAction.setToolTip(neoConDict["URL"]) newConAction.setCheckable(True) newConAction.setChecked(True) newConAction.triggered.connect(self.connectionClicked) self.tbConnection.addAction(newConAction) # add a tabPage widget to the stacked widget newPageWidget = PageWidget(parent=self) newPageWidget.pageType = "Schema" widgetIndex = self.stackedPageItems.addWidget(newPageWidget) # save new pageItem newPageItem = PageItem(neoConName=neoConName, actionButton=newConAction, pageWidget=newPageWidget, pageWidgetIndex=widgetIndex) self.pageDict[neoConName] = newPageItem # add the schema tab cypherTab = CypherPageWidget(parent=self, pageItem=newPageItem) newPageWidget.tabPage.addTab(cypherTab, "Schema - {}".format(neoConName)) # click the new action to force selection logic newConAction.trigger() self.logMsg("Open Connection: {}".format(neoConName)) @pyqtSlot() def connectionClicked(self): ''' User clicks on a connection in the menu bar so switch to that stacked widget ''' self.logMsg("connection clicked {}".format(self.sender().text())) # uncheck all the page action buttons for pageName in self.pageDict: self.pageDict[pageName].actionButton.setChecked(False) # check the one just clicked self.sender().setChecked(True) # save the current page name self.curPage = self.sender().data() # switch the stacked page widget to the one just clicked self.stackedPageItems.setCurrentIndex( self.pageDict[self.curPage].pageWidgetIndex) # update the main window title self.setTitle(self.curPage) # adjust the menu items self.setMenuAccess() @pyqtSlot() def on_actionClose_Connection_triggered(self): """ Close the active connection and remove the page from the UI """ if self.curPage: if self.curPage in self.pageDict: # must find the schema tab and tell it to close, it will tell all other tabs to close. schema tab is always the first one (index=0) self.pageDict[self.curPage].pageWidget.closeSchemaTab() self.removeConnection() def removeConnection(self, ): '''if the tab page widget is responding to the close request it only needs this logic to remove the connection ''' curPage = self.pageDict.get(self.curPage, None) if not curPage is None: # remove the pageWidget from the stacked widget self.stackedPageItems.removeWidget( self.pageDict[self.curPage].pageWidget) del self.pageDict[self.curPage].pageWidget # remove the action from menu's and toolbars self.tbConnection.removeAction( self.pageDict[self.curPage].actionButton) # take the page out of the dictionary del self.pageDict[self.curPage] # if any pages left select the first one if len(self.pageDict) > 0: for pageName in self.pageDict: self.pageDict[pageName].actionButton.trigger() break else: # there are no open connections and the home page is closed self.setTitle("No Connection") @pyqtSlot() def on_actionNeo4j_Connection_Manager_triggered(self): """ Display the Connection Manager """ d = dlgNeoCons(parent=self) if d.exec_(): pass @pyqtSlot() def on_actionExit_triggered(self): """ User selected the File / Exit menu item. Tell all the open connections to close """ self.closeOpenStuff() # close the app self.close() def closeEvent(self, event): # close open connections self.closeOpenStuff() #save the window state self.settings.setValue("MainWindow/Size", self.size()) self.settings.setValue("MainWindow/Position", self.pos()) event.accept() def closeOpenStuff(self): # get a list of all the keys in the pageDict dictionary keys = list(self.pageDict.keys()) # iterate thru the list of dictionary keys. this is required as the dictionary will be changing size, i.e. you can't simply iterate thru the dictionary for key in keys: pageItem = self.pageDict[key] actionButton = pageItem.actionButton actionButton.trigger() # must find the schema tab and tell it to close, it will tell all other tabs to close. schema tab is always the first one (index=0) pageItem.pageWidget.closeSchemaTab() self.removeConnection() ##################################################################### # SETTINGS DROPDOWNS ##################################################################### @pyqtSlot() def on_actionSystem_Preferences_triggered(self): """ User selects the System Preferences menu item. Display the system preferences dialog box. """ self.editSystemPreferences() def editSystemPreferences(self, ): """ User selects the System Preferences menu item. Display the system preferences dialog box. """ if not (self.settings is None): d = SystemPreferenceBox(self, settings=self.settings) if d.exec_(): self.settings.sync() ##################################################################### # PROJECT METHODS ##################################################################### @pyqtSlot() def on_actionNewProject_triggered(self): """ Open new project """ self.loadProject(fileName=None) @pyqtSlot() def on_actionOpenProject_triggered(self): """ Open an existing project file """ dlg = QFileDialog() dlg.setFileMode(QFileDialog.ExistingFile) dlg.setAcceptMode(QFileDialog.AcceptOpen) dlg.setNameFilters(["NodeEra Project (*.mdl)", "all files (*.*)"]) dlg.setDirectory(self.settings.value("Default/ProjPath")) if dlg.exec_(): fileNames = dlg.selectedFiles() if fileNames: fileName = fileNames[0] self.loadProject(fileName=fileName) @pyqtSlot() def on_actionSaveProject_triggered(self): """ Save the open project """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": newName = curPageWidget.tabPage.currentWidget().saveProject() # if this was an unsaved project then it was really a save as so the tab name has to be updated if newName is not None: curPageWidget.tabPage.setTabText( curPageWidget.tabPage.currentIndex(), "Project: {} - {}".format( newName, self.pageDict[self.curPage].neoConName)) @pyqtSlot() def on_actionSaveProjectAs_triggered(self): """ Save Project As """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": newName = curPageWidget.tabPage.currentWidget().saveProjectAs() if newName is not None: curPageWidget.tabPage.setTabText( curPageWidget.tabPage.currentIndex(), "Project: {} - {}".format( newName, self.pageDict[self.curPage].neoConName)) @pyqtSlot() def on_actionReverse_Engineer_triggered(self): """ User selects the Reverse Engineer menu item. Display the Reverse Engineer dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().reverseEngineerGraph() else: self.helper.displayErrMsg( "Reverse Engineer", "{} not a project".format( curPageWidget.tabPage.currentWidget().pageType)) @pyqtSlot() def on_actionProjectProperties_triggered(self): """ User selects the project properties menu item. Display the project properties dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().editProjectProperties() else: self.helper.displayErrMsg( "Project Properties", "{} not a project".format( curPageWidget.tabPage.currentWidget().pageType)) def getSchemaObject(self, ): curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "CYPHER": return tab.schemaModel return None def getSchemaTab(self, ): curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "CYPHER": return tab return None def loadProject(self, fileName=None): ''' Load the project file with name fileName. If fileName is None, create a new empty project. ''' # create a temporary file name if its a new project if fileName is None: # update unnamed file counter global unNamedFileCounter unNamedFileCounter = unNamedFileCounter + 1 shortName = "{}".format( "New Project-0{}".format(unNamedFileCounter)) else: head, shortName = ntpath.split(QFileInfo(fileName).fileName()) # make sure the project isn't already loaded curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "PROJECT": if (not fileName is None) and (tab.fileName == fileName): self.helper.displayErrMsg( "Open Project", "The project file: {} is already open. It can only be open once." .format(fileName)) return # create the project widget projectTab = ProjectPageWidget(parent=self, settings=self.settings, pageItem=self.pageDict[self.curPage], fileName=fileName) curPageWidget = self.stackedPageItems.currentWidget() # add the project widget as a tab on the current page widget x = curPageWidget.tabPage.addTab( projectTab, "Project: {} - {}".format(shortName, self.pageDict[self.curPage].neoConName)) curPageWidget.tabPage.setCurrentIndex(x) @pyqtSlot() def on_actionOnline_Help_triggered(self): """ User selects the Online Help menu item. Dislay Online Help menu. """ d = OnlineHelpDLG(self) if d.exec_(): pass @pyqtSlot() def on_actionAbout_triggered(self): """ User selects Help / About menu item. Display the about dialog box """ d = HelpAboutDLG(self) if d.exec_(): pass @pyqtSlot() def on_actionGenerate_Schema_triggered(self): """ User requests the generate schema dialog box from main menu """ d = GenerateSchemaDlg(self) if d.exec_(): pass @pyqtSlot() def on_actionForward_Engineer_triggered(self): """ User requests to perform forward engineering from the open project from main menu """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().forwardEngineerGraph() else: self.logMsg( "User requested to forward engineer but current tab is not a Project" ) @pyqtSlot() def on_actionGenerate_Reports_triggered(self): """ User selects the Generate Project Reports menu item. Display the Generate Reports dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().generateProjectReports() @pyqtSlot() def on_actionReset_User_Password_triggered(self): """ User requests the change user password from main menu """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "CYPHER": curPageWidget.tabPage.currentWidget().resetPassword() @pyqtSlot(QAction) def on_menuRecent_Projects_triggered(self, action): """ User clicked on a recent file menu item @param action DESCRIPTION @type QAction """ self.loadProject(fileName=action.data()) @pyqtSlot() def on_menuRecent_Projects_aboutToShow(self): """ user hovering on recent projects menu item """ recentList = self.settings.value("Default/RecentList") if len(recentList) == 0: return else: # remove any existing actions self.menuRecent_Projects.clear() for projectFile in recentList: # create actions for the recent files aSubAction = self.menuRecent_Projects.addAction(projectFile) aSubAction.setData(projectFile)
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 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")