# Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007-2009 Douglas S. Blank # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # $Id$ #------------------------------------------------------------------------ # # Python modules # #------------------------------------------------------------------------ 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 #------------------------------------------------------------------------ # # GRAMPS modules # #------------------------------------------------------------------------ from gen.plug import 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 # #------------------------------------------------------------------------ class HeadlineNewsGramplet(Gramplet): """ Headlines News Gramplet reads the Headline News every hour. """ RAW = URL_WIKISTRING + "%s&action=raw" URL = URL_WIKISTRING + "%s" def init(self): """ Initialize gramplet. Start up update timer. """ 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-rss2", 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) def update_by_timer(self): """ Update, and return True to continually update on interval. """ self.update() return True # keep updating! def main(self): 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 = u"" # 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 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 = u"" 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) for (g1, g2) in matches: text = text.replace("[[%s|%s]]" % (g1, g2), ("""%s""" % (self.wiki(g1), self.nice_title(g2)))) ### Internal wiki URL: pattern = re.compile('\[\[(.*?)\]\]') matches = pattern.findall(text) for match in matches: text = text.replace("[[%s]]" % match, ("""%s""" % (self.wiki(match), self.nice_title(match)))) ### URL with title: pattern = re.compile('\[http\:\/\/(.*?) (.*?)\]') matches = pattern.findall(text) for (g1, g2) in matches: text = text.replace("[http://%s %s]" % (g1, g2), ("""%s""" % (g1, g2))) ### URL: pattern = re.compile('\[http\:\/\/(.*?)\]') matches = pattern.findall(text) count = 1 for g1 in matches: text = text.replace("[http://%s]" % (g1), ("""%s""" % (g1, ("[%d]" % count)))) count += 1 ### Bold: pattern = re.compile("'''(.*?)'''") matches = pattern.findall(text) for match in matches: text = text.replace("'''%s'''" % match, "%s" % match) return text def wiki(self, title): return (self.URL % title) def nice_title(self, title): return title.replace("_", " ")