Exemple #1
0
    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()
Exemple #2
0
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