Only remove contents of directory when clicking 'clear cache'
[mevemon] / package / src / mevemon.py
index 600f058..f8036e4 100755 (executable)
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+import os
+import time
+import sys
+import logging
+import logging.handlers
+import util
 
 import hildon
 import gtk
-from eveapi import eveapi
-import fetchimg
-import apicache
-import os.path
-import traceback
-import time
-
 #conic is used for connection handling
 import conic
-#import socket for handling socket exceptions
-import socket
 
-# we will store our preferences in gconf
-import gnome.gconf
+from eveapi import eveapi
+import fetchimg
+import apicache
+import file_settings as settings
+from constants import LOGPATH, MAXBYTES, LOGCOUNT, CONFIG_DIR, IMG_CACHE_PATH
+from constants import APICACHE_PATH
 
 #ugly hack to check maemo version. any better way?
 if hasattr(hildon, "StackableWindow"):
@@ -41,31 +42,14 @@ if hasattr(hildon, "StackableWindow"):
 else:
     from ui.diablo import gui
 
-class mEveMon():
-    """
-    The controller class for mEvemon. The intent is to help
-    abstract the EVE API and settings code from the UI code.
-
+class mEveMon:
+    """ The controller class for mEvemon. The intent is to help
+        abstract the EVE API and settings code from the UI code.
     """
-
-    about_name = 'mEveMon'
-    about_text = ('Mobile character monitor for EVE Online')
-    about_authors = ['Ryan Campbell <campbellr@gmail.com>',
-                     'Danny Campbell <danny.campbell@gmail.com>']
-
-    about_website = 'http://mevemon.garage.maemo.org'
-    app_version = '0.4-8'
-
-
-    GCONF_DIR = "/apps/maemo/mevemon"
-
     def __init__(self):
         self.program = hildon.Program()
-        self.program.__init__()
-        self.gconf = gnome.gconf.client_get_default()
-        #NOTE: remove this after a few releases
-        self.update_settings()
         self.connect_to_network()
+        self.settings = settings.Settings()
         self.cached_api = eveapi.EVEAPIConnection( cacheHandler = \
                 apicache.cache_handler(debug=False))
         self.gui = gui.mEveMonUI(self)
@@ -77,92 +61,42 @@ class mEveMon():
     def quit(self, *args):
         gtk.main_quit()
 
-    def update_settings(self):
-        """
-        Update from the old pre 0.3 settings to the new settings layout.
-        We should remove this eventually, once no one is using pre-0.3 mEveMon
-        """
-        uid = self.gconf.get_string("%s/eve_uid" % self.GCONF_DIR)
-        
-        if uid:
-            key = self.gconf.get_string("%s/eve_api_key" % self.GCONF_DIR)
-            self.add_account(uid, key)
-            self.gconf.unset("%s/eve_uid" % self.GCONF_DIR)
-            self.gconf.unset("%s/eve_api_key" % self.GCONF_DIR)
-
-
-    def get_accounts(self):
-        """
-        Returns a dictionary containing uid:api_key pairs gathered from gconf
-        """
-        accounts = {}
-        entries = self.gconf.all_entries("%s/accounts" % self.GCONF_DIR)
-
-        for entry in entries:
-            key = os.path.basename(entry.get_key())
-            value = entry.get_value().to_string()
-            accounts[key] = value
-
-        return accounts
-        
-    def get_api_key(self, uid):
-        """
-        Returns the api key associated with the given uid.
-        """
-        return self.gconf.get_string("%s/accounts/%s" % (self.GCONF_DIR, uid)) or ''
-
-    def remove_account(self, uid):
-        """
-        Removes the provided uid key from gconf
-        """
-        self.gconf.unset("%s/accounts/%s" % (self.GCONF_DIR, uid))
-
-    def add_account(self, uid, api_key):
-        """
-        Adds the provided uid:api_key pair to gconf.
-        """
-        self.gconf.set_string("%s/accounts/%s" % (self.GCONF_DIR, uid), api_key)
-
     def get_auth(self, uid):
+        """ Returns an authentication object to be used for eveapi calls
+            that require authentication.
         """
-        Returns an authentication object to be used for eveapi calls
-        that require authentication.
-        """
-        api_key = self.get_api_key(uid)
+        api_key = self.settings.get_api_key(uid)
 
         try:
             auth = self.cached_api.auth(userID=uid, apiKey=api_key)
         except Exception, e:
             self.gui.report_error(str(e))
