~ubuntu-branches/ubuntu/vivid/wxbanker/vivid

« back to all changes in this revision

Viewing changes to wxbanker/mint/api.py

  • Committer: Package Import Robot
  • Author(s): Michael Rooney
  • Date: 2013-11-19 16:13:52 UTC
  • mto: This revision was merged to the branch mainline in revision 15.
  • Revision ID: package-import@ubuntu.com-20131119161352-7ijg07r997ouew0i
Tags: upstream-0.9.0
Import upstream version 0.9.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
#    along with wxBanker.  If not, see <http://www.gnu.org/licenses/>.
20
20
 
21
21
from wx.lib.pubsub import Publisher
22
 
from wxbanker.mint import web
23
 
web.enablecookies()
24
 
from BeautifulSoup import BeautifulSoup
25
 
import re
26
 
 
27
 
try:
28
 
    from wxbanker.mint.keyring import Keyring
29
 
except ImportError:
30
 
    print "[WARNING] Unable to import keyring module, Mint.com integration isn't possible."
31
 
    import traceback; traceback.print_exc()
32
 
    Keyring = None
33
 
 
34
 
class MintLoginException(Exception):
35
 
    """Thrown on invalid credentials or login error."""
36
 
    pass
37
 
 
38
 
class MintConnection:
39
 
    """A MintConnection represents a static connection to Mint.com."""
40
 
    _shared_state = {}
 
22
 
 
23
from wxbanker.lib.mint import api as mintapi
 
24
from wxbanker.mint.kring import Keyring
 
25
 
 
26
class Mint:
 
27
    """A collection of methods for interfacing with a MintConnection."""
 
28
    _CachedAccounts = None
 
29
 
 
30
    @classmethod
 
31
    def IsLoggedIn(cls):
 
32
        return cls._CachedAccounts is not None
41
33
    
42
 
    def __init__(self):
43
 
        self.__dict__ = self._shared_state
44
 
        if not hasattr(self, "_CachedSummary"):
45
 
            self._CachedSummary = None
46
 
 
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:
 
34
    @classmethod
 
35
    def Login(cls, username, password, notify=True):
 
36
        if cls.IsLoggedIn():
50
37
            return
51
 
        
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")
56
 
        
57
 
        self._CachedSummary = result
58
 
        
 
38
 
 
39
        accounts = {}
 
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
 
44
 
59
45
        if notify:
60
46
            Publisher.sendMessage("mint.updated")
61
 
        
62
 
    def GetSummary(self):
63
 
        if self._CachedSummary is None:
64
 
            raise Exception("Please call Login(username, password) first.")
65
 
        
66
 
        return self._CachedSummary
67
 
        
68
 
 
69
 
class Mint:
70
 
    """A collection of methods for interfacing with a MintConnection."""
71
 
    _CachedAccounts = None
72
 
    
73
 
    @staticmethod
74
 
    def IsLoggedIn():
75
 
        return MintConnection()._CachedSummary is not None
76
 
    
77
 
    @staticmethod
78
 
    def Login(username, password, notify=True):
79
 
        return MintConnection().Login(username, password, notify)
80
 
 
81
 
    @staticmethod
82
 
    def LoginFromKeyring(notify=True):
 
47
 
 
48
    @classmethod
 
49
    def LoginFromKeyring(cls, notify=True):
83
50
        if Keyring is None:
84
51
            raise Exception("Keyring was unable to be imported")
85
52
 
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")
89
56
 
90
57
        user, passwd = keyring.get_credentials()
91
 
        return Mint.Login(user, passwd, notify)
 
58
        return cls.Login(user, passwd, notify)
92
59
        
93
 
    @staticmethod
94
 
    def GetAccounts():
 
60
    @classmethod
 
61
    def GetAccounts(cls):
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)
99
 
            mintAccounts = {}
100
 
            
101
 
            accountRe = re.compile("account( refreshing|)")
102
 
            for li in soup.findAll("li", {"class": accountRe}):
103
 
                h4 = li.find("h4")
104
 
                h6 = li.find("h6")
105
 
                balanceStr = h4.find("span").contents[0]
106
 
                balanceStr = balanceStr.replace("–".decode("utf-8"), "-") # Mint uses a weird negative sign!
107
 
                for char in ",$":
108
 
                    balanceStr = balanceStr.replace(char, "")
109
 
                    
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}
119
 
                
120
 
            Mint._CachedAccounts = mintAccounts
121
 
             
122
 
        return Mint._CachedAccounts
 
63
        return cls._CachedAccounts
123
64
 
124
 
    @staticmethod
125
 
    def GetAccount(accountid):
126
 
        accounts = Mint.GetAccounts()
127
 
        account = accounts.get(accountid, None)
 
65
    @classmethod
 
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))
130
70
        return account
131
71
        
132
 
    @staticmethod
133
 
    def GetAccountBalance(accountid):
134
 
        return Mint.GetAccount(accountid)['balance']
 
72
    @classmethod
 
73
    def GetAccountBalance(cls, accountid):
 
74
        return cls.GetAccount(accountid)['balance']
135
75
 
136
76
    @staticmethod
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)
139
80
 
140
81
 
146
87
    #Mint.LoginFromKeyring()
147
88
    Mint.Login(username, password)
148
89
    accounts = Mint.GetAccounts()
149
 
    pprint.pprint(accounts)
150
90
 
151
91
    for account in accounts:
152
92
        print account, accounts[account]
153
 
    
 
93
 
154
94
if __name__ == "__main__":
155
95
    main()