Esempio n. 1
0
 def onTransactionAmountChanged(self, message):
     transaction = message.data
     if transaction.Parent == self:
         debug.debug("Updating balance because I am %s: %s" % (self.Name, transaction))
         self.Balance = sum((t.Amount for t in self.Transactions))
     else:
         debug.debug("Ignoring transaction because I am %s: %s" % (self.Name, transaction))
Esempio n. 2
0
 def loadProfiles(self):
     self.profiles = shippedProfiles
     try:
         contents = open(self.configFile, 'r')
         storedProfiles = json.load(contents)
     except Exception, e:
         debug.debug("Unable to read CSV profiles file:", e)
Esempio n. 3
0
 def syncBalances(self):
     debug.debug("Syncing balances...")
     # Load the model, necessary to sync the balances.
     model = self.GetModel()
     for account in model.Accounts:
         account.Balance = sum([t.Amount for t in account.Transactions])
     self.commitIfAppropriate()
Esempio n. 4
0
    def onORMObjectUpdated(self, message):
        topic, data = message.topic, message.data
        classname, attrname = topic[-2:]
        ormobj = data

        table = ormobj.ORM_TABLE
        value = ormobj.getAttrValue(attrname)

        if isinstance(ormobj, ORMKeyValueObject):
            result = self.dbconn.cursor().execute(
                "UPDATE %s SET value=? WHERE name=?" % table,
                (repr(value), attrname))
        else:
            # Figure out the name of the column
            colname = attrname.strip("_")
            colname = colname[0].lower() + colname[1:]
            colname = {
                "repeatOn": "repeatsOn",
                "source": "sourceId",
                "linkedTransaction": "linkId"
            }.get(colname, colname)

            query = "UPDATE %s SET %s=? WHERE id=?" % (table, colname)
            objId = ormobj.ID
            self.dbconn.cursor().execute(query, (value, objId))

        self.commitIfAppropriate()
        debug.debug("Persisting %s.%s update (%s)" %
                    (classname, attrname, value))
Esempio n. 5
0
 def loadProfiles(self):
     self.profiles = shippedProfiles
     try:
         contents = open(self.configFile, 'r')
         storedProfiles = json.load(contents)
     except Exception, e:
         debug.debug("Unable to read CSV profiles file:", e)
Esempio n. 6
0
 def syncBalances(self):
     debug.debug("Syncing balances...")
     # Load the model, necessary to sync the balances.
     model = self.GetModel()
     for account in model.Accounts:
         account.Balance = sum([t.Amount for t in account.Transactions])
     self.commitIfAppropriate()
Esempio n. 7
0
    def onORMObjectUpdated(self, message):
        topic, data = message.topic, message.data
        classname, attrname = topic[-2:]
        ormobj = data

        table = ormobj.ORM_TABLE
        value = ormobj.getAttrValue(attrname)

        if isinstance(ormobj, ORMKeyValueObject):
            result = self.dbconn.cursor().execute(
                "UPDATE %s SET value=? WHERE name=?" % table, (repr(value), attrname)
            )  # @UnusedVariable
        else:
            # Figure out the name of the column
            colname = attrname.strip("_")
            colname = colname[0].lower() + colname[1:]
            colname = {"repeatOn": "repeatsOn", "source": "sourceId", "linkedTransaction": "linkId"}.get(
                colname, colname
            )

            query = "UPDATE %s SET %s=? WHERE id=?" % (table, colname)
            objId = ormobj.ID
            self.dbconn.cursor().execute(query, (value, objId))

        self.commitIfAppropriate()
        debug.debug("Persisting %s.%s update (%s)" % (classname, attrname, value))
Esempio n. 8
0
    def Save(self):
        import time

        t = time.time()
        self.dbconn.commit()
        debug.debug("Committed in %s seconds" % (time.time() - t))
        self.Dirty = False
Esempio n. 9
0
 def onTransactionAmountChanged(self, message):
     transaction = message.data
     if transaction.Parent == self:
         debug.debug("Updating balance because I am %s: %s" %
                     (self.Name, transaction))
         self.Balance = sum((t.Amount for t in self.Transactions))
     else:
         debug.debug("Ignoring transaction because I am %s: %s" %
                     (self.Name, transaction))
