Merge commit '95b2b55820745606cc7dae2df4fbbb214eeb161c'
authorYves Marcoz <yves@marcoz.org>
Sun, 27 Nov 2011 18:43:13 +0000 (10:43 -0800)
committerYves Marcoz <yves@marcoz.org>
Sun, 27 Nov 2011 18:43:13 +0000 (10:43 -0800)
src/FeedingIt.py
src/config.py
src/rss_sqlite.py
src/wc.py

index b143b0b..29dc7fa 100644 (file)
@@ -149,6 +149,28 @@ def notify(message):
         get_iface()
         doit()
 
+def open_in_browser(link):
+    bus = dbus.SessionBus()
+    b_proxy = bus.get_object("com.nokia.osso_browser",
+                             "/com/nokia/osso_browser/request")
+    b_iface = dbus.Interface(b_proxy, 'com.nokia.osso_browser')
+
+    notify("Opening %s" % link)
+
+    # We open the link asynchronously: if the web browser is not
+    # already running, this can take a while.
+    def error_handler():
+        """
+        Something went wrong opening the URL.
+        """
+        def e(exception):
+            notify("Error opening %s: %s" % (link, str(exception)))
+        return e
+
+    b_iface.open_new_window(link,
+                            reply_handler=lambda *args: None,
+                            error_handler=error_handler())
+
 ##
 # Removes HTML or XML character references and entities from a text string.
 #
@@ -804,26 +826,7 @@ class DisplayArticle(hildon.StackableWindow):
         if link == None:
             link = self.currentUrl
 
-        bus = dbus.SessionBus()
-        b_proxy = bus.get_object("com.nokia.osso_browser",
-                                 "/com/nokia/osso_browser/request")
-        b_iface = dbus.Interface(b_proxy, 'com.nokia.osso_browser')
-
-        notify("Opening %s" % link)
-
-        # We open the link asynchronously: if the web browser is not
-        # already running, this can take a while.
-        def error_handler():
-            """
-            Something went wrong opening the URL.
-            """
-            def e(exception):
-                notify("Error opening %s: %s" % (link, str(exception)))
-            return e
-
-        b_iface.open_new_window(link,
-                                reply_handler=lambda *args: None,
-                                error_handler=error_handler())
+        open_in_browser(link)
 
 class DisplayFeed(hildon.StackableWindow):
     def __init__(self, listing, config):
@@ -1259,12 +1262,47 @@ class FeedingIt:
         self.checkAutoUpdate()
 
         gobject.idle_add(self.build_feed_display)
+        gobject.idle_add(self.check_for_woodchuck)
 
     def build_feed_display(self):
         if not hasattr(self, 'disp'):
             self.disp = DisplayFeed(self.listing, self.config)
             self.disp.connect("feed-closed", self.onFeedClosed)
 
+    def check_for_woodchuck(self):
+        if self.config.getAskedAboutWoodchuck():
+            return
+
+        try:
+            import woodchuck
+            # It is already installed successfully.
+            self.config.setAskedAboutWoodchuck(True)
+            return
+        except ImportError:
+            pass
+
+        note = hildon.hildon_note_new_confirmation(
+            self.window,
+            "\nFeedingIt can use Woodchuck, a network transfer "
+            + "daemon, to schedule transfers more intelligently.\n\n"
+            + "Install Woodchuck? (This is recommended.)\n")
+        note.set_button_texts("Install", "Cancel")
+        note.add_button("Learn More", 42)
+
+        while True:
+            response = gtk.Dialog.run(note)
+            if response == 42:
+                open_in_browser("http://hssl.cs.jhu.edu/~neal/woodchuck")
+                continue
+
+            break
+
+        note.destroy()
+
+        if response == gtk.RESPONSE_OK:
+            open_in_browser("http://maemo.org/downloads/product/raw/Maemo5/murmeltier?get_installfile")
+        self.config.setAskedAboutWoodchuck(True)
+
     def button_markAll(self, button):
         for key in self.listing.getListOfFeeds():
             feed = self.listing.getFeed(key)