-            traceback.print_exc()
+            logging.getLogger('mevemon').exception("Failed to get character name")
             return None
 
         return auth
 
     def get_char_sheet(self, uid, char_id):
-        """
-        Returns an object containing information about the character specified
-        by the provided character ID.
+        """ Returns an object containing information about the character specified
+            by the provided character ID.
         """
         try:
             sheet = self.get_auth(uid).character(char_id).CharacterSheet()
         except Exception, e:
             self.gui.report_error(str(e))
-            # TODO: we should really have a logger that logs this error somewhere
-            traceback.print_exc()
+            logging.getLogger('mevemon').exception("Failed to get character name")
             return None
 
         return sheet
 
     def charid2uid(self, char_id):
-        """
-        Takes a character ID and returns the user ID of the account containing
-        the character.
+        """ Takes a character ID and returns the user ID of the account containing
+            the character.
 
-        Returns None if the character isn't found in any of the registered accounts.
+            Returns None if the character isn't found in any of the registered accounts.
 
         """
-        acct_dict = self.get_accounts()
+        acct_dict = self.settings.get_accounts()
         
         for uid, api_key in acct_dict.items():
             auth = self.cached_api.auth(userID=uid, apiKey=api_key)
@@ -176,32 +110,28 @@ class mEveMon():
                 if character.characterID == char_id:
                     return uid
 
-        
-        return None
     
     def char_id2name(self, char_id):
-        """
-        Takes a character ID and returns the character name associated with
-        that ID.
-        The EVE API accepts a comma-separated list of IDs, but for now we
-        will just handle a single ID.
+        """ Takes a character ID and returns the character name associated with
+            that ID.
+            The EVE API accepts a comma-separated list of IDs, but for now we
+            will just handle a single ID.
         """
         try:
             chars = self.cached_api.eve.CharacterName(ids=char_id).characters
             name = chars[0].characterName
         except Exception, e:
             self.gui.report_error(str(e))
-            traceback.print_exc()
+            logging.getLogger('mevemon').exception("Failed to get character name")
             return None
 
         return name
 
     def char_name2id(self, name):
-        """
-        Takes the name of an EVE character and returns the characterID.
+        """ Takes the name of an EVE character and returns the characterID.
         
-        The EVE api accepts a comma separated list of names, but for now
-        we will just handle single names/
+            The EVE api accepts a comma separated list of names, but for now
+            we will just handle single names/
         """
         try:
             chars = self.cached_api.eve.CharacterID(names=name).characters
@@ -209,14 +139,13 @@ class mEveMon():
             char_name = chars[0].name
         except Exception, e:
             self.gui.report_error(str(e))
-            traceback.print_exc()
+            logging.getLogger('mevemon').exception("Failed to get ID")
             return None
 
         return char_id
 
     def get_chars_from_acct(self, uid):
-        """
-        Returns a list of characters associated with the provided user ID.
+        """ Returns a list of characters associated with the provided user ID.
         """
         auth = self.get_auth(uid)
         if not auth:
@@ -227,28 +156,26 @@ class mEveMon():
                 char_list = [char.name for char in api_char_list.characters]
             except Exception, e:
                 self.gui.report_error(str(e))
-                traceback.print_exc()
+                logging.getLogger('mevemon').exception("Failed to get character list")
                 return None
 
         return char_list
 
     def get_characters(self):
-        """
-        Returns a list of (character_name, image_path, uid) tuples from all the
-        accounts that are registered to mEveMon.
+        """ Returns a list of (character_name, image_path, uid) tuples from all the
+            accounts that are registered to mEveMon.
         
-        If there is an authentication issue, then instead of adding a valid
-        pair to the list, it appends an 'error message' 
-
+            If there is an authentication issue, then instead of adding a valid
+            pair to the list, it appends an 'error message' 
         """
 
         ui_char_list = []
         err_img = "/usr/share/mevemon/imgs/error.jpg"
-        err_txt = "Problem fetching info for account"
+        err_txt = "Problem fetching info for account (or no accounts added)"
 
         placeholder_chars = (err_txt, err_img, None)
         
-        acct_dict = self.get_accounts()
+        acct_dict = self.settings.get_accounts()
         if not acct_dict:
             return [placeholder_chars]
 
@@ -266,54 +193,49 @@ class mEveMon():
         return ui_char_list
 
     def get_portrait(self, char_name, size):