Esempio n. 10
0
 def MigrateIfFound(self, fromPath, toPath):
     """Migrate a file from fromPath (if it exists) to toPath."""
     if os.path.exists(fromPath):
         import shutil
         try:
             shutil.move(fromPath, toPath)
         except IOError:
             debug.debug("Unable to move %s to %s, attempting a copy instead..." % (fromPath, toPath))
             shutil.copyfile(fromPath, toPath)
         return True
Esempio n. 11
0
    def SetAutoSave(self, val):
        self._AutoSave = val
        wx.Config.Get().WriteBool("AUTO-SAVE", val)
        Publisher.sendMessage("controller.autosave_toggled", val)
        for model in self.Models:
            debug.debug("Setting auto-save to: %s" % val)
            model.Store.AutoSave = val

        # If the user enables auto-save, we want to also save.
        if self.AutoSave:
            Publisher.sendMessage("user.saved")
Esempio n. 12
0
    def SetAutoSave(self, val):
        self._AutoSave = val
        wx.Config.Get().WriteBool("AUTO-SAVE", val)
        Publisher.sendMessage("controller.autosave_toggled", val)
        for model in self.Models:
            debug.debug("Setting auto-save to: %s" % val)
            model.Store.AutoSave = val

        # If the user enables auto-save, we want to also save.
        if self.AutoSave:
            Publisher.sendMessage("user.saved")
Esempio n. 13
0
 def MigrateIfFound(self, fromPath, toPath):
     """Migrate a file from fromPath (if it exists) to toPath."""
     if os.path.exists(fromPath):
         import shutil
         try:
             shutil.move(fromPath, toPath)
         except IOError:
             debug.debug(
                 "Unable to move %s to %s, attempting a copy instead..." %
                 (fromPath, toPath))
             shutil.copyfile(fromPath, toPath)
         return True
Esempio n. 14
0
    def __init__(self, path, autoSave=True):
        self.Subscriptions = []
        self.Version = 13
        self.Path = path
        self.AutoSave = False
        self.Dirty = False
        self.BatchDepth = 0
        self.cachedModel = None
        # Upgrades can't enable syncing if needed from older versions.
        self.needsSync = False
        existed = True

        # See if the path already exists to decide what to do.
        if not os.path.exists(self.Path):
            existed = False

        # Initialize the connection and optimize it.
        connection = sqlite.connect(self.Path)
        self.dbconn = connection
        # Disable synchronous I/O, which makes everything MUCH faster, at the potential cost of durability.
        self.dbconn.execute("PRAGMA synchronous=OFF;")

        # If the db doesn't exist, initialize it.
        if not existed:
            debug.debug('Initializing', self.Path)
            self.initialize()
        else:
            debug.debug('Loading', self.Path)

        self.Meta = self.getMeta()
        debug.debug(self.Meta)
        while self.Meta['VERSION'] < self.Version:
            # If we are creating a new db, we don't need to backup each iteration.
            self.upgradeDb(self.Meta['VERSION'], backup=existed)
            self.Meta = self.getMeta()
            debug.debug(self.Meta)
         
        # We have to subscribe before syncing otherwise it won't get synced if there aren't other changes.
        self.Subscriptions = (
            (self.onORMObjectUpdated, "ormobject.updated"),
            (self.onAccountBalanceChanged, "account.balance changed"),
            (self.onAccountRemoved, "account.removed"),
            (self.onBatchEvent, "batch"),
            (self.onExit, "exiting"),
        )
        for callback, topic in self.Subscriptions:
            Publisher.subscribe(callback, topic)
            
        # If the upgrade process requires a sync, do so now.
        if self.needsSync:
            self.syncBalances()
            self.needsSync = False

        self.AutoSave = autoSave
        self.commitIfAppropriate()