index 9f7ec81..3abb490 100644 (file)
@@ -2,6 +2,7 @@
 
 # 
 # Copyright (c) 2007-2008 INdT.
+# Copyright (c) 2011 Neal H. Walfield.
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -39,7 +40,7 @@ logger = logging.getLogger(__name__)
 VERSION = "52"
 
 section = "FeedingIt"
-ranges = { "updateInterval":[0.5, 1, 2, 4, 12, 24], "expiry":[24, 48, 72, 144, 288], "fontSize":range(12,24), "orientation":["Automatic", "Landscape", "Portrait"], "artFontSize":[10, 12, 14, 16, 18, 20], "feedsort":["Manual", "Most unread", "Least unread", "Most recent", "Least recent"] }
+ranges = { "updateInterval":[0.5, 1, 2, 4, 8, 12, 24], "expiry":[24, 48, 72, 144, 288], "fontSize":range(12,24), "orientation":["Automatic", "Landscape", "Portrait"], "artFontSize":[10, 12, 14, 16, 18, 20], "feedsort":["Manual", "Most unread", "Least unread", "Most recent", "Least recent"] }
 titles = {"updateInterval":"Auto-update interval", "expiry":"Delete articles", "fontSize":"List font size", "orientation":"Display orientation", "artFontSize":"Article font size","feedsort":"Feed sort order"}
 subtitles = {"updateInterval":"Every %s hours", "expiry":"After %s hours", "fontSize":"%s pixels", "orientation":"%s", "artFontSize":"%s pixels", "feedsort":"%s"}
 
@@ -116,10 +117,34 @@ class Config():
 
         heading('Updating')
         button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        button.set_label("Automatically update feeds")
+        button.set_label("Time-Based Automatic Update\n"
+                         + "(requires use of FeedingIt widget)")
         button.set_active(self.config["autoupdate"])
         button.connect("toggled", self.button_toggled, "autoupdate")
         vbox.pack_start(button, expand=False)
+        button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
+        button.set_label("Woodchuck-Based Automatic Update")
+        button.set_active(self.config["woodchuck"])
+        button.connect("toggled", self.button_toggled, "woodchuck")
+        vbox.pack_start(button, expand=False)
+
+        try:
+            import woodchuck
+            woodchuck_installed = True
+        except ImportError:
+            woodchuck_installed = False
+
+        if not woodchuck_installed:
+            def install_woodchuck_clicked(button):
+                from FeedingIt import open_in_browser
+                open_in_browser("http://maemo.org/downloads/product/raw/Maemo5/murmeltier?get_installfile")
+
+            button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+            button.set_label("Install Woodchuck")
+            button.connect("clicked", install_woodchuck_clicked)
+            button.set_alignment(0,0,1,1)
+            vbox.pack_start(button, expand=False)
+
         add_setting('updateInterval')
         add_setting('expiry')
 
@@ -175,6 +200,14 @@ class Config():
         else:
             self.config[configName] = False
         #print "autoup",  self.autoupdate
+
+        if configName == 'woodchuck':
+            try:
+                from wc import wc_disable_set
+                wc_disable_set(not self.config['woodchuck'])
+            except Exception:
+                logger.exception("Disabling Woodchuck")
+
         self.saveConfig()
         
     def selection_changed(self, selector, button, setting):
@@ -190,43 +223,38 @@ class Config():
         
     def loadConfig(self):
         self.config = {}
+
+        configParser = RawConfigParser()
         try:
-            configParser = RawConfigParser()
             configParser.read(self.configFilename)
