Merge lp:~kolmis/wxbanker/transaction-tagging into lp:wxbanker

Proposed by Michael Rooney
Status: Merged
Merge reported by: Michael Rooney
Merged at revision: not available
Proposed branch: lp:~kolmis/wxbanker/transaction-tagging
Merge into: lp:wxbanker
Diff against target: None lines
To merge this branch: bzr merge lp:~kolmis/wxbanker/transaction-tagging
Reviewer Review Type Date Requested Status
Michael Rooney Needs Information
Review via email: mp+7356@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Michael Rooney (mrooney) wrote :

I'll propose this for a merge since it would be nice to integrate, and then I can potentially merge the categories branch as well.

One issue I noticed off-hand is that using a list as a default argument probably isn't what you want (tags=[]) as default arguments are only evaluated once thus it will be the same list:

>>> def foo(x=[]):
... x.append(1)
... print x
...
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]

So typically in this case what you do is set tags=None and at the top of the function say if tags is None: tags = []

What are your thoughts on merging this branch? I don't want an extra tags column by default as I want wxBanker to be as simple and clean as possible as surely some users won't need tags, but I'd like it to be as discoverable as possible. Perhaps have some exposed way to tag a transaction, and then only show the column for accounts which have a tagged transaction.

Revision history for this message
Michael Rooney (mrooney) :
review: Needs Information
Revision history for this message
Karel Kolman (kolmis) wrote :

Michael, there are a few issues:
- i need access to bankModel in persistenStore; i had reverted the code that cached the bankModel in a persistentStore member variable, however i didn't realize the bankModel was used to access cached Tag objects. could you spare a simple explanation on the Model - Store coupling, or explain the way multiple models/stores are used in the unit tests (that failed by caching the bankModel)

- as for the tag column hiding, the visibility toggling you're suggesting seems like a good idea to me

- i was not aware of python's default argument once-only-evaluation, thanks for pointing that out

- i promised writing a few tests for the tags, i didn't get around to it yet (i admit there are times when i don't enjoy writing tests)

Revision history for this message
Michael Rooney (mrooney) wrote :

The tests rely on getting a fresh model from the db in a few cases, to make sure something changed on the model actually ended up in the db and is reflected in a new model. See the test*IsStored tests in modeltests.

I've push up a new branch (~mrooney/wxbanker/transaction-tagging) and r248 should address this for both of us. GetModel now uses a cached model by default if one exists, but the tests pass in useCached=False to make sure they get a fresh one.

I also fixed the default argument issue in r249, so I'd recommend merging my branch and continuing from there.