-        """
-        Returns the file path of the retrieved portrait
+        """ Returns the file path of the retrieved portrait
         """
         char_id = self.char_name2id(char_name)
         
         return fetchimg.portrait_filename(char_id, size)
 
     def get_skill_tree(self):
-        """
-        Returns an object from eveapi containing skill tree info
+        """ Returns an object from eveapi containing skill tree info
         """
         try:
             tree = self.cached_api.eve.SkillTree()
         except Exception, e:
             self.gui.report_error(str(e))
-            traceback.print_exc()
+            logging.getLogger('mevemon').exception("Failed to get skill-in-training:")
             return None
         
         return tree
 
     def get_skill_in_training(self, uid, char_id):
-        """
-        Returns an object from eveapi containing information about the
-        current skill in training
+        """ Returns an object from eveapi containing information about the
+            current skill in training
         """
         try:
             skill = self.get_auth(uid).character(char_id).SkillInTraining()
         except Exception, e:
             self.gui.report_error(str(e))
-            traceback.print_exc()
+            logging.getLogger('mevemon').exception("Failed to get skill-in-training:")
             return None
 
         return skill
 
     def connection_cb(self, connection, event, mgc):
-        """
-        I'm not sure why we need this, but connection.connect() won't work
-        without it, even empty.
+        """ I'm not sure why we need this, but connection.connect() won't work
+            without it, even empty.
         """
         pass    
 
 
     def connect_to_network(self):
-        """
-        This will connect to the default network if avaliable, or pop up the
-        connection dialog to select a connection.
-        Running this when we start the program ensures we are connected to a
-        network.
+        """ This will connect to the default network if avaliable, or pop up the
+            connection dialog to select a connection.
+            Running this when we start the program ensures we are connected to a
+            network.
         """
         connection = conic.Connection()
         #why 0xAA55?
@@ -322,9 +244,8 @@ class mEveMon():
 
 
     def get_sp(self, uid, char_id):
-        """
-        Adds up the SP for all known skills, then calculates the SP gained
-        from an in-training skill.
+        """ Adds up the SP for all known skills, then calculates the SP gained
+            from an in-training skill.
         """
         actual_sp = 0
         
@@ -337,8 +258,7 @@ class mEveMon():
         return live_sp
 
     def get_spps(self, uid, char_id):
-        """
-        Calculate and returns the skill points per hour for the given character.
+        """ Calculate and returns the skill points per hour for the given character.
         """
         skill = self.get_skill_in_training(uid, char_id)
         
@@ -353,8 +273,7 @@ class mEveMon():
         return (spps, skill.trainingStartTime)
 
     def get_training_sp(self, uid, char_id):
-        """
-        returns the additional SP that the in-training skill has acquired
+        """ returns the additional SP that the in-training skill has acquired
         """
         spps_tuple = self.get_spps(uid, char_id)
         
@@ -366,7 +285,48 @@ class mEveMon():
 
         return (spps * time_diff) 
 
+    def clear_cache(self):
+        """ Clears all cached data (images and eveapi cache) """
+        try:
+            util.clean_dir(IMG_CACHE_PATH)
+            util.clean_dir(APICACHE_PATH)
+        except OSError, e:
+            logging.getLogger('mevemon').exception("Failed to clear cache")
+
+def excepthook(ex_type, value, tb):
+    """ a replacement for the default exception handler that logs errors"""
+    logging.getLogger("mevemon").error('Uncaught exception:', 
+                      exc_info=(ex_type, value, tb))
+
+def setupLogger():
+    """ sets up the logging """
+    if not os.path.exists(CONFIG_DIR):
+        os.makedirs(CONFIG_DIR)
+
+    logger = logging.getLogger("mevemon")
+    logger.setLevel(logging.DEBUG)
+    
+    fileHandler = logging.handlers.RotatingFileHandler(LOGPATH,
+                                                    maxBytes=MAXBYTES,
+                                                    backupCount=LOGCOUNT)
+    file_fmt = logging.Formatter('%(asctime)s %(name)-10s %(levelname)-5s %(message)s')
+    console_fmt = logging.Formatter('%(name)-10s %(levelname)-5s %(message)s')
+    fileHandler.setFormatter(file_fmt)
+    logger.addHandler(fileHandler)
+
+    #create console handler
+    console = logging.StreamHandler()
+    console.setLevel(logging.DEBUG)
+    console.setFormatter(console_fmt)
+    logger.addHandler(console)
+    logger.debug("Logging successfully set-up.")
+
 
 if __name__ == "__main__":
+    setupLogger()
+    sys.excepthook = excepthook
     app = mEveMon()
-    app.run()
+    try:
+        app.run()
+    except KeyboardInterrupt:
+        sys.exit(0)