-            self.config["fontSize"] = configParser.getint(section, "fontSize")
-            self.config["artFontSize"] = configParser.getint(section, "artFontSize")
-            self.config["expiry"] = configParser.getint(section, "expiry")
-            self.config["autoupdate"] = configParser.getboolean(section, "autoupdate")
-            self.config["updateInterval"] = configParser.getfloat(section, "updateInterval")
-            self.config["orientation"] = configParser.get(section, "orientation")
-            self.config["imageCache"] = configParser.getboolean(section, "imageCache")
-        except:
-            self.config["fontSize"] = 17
-            self.config["artFontSize"] = 14
-            self.config["expiry"] = 24
-            self.config["autoupdate"] = False
-            self.config["updateInterval"] = 4
-            self.config["orientation"] = "Automatic"
-            self.config["imageCache"] = False
-        try:
-            self.config["proxy"] = configParser.getboolean(section, "proxy")
-        except:
-            self.config["proxy"] = True
-        try:
-            self.config["hidereadfeeds"] = configParser.getboolean(section, "hidereadfeeds")
-            self.config["hidereadarticles"] = configParser.getboolean(section, "hidereadarticles")
-        except:
-            self.config["hidereadfeeds"] = False
-            self.config["hidereadarticles"] = False
-        try:
-            self.config["extBrowser"] = configParser.getboolean(section, "extBrowser")
-        except:
-            self.config["extBrowser"] = False
-        try:
-            self.config["feedsort"] = configParser.get(section, "feedsort")
-        except:
-            self.config["feedsort"] = "Manual"
-        
+        except Exception:
+            logger.exception("Reading %s", self.configFilename)
+
+        # The function to use to fetch the parameter, the parameter's
+        # name and the default value.
+        values = ((configParser.getint, "fontSize", 17),
+                  (configParser.getint, "artFontSize", 14),
+                  (configParser.getint, "expiry", 24),
+                  (configParser.getboolean, "autoupdate", False),
+                  (configParser.getboolean, "woodchuck", True),
+                  (configParser.getboolean, "askedAboutWoodchuck", False),
+                  (configParser.getint, "updateInterval", 4),
+                  (configParser.get, "orientation", "Automatic"),
+                  (configParser.getboolean, "imageCache", False),
+                  (configParser.getboolean, "proxy", True),
+                  (configParser.getboolean, "hidereadfeeds", False),
+                  (configParser.getboolean, "hidereadarticles", False),
+                  (configParser.getboolean, "extBrowser", False),
+                  (configParser.get, "feedsort", "Manual"))
+
+        for fetcher, name, default in values:
+            try:
+                v = fetcher(section, name)
+            except Exception:
+                logger.exception("Reading config variable %s", name)
+                v = default
+            self.config[name] = v
+
     def saveConfig(self):
         configParser = RawConfigParser()
         configParser.add_section(section)
@@ -235,6 +263,8 @@ class Config():
         configParser.set(section, 'expiry', str(self.config["expiry"]))
         configParser.set(section, 'autoupdate', str(self.config["autoupdate"]))
         configParser.set(section, 'updateInterval', str(self.config["updateInterval"]))
+        configParser.set(section, 'woodchuck', str(self.config["woodchuck"]))
+        configParser.set(section, 'askedAboutWoodchuck', str(self.config["askedAboutWoodchuck"]))
         configParser.set(section, 'orientation', str(self.config["orientation"]))
         configParser.set(section, 'imageCache', str(self.config["imageCache"]))
         configParser.set(section, 'proxy', str(self.config["proxy"]))
@@ -273,6 +303,13 @@ class Config():
         return self.config["expiry"]
     def isAutoUpdateEnabled(self):
         return self.config["autoupdate"]
+    def getWoodchuckEnabled(self):
+        return self.config["woodchuck"]
+    def getAskedAboutWoodchuck(self):
+        return self.config["askedAboutWoodchuck"]
+    def setAskedAboutWoodchuck(self, value):
+        self.config["askedAboutWoodchuck"] = value
+        self.saveConfig()
     def getUpdateInterval(self):
         return float(self.config["updateInterval"])
     def getReadFont(self):
