Added live 'total sp' counter in fremantle
[mevemon] / package / src / mevemon.py
1 #!/usr/bin/env python
2 #
3 # mEveMon - A character monitor for EVE Online
4 # Copyright (c) 2010  Ryan and Danny Campbell, and the mEveMon Team
5 #
6 # mEveMon is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # mEveMon is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 #
19
20
21 import hildon
22 import gtk
23 from eveapi import eveapi
24 import fetchimg
25 import apicache
26 import os.path
27 import traceback
28 import time
29
30 #conic is used for connection handling
31 import conic
32 #import socket for handling socket exceptions
33 import socket
34
35 # we will store our preferences in gconf
36 import gnome.gconf
37
38 #ugly hack to check maemo version. any better way?
39 if hasattr(hildon, "StackableWindow"):
40     from ui.fremantle import gui
41 else:
42     from ui.diablo import gui
43
44 class mEveMon():
45     """
46     The controller class for mEvemon. The intent is to help
47     abstract the EVE API and settings code from the UI code.
48
49     """
50
51     GCONF_DIR = "/apps/maemo/mevemon"
52
53     def __init__(self):
54         self.program = hildon.Program()
55         self.program.__init__()
56         self.gconf = gnome.gconf.client_get_default()
57         #NOTE: remove this after a few releases
58         self.update_settings()
59         self.connect_to_network()
60         self.cached_api = eveapi.EVEAPIConnection( cacheHandler = \
61                 apicache.cache_handler(debug=False))
62         self.gui = gui.mEveMonUI(self)
63
64     def run(self):
65         gtk.main()
66     
67     def quit(self, *args):
68         gtk.main_quit()
69
70     def update_settings(self):
71         """
72         Update from the old pre 0.3 settings to the new settings layout.
73         We should remove this eventually, once no one is using pre-0.3 mEveMon
74         """
75         uid = self.gconf.get_string("%s/eve_uid" % self.GCONF_DIR)
76         
77         if uid:
78             key = self.gconf.get_string("%s/eve_api_key" % self.GCONF_DIR)
79             self.add_account(uid, key)
80             self.gconf.unset("%s/eve_uid" % self.GCONF_DIR)
81             self.gconf.unset("%s/eve_api_key" % self.GCONF_DIR)
82
83
84     def get_accounts(self):
85         """
86         Returns a dictionary containing uid:api_key pairs gathered from gconf
87         """
88         accounts = {}
89         entries = self.gconf.all_entries("%s/accounts" % self.GCONF_DIR)
90
91         for entry in entries:
92             key = os.path.basename(entry.get_key())
93             value = entry.get_value().to_string()
94             accounts[key] = value
95
96         return accounts
97         
98     def get_api_key(self, uid):
99         """
100         Returns the api key associated with the given uid.
101         """
102         return self.gconf.get_string("%s/accounts/%s" % (self.GCONF_DIR, uid)) or ''
103
104     def remove_account(self, uid):
105         """
106         Removes the provided uid key from gconf
107         """
108         self.gconf.unset("%s/accounts/%s" % (self.GCONF_DIR, uid))
109
110     def add_account(self, uid, api_key):
111         """
112         Adds the provided uid:api_key pair to gconf.
113         """
114         self.gconf.set_string("%s/accounts/%s" % (self.GCONF_DIR, uid), api_key)
115
116     def get_auth(self, uid):
117         """
118         Returns an authentication object to be used for eveapi calls
119         that require authentication.
120         """
121         api_key = self.get_api_key(uid)
122
123         try:
124             auth = self.cached_api.auth(userID=uid, apiKey=api_key)
125         except:
126             traceback.print_exc()
127             return None
128
129         return auth
130
131     def get_char_sheet(self, uid, char_id):
132         """
133         Returns an object containing information about the character specified
134         by the provided character ID.
135         """
136         try:
137             sheet = self.get_auth(uid).character(char_id).CharacterSheet()
138         except:
139             # TODO: we should really have a logger that logs this error somewhere
140             traceback.print_exc()
141             return None
142
143         return sheet
144
145     def charid2uid(self, char_id):
146         """
147         Takes a character ID and returns the user ID of the account containing
148         the character.
149
150         Returns None if the character isn't found in any of the registered accounts.
151
152         """
153         acct_dict = self.get_accounts()
154         
155         for uid, api_key in acct_dict.items():
156             auth = self.cached_api.auth(userID=uid, apiKey=api_key)
157             api_char_list = auth.account.Characters()
158             
159             for character in api_char_list.characters:
160                 if character.characterID == char_id:
161                     return uid
162
163         
164         return None
165     
166     def char_id2name(self, char_id):
167         """
168         Takes a character ID and returns the character name associated with
169         that ID.
170         The EVE API accepts a comma-separated list of IDs, but for now we
171         will just handle a single ID.
172         """
173         try:
174             chars = self.cached_api.eve.CharacterName(ids=char_id).characters
175             name = chars[0].characterName
176         except:
177             traceback.print_exc()
178             return None
179
180         return name
181
182     def char_name2id(self, name):
183         """
184         Takes the name of an EVE character and returns the characterID.
185         
186         The EVE api accepts a comma separated list of names, but for now
187         we will just handle single names/
188         """
189         try:
190             chars = self.cached_api.eve.CharacterID(names=name).characters
191             char_id = chars[0].characterID
192             char_name = chars[0].name
193         except:
194             traceback.print_exc()
195             return None
196
197         return char_id
198
199     def get_chars_from_acct(self, uid):
200         """
201         Returns a list of characters associated with the provided user ID.
202         """
203         auth = self.get_auth(uid)
204         if not auth:
205             return None
206         else:
207             try:
208                 api_char_list = auth.account.Characters()
209                 char_list = [char.name for char in api_char_list.characters]
210             except:
211                 traceback.print_exc()
212                 return None
213
214         return char_list
215
216     def get_characters(self):
217         """
218         Returns a list of (character_name, image_path, uid) tuples from all the
219         accounts that are registered to mEveMon.
220         
221         If there is an authentication issue, then instead of adding a valid
222         pair to the list, it appends an 'error message' 
223
224         """
225         ui_char_list = []
226         err_img = "/usr/share/mevemon/imgs/error.jpg"
227
228         placeholder_chars = ("Please check your API settings.", err_img, "0")
229         
230         acct_dict = self.get_accounts()
231         if not acct_dict:
232             return [placeholder_chars]
233
234         for uid in acct_dict.keys():
235             char_names = self.get_chars_from_acct(uid)
236             
237             if not char_names:
238                 ui_char_list.append(placeholder_chars)
239             else:
240                 # append each char we get to the list we'll return to the
241                 # UI --danny
242                 for char_name in char_names:
243                     ui_char_list.append((char_name, self.get_portrait(char_name, 64) , uid) )
244         
245         return ui_char_list
246
247     def get_portrait(self, char_name, size):
248         """
249         Returns the file path of the retrieved portrait
250         """
251         char_id = self.char_name2id(char_name)
252         
253         return fetchimg.portrait_filename(char_id, size)
254
255     def get_skill_tree(self):
256         """
257         Returns an object from eveapi containing skill tree info
258         """
259         try:
260             tree = self.cached_api.eve.SkillTree()
261         except:
262             traceback.print_exc()
263             return None
264         
265         return tree
266
267     def get_skill_in_training(self, uid, char_id):
268         """
269         Returns an object from eveapi containing information about the
270         current skill in training
271
272         """
273         try:
274             skill = self.get_auth(uid).character(char_id).SkillInTraining()
275         except:
276             traceback.print_exc()
277             return None
278
279         return skill
280
281     def connection_cb(self, connection, event, mgc):
282         pass    
283
284
285     def connect_to_network(self):
286         connection = conic.Connection()
287         #why 0xAA55?
288         connection.connect("connection-event", self.connection_cb, 0xAA55)
289         assert(connection.request_connection(conic.CONNECT_FLAG_NONE))
290
291
292     def get_sp(self, uid, char_id):
293         sheet = self.get_char_sheet(uid, char_id)
294         
295         # TODO: we also have to calculate how much we earned from a
296         # currently training skill
297
298         actual_sp = 0
299         
300         for skill in sheet.skills:
301             actual_sp += skill.skillpoints
302
303         live_sp = actual_sp + self.get_training_sp(uid, char_id)
304
305         return live_sp
306
307     def get_spps(self, uid, char_id):
308         """
309         Calculate and returns the skill points per hour for the given character.
310         """
311         skill = self.get_skill_in_training(uid, char_id)
312         
313         if not skill.skillInTraining:
314             return (0, 0)
315
316         total_sp = skill.trainingDestinationSP - skill.trainingStartSP
317         total_time = skill.trainingEndTime - skill.trainingStartTime
318         
319         spps = float(total_sp) / total_time
320     
321         return (spps, skill.trainingStartTime)
322
323     def get_training_sp(self, uid, char_id):
324         """
325         returns the additional SP that the in-training skill has acquired
326         """
327
328         spps_tuple = self.get_spps(uid, char_id)
329         
330         print spps_tuple
331
332         if not spps_tuple:
333             return 0
334
335         spps, start_time = spps_tuple
336         
337         eve_time = time.time() #evetime is utc, right?
338         
339         time_diff =  eve_time - start_time
340
341         return (spps * time_diff) 
342
343
344 if __name__ == "__main__":
345     app = mEveMon()
346     app.run()