diff --git a/src/plugins/gramplet/HeadlineNewsGramplet.py b/src/plugins/gramplet/HeadlineNewsGramplet.py index f21096214..9e0e84c64 100644 --- a/src/plugins/gramplet/HeadlineNewsGramplet.py +++ b/src/plugins/gramplet/HeadlineNewsGramplet.py @@ -26,7 +26,19 @@ import re import gobject import urllib +from xml.dom import minidom, Node +# FIXME For Python 3: +# Change: +# import urllib +# To: +# import urllib.request +# Change: +# url_info = urllib.urlopen(URL) +# To: +# url_info = urllib.request.urlopen(URL) import sys +from htmlentitydefs import name2codepoint as n2cp +import re #------------------------------------------------------------------------ # @@ -37,6 +49,26 @@ from DataViews import register, Gramplet from const import URL_WIKISTRING from TransUtils import sgettext as _ +#------------------------------------------------------------------------ +# +# Local functions +# +#------------------------------------------------------------------------ +def substitute(match): + ent = match.group(2) + if match.group(1) == "#": + return unichr(int(ent)) + else: + cp = n2cp.get(ent) + if cp: + return unichr(cp) + else: + return match.group() + +def decode_html(string): + entity_re = re.compile("&(#?)(\d{1,5}|\w{1,8});") + return entity_re.subn(substitute, string)[0] + #------------------------------------------------------------------------ # # Gramplet class @@ -53,8 +85,21 @@ class HeadlineNewsGramplet(Gramplet): """ Initialize gramplet. Start up update timer. """ - self.set_tooltip(_("Read headline news from the GRAMPS wiki")) + self.limit = 5 + # Description, Type, URL, Pretty URL for User + self.feeds = [ + ("GRAMPS Wiki Headline News", "wiki", (self.RAW % "HeadlineNews"), (self.URL % "HeadlineNews")), + ("GRAMPS Blog Comments", "rss", "http://blog.gramps-project.org/?feed=comments-rss", None), + ("GRAMPS Blog Posts", "rss", "http://blog.gramps-project.org/?feed=rss", None), + ("GRAMPS Wiki Changes", "rss", "http://www.gramps-project.org/wiki/index.php?title=Special:RecentChanges&feed=rss", None), + ("GRAMPS Bugtracker Issues", "rss", "http://www.gramps-project.org/bugs/issues_rss.php?key=ece7d21451d76337acf776c9a4384773", None), + ("GRAMPS SVN Commits", "rss", "http://cia.vc/stats/project/Gramps/.rss", None), + ] + self.set_tooltip(_("Read GRAMPS headline news")) self.update_interval = 3600 * 1000 # in miliseconds (1 hour) + self.set_use_markup(True) + self.set_wrap(False) + self.set_text(_("No Family Tree loaded.")) self.timer = gobject.timeout_add(self.update_interval, self.update_by_timer) @@ -66,22 +111,118 @@ class HeadlineNewsGramplet(Gramplet): return True # keep updating! def main(self): - continuation = self.process('HeadlineNews') - retval = True - while retval: - retval, text = continuation.next() - self.set_text(text) - yield True - self.cleanup(text) - yield False + self.set_text("Loading GRAMPS Headline News...\n") + fresh = True + yield True + for (feed_description, feed_type, feed_url, pretty_url) in self.feeds: + fp = urllib.urlopen(feed_url) + if feed_type == "wiki": + text = fp.read() + if fresh: + self.clear_text() + fresh = False + self.render_text("""%s [wiki]\n""" % (feed_description, pretty_url)) + self.render_text(self.decode_wiki(text).strip()) + self.append_text("\n") + yield True + elif feed_type == "rss": + try: + xmldoc = minidom.parse(fp) + except Exception, e: + print "Headline News Gramplet Error: RSS parse failed on '%s': %s" % (feed_description, e) + continue + if fresh: + self.clear_text() + fresh = False + self.render_text("""%s [RSS]\n""" % (feed_description, feed_url)) + yield True + rootNode = xmldoc.documentElement + for node in rootNode.childNodes: + #print "> ", node.nodeName + if (node.nodeName == "channel"): + count = 1 + for node2 in node.childNodes: + if count > 5: break + if (node2.nodeName == "item"): + title = "" + link = "" + desc = "" + # Gather up the data: + for item_node in node2.childNodes: + #print "---> ", item_node.nodeName + if (item_node.nodeName == "title"): + for text_node in item_node.childNodes: + if (text_node.nodeType == node.TEXT_NODE): + title += text_node.nodeValue + elif (item_node.nodeName == "link"): + for text_node in item_node.childNodes: + if (text_node.nodeType == node.TEXT_NODE): + link += text_node.nodeValue + elif (item_node.nodeName == "description"): + for text_node in item_node.childNodes: + if (text_node.nodeType == node.TEXT_NODE): + desc += text_node.nodeValue + if title: + if link: + self.render_text(" %d. " % count) + self.link(title, "URL", link, tooltip=link) + else: + self.render_text(" %d. %s" % (count, title)) + self.append_text(" - ") + self.append_text(self.first_line(desc)) + self.append_text("\n") + count += 1 + yield True + self.append_text("\n") + self.append_text("", scroll_to="begin") - def cleanup(self, text): + def first_line(self, text): + text = self.strip_html(text) + text = decode_html(text) + text = text.split("\n")[0] + if len(text) > 30: + text = text[:30] + return text + "..." + + def strip_html(self, text): + text = text.replace("nbsp;", " ") + retval = "" + last_c = None + state = "plain" + for c in text: + if c == "<": + state = "skip" + if state == "plain": + if c in ["\t", " ", "\n"]: + if (c == last_c): + continue + retval += c + last_c = c + if c == ">": + state = "plain" + return retval + + def decode_wiki(self, text): # final text text = text.replace("
", "\n") while "\n\n\n" in text: text = text.replace("\n\n\n", "\n\n") text = text.strip() ## Wiki text: + ## Templates: + pattern = '{{.*?}}' + matches = re.findall(pattern, text) + for match in matches: + page = match[2:-2] + oldtext = match + if "|" in page: + template, heading, body = page.split("|", 2) + if template.lower() == "release": + newtext = "GRAMPS " + heading + " released.\n\n" + else: + #newtext = "%s\n\n" % heading + newtext = "" + text = text.replace(oldtext, newtext) ### Internal wiki URL with title: pattern = re.compile('\[\[(.*?)\|(.*?)\]\]') matches = pattern.findall(text) @@ -117,16 +258,7 @@ class HeadlineNewsGramplet(Gramplet): matches = pattern.findall(text) for match in matches: text = text.replace("'''%s'''" % match, "%s" % match) - text = """Live update from www.gramps-project.org:\n\n""" + text - self.clear_text() - self.set_use_markup(True) - try: - self.render_text(text) - except: - cla, exc, trbk = sys.exc_info() - self.append_text(_("Error") + (" : %s %s\n\n" %(cla, exc))) - self.append_text(text) - self.append_text("", scroll_to="begin") + return text def wiki(self, title): return (self.URL % title) @@ -134,55 +266,6 @@ class HeadlineNewsGramplet(Gramplet): def nice_title(self, title): return title.replace("_", " ") - def process(self, title): - #print "processing '%s'..." % title - title = self.nice_title(title) - yield True, (_("Reading") + " '%s'..." % title) - fp = urllib.urlopen(self.RAW % title) - text = fp.read() - #text = text.replace("\n", " ") - html = re.findall('<.*?>', text) - for exp in html: - text = text.replace(exp, "") - text = text.replace("\n", "
") - fp.close() - pattern = '{{.*?}}' - matches = re.findall(pattern, text) - #print " before:", text - for match in matches: - page = match[2:-2] - oldtext = match - if "|" in page: - template, heading, body = page.split("|", 2) - if template.lower() == "release": - newtext = "GRAMPS " + heading + " released.

" - else: - newtext = "%s

" % heading - newtext += body + "
" - text = text.replace(oldtext, newtext) - else: # a macro/redirect - continuation = self.process("Template:" + page) - retval = True - while retval: - retval, newtext = continuation.next() - yield True, newtext - text = text.replace(oldtext, newtext) - #print " after:", text - pattern = '#REDIRECT \[\[.*?\]\]' - matches = re.findall(pattern, text) - #print " before:", text - for match in matches: - page = match[12:-2] - oldtext = match - continuation = self.process(page) - retval = True - while retval: - retval, newtext = continuation.next() - yield True, newtext - text = text.replace(oldtext, newtext) - #print " after:", text - yield False, text - #------------------------------------------------------------------------ # # Register Gramplet