Esempio n. 15
0
    def __init__(self, path, autoSave=True):
        self.Subscriptions = []
        self.Version = 13
        self.Path = path
        self.AutoSave = False
        self.Dirty = False
        self.BatchDepth = 0
        self.cachedModel = None
        # Upgrades can't enable syncing if needed from older versions.
        self.needsSync = False
        existed = True

        # See if the path already exists to decide what to do.
        if not os.path.exists(self.Path):
            existed = False

        # Initialize the connection and optimize it.
        connection = sqlite.connect(self.Path)
        self.dbconn = connection
        # Disable synchronous I/O, which makes everything MUCH faster, at the potential cost of durability.
        self.dbconn.execute("PRAGMA synchronous=OFF;")

        # If the db doesn't exist, initialize it.
        if not existed:
            debug.debug('Initializing', self.Path)
            self.initialize()
        else:
            debug.debug('Loading', self.Path)

        self.Meta = self.getMeta()
        debug.debug(self.Meta)
        while self.Meta['VERSION'] < self.Version:
            # If we are creating a new db, we don't need to backup each iteration.
            self.upgradeDb(self.Meta['VERSION'], backup=existed)
            self.Meta = self.getMeta()
            debug.debug(self.Meta)

        # We have to subscribe before syncing otherwise it won't get synced if there aren't other changes.
        self.Subscriptions = (
            (self.onORMObjectUpdated, "ormobject.updated"),
            (self.onAccountBalanceChanged, "account.balance changed"),
            (self.onAccountRemoved, "account.removed"),
            (self.onBatchEvent, "batch"),
            (self.onExit, "exiting"),
        )
        for callback, topic in self.Subscriptions:
            Publisher.subscribe(callback, topic)

        # If the upgrade process requires a sync, do so now.
        if self.needsSync:
            self.syncBalances()
            self.needsSync = False

        self.AutoSave = autoSave
        self.commitIfAppropriate()
Esempio n. 16
0
    def GetModel(self, useCached=True):
        if self.cachedModel is None or not useCached:
            debug.debug('Creating model...')
            self.cachedModel = BankModel(self)

        return self.cachedModel