index 773753b..a12068b 100644 (file)
@@ -988,7 +988,7 @@ class Listing(BaseObject):
         # state.
         try:
             updater = os.path.basename(sys.argv[0]) == 'update_feeds.py'
-            wc_init (self, True if updater else False)
+            wc_init(config, self, True if updater else False)
             if wc().available() and updater:
                 # The list of known streams.
                 streams = wc().streams_list ()
@@ -1004,13 +1004,16 @@ class Listing(BaseObject):
                         logger.debug(
                             "Registering previously unknown channel: %s (%s)"
                             % (key, title,))
-                        # Use a default refresh interval of 6 hours.
-                        wc().stream_register (key, title, 6 * 60 * 60)
+                        wc().stream_register(
+                            key, title,
+                            self.config.getUpdateInterval() * 60 * 60)
                     else:
                         # Make sure the human readable name is up to date.
                         if wc()[key].human_readable_name != title:
                             wc()[key].human_readable_name = title
                         stream_ids.remove (key)
+                        wc()[key].freshness \
+                            = self.config.getUpdateInterval() * 60 * 60
                         
     
                 # Unregister any streams that are no longer subscribed to.
index c28d1d7..d9a2efd 100644 (file)
--- a/src/wc.py
+++ b/src/wc.py
@@ -32,7 +32,7 @@ except ImportError, exception:
         % traceback.format_exc ())
     woodchuck_imported = False
     class PyWoodchuck (object):
-        def available(self):
+        def available(self, *args, **kwargs):
             return False
     woodchuck = None
 
@@ -40,7 +40,7 @@ except ImportError, exception:
 refresh_interval = 6 * 60 * 60
 
 class mywoodchuck (PyWoodchuck):
-    def __init__(self, listing, human_readable_name, identifier,
+    def __init__(self, config, listing, human_readable_name, identifier,
                  request_feedback):
         try:
             PyWoodchuck.__init__ (self, human_readable_name, identifier,
@@ -52,9 +52,22 @@ class mywoodchuck (PyWoodchuck):
             self.available = self.not_available
             return
 
+        self.config = config
         self.listing = listing
 
-    def not_available(self):
+        try:
+            self.enabled = config.getWoodchuckEnabled()
+        except Exception:
+            logging.exception("Setting enabled")
+
+    def available(self, check_config=True):
+        if not PyWoodchuck.available(self):
+            return False
+        if check_config:
+            return self.config.getWoodchuckEnabled()
+        return True
+
+    def not_available(self, *args, **kwargs):
         return False
 
     # Woodchuck upcalls.
@@ -85,12 +98,12 @@ class mywoodchuck (PyWoodchuck):
                            str(e)))
 
 _w = None
-def wc_init(listing, request_feedback=False):
+def wc_init(config, listing, request_feedback=False):
     """Connect to the woodchuck server and initialize any state."""
     global _w
     assert _w is None
     
-    _w = mywoodchuck (listing, "FeedingIt", "org.marcoz.feedingit",
+    _w = mywoodchuck (config, listing, "FeedingIt", "org.marcoz.feedingit",
                       request_feedback)
 
     if not woodchuck_imported or not _w.available ():
@@ -98,6 +111,27 @@ def wc_init(listing, request_feedback=False):
     else:
         logger.debug("Woodchuck appears to be available.")
 
+def wc_disable_set(disable=True):
+    """Disable Woodchuck."""
+    if disable:
+        logger.info("Disabling Woodchuck")
+    else:
+        logger.info("Enabling Woodchuck")
+
+    global _w
+    if _w is None:
+        logging.info("Woodchuck not loaded.  Not doing anything.")
+        return
+
+    if not _w.available(check_config=False):
+        logging.info("Woodchuck not available.  Not doing anything.")
+        return
+
+    try:
+        _w.enabled = not disable
+    except Exception:
+        logger.exception("Disabling Woodchuck")
+
 def wc():
     """Return the Woodchuck singleton."""
     global _w