19
19
# along with wxBanker. If not, see <http://www.gnu.org/licenses/>.
21
21
from wx.lib.pubsub import Publisher
22
from wxbanker.mint import web
24
from BeautifulSoup import BeautifulSoup
28
from wxbanker.mint.keyring import Keyring
30
print "[WARNING] Unable to import keyring module, Mint.com integration isn't possible."
31
import traceback; traceback.print_exc()
34
class MintLoginException(Exception):
35
"""Thrown on invalid credentials or login error."""
39
"""A MintConnection represents a static connection to Mint.com."""
23
from wxbanker.lib.mint import api as mintapi
24
from wxbanker.mint.kring import Keyring
27
"""A collection of methods for interfacing with a MintConnection."""
28
_CachedAccounts = None
32
return cls._CachedAccounts is not None
43
self.__dict__ = self._shared_state
44
if not hasattr(self, "_CachedSummary"):
45
self._CachedSummary = None
47
def Login(self, username, password, notify=True):
48
# If we are already logged in, this is a no-op. Use Refresh if you want updated data.
49
if self._CachedSummary:
35
def Login(cls, username, password, notify=True):
52
postArgs = {"username": username, "password": password, "task": "L", "nextPage": ""}
53
result = web.post("https://wwws.mint.com/loginUserSubmit.xevent", postArgs)
54
if "your password?" in result.lower():
55
raise MintLoginException("Invalid credentials")
57
self._CachedSummary = result
40
for account in mintapi.get_accounts(username, password):
41
account['balance'] = account['value'] # convert to wxBanker speak
42
accounts[account['accountId']] = account
43
cls._CachedAccounts = accounts
60
46
Publisher.sendMessage("mint.updated")
63
if self._CachedSummary is None:
64
raise Exception("Please call Login(username, password) first.")
66
return self._CachedSummary
70
"""A collection of methods for interfacing with a MintConnection."""
71
_CachedAccounts = None
75
return MintConnection()._CachedSummary is not None
78
def Login(username, password, notify=True):
79
return MintConnection().Login(username, password, notify)
82
def LoginFromKeyring(notify=True):
49
def LoginFromKeyring(cls, notify=True):
83
50
if Keyring is None:
84
51
raise Exception("Keyring was unable to be imported")
86
53
keyring = Keyring()
87
if not keyring.has_credentials():
54
if keyring.get_credentials() is None:
88
55
raise Exception("Keyring does not have Mint.com credentials")
90
57
user, passwd = keyring.get_credentials()
91
return Mint.Login(user, passwd, notify)
58
return cls.Login(user, passwd, notify)
95
62
"""Returns a dictionary like {account_id: {'name': name, 'balance': balance}}"""
96
if Mint._CachedAccounts is None:
97
summary = MintConnection().GetSummary()
98
soup = BeautifulSoup(summary)
101
accountRe = re.compile("account( refreshing|)")
102
for li in soup.findAll("li", {"class": accountRe}):
105
balanceStr = h4.find("span").contents[0]
106
balanceStr = balanceStr.replace("–".decode("utf-8"), "-") # Mint uses a weird negative sign!
108
balanceStr = balanceStr.replace(char, "")
110
aid = int(li.get("id").split("-")[1])
111
balance = float(balanceStr)
112
bankName = h4.find("a").contents[0]
113
accountName = h6.contents[1]
114
# Support BeautifulSoup 3(.2.0)
115
if hasattr(accountName, 'text'):
116
accountName = accountName.text
117
name = bankName + ' ' + accountName
118
mintAccounts[aid] = {'name': name, 'balance': balance}
120
Mint._CachedAccounts = mintAccounts
122
return Mint._CachedAccounts
63
return cls._CachedAccounts
125
def GetAccount(accountid):
126
accounts = Mint.GetAccounts()
127
account = accounts.get(accountid, None)
66
def GetAccount(cls, accountid):
67
account = cls.GetAccounts().get(accountid)
128
68
if account is None:
129
69
raise Exception("No such account with ID: %r. Valid accounts: %s" % (accountid, accounts))
133
def GetAccountBalance(accountid):
134
return Mint.GetAccount(accountid)['balance']
73
def GetAccountBalance(cls, accountid):
74
return cls.GetAccount(accountid)['balance']
137
77
def GetAccountTransactionsCSV(accountid):
78
#TODO: update for new Mint.
138
79
return web.read("https://wwws.mint.com/transactionDownload.event?accountId=%s&comparableType=8&offset=0" % accountid)