Esempio n. 17
0
    def upgradeDb(self, fromVer, backup=True):
        if backup:
            # Make a backup
            source = self.Path
            dest = self.Path + ".backup-v%i-%s" % (fromVer, datetime.date.today().strftime("%Y-%m-%d"))
            debug.debug("Making backup to %s" % dest)
            import shutil
            try:
                shutil.copyfile(source, dest)
            except IOError:
                import traceback; traceback.print_exc()
                raise Exception("Unable to make backup before proceeding with database upgrade...bailing.")

        debug.debug('Upgrading db from %i' % fromVer)
        cursor = self.dbconn.cursor()

        if fromVer == 1:
            # Add `currency` column to the accounts table with default value 0.
            cursor.execute('ALTER TABLE accounts ADD currency INTEGER not null DEFAULT 0')
            # Add metadata table, with version: 2
            cursor.execute('CREATE TABLE meta (id INTEGER PRIMARY KEY, name VARCHAR(255), value VARCHAR(255))')
            cursor.execute('INSERT INTO meta VALUES (null, ?, ?)', ('VERSION', '2'))
        elif fromVer == 2:
            # Add `total` column to the accounts table.
            cursor.execute('ALTER TABLE accounts ADD balance FLOAT not null DEFAULT 0.0')
            # The total column will need to be synced.
            self.needsSync = True
            # Update the meta version number.
        elif fromVer == 3:
            # Add `linkId` column to transactions for transfers.
            cursor.execute('ALTER TABLE transactions ADD linkId INTEGER')
        elif fromVer == 4:
            # Add recurring transactions table.
            transactionBase = "id INTEGER PRIMARY KEY, accountId INTEGER, amount FLOAT, description VARCHAR(255), date CHAR(10)"
            recurringExtra = "repeatType INTEGER, repeatEvery INTEGER, repeatsOn VARCHAR(255), endDate CHAR(10)"
            cursor.execute('CREATE TABLE recurring_transactions (%s, %s)' % (transactionBase, recurringExtra))
        elif fromVer == 5:
            cursor.execute('ALTER TABLE recurring_transactions ADD sourceId INTEGER')
            cursor.execute('ALTER TABLE recurring_transactions ADD lastTransacted CHAR(10)')
        elif fromVer == 6:
            # Handle LP: #496341 by ignoring the error if this table already exists.
            try:
                cursor.execute('ALTER TABLE transactions ADD recurringParent INTEGER')
            except sqlite3.OperationalError:
                debug.debug("Detected a broken database from the 0.4 -> 0.6 upgrade, handling appropriately.")
        elif fromVer == 7:
            # Force a re-sync for the 0.6.1 release after fixing LP: #496341
            self.needsSync = True
        elif fromVer == 8:
            # Add the LastAccountId meta key.
            cursor.execute('INSERT INTO meta VALUES (null, ?, ?)', ('LastAccountId', None))
        elif fromVer == 9:
            # Mint integration
            cursor.execute('INSERT INTO meta VALUES (null, ?, ?)', ('MintEnabled', False))
            cursor.execute('ALTER TABLE accounts ADD mintId INTEGER')
        elif fromVer == 10:
            # The obvious index to create, had I known about indexes previously.
            # It is nice to use IF NOT EXISTS here as some devs and branch users might have it outside of an upgrade.
            cursor.execute('CREATE INDEX IF NOT EXISTS transactions_accountId_idx ON transactions(accountId)')
            # Due to bug LP #605591 there can be orphaned transactions which can cause errors if linked.
            # This is tested by testOrphanedTransactionsAreDeleted (dbupgradetests.DBUpgradeTest)
            # Also takes care of LP #249954 without the explicit need to defensively remove on any account creation.
            self.cleanOrphanedTransactions()
        elif fromVer == 11:
            self.needsSync = True
        elif fromVer == 12:
            # globalCurrency entry
            cursor.execute('INSERT INTO meta VALUES (null, ?, ?)', ('GlobalCurrency', 0))
        else:
            raise Exception("Cannot upgrade database from version %i"%fromVer)
        
        """
        # Tagging infrastructure (currently unused due to speed of parsing on startup).
        cursor.execute('CREATE TABLE tags (id INTEGER PRIMARY KEY, name VARCHAR(255))')
        cursor.execute('CREATE TABLE transactions_tags_link (id INTEGER PRIMARY KEY, transactionId INTEGER, tagId INTEGER)')
        cursor.execute('CREATE INDEX transactions_tags_transactionId_idx ON transactions_tags_link(transactionId)')
        cursor.execute('CREATE INDEX transactions_tags_tagId_idx ON transactions_tags_link(tagId)')
        """

        metaVer = fromVer + 1
        cursor.execute('UPDATE meta SET value=? WHERE name=?', (metaVer, "VERSION"))
        self.commitIfAppropriate()
Esempio n. 18
0
    def GetModel(self, useCached=True):
        if self.cachedModel is None or not useCached:
            debug.debug('Creating model...')
            self.cachedModel = BankModel(self)

        return self.cachedModel
Esempio n. 19
0
 def Save(self):
     import time
     t = time.time()
     self.dbconn.commit()
     debug.debug("Committed in %s seconds" % (time.time() - t))
     self.Dirty = False
Esempio n. 20
0
    def upgradeDb(self, fromVer, backup=True):
        if backup:
            # Make a backup
            source = self.Path
            dest = self.Path + ".backup-v%i-%s" % (
                fromVer, datetime.date.today().strftime("%Y-%m-%d"))
            debug.debug("Making backup to %s" % dest)
            import shutil
            try:
                shutil.copyfile(source, dest)
            except IOError:
                import traceback
                traceback.print_exc()
                raise Exception(
                    "Unable to make backup before proceeding with database upgrade...bailing."
                )

        debug.debug('Upgrading db from %i' % fromVer)
        cursor = self.dbconn.cursor()

        if fromVer == 1:
            # Add `currency` column to the accounts table with default value 0.
            cursor.execute(
                'ALTER TABLE accounts ADD currency INTEGER not null DEFAULT 0')
            # Add metadata table, with version: 2
            cursor.execute(
                'CREATE TABLE meta (id INTEGER PRIMARY KEY, name VARCHAR(255), value VARCHAR(255))'
            )
            cursor.execute('INSERT INTO meta VALUES (null, ?, ?)',
                           ('VERSION', '2'))
        elif fromVer == 2:
            # Add `total` column to the accounts table.
            cursor.execute(
                'ALTER TABLE accounts ADD balance FLOAT not null DEFAULT 0.0')
            # The total column will need to be synced.
            self.needsSync = True
            # Update the meta version number.
        elif fromVer == 3:
            # Add `linkId` column to transactions for transfers.
            cursor.execute('ALTER TABLE transactions ADD linkId INTEGER')
        elif fromVer == 4:
            # Add recurring transactions table.
            transactionBase = "id INTEGER PRIMARY KEY, accountId INTEGER, amount FLOAT, description VARCHAR(255), date CHAR(10)"
            recurringExtra = "repeatType INTEGER, repeatEvery INTEGER, repeatsOn VARCHAR(255), endDate CHAR(10)"
            cursor.execute('CREATE TABLE recurring_transactions (%s, %s)' %
                           (transactionBase, recurringExtra))
        elif fromVer == 5:
            cursor.execute(
                'ALTER TABLE recurring_transactions ADD sourceId INTEGER')
            cursor.execute(
                'ALTER TABLE recurring_transactions ADD lastTransacted CHAR(10)'
            )
        elif fromVer == 6:
            # Handle LP: #496341 by ignoring the error if this table already exists.
            try:
                cursor.execute(
                    'ALTER TABLE transactions ADD recurringParent INTEGER')
            except sqlite3.OperationalError:
                debug.debug(
                    "Detected a broken database from the 0.4 -> 0.6 upgrade, handling appropriately."
                )
        elif fromVer == 7:
            # Force a re-sync for the 0.6.1 release after fixing LP: #496341
            self.needsSync = True
        elif fromVer == 8:
            # Add the LastAccountId meta key.
            cursor.execute('INSERT INTO meta VALUES (null, ?, ?)',
                           ('LastAccountId', None))
        elif fromVer == 9:
            # Mint integration
            cursor.execute('INSERT INTO meta VALUES (null, ?, ?)',
                           ('MintEnabled', False))
            cursor.execute('ALTER TABLE accounts ADD mintId INTEGER')
        elif fromVer == 10:
            # The obvious index to create, had I known about indexes previously.
            # It is nice to use IF NOT EXISTS here as some devs and branch users might have it outside of an upgrade.
            cursor.execute(
                'CREATE INDEX IF NOT EXISTS transactions_accountId_idx ON transactions(accountId)'
            )
            # Due to bug LP #605591 there can be orphaned transactions which can cause errors if linked.
            # This is tested by testOrphanedTransactionsAreDeleted (dbupgradetests.DBUpgradeTest)
            # Also takes care of LP #249954 without the explicit need to defensively remove on any account creation.
            self.cleanOrphanedTransactions()
        elif fromVer == 11:
            self.needsSync = True
        elif fromVer == 12:
            # globalCurrency entry
            cursor.execute('INSERT INTO meta VALUES (null, ?, ?)',
                           ('GlobalCurrency', 0))
        else:
            raise Exception("Cannot upgrade database from version %i" %
                            fromVer)
        """
        # Tagging infrastructure (currently unused due to speed of parsing on startup).
        cursor.execute('CREATE TABLE tags (id INTEGER PRIMARY KEY, name VARCHAR(255))')
        cursor.execute('CREATE TABLE transactions_tags_link (id INTEGER PRIMARY KEY, transactionId INTEGER, tagId INTEGER)')
        cursor.execute('CREATE INDEX transactions_tags_transactionId_idx ON transactions_tags_link(transactionId)')
        cursor.execute('CREATE INDEX transactions_tags_tagId_idx ON transactions_tags_link(tagId)')
        """

        metaVer = fromVer + 1
        cursor.execute('UPDATE meta SET value=? WHERE name=?',
                       (metaVer, "VERSION"))
        self.commitIfAppropriate()