def _importDatabaseFromServer( self ): ftpini = FtpIni( "ftp.ini" ) dlg = QInputDialog() dlg.move( self._mainwin.cursor().pos() ) name, ok = dlg.getText( self._mainwin, "Immo-Datenbank importieren", "Datenbank wird ins Verzeichnis\n\n'%s'\n\n importiert.\n\n" "<<<<Sie wird nicht für die laufende Anwendung verwendet!!>>>>\n\n" "Lokalen Namen für die Datenbank angeben: " % ftpini.getLocalPath(), QLineEdit.Normal, "immo.db.imported" ) if not ( ok and name ): return ftp = Ftp( ftpini ) try: ftp.connect() ftp.download( "immo.db", name ) box = InfoBox( "Immo-Datenbak importieren", "Importieren abgeschlossen.", "", "OK" ) box.move( self._mainwin.cursor().pos() ) box.exec_() except Exception as ex: box = ErrorBox( "File Transfer failed", "Can't export immo.db to server:\n", str( ex ) ) box.exec_() finally: ftp.quit()
class IccStateHandler: """ IccFileTransfer handles all ftp up- and downloads concerning the ImmoControlCenter application. - beauskunftet den Status der immo-Datenbanken a) auf dem Server b) lokal using file immo_state. Uploads and downloads immo_state and immo.db. Structure of file immo_state: state=not_in_use last_update=2021-04-22 - processes up- and donwloads as needed """ attributename_state = "state" attributename_lastupdate = "last_update" state_in_use = "in_use" state_not_in_use = "not_in_use" state_last_update_unknown = "unknown" immostate_sep = "=" def __init__(self, ftpini_pathnfile: str): self._ftpini_pathnfile = ftpini_pathnfile self.immostate_filename = "immo_state" # remote and local filename self.immostate_localfilename_tmp = "immo_state.tmp" # local name after download self.immo_dbname = "immo.db" # remote and local filename self._ftp: Ftp = None self._ftpini: FtpIni = None self.currentState: ImmoState = None def startApplication(self): """ Connects to FTP-Server. Checks state. Downloads database. Might throw exception. Don't forget calling stopApplication before exiting the application. :return: """ if self._ftp: # FTP connection already established return # get paths and filenames needed to establish a FTP connection: self._ftpini = FtpIni(self._ftpini_pathnfile) # initialize Ftp class and connect to FTP server self._ftp = Ftp(self._ftpini) self._ftp.connect() # for comparing purposes download serverside immo_state try: serverstate: ImmoState = self._getServerState() except Exception as ex: raise Exception( "IccStateHandler.startApplication():\nCan't get serverside state:\n%s" % str(ex)) if serverstate.isInUse: raise Exception( "IccStateHandler.startApplication():\nCan't start application - Database is in use" ) try: localstate: ImmoState = self._getLocalState() except Exception as ex: raise Exception( "IccStateHandler.startApplication():\nCan't get local state:\n%s" % str(ex)) # compare serverside and locas last updates and raise exception if local last update is newer than # serverside last update if serverstate.lastUpdate < localstate.lastUpdate: raise Exception( "IccStateHandler.startApplication():\n" "Expected serverside last update to be newer than local last update but found:\n" "serverside = %s -- local = %s" % (serverstate.lastUpdate, localstate.lastUpdate)) else: # Everything is okay. We may download the serverside database now for local use. try: self._ftp.download(self.immo_dbname, self.immo_dbname) except Exception as ex: raise Exception("IccStateHandler.startApplication():\n" "Download of serverside immo.db failed:\n%s" % str(ex)) # Set is-in-use state in local and serverside immo_state files. # Attribute last_update remains unchanged. localstate.isInUse = True try: self._setStateAndSave(localstate) except Exception as ex: raise Exception( "IccStateHandler.startApplication():\n" "After download of serverside immo.db:\nFailed to set state:\n%s" % str(ex)) def stopApplication(self): """ Uploads database Sets state and last_update attributes in immo_state. Uploads immo_state Disconnects from FTP-Server Might throw exception. :return: """ try: self._ftp.upload(self.immo_dbname, self.immo_dbname) except Exception as ex: raise Exception("IccStateHandler.stopApplication():\n" "Upload immo.db failed:\n%s" % str(ex)) state = ImmoState() state.isInUse = False state.lastUpdate = datehelper.getCurrentTimestampIso() try: self._setStateAndSave(state) except Exception as ex: raise Exception( "IccStateHandler.stopApplication():\n" "After upload of local immo.db:\nFailed to set state:\n%s" % str(ex)) self._ftp.quit() self._ftp = None def _getServerState(self) -> ImmoState: self._ftp.download(self.immostate_filename, self.immostate_localfilename_tmp) localpathnfile_tmp = self._ftpini.getLocalPath( ) + self.immostate_localfilename_tmp serverstate: ImmoState = self._readAttributesFromState( localpathnfile_tmp) return serverstate def _setStateAndSave(self, immoState: ImmoState) -> None: state = self.state_in_use if immoState.isInUse else self.state_not_in_use last_update = immoState.lastUpdate stateline = self.attributename_state + self.immostate_sep + state lastupdateline = self.attributename_lastupdate + self.immostate_sep + last_update self._setLocalAndServerStateAndSave([stateline, "\n", lastupdateline]) def _setLocalAndServerStateAndSave(self, lines: list) -> None: """ :param lines: attribute names and values to write into immo_state :return: """ pathnfile = self._ftpini.getLocalPath() + self.immostate_filename try: with open(pathnfile, "w") as statefile: for line in lines: statefile.write(line) except Exception as ex: raise Exception( "IccStateHandler._setLocalAndServerStateAndSave():\n" "Local storage of file immo_state failed.\n%s" % str(ex)) try: self._ftp.upload(self.immostate_filename, self.immostate_filename) except Exception as ex: raise Exception( "IccStateHandler._setLocalAndServerStateAndSave():\n" "Serverside storage of file immo_state failed.\n%s" % str(ex)) def _getLocalState(self) -> ImmoState: """ gets state attributes from local immo_state file :return: """ localpathnfile = self._ftpini.getLocalPath() + self.immostate_filename localstate: ImmoState = self._readAttributesFromState(localpathnfile) return localstate def _readAttributesFromState(self, pathnfile: str) -> ImmoState: """ provides informations "database last update" and "database is in use" from file <pathnfile> :param pathnfile: immo_state file server or local side in the prescribed structure (see class comment) :return: an ImmoState object containing the attributes found in <pathnfile> """ immostate = ImmoState() serverstatefile = open(pathnfile, "r") content = serverstatefile.read() serverstatefile.close() lines = content.splitlines() for line in lines: parts = line.split( self.immostate_sep) # list like so: ['state', 'not_in_use'] if len(parts) != 2: raise Exception( "IccFileTransfer._readAttributesFromState: Attribute invalid\n" "Expected <attribute_name>=<attribute_value> -- found: %s" % parts) if parts[0] == self.attributename_state: immostate.isInUse = True if parts[ 1] == self.state_in_use else False elif parts[0] == self.attributename_lastupdate: immostate.lastUpdate = parts[1] return immostate