Any ideas on how to expose tagging a transaction in a discoverable way? Or, is there already such a way in your branch?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bankobjects.py'
--- bankobjects.py 2009-04-08 02:30:18 +0000
+++ bankobjects.py 2009-05-15 12:07:21 +0000
@@ -24,14 +24,30 @@
2424
2525
26class BankModel(object):26class BankModel(object):
27 def __init__(self, store, accountList):27 def __init__(self, store):
28 self.Store = store28 self.Store = store
29 self.Accounts = accountList29 self.Accounts = AccountList(store, store.getAccounts())
30 self.Tags = store.getTags()
30 31
31 Publisher().subscribe(self.onCurrencyChanged, "user.currency_changed")32 Publisher().subscribe(self.onCurrencyChanged, "user.currency_changed")
32 33
33 def GetBalance(self):34 def GetBalance(self):
34 return self.Accounts.Balance35 return self.Accounts.Balance
36
37 def GetTagById(self, id):
38 for tag in self.Tags:
39 if tag.ID == id:
40 return tag
41 return None
42
43 def GetTagByName(self, name):
44 ''' returns a tag by name, creates a new one if none by that name exists '''
45 for tag in self.Tags:
46 if tag.Name == name:
47 return tag
48 newTag = self.Store.CreateTag(name)
49 self.Tags.append(newTag)
50 return newTag
35 51
36 def GetTransactions(self):52 def GetTransactions(self):
37 transactions = []53 transactions = []
@@ -56,7 +72,7 @@
56 72
57 def Search(self, searchString, account=None, matchIndex=1, matchCase=False):73 def Search(self, searchString, account=None, matchIndex=1, matchCase=False):
58 """74 """
59 matchIndex: 0: Amount, 1: Description, 2: Date75 matchIndex: 0: Amount, 1: Description, 2: Date, 3: Tags
60 I originally used strings here but passing around and then validating on translated76 I originally used strings here but passing around and then validating on translated
61 strings seems like a bad and fragile idea.77 strings seems like a bad and fragile idea.
62 """78 """
@@ -73,9 +89,13 @@
73 matches = []89 matches = []
74 for trans in potentials:90 for trans in potentials:
75 #print unicode(trans.Description), searchString91 #print unicode(trans.Description), searchString
76 potentialStr = unicode((trans.Amount, trans.Description, trans.Date)[matchIndex])92 potentialStrings = (
77 if re.findall(searchString, potentialStr, flags=reFlag):93 (trans.Amount, ), (trans.Description, ), (trans.Date, ), trans.Tags or ('', )
78 matches.append(trans)94 )[matchIndex]
95 for potentialStr in potentialStrings:
96 if re.search(searchString, unicode(potentialStr), flags=reFlag):
97 matches.append(trans)
98 break
79 return matches99 return matches
80 100
81 def Save(self):101 def Save(self):
@@ -316,6 +336,18 @@
316 self.RemoveTransactions(transactions)336 self.RemoveTransactions(transactions)
317 destAccount.AddTransactions(transactions)337 destAccount.AddTransactions(transactions)
318 Publisher.sendMessage("batch.end")338 Publisher.sendMessage("batch.end")
339
340 def TagTransactions(self, transactions, tag):
341 Publisher.sendMessage("batch.start")
342 for t in transactions:
343 t.AddTag(tag)
344 Publisher.sendMessage("batch.end")
345
346 def UntagTransactions(self, transactions, tag):
347 Publisher.sendMessage("batch.start")
348 for t in transactions:
349 t.RemoveTag(tag)
350 Publisher.sendMessage("batch.end")
319 351
320 def onTransactionAmountChanged(self, message):352 def onTransactionAmountChanged(self, message):
321 transaction, difference = message.data353 transaction, difference = message.data
@@ -371,7 +403,7 @@
371 Changes to this object get sent out via pubsub,403 Changes to this object get sent out via pubsub,
372 typically causing the model to make the change.404 typically causing the model to make the change.
373 """405 """
374 def __init__(self, tID, parent, amount, description, date):406 def __init__(self, tID, parent, amount, description, date, tags=[]):
375 self.IsFrozen = True407 self.IsFrozen = True
376 408
377 self.ID = tID409 self.ID = tID
@@ -379,9 +411,29 @@
379 self.Date = date411 self.Date = date
380 self.Description = description412 self.Description = description
381 self.Amount = amount413 self.Amount = amount
414 self.Tags = tags
382 415
383 self.IsFrozen = False416 self.IsFrozen = False
384 417
418 def GetTags(self):
419 return self._Tags
420
421 def SetTags(self, tags):
422 self._Tags = tags
423
424 if not self.IsFrozen:
425 Publisher.sendMessage("transaction.updated.tags", (self, None))
426
427 def AddTag(self, tag):
428 if not tag in self.Tags:
429 self.Tags.append(tag)
430 Publisher.sendMessage("transaction.updated.tags", (self, None))
431
432 def RemoveTag(self, tag):
433 if tag in self.Tags:
434 self.Tags.remove(tag)
435 Publisher.sendMessage("transaction.updated.tags", (self, None))
436
385 def GetDate(self):437 def GetDate(self):
386 return self._Date438 return self._Date
387 439
@@ -480,8 +532,21 @@
480 532
481 Date = property(GetDate, SetDate)533 Date = property(GetDate, SetDate)
482 Description = property(GetDescription, SetDescription)534 Description = property(GetDescription, SetDescription)
535 Tags = property(GetTags, SetTags)
483 Amount = property(GetAmount, SetAmount)536 Amount = property(GetAmount, SetAmount)
484 537
538
539class Tag(object):
540 def __init__(self, aID, name):
541 self.ID = aID
542 self.Name = name
543
544 def __str__(self):
545 return self.Name
546
547 def __cmp__(self, other):
548 return cmp(self.ID, other.ID)
549
485 550
486if __name__ == "__main__":551if __name__ == "__main__":
487 import doctest552 import doctest
488553
=== modified file 'persistentstore.py'
--- persistentstore.py 2009-04-08 02:30:18 +0000
+++ persistentstore.py 2009-05-15 13:15:36 +0000
@@ -44,7 +44,7 @@
44 back the changes.44 back the changes.
45 """45 """
46 def __init__(self, path, autoSave=True):46 def __init__(self, path, autoSave=True):
47 self.Version = 347 self.Version = 4
48 self.Path = path48 self.Path = path
49 self.AutoSave = autoSave49 self.AutoSave = autoSave
50 self.Dirty = False50 self.Dirty = False
@@ -70,6 +70,8 @@
70 debug.debug(self.Meta)70 debug.debug(self.Meta)
71 71
72 self.commitIfAppropriate()72 self.commitIfAppropriate()
73 # better delete tags on exit ?
74 #self.DeleteUnusedTags()
73 75
74 self.Subscriptions = (76 self.Subscriptions = (
75 (self.onTransactionUpdated, "transaction.updated"),77 (self.onTransactionUpdated, "transaction.updated"),
@@ -88,9 +90,7 @@
88 if "--sync-balances" in sys.argv:90 if "--sync-balances" in sys.argv:
89 self.syncBalances()91 self.syncBalances()
90 92
91 accounts = self.getAccounts()93 bankmodel = bankobjects.BankModel(self)
92 accountList = bankobjects.AccountList(self, accounts)
93 bankmodel = bankobjects.BankModel(self, accountList)
94 94
95 return bankmodel95 return bankmodel
96 96
@@ -134,6 +134,19 @@
134 # everything is fine. So just return True, as there we no errors that we are aware of.134 # everything is fine. So just return True, as there we no errors that we are aware of.
135 return True135 return True
136 136
137 def CreateTag(self, name):
138 cursor = self.dbconn.cursor()
139 cursor.execute('INSERT INTO tags (name) VALUES (?)', [name])
140 ID = cursor.lastrowid
141 self.commitIfAppropriate()
142
143 return bankobjects.Tag(ID, name)
144
145 def DeleteUnusedTags(self):
146 cursor = self.dbconn.cursor()
147 cursor.execute('DELETE FROM tags WHERE NOT EXISTS (SELECT 1 FROM transactions_tags_link l WHERE tagId = tags.id)')
148 self.commitIfAppropriate()
149
137 def Save(self):150 def Save(self):
138 import time; t = time.time()151 import time; t = time.time()
139 self.dbconn.commit()152 self.dbconn.commit()
@@ -173,9 +186,15 @@
173186
174 cursor.execute('CREATE TABLE accounts (id INTEGER PRIMARY KEY, name VARCHAR(255), currency INTEGER, balance FLOAT)')187 cursor.execute('CREATE TABLE accounts (id INTEGER PRIMARY KEY, name VARCHAR(255), currency INTEGER, balance FLOAT)')
175 cursor.execute('CREATE TABLE transactions (id INTEGER PRIMARY KEY, accountId INTEGER, amount FLOAT, description VARCHAR(255), date CHAR(10))')188 cursor.execute('CREATE TABLE transactions (id INTEGER PRIMARY KEY, accountId INTEGER, amount FLOAT, description VARCHAR(255), date CHAR(10))')
189 cursor.execute('CREATE INDEX transactions_accountId_idx ON transactions(accountId)')
190
191 cursor.execute('CREATE TABLE tags (id INTEGER PRIMARY KEY, name VARCHAR(255))')
192 cursor.execute('CREATE TABLE transactions_tags_link (id INTEGER PRIMARY KEY, transactionId INTEGER, tagId INTEGER)')
193 cursor.execute('CREATE INDEX transactions_tags_transactionId_idx ON transactions_tags_link(transactionId)')
194 cursor.execute('CREATE INDEX transactions_tags_tagId_idx ON transactions_tags_link(tagId)')
176 195
177 cursor.execute('CREATE TABLE meta (id INTEGER PRIMARY KEY, name VARCHAR(255), value VARCHAR(255))')196 cursor.execute('CREATE TABLE meta (id INTEGER PRIMARY KEY, name VARCHAR(255), value VARCHAR(255))')
178 cursor.execute('INSERT INTO meta VALUES (null, ?, ?)', ('VERSION', '3'))197 cursor.execute('INSERT INTO meta VALUES (null, ?, ?)', ('VERSION', self.Version))
179198
180 return connection199 return connection
181 200
@@ -219,12 +238,24 @@
219 # Add `total` column to the accounts table.238 # Add `total` column to the accounts table.
220 cursor.execute('ALTER TABLE accounts ADD balance FLOAT not null DEFAULT 0.0')239 cursor.execute('ALTER TABLE accounts ADD balance FLOAT not null DEFAULT 0.0')
221 self.syncBalances()240 self.syncBalances()
222 # Update the meta version number.241 self.__updateDbVersion(3)
223 cursor.execute('UPDATE meta SET value=? WHERE name=?', (3, "VERSION"))242 elif fromVer == 3:
243 # index for the accountId column in transactions
244 cursor.execute('CREATE INDEX transactions_accountId_idx ON transactions(accountId)')
245 # tags table; transactions - tags link table
246 cursor.execute('CREATE TABLE tags (id INTEGER PRIMARY KEY, name VARCHAR(255))')
247 cursor.execute('CREATE TABLE transactions_tags_link (id INTEGER PRIMARY KEY, transactionId INTEGER, tagId INTEGER)')
248 cursor.execute('CREATE INDEX transactions_tags_transactionId_idx ON transactions_tags_link(transactionId)')
249 cursor.execute('CREATE INDEX transactions_tags_tagId_idx ON transactions_tags_link(tagId)')
250 self.__updateDbVersion(4)
224 else:251 else:
225 raise Exception("Cannot upgrade database from version %i"%fromVer)252 raise Exception("Cannot upgrade database from version %i"%fromVer)
226 253
227 self.commitIfAppropriate()254 self.commitIfAppropriate()
255
256 def __updateDbVersion(self, version):
257 # Update the meta version number.
258 self.dbconn.cursor().execute('UPDATE meta SET value=? WHERE name=?', (version, "VERSION"))
228 259
229 def syncBalances(self):260 def syncBalances(self):
230 debug.debug("Syncing balances...")261 debug.debug("Syncing balances...")
@@ -252,17 +283,32 @@
252 self.dbconn.cursor().execute('DELETE FROM transactions WHERE accountId=?', (account.ID,))283 self.dbconn.cursor().execute('DELETE FROM transactions WHERE accountId=?', (account.ID,))
253 self.commitIfAppropriate()284 self.commitIfAppropriate()
254 285
286 def getTags(self):
287 return [self.result2tag(result) for result in self.dbconn.cursor().execute("SELECT * FROM tags").fetchall()]
288
289 def result2tag(self, result):
290 ID, name = result
291 return bankobjects.Tag(ID, name)
292
293 def getTagsForTransaction(self, transId):
294 result = self.dbconn.cursor().execute(
295 'SELECT tagId FROM transactions_tags_link WHERE transactionId=?', (transId, )).fetchall()
296 return [self.bankModel.GetTagById(row[0]) for row in result]
297
255 def getTransactionsFrom(self, account):298 def getTransactionsFrom(self, account):
256 transactions = bankobjects.TransactionList()299 transactions = bankobjects.TransactionList()
257 for result in self.dbconn.cursor().execute('SELECT * FROM transactions WHERE accountId=?', (account.ID,)).fetchall():300 for result in self.dbconn.cursor().execute('SELECT * FROM transactions WHERE accountId=?', (account.ID,)).fetchall():
258 tid, pid, amount, description, date = result301 tid, pid, amount, description, date = result
259 transactions.append(bankobjects.Transaction(tid, account, amount, description, date))302 transactions.append(bankobjects.Transaction(tid, account, amount, description, date, self.getTagsForTransaction(tid)))
260 return transactions303 return transactions
261 304
262 def updateTransaction(self, transObj):305 def updateTransaction(self, transObj):
263 result = self.transaction2result(transObj)306 result = self.transaction2result(transObj)
264 result.append( result.pop(0) ) # Move the uid to the back as it is last in the args below.307 result.append( result.pop(0) ) # Move the uid to the back as it is last in the args below.
265 self.dbconn.cursor().execute('UPDATE transactions SET amount=?, description=?, date=? WHERE id=?', result)308 self.dbconn.cursor().execute('UPDATE transactions SET amount=?, description=?, date=? WHERE id=?', result)
309 self.dbconn.cursor().execute('DELETE FROM transactions_tags_link WHERE transactionId=?', (transObj.ID, ))
310 for tag in transObj.Tags:
311 self.dbconn.cursor().execute('INSERT INTO transactions_tags_link (transactionId, tagId) VALUES (?, ?)', (transObj.ID, tag.ID))
266 self.commitIfAppropriate()312 self.commitIfAppropriate()
267 313
268 def renameAccount(self, oldName, account):314 def renameAccount(self, oldName, account):
269315
=== modified file 'searchctrl.py'
--- searchctrl.py 2009-04-13 03:09:07 +0000
+++ searchctrl.py 2009-05-14 08:11:28 +0000
@@ -37,7 +37,7 @@
37 # The More/Less button.37 # The More/Less button.
38 self.moreButton = bankcontrols.MultiStateButton(self, labelDict={True: _("More options"), False: _("Less options")}, state=True)38 self.moreButton = bankcontrols.MultiStateButton(self, labelDict={True: _("More options"), False: _("Less options")}, state=True)
3939
40 self.matchChoices = [_("Amount"), _("Description"), _("Date")]40 self.matchChoices = [_("Amount"), _("Description"), _("Date"), _("Tags")]
41 self.matchBox = bankcontrols.CompactableComboBox(self, value=self.matchChoices[1], choices=self.matchChoices, style=wx.CB_READONLY)41 self.matchBox = bankcontrols.CompactableComboBox(self, value=self.matchChoices[1], choices=self.matchChoices, style=wx.CB_READONLY)
4242
43 self.caseCheck = wx.CheckBox(self, label=_("Case Sensitive"))43 self.caseCheck = wx.CheckBox(self, label=_("Case Sensitive"))
@@ -107,4 +107,4 @@
107 # Give or take the appropriate amount of space.107 # Give or take the appropriate amount of space.
108 self.Parent.Layout()108 self.Parent.Layout()
109 Publisher().sendMessage("SEARCH.MORETOGGLED")109 Publisher().sendMessage("SEARCH.MORETOGGLED")
110
111\ No newline at end of file110\ No newline at end of file
111
112112
=== modified file 'transactionolv.py'
--- transactionolv.py 2009-05-06 22:24:20 +0000
+++ transactionolv.py 2009-05-12 21:24:35 +0000
@@ -46,6 +46,7 @@
46 self.oddRowsBackColor = wx.WHITE46 self.oddRowsBackColor = wx.WHITE
47 self.cellEditMode = GroupListView.CELLEDIT_DOUBLECLICK47 self.cellEditMode = GroupListView.CELLEDIT_DOUBLECLICK
48 self.SetEmptyListMsg(_("No transactions entered."))48 self.SetEmptyListMsg(_("No transactions entered."))
49 self.TagSeparator = ','
49 50
50 # Calculate the necessary width for the date column.51 # Calculate the necessary width for the date column.
51 dateStr = str(datetime.date.today())52 dateStr = str(datetime.date.today())
@@ -61,6 +62,7 @@
61 ColumnDefn(_("Description"), valueGetter="Description", isSpaceFilling=True),62 ColumnDefn(_("Description"), valueGetter="Description", isSpaceFilling=True),
62 ColumnDefn(_("Amount"), "right", valueGetter="Amount", stringConverter=self.renderFloat),63 ColumnDefn(_("Amount"), "right", valueGetter="Amount", stringConverter=self.renderFloat),
63 ColumnDefn(_("Total"), "right", valueGetter=self.getTotal, stringConverter=self.renderFloat, isEditable=False),64 ColumnDefn(_("Total"), "right", valueGetter=self.getTotal, stringConverter=self.renderFloat, isEditable=False),
65 ColumnDefn(_("Tags"), valueGetter=self.getTagsString, valueSetter=self.setTagsFromString),
64 ])66 ])
65 # Our custom hack in OLV.py:2017 will render amount floats appropriately as %.2f when editing.67 # Our custom hack in OLV.py:2017 will render amount floats appropriately as %.2f when editing.
66 68
@@ -106,6 +108,13 @@
106 self.Freeze()108 self.Freeze()
107 self.SortBy(self.SORT_COL)109 self.SortBy(self.SORT_COL)
108 self.Thaw()110 self.Thaw()
111
112 def getTagsString(self, transaction):
113 return (self.TagSeparator + ' ').join([tag.Name for tag in transaction.Tags])
114
115 def setTagsFromString(self, transaction, tags):
116 tagNames = [tag.strip() for tag in tags.split(self.TagSeparator) if tag.strip()]
117 transaction.Tags = [self.BankController.Model.GetTagByName(name) for name in tagNames]
109 118
110 def getTotal(self, transObj):119 def getTotal(self, transObj):
111 """120 """
@@ -192,9 +201,13 @@
192 if len(transactions) == 1:201 if len(transactions) == 1:
193 removeStr = _("Remove this transaction")202 removeStr = _("Remove this transaction")
194 moveStr = _("Move this transaction to account")203 moveStr = _("Move this transaction to account")
204 addTagStr = _("Tag the transaction with")
205 removeTagStr = _("Untag the transaction with")
195 else:206 else:
196 removeStr = _("Remove these %i transactions") % len(transactions)207 removeStr = _("Remove these %i transactions") % len(transactions)
197 moveStr = _("Move these %i transactions to account") % len(transactions)208 moveStr = _("Move these %i transactions to account") % len(transactions)
209 addTagStr = _("Tag these %i transactions with") % len(transactions)
210 removeTagStr = _("Untag these %i transactions with") % len(transactions)
198 211
199 removeItem = wx.MenuItem(menu, -1, removeStr)212 removeItem = wx.MenuItem(menu, -1, removeStr)
200 menu.Bind(wx.EVT_MENU, lambda e: self.onRemoveTransactions(transactions), source=removeItem)213 menu.Bind(wx.EVT_MENU, lambda e: self.onRemoveTransactions(transactions), source=removeItem)
@@ -211,6 +224,42 @@
211 accountsMenu.Bind(wx.EVT_MENU, lambda e, account=account: self.onMoveTransactions(transactions, account), source=accountItem)224 accountsMenu.Bind(wx.EVT_MENU, lambda e, account=account: self.onMoveTransactions(transactions, account), source=accountItem)
212 moveToAccountItem.SetSubMenu(accountsMenu)225 moveToAccountItem.SetSubMenu(accountsMenu)
213 menu.AppendItem(moveToAccountItem)226 menu.AppendItem(moveToAccountItem)
227
228 # Tagging
229 tags = sorted(self.BankController.Model.Tags, key=lambda x: x.Name)
230
231 tagActionItem = wx.MenuItem(menu, -1, addTagStr)
232 tagsMenu = wx.Menu()
233 for tag in tags:
234 tagItem = wx.MenuItem(menu, -1, tag.Name)
235 tagsMenu.AppendItem(tagItem)
236 tagsMenu.Bind(wx.EVT_MENU, lambda e, tag=tag: self.onTagTransactions(transactions, tag), source=tagItem)
237
238 tagsMenu.AppendSeparator()
239
240 newTagItem = wx.MenuItem(menu,-1, _("New Tag"))
241 tagsMenu.AppendItem(newTagItem)
242 tagsMenu.Bind(wx.EVT_MENU, lambda e: self.onTagTransactions(transactions), source=newTagItem)
243
244 tagActionItem.SetSubMenu(tagsMenu)
245 menu.AppendItem(tagActionItem)
246
247 # get the tags currently applied to the selected transactions
248 currentTags = set()
249 for t in transactions:
250 currentTags.update(t.Tags)
251 currentTags = sorted(currentTags, key=lambda x: x.Name)
252
253 if len(currentTags) > 0:
254 tagActionItem = wx.MenuItem(menu, -1, removeTagStr)
255 tagsMenu = wx.Menu()
256
257 for tag in currentTags:
258 tagItem = wx.MenuItem(menu, -1, tag.Name)
259 tagsMenu.AppendItem(tagItem)
260 tagsMenu.Bind(wx.EVT_MENU, lambda e, tag=tag: self.onUntagTransactions(transactions, tag), source=tagItem)
261 tagActionItem.SetSubMenu(tagsMenu)
262 menu.AppendItem(tagActionItem)
214263
215 # Show the menu and then destroy it afterwards.264 # Show the menu and then destroy it afterwards.
216 self.PopupMenu(menu)265 self.PopupMenu(menu)
@@ -242,6 +291,20 @@
242 def onMoveTransactions(self, transactions, targetAccount):291 def onMoveTransactions(self, transactions, targetAccount):
243 """Move the transactions to the target account."""292 """Move the transactions to the target account."""
244 self.CurrentAccount.MoveTransactions(transactions, targetAccount)293 self.CurrentAccount.MoveTransactions(transactions, targetAccount)
294
295 def onTagTransactions(self, transactions, tag=None):
296 if tag is None:
297 dialog = wx.TextEntryDialog(self, _("Enter new tag"), _("New Tag"))
298 dialog.ShowModal()
299 dialog.Destroy()
300 tagName = dialog.GetValue().strip()
301 if len(tagName) == 0:
302 return
303 tag = self.BankController.Model.GetTagByName(tagName)
304 self.CurrentAccount.TagTransactions(transactions, tag)
305
306 def onUntagTransactions(self, transactions, tag):
307 self.CurrentAccount.UntagTransactions(transactions, tag)
245 308
246 def frozenResize(self):309 def frozenResize(self):
247 self.Parent.Layout()310 self.Parent.Layout()

Subscribers

People subscribed via source